xref: /openbsd-src/usr.bin/mandoc/mdoc_validate.c (revision d59bb9942320b767f2a19aaa7690c8c6e30b724c)
1 /*	$OpenBSD: mdoc_validate.c,v 1.234 2017/02/06 03:41:44 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 	if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) ||
1006 	    (n->child != NULL && n->child->type == ROFFT_TEXT) ||
1007 	    mdoc->meta.name == NULL)
1008 		return;
1009 
1010 	mdoc->next = ROFF_NEXT_CHILD;
1011 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1012 	mdoc->last->flags |= NODE_NOSRC;
1013 	mdoc->last = n;
1014 }
1015 
1016 static void
1017 post_nd(POST_ARGS)
1018 {
1019 	struct roff_node	*n;
1020 
1021 	n = mdoc->last;
1022 
1023 	if (n->type != ROFFT_BODY)
1024 		return;
1025 
1026 	if (n->child == NULL)
1027 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1028 		    n->line, n->pos, "Nd");
1029 
1030 	post_hyph(mdoc);
1031 }
1032 
1033 static void
1034 post_display(POST_ARGS)
1035 {
1036 	struct roff_node *n, *np;
1037 
1038 	n = mdoc->last;
1039 	switch (n->type) {
1040 	case ROFFT_BODY:
1041 		if (n->end != ENDBODY_NOT) {
1042 			if (n->tok == MDOC_Bd &&
1043 			    n->body->parent->args == NULL)
1044 				roff_node_delete(mdoc, n);
1045 		} else if (n->child == NULL)
1046 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1047 			    n->line, n->pos, mdoc_macronames[n->tok]);
1048 		else if (n->tok == MDOC_D1)
1049 			post_hyph(mdoc);
1050 		break;
1051 	case ROFFT_BLOCK:
1052 		if (n->tok == MDOC_Bd) {
1053 			if (n->args == NULL) {
1054 				mandoc_msg(MANDOCERR_BD_NOARG,
1055 				    mdoc->parse, n->line, n->pos, "Bd");
1056 				mdoc->next = ROFF_NEXT_SIBLING;
1057 				while (n->body->child != NULL)
1058 					mdoc_node_relink(mdoc,
1059 					    n->body->child);
1060 				roff_node_delete(mdoc, n);
1061 				break;
1062 			}
1063 			post_bd(mdoc);
1064 			post_prevpar(mdoc);
1065 		}
1066 		for (np = n->parent; np != NULL; np = np->parent) {
1067 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1068 				mandoc_vmsg(MANDOCERR_BD_NEST,
1069 				    mdoc->parse, n->line, n->pos,
1070 				    "%s in Bd", mdoc_macronames[n->tok]);
1071 				break;
1072 			}
1073 		}
1074 		break;
1075 	default:
1076 		break;
1077 	}
1078 }
1079 
1080 static void
1081 post_defaults(POST_ARGS)
1082 {
1083 	struct roff_node *nn;
1084 
1085 	/*
1086 	 * The `Ar' defaults to "file ..." if no value is provided as an
1087 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1088 	 * gets an empty string.
1089 	 */
1090 
1091 	if (mdoc->last->child != NULL)
1092 		return;
1093 
1094 	nn = mdoc->last;
1095 
1096 	switch (nn->tok) {
1097 	case MDOC_Ar:
1098 		mdoc->next = ROFF_NEXT_CHILD;
1099 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1100 		mdoc->last->flags |= NODE_NOSRC;
1101 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1102 		mdoc->last->flags |= NODE_NOSRC;
1103 		break;
1104 	case MDOC_Pa:
1105 	case MDOC_Mt:
1106 		mdoc->next = ROFF_NEXT_CHILD;
1107 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1108 		mdoc->last->flags |= NODE_NOSRC;
1109 		break;
1110 	default:
1111 		abort();
1112 	}
1113 	mdoc->last = nn;
1114 }
1115 
1116 static void
1117 post_at(POST_ARGS)
1118 {
1119 	struct roff_node	*n, *nch;
1120 	const char		*att;
1121 
1122 	n = mdoc->last;
1123 	nch = n->child;
1124 
1125 	/*
1126 	 * If we have a child, look it up in the standard keys.  If a
1127 	 * key exist, use that instead of the child; if it doesn't,
1128 	 * prefix "AT&T UNIX " to the existing data.
1129 	 */
1130 
1131 	att = NULL;
1132 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1133 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1134 		    nch->line, nch->pos, "At %s", nch->string);
1135 
1136 	mdoc->next = ROFF_NEXT_CHILD;
1137 	if (att != NULL) {
1138 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
1139 		nch->flags |= NODE_NOPRT;
1140 	} else
1141 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1142 	mdoc->last->flags |= NODE_NOSRC;
1143 	mdoc->last = n;
1144 }
1145 
1146 static void
1147 post_an(POST_ARGS)
1148 {
1149 	struct roff_node *np, *nch;
1150 
1151 	post_an_norm(mdoc);
1152 
1153 	np = mdoc->last;
1154 	nch = np->child;
1155 	if (np->norm->An.auth == AUTH__NONE) {
1156 		if (nch == NULL)
1157 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1158 			    np->line, np->pos, "An");
1159 	} else if (nch != NULL)
1160 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1161 		    nch->line, nch->pos, "An ... %s", nch->string);
1162 }
1163 
1164 static void
1165 post_en(POST_ARGS)
1166 {
1167 
1168 	post_obsolete(mdoc);
1169 	if (mdoc->last->type == ROFFT_BLOCK)
1170 		mdoc->last->norm->Es = mdoc->last_es;
1171 }
1172 
1173 static void
1174 post_es(POST_ARGS)
1175 {
1176 
1177 	post_obsolete(mdoc);
1178 	mdoc->last_es = mdoc->last;
1179 }
1180 
1181 static void
1182 post_xx(POST_ARGS)
1183 {
1184 	struct roff_node	*n;
1185 	const char		*os;
1186 
1187 	n = mdoc->last;
1188 	switch (n->tok) {
1189 	case MDOC_Bsx:
1190 		os = "BSD/OS";
1191 		break;
1192 	case MDOC_Dx:
1193 		os = "DragonFly";
1194 		break;
1195 	case MDOC_Fx:
1196 		os = "FreeBSD";
1197 		break;
1198 	case MDOC_Nx:
1199 		os = "NetBSD";
1200 		break;
1201 	case MDOC_Ox:
1202 		os = "OpenBSD";
1203 		break;
1204 	case MDOC_Ux:
1205 		os = "UNIX";
1206 		break;
1207 	default:
1208 		abort();
1209 	}
1210 	mdoc->next = ROFF_NEXT_CHILD;
1211 	roff_word_alloc(mdoc, n->line, n->pos, os);
1212 	mdoc->last->flags |= NODE_NOSRC;
1213 	mdoc->last = n;
1214 }
1215 
1216 static void
1217 post_it(POST_ARGS)
1218 {
1219 	struct roff_node *nbl, *nit, *nch;
1220 	int		  i, cols;
1221 	enum mdoc_list	  lt;
1222 
1223 	post_prevpar(mdoc);
1224 
1225 	nit = mdoc->last;
1226 	if (nit->type != ROFFT_BLOCK)
1227 		return;
1228 
1229 	nbl = nit->parent->parent;
1230 	lt = nbl->norm->Bl.type;
1231 
1232 	switch (lt) {
1233 	case LIST_tag:
1234 	case LIST_hang:
1235 	case LIST_ohang:
1236 	case LIST_inset:
1237 	case LIST_diag:
1238 		if (nit->head->child == NULL)
1239 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1240 			    mdoc->parse, nit->line, nit->pos,
1241 			    "Bl -%s It",
1242 			    mdoc_argnames[nbl->args->argv[0].arg]);
1243 		break;
1244 	case LIST_bullet:
1245 	case LIST_dash:
1246 	case LIST_enum:
1247 	case LIST_hyphen:
1248 		if (nit->body == NULL || nit->body->child == NULL)
1249 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1250 			    mdoc->parse, nit->line, nit->pos,
1251 			    "Bl -%s It",
1252 			    mdoc_argnames[nbl->args->argv[0].arg]);
1253 		/* FALLTHROUGH */
1254 	case LIST_item:
1255 		if ((nch = nit->head->child) != NULL)
1256 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1257 			    mdoc->parse, nit->line, nit->pos,
1258 			    "It %s", nch->string == NULL ?
1259 			    mdoc_macronames[nch->tok] : nch->string);
1260 		break;
1261 	case LIST_column:
1262 		cols = (int)nbl->norm->Bl.ncols;
1263 
1264 		assert(nit->head->child == NULL);
1265 
1266 		i = 0;
1267 		for (nch = nit->child; nch != NULL; nch = nch->next)
1268 			if (nch->type == ROFFT_BODY)
1269 				i++;
1270 
1271 		if (i < cols || i > cols + 1)
1272 			mandoc_vmsg(MANDOCERR_BL_COL,
1273 			    mdoc->parse, nit->line, nit->pos,
1274 			    "%d columns, %d cells", cols, i);
1275 		break;
1276 	default:
1277 		abort();
1278 	}
1279 }
1280 
1281 static void
1282 post_bl_block(POST_ARGS)
1283 {
1284 	struct roff_node *n, *ni, *nc;
1285 
1286 	post_prevpar(mdoc);
1287 
1288 	n = mdoc->last;
1289 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1290 		if (ni->body == NULL)
1291 			continue;
1292 		nc = ni->body->last;
1293 		while (nc != NULL) {
1294 			switch (nc->tok) {
1295 			case MDOC_Pp:
1296 			case MDOC_Lp:
1297 			case MDOC_br:
1298 				break;
1299 			default:
1300 				nc = NULL;
1301 				continue;
1302 			}
1303 			if (ni->next == NULL) {
1304 				mandoc_msg(MANDOCERR_PAR_MOVE,
1305 				    mdoc->parse, nc->line, nc->pos,
1306 				    mdoc_macronames[nc->tok]);
1307 				mdoc_node_relink(mdoc, nc);
1308 			} else if (n->norm->Bl.comp == 0 &&
1309 			    n->norm->Bl.type != LIST_column) {
1310 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1311 				    mdoc->parse, nc->line, nc->pos,
1312 				    "%s before It",
1313 				    mdoc_macronames[nc->tok]);
1314 				roff_node_delete(mdoc, nc);
1315 			} else
1316 				break;
1317 			nc = ni->body->last;
1318 		}
1319 	}
1320 }
1321 
1322 /*
1323  * If the argument of -offset or -width is a macro,
1324  * replace it with the associated default width.
1325  */
1326 void
1327 rewrite_macro2len(char **arg)
1328 {
1329 	size_t		  width;
1330 	int		  tok;
1331 
1332 	if (*arg == NULL)
1333 		return;
1334 	else if ( ! strcmp(*arg, "Ds"))
1335 		width = 6;
1336 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1337 		return;
1338 	else
1339 		width = macro2len(tok);
1340 
1341 	free(*arg);
1342 	mandoc_asprintf(arg, "%zun", width);
1343 }
1344 
1345 static void
1346 post_bl_head(POST_ARGS)
1347 {
1348 	struct roff_node *nbl, *nh, *nch, *nnext;
1349 	struct mdoc_argv *argv;
1350 	int		  i, j;
1351 
1352 	post_bl_norm(mdoc);
1353 
1354 	nh = mdoc->last;
1355 	if (nh->norm->Bl.type != LIST_column) {
1356 		if ((nch = nh->child) == NULL)
1357 			return;
1358 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1359 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1360 		while (nch != NULL) {
1361 			roff_node_delete(mdoc, nch);
1362 			nch = nh->child;
1363 		}
1364 		return;
1365 	}
1366 
1367 	/*
1368 	 * Append old-style lists, where the column width specifiers
1369 	 * trail as macro parameters, to the new-style ("normal-form")
1370 	 * lists where they're argument values following -column.
1371 	 */
1372 
1373 	if (nh->child == NULL)
1374 		return;
1375 
1376 	nbl = nh->parent;
1377 	for (j = 0; j < (int)nbl->args->argc; j++)
1378 		if (nbl->args->argv[j].arg == MDOC_Column)
1379 			break;
1380 
1381 	assert(j < (int)nbl->args->argc);
1382 
1383 	/*
1384 	 * Accommodate for new-style groff column syntax.  Shuffle the
1385 	 * child nodes, all of which must be TEXT, as arguments for the
1386 	 * column field.  Then, delete the head children.
1387 	 */
1388 
1389 	argv = nbl->args->argv + j;
1390 	i = argv->sz;
1391 	for (nch = nh->child; nch != NULL; nch = nch->next)
1392 		argv->sz++;
1393 	argv->value = mandoc_reallocarray(argv->value,
1394 	    argv->sz, sizeof(char *));
1395 
1396 	nh->norm->Bl.ncols = argv->sz;
1397 	nh->norm->Bl.cols = (void *)argv->value;
1398 
1399 	for (nch = nh->child; nch != NULL; nch = nnext) {
1400 		argv->value[i++] = nch->string;
1401 		nch->string = NULL;
1402 		nnext = nch->next;
1403 		roff_node_delete(NULL, nch);
1404 	}
1405 	nh->child = NULL;
1406 }
1407 
1408 static void
1409 post_bl(POST_ARGS)
1410 {
1411 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1412 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1413 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1414 
1415 	nbody = mdoc->last;
1416 	switch (nbody->type) {
1417 	case ROFFT_BLOCK:
1418 		post_bl_block(mdoc);
1419 		return;
1420 	case ROFFT_HEAD:
1421 		post_bl_head(mdoc);
1422 		return;
1423 	case ROFFT_BODY:
1424 		break;
1425 	default:
1426 		return;
1427 	}
1428 	if (nbody->end != ENDBODY_NOT)
1429 		return;
1430 
1431 	nchild = nbody->child;
1432 	if (nchild == NULL) {
1433 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1434 		    nbody->line, nbody->pos, "Bl");
1435 		return;
1436 	}
1437 	while (nchild != NULL) {
1438 		nnext = nchild->next;
1439 		if (nchild->tok == MDOC_It ||
1440 		    (nchild->tok == MDOC_Sm &&
1441 		     nnext != NULL && nnext->tok == MDOC_It)) {
1442 			nchild = nnext;
1443 			continue;
1444 		}
1445 
1446 		/*
1447 		 * In .Bl -column, the first rows may be implicit,
1448 		 * that is, they may not start with .It macros.
1449 		 * Such rows may be followed by nodes generated on the
1450 		 * roff level, for example .TS, which cannot be moved
1451 		 * out of the list.  In that case, wrap such roff nodes
1452 		 * into an implicit row.
1453 		 */
1454 
1455 		if (nchild->prev != NULL) {
1456 			mdoc->last = nchild;
1457 			mdoc->next = ROFF_NEXT_SIBLING;
1458 			roff_block_alloc(mdoc, nchild->line,
1459 			    nchild->pos, MDOC_It);
1460 			roff_head_alloc(mdoc, nchild->line,
1461 			    nchild->pos, MDOC_It);
1462 			mdoc->next = ROFF_NEXT_SIBLING;
1463 			roff_body_alloc(mdoc, nchild->line,
1464 			    nchild->pos, MDOC_It);
1465 			while (nchild->tok != MDOC_It) {
1466 				mdoc_node_relink(mdoc, nchild);
1467 				if ((nchild = nnext) == NULL)
1468 					break;
1469 				nnext = nchild->next;
1470 				mdoc->next = ROFF_NEXT_SIBLING;
1471 			}
1472 			mdoc->last = nbody;
1473 			continue;
1474 		}
1475 
1476 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1477 		    nchild->line, nchild->pos,
1478 		    mdoc_macronames[nchild->tok]);
1479 
1480 		/*
1481 		 * Move the node out of the Bl block.
1482 		 * First, collect all required node pointers.
1483 		 */
1484 
1485 		nblock  = nbody->parent;
1486 		nprev   = nblock->prev;
1487 		nparent = nblock->parent;
1488 
1489 		/*
1490 		 * Unlink this child.
1491 		 */
1492 
1493 		nbody->child = nnext;
1494 		if (nnext == NULL)
1495 			nbody->last  = NULL;
1496 		else
1497 			nnext->prev = NULL;
1498 
1499 		/*
1500 		 * Relink this child.
1501 		 */
1502 
1503 		nchild->parent = nparent;
1504 		nchild->prev   = nprev;
1505 		nchild->next   = nblock;
1506 
1507 		nblock->prev = nchild;
1508 		if (nprev == NULL)
1509 			nparent->child = nchild;
1510 		else
1511 			nprev->next = nchild;
1512 
1513 		nchild = nnext;
1514 	}
1515 }
1516 
1517 static void
1518 post_bk(POST_ARGS)
1519 {
1520 	struct roff_node	*n;
1521 
1522 	n = mdoc->last;
1523 
1524 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1525 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1526 		    mdoc->parse, n->line, n->pos, "Bk");
1527 		roff_node_delete(mdoc, n);
1528 	}
1529 }
1530 
1531 static void
1532 post_sm(POST_ARGS)
1533 {
1534 	struct roff_node	*nch;
1535 
1536 	nch = mdoc->last->child;
1537 
1538 	if (nch == NULL) {
1539 		mdoc->flags ^= MDOC_SMOFF;
1540 		return;
1541 	}
1542 
1543 	assert(nch->type == ROFFT_TEXT);
1544 
1545 	if ( ! strcmp(nch->string, "on")) {
1546 		mdoc->flags &= ~MDOC_SMOFF;
1547 		return;
1548 	}
1549 	if ( ! strcmp(nch->string, "off")) {
1550 		mdoc->flags |= MDOC_SMOFF;
1551 		return;
1552 	}
1553 
1554 	mandoc_vmsg(MANDOCERR_SM_BAD,
1555 	    mdoc->parse, nch->line, nch->pos,
1556 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1557 	mdoc_node_relink(mdoc, nch);
1558 	return;
1559 }
1560 
1561 static void
1562 post_root(POST_ARGS)
1563 {
1564 	struct roff_node *n;
1565 
1566 	/* Add missing prologue data. */
1567 
1568 	if (mdoc->meta.date == NULL)
1569 		mdoc->meta.date = mdoc->quick ?
1570 		    mandoc_strdup("") :
1571 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
1572 
1573 	if (mdoc->meta.title == NULL) {
1574 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1575 		    mdoc->parse, 0, 0, "EOF");
1576 		mdoc->meta.title = mandoc_strdup("UNTITLED");
1577 	}
1578 
1579 	if (mdoc->meta.vol == NULL)
1580 		mdoc->meta.vol = mandoc_strdup("LOCAL");
1581 
1582 	if (mdoc->meta.os == NULL) {
1583 		mandoc_msg(MANDOCERR_OS_MISSING,
1584 		    mdoc->parse, 0, 0, NULL);
1585 		mdoc->meta.os = mandoc_strdup("");
1586 	}
1587 
1588 	/* Check that we begin with a proper `Sh'. */
1589 
1590 	n = mdoc->first->child;
1591 	while (n != NULL && n->tok != TOKEN_NONE &&
1592 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1593 		n = n->next;
1594 
1595 	if (n == NULL)
1596 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1597 	else if (n->tok != MDOC_Sh)
1598 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1599 		    n->line, n->pos, mdoc_macronames[n->tok]);
1600 }
1601 
1602 static void
1603 post_rs(POST_ARGS)
1604 {
1605 	struct roff_node *np, *nch, *next, *prev;
1606 	int		  i, j;
1607 
1608 	np = mdoc->last;
1609 
1610 	if (np->type != ROFFT_BODY)
1611 		return;
1612 
1613 	if (np->child == NULL) {
1614 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1615 		    np->line, np->pos, "Rs");
1616 		return;
1617 	}
1618 
1619 	/*
1620 	 * The full `Rs' block needs special handling to order the
1621 	 * sub-elements according to `rsord'.  Pick through each element
1622 	 * and correctly order it.  This is an insertion sort.
1623 	 */
1624 
1625 	next = NULL;
1626 	for (nch = np->child->next; nch != NULL; nch = next) {
1627 		/* Determine order number of this child. */
1628 		for (i = 0; i < RSORD_MAX; i++)
1629 			if (rsord[i] == nch->tok)
1630 				break;
1631 
1632 		if (i == RSORD_MAX) {
1633 			mandoc_msg(MANDOCERR_RS_BAD,
1634 			    mdoc->parse, nch->line, nch->pos,
1635 			    mdoc_macronames[nch->tok]);
1636 			i = -1;
1637 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1638 			np->norm->Rs.quote_T++;
1639 
1640 		/*
1641 		 * Remove this child from the chain.  This somewhat
1642 		 * repeats roff_node_unlink(), but since we're
1643 		 * just re-ordering, there's no need for the
1644 		 * full unlink process.
1645 		 */
1646 
1647 		if ((next = nch->next) != NULL)
1648 			next->prev = nch->prev;
1649 
1650 		if ((prev = nch->prev) != NULL)
1651 			prev->next = nch->next;
1652 
1653 		nch->prev = nch->next = NULL;
1654 
1655 		/*
1656 		 * Scan back until we reach a node that's
1657 		 * to be ordered before this child.
1658 		 */
1659 
1660 		for ( ; prev ; prev = prev->prev) {
1661 			/* Determine order of `prev'. */
1662 			for (j = 0; j < RSORD_MAX; j++)
1663 				if (rsord[j] == prev->tok)
1664 					break;
1665 			if (j == RSORD_MAX)
1666 				j = -1;
1667 
1668 			if (j <= i)
1669 				break;
1670 		}
1671 
1672 		/*
1673 		 * Set this child back into its correct place
1674 		 * in front of the `prev' node.
1675 		 */
1676 
1677 		nch->prev = prev;
1678 
1679 		if (prev == NULL) {
1680 			np->child->prev = nch;
1681 			nch->next = np->child;
1682 			np->child = nch;
1683 		} else {
1684 			if (prev->next)
1685 				prev->next->prev = nch;
1686 			nch->next = prev->next;
1687 			prev->next = nch;
1688 		}
1689 	}
1690 }
1691 
1692 /*
1693  * For some arguments of some macros,
1694  * convert all breakable hyphens into ASCII_HYPH.
1695  */
1696 static void
1697 post_hyph(POST_ARGS)
1698 {
1699 	struct roff_node	*nch;
1700 	char			*cp;
1701 
1702 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1703 		if (nch->type != ROFFT_TEXT)
1704 			continue;
1705 		cp = nch->string;
1706 		if (*cp == '\0')
1707 			continue;
1708 		while (*(++cp) != '\0')
1709 			if (*cp == '-' &&
1710 			    isalpha((unsigned char)cp[-1]) &&
1711 			    isalpha((unsigned char)cp[1]))
1712 				*cp = ASCII_HYPH;
1713 	}
1714 }
1715 
1716 static void
1717 post_ns(POST_ARGS)
1718 {
1719 
1720 	if (mdoc->last->flags & NODE_LINE)
1721 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1722 		    mdoc->last->line, mdoc->last->pos, NULL);
1723 }
1724 
1725 static void
1726 post_sh(POST_ARGS)
1727 {
1728 
1729 	post_ignpar(mdoc);
1730 
1731 	switch (mdoc->last->type) {
1732 	case ROFFT_HEAD:
1733 		post_sh_head(mdoc);
1734 		break;
1735 	case ROFFT_BODY:
1736 		switch (mdoc->lastsec)  {
1737 		case SEC_NAME:
1738 			post_sh_name(mdoc);
1739 			break;
1740 		case SEC_SEE_ALSO:
1741 			post_sh_see_also(mdoc);
1742 			break;
1743 		case SEC_AUTHORS:
1744 			post_sh_authors(mdoc);
1745 			break;
1746 		default:
1747 			break;
1748 		}
1749 		break;
1750 	default:
1751 		break;
1752 	}
1753 }
1754 
1755 static void
1756 post_sh_name(POST_ARGS)
1757 {
1758 	struct roff_node *n;
1759 	int hasnm, hasnd;
1760 
1761 	hasnm = hasnd = 0;
1762 
1763 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1764 		switch (n->tok) {
1765 		case MDOC_Nm:
1766 			if (hasnm && n->child != NULL)
1767 				mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
1768 				    mdoc->parse, n->line, n->pos,
1769 				    "Nm %s", n->child->string);
1770 			hasnm = 1;
1771 			continue;
1772 		case MDOC_Nd:
1773 			hasnd = 1;
1774 			if (n->next != NULL)
1775 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1776 				    mdoc->parse, n->line, n->pos, NULL);
1777 			break;
1778 		case TOKEN_NONE:
1779 			if (n->type == ROFFT_TEXT &&
1780 			    n->string[0] == ',' && n->string[1] == '\0' &&
1781 			    n->next != NULL && n->next->tok == MDOC_Nm) {
1782 				n = n->next;
1783 				continue;
1784 			}
1785 			/* FALLTHROUGH */
1786 		default:
1787 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1788 			    n->line, n->pos, mdoc_macronames[n->tok]);
1789 			continue;
1790 		}
1791 		break;
1792 	}
1793 
1794 	if ( ! hasnm)
1795 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1796 		    mdoc->last->line, mdoc->last->pos, NULL);
1797 	if ( ! hasnd)
1798 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1799 		    mdoc->last->line, mdoc->last->pos, NULL);
1800 }
1801 
1802 static void
1803 post_sh_see_also(POST_ARGS)
1804 {
1805 	const struct roff_node	*n;
1806 	const char		*name, *sec;
1807 	const char		*lastname, *lastsec, *lastpunct;
1808 	int			 cmp;
1809 
1810 	n = mdoc->last->child;
1811 	lastname = lastsec = lastpunct = NULL;
1812 	while (n != NULL) {
1813 		if (n->tok != MDOC_Xr ||
1814 		    n->child == NULL ||
1815 		    n->child->next == NULL)
1816 			break;
1817 
1818 		/* Process one .Xr node. */
1819 
1820 		name = n->child->string;
1821 		sec = n->child->next->string;
1822 		if (lastsec != NULL) {
1823 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1824 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1825 				    mdoc->parse, n->line, n->pos,
1826 				    "%s before %s(%s)", lastpunct,
1827 				    name, sec);
1828 			cmp = strcmp(lastsec, sec);
1829 			if (cmp > 0)
1830 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1831 				    mdoc->parse, n->line, n->pos,
1832 				    "%s(%s) after %s(%s)", name,
1833 				    sec, lastname, lastsec);
1834 			else if (cmp == 0 &&
1835 			    strcasecmp(lastname, name) > 0)
1836 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1837 				    mdoc->parse, n->line, n->pos,
1838 				    "%s after %s", name, lastname);
1839 		}
1840 		lastname = name;
1841 		lastsec = sec;
1842 
1843 		/* Process the following node. */
1844 
1845 		n = n->next;
1846 		if (n == NULL)
1847 			break;
1848 		if (n->tok == MDOC_Xr) {
1849 			lastpunct = "none";
1850 			continue;
1851 		}
1852 		if (n->type != ROFFT_TEXT)
1853 			break;
1854 		for (name = n->string; *name != '\0'; name++)
1855 			if (isalpha((const unsigned char)*name))
1856 				return;
1857 		lastpunct = n->string;
1858 		if (n->next == NULL)
1859 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1860 			    n->line, n->pos, "%s after %s(%s)",
1861 			    lastpunct, lastname, lastsec);
1862 		n = n->next;
1863 	}
1864 }
1865 
1866 static int
1867 child_an(const struct roff_node *n)
1868 {
1869 
1870 	for (n = n->child; n != NULL; n = n->next)
1871 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1872 			return 1;
1873 	return 0;
1874 }
1875 
1876 static void
1877 post_sh_authors(POST_ARGS)
1878 {
1879 
1880 	if ( ! child_an(mdoc->last))
1881 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1882 		    mdoc->last->line, mdoc->last->pos, NULL);
1883 }
1884 
1885 static void
1886 post_sh_head(POST_ARGS)
1887 {
1888 	struct roff_node	*nch;
1889 	const char		*goodsec;
1890 	enum roff_sec		 sec;
1891 
1892 	/*
1893 	 * Process a new section.  Sections are either "named" or
1894 	 * "custom".  Custom sections are user-defined, while named ones
1895 	 * follow a conventional order and may only appear in certain
1896 	 * manual sections.
1897 	 */
1898 
1899 	sec = mdoc->last->sec;
1900 
1901 	/* The NAME should be first. */
1902 
1903 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1904 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1905 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
1906 		    sec != SEC_CUSTOM ? secnames[sec] :
1907 		    (nch = mdoc->last->child) == NULL ? "" :
1908 		    nch->type == ROFFT_TEXT ? nch->string :
1909 		    mdoc_macronames[nch->tok]);
1910 
1911 	/* The SYNOPSIS gets special attention in other areas. */
1912 
1913 	if (sec == SEC_SYNOPSIS) {
1914 		roff_setreg(mdoc->roff, "nS", 1, '=');
1915 		mdoc->flags |= MDOC_SYNOPSIS;
1916 	} else {
1917 		roff_setreg(mdoc->roff, "nS", 0, '=');
1918 		mdoc->flags &= ~MDOC_SYNOPSIS;
1919 	}
1920 
1921 	/* Mark our last section. */
1922 
1923 	mdoc->lastsec = sec;
1924 
1925 	/* We don't care about custom sections after this. */
1926 
1927 	if (sec == SEC_CUSTOM)
1928 		return;
1929 
1930 	/*
1931 	 * Check whether our non-custom section is being repeated or is
1932 	 * out of order.
1933 	 */
1934 
1935 	if (sec == mdoc->lastnamed)
1936 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1937 		    mdoc->last->line, mdoc->last->pos,
1938 		    "Sh %s", secnames[sec]);
1939 
1940 	if (sec < mdoc->lastnamed)
1941 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1942 		    mdoc->last->line, mdoc->last->pos,
1943 		    "Sh %s", secnames[sec]);
1944 
1945 	/* Mark the last named section. */
1946 
1947 	mdoc->lastnamed = sec;
1948 
1949 	/* Check particular section/manual conventions. */
1950 
1951 	if (mdoc->meta.msec == NULL)
1952 		return;
1953 
1954 	goodsec = NULL;
1955 	switch (sec) {
1956 	case SEC_ERRORS:
1957 		if (*mdoc->meta.msec == '4')
1958 			break;
1959 		goodsec = "2, 3, 4, 9";
1960 		/* FALLTHROUGH */
1961 	case SEC_RETURN_VALUES:
1962 	case SEC_LIBRARY:
1963 		if (*mdoc->meta.msec == '2')
1964 			break;
1965 		if (*mdoc->meta.msec == '3')
1966 			break;
1967 		if (NULL == goodsec)
1968 			goodsec = "2, 3, 9";
1969 		/* FALLTHROUGH */
1970 	case SEC_CONTEXT:
1971 		if (*mdoc->meta.msec == '9')
1972 			break;
1973 		if (NULL == goodsec)
1974 			goodsec = "9";
1975 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1976 		    mdoc->last->line, mdoc->last->pos,
1977 		    "Sh %s for %s only", secnames[sec], goodsec);
1978 		break;
1979 	default:
1980 		break;
1981 	}
1982 }
1983 
1984 static void
1985 post_xr(POST_ARGS)
1986 {
1987 	struct roff_node *n, *nch;
1988 
1989 	n = mdoc->last;
1990 	nch = n->child;
1991 	if (nch->next == NULL) {
1992 		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
1993 		    n->line, n->pos, "Xr %s", nch->string);
1994 		return;
1995 	}
1996 	assert(nch->next == n->last);
1997 }
1998 
1999 static void
2000 post_ignpar(POST_ARGS)
2001 {
2002 	struct roff_node *np;
2003 
2004 	switch (mdoc->last->type) {
2005 	case ROFFT_HEAD:
2006 		post_hyph(mdoc);
2007 		return;
2008 	case ROFFT_BODY:
2009 		break;
2010 	default:
2011 		return;
2012 	}
2013 
2014 	if ((np = mdoc->last->child) != NULL)
2015 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2016 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
2017 			    mdoc->parse, np->line, np->pos,
2018 			    "%s after %s", mdoc_macronames[np->tok],
2019 			    mdoc_macronames[mdoc->last->tok]);
2020 			roff_node_delete(mdoc, np);
2021 		}
2022 
2023 	if ((np = mdoc->last->last) != NULL)
2024 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2025 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2026 			    np->line, np->pos, "%s at the end of %s",
2027 			    mdoc_macronames[np->tok],
2028 			    mdoc_macronames[mdoc->last->tok]);
2029 			roff_node_delete(mdoc, np);
2030 		}
2031 }
2032 
2033 static void
2034 post_prevpar(POST_ARGS)
2035 {
2036 	struct roff_node *n;
2037 
2038 	n = mdoc->last;
2039 	if (NULL == n->prev)
2040 		return;
2041 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2042 		return;
2043 
2044 	/*
2045 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2046 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2047 	 */
2048 
2049 	if (n->prev->tok != MDOC_Pp &&
2050 	    n->prev->tok != MDOC_Lp &&
2051 	    n->prev->tok != MDOC_br)
2052 		return;
2053 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2054 		return;
2055 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2056 		return;
2057 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2058 		return;
2059 
2060 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2061 	    n->prev->line, n->prev->pos,
2062 	    "%s before %s", mdoc_macronames[n->prev->tok],
2063 	    mdoc_macronames[n->tok]);
2064 	roff_node_delete(mdoc, n->prev);
2065 }
2066 
2067 static void
2068 post_par(POST_ARGS)
2069 {
2070 	struct roff_node *np;
2071 
2072 	np = mdoc->last;
2073 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
2074 		post_prevpar(mdoc);
2075 
2076 	if (np->tok == MDOC_sp) {
2077 		if (np->child != NULL && np->child->next != NULL)
2078 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2079 			    np->child->next->line, np->child->next->pos,
2080 			    "sp ... %s", np->child->next->string);
2081 	} else if (np->child != NULL)
2082 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2083 		    mdoc->parse, np->line, np->pos, "%s %s",
2084 		    mdoc_macronames[np->tok], np->child->string);
2085 
2086 	if ((np = mdoc->last->prev) == NULL) {
2087 		np = mdoc->last->parent;
2088 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2089 			return;
2090 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2091 	    (mdoc->last->tok != MDOC_br ||
2092 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
2093 		return;
2094 
2095 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2096 	    mdoc->last->line, mdoc->last->pos,
2097 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
2098 	    mdoc_macronames[np->tok]);
2099 	roff_node_delete(mdoc, mdoc->last);
2100 }
2101 
2102 static void
2103 post_dd(POST_ARGS)
2104 {
2105 	struct roff_node *n;
2106 	char		 *datestr;
2107 
2108 	n = mdoc->last;
2109 	n->flags |= NODE_NOPRT;
2110 
2111 	if (mdoc->meta.date != NULL) {
2112 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2113 		    n->line, n->pos, "Dd");
2114 		free(mdoc->meta.date);
2115 	} else if (mdoc->flags & MDOC_PBODY)
2116 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2117 		    n->line, n->pos, "Dd");
2118 	else if (mdoc->meta.title != NULL)
2119 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2120 		    n->line, n->pos, "Dd after Dt");
2121 	else if (mdoc->meta.os != NULL)
2122 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2123 		    n->line, n->pos, "Dd after Os");
2124 
2125 	if (n->child == NULL || n->child->string[0] == '\0') {
2126 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2127 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2128 		return;
2129 	}
2130 
2131 	datestr = NULL;
2132 	deroff(&datestr, n);
2133 	if (mdoc->quick)
2134 		mdoc->meta.date = datestr;
2135 	else {
2136 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
2137 		    datestr, n->line, n->pos);
2138 		free(datestr);
2139 	}
2140 }
2141 
2142 static void
2143 post_dt(POST_ARGS)
2144 {
2145 	struct roff_node *nn, *n;
2146 	const char	 *cp;
2147 	char		 *p;
2148 
2149 	n = mdoc->last;
2150 	n->flags |= NODE_NOPRT;
2151 
2152 	if (mdoc->flags & MDOC_PBODY) {
2153 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2154 		    n->line, n->pos, "Dt");
2155 		return;
2156 	}
2157 
2158 	if (mdoc->meta.title != NULL)
2159 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2160 		    n->line, n->pos, "Dt");
2161 	else if (mdoc->meta.os != NULL)
2162 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2163 		    n->line, n->pos, "Dt after Os");
2164 
2165 	free(mdoc->meta.title);
2166 	free(mdoc->meta.msec);
2167 	free(mdoc->meta.vol);
2168 	free(mdoc->meta.arch);
2169 
2170 	mdoc->meta.title = NULL;
2171 	mdoc->meta.msec = NULL;
2172 	mdoc->meta.vol = NULL;
2173 	mdoc->meta.arch = NULL;
2174 
2175 	/* Mandatory first argument: title. */
2176 
2177 	nn = n->child;
2178 	if (nn == NULL || *nn->string == '\0') {
2179 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2180 		    mdoc->parse, n->line, n->pos, "Dt");
2181 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2182 	} else {
2183 		mdoc->meta.title = mandoc_strdup(nn->string);
2184 
2185 		/* Check that all characters are uppercase. */
2186 
2187 		for (p = nn->string; *p != '\0'; p++)
2188 			if (islower((unsigned char)*p)) {
2189 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2190 				    mdoc->parse, nn->line,
2191 				    nn->pos + (p - nn->string),
2192 				    "Dt %s", nn->string);
2193 				break;
2194 			}
2195 	}
2196 
2197 	/* Mandatory second argument: section. */
2198 
2199 	if (nn != NULL)
2200 		nn = nn->next;
2201 
2202 	if (nn == NULL) {
2203 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2204 		    mdoc->parse, n->line, n->pos,
2205 		    "Dt %s", mdoc->meta.title);
2206 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2207 		return;  /* msec and arch remain NULL. */
2208 	}
2209 
2210 	mdoc->meta.msec = mandoc_strdup(nn->string);
2211 
2212 	/* Infer volume title from section number. */
2213 
2214 	cp = mandoc_a2msec(nn->string);
2215 	if (cp == NULL) {
2216 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2217 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2218 		mdoc->meta.vol = mandoc_strdup(nn->string);
2219 	} else
2220 		mdoc->meta.vol = mandoc_strdup(cp);
2221 
2222 	/* Optional third argument: architecture. */
2223 
2224 	if ((nn = nn->next) == NULL)
2225 		return;
2226 
2227 	for (p = nn->string; *p != '\0'; p++)
2228 		*p = tolower((unsigned char)*p);
2229 	mdoc->meta.arch = mandoc_strdup(nn->string);
2230 
2231 	/* Ignore fourth and later arguments. */
2232 
2233 	if ((nn = nn->next) != NULL)
2234 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2235 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2236 }
2237 
2238 static void
2239 post_bx(POST_ARGS)
2240 {
2241 	struct roff_node	*n, *nch;
2242 
2243 	n = mdoc->last;
2244 	nch = n->child;
2245 
2246 	if (nch != NULL) {
2247 		mdoc->last = nch;
2248 		nch = nch->next;
2249 		mdoc->next = ROFF_NEXT_SIBLING;
2250 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2251 		mdoc->last->flags |= NODE_NOSRC;
2252 		mdoc->next = ROFF_NEXT_SIBLING;
2253 	} else
2254 		mdoc->next = ROFF_NEXT_CHILD;
2255 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2256 	mdoc->last->flags |= NODE_NOSRC;
2257 
2258 	if (nch == NULL) {
2259 		mdoc->last = n;
2260 		return;
2261 	}
2262 
2263 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2264 	mdoc->last->flags |= NODE_NOSRC;
2265 	mdoc->next = ROFF_NEXT_SIBLING;
2266 	roff_word_alloc(mdoc, n->line, n->pos, "-");
2267 	mdoc->last->flags |= NODE_NOSRC;
2268 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2269 	mdoc->last->flags |= NODE_NOSRC;
2270 	mdoc->last = n;
2271 
2272 	/*
2273 	 * Make `Bx's second argument always start with an uppercase
2274 	 * letter.  Groff checks if it's an "accepted" term, but we just
2275 	 * uppercase blindly.
2276 	 */
2277 
2278 	*nch->string = (char)toupper((unsigned char)*nch->string);
2279 }
2280 
2281 static void
2282 post_os(POST_ARGS)
2283 {
2284 #ifndef OSNAME
2285 	struct utsname	  utsname;
2286 	static char	 *defbuf;
2287 #endif
2288 	struct roff_node *n;
2289 
2290 	n = mdoc->last;
2291 	n->flags |= NODE_NOPRT;
2292 
2293 	if (mdoc->meta.os != NULL)
2294 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2295 		    n->line, n->pos, "Os");
2296 	else if (mdoc->flags & MDOC_PBODY)
2297 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2298 		    n->line, n->pos, "Os");
2299 
2300 	/*
2301 	 * Set the operating system by way of the `Os' macro.
2302 	 * The order of precedence is:
2303 	 * 1. the argument of the `Os' macro, unless empty
2304 	 * 2. the -Ios=foo command line argument, if provided
2305 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2306 	 * 4. "sysname release" from uname(3)
2307 	 */
2308 
2309 	free(mdoc->meta.os);
2310 	mdoc->meta.os = NULL;
2311 	deroff(&mdoc->meta.os, n);
2312 	if (mdoc->meta.os)
2313 		return;
2314 
2315 	if (mdoc->defos) {
2316 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2317 		return;
2318 	}
2319 
2320 #ifdef OSNAME
2321 	mdoc->meta.os = mandoc_strdup(OSNAME);
2322 #else /*!OSNAME */
2323 	if (defbuf == NULL) {
2324 		if (uname(&utsname) == -1) {
2325 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2326 			    n->line, n->pos, "Os");
2327 			defbuf = mandoc_strdup("UNKNOWN");
2328 		} else
2329 			mandoc_asprintf(&defbuf, "%s %s",
2330 			    utsname.sysname, utsname.release);
2331 	}
2332 	mdoc->meta.os = mandoc_strdup(defbuf);
2333 #endif /*!OSNAME*/
2334 }
2335 
2336 enum roff_sec
2337 mdoc_a2sec(const char *p)
2338 {
2339 	int		 i;
2340 
2341 	for (i = 0; i < (int)SEC__MAX; i++)
2342 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2343 			return (enum roff_sec)i;
2344 
2345 	return SEC_CUSTOM;
2346 }
2347 
2348 static size_t
2349 macro2len(int macro)
2350 {
2351 
2352 	switch (macro) {
2353 	case MDOC_Ad:
2354 		return 12;
2355 	case MDOC_Ao:
2356 		return 12;
2357 	case MDOC_An:
2358 		return 12;
2359 	case MDOC_Aq:
2360 		return 12;
2361 	case MDOC_Ar:
2362 		return 12;
2363 	case MDOC_Bo:
2364 		return 12;
2365 	case MDOC_Bq:
2366 		return 12;
2367 	case MDOC_Cd:
2368 		return 12;
2369 	case MDOC_Cm:
2370 		return 10;
2371 	case MDOC_Do:
2372 		return 10;
2373 	case MDOC_Dq:
2374 		return 12;
2375 	case MDOC_Dv:
2376 		return 12;
2377 	case MDOC_Eo:
2378 		return 12;
2379 	case MDOC_Em:
2380 		return 10;
2381 	case MDOC_Er:
2382 		return 17;
2383 	case MDOC_Ev:
2384 		return 15;
2385 	case MDOC_Fa:
2386 		return 12;
2387 	case MDOC_Fl:
2388 		return 10;
2389 	case MDOC_Fo:
2390 		return 16;
2391 	case MDOC_Fn:
2392 		return 16;
2393 	case MDOC_Ic:
2394 		return 10;
2395 	case MDOC_Li:
2396 		return 16;
2397 	case MDOC_Ms:
2398 		return 6;
2399 	case MDOC_Nm:
2400 		return 10;
2401 	case MDOC_No:
2402 		return 12;
2403 	case MDOC_Oo:
2404 		return 10;
2405 	case MDOC_Op:
2406 		return 14;
2407 	case MDOC_Pa:
2408 		return 32;
2409 	case MDOC_Pf:
2410 		return 12;
2411 	case MDOC_Po:
2412 		return 12;
2413 	case MDOC_Pq:
2414 		return 12;
2415 	case MDOC_Ql:
2416 		return 16;
2417 	case MDOC_Qo:
2418 		return 12;
2419 	case MDOC_So:
2420 		return 12;
2421 	case MDOC_Sq:
2422 		return 12;
2423 	case MDOC_Sy:
2424 		return 6;
2425 	case MDOC_Sx:
2426 		return 16;
2427 	case MDOC_Tn:
2428 		return 10;
2429 	case MDOC_Va:
2430 		return 12;
2431 	case MDOC_Vt:
2432 		return 12;
2433 	case MDOC_Xr:
2434 		return 10;
2435 	default:
2436 		break;
2437 	};
2438 	return 0;
2439 }
2440