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