xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 998de4a5)
1 /*	$OpenBSD: mdoc_validate.c,v 1.224 2016/08/20 14:43:39 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 		mdoclt = MDOC_Item;
504 	}
505 
506 	/*
507 	 * Validate the width field.  Some list types don't need width
508 	 * types and should be warned about them.  Others should have it
509 	 * and must also be warned.  Yet others have a default and need
510 	 * no warning.
511 	 */
512 
513 	switch (n->norm->Bl.type) {
514 	case LIST_tag:
515 		if (NULL == n->norm->Bl.width)
516 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
517 			    n->line, n->pos, "Bl -tag");
518 		break;
519 	case LIST_column:
520 	case LIST_diag:
521 	case LIST_ohang:
522 	case LIST_inset:
523 	case LIST_item:
524 		if (n->norm->Bl.width)
525 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
526 			    wa->line, wa->pos, "Bl -%s",
527 			    mdoc_argnames[mdoclt]);
528 		break;
529 	case LIST_bullet:
530 	case LIST_dash:
531 	case LIST_hyphen:
532 		if (NULL == n->norm->Bl.width)
533 			n->norm->Bl.width = "2n";
534 		break;
535 	case LIST_enum:
536 		if (NULL == n->norm->Bl.width)
537 			n->norm->Bl.width = "3n";
538 		break;
539 	default:
540 		break;
541 	}
542 }
543 
544 static void
545 post_bd(POST_ARGS)
546 {
547 	struct roff_node *n;
548 	struct mdoc_argv *argv;
549 	int		  i;
550 	enum mdoc_disp	  dt;
551 
552 	n = mdoc->last;
553 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
554 		argv = n->args->argv + i;
555 		dt = DISP__NONE;
556 
557 		switch (argv->arg) {
558 		case MDOC_Centred:
559 			dt = DISP_centered;
560 			break;
561 		case MDOC_Ragged:
562 			dt = DISP_ragged;
563 			break;
564 		case MDOC_Unfilled:
565 			dt = DISP_unfilled;
566 			break;
567 		case MDOC_Filled:
568 			dt = DISP_filled;
569 			break;
570 		case MDOC_Literal:
571 			dt = DISP_literal;
572 			break;
573 		case MDOC_File:
574 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
575 			    n->line, n->pos, NULL);
576 			break;
577 		case MDOC_Offset:
578 			if (0 == argv->sz) {
579 				mandoc_msg(MANDOCERR_ARG_EMPTY,
580 				    mdoc->parse, argv->line,
581 				    argv->pos, "Bd -offset");
582 				break;
583 			}
584 			if (NULL != n->norm->Bd.offs)
585 				mandoc_vmsg(MANDOCERR_ARG_REP,
586 				    mdoc->parse, argv->line,
587 				    argv->pos, "Bd -offset %s",
588 				    argv->value[0]);
589 			rewrite_macro2len(argv->value);
590 			n->norm->Bd.offs = argv->value[0];
591 			break;
592 		case MDOC_Compact:
593 			if (n->norm->Bd.comp)
594 				mandoc_msg(MANDOCERR_ARG_REP,
595 				    mdoc->parse, argv->line,
596 				    argv->pos, "Bd -compact");
597 			n->norm->Bd.comp = 1;
598 			break;
599 		default:
600 			abort();
601 		}
602 		if (DISP__NONE == dt)
603 			continue;
604 
605 		if (DISP__NONE == n->norm->Bd.type)
606 			n->norm->Bd.type = dt;
607 		else
608 			mandoc_vmsg(MANDOCERR_BD_REP,
609 			    mdoc->parse, n->line, n->pos,
610 			    "Bd -%s", mdoc_argnames[argv->arg]);
611 	}
612 
613 	if (DISP__NONE == n->norm->Bd.type) {
614 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
615 		    n->line, n->pos, "Bd");
616 		n->norm->Bd.type = DISP_ragged;
617 	}
618 }
619 
620 static void
621 post_an_norm(POST_ARGS)
622 {
623 	struct roff_node *n;
624 	struct mdoc_argv *argv;
625 	size_t	 i;
626 
627 	n = mdoc->last;
628 	if (n->args == NULL)
629 		return;
630 
631 	for (i = 1; i < n->args->argc; i++) {
632 		argv = n->args->argv + i;
633 		mandoc_vmsg(MANDOCERR_AN_REP,
634 		    mdoc->parse, argv->line, argv->pos,
635 		    "An -%s", mdoc_argnames[argv->arg]);
636 	}
637 
638 	argv = n->args->argv;
639 	if (argv->arg == MDOC_Split)
640 		n->norm->An.auth = AUTH_split;
641 	else if (argv->arg == MDOC_Nosplit)
642 		n->norm->An.auth = AUTH_nosplit;
643 	else
644 		abort();
645 }
646 
647 static void
648 post_std(POST_ARGS)
649 {
650 	struct roff_node *n;
651 
652 	n = mdoc->last;
653 	if (n->args && n->args->argc == 1)
654 		if (n->args->argv[0].arg == MDOC_Std)
655 			return;
656 
657 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
658 	    n->line, n->pos, mdoc_macronames[n->tok]);
659 }
660 
661 static void
662 post_obsolete(POST_ARGS)
663 {
664 	struct roff_node *n;
665 
666 	n = mdoc->last;
667 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
668 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
669 		    n->line, n->pos, mdoc_macronames[n->tok]);
670 }
671 
672 static void
673 post_bf(POST_ARGS)
674 {
675 	struct roff_node *np, *nch;
676 
677 	/*
678 	 * Unlike other data pointers, these are "housed" by the HEAD
679 	 * element, which contains the goods.
680 	 */
681 
682 	np = mdoc->last;
683 	if (np->type != ROFFT_HEAD)
684 		return;
685 
686 	assert(np->parent->type == ROFFT_BLOCK);
687 	assert(np->parent->tok == MDOC_Bf);
688 
689 	/* Check the number of arguments. */
690 
691 	nch = np->child;
692 	if (np->parent->args == NULL) {
693 		if (nch == NULL) {
694 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
695 			    np->line, np->pos, "Bf");
696 			return;
697 		}
698 		nch = nch->next;
699 	}
700 	if (nch != NULL)
701 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
702 		    nch->line, nch->pos, "Bf ... %s", nch->string);
703 
704 	/* Extract argument into data. */
705 
706 	if (np->parent->args != NULL) {
707 		switch (np->parent->args->argv[0].arg) {
708 		case MDOC_Emphasis:
709 			np->norm->Bf.font = FONT_Em;
710 			break;
711 		case MDOC_Literal:
712 			np->norm->Bf.font = FONT_Li;
713 			break;
714 		case MDOC_Symbolic:
715 			np->norm->Bf.font = FONT_Sy;
716 			break;
717 		default:
718 			abort();
719 		}
720 		return;
721 	}
722 
723 	/* Extract parameter into data. */
724 
725 	if ( ! strcmp(np->child->string, "Em"))
726 		np->norm->Bf.font = FONT_Em;
727 	else if ( ! strcmp(np->child->string, "Li"))
728 		np->norm->Bf.font = FONT_Li;
729 	else if ( ! strcmp(np->child->string, "Sy"))
730 		np->norm->Bf.font = FONT_Sy;
731 	else
732 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
733 		    np->child->line, np->child->pos,
734 		    "Bf %s", np->child->string);
735 }
736 
737 static void
738 post_lb(POST_ARGS)
739 {
740 	struct roff_node	*n;
741 	char			*libname;
742 
743 	n = mdoc->last->child;
744 	assert(n->type == ROFFT_TEXT);
745 	mandoc_asprintf(&libname, "library \\(Lq%s\\(Rq", n->string);
746 	free(n->string);
747 	n->string = libname;
748 }
749 
750 static void
751 post_eoln(POST_ARGS)
752 {
753 	const struct roff_node *n;
754 
755 	n = mdoc->last;
756 	if (n->child != NULL)
757 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
758 		    mdoc->parse, n->line, n->pos,
759 		    "%s %s", mdoc_macronames[n->tok],
760 		    n->child->string);
761 }
762 
763 static void
764 post_fname(POST_ARGS)
765 {
766 	const struct roff_node	*n;
767 	const char		*cp;
768 	size_t			 pos;
769 
770 	n = mdoc->last->child;
771 	pos = strcspn(n->string, "()");
772 	cp = n->string + pos;
773 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
774 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
775 		    n->line, n->pos + pos, n->string);
776 }
777 
778 static void
779 post_fn(POST_ARGS)
780 {
781 
782 	post_fname(mdoc);
783 	post_fa(mdoc);
784 }
785 
786 static void
787 post_fo(POST_ARGS)
788 {
789 	const struct roff_node	*n;
790 
791 	n = mdoc->last;
792 
793 	if (n->type != ROFFT_HEAD)
794 		return;
795 
796 	if (n->child == NULL) {
797 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
798 		    n->line, n->pos, "Fo");
799 		return;
800 	}
801 	if (n->child != n->last) {
802 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
803 		    n->child->next->line, n->child->next->pos,
804 		    "Fo ... %s", n->child->next->string);
805 		while (n->child != n->last)
806 			roff_node_delete(mdoc, n->last);
807 	}
808 
809 	post_fname(mdoc);
810 }
811 
812 static void
813 post_fa(POST_ARGS)
814 {
815 	const struct roff_node *n;
816 	const char *cp;
817 
818 	for (n = mdoc->last->child; n != NULL; n = n->next) {
819 		for (cp = n->string; *cp != '\0'; cp++) {
820 			/* Ignore callbacks and alterations. */
821 			if (*cp == '(' || *cp == '{')
822 				break;
823 			if (*cp != ',')
824 				continue;
825 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
826 			    n->line, n->pos + (cp - n->string),
827 			    n->string);
828 			break;
829 		}
830 	}
831 }
832 
833 static void
834 post_nm(POST_ARGS)
835 {
836 	struct roff_node	*n;
837 
838 	n = mdoc->last;
839 
840 	if (n->last != NULL &&
841 	    (n->last->tok == MDOC_Pp ||
842 	     n->last->tok == MDOC_Lp))
843 		mdoc_node_relink(mdoc, n->last);
844 
845 	if (mdoc->meta.name != NULL)
846 		return;
847 
848 	deroff(&mdoc->meta.name, n);
849 
850 	if (mdoc->meta.name == NULL)
851 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
852 		    n->line, n->pos, "Nm");
853 }
854 
855 static void
856 post_nd(POST_ARGS)
857 {
858 	struct roff_node	*n;
859 
860 	n = mdoc->last;
861 
862 	if (n->type != ROFFT_BODY)
863 		return;
864 
865 	if (n->child == NULL)
866 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
867 		    n->line, n->pos, "Nd");
868 
869 	post_hyph(mdoc);
870 }
871 
872 static void
873 post_display(POST_ARGS)
874 {
875 	struct roff_node *n, *np;
876 
877 	n = mdoc->last;
878 	switch (n->type) {
879 	case ROFFT_BODY:
880 		if (n->end != ENDBODY_NOT) {
881 			if (n->tok == MDOC_Bd &&
882 			    n->body->parent->args == NULL)
883 				roff_node_delete(mdoc, n);
884 		} else if (n->child == NULL)
885 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
886 			    n->line, n->pos, mdoc_macronames[n->tok]);
887 		else if (n->tok == MDOC_D1)
888 			post_hyph(mdoc);
889 		break;
890 	case ROFFT_BLOCK:
891 		if (n->tok == MDOC_Bd) {
892 			if (n->args == NULL) {
893 				mandoc_msg(MANDOCERR_BD_NOARG,
894 				    mdoc->parse, n->line, n->pos, "Bd");
895 				mdoc->next = ROFF_NEXT_SIBLING;
896 				while (n->body->child != NULL)
897 					mdoc_node_relink(mdoc,
898 					    n->body->child);
899 				roff_node_delete(mdoc, n);
900 				break;
901 			}
902 			post_bd(mdoc);
903 			post_prevpar(mdoc);
904 		}
905 		for (np = n->parent; np != NULL; np = np->parent) {
906 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
907 				mandoc_vmsg(MANDOCERR_BD_NEST,
908 				    mdoc->parse, n->line, n->pos,
909 				    "%s in Bd", mdoc_macronames[n->tok]);
910 				break;
911 			}
912 		}
913 		break;
914 	default:
915 		break;
916 	}
917 }
918 
919 static void
920 post_defaults(POST_ARGS)
921 {
922 	struct roff_node *nn;
923 
924 	/*
925 	 * The `Ar' defaults to "file ..." if no value is provided as an
926 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
927 	 * gets an empty string.
928 	 */
929 
930 	if (mdoc->last->child != NULL)
931 		return;
932 
933 	nn = mdoc->last;
934 
935 	switch (nn->tok) {
936 	case MDOC_Ar:
937 		mdoc->next = ROFF_NEXT_CHILD;
938 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
939 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
940 		break;
941 	case MDOC_Pa:
942 	case MDOC_Mt:
943 		mdoc->next = ROFF_NEXT_CHILD;
944 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
945 		break;
946 	default:
947 		abort();
948 	}
949 	mdoc->last = nn;
950 }
951 
952 static void
953 post_at(POST_ARGS)
954 {
955 	struct roff_node	*n;
956 	const char		*std_att;
957 	char			*att;
958 
959 	n = mdoc->last;
960 	if (n->child == NULL) {
961 		mdoc->next = ROFF_NEXT_CHILD;
962 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
963 		mdoc->last = n;
964 		return;
965 	}
966 
967 	/*
968 	 * If we have a child, look it up in the standard keys.  If a
969 	 * key exist, use that instead of the child; if it doesn't,
970 	 * prefix "AT&T UNIX " to the existing data.
971 	 */
972 
973 	n = n->child;
974 	assert(n->type == ROFFT_TEXT);
975 	if ((std_att = mdoc_a2att(n->string)) == NULL) {
976 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
977 		    n->line, n->pos, "At %s", n->string);
978 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
979 	} else
980 		att = mandoc_strdup(std_att);
981 
982 	free(n->string);
983 	n->string = att;
984 }
985 
986 static void
987 post_an(POST_ARGS)
988 {
989 	struct roff_node *np, *nch;
990 
991 	post_an_norm(mdoc);
992 
993 	np = mdoc->last;
994 	nch = np->child;
995 	if (np->norm->An.auth == AUTH__NONE) {
996 		if (nch == NULL)
997 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
998 			    np->line, np->pos, "An");
999 	} else if (nch != NULL)
1000 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1001 		    nch->line, nch->pos, "An ... %s", nch->string);
1002 }
1003 
1004 static void
1005 post_en(POST_ARGS)
1006 {
1007 
1008 	post_obsolete(mdoc);
1009 	if (mdoc->last->type == ROFFT_BLOCK)
1010 		mdoc->last->norm->Es = mdoc->last_es;
1011 }
1012 
1013 static void
1014 post_es(POST_ARGS)
1015 {
1016 
1017 	post_obsolete(mdoc);
1018 	mdoc->last_es = mdoc->last;
1019 }
1020 
1021 static void
1022 post_it(POST_ARGS)
1023 {
1024 	struct roff_node *nbl, *nit, *nch;
1025 	int		  i, cols;
1026 	enum mdoc_list	  lt;
1027 
1028 	post_prevpar(mdoc);
1029 
1030 	nit = mdoc->last;
1031 	if (nit->type != ROFFT_BLOCK)
1032 		return;
1033 
1034 	nbl = nit->parent->parent;
1035 	lt = nbl->norm->Bl.type;
1036 
1037 	switch (lt) {
1038 	case LIST_tag:
1039 	case LIST_hang:
1040 	case LIST_ohang:
1041 	case LIST_inset:
1042 	case LIST_diag:
1043 		if (nit->head->child == NULL)
1044 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1045 			    mdoc->parse, nit->line, nit->pos,
1046 			    "Bl -%s It",
1047 			    mdoc_argnames[nbl->args->argv[0].arg]);
1048 		break;
1049 	case LIST_bullet:
1050 	case LIST_dash:
1051 	case LIST_enum:
1052 	case LIST_hyphen:
1053 		if (nit->body == NULL || nit->body->child == NULL)
1054 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1055 			    mdoc->parse, nit->line, nit->pos,
1056 			    "Bl -%s It",
1057 			    mdoc_argnames[nbl->args->argv[0].arg]);
1058 		/* FALLTHROUGH */
1059 	case LIST_item:
1060 		if ((nch = nit->head->child) != NULL)
1061 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1062 			    mdoc->parse, nit->line, nit->pos,
1063 			    "It %s", nch->string == NULL ?
1064 			    mdoc_macronames[nch->tok] : nch->string);
1065 		break;
1066 	case LIST_column:
1067 		cols = (int)nbl->norm->Bl.ncols;
1068 
1069 		assert(nit->head->child == NULL);
1070 
1071 		i = 0;
1072 		for (nch = nit->child; nch != NULL; nch = nch->next)
1073 			if (nch->type == ROFFT_BODY)
1074 				i++;
1075 
1076 		if (i < cols || i > cols + 1)
1077 			mandoc_vmsg(MANDOCERR_BL_COL,
1078 			    mdoc->parse, nit->line, nit->pos,
1079 			    "%d columns, %d cells", cols, i);
1080 		break;
1081 	default:
1082 		abort();
1083 	}
1084 }
1085 
1086 static void
1087 post_bl_block(POST_ARGS)
1088 {
1089 	struct roff_node *n, *ni, *nc;
1090 
1091 	post_prevpar(mdoc);
1092 
1093 	/*
1094 	 * These are fairly complicated, so we've broken them into two
1095 	 * functions.  post_bl_block_tag() is called when a -tag is
1096 	 * specified, but no -width (it must be guessed).  The second
1097 	 * when a -width is specified (macro indicators must be
1098 	 * rewritten into real lengths).
1099 	 */
1100 
1101 	n = mdoc->last;
1102 
1103 	if (n->norm->Bl.type == LIST_tag &&
1104 	    n->norm->Bl.width == NULL) {
1105 		post_bl_block_tag(mdoc);
1106 		assert(n->norm->Bl.width != NULL);
1107 	}
1108 
1109 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1110 		if (ni->body == NULL)
1111 			continue;
1112 		nc = ni->body->last;
1113 		while (nc != NULL) {
1114 			switch (nc->tok) {
1115 			case MDOC_Pp:
1116 			case MDOC_Lp:
1117 			case MDOC_br:
1118 				break;
1119 			default:
1120 				nc = NULL;
1121 				continue;
1122 			}
1123 			if (ni->next == NULL) {
1124 				mandoc_msg(MANDOCERR_PAR_MOVE,
1125 				    mdoc->parse, nc->line, nc->pos,
1126 				    mdoc_macronames[nc->tok]);
1127 				mdoc_node_relink(mdoc, nc);
1128 			} else if (n->norm->Bl.comp == 0 &&
1129 			    n->norm->Bl.type != LIST_column) {
1130 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1131 				    mdoc->parse, nc->line, nc->pos,
1132 				    "%s before It",
1133 				    mdoc_macronames[nc->tok]);
1134 				roff_node_delete(mdoc, nc);
1135 			} else
1136 				break;
1137 			nc = ni->body->last;
1138 		}
1139 	}
1140 }
1141 
1142 /*
1143  * If the argument of -offset or -width is a macro,
1144  * replace it with the associated default width.
1145  */
1146 void
1147 rewrite_macro2len(char **arg)
1148 {
1149 	size_t		  width;
1150 	int		  tok;
1151 
1152 	if (*arg == NULL)
1153 		return;
1154 	else if ( ! strcmp(*arg, "Ds"))
1155 		width = 6;
1156 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1157 		return;
1158 	else
1159 		width = macro2len(tok);
1160 
1161 	free(*arg);
1162 	mandoc_asprintf(arg, "%zun", width);
1163 }
1164 
1165 static void
1166 post_bl_block_tag(POST_ARGS)
1167 {
1168 	struct roff_node *n, *nn;
1169 	size_t		  sz, ssz;
1170 	int		  i;
1171 	char		  buf[24];
1172 
1173 	/*
1174 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1175 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1176 	 * ONLY if the -width argument has NOT been provided.  See
1177 	 * rewrite_macro2len() for converting the -width string.
1178 	 */
1179 
1180 	sz = 10;
1181 	n = mdoc->last;
1182 
1183 	for (nn = n->body->child; nn != NULL; nn = nn->next) {
1184 		if (nn->tok != MDOC_It)
1185 			continue;
1186 
1187 		assert(nn->type == ROFFT_BLOCK);
1188 		nn = nn->head->child;
1189 
1190 		if (nn == NULL)
1191 			break;
1192 
1193 		if (nn->type == ROFFT_TEXT) {
1194 			sz = strlen(nn->string) + 1;
1195 			break;
1196 		}
1197 
1198 		if (0 != (ssz = macro2len(nn->tok)))
1199 			sz = ssz;
1200 
1201 		break;
1202 	}
1203 
1204 	/* Defaults to ten ens. */
1205 
1206 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
1207 
1208 	/*
1209 	 * We have to dynamically add this to the macro's argument list.
1210 	 * We're guaranteed that a MDOC_Width doesn't already exist.
1211 	 */
1212 
1213 	assert(n->args != NULL);
1214 	i = (int)(n->args->argc)++;
1215 
1216 	n->args->argv = mandoc_reallocarray(n->args->argv,
1217 	    n->args->argc, sizeof(struct mdoc_argv));
1218 
1219 	n->args->argv[i].arg = MDOC_Width;
1220 	n->args->argv[i].line = n->line;
1221 	n->args->argv[i].pos = n->pos;
1222 	n->args->argv[i].sz = 1;
1223 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1224 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1225 
1226 	/* Set our width! */
1227 	n->norm->Bl.width = n->args->argv[i].value[0];
1228 }
1229 
1230 static void
1231 post_bl_head(POST_ARGS)
1232 {
1233 	struct roff_node *nbl, *nh, *nch, *nnext;
1234 	struct mdoc_argv *argv;
1235 	int		  i, j;
1236 
1237 	post_bl_norm(mdoc);
1238 
1239 	nh = mdoc->last;
1240 	if (nh->norm->Bl.type != LIST_column) {
1241 		if ((nch = nh->child) == NULL)
1242 			return;
1243 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1244 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1245 		while (nch != NULL) {
1246 			roff_node_delete(mdoc, nch);
1247 			nch = nh->child;
1248 		}
1249 		return;
1250 	}
1251 
1252 	/*
1253 	 * Append old-style lists, where the column width specifiers
1254 	 * trail as macro parameters, to the new-style ("normal-form")
1255 	 * lists where they're argument values following -column.
1256 	 */
1257 
1258 	if (nh->child == NULL)
1259 		return;
1260 
1261 	nbl = nh->parent;
1262 	for (j = 0; j < (int)nbl->args->argc; j++)
1263 		if (nbl->args->argv[j].arg == MDOC_Column)
1264 			break;
1265 
1266 	assert(j < (int)nbl->args->argc);
1267 
1268 	/*
1269 	 * Accommodate for new-style groff column syntax.  Shuffle the
1270 	 * child nodes, all of which must be TEXT, as arguments for the
1271 	 * column field.  Then, delete the head children.
1272 	 */
1273 
1274 	argv = nbl->args->argv + j;
1275 	i = argv->sz;
1276 	for (nch = nh->child; nch != NULL; nch = nch->next)
1277 		argv->sz++;
1278 	argv->value = mandoc_reallocarray(argv->value,
1279 	    argv->sz, sizeof(char *));
1280 
1281 	nh->norm->Bl.ncols = argv->sz;
1282 	nh->norm->Bl.cols = (void *)argv->value;
1283 
1284 	for (nch = nh->child; nch != NULL; nch = nnext) {
1285 		argv->value[i++] = nch->string;
1286 		nch->string = NULL;
1287 		nnext = nch->next;
1288 		roff_node_delete(NULL, nch);
1289 	}
1290 	nh->child = NULL;
1291 }
1292 
1293 static void
1294 post_bl(POST_ARGS)
1295 {
1296 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1297 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1298 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1299 
1300 	nbody = mdoc->last;
1301 	switch (nbody->type) {
1302 	case ROFFT_BLOCK:
1303 		post_bl_block(mdoc);
1304 		return;
1305 	case ROFFT_HEAD:
1306 		post_bl_head(mdoc);
1307 		return;
1308 	case ROFFT_BODY:
1309 		break;
1310 	default:
1311 		return;
1312 	}
1313 	if (nbody->end != ENDBODY_NOT)
1314 		return;
1315 
1316 	nchild = nbody->child;
1317 	if (nchild == NULL) {
1318 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1319 		    nbody->line, nbody->pos, "Bl");
1320 		return;
1321 	}
1322 	while (nchild != NULL) {
1323 		nnext = nchild->next;
1324 		if (nchild->tok == MDOC_It ||
1325 		    (nchild->tok == MDOC_Sm &&
1326 		     nnext != NULL && nnext->tok == MDOC_It)) {
1327 			nchild = nnext;
1328 			continue;
1329 		}
1330 
1331 		/*
1332 		 * In .Bl -column, the first rows may be implicit,
1333 		 * that is, they may not start with .It macros.
1334 		 * Such rows may be followed by nodes generated on the
1335 		 * roff level, for example .TS, which cannot be moved
1336 		 * out of the list.  In that case, wrap such roff nodes
1337 		 * into an implicit row.
1338 		 */
1339 
1340 		if (nchild->prev != NULL) {
1341 			mdoc->last = nchild;
1342 			mdoc->next = ROFF_NEXT_SIBLING;
1343 			roff_block_alloc(mdoc, nchild->line,
1344 			    nchild->pos, MDOC_It);
1345 			roff_head_alloc(mdoc, nchild->line,
1346 			    nchild->pos, MDOC_It);
1347 			mdoc->next = ROFF_NEXT_SIBLING;
1348 			roff_body_alloc(mdoc, nchild->line,
1349 			    nchild->pos, MDOC_It);
1350 			while (nchild->tok != MDOC_It) {
1351 				mdoc_node_relink(mdoc, nchild);
1352 				if ((nchild = nnext) == NULL)
1353 					break;
1354 				nnext = nchild->next;
1355 				mdoc->next = ROFF_NEXT_SIBLING;
1356 			}
1357 			mdoc->last = nbody;
1358 			continue;
1359 		}
1360 
1361 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1362 		    nchild->line, nchild->pos,
1363 		    mdoc_macronames[nchild->tok]);
1364 
1365 		/*
1366 		 * Move the node out of the Bl block.
1367 		 * First, collect all required node pointers.
1368 		 */
1369 
1370 		nblock  = nbody->parent;
1371 		nprev   = nblock->prev;
1372 		nparent = nblock->parent;
1373 
1374 		/*
1375 		 * Unlink this child.
1376 		 */
1377 
1378 		nbody->child = nnext;
1379 		if (nnext == NULL)
1380 			nbody->last  = NULL;
1381 		else
1382 			nnext->prev = NULL;
1383 
1384 		/*
1385 		 * Relink this child.
1386 		 */
1387 
1388 		nchild->parent = nparent;
1389 		nchild->prev   = nprev;
1390 		nchild->next   = nblock;
1391 
1392 		nblock->prev = nchild;
1393 		if (nprev == NULL)
1394 			nparent->child = nchild;
1395 		else
1396 			nprev->next = nchild;
1397 
1398 		nchild = nnext;
1399 	}
1400 }
1401 
1402 static void
1403 post_bk(POST_ARGS)
1404 {
1405 	struct roff_node	*n;
1406 
1407 	n = mdoc->last;
1408 
1409 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1410 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1411 		    mdoc->parse, n->line, n->pos, "Bk");
1412 		roff_node_delete(mdoc, n);
1413 	}
1414 }
1415 
1416 static void
1417 post_sm(POST_ARGS)
1418 {
1419 	struct roff_node	*nch;
1420 
1421 	nch = mdoc->last->child;
1422 
1423 	if (nch == NULL) {
1424 		mdoc->flags ^= MDOC_SMOFF;
1425 		return;
1426 	}
1427 
1428 	assert(nch->type == ROFFT_TEXT);
1429 
1430 	if ( ! strcmp(nch->string, "on")) {
1431 		mdoc->flags &= ~MDOC_SMOFF;
1432 		return;
1433 	}
1434 	if ( ! strcmp(nch->string, "off")) {
1435 		mdoc->flags |= MDOC_SMOFF;
1436 		return;
1437 	}
1438 
1439 	mandoc_vmsg(MANDOCERR_SM_BAD,
1440 	    mdoc->parse, nch->line, nch->pos,
1441 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1442 	mdoc_node_relink(mdoc, nch);
1443 	return;
1444 }
1445 
1446 static void
1447 post_root(POST_ARGS)
1448 {
1449 	struct roff_node *n;
1450 
1451 	/* Add missing prologue data. */
1452 
1453 	if (mdoc->meta.date == NULL)
1454 		mdoc->meta.date = mdoc->quick ?
1455 		    mandoc_strdup("") :
1456 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
1457 
1458 	if (mdoc->meta.title == NULL) {
1459 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1460 		    mdoc->parse, 0, 0, "EOF");
1461 		mdoc->meta.title = mandoc_strdup("UNTITLED");
1462 	}
1463 
1464 	if (mdoc->meta.vol == NULL)
1465 		mdoc->meta.vol = mandoc_strdup("LOCAL");
1466 
1467 	if (mdoc->meta.os == NULL) {
1468 		mandoc_msg(MANDOCERR_OS_MISSING,
1469 		    mdoc->parse, 0, 0, NULL);
1470 		mdoc->meta.os = mandoc_strdup("");
1471 	}
1472 
1473 	/* Check that we begin with a proper `Sh'. */
1474 
1475 	n = mdoc->first->child;
1476 	while (n != NULL && n->tok != TOKEN_NONE &&
1477 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1478 		n = n->next;
1479 
1480 	if (n == NULL)
1481 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1482 	else if (n->tok != MDOC_Sh)
1483 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1484 		    n->line, n->pos, mdoc_macronames[n->tok]);
1485 }
1486 
1487 static void
1488 post_st(POST_ARGS)
1489 {
1490 	struct roff_node	 *n, *nch;
1491 	const char		 *p;
1492 
1493 	n = mdoc->last;
1494 	nch = n->child;
1495 
1496 	assert(nch->type == ROFFT_TEXT);
1497 
1498 	if ((p = mdoc_a2st(nch->string)) == NULL) {
1499 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1500 		    nch->line, nch->pos, "St %s", nch->string);
1501 		roff_node_delete(mdoc, n);
1502 	} else {
1503 		free(nch->string);
1504 		nch->string = mandoc_strdup(p);
1505 	}
1506 }
1507 
1508 static void
1509 post_rs(POST_ARGS)
1510 {
1511 	struct roff_node *np, *nch, *next, *prev;
1512 	int		  i, j;
1513 
1514 	np = mdoc->last;
1515 
1516 	if (np->type != ROFFT_BODY)
1517 		return;
1518 
1519 	if (np->child == NULL) {
1520 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1521 		    np->line, np->pos, "Rs");
1522 		return;
1523 	}
1524 
1525 	/*
1526 	 * The full `Rs' block needs special handling to order the
1527 	 * sub-elements according to `rsord'.  Pick through each element
1528 	 * and correctly order it.  This is an insertion sort.
1529 	 */
1530 
1531 	next = NULL;
1532 	for (nch = np->child->next; nch != NULL; nch = next) {
1533 		/* Determine order number of this child. */
1534 		for (i = 0; i < RSORD_MAX; i++)
1535 			if (rsord[i] == nch->tok)
1536 				break;
1537 
1538 		if (i == RSORD_MAX) {
1539 			mandoc_msg(MANDOCERR_RS_BAD,
1540 			    mdoc->parse, nch->line, nch->pos,
1541 			    mdoc_macronames[nch->tok]);
1542 			i = -1;
1543 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1544 			np->norm->Rs.quote_T++;
1545 
1546 		/*
1547 		 * Remove this child from the chain.  This somewhat
1548 		 * repeats roff_node_unlink(), but since we're
1549 		 * just re-ordering, there's no need for the
1550 		 * full unlink process.
1551 		 */
1552 
1553 		if ((next = nch->next) != NULL)
1554 			next->prev = nch->prev;
1555 
1556 		if ((prev = nch->prev) != NULL)
1557 			prev->next = nch->next;
1558 
1559 		nch->prev = nch->next = NULL;
1560 
1561 		/*
1562 		 * Scan back until we reach a node that's
1563 		 * to be ordered before this child.
1564 		 */
1565 
1566 		for ( ; prev ; prev = prev->prev) {
1567 			/* Determine order of `prev'. */
1568 			for (j = 0; j < RSORD_MAX; j++)
1569 				if (rsord[j] == prev->tok)
1570 					break;
1571 			if (j == RSORD_MAX)
1572 				j = -1;
1573 
1574 			if (j <= i)
1575 				break;
1576 		}
1577 
1578 		/*
1579 		 * Set this child back into its correct place
1580 		 * in front of the `prev' node.
1581 		 */
1582 
1583 		nch->prev = prev;
1584 
1585 		if (prev == NULL) {
1586 			np->child->prev = nch;
1587 			nch->next = np->child;
1588 			np->child = nch;
1589 		} else {
1590 			if (prev->next)
1591 				prev->next->prev = nch;
1592 			nch->next = prev->next;
1593 			prev->next = nch;
1594 		}
1595 	}
1596 }
1597 
1598 /*
1599  * For some arguments of some macros,
1600  * convert all breakable hyphens into ASCII_HYPH.
1601  */
1602 static void
1603 post_hyph(POST_ARGS)
1604 {
1605 	struct roff_node	*nch;
1606 	char			*cp;
1607 
1608 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1609 		if (nch->type != ROFFT_TEXT)
1610 			continue;
1611 		cp = nch->string;
1612 		if (*cp == '\0')
1613 			continue;
1614 		while (*(++cp) != '\0')
1615 			if (*cp == '-' &&
1616 			    isalpha((unsigned char)cp[-1]) &&
1617 			    isalpha((unsigned char)cp[1]))
1618 				*cp = ASCII_HYPH;
1619 	}
1620 }
1621 
1622 static void
1623 post_ns(POST_ARGS)
1624 {
1625 
1626 	if (mdoc->last->flags & MDOC_LINE)
1627 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1628 		    mdoc->last->line, mdoc->last->pos, NULL);
1629 }
1630 
1631 static void
1632 post_sh(POST_ARGS)
1633 {
1634 
1635 	post_ignpar(mdoc);
1636 
1637 	switch (mdoc->last->type) {
1638 	case ROFFT_HEAD:
1639 		post_sh_head(mdoc);
1640 		break;
1641 	case ROFFT_BODY:
1642 		switch (mdoc->lastsec)  {
1643 		case SEC_NAME:
1644 			post_sh_name(mdoc);
1645 			break;
1646 		case SEC_SEE_ALSO:
1647 			post_sh_see_also(mdoc);
1648 			break;
1649 		case SEC_AUTHORS:
1650 			post_sh_authors(mdoc);
1651 			break;
1652 		default:
1653 			break;
1654 		}
1655 		break;
1656 	default:
1657 		break;
1658 	}
1659 }
1660 
1661 static void
1662 post_sh_name(POST_ARGS)
1663 {
1664 	struct roff_node *n;
1665 	int hasnm, hasnd;
1666 
1667 	hasnm = hasnd = 0;
1668 
1669 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1670 		switch (n->tok) {
1671 		case MDOC_Nm:
1672 			hasnm = 1;
1673 			break;
1674 		case MDOC_Nd:
1675 			hasnd = 1;
1676 			if (n->next != NULL)
1677 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1678 				    mdoc->parse, n->line, n->pos, NULL);
1679 			break;
1680 		case TOKEN_NONE:
1681 			if (hasnm)
1682 				break;
1683 			/* FALLTHROUGH */
1684 		default:
1685 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1686 			    n->line, n->pos, mdoc_macronames[n->tok]);
1687 			break;
1688 		}
1689 	}
1690 
1691 	if ( ! hasnm)
1692 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1693 		    mdoc->last->line, mdoc->last->pos, NULL);
1694 	if ( ! hasnd)
1695 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1696 		    mdoc->last->line, mdoc->last->pos, NULL);
1697 }
1698 
1699 static void
1700 post_sh_see_also(POST_ARGS)
1701 {
1702 	const struct roff_node	*n;
1703 	const char		*name, *sec;
1704 	const char		*lastname, *lastsec, *lastpunct;
1705 	int			 cmp;
1706 
1707 	n = mdoc->last->child;
1708 	lastname = lastsec = lastpunct = NULL;
1709 	while (n != NULL) {
1710 		if (n->tok != MDOC_Xr ||
1711 		    n->child == NULL ||
1712 		    n->child->next == NULL)
1713 			break;
1714 
1715 		/* Process one .Xr node. */
1716 
1717 		name = n->child->string;
1718 		sec = n->child->next->string;
1719 		if (lastsec != NULL) {
1720 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1721 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1722 				    mdoc->parse, n->line, n->pos,
1723 				    "%s before %s(%s)", lastpunct,
1724 				    name, sec);
1725 			cmp = strcmp(lastsec, sec);
1726 			if (cmp > 0)
1727 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1728 				    mdoc->parse, n->line, n->pos,
1729 				    "%s(%s) after %s(%s)", name,
1730 				    sec, lastname, lastsec);
1731 			else if (cmp == 0 &&
1732 			    strcasecmp(lastname, name) > 0)
1733 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1734 				    mdoc->parse, n->line, n->pos,
1735 				    "%s after %s", name, lastname);
1736 		}
1737 		lastname = name;
1738 		lastsec = sec;
1739 
1740 		/* Process the following node. */
1741 
1742 		n = n->next;
1743 		if (n == NULL)
1744 			break;
1745 		if (n->tok == MDOC_Xr) {
1746 			lastpunct = "none";
1747 			continue;
1748 		}
1749 		if (n->type != ROFFT_TEXT)
1750 			break;
1751 		for (name = n->string; *name != '\0'; name++)
1752 			if (isalpha((const unsigned char)*name))
1753 				return;
1754 		lastpunct = n->string;
1755 		if (n->next == NULL)
1756 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1757 			    n->line, n->pos, "%s after %s(%s)",
1758 			    lastpunct, lastname, lastsec);
1759 		n = n->next;
1760 	}
1761 }
1762 
1763 static int
1764 child_an(const struct roff_node *n)
1765 {
1766 
1767 	for (n = n->child; n != NULL; n = n->next)
1768 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1769 			return 1;
1770 	return 0;
1771 }
1772 
1773 static void
1774 post_sh_authors(POST_ARGS)
1775 {
1776 
1777 	if ( ! child_an(mdoc->last))
1778 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1779 		    mdoc->last->line, mdoc->last->pos, NULL);
1780 }
1781 
1782 static void
1783 post_sh_head(POST_ARGS)
1784 {
1785 	struct roff_node	*nch;
1786 	const char		*goodsec;
1787 	enum roff_sec		 sec;
1788 
1789 	/*
1790 	 * Process a new section.  Sections are either "named" or
1791 	 * "custom".  Custom sections are user-defined, while named ones
1792 	 * follow a conventional order and may only appear in certain
1793 	 * manual sections.
1794 	 */
1795 
1796 	sec = mdoc->last->sec;
1797 
1798 	/* The NAME should be first. */
1799 
1800 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1801 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1802 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
1803 		    sec != SEC_CUSTOM ? secnames[sec] :
1804 		    (nch = mdoc->last->child) == NULL ? "" :
1805 		    nch->type == ROFFT_TEXT ? nch->string :
1806 		    mdoc_macronames[nch->tok]);
1807 
1808 	/* The SYNOPSIS gets special attention in other areas. */
1809 
1810 	if (sec == SEC_SYNOPSIS) {
1811 		roff_setreg(mdoc->roff, "nS", 1, '=');
1812 		mdoc->flags |= MDOC_SYNOPSIS;
1813 	} else {
1814 		roff_setreg(mdoc->roff, "nS", 0, '=');
1815 		mdoc->flags &= ~MDOC_SYNOPSIS;
1816 	}
1817 
1818 	/* Mark our last section. */
1819 
1820 	mdoc->lastsec = sec;
1821 
1822 	/* We don't care about custom sections after this. */
1823 
1824 	if (sec == SEC_CUSTOM)
1825 		return;
1826 
1827 	/*
1828 	 * Check whether our non-custom section is being repeated or is
1829 	 * out of order.
1830 	 */
1831 
1832 	if (sec == mdoc->lastnamed)
1833 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1834 		    mdoc->last->line, mdoc->last->pos,
1835 		    "Sh %s", secnames[sec]);
1836 
1837 	if (sec < mdoc->lastnamed)
1838 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1839 		    mdoc->last->line, mdoc->last->pos,
1840 		    "Sh %s", secnames[sec]);
1841 
1842 	/* Mark the last named section. */
1843 
1844 	mdoc->lastnamed = sec;
1845 
1846 	/* Check particular section/manual conventions. */
1847 
1848 	if (mdoc->meta.msec == NULL)
1849 		return;
1850 
1851 	goodsec = NULL;
1852 	switch (sec) {
1853 	case SEC_ERRORS:
1854 		if (*mdoc->meta.msec == '4')
1855 			break;
1856 		goodsec = "2, 3, 4, 9";
1857 		/* FALLTHROUGH */
1858 	case SEC_RETURN_VALUES:
1859 	case SEC_LIBRARY:
1860 		if (*mdoc->meta.msec == '2')
1861 			break;
1862 		if (*mdoc->meta.msec == '3')
1863 			break;
1864 		if (NULL == goodsec)
1865 			goodsec = "2, 3, 9";
1866 		/* FALLTHROUGH */
1867 	case SEC_CONTEXT:
1868 		if (*mdoc->meta.msec == '9')
1869 			break;
1870 		if (NULL == goodsec)
1871 			goodsec = "9";
1872 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1873 		    mdoc->last->line, mdoc->last->pos,
1874 		    "Sh %s for %s only", secnames[sec], goodsec);
1875 		break;
1876 	default:
1877 		break;
1878 	}
1879 }
1880 
1881 static void
1882 post_ignpar(POST_ARGS)
1883 {
1884 	struct roff_node *np;
1885 
1886 	switch (mdoc->last->type) {
1887 	case ROFFT_HEAD:
1888 		post_hyph(mdoc);
1889 		return;
1890 	case ROFFT_BODY:
1891 		break;
1892 	default:
1893 		return;
1894 	}
1895 
1896 	if ((np = mdoc->last->child) != NULL)
1897 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1898 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
1899 			    mdoc->parse, np->line, np->pos,
1900 			    "%s after %s", mdoc_macronames[np->tok],
1901 			    mdoc_macronames[mdoc->last->tok]);
1902 			roff_node_delete(mdoc, np);
1903 		}
1904 
1905 	if ((np = mdoc->last->last) != NULL)
1906 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1907 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1908 			    np->line, np->pos, "%s at the end of %s",
1909 			    mdoc_macronames[np->tok],
1910 			    mdoc_macronames[mdoc->last->tok]);
1911 			roff_node_delete(mdoc, np);
1912 		}
1913 }
1914 
1915 static void
1916 post_prevpar(POST_ARGS)
1917 {
1918 	struct roff_node *n;
1919 
1920 	n = mdoc->last;
1921 	if (NULL == n->prev)
1922 		return;
1923 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
1924 		return;
1925 
1926 	/*
1927 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1928 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1929 	 */
1930 
1931 	if (n->prev->tok != MDOC_Pp &&
1932 	    n->prev->tok != MDOC_Lp &&
1933 	    n->prev->tok != MDOC_br)
1934 		return;
1935 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
1936 		return;
1937 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
1938 		return;
1939 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
1940 		return;
1941 
1942 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1943 	    n->prev->line, n->prev->pos,
1944 	    "%s before %s", mdoc_macronames[n->prev->tok],
1945 	    mdoc_macronames[n->tok]);
1946 	roff_node_delete(mdoc, n->prev);
1947 }
1948 
1949 static void
1950 post_par(POST_ARGS)
1951 {
1952 	struct roff_node *np;
1953 
1954 	np = mdoc->last;
1955 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
1956 		post_prevpar(mdoc);
1957 
1958 	if (np->tok == MDOC_sp) {
1959 		if (np->child != NULL && np->child->next != NULL)
1960 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1961 			    np->child->next->line, np->child->next->pos,
1962 			    "sp ... %s", np->child->next->string);
1963 	} else if (np->child != NULL)
1964 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
1965 		    mdoc->parse, np->line, np->pos, "%s %s",
1966 		    mdoc_macronames[np->tok], np->child->string);
1967 
1968 	if ((np = mdoc->last->prev) == NULL) {
1969 		np = mdoc->last->parent;
1970 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
1971 			return;
1972 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
1973 	    (mdoc->last->tok != MDOC_br ||
1974 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
1975 		return;
1976 
1977 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1978 	    mdoc->last->line, mdoc->last->pos,
1979 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
1980 	    mdoc_macronames[np->tok]);
1981 	roff_node_delete(mdoc, mdoc->last);
1982 }
1983 
1984 static void
1985 post_dd(POST_ARGS)
1986 {
1987 	struct roff_node *n;
1988 	char		 *datestr;
1989 
1990 	n = mdoc->last;
1991 	if (mdoc->meta.date != NULL) {
1992 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
1993 		    n->line, n->pos, "Dd");
1994 		free(mdoc->meta.date);
1995 	} else if (mdoc->flags & MDOC_PBODY)
1996 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
1997 		    n->line, n->pos, "Dd");
1998 	else if (mdoc->meta.title != NULL)
1999 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2000 		    n->line, n->pos, "Dd after Dt");
2001 	else if (mdoc->meta.os != NULL)
2002 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2003 		    n->line, n->pos, "Dd after Os");
2004 
2005 	if (n->child == NULL || n->child->string[0] == '\0') {
2006 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2007 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2008 		goto out;
2009 	}
2010 
2011 	datestr = NULL;
2012 	deroff(&datestr, n);
2013 	if (mdoc->quick)
2014 		mdoc->meta.date = datestr;
2015 	else {
2016 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
2017 		    datestr, n->line, n->pos);
2018 		free(datestr);
2019 	}
2020 out:
2021 	roff_node_delete(mdoc, n);
2022 }
2023 
2024 static void
2025 post_dt(POST_ARGS)
2026 {
2027 	struct roff_node *nn, *n;
2028 	const char	 *cp;
2029 	char		 *p;
2030 
2031 	n = mdoc->last;
2032 	if (mdoc->flags & MDOC_PBODY) {
2033 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2034 		    n->line, n->pos, "Dt");
2035 		goto out;
2036 	}
2037 
2038 	if (mdoc->meta.title != NULL)
2039 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2040 		    n->line, n->pos, "Dt");
2041 	else if (mdoc->meta.os != NULL)
2042 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2043 		    n->line, n->pos, "Dt after Os");
2044 
2045 	free(mdoc->meta.title);
2046 	free(mdoc->meta.msec);
2047 	free(mdoc->meta.vol);
2048 	free(mdoc->meta.arch);
2049 
2050 	mdoc->meta.title = NULL;
2051 	mdoc->meta.msec = NULL;
2052 	mdoc->meta.vol = NULL;
2053 	mdoc->meta.arch = NULL;
2054 
2055 	/* Mandatory first argument: title. */
2056 
2057 	nn = n->child;
2058 	if (nn == NULL || *nn->string == '\0') {
2059 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2060 		    mdoc->parse, n->line, n->pos, "Dt");
2061 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2062 	} else {
2063 		mdoc->meta.title = mandoc_strdup(nn->string);
2064 
2065 		/* Check that all characters are uppercase. */
2066 
2067 		for (p = nn->string; *p != '\0'; p++)
2068 			if (islower((unsigned char)*p)) {
2069 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2070 				    mdoc->parse, nn->line,
2071 				    nn->pos + (p - nn->string),
2072 				    "Dt %s", nn->string);
2073 				break;
2074 			}
2075 	}
2076 
2077 	/* Mandatory second argument: section.�*/
2078 
2079 	if (nn != NULL)
2080 		nn = nn->next;
2081 
2082 	if (nn == NULL) {
2083 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2084 		    mdoc->parse, n->line, n->pos,
2085 		    "Dt %s", mdoc->meta.title);
2086 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2087 		goto out;  /* msec and arch remain NULL. */
2088 	}
2089 
2090 	mdoc->meta.msec = mandoc_strdup(nn->string);
2091 
2092 	/* Infer volume title from section number. */
2093 
2094 	cp = mandoc_a2msec(nn->string);
2095 	if (cp == NULL) {
2096 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2097 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2098 		mdoc->meta.vol = mandoc_strdup(nn->string);
2099 	} else
2100 		mdoc->meta.vol = mandoc_strdup(cp);
2101 
2102 	/* Optional third argument: architecture. */
2103 
2104 	if ((nn = nn->next) == NULL)
2105 		goto out;
2106 
2107 	for (p = nn->string; *p != '\0'; p++)
2108 		*p = tolower((unsigned char)*p);
2109 	mdoc->meta.arch = mandoc_strdup(nn->string);
2110 
2111 	/* Ignore fourth and later arguments. */
2112 
2113 	if ((nn = nn->next) != NULL)
2114 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2115 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2116 
2117 out:
2118 	roff_node_delete(mdoc, n);
2119 }
2120 
2121 static void
2122 post_bx(POST_ARGS)
2123 {
2124 	struct roff_node	*n;
2125 
2126 	/*
2127 	 * Make `Bx's second argument always start with an uppercase
2128 	 * letter.  Groff checks if it's an "accepted" term, but we just
2129 	 * uppercase blindly.
2130 	 */
2131 
2132 	if ((n = mdoc->last->child) != NULL && (n = n->next) != NULL)
2133 		*n->string = (char)toupper((unsigned char)*n->string);
2134 }
2135 
2136 static void
2137 post_os(POST_ARGS)
2138 {
2139 #ifndef OSNAME
2140 	struct utsname	  utsname;
2141 	static char	 *defbuf;
2142 #endif
2143 	struct roff_node *n;
2144 
2145 	n = mdoc->last;
2146 	if (mdoc->meta.os != NULL)
2147 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2148 		    n->line, n->pos, "Os");
2149 	else if (mdoc->flags & MDOC_PBODY)
2150 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2151 		    n->line, n->pos, "Os");
2152 
2153 	/*
2154 	 * Set the operating system by way of the `Os' macro.
2155 	 * The order of precedence is:
2156 	 * 1. the argument of the `Os' macro, unless empty
2157 	 * 2. the -Ios=foo command line argument, if provided
2158 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2159 	 * 4. "sysname release" from uname(3)
2160 	 */
2161 
2162 	free(mdoc->meta.os);
2163 	mdoc->meta.os = NULL;
2164 	deroff(&mdoc->meta.os, n);
2165 	if (mdoc->meta.os)
2166 		goto out;
2167 
2168 	if (mdoc->defos) {
2169 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2170 		goto out;
2171 	}
2172 
2173 #ifdef OSNAME
2174 	mdoc->meta.os = mandoc_strdup(OSNAME);
2175 #else /*!OSNAME */
2176 	if (defbuf == NULL) {
2177 		if (uname(&utsname) == -1) {
2178 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2179 			    n->line, n->pos, "Os");
2180 			defbuf = mandoc_strdup("UNKNOWN");
2181 		} else
2182 			mandoc_asprintf(&defbuf, "%s %s",
2183 			    utsname.sysname, utsname.release);
2184 	}
2185 	mdoc->meta.os = mandoc_strdup(defbuf);
2186 #endif /*!OSNAME*/
2187 
2188 out:
2189 	roff_node_delete(mdoc, n);
2190 }
2191 
2192 /*
2193  * If no argument is provided,
2194  * fill in the name of the current manual page.
2195  */
2196 static void
2197 post_ex(POST_ARGS)
2198 {
2199 	struct roff_node *n;
2200 
2201 	post_std(mdoc);
2202 
2203 	n = mdoc->last;
2204 	if (n->child != NULL)
2205 		return;
2206 
2207 	if (mdoc->meta.name == NULL) {
2208 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2209 		    n->line, n->pos, "Ex");
2210 		return;
2211 	}
2212 
2213 	mdoc->next = ROFF_NEXT_CHILD;
2214 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2215 	mdoc->last = n;
2216 }
2217 
2218 enum roff_sec
2219 mdoc_a2sec(const char *p)
2220 {
2221 	int		 i;
2222 
2223 	for (i = 0; i < (int)SEC__MAX; i++)
2224 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2225 			return (enum roff_sec)i;
2226 
2227 	return SEC_CUSTOM;
2228 }
2229 
2230 static size_t
2231 macro2len(int macro)
2232 {
2233 
2234 	switch (macro) {
2235 	case MDOC_Ad:
2236 		return 12;
2237 	case MDOC_Ao:
2238 		return 12;
2239 	case MDOC_An:
2240 		return 12;
2241 	case MDOC_Aq:
2242 		return 12;
2243 	case MDOC_Ar:
2244 		return 12;
2245 	case MDOC_Bo:
2246 		return 12;
2247 	case MDOC_Bq:
2248 		return 12;
2249 	case MDOC_Cd:
2250 		return 12;
2251 	case MDOC_Cm:
2252 		return 10;
2253 	case MDOC_Do:
2254 		return 10;
2255 	case MDOC_Dq:
2256 		return 12;
2257 	case MDOC_Dv:
2258 		return 12;
2259 	case MDOC_Eo:
2260 		return 12;
2261 	case MDOC_Em:
2262 		return 10;
2263 	case MDOC_Er:
2264 		return 17;
2265 	case MDOC_Ev:
2266 		return 15;
2267 	case MDOC_Fa:
2268 		return 12;
2269 	case MDOC_Fl:
2270 		return 10;
2271 	case MDOC_Fo:
2272 		return 16;
2273 	case MDOC_Fn:
2274 		return 16;
2275 	case MDOC_Ic:
2276 		return 10;
2277 	case MDOC_Li:
2278 		return 16;
2279 	case MDOC_Ms:
2280 		return 6;
2281 	case MDOC_Nm:
2282 		return 10;
2283 	case MDOC_No:
2284 		return 12;
2285 	case MDOC_Oo:
2286 		return 10;
2287 	case MDOC_Op:
2288 		return 14;
2289 	case MDOC_Pa:
2290 		return 32;
2291 	case MDOC_Pf:
2292 		return 12;
2293 	case MDOC_Po:
2294 		return 12;
2295 	case MDOC_Pq:
2296 		return 12;
2297 	case MDOC_Ql:
2298 		return 16;
2299 	case MDOC_Qo:
2300 		return 12;
2301 	case MDOC_So:
2302 		return 12;
2303 	case MDOC_Sq:
2304 		return 12;
2305 	case MDOC_Sy:
2306 		return 6;
2307 	case MDOC_Sx:
2308 		return 16;
2309 	case MDOC_Tn:
2310 		return 10;
2311 	case MDOC_Va:
2312 		return 12;
2313 	case MDOC_Vt:
2314 		return 12;
2315 	case MDOC_Xr:
2316 		return 10;
2317 	default:
2318 		break;
2319 	};
2320 	return 0;
2321 }
2322