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