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