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