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