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