xref: /openbsd-src/usr.bin/mandoc/mdoc_validate.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: mdoc_validate.c,v 1.217 2016/01/08 17:48:04 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org>
5  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include <sys/types.h>
20 #ifndef OSNAME
21 #include <sys/utsname.h>
22 #endif
23 
24 #include <assert.h>
25 #include <ctype.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 
32 #include "mandoc_aux.h"
33 #include "mandoc.h"
34 #include "roff.h"
35 #include "mdoc.h"
36 #include "libmandoc.h"
37 #include "roff_int.h"
38 #include "libmdoc.h"
39 
40 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
41 
42 #define	POST_ARGS struct roff_man *mdoc
43 
44 enum	check_ineq {
45 	CHECK_LT,
46 	CHECK_GT,
47 	CHECK_EQ
48 };
49 
50 typedef	void	(*v_post)(POST_ARGS);
51 
52 static	void	 check_text(struct roff_man *, int, int, char *);
53 static	void	 check_argv(struct roff_man *,
54 			struct roff_node *, struct mdoc_argv *);
55 static	void	 check_args(struct roff_man *, struct roff_node *);
56 static	int	 child_an(const struct roff_node *);
57 static	size_t		macro2len(int);
58 static	void	 rewrite_macro2len(char **);
59 
60 static	void	 post_an(POST_ARGS);
61 static	void	 post_an_norm(POST_ARGS);
62 static	void	 post_at(POST_ARGS);
63 static	void	 post_bd(POST_ARGS);
64 static	void	 post_bf(POST_ARGS);
65 static	void	 post_bk(POST_ARGS);
66 static	void	 post_bl(POST_ARGS);
67 static	void	 post_bl_block(POST_ARGS);
68 static	void	 post_bl_block_tag(POST_ARGS);
69 static	void	 post_bl_head(POST_ARGS);
70 static	void	 post_bl_norm(POST_ARGS);
71 static	void	 post_bx(POST_ARGS);
72 static	void	 post_defaults(POST_ARGS);
73 static	void	 post_display(POST_ARGS);
74 static	void	 post_dd(POST_ARGS);
75 static	void	 post_dt(POST_ARGS);
76 static	void	 post_en(POST_ARGS);
77 static	void	 post_es(POST_ARGS);
78 static	void	 post_eoln(POST_ARGS);
79 static	void	 post_ex(POST_ARGS);
80 static	void	 post_fa(POST_ARGS);
81 static	void	 post_fn(POST_ARGS);
82 static	void	 post_fname(POST_ARGS);
83 static	void	 post_fo(POST_ARGS);
84 static	void	 post_hyph(POST_ARGS);
85 static	void	 post_ignpar(POST_ARGS);
86 static	void	 post_it(POST_ARGS);
87 static	void	 post_lb(POST_ARGS);
88 static	void	 post_nd(POST_ARGS);
89 static	void	 post_nm(POST_ARGS);
90 static	void	 post_ns(POST_ARGS);
91 static	void	 post_obsolete(POST_ARGS);
92 static	void	 post_os(POST_ARGS);
93 static	void	 post_par(POST_ARGS);
94 static	void	 post_prevpar(POST_ARGS);
95 static	void	 post_root(POST_ARGS);
96 static	void	 post_rs(POST_ARGS);
97 static	void	 post_sh(POST_ARGS);
98 static	void	 post_sh_head(POST_ARGS);
99 static	void	 post_sh_name(POST_ARGS);
100 static	void	 post_sh_see_also(POST_ARGS);
101 static	void	 post_sh_authors(POST_ARGS);
102 static	void	 post_sm(POST_ARGS);
103 static	void	 post_st(POST_ARGS);
104 static	void	 post_std(POST_ARGS);
105 
106 static	v_post mdoc_valids[MDOC_MAX] = {
107 	NULL,		/* Ap */
108 	post_dd,	/* Dd */
109 	post_dt,	/* Dt */
110 	post_os,	/* Os */
111 	post_sh,	/* Sh */
112 	post_ignpar,	/* Ss */
113 	post_par,	/* Pp */
114 	post_display,	/* D1 */
115 	post_display,	/* Dl */
116 	post_display,	/* Bd */
117 	NULL,		/* Ed */
118 	post_bl,	/* Bl */
119 	NULL,		/* El */
120 	post_it,	/* It */
121 	NULL,		/* Ad */
122 	post_an,	/* An */
123 	post_defaults,	/* Ar */
124 	NULL,		/* Cd */
125 	NULL,		/* Cm */
126 	NULL,		/* Dv */
127 	NULL,		/* Er */
128 	NULL,		/* Ev */
129 	post_ex,	/* Ex */
130 	post_fa,	/* Fa */
131 	NULL,		/* Fd */
132 	NULL,		/* Fl */
133 	post_fn,	/* Fn */
134 	NULL,		/* Ft */
135 	NULL,		/* Ic */
136 	NULL,		/* In */
137 	post_defaults,	/* Li */
138 	post_nd,	/* Nd */
139 	post_nm,	/* Nm */
140 	NULL,		/* Op */
141 	post_obsolete,	/* Ot */
142 	post_defaults,	/* Pa */
143 	post_std,	/* Rv */
144 	post_st,	/* St */
145 	NULL,		/* Va */
146 	NULL,		/* Vt */
147 	NULL,		/* Xr */
148 	NULL,		/* %A */
149 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
150 	NULL,		/* %D */
151 	NULL,		/* %I */
152 	NULL,		/* %J */
153 	post_hyph,	/* %N */
154 	post_hyph,	/* %O */
155 	NULL,		/* %P */
156 	post_hyph,	/* %R */
157 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
158 	NULL,		/* %V */
159 	NULL,		/* Ac */
160 	NULL,		/* Ao */
161 	NULL,		/* Aq */
162 	post_at,	/* At */
163 	NULL,		/* Bc */
164 	post_bf,	/* Bf */
165 	NULL,		/* Bo */
166 	NULL,		/* Bq */
167 	NULL,		/* Bsx */
168 	post_bx,	/* Bx */
169 	post_obsolete,	/* Db */
170 	NULL,		/* Dc */
171 	NULL,		/* Do */
172 	NULL,		/* Dq */
173 	NULL,		/* Ec */
174 	NULL,		/* Ef */
175 	NULL,		/* Em */
176 	NULL,		/* Eo */
177 	NULL,		/* Fx */
178 	NULL,		/* Ms */
179 	NULL,		/* No */
180 	post_ns,	/* Ns */
181 	NULL,		/* Nx */
182 	NULL,		/* Ox */
183 	NULL,		/* Pc */
184 	NULL,		/* Pf */
185 	NULL,		/* Po */
186 	NULL,		/* Pq */
187 	NULL,		/* Qc */
188 	NULL,		/* Ql */
189 	NULL,		/* Qo */
190 	NULL,		/* Qq */
191 	NULL,		/* Re */
192 	post_rs,	/* Rs */
193 	NULL,		/* Sc */
194 	NULL,		/* So */
195 	NULL,		/* Sq */
196 	post_sm,	/* Sm */
197 	post_hyph,	/* Sx */
198 	NULL,		/* Sy */
199 	NULL,		/* Tn */
200 	NULL,		/* Ux */
201 	NULL,		/* Xc */
202 	NULL,		/* Xo */
203 	post_fo,	/* Fo */
204 	NULL,		/* Fc */
205 	NULL,		/* Oo */
206 	NULL,		/* Oc */
207 	post_bk,	/* Bk */
208 	NULL,		/* Ek */
209 	post_eoln,	/* Bt */
210 	NULL,		/* Hf */
211 	post_obsolete,	/* Fr */
212 	post_eoln,	/* Ud */
213 	post_lb,	/* Lb */
214 	post_par,	/* Lp */
215 	NULL,		/* Lk */
216 	post_defaults,	/* Mt */
217 	NULL,		/* Brq */
218 	NULL,		/* Bro */
219 	NULL,		/* Brc */
220 	NULL,		/* %C */
221 	post_es,	/* Es */
222 	post_en,	/* En */
223 	NULL,		/* Dx */
224 	NULL,		/* %Q */
225 	post_par,	/* br */
226 	post_par,	/* sp */
227 	NULL,		/* %U */
228 	NULL,		/* Ta */
229 	NULL,		/* ll */
230 };
231 
232 #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
233 
234 static	const int rsord[RSORD_MAX] = {
235 	MDOC__A,
236 	MDOC__T,
237 	MDOC__B,
238 	MDOC__I,
239 	MDOC__J,
240 	MDOC__R,
241 	MDOC__N,
242 	MDOC__V,
243 	MDOC__U,
244 	MDOC__P,
245 	MDOC__Q,
246 	MDOC__C,
247 	MDOC__D,
248 	MDOC__O
249 };
250 
251 static	const char * const secnames[SEC__MAX] = {
252 	NULL,
253 	"NAME",
254 	"LIBRARY",
255 	"SYNOPSIS",
256 	"DESCRIPTION",
257 	"CONTEXT",
258 	"IMPLEMENTATION NOTES",
259 	"RETURN VALUES",
260 	"ENVIRONMENT",
261 	"FILES",
262 	"EXIT STATUS",
263 	"EXAMPLES",
264 	"DIAGNOSTICS",
265 	"COMPATIBILITY",
266 	"ERRORS",
267 	"SEE ALSO",
268 	"STANDARDS",
269 	"HISTORY",
270 	"AUTHORS",
271 	"CAVEATS",
272 	"BUGS",
273 	"SECURITY CONSIDERATIONS",
274 	NULL
275 };
276 
277 
278 void
279 mdoc_node_validate(struct roff_man *mdoc)
280 {
281 	struct roff_node *n;
282 	v_post *p;
283 
284 	n = mdoc->last;
285 	mdoc->last = mdoc->last->child;
286 	while (mdoc->last != NULL) {
287 		mdoc_node_validate(mdoc);
288 		if (mdoc->last == n)
289 			mdoc->last = mdoc->last->child;
290 		else
291 			mdoc->last = mdoc->last->next;
292 	}
293 
294 	mdoc->last = n;
295 	mdoc->next = ROFF_NEXT_SIBLING;
296 	switch (n->type) {
297 	case ROFFT_TEXT:
298 		if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
299 			check_text(mdoc, n->line, n->pos, n->string);
300 		break;
301 	case ROFFT_EQN:
302 	case ROFFT_TBL:
303 		break;
304 	case ROFFT_ROOT:
305 		post_root(mdoc);
306 		break;
307 	default:
308 		check_args(mdoc, mdoc->last);
309 
310 		/*
311 		 * Closing delimiters are not special at the
312 		 * beginning of a block, opening delimiters
313 		 * are not special at the end.
314 		 */
315 
316 		if (n->child != NULL)
317 			n->child->flags &= ~MDOC_DELIMC;
318 		if (n->last != NULL)
319 			n->last->flags &= ~MDOC_DELIMO;
320 
321 		/* Call the macro's postprocessor. */
322 
323 		p = mdoc_valids + n->tok;
324 		if (*p)
325 			(*p)(mdoc);
326 		if (mdoc->last == n)
327 			mdoc_state(mdoc, n);
328 		break;
329 	}
330 }
331 
332 static void
333 check_args(struct roff_man *mdoc, struct roff_node *n)
334 {
335 	int		 i;
336 
337 	if (NULL == n->args)
338 		return;
339 
340 	assert(n->args->argc);
341 	for (i = 0; i < (int)n->args->argc; i++)
342 		check_argv(mdoc, n, &n->args->argv[i]);
343 }
344 
345 static void
346 check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
347 {
348 	int		 i;
349 
350 	for (i = 0; i < (int)v->sz; i++)
351 		check_text(mdoc, v->line, v->pos, v->value[i]);
352 }
353 
354 static void
355 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
356 {
357 	char		*cp;
358 
359 	if (MDOC_LITERAL & mdoc->flags)
360 		return;
361 
362 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
363 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
364 		    ln, pos + (int)(p - cp), NULL);
365 }
366 
367 static void
368 post_bl_norm(POST_ARGS)
369 {
370 	struct roff_node *n;
371 	struct mdoc_argv *argv, *wa;
372 	int		  i;
373 	enum mdocargt	  mdoclt;
374 	enum mdoc_list	  lt;
375 
376 	n = mdoc->last->parent;
377 	n->norm->Bl.type = LIST__NONE;
378 
379 	/*
380 	 * First figure out which kind of list to use: bind ourselves to
381 	 * the first mentioned list type and warn about any remaining
382 	 * ones.  If we find no list type, we default to LIST_item.
383 	 */
384 
385 	wa = (n->args == NULL) ? NULL : n->args->argv;
386 	mdoclt = MDOC_ARG_MAX;
387 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
388 		argv = n->args->argv + i;
389 		lt = LIST__NONE;
390 		switch (argv->arg) {
391 		/* Set list types. */
392 		case MDOC_Bullet:
393 			lt = LIST_bullet;
394 			break;
395 		case MDOC_Dash:
396 			lt = LIST_dash;
397 			break;
398 		case MDOC_Enum:
399 			lt = LIST_enum;
400 			break;
401 		case MDOC_Hyphen:
402 			lt = LIST_hyphen;
403 			break;
404 		case MDOC_Item:
405 			lt = LIST_item;
406 			break;
407 		case MDOC_Tag:
408 			lt = LIST_tag;
409 			break;
410 		case MDOC_Diag:
411 			lt = LIST_diag;
412 			break;
413 		case MDOC_Hang:
414 			lt = LIST_hang;
415 			break;
416 		case MDOC_Ohang:
417 			lt = LIST_ohang;
418 			break;
419 		case MDOC_Inset:
420 			lt = LIST_inset;
421 			break;
422 		case MDOC_Column:
423 			lt = LIST_column;
424 			break;
425 		/* Set list arguments. */
426 		case MDOC_Compact:
427 			if (n->norm->Bl.comp)
428 				mandoc_msg(MANDOCERR_ARG_REP,
429 				    mdoc->parse, argv->line,
430 				    argv->pos, "Bl -compact");
431 			n->norm->Bl.comp = 1;
432 			break;
433 		case MDOC_Width:
434 			wa = argv;
435 			if (0 == argv->sz) {
436 				mandoc_msg(MANDOCERR_ARG_EMPTY,
437 				    mdoc->parse, argv->line,
438 				    argv->pos, "Bl -width");
439 				n->norm->Bl.width = "0n";
440 				break;
441 			}
442 			if (NULL != n->norm->Bl.width)
443 				mandoc_vmsg(MANDOCERR_ARG_REP,
444 				    mdoc->parse, argv->line,
445 				    argv->pos, "Bl -width %s",
446 				    argv->value[0]);
447 			rewrite_macro2len(argv->value);
448 			n->norm->Bl.width = argv->value[0];
449 			break;
450 		case MDOC_Offset:
451 			if (0 == argv->sz) {
452 				mandoc_msg(MANDOCERR_ARG_EMPTY,
453 				    mdoc->parse, argv->line,
454 				    argv->pos, "Bl -offset");
455 				break;
456 			}
457 			if (NULL != n->norm->Bl.offs)
458 				mandoc_vmsg(MANDOCERR_ARG_REP,
459 				    mdoc->parse, argv->line,
460 				    argv->pos, "Bl -offset %s",
461 				    argv->value[0]);
462 			rewrite_macro2len(argv->value);
463 			n->norm->Bl.offs = argv->value[0];
464 			break;
465 		default:
466 			continue;
467 		}
468 		if (LIST__NONE == lt)
469 			continue;
470 		mdoclt = argv->arg;
471 
472 		/* Check: multiple list types. */
473 
474 		if (LIST__NONE != n->norm->Bl.type) {
475 			mandoc_vmsg(MANDOCERR_BL_REP,
476 			    mdoc->parse, n->line, n->pos,
477 			    "Bl -%s", mdoc_argnames[argv->arg]);
478 			continue;
479 		}
480 
481 		/* The list type should come first. */
482 
483 		if (n->norm->Bl.width ||
484 		    n->norm->Bl.offs ||
485 		    n->norm->Bl.comp)
486 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
487 			    mdoc->parse, n->line, n->pos, "Bl -%s",
488 			    mdoc_argnames[n->args->argv[0].arg]);
489 
490 		n->norm->Bl.type = lt;
491 		if (LIST_column == lt) {
492 			n->norm->Bl.ncols = argv->sz;
493 			n->norm->Bl.cols = (void *)argv->value;
494 		}
495 	}
496 
497 	/* Allow lists to default to LIST_item. */
498 
499 	if (LIST__NONE == n->norm->Bl.type) {
500 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
501 		    n->line, n->pos, "Bl");
502 		n->norm->Bl.type = LIST_item;
503 	}
504 
505 	/*
506 	 * Validate the width field.  Some list types don't need width
507 	 * types and should be warned about them.  Others should have it
508 	 * and must also be warned.  Yet others have a default and need
509 	 * no warning.
510 	 */
511 
512 	switch (n->norm->Bl.type) {
513 	case LIST_tag:
514 		if (NULL == n->norm->Bl.width)
515 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
516 			    n->line, n->pos, "Bl -tag");
517 		break;
518 	case LIST_column:
519 	case LIST_diag:
520 	case LIST_ohang:
521 	case LIST_inset:
522 	case LIST_item:
523 		if (n->norm->Bl.width)
524 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
525 			    wa->line, wa->pos, "Bl -%s",
526 			    mdoc_argnames[mdoclt]);
527 		break;
528 	case LIST_bullet:
529 	case LIST_dash:
530 	case LIST_hyphen:
531 		if (NULL == n->norm->Bl.width)
532 			n->norm->Bl.width = "2n";
533 		break;
534 	case LIST_enum:
535 		if (NULL == n->norm->Bl.width)
536 			n->norm->Bl.width = "3n";
537 		break;
538 	default:
539 		break;
540 	}
541 }
542 
543 static void
544 post_bd(POST_ARGS)
545 {
546 	struct roff_node *n;
547 	struct mdoc_argv *argv;
548 	int		  i;
549 	enum mdoc_disp	  dt;
550 
551 	n = mdoc->last;
552 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
553 		argv = n->args->argv + i;
554 		dt = DISP__NONE;
555 
556 		switch (argv->arg) {
557 		case MDOC_Centred:
558 			dt = DISP_centered;
559 			break;
560 		case MDOC_Ragged:
561 			dt = DISP_ragged;
562 			break;
563 		case MDOC_Unfilled:
564 			dt = DISP_unfilled;
565 			break;
566 		case MDOC_Filled:
567 			dt = DISP_filled;
568 			break;
569 		case MDOC_Literal:
570 			dt = DISP_literal;
571 			break;
572 		case MDOC_File:
573 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
574 			    n->line, n->pos, NULL);
575 			break;
576 		case MDOC_Offset:
577 			if (0 == argv->sz) {
578 				mandoc_msg(MANDOCERR_ARG_EMPTY,
579 				    mdoc->parse, argv->line,
580 				    argv->pos, "Bd -offset");
581 				break;
582 			}
583 			if (NULL != n->norm->Bd.offs)
584 				mandoc_vmsg(MANDOCERR_ARG_REP,
585 				    mdoc->parse, argv->line,
586 				    argv->pos, "Bd -offset %s",
587 				    argv->value[0]);
588 			rewrite_macro2len(argv->value);
589 			n->norm->Bd.offs = argv->value[0];
590 			break;
591 		case MDOC_Compact:
592 			if (n->norm->Bd.comp)
593 				mandoc_msg(MANDOCERR_ARG_REP,
594 				    mdoc->parse, argv->line,
595 				    argv->pos, "Bd -compact");
596 			n->norm->Bd.comp = 1;
597 			break;
598 		default:
599 			abort();
600 		}
601 		if (DISP__NONE == dt)
602 			continue;
603 
604 		if (DISP__NONE == n->norm->Bd.type)
605 			n->norm->Bd.type = dt;
606 		else
607 			mandoc_vmsg(MANDOCERR_BD_REP,
608 			    mdoc->parse, n->line, n->pos,
609 			    "Bd -%s", mdoc_argnames[argv->arg]);
610 	}
611 
612 	if (DISP__NONE == n->norm->Bd.type) {
613 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
614 		    n->line, n->pos, "Bd");
615 		n->norm->Bd.type = DISP_ragged;
616 	}
617 }
618 
619 static void
620 post_an_norm(POST_ARGS)
621 {
622 	struct roff_node *n;
623 	struct mdoc_argv *argv;
624 	size_t	 i;
625 
626 	n = mdoc->last;
627 	if (n->args == NULL)
628 		return;
629 
630 	for (i = 1; i < n->args->argc; i++) {
631 		argv = n->args->argv + i;
632 		mandoc_vmsg(MANDOCERR_AN_REP,
633 		    mdoc->parse, argv->line, argv->pos,
634 		    "An -%s", mdoc_argnames[argv->arg]);
635 	}
636 
637 	argv = n->args->argv;
638 	if (argv->arg == MDOC_Split)
639 		n->norm->An.auth = AUTH_split;
640 	else if (argv->arg == MDOC_Nosplit)
641 		n->norm->An.auth = AUTH_nosplit;
642 	else
643 		abort();
644 }
645 
646 static void
647 post_std(POST_ARGS)
648 {
649 	struct roff_node *n;
650 
651 	n = mdoc->last;
652 	if (n->args && n->args->argc == 1)
653 		if (n->args->argv[0].arg == MDOC_Std)
654 			return;
655 
656 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
657 	    n->line, n->pos, mdoc_macronames[n->tok]);
658 }
659 
660 static void
661 post_obsolete(POST_ARGS)
662 {
663 	struct roff_node *n;
664 
665 	n = mdoc->last;
666 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
667 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
668 		    n->line, n->pos, mdoc_macronames[n->tok]);
669 }
670 
671 static void
672 post_bf(POST_ARGS)
673 {
674 	struct roff_node *np, *nch;
675 
676 	/*
677 	 * Unlike other data pointers, these are "housed" by the HEAD
678 	 * element, which contains the goods.
679 	 */
680 
681 	np = mdoc->last;
682 	if (np->type != ROFFT_HEAD)
683 		return;
684 
685 	assert(np->parent->type == ROFFT_BLOCK);
686 	assert(np->parent->tok == MDOC_Bf);
687 
688 	/* Check the number of arguments. */
689 
690 	nch = np->child;
691 	if (np->parent->args == NULL) {
692 		if (nch == NULL) {
693 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
694 			    np->line, np->pos, "Bf");
695 			return;
696 		}
697 		nch = nch->next;
698 	}
699 	if (nch != NULL)
700 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
701 		    nch->line, nch->pos, "Bf ... %s", nch->string);
702 
703 	/* Extract argument into data. */
704 
705 	if (np->parent->args != NULL) {
706 		switch (np->parent->args->argv[0].arg) {
707 		case MDOC_Emphasis:
708 			np->norm->Bf.font = FONT_Em;
709 			break;
710 		case MDOC_Literal:
711 			np->norm->Bf.font = FONT_Li;
712 			break;
713 		case MDOC_Symbolic:
714 			np->norm->Bf.font = FONT_Sy;
715 			break;
716 		default:
717 			abort();
718 		}
719 		return;
720 	}
721 
722 	/* Extract parameter into data. */
723 
724 	if ( ! strcmp(np->child->string, "Em"))
725 		np->norm->Bf.font = FONT_Em;
726 	else if ( ! strcmp(np->child->string, "Li"))
727 		np->norm->Bf.font = FONT_Li;
728 	else if ( ! strcmp(np->child->string, "Sy"))
729 		np->norm->Bf.font = FONT_Sy;
730 	else
731 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
732 		    np->child->line, np->child->pos,
733 		    "Bf %s", np->child->string);
734 }
735 
736 static void
737 post_lb(POST_ARGS)
738 {
739 	struct roff_node	*n;
740 	char			*libname;
741 
742 	n = mdoc->last->child;
743 	assert(n->type == ROFFT_TEXT);
744 	mandoc_asprintf(&libname, "library \\(Lq%s\\(Rq", n->string);
745 	free(n->string);
746 	n->string = libname;
747 }
748 
749 static void
750 post_eoln(POST_ARGS)
751 {
752 	const struct roff_node *n;
753 
754 	n = mdoc->last;
755 	if (n->child != NULL)
756 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
757 		    mdoc->parse, n->line, n->pos,
758 		    "%s %s", mdoc_macronames[n->tok],
759 		    n->child->string);
760 }
761 
762 static void
763 post_fname(POST_ARGS)
764 {
765 	const struct roff_node	*n;
766 	const char		*cp;
767 	size_t			 pos;
768 
769 	n = mdoc->last->child;
770 	pos = strcspn(n->string, "()");
771 	cp = n->string + pos;
772 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
773 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
774 		    n->line, n->pos + pos, n->string);
775 }
776 
777 static void
778 post_fn(POST_ARGS)
779 {
780 
781 	post_fname(mdoc);
782 	post_fa(mdoc);
783 }
784 
785 static void
786 post_fo(POST_ARGS)
787 {
788 	const struct roff_node	*n;
789 
790 	n = mdoc->last;
791 
792 	if (n->type != ROFFT_HEAD)
793 		return;
794 
795 	if (n->child == NULL) {
796 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
797 		    n->line, n->pos, "Fo");
798 		return;
799 	}
800 	if (n->child != n->last) {
801 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
802 		    n->child->next->line, n->child->next->pos,
803 		    "Fo ... %s", n->child->next->string);
804 		while (n->child != n->last)
805 			roff_node_delete(mdoc, n->last);
806 	}
807 
808 	post_fname(mdoc);
809 }
810 
811 static void
812 post_fa(POST_ARGS)
813 {
814 	const struct roff_node *n;
815 	const char *cp;
816 
817 	for (n = mdoc->last->child; n != NULL; n = n->next) {
818 		for (cp = n->string; *cp != '\0'; cp++) {
819 			/* Ignore callbacks and alterations. */
820 			if (*cp == '(' || *cp == '{')
821 				break;
822 			if (*cp != ',')
823 				continue;
824 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
825 			    n->line, n->pos + (cp - n->string),
826 			    n->string);
827 			break;
828 		}
829 	}
830 }
831 
832 static void
833 post_nm(POST_ARGS)
834 {
835 	struct roff_node	*n;
836 
837 	n = mdoc->last;
838 
839 	if (n->last != NULL &&
840 	    (n->last->tok == MDOC_Pp ||
841 	     n->last->tok == MDOC_Lp))
842 		mdoc_node_relink(mdoc, n->last);
843 
844 	if (mdoc->meta.name != NULL)
845 		return;
846 
847 	deroff(&mdoc->meta.name, n);
848 
849 	if (mdoc->meta.name == NULL)
850 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
851 		    n->line, n->pos, "Nm");
852 }
853 
854 static void
855 post_nd(POST_ARGS)
856 {
857 	struct roff_node	*n;
858 
859 	n = mdoc->last;
860 
861 	if (n->type != ROFFT_BODY)
862 		return;
863 
864 	if (n->child == NULL)
865 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
866 		    n->line, n->pos, "Nd");
867 
868 	post_hyph(mdoc);
869 }
870 
871 static void
872 post_display(POST_ARGS)
873 {
874 	struct roff_node *n, *np;
875 
876 	n = mdoc->last;
877 	switch (n->type) {
878 	case ROFFT_BODY:
879 		if (n->end != ENDBODY_NOT)
880 			break;
881 		if (n->child == NULL)
882 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
883 			    n->line, n->pos, mdoc_macronames[n->tok]);
884 		else if (n->tok == MDOC_D1)
885 			post_hyph(mdoc);
886 		break;
887 	case ROFFT_BLOCK:
888 		if (n->tok == MDOC_Bd) {
889 			if (n->args == NULL) {
890 				mandoc_msg(MANDOCERR_BD_NOARG,
891 				    mdoc->parse, n->line, n->pos, "Bd");
892 				mdoc->next = ROFF_NEXT_SIBLING;
893 				while (n->body->child != NULL)
894 					mdoc_node_relink(mdoc,
895 					    n->body->child);
896 				roff_node_delete(mdoc, n);
897 				break;
898 			}
899 			post_bd(mdoc);
900 			post_prevpar(mdoc);
901 		}
902 		for (np = n->parent; np != NULL; np = np->parent) {
903 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
904 				mandoc_vmsg(MANDOCERR_BD_NEST,
905 				    mdoc->parse, n->line, n->pos,
906 				    "%s in Bd", mdoc_macronames[n->tok]);
907 				break;
908 			}
909 		}
910 		break;
911 	default:
912 		break;
913 	}
914 }
915 
916 static void
917 post_defaults(POST_ARGS)
918 {
919 	struct roff_node *nn;
920 
921 	/*
922 	 * The `Ar' defaults to "file ..." if no value is provided as an
923 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
924 	 * gets an empty string.
925 	 */
926 
927 	if (mdoc->last->child != NULL)
928 		return;
929 
930 	nn = mdoc->last;
931 
932 	switch (nn->tok) {
933 	case MDOC_Ar:
934 		mdoc->next = ROFF_NEXT_CHILD;
935 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
936 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
937 		break;
938 	case MDOC_Pa:
939 	case MDOC_Mt:
940 		mdoc->next = ROFF_NEXT_CHILD;
941 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
942 		break;
943 	default:
944 		abort();
945 	}
946 	mdoc->last = nn;
947 }
948 
949 static void
950 post_at(POST_ARGS)
951 {
952 	struct roff_node	*n;
953 	const char		*std_att;
954 	char			*att;
955 
956 	n = mdoc->last;
957 	if (n->child == NULL) {
958 		mdoc->next = ROFF_NEXT_CHILD;
959 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
960 		mdoc->last = n;
961 		return;
962 	}
963 
964 	/*
965 	 * If we have a child, look it up in the standard keys.  If a
966 	 * key exist, use that instead of the child; if it doesn't,
967 	 * prefix "AT&T UNIX " to the existing data.
968 	 */
969 
970 	n = n->child;
971 	assert(n->type == ROFFT_TEXT);
972 	if ((std_att = mdoc_a2att(n->string)) == NULL) {
973 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
974 		    n->line, n->pos, "At %s", n->string);
975 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
976 	} else
977 		att = mandoc_strdup(std_att);
978 
979 	free(n->string);
980 	n->string = att;
981 }
982 
983 static void
984 post_an(POST_ARGS)
985 {
986 	struct roff_node *np, *nch;
987 
988 	post_an_norm(mdoc);
989 
990 	np = mdoc->last;
991 	nch = np->child;
992 	if (np->norm->An.auth == AUTH__NONE) {
993 		if (nch == NULL)
994 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
995 			    np->line, np->pos, "An");
996 	} else if (nch != NULL)
997 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
998 		    nch->line, nch->pos, "An ... %s", nch->string);
999 }
1000 
1001 static void
1002 post_en(POST_ARGS)
1003 {
1004 
1005 	post_obsolete(mdoc);
1006 	if (mdoc->last->type == ROFFT_BLOCK)
1007 		mdoc->last->norm->Es = mdoc->last_es;
1008 }
1009 
1010 static void
1011 post_es(POST_ARGS)
1012 {
1013 
1014 	post_obsolete(mdoc);
1015 	mdoc->last_es = mdoc->last;
1016 }
1017 
1018 static void
1019 post_it(POST_ARGS)
1020 {
1021 	struct roff_node *nbl, *nit, *nch;
1022 	int		  i, cols;
1023 	enum mdoc_list	  lt;
1024 
1025 	post_prevpar(mdoc);
1026 
1027 	nit = mdoc->last;
1028 	if (nit->type != ROFFT_BLOCK)
1029 		return;
1030 
1031 	nbl = nit->parent->parent;
1032 	lt = nbl->norm->Bl.type;
1033 
1034 	switch (lt) {
1035 	case LIST_tag:
1036 	case LIST_hang:
1037 	case LIST_ohang:
1038 	case LIST_inset:
1039 	case LIST_diag:
1040 		if (nit->head->child == NULL)
1041 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1042 			    mdoc->parse, nit->line, nit->pos,
1043 			    "Bl -%s It",
1044 			    mdoc_argnames[nbl->args->argv[0].arg]);
1045 		break;
1046 	case LIST_bullet:
1047 	case LIST_dash:
1048 	case LIST_enum:
1049 	case LIST_hyphen:
1050 		if (nit->body == NULL || nit->body->child == NULL)
1051 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1052 			    mdoc->parse, nit->line, nit->pos,
1053 			    "Bl -%s It",
1054 			    mdoc_argnames[nbl->args->argv[0].arg]);
1055 		/* FALLTHROUGH */
1056 	case LIST_item:
1057 		if (nit->head->child != NULL)
1058 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1059 			    mdoc->parse, nit->line, nit->pos,
1060 			    "It %s", nit->head->child->string);
1061 		break;
1062 	case LIST_column:
1063 		cols = (int)nbl->norm->Bl.ncols;
1064 
1065 		assert(nit->head->child == NULL);
1066 
1067 		i = 0;
1068 		for (nch = nit->child; nch != NULL; nch = nch->next)
1069 			if (nch->type == ROFFT_BODY)
1070 				i++;
1071 
1072 		if (i < cols || i > cols + 1)
1073 			mandoc_vmsg(MANDOCERR_BL_COL,
1074 			    mdoc->parse, nit->line, nit->pos,
1075 			    "%d columns, %d cells", cols, i);
1076 		break;
1077 	default:
1078 		abort();
1079 	}
1080 }
1081 
1082 static void
1083 post_bl_block(POST_ARGS)
1084 {
1085 	struct roff_node *n, *ni, *nc;
1086 
1087 	post_prevpar(mdoc);
1088 
1089 	/*
1090 	 * These are fairly complicated, so we've broken them into two
1091 	 * functions.  post_bl_block_tag() is called when a -tag is
1092 	 * specified, but no -width (it must be guessed).  The second
1093 	 * when a -width is specified (macro indicators must be
1094 	 * rewritten into real lengths).
1095 	 */
1096 
1097 	n = mdoc->last;
1098 
1099 	if (n->norm->Bl.type == LIST_tag &&
1100 	    n->norm->Bl.width == NULL) {
1101 		post_bl_block_tag(mdoc);
1102 		assert(n->norm->Bl.width != NULL);
1103 	}
1104 
1105 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1106 		if (ni->body == NULL)
1107 			continue;
1108 		nc = ni->body->last;
1109 		while (nc != NULL) {
1110 			switch (nc->tok) {
1111 			case MDOC_Pp:
1112 			case MDOC_Lp:
1113 			case MDOC_br:
1114 				break;
1115 			default:
1116 				nc = NULL;
1117 				continue;
1118 			}
1119 			if (ni->next == NULL) {
1120 				mandoc_msg(MANDOCERR_PAR_MOVE,
1121 				    mdoc->parse, nc->line, nc->pos,
1122 				    mdoc_macronames[nc->tok]);
1123 				mdoc_node_relink(mdoc, nc);
1124 			} else if (n->norm->Bl.comp == 0 &&
1125 			    n->norm->Bl.type != LIST_column) {
1126 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1127 				    mdoc->parse, nc->line, nc->pos,
1128 				    "%s before It",
1129 				    mdoc_macronames[nc->tok]);
1130 				roff_node_delete(mdoc, nc);
1131 			} else
1132 				break;
1133 			nc = ni->body->last;
1134 		}
1135 	}
1136 }
1137 
1138 /*
1139  * If the argument of -offset or -width is a macro,
1140  * replace it with the associated default width.
1141  */
1142 void
1143 rewrite_macro2len(char **arg)
1144 {
1145 	size_t		  width;
1146 	int		  tok;
1147 
1148 	if (*arg == NULL)
1149 		return;
1150 	else if ( ! strcmp(*arg, "Ds"))
1151 		width = 6;
1152 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1153 		return;
1154 	else
1155 		width = macro2len(tok);
1156 
1157 	free(*arg);
1158 	mandoc_asprintf(arg, "%zun", width);
1159 }
1160 
1161 static void
1162 post_bl_block_tag(POST_ARGS)
1163 {
1164 	struct roff_node *n, *nn;
1165 	size_t		  sz, ssz;
1166 	int		  i;
1167 	char		  buf[24];
1168 
1169 	/*
1170 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1171 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1172 	 * ONLY if the -width argument has NOT been provided.  See
1173 	 * rewrite_macro2len() for converting the -width string.
1174 	 */
1175 
1176 	sz = 10;
1177 	n = mdoc->last;
1178 
1179 	for (nn = n->body->child; nn != NULL; nn = nn->next) {
1180 		if (nn->tok != MDOC_It)
1181 			continue;
1182 
1183 		assert(nn->type == ROFFT_BLOCK);
1184 		nn = nn->head->child;
1185 
1186 		if (nn == NULL)
1187 			break;
1188 
1189 		if (nn->type == ROFFT_TEXT) {
1190 			sz = strlen(nn->string) + 1;
1191 			break;
1192 		}
1193 
1194 		if (0 != (ssz = macro2len(nn->tok)))
1195 			sz = ssz;
1196 
1197 		break;
1198 	}
1199 
1200 	/* Defaults to ten ens. */
1201 
1202 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1203 
1204 	/*
1205 	 * We have to dynamically add this to the macro's argument list.
1206 	 * We're guaranteed that a MDOC_Width doesn't already exist.
1207 	 */
1208 
1209 	assert(n->args != NULL);
1210 	i = (int)(n->args->argc)++;
1211 
1212 	n->args->argv = mandoc_reallocarray(n->args->argv,
1213 	    n->args->argc, sizeof(struct mdoc_argv));
1214 
1215 	n->args->argv[i].arg = MDOC_Width;
1216 	n->args->argv[i].line = n->line;
1217 	n->args->argv[i].pos = n->pos;
1218 	n->args->argv[i].sz = 1;
1219 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1220 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1221 
1222 	/* Set our width! */
1223 	n->norm->Bl.width = n->args->argv[i].value[0];
1224 }
1225 
1226 static void
1227 post_bl_head(POST_ARGS)
1228 {
1229 	struct roff_node *nbl, *nh, *nch, *nnext;
1230 	struct mdoc_argv *argv;
1231 	int		  i, j;
1232 
1233 	post_bl_norm(mdoc);
1234 
1235 	nh = mdoc->last;
1236 	if (nh->norm->Bl.type != LIST_column) {
1237 		if ((nch = nh->child) == NULL)
1238 			return;
1239 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1240 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1241 		while (nch != NULL) {
1242 			roff_node_delete(mdoc, nch);
1243 			nch = nh->child;
1244 		}
1245 		return;
1246 	}
1247 
1248 	/*
1249 	 * Append old-style lists, where the column width specifiers
1250 	 * trail as macro parameters, to the new-style ("normal-form")
1251 	 * lists where they're argument values following -column.
1252 	 */
1253 
1254 	if (nh->child == NULL)
1255 		return;
1256 
1257 	nbl = nh->parent;
1258 	for (j = 0; j < (int)nbl->args->argc; j++)
1259 		if (nbl->args->argv[j].arg == MDOC_Column)
1260 			break;
1261 
1262 	assert(j < (int)nbl->args->argc);
1263 
1264 	/*
1265 	 * Accommodate for new-style groff column syntax.  Shuffle the
1266 	 * child nodes, all of which must be TEXT, as arguments for the
1267 	 * column field.  Then, delete the head children.
1268 	 */
1269 
1270 	argv = nbl->args->argv + j;
1271 	i = argv->sz;
1272 	for (nch = nh->child; nch != NULL; nch = nch->next)
1273 		argv->sz++;
1274 	argv->value = mandoc_reallocarray(argv->value,
1275 	    argv->sz, sizeof(char *));
1276 
1277 	nh->norm->Bl.ncols = argv->sz;
1278 	nh->norm->Bl.cols = (void *)argv->value;
1279 
1280 	for (nch = nh->child; nch != NULL; nch = nnext) {
1281 		argv->value[i++] = nch->string;
1282 		nch->string = NULL;
1283 		nnext = nch->next;
1284 		roff_node_delete(NULL, nch);
1285 	}
1286 	nh->child = NULL;
1287 }
1288 
1289 static void
1290 post_bl(POST_ARGS)
1291 {
1292 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1293 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1294 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1295 
1296 	nbody = mdoc->last;
1297 	switch (nbody->type) {
1298 	case ROFFT_BLOCK:
1299 		post_bl_block(mdoc);
1300 		return;
1301 	case ROFFT_HEAD:
1302 		post_bl_head(mdoc);
1303 		return;
1304 	case ROFFT_BODY:
1305 		break;
1306 	default:
1307 		return;
1308 	}
1309 	if (nbody->end != ENDBODY_NOT)
1310 		return;
1311 
1312 	nchild = nbody->child;
1313 	if (nchild == NULL) {
1314 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1315 		    nbody->line, nbody->pos, "Bl");
1316 		return;
1317 	}
1318 	while (nchild != NULL) {
1319 		if (nchild->tok == MDOC_It ||
1320 		    (nchild->tok == MDOC_Sm &&
1321 		     nchild->next != NULL &&
1322 		     nchild->next->tok == MDOC_It)) {
1323 			nchild = nchild->next;
1324 			continue;
1325 		}
1326 
1327 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1328 		    nchild->line, nchild->pos,
1329 		    mdoc_macronames[nchild->tok]);
1330 
1331 		/*
1332 		 * Move the node out of the Bl block.
1333 		 * First, collect all required node pointers.
1334 		 */
1335 
1336 		nblock  = nbody->parent;
1337 		nprev   = nblock->prev;
1338 		nparent = nblock->parent;
1339 		nnext   = nchild->next;
1340 
1341 		/*
1342 		 * Unlink this child.
1343 		 */
1344 
1345 		assert(nchild->prev == NULL);
1346 		nbody->child = nnext;
1347 		if (nnext == NULL)
1348 			nbody->last  = NULL;
1349 		else
1350 			nnext->prev = NULL;
1351 
1352 		/*
1353 		 * Relink this child.
1354 		 */
1355 
1356 		nchild->parent = nparent;
1357 		nchild->prev   = nprev;
1358 		nchild->next   = nblock;
1359 
1360 		nblock->prev = nchild;
1361 		if (nprev == NULL)
1362 			nparent->child = nchild;
1363 		else
1364 			nprev->next = nchild;
1365 
1366 		nchild = nnext;
1367 	}
1368 }
1369 
1370 static void
1371 post_bk(POST_ARGS)
1372 {
1373 	struct roff_node	*n;
1374 
1375 	n = mdoc->last;
1376 
1377 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1378 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1379 		    mdoc->parse, n->line, n->pos, "Bk");
1380 		roff_node_delete(mdoc, n);
1381 	}
1382 }
1383 
1384 static void
1385 post_sm(POST_ARGS)
1386 {
1387 	struct roff_node	*nch;
1388 
1389 	nch = mdoc->last->child;
1390 
1391 	if (nch == NULL) {
1392 		mdoc->flags ^= MDOC_SMOFF;
1393 		return;
1394 	}
1395 
1396 	assert(nch->type == ROFFT_TEXT);
1397 
1398 	if ( ! strcmp(nch->string, "on")) {
1399 		mdoc->flags &= ~MDOC_SMOFF;
1400 		return;
1401 	}
1402 	if ( ! strcmp(nch->string, "off")) {
1403 		mdoc->flags |= MDOC_SMOFF;
1404 		return;
1405 	}
1406 
1407 	mandoc_vmsg(MANDOCERR_SM_BAD,
1408 	    mdoc->parse, nch->line, nch->pos,
1409 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1410 	mdoc_node_relink(mdoc, nch);
1411 	return;
1412 }
1413 
1414 static void
1415 post_root(POST_ARGS)
1416 {
1417 	struct roff_node *n;
1418 
1419 	/* Add missing prologue data. */
1420 
1421 	if (mdoc->meta.date == NULL)
1422 		mdoc->meta.date = mdoc->quick ?
1423 		    mandoc_strdup("") :
1424 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
1425 
1426 	if (mdoc->meta.title == NULL) {
1427 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1428 		    mdoc->parse, 0, 0, "EOF");
1429 		mdoc->meta.title = mandoc_strdup("UNTITLED");
1430 	}
1431 
1432 	if (mdoc->meta.vol == NULL)
1433 		mdoc->meta.vol = mandoc_strdup("LOCAL");
1434 
1435 	if (mdoc->meta.os == NULL) {
1436 		mandoc_msg(MANDOCERR_OS_MISSING,
1437 		    mdoc->parse, 0, 0, NULL);
1438 		mdoc->meta.os = mandoc_strdup("");
1439 	}
1440 
1441 	/* Check that we begin with a proper `Sh'. */
1442 
1443 	n = mdoc->first->child;
1444 	while (n != NULL && n->tok != TOKEN_NONE &&
1445 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1446 		n = n->next;
1447 
1448 	if (n == NULL)
1449 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1450 	else if (n->tok != MDOC_Sh)
1451 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1452 		    n->line, n->pos, mdoc_macronames[n->tok]);
1453 }
1454 
1455 static void
1456 post_st(POST_ARGS)
1457 {
1458 	struct roff_node	 *n, *nch;
1459 	const char		 *p;
1460 
1461 	n = mdoc->last;
1462 	nch = n->child;
1463 
1464 	assert(nch->type == ROFFT_TEXT);
1465 
1466 	if ((p = mdoc_a2st(nch->string)) == NULL) {
1467 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1468 		    nch->line, nch->pos, "St %s", nch->string);
1469 		roff_node_delete(mdoc, n);
1470 	} else {
1471 		free(nch->string);
1472 		nch->string = mandoc_strdup(p);
1473 	}
1474 }
1475 
1476 static void
1477 post_rs(POST_ARGS)
1478 {
1479 	struct roff_node *np, *nch, *next, *prev;
1480 	int		  i, j;
1481 
1482 	np = mdoc->last;
1483 
1484 	if (np->type != ROFFT_BODY)
1485 		return;
1486 
1487 	if (np->child == NULL) {
1488 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1489 		    np->line, np->pos, "Rs");
1490 		return;
1491 	}
1492 
1493 	/*
1494 	 * The full `Rs' block needs special handling to order the
1495 	 * sub-elements according to `rsord'.  Pick through each element
1496 	 * and correctly order it.  This is an insertion sort.
1497 	 */
1498 
1499 	next = NULL;
1500 	for (nch = np->child->next; nch != NULL; nch = next) {
1501 		/* Determine order number of this child. */
1502 		for (i = 0; i < RSORD_MAX; i++)
1503 			if (rsord[i] == nch->tok)
1504 				break;
1505 
1506 		if (i == RSORD_MAX) {
1507 			mandoc_msg(MANDOCERR_RS_BAD,
1508 			    mdoc->parse, nch->line, nch->pos,
1509 			    mdoc_macronames[nch->tok]);
1510 			i = -1;
1511 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1512 			np->norm->Rs.quote_T++;
1513 
1514 		/*
1515 		 * Remove this child from the chain.  This somewhat
1516 		 * repeats roff_node_unlink(), but since we're
1517 		 * just re-ordering, there's no need for the
1518 		 * full unlink process.
1519 		 */
1520 
1521 		if ((next = nch->next) != NULL)
1522 			next->prev = nch->prev;
1523 
1524 		if ((prev = nch->prev) != NULL)
1525 			prev->next = nch->next;
1526 
1527 		nch->prev = nch->next = NULL;
1528 
1529 		/*
1530 		 * Scan back until we reach a node that's
1531 		 * to be ordered before this child.
1532 		 */
1533 
1534 		for ( ; prev ; prev = prev->prev) {
1535 			/* Determine order of `prev'. */
1536 			for (j = 0; j < RSORD_MAX; j++)
1537 				if (rsord[j] == prev->tok)
1538 					break;
1539 			if (j == RSORD_MAX)
1540 				j = -1;
1541 
1542 			if (j <= i)
1543 				break;
1544 		}
1545 
1546 		/*
1547 		 * Set this child back into its correct place
1548 		 * in front of the `prev' node.
1549 		 */
1550 
1551 		nch->prev = prev;
1552 
1553 		if (prev == NULL) {
1554 			np->child->prev = nch;
1555 			nch->next = np->child;
1556 			np->child = nch;
1557 		} else {
1558 			if (prev->next)
1559 				prev->next->prev = nch;
1560 			nch->next = prev->next;
1561 			prev->next = nch;
1562 		}
1563 	}
1564 }
1565 
1566 /*
1567  * For some arguments of some macros,
1568  * convert all breakable hyphens into ASCII_HYPH.
1569  */
1570 static void
1571 post_hyph(POST_ARGS)
1572 {
1573 	struct roff_node	*nch;
1574 	char			*cp;
1575 
1576 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1577 		if (nch->type != ROFFT_TEXT)
1578 			continue;
1579 		cp = nch->string;
1580 		if (*cp == '\0')
1581 			continue;
1582 		while (*(++cp) != '\0')
1583 			if (*cp == '-' &&
1584 			    isalpha((unsigned char)cp[-1]) &&
1585 			    isalpha((unsigned char)cp[1]))
1586 				*cp = ASCII_HYPH;
1587 	}
1588 }
1589 
1590 static void
1591 post_ns(POST_ARGS)
1592 {
1593 
1594 	if (mdoc->last->flags & MDOC_LINE)
1595 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1596 		    mdoc->last->line, mdoc->last->pos, NULL);
1597 }
1598 
1599 static void
1600 post_sh(POST_ARGS)
1601 {
1602 
1603 	post_ignpar(mdoc);
1604 
1605 	switch (mdoc->last->type) {
1606 	case ROFFT_HEAD:
1607 		post_sh_head(mdoc);
1608 		break;
1609 	case ROFFT_BODY:
1610 		switch (mdoc->lastsec)  {
1611 		case SEC_NAME:
1612 			post_sh_name(mdoc);
1613 			break;
1614 		case SEC_SEE_ALSO:
1615 			post_sh_see_also(mdoc);
1616 			break;
1617 		case SEC_AUTHORS:
1618 			post_sh_authors(mdoc);
1619 			break;
1620 		default:
1621 			break;
1622 		}
1623 		break;
1624 	default:
1625 		break;
1626 	}
1627 }
1628 
1629 static void
1630 post_sh_name(POST_ARGS)
1631 {
1632 	struct roff_node *n;
1633 	int hasnm, hasnd;
1634 
1635 	hasnm = hasnd = 0;
1636 
1637 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1638 		switch (n->tok) {
1639 		case MDOC_Nm:
1640 			hasnm = 1;
1641 			break;
1642 		case MDOC_Nd:
1643 			hasnd = 1;
1644 			if (n->next != NULL)
1645 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1646 				    mdoc->parse, n->line, n->pos, NULL);
1647 			break;
1648 		case TOKEN_NONE:
1649 			if (hasnm)
1650 				break;
1651 			/* FALLTHROUGH */
1652 		default:
1653 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1654 			    n->line, n->pos, mdoc_macronames[n->tok]);
1655 			break;
1656 		}
1657 	}
1658 
1659 	if ( ! hasnm)
1660 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1661 		    mdoc->last->line, mdoc->last->pos, NULL);
1662 	if ( ! hasnd)
1663 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1664 		    mdoc->last->line, mdoc->last->pos, NULL);
1665 }
1666 
1667 static void
1668 post_sh_see_also(POST_ARGS)
1669 {
1670 	const struct roff_node	*n;
1671 	const char		*name, *sec;
1672 	const char		*lastname, *lastsec, *lastpunct;
1673 	int			 cmp;
1674 
1675 	n = mdoc->last->child;
1676 	lastname = lastsec = lastpunct = NULL;
1677 	while (n != NULL) {
1678 		if (n->tok != MDOC_Xr ||
1679 		    n->child == NULL ||
1680 		    n->child->next == NULL)
1681 			break;
1682 
1683 		/* Process one .Xr node. */
1684 
1685 		name = n->child->string;
1686 		sec = n->child->next->string;
1687 		if (lastsec != NULL) {
1688 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1689 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1690 				    mdoc->parse, n->line, n->pos,
1691 				    "%s before %s(%s)", lastpunct,
1692 				    name, sec);
1693 			cmp = strcmp(lastsec, sec);
1694 			if (cmp > 0)
1695 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1696 				    mdoc->parse, n->line, n->pos,
1697 				    "%s(%s) after %s(%s)", name,
1698 				    sec, lastname, lastsec);
1699 			else if (cmp == 0 &&
1700 			    strcasecmp(lastname, name) > 0)
1701 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1702 				    mdoc->parse, n->line, n->pos,
1703 				    "%s after %s", name, lastname);
1704 		}
1705 		lastname = name;
1706 		lastsec = sec;
1707 
1708 		/* Process the following node. */
1709 
1710 		n = n->next;
1711 		if (n == NULL)
1712 			break;
1713 		if (n->tok == MDOC_Xr) {
1714 			lastpunct = "none";
1715 			continue;
1716 		}
1717 		if (n->type != ROFFT_TEXT)
1718 			break;
1719 		for (name = n->string; *name != '\0'; name++)
1720 			if (isalpha((const unsigned char)*name))
1721 				return;
1722 		lastpunct = n->string;
1723 		if (n->next == NULL)
1724 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1725 			    n->line, n->pos, "%s after %s(%s)",
1726 			    lastpunct, lastname, lastsec);
1727 		n = n->next;
1728 	}
1729 }
1730 
1731 static int
1732 child_an(const struct roff_node *n)
1733 {
1734 
1735 	for (n = n->child; n != NULL; n = n->next)
1736 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1737 			return 1;
1738 	return 0;
1739 }
1740 
1741 static void
1742 post_sh_authors(POST_ARGS)
1743 {
1744 
1745 	if ( ! child_an(mdoc->last))
1746 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1747 		    mdoc->last->line, mdoc->last->pos, NULL);
1748 }
1749 
1750 static void
1751 post_sh_head(POST_ARGS)
1752 {
1753 	const char	*goodsec;
1754 	enum roff_sec	 sec;
1755 
1756 	/*
1757 	 * Process a new section.  Sections are either "named" or
1758 	 * "custom".  Custom sections are user-defined, while named ones
1759 	 * follow a conventional order and may only appear in certain
1760 	 * manual sections.
1761 	 */
1762 
1763 	sec = mdoc->last->sec;
1764 
1765 	/* The NAME should be first. */
1766 
1767 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1768 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1769 		    mdoc->last->line, mdoc->last->pos,
1770 		    "Sh %s", secnames[sec]);
1771 
1772 	/* The SYNOPSIS gets special attention in other areas. */
1773 
1774 	if (sec == SEC_SYNOPSIS) {
1775 		roff_setreg(mdoc->roff, "nS", 1, '=');
1776 		mdoc->flags |= MDOC_SYNOPSIS;
1777 	} else {
1778 		roff_setreg(mdoc->roff, "nS", 0, '=');
1779 		mdoc->flags &= ~MDOC_SYNOPSIS;
1780 	}
1781 
1782 	/* Mark our last section. */
1783 
1784 	mdoc->lastsec = sec;
1785 
1786 	/* We don't care about custom sections after this. */
1787 
1788 	if (sec == SEC_CUSTOM)
1789 		return;
1790 
1791 	/*
1792 	 * Check whether our non-custom section is being repeated or is
1793 	 * out of order.
1794 	 */
1795 
1796 	if (sec == mdoc->lastnamed)
1797 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1798 		    mdoc->last->line, mdoc->last->pos,
1799 		    "Sh %s", secnames[sec]);
1800 
1801 	if (sec < mdoc->lastnamed)
1802 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1803 		    mdoc->last->line, mdoc->last->pos,
1804 		    "Sh %s", secnames[sec]);
1805 
1806 	/* Mark the last named section. */
1807 
1808 	mdoc->lastnamed = sec;
1809 
1810 	/* Check particular section/manual conventions. */
1811 
1812 	if (mdoc->meta.msec == NULL)
1813 		return;
1814 
1815 	goodsec = NULL;
1816 	switch (sec) {
1817 	case SEC_ERRORS:
1818 		if (*mdoc->meta.msec == '4')
1819 			break;
1820 		goodsec = "2, 3, 4, 9";
1821 		/* FALLTHROUGH */
1822 	case SEC_RETURN_VALUES:
1823 	case SEC_LIBRARY:
1824 		if (*mdoc->meta.msec == '2')
1825 			break;
1826 		if (*mdoc->meta.msec == '3')
1827 			break;
1828 		if (NULL == goodsec)
1829 			goodsec = "2, 3, 9";
1830 		/* FALLTHROUGH */
1831 	case SEC_CONTEXT:
1832 		if (*mdoc->meta.msec == '9')
1833 			break;
1834 		if (NULL == goodsec)
1835 			goodsec = "9";
1836 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1837 		    mdoc->last->line, mdoc->last->pos,
1838 		    "Sh %s for %s only", secnames[sec], goodsec);
1839 		break;
1840 	default:
1841 		break;
1842 	}
1843 }
1844 
1845 static void
1846 post_ignpar(POST_ARGS)
1847 {
1848 	struct roff_node *np;
1849 
1850 	switch (mdoc->last->type) {
1851 	case ROFFT_HEAD:
1852 		post_hyph(mdoc);
1853 		return;
1854 	case ROFFT_BODY:
1855 		break;
1856 	default:
1857 		return;
1858 	}
1859 
1860 	if ((np = mdoc->last->child) != NULL)
1861 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1862 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
1863 			    mdoc->parse, np->line, np->pos,
1864 			    "%s after %s", mdoc_macronames[np->tok],
1865 			    mdoc_macronames[mdoc->last->tok]);
1866 			roff_node_delete(mdoc, np);
1867 		}
1868 
1869 	if ((np = mdoc->last->last) != NULL)
1870 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1871 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1872 			    np->line, np->pos, "%s at the end of %s",
1873 			    mdoc_macronames[np->tok],
1874 			    mdoc_macronames[mdoc->last->tok]);
1875 			roff_node_delete(mdoc, np);
1876 		}
1877 }
1878 
1879 static void
1880 post_prevpar(POST_ARGS)
1881 {
1882 	struct roff_node *n;
1883 
1884 	n = mdoc->last;
1885 	if (NULL == n->prev)
1886 		return;
1887 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
1888 		return;
1889 
1890 	/*
1891 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1892 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1893 	 */
1894 
1895 	if (n->prev->tok != MDOC_Pp &&
1896 	    n->prev->tok != MDOC_Lp &&
1897 	    n->prev->tok != MDOC_br)
1898 		return;
1899 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
1900 		return;
1901 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
1902 		return;
1903 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
1904 		return;
1905 
1906 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1907 	    n->prev->line, n->prev->pos,
1908 	    "%s before %s", mdoc_macronames[n->prev->tok],
1909 	    mdoc_macronames[n->tok]);
1910 	roff_node_delete(mdoc, n->prev);
1911 }
1912 
1913 static void
1914 post_par(POST_ARGS)
1915 {
1916 	struct roff_node *np;
1917 
1918 	np = mdoc->last;
1919 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
1920 		post_prevpar(mdoc);
1921 
1922 	if (np->tok == MDOC_sp) {
1923 		if (np->child != NULL && np->child->next != NULL)
1924 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1925 			    np->child->next->line, np->child->next->pos,
1926 			    "sp ... %s", np->child->next->string);
1927 	} else if (np->child != NULL)
1928 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
1929 		    mdoc->parse, np->line, np->pos, "%s %s",
1930 		    mdoc_macronames[np->tok], np->child->string);
1931 
1932 	if ((np = mdoc->last->prev) == NULL) {
1933 		np = mdoc->last->parent;
1934 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
1935 			return;
1936 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
1937 	    (mdoc->last->tok != MDOC_br ||
1938 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
1939 		return;
1940 
1941 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1942 	    mdoc->last->line, mdoc->last->pos,
1943 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
1944 	    mdoc_macronames[np->tok]);
1945 	roff_node_delete(mdoc, mdoc->last);
1946 }
1947 
1948 static void
1949 post_dd(POST_ARGS)
1950 {
1951 	struct roff_node *n;
1952 	char		 *datestr;
1953 
1954 	n = mdoc->last;
1955 	if (mdoc->meta.date != NULL) {
1956 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
1957 		    n->line, n->pos, "Dd");
1958 		free(mdoc->meta.date);
1959 	} else if (mdoc->flags & MDOC_PBODY)
1960 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
1961 		    n->line, n->pos, "Dd");
1962 	else if (mdoc->meta.title != NULL)
1963 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
1964 		    n->line, n->pos, "Dd after Dt");
1965 	else if (mdoc->meta.os != NULL)
1966 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
1967 		    n->line, n->pos, "Dd after Os");
1968 
1969 	if (n->child == NULL || n->child->string[0] == '\0') {
1970 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1971 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
1972 		goto out;
1973 	}
1974 
1975 	datestr = NULL;
1976 	deroff(&datestr, n);
1977 	if (mdoc->quick)
1978 		mdoc->meta.date = datestr;
1979 	else {
1980 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
1981 		    datestr, n->line, n->pos);
1982 		free(datestr);
1983 	}
1984 out:
1985 	roff_node_delete(mdoc, n);
1986 }
1987 
1988 static void
1989 post_dt(POST_ARGS)
1990 {
1991 	struct roff_node *nn, *n;
1992 	const char	 *cp;
1993 	char		 *p;
1994 
1995 	n = mdoc->last;
1996 	if (mdoc->flags & MDOC_PBODY) {
1997 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
1998 		    n->line, n->pos, "Dt");
1999 		goto out;
2000 	}
2001 
2002 	if (mdoc->meta.title != NULL)
2003 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2004 		    n->line, n->pos, "Dt");
2005 	else if (mdoc->meta.os != NULL)
2006 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2007 		    n->line, n->pos, "Dt after Os");
2008 
2009 	free(mdoc->meta.title);
2010 	free(mdoc->meta.msec);
2011 	free(mdoc->meta.vol);
2012 	free(mdoc->meta.arch);
2013 
2014 	mdoc->meta.title = NULL;
2015 	mdoc->meta.msec = NULL;
2016 	mdoc->meta.vol = NULL;
2017 	mdoc->meta.arch = NULL;
2018 
2019 	/* Mandatory first argument: title. */
2020 
2021 	nn = n->child;
2022 	if (nn == NULL || *nn->string == '\0') {
2023 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2024 		    mdoc->parse, n->line, n->pos, "Dt");
2025 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2026 	} else {
2027 		mdoc->meta.title = mandoc_strdup(nn->string);
2028 
2029 		/* Check that all characters are uppercase. */
2030 
2031 		for (p = nn->string; *p != '\0'; p++)
2032 			if (islower((unsigned char)*p)) {
2033 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2034 				    mdoc->parse, nn->line,
2035 				    nn->pos + (p - nn->string),
2036 				    "Dt %s", nn->string);
2037 				break;
2038 			}
2039 	}
2040 
2041 	/* Mandatory second argument: section.�*/
2042 
2043 	if (nn != NULL)
2044 		nn = nn->next;
2045 
2046 	if (nn == NULL) {
2047 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2048 		    mdoc->parse, n->line, n->pos,
2049 		    "Dt %s", mdoc->meta.title);
2050 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2051 		goto out;  /* msec and arch remain NULL. */
2052 	}
2053 
2054 	mdoc->meta.msec = mandoc_strdup(nn->string);
2055 
2056 	/* Infer volume title from section number. */
2057 
2058 	cp = mandoc_a2msec(nn->string);
2059 	if (cp == NULL) {
2060 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2061 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2062 		mdoc->meta.vol = mandoc_strdup(nn->string);
2063 	} else
2064 		mdoc->meta.vol = mandoc_strdup(cp);
2065 
2066 	/* Optional third argument: architecture. */
2067 
2068 	if ((nn = nn->next) == NULL)
2069 		goto out;
2070 
2071 	for (p = nn->string; *p != '\0'; p++)
2072 		*p = tolower((unsigned char)*p);
2073 	mdoc->meta.arch = mandoc_strdup(nn->string);
2074 
2075 	/* Ignore fourth and later arguments. */
2076 
2077 	if ((nn = nn->next) != NULL)
2078 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2079 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2080 
2081 out:
2082 	roff_node_delete(mdoc, n);
2083 }
2084 
2085 static void
2086 post_bx(POST_ARGS)
2087 {
2088 	struct roff_node	*n;
2089 
2090 	/*
2091 	 * Make `Bx's second argument always start with an uppercase
2092 	 * letter.  Groff checks if it's an "accepted" term, but we just
2093 	 * uppercase blindly.
2094 	 */
2095 
2096 	if ((n = mdoc->last->child) != NULL && (n = n->next) != NULL)
2097 		*n->string = (char)toupper((unsigned char)*n->string);
2098 }
2099 
2100 static void
2101 post_os(POST_ARGS)
2102 {
2103 #ifndef OSNAME
2104 	struct utsname	  utsname;
2105 	static char	 *defbuf;
2106 #endif
2107 	struct roff_node *n;
2108 
2109 	n = mdoc->last;
2110 	if (mdoc->meta.os != NULL)
2111 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2112 		    n->line, n->pos, "Os");
2113 	else if (mdoc->flags & MDOC_PBODY)
2114 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2115 		    n->line, n->pos, "Os");
2116 
2117 	/*
2118 	 * Set the operating system by way of the `Os' macro.
2119 	 * The order of precedence is:
2120 	 * 1. the argument of the `Os' macro, unless empty
2121 	 * 2. the -Ios=foo command line argument, if provided
2122 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2123 	 * 4. "sysname release" from uname(3)
2124 	 */
2125 
2126 	free(mdoc->meta.os);
2127 	mdoc->meta.os = NULL;
2128 	deroff(&mdoc->meta.os, n);
2129 	if (mdoc->meta.os)
2130 		goto out;
2131 
2132 	if (mdoc->defos) {
2133 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2134 		goto out;
2135 	}
2136 
2137 #ifdef OSNAME
2138 	mdoc->meta.os = mandoc_strdup(OSNAME);
2139 #else /*!OSNAME */
2140 	if (defbuf == NULL) {
2141 		if (uname(&utsname) == -1) {
2142 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2143 			    n->line, n->pos, "Os");
2144 			defbuf = mandoc_strdup("UNKNOWN");
2145 		} else
2146 			mandoc_asprintf(&defbuf, "%s %s",
2147 			    utsname.sysname, utsname.release);
2148 	}
2149 	mdoc->meta.os = mandoc_strdup(defbuf);
2150 #endif /*!OSNAME*/
2151 
2152 out:
2153 	roff_node_delete(mdoc, n);
2154 }
2155 
2156 /*
2157  * If no argument is provided,
2158  * fill in the name of the current manual page.
2159  */
2160 static void
2161 post_ex(POST_ARGS)
2162 {
2163 	struct roff_node *n;
2164 
2165 	post_std(mdoc);
2166 
2167 	n = mdoc->last;
2168 	if (n->child != NULL)
2169 		return;
2170 
2171 	if (mdoc->meta.name == NULL) {
2172 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2173 		    n->line, n->pos, "Ex");
2174 		return;
2175 	}
2176 
2177 	mdoc->next = ROFF_NEXT_CHILD;
2178 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2179 	mdoc->last = n;
2180 }
2181 
2182 enum roff_sec
2183 mdoc_a2sec(const char *p)
2184 {
2185 	int		 i;
2186 
2187 	for (i = 0; i < (int)SEC__MAX; i++)
2188 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2189 			return (enum roff_sec)i;
2190 
2191 	return SEC_CUSTOM;
2192 }
2193 
2194 static size_t
2195 macro2len(int macro)
2196 {
2197 
2198 	switch (macro) {
2199 	case MDOC_Ad:
2200 		return 12;
2201 	case MDOC_Ao:
2202 		return 12;
2203 	case MDOC_An:
2204 		return 12;
2205 	case MDOC_Aq:
2206 		return 12;
2207 	case MDOC_Ar:
2208 		return 12;
2209 	case MDOC_Bo:
2210 		return 12;
2211 	case MDOC_Bq:
2212 		return 12;
2213 	case MDOC_Cd:
2214 		return 12;
2215 	case MDOC_Cm:
2216 		return 10;
2217 	case MDOC_Do:
2218 		return 10;
2219 	case MDOC_Dq:
2220 		return 12;
2221 	case MDOC_Dv:
2222 		return 12;
2223 	case MDOC_Eo:
2224 		return 12;
2225 	case MDOC_Em:
2226 		return 10;
2227 	case MDOC_Er:
2228 		return 17;
2229 	case MDOC_Ev:
2230 		return 15;
2231 	case MDOC_Fa:
2232 		return 12;
2233 	case MDOC_Fl:
2234 		return 10;
2235 	case MDOC_Fo:
2236 		return 16;
2237 	case MDOC_Fn:
2238 		return 16;
2239 	case MDOC_Ic:
2240 		return 10;
2241 	case MDOC_Li:
2242 		return 16;
2243 	case MDOC_Ms:
2244 		return 6;
2245 	case MDOC_Nm:
2246 		return 10;
2247 	case MDOC_No:
2248 		return 12;
2249 	case MDOC_Oo:
2250 		return 10;
2251 	case MDOC_Op:
2252 		return 14;
2253 	case MDOC_Pa:
2254 		return 32;
2255 	case MDOC_Pf:
2256 		return 12;
2257 	case MDOC_Po:
2258 		return 12;
2259 	case MDOC_Pq:
2260 		return 12;
2261 	case MDOC_Ql:
2262 		return 16;
2263 	case MDOC_Qo:
2264 		return 12;
2265 	case MDOC_So:
2266 		return 12;
2267 	case MDOC_Sq:
2268 		return 12;
2269 	case MDOC_Sy:
2270 		return 6;
2271 	case MDOC_Sx:
2272 		return 16;
2273 	case MDOC_Tn:
2274 		return 10;
2275 	case MDOC_Va:
2276 		return 12;
2277 	case MDOC_Vt:
2278 		return 12;
2279 	case MDOC_Xr:
2280 		return 10;
2281 	default:
2282 		break;
2283 	};
2284 	return 0;
2285 }
2286