Home | History | Annotate | Line # | Download | only in dist
      1 /* Id: mdoc_validate.c,v 1.389 2021/07/18 11:41:23 schwarze Exp  */
      2 /*
      3  * Copyright (c) 2010-2020 Ingo Schwarze <schwarze (at) openbsd.org>
      4  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps (at) bsd.lv>
      5  * Copyright (c) 2010 Joerg Sonnenberger <joerg (at) 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->next != NULL) {
   1117 			nn = nn->next;
   1118 			break;
   1119 		}
   1120 	}
   1121 
   1122 	/* Find the tag. */
   1123 	nt = nch = n->child;
   1124 	if (nch == NULL && nn != NULL && nn->child != NULL &&
   1125 	    nn->child->type == ROFFT_TEXT)
   1126 		nt = nn->child;
   1127 
   1128 	/* Validate the tag. */
   1129 	if (nt == NULL || *nt->string == '\0')
   1130 		mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
   1131 	if (nt == NULL) {
   1132 		roff_node_delete(mdoc, n);
   1133 		return;
   1134 	}
   1135 	len = strcspn(nt->string, " \t\\");
   1136 	if (nt->string[len] != '\0')
   1137 		mandoc_msg(MANDOCERR_TG_SPC, nt->line,
   1138 		    nt->pos + len, "Tg %s", nt->string);
   1139 
   1140 	/* Keep only the first argument. */
   1141 	if (nch != NULL && nch->next != NULL) {
   1142 		mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
   1143 		    nch->next->pos, "Tg ... %s", nch->next->string);
   1144 		while (nch->next != NULL)
   1145 			roff_node_delete(mdoc, nch->next);
   1146 	}
   1147 
   1148 	/* Drop the macro if the first argument is invalid. */
   1149 	if (len == 0 || nt->string[len] != '\0') {
   1150 		roff_node_delete(mdoc, n);
   1151 		return;
   1152 	}
   1153 
   1154 	/* By default, tag the .Tg node itself. */
   1155 	if (nn == NULL || nn->flags & NODE_ID)
   1156 		nn = n;
   1157 
   1158 	/* Explicit tagging of specific macros. */
   1159 	switch (nn->tok) {
   1160 	case MDOC_Sh:
   1161 	case MDOC_Ss:
   1162 	case MDOC_Fo:
   1163 		nn = nn->head->child == NULL ? n : nn->head;
   1164 		break;
   1165 	case MDOC_It:
   1166 		np = nn->parent;
   1167 		while (np->tok != MDOC_Bl)
   1168 			np = np->parent;
   1169 		switch (np->norm->Bl.type) {
   1170 		case LIST_column:
   1171 			break;
   1172 		case LIST_diag:
   1173 		case LIST_hang:
   1174 		case LIST_inset:
   1175 		case LIST_ohang:
   1176 		case LIST_tag:
   1177 			nn = nn->head;
   1178 			break;
   1179 		case LIST_bullet:
   1180 		case LIST_dash:
   1181 		case LIST_enum:
   1182 		case LIST_hyphen:
   1183 		case LIST_item:
   1184 			nn = nn->body->child == NULL ? n : nn->body;
   1185 			break;
   1186 		default:
   1187 			abort();
   1188 		}
   1189 		break;
   1190 	case MDOC_Bd:
   1191 	case MDOC_Bl:
   1192 	case MDOC_D1:
   1193 	case MDOC_Dl:
   1194 		nn = nn->body->child == NULL ? n : nn->body;
   1195 		break;
   1196 	case MDOC_Pp:
   1197 		break;
   1198 	case MDOC_Cm:
   1199 	case MDOC_Dv:
   1200 	case MDOC_Em:
   1201 	case MDOC_Er:
   1202 	case MDOC_Ev:
   1203 	case MDOC_Fl:
   1204 	case MDOC_Fn:
   1205 	case MDOC_Ic:
   1206 	case MDOC_Li:
   1207 	case MDOC_Ms:
   1208 	case MDOC_No:
   1209 	case MDOC_Sy:
   1210 		if (nn->child == NULL)
   1211 			nn = n;
   1212 		break;
   1213 	default:
   1214 		nn = n;
   1215 		break;
   1216 	}
   1217 	tag_put(nt->string, TAG_MANUAL, nn);
   1218 	if (nn != n)
   1219 		n->flags |= NODE_NOPRT;
   1220 }
   1221 
   1222 static void
   1223 post_obsolete(POST_ARGS)
   1224 {
   1225 	struct roff_node *n;
   1226 
   1227 	n = mdoc->last;
   1228 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
   1229 		mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
   1230 		    "%s", roff_name[n->tok]);
   1231 }
   1232 
   1233 static void
   1234 post_useless(POST_ARGS)
   1235 {
   1236 	struct roff_node *n;
   1237 
   1238 	n = mdoc->last;
   1239 	mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
   1240 	    "%s", roff_name[n->tok]);
   1241 }
   1242 
   1243 /*
   1244  * Block macros.
   1245  */
   1246 
   1247 static void
   1248 post_bf(POST_ARGS)
   1249 {
   1250 	struct roff_node *np, *nch;
   1251 
   1252 	/*
   1253 	 * Unlike other data pointers, these are "housed" by the HEAD
   1254 	 * element, which contains the goods.
   1255 	 */
   1256 
   1257 	np = mdoc->last;
   1258 	if (np->type != ROFFT_HEAD)
   1259 		return;
   1260 
   1261 	assert(np->parent->type == ROFFT_BLOCK);
   1262 	assert(np->parent->tok == MDOC_Bf);
   1263 
   1264 	/* Check the number of arguments. */
   1265 
   1266 	nch = np->child;
   1267 	if (np->parent->args == NULL) {
   1268 		if (nch == NULL) {
   1269 			mandoc_msg(MANDOCERR_BF_NOFONT,
   1270 			    np->line, np->pos, "Bf");
   1271 			return;
   1272 		}
   1273 		nch = nch->next;
   1274 	}
   1275 	if (nch != NULL)
   1276 		mandoc_msg(MANDOCERR_ARG_EXCESS,
   1277 		    nch->line, nch->pos, "Bf ... %s", nch->string);
   1278 
   1279 	/* Extract argument into data. */
   1280 
   1281 	if (np->parent->args != NULL) {
   1282 		switch (np->parent->args->argv[0].arg) {
   1283 		case MDOC_Emphasis:
   1284 			np->norm->Bf.font = FONT_Em;
   1285 			break;
   1286 		case MDOC_Literal:
   1287 			np->norm->Bf.font = FONT_Li;
   1288 			break;
   1289 		case MDOC_Symbolic:
   1290 			np->norm->Bf.font = FONT_Sy;
   1291 			break;
   1292 		default:
   1293 			abort();
   1294 		}
   1295 		return;
   1296 	}
   1297 
   1298 	/* Extract parameter into data. */
   1299 
   1300 	if ( ! strcmp(np->child->string, "Em"))
   1301 		np->norm->Bf.font = FONT_Em;
   1302 	else if ( ! strcmp(np->child->string, "Li"))
   1303 		np->norm->Bf.font = FONT_Li;
   1304 	else if ( ! strcmp(np->child->string, "Sy"))
   1305 		np->norm->Bf.font = FONT_Sy;
   1306 	else
   1307 		mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
   1308 		    np->child->pos, "Bf %s", np->child->string);
   1309 }
   1310 
   1311 static void
   1312 post_fname(POST_ARGS)
   1313 {
   1314 	struct roff_node	*n, *nch;
   1315 	const char		*cp;
   1316 	size_t			 pos;
   1317 
   1318 	n = mdoc->last;
   1319 	nch = n->child;
   1320 	cp = nch->string;
   1321 	if (*cp == '(') {
   1322 		if (cp[strlen(cp + 1)] == ')')
   1323 			return;
   1324 		pos = 0;
   1325 	} else {
   1326 		pos = strcspn(cp, "()");
   1327 		if (cp[pos] == '\0') {
   1328 			if (n->sec == SEC_DESCRIPTION ||
   1329 			    n->sec == SEC_CUSTOM)
   1330 				tag_put(NULL, fn_prio++, n);
   1331 			return;
   1332 		}
   1333 	}
   1334 	mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
   1335 }
   1336 
   1337 static void
   1338 post_fn(POST_ARGS)
   1339 {
   1340 	post_fname(mdoc);
   1341 	post_fa(mdoc);
   1342 }
   1343 
   1344 static void
   1345 post_fo(POST_ARGS)
   1346 {
   1347 	const struct roff_node	*n;
   1348 
   1349 	n = mdoc->last;
   1350 
   1351 	if (n->type != ROFFT_HEAD)
   1352 		return;
   1353 
   1354 	if (n->child == NULL) {
   1355 		mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
   1356 		return;
   1357 	}
   1358 	if (n->child != n->last) {
   1359 		mandoc_msg(MANDOCERR_ARG_EXCESS,
   1360 		    n->child->next->line, n->child->next->pos,
   1361 		    "Fo ... %s", n->child->next->string);
   1362 		while (n->child != n->last) {
   1363 			struct roff_node *p = n->last;
   1364 			roff_node_delete(mdoc, p);
   1365 		}
   1366 
   1367 	} else
   1368 		post_delim(mdoc);
   1369 
   1370 	post_fname(mdoc);
   1371 }
   1372 
   1373 static void
   1374 post_fa(POST_ARGS)
   1375 {
   1376 	const struct roff_node *n;
   1377 	const char *cp;
   1378 
   1379 	for (n = mdoc->last->child; n != NULL; n = n->next) {
   1380 		for (cp = n->string; *cp != '\0'; cp++) {
   1381 			/* Ignore callbacks and alterations. */
   1382 			if (*cp == '(' || *cp == '{')
   1383 				break;
   1384 			if (*cp != ',')
   1385 				continue;
   1386 			mandoc_msg(MANDOCERR_FA_COMMA, n->line,
   1387 			    n->pos + (int)(cp - n->string), "%s", n->string);
   1388 			break;
   1389 		}
   1390 	}
   1391 	post_delim_nb(mdoc);
   1392 }
   1393 
   1394 static void
   1395 post_nm(POST_ARGS)
   1396 {
   1397 	struct roff_node	*n;
   1398 
   1399 	n = mdoc->last;
   1400 
   1401 	if (n->sec == SEC_NAME && n->child != NULL &&
   1402 	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
   1403 		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
   1404 
   1405 	if (n->last != NULL && n->last->tok == MDOC_Pp)
   1406 		roff_node_relink(mdoc, n->last);
   1407 
   1408 	if (mdoc->meta.name == NULL)
   1409 		deroff(&mdoc->meta.name, n);
   1410 
   1411 	if (mdoc->meta.name == NULL ||
   1412 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
   1413 		mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
   1414 
   1415 	switch (n->type) {
   1416 	case ROFFT_ELEM:
   1417 		post_delim_nb(mdoc);
   1418 		break;
   1419 	case ROFFT_HEAD:
   1420 		post_delim(mdoc);
   1421 		break;
   1422 	default:
   1423 		return;
   1424 	}
   1425 
   1426 	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
   1427 	    mdoc->meta.name == NULL)
   1428 		return;
   1429 
   1430 	mdoc->next = ROFF_NEXT_CHILD;
   1431 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
   1432 	mdoc->last->flags |= NODE_NOSRC;
   1433 	mdoc->last = n;
   1434 }
   1435 
   1436 static void
   1437 post_nd(POST_ARGS)
   1438 {
   1439 	struct roff_node	*n;
   1440 
   1441 	n = mdoc->last;
   1442 
   1443 	if (n->type != ROFFT_BODY)
   1444 		return;
   1445 
   1446 	if (n->sec != SEC_NAME)
   1447 		mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
   1448 
   1449 	if (n->child == NULL)
   1450 		mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
   1451 	else
   1452 		post_delim(mdoc);
   1453 
   1454 	post_hyph(mdoc);
   1455 }
   1456 
   1457 static void
   1458 post_display(POST_ARGS)
   1459 {
   1460 	struct roff_node *n, *np;
   1461 
   1462 	n = mdoc->last;
   1463 	switch (n->type) {
   1464 	case ROFFT_BODY:
   1465 		if (n->end != ENDBODY_NOT) {
   1466 			if (n->tok == MDOC_Bd &&
   1467 			    n->body->parent->args == NULL)
   1468 				roff_node_delete(mdoc, n);
   1469 		} else if (n->child == NULL)
   1470 			mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
   1471 			    "%s", roff_name[n->tok]);
   1472 		else if (n->tok == MDOC_D1)
   1473 			post_hyph(mdoc);
   1474 		break;
   1475 	case ROFFT_BLOCK:
   1476 		if (n->tok == MDOC_Bd) {
   1477 			if (n->args == NULL) {
   1478 				mandoc_msg(MANDOCERR_BD_NOARG,
   1479 				    n->line, n->pos, "Bd");
   1480 				mdoc->next = ROFF_NEXT_SIBLING;
   1481 				while (n->body->child != NULL)
   1482 					roff_node_relink(mdoc,
   1483 					    n->body->child);
   1484 				roff_node_delete(mdoc, n);
   1485 				break;
   1486 			}
   1487 			post_bd(mdoc);
   1488 			post_prevpar(mdoc);
   1489 		}
   1490 		for (np = n->parent; np != NULL; np = np->parent) {
   1491 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
   1492 				mandoc_msg(MANDOCERR_BD_NEST, n->line,
   1493 				    n->pos, "%s in Bd", roff_name[n->tok]);
   1494 				break;
   1495 			}
   1496 		}
   1497 		break;
   1498 	default:
   1499 		break;
   1500 	}
   1501 }
   1502 
   1503 static void
   1504 post_defaults(POST_ARGS)
   1505 {
   1506 	struct roff_node *n;
   1507 
   1508 	n = mdoc->last;
   1509 	if (n->child != NULL) {
   1510 		post_delim_nb(mdoc);
   1511 		return;
   1512 	}
   1513 	mdoc->next = ROFF_NEXT_CHILD;
   1514 	switch (n->tok) {
   1515 	case MDOC_Ar:
   1516 		roff_word_alloc(mdoc, n->line, n->pos, "file");
   1517 		mdoc->last->flags |= NODE_NOSRC;
   1518 		roff_word_alloc(mdoc, n->line, n->pos, "...");
   1519 		break;
   1520 	case MDOC_Pa:
   1521 	case MDOC_Mt:
   1522 		roff_word_alloc(mdoc, n->line, n->pos, "~");
   1523 		break;
   1524 	default:
   1525 		abort();
   1526 	}
   1527 	mdoc->last->flags |= NODE_NOSRC;
   1528 	mdoc->last = n;
   1529 }
   1530 
   1531 static void
   1532 post_at(POST_ARGS)
   1533 {
   1534 	struct roff_node	*n, *nch;
   1535 	const char		*att;
   1536 
   1537 	n = mdoc->last;
   1538 	nch = n->child;
   1539 
   1540 	/*
   1541 	 * If we have a child, look it up in the standard keys.  If a
   1542 	 * key exist, use that instead of the child; if it doesn't,
   1543 	 * prefix "AT&T UNIX " to the existing data.
   1544 	 */
   1545 
   1546 	att = NULL;
   1547 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
   1548 		mandoc_msg(MANDOCERR_AT_BAD,
   1549 		    nch->line, nch->pos, "At %s", nch->string);
   1550 
   1551 	mdoc->next = ROFF_NEXT_CHILD;
   1552 	if (att != NULL) {
   1553 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
   1554 		nch->flags |= NODE_NOPRT;
   1555 	} else
   1556 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
   1557 	mdoc->last->flags |= NODE_NOSRC;
   1558 	mdoc->last = n;
   1559 }
   1560 
   1561 static void
   1562 post_an(POST_ARGS)
   1563 {
   1564 	struct roff_node *np, *nch;
   1565 
   1566 	post_an_norm(mdoc);
   1567 
   1568 	np = mdoc->last;
   1569 	nch = np->child;
   1570 	if (np->norm->An.auth == AUTH__NONE) {
   1571 		if (nch == NULL)
   1572 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
   1573 			    np->line, np->pos, "An");
   1574 		else
   1575 			post_delim_nb(mdoc);
   1576 	} else if (nch != NULL)
   1577 		mandoc_msg(MANDOCERR_ARG_EXCESS,
   1578 		    nch->line, nch->pos, "An ... %s", nch->string);
   1579 }
   1580 
   1581 static void
   1582 post_em(POST_ARGS)
   1583 {
   1584 	post_tag(mdoc);
   1585 	tag_put(NULL, TAG_FALLBACK, mdoc->last);
   1586 }
   1587 
   1588 static void
   1589 post_en(POST_ARGS)
   1590 {
   1591 	post_obsolete(mdoc);
   1592 	if (mdoc->last->type == ROFFT_BLOCK)
   1593 		mdoc->last->norm->Es = mdoc->last_es;
   1594 }
   1595 
   1596 static void
   1597 post_er(POST_ARGS)
   1598 {
   1599 	struct roff_node *n;
   1600 
   1601 	n = mdoc->last;
   1602 	if (n->sec == SEC_ERRORS &&
   1603 	    (n->parent->tok == MDOC_It ||
   1604 	     (n->parent->tok == MDOC_Bq &&
   1605 	      n->parent->parent->parent->tok == MDOC_It)))
   1606 		tag_put(NULL, TAG_STRONG, n);
   1607 	post_delim_nb(mdoc);
   1608 }
   1609 
   1610 static void
   1611 post_tag(POST_ARGS)
   1612 {
   1613 	struct roff_node *n;
   1614 
   1615 	n = mdoc->last;
   1616 	if ((n->prev == NULL ||
   1617 	     (n->prev->type == ROFFT_TEXT &&
   1618 	      strcmp(n->prev->string, "|") == 0)) &&
   1619 	    (n->parent->tok == MDOC_It ||
   1620 	     (n->parent->tok == MDOC_Xo &&
   1621 	      n->parent->parent->prev == NULL &&
   1622 	      n->parent->parent->parent->tok == MDOC_It)))
   1623 		tag_put(NULL, TAG_STRONG, n);
   1624 	post_delim_nb(mdoc);
   1625 }
   1626 
   1627 static void
   1628 post_es(POST_ARGS)
   1629 {
   1630 	post_obsolete(mdoc);
   1631 	mdoc->last_es = mdoc->last;
   1632 }
   1633 
   1634 static void
   1635 post_fl(POST_ARGS)
   1636 {
   1637 	struct roff_node	*n;
   1638 	char			*cp;
   1639 
   1640 	/*
   1641 	 * Transform ".Fl Fl long" to ".Fl \-long",
   1642 	 * resulting for example in better HTML output.
   1643 	 */
   1644 
   1645 	n = mdoc->last;
   1646 	if (n->prev != NULL && n->prev->tok == MDOC_Fl &&
   1647 	    n->prev->child == NULL && n->child != NULL &&
   1648 	    (n->flags & NODE_LINE) == 0) {
   1649 		mandoc_asprintf(&cp, "\\-%s", n->child->string);
   1650 		free(n->child->string);
   1651 		n->child->string = cp;
   1652 		roff_node_delete(mdoc, n->prev);
   1653 	}
   1654 	post_tag(mdoc);
   1655 }
   1656 
   1657 static void
   1658 post_xx(POST_ARGS)
   1659 {
   1660 	struct roff_node	*n;
   1661 	const char		*os;
   1662 	char			*v;
   1663 
   1664 	post_delim_nb(mdoc);
   1665 
   1666 	n = mdoc->last;
   1667 	switch (n->tok) {
   1668 	case MDOC_Bsx:
   1669 		os = "BSD/OS";
   1670 		break;
   1671 	case MDOC_Dx:
   1672 		os = "DragonFly";
   1673 		break;
   1674 	case MDOC_Fx:
   1675 		os = "FreeBSD";
   1676 		break;
   1677 	case MDOC_Nx:
   1678 		os = "NetBSD";
   1679 		if (n->child == NULL)
   1680 			break;
   1681 		v = n->child->string;
   1682 		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
   1683 		    v[2] < '0' || v[2] > '9' ||
   1684 		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
   1685 			break;
   1686 		n->child->flags |= NODE_NOPRT;
   1687 		mdoc->next = ROFF_NEXT_CHILD;
   1688 		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
   1689 		v = mdoc->last->string;
   1690 		v[3] = toupper((unsigned char)v[3]);
   1691 		mdoc->last->flags |= NODE_NOSRC;
   1692 		mdoc->last = n;
   1693 		break;
   1694 	case MDOC_Ox:
   1695 		os = "OpenBSD";
   1696 		break;
   1697 	case MDOC_Ux:
   1698 		os = "UNIX";
   1699 		break;
   1700 	default:
   1701 		abort();
   1702 	}
   1703 	mdoc->next = ROFF_NEXT_CHILD;
   1704 	roff_word_alloc(mdoc, n->line, n->pos, os);
   1705 	mdoc->last->flags |= NODE_NOSRC;
   1706 	mdoc->last = n;
   1707 }
   1708 
   1709 static void
   1710 post_it(POST_ARGS)
   1711 {
   1712 	struct roff_node *nbl, *nit, *nch;
   1713 	int		  i, cols;
   1714 	enum mdoc_list	  lt;
   1715 
   1716 	post_prevpar(mdoc);
   1717 
   1718 	nit = mdoc->last;
   1719 	if (nit->type != ROFFT_BLOCK)
   1720 		return;
   1721 
   1722 	nbl = nit->parent->parent;
   1723 	lt = nbl->norm->Bl.type;
   1724 
   1725 	switch (lt) {
   1726 	case LIST_tag:
   1727 	case LIST_hang:
   1728 	case LIST_ohang:
   1729 	case LIST_inset:
   1730 	case LIST_diag:
   1731 		if (nit->head->child == NULL)
   1732 			mandoc_msg(MANDOCERR_IT_NOHEAD,
   1733 			    nit->line, nit->pos, "Bl -%s It",
   1734 			    mdoc_argnames[nbl->args->argv[0].arg]);
   1735 		break;
   1736 	case LIST_bullet:
   1737 	case LIST_dash:
   1738 	case LIST_enum:
   1739 	case LIST_hyphen:
   1740 		if (nit->body == NULL || nit->body->child == NULL)
   1741 			mandoc_msg(MANDOCERR_IT_NOBODY,
   1742 			    nit->line, nit->pos, "Bl -%s It",
   1743 			    mdoc_argnames[nbl->args->argv[0].arg]);
   1744 		/* FALLTHROUGH */
   1745 	case LIST_item:
   1746 		if ((nch = nit->head->child) != NULL)
   1747 			mandoc_msg(MANDOCERR_ARG_SKIP,
   1748 			    nit->line, nit->pos, "It %s",
   1749 			    nch->type == ROFFT_TEXT ? nch->string :
   1750 			    roff_name[nch->tok]);
   1751 		break;
   1752 	case LIST_column:
   1753 		cols = (int)nbl->norm->Bl.ncols;
   1754 
   1755 		assert(nit->head->child == NULL);
   1756 
   1757 		if (nit->head->next->child == NULL &&
   1758 		    nit->head->next->next == NULL) {
   1759 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
   1760 			    nit->line, nit->pos, "It");
   1761 			roff_node_delete(mdoc, nit);
   1762 			break;
   1763 		}
   1764 
   1765 		i = 0;
   1766 		for (nch = nit->child; nch != NULL; nch = nch->next) {
   1767 			if (nch->type != ROFFT_BODY)
   1768 				continue;
   1769 			if (i++ && nch->flags & NODE_LINE)
   1770 				mandoc_msg(MANDOCERR_TA_LINE,
   1771 				    nch->line, nch->pos, "Ta");
   1772 		}
   1773 		if (i < cols || i > cols + 1)
   1774 			mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
   1775 			    "%d columns, %d cells", cols, i);
   1776 		else if (nit->head->next->child != NULL &&
   1777 		    nit->head->next->child->flags & NODE_LINE)
   1778 			mandoc_msg(MANDOCERR_IT_NOARG,
   1779 			    nit->line, nit->pos, "Bl -column It");
   1780 		break;
   1781 	default:
   1782 		abort();
   1783 	}
   1784 }
   1785 
   1786 static void
   1787 post_bl_block(POST_ARGS)
   1788 {
   1789 	struct roff_node *n, *ni, *nc;
   1790 
   1791 	post_prevpar(mdoc);
   1792 
   1793 	n = mdoc->last;
   1794 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
   1795 		if (ni->body == NULL)
   1796 			continue;
   1797 		nc = ni->body->last;
   1798 		while (nc != NULL) {
   1799 			switch (nc->tok) {
   1800 			case MDOC_Pp:
   1801 			case ROFF_br:
   1802 				break;
   1803 			default:
   1804 				nc = NULL;
   1805 				continue;
   1806 			}
   1807 			if (ni->next == NULL) {
   1808 				mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
   1809 				    nc->pos, "%s", roff_name[nc->tok]);
   1810 				roff_node_relink(mdoc, nc);
   1811 			} else if (n->norm->Bl.comp == 0 &&
   1812 			    n->norm->Bl.type != LIST_column) {
   1813 				mandoc_msg(MANDOCERR_PAR_SKIP,
   1814 				    nc->line, nc->pos,
   1815 				    "%s before It", roff_name[nc->tok]);
   1816 				roff_node_delete(mdoc, nc);
   1817 			} else
   1818 				break;
   1819 			nc = ni->body->last;
   1820 		}
   1821 	}
   1822 }
   1823 
   1824 /*
   1825  * If the argument of -offset or -width is a macro,
   1826  * replace it with the associated default width.
   1827  */
   1828 static void
   1829 rewrite_macro2len(struct roff_man *mdoc, char **arg)
   1830 {
   1831 	size_t		  width;
   1832 	enum roff_tok	  tok;
   1833 
   1834 	if (*arg == NULL)
   1835 		return;
   1836 	else if ( ! strcmp(*arg, "Ds"))
   1837 		width = 6;
   1838 	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
   1839 		return;
   1840 	else
   1841 		width = macro2len(tok);
   1842 
   1843 	free(*arg);
   1844 	mandoc_asprintf(arg, "%zun", width);
   1845 }
   1846 
   1847 static void
   1848 post_bl_head(POST_ARGS)
   1849 {
   1850 	struct roff_node *nbl, *nh, *nch, *nnext;
   1851 	struct mdoc_argv *argv;
   1852 	int		  i, j;
   1853 
   1854 	post_bl_norm(mdoc);
   1855 
   1856 	nh = mdoc->last;
   1857 	if (nh->norm->Bl.type != LIST_column) {
   1858 		if ((nch = nh->child) == NULL)
   1859 			return;
   1860 		mandoc_msg(MANDOCERR_ARG_EXCESS,
   1861 		    nch->line, nch->pos, "Bl ... %s", nch->string);
   1862 		while (nch != NULL) {
   1863 			roff_node_delete(mdoc, nch);
   1864 			nch = nh->child;
   1865 		}
   1866 		return;
   1867 	}
   1868 
   1869 	/*
   1870 	 * Append old-style lists, where the column width specifiers
   1871 	 * trail as macro parameters, to the new-style ("normal-form")
   1872 	 * lists where they're argument values following -column.
   1873 	 */
   1874 
   1875 	if (nh->child == NULL)
   1876 		return;
   1877 
   1878 	nbl = nh->parent;
   1879 	for (j = 0; j < (int)nbl->args->argc; j++)
   1880 		if (nbl->args->argv[j].arg == MDOC_Column)
   1881 			break;
   1882 
   1883 	assert(j < (int)nbl->args->argc);
   1884 
   1885 	/*
   1886 	 * Accommodate for new-style groff column syntax.  Shuffle the
   1887 	 * child nodes, all of which must be TEXT, as arguments for the
   1888 	 * column field.  Then, delete the head children.
   1889 	 */
   1890 
   1891 	argv = nbl->args->argv + j;
   1892 	i = argv->sz;
   1893 	for (nch = nh->child; nch != NULL; nch = nch->next)
   1894 		argv->sz++;
   1895 	argv->value = mandoc_reallocarray(argv->value,
   1896 	    argv->sz, sizeof(char *));
   1897 
   1898 	nh->norm->Bl.ncols = argv->sz;
   1899 	nh->norm->Bl.cols = (void *)argv->value;
   1900 
   1901 	for (nch = nh->child; nch != NULL; nch = nnext) {
   1902 		argv->value[i++] = nch->string;
   1903 		nch->string = NULL;
   1904 		nnext = nch->next;
   1905 		roff_node_delete(NULL, nch);
   1906 	}
   1907 	nh->child = NULL;
   1908 }
   1909 
   1910 static void
   1911 post_bl(POST_ARGS)
   1912 {
   1913 	struct roff_node	*nbody;           /* of the Bl */
   1914 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
   1915 	const char		*prev_Er;
   1916 	int			 order;
   1917 
   1918 	nbody = mdoc->last;
   1919 	switch (nbody->type) {
   1920 	case ROFFT_BLOCK:
   1921 		post_bl_block(mdoc);
   1922 		return;
   1923 	case ROFFT_HEAD:
   1924 		post_bl_head(mdoc);
   1925 		return;
   1926 	case ROFFT_BODY:
   1927 		break;
   1928 	default:
   1929 		return;
   1930 	}
   1931 	if (nbody->end != ENDBODY_NOT)
   1932 		return;
   1933 
   1934 	/*
   1935 	 * Up to the first item, move nodes before the list,
   1936 	 * but leave transparent nodes where they are
   1937 	 * if they precede an item.
   1938 	 * The next non-transparent node is kept in nchild.
   1939 	 * It only needs to be updated after a non-transparent
   1940 	 * node was moved out, and at the very beginning
   1941 	 * when no node at all was moved yet.
   1942 	 */
   1943 
   1944 	nchild = mdoc->last;
   1945 	for (;;) {
   1946 		if (nchild == mdoc->last)
   1947 			nchild = roff_node_child(nbody);
   1948 		if (nchild == NULL) {
   1949 			mdoc->last = nbody;
   1950 			mandoc_msg(MANDOCERR_BLK_EMPTY,
   1951 			    nbody->line, nbody->pos, "Bl");
   1952 			return;
   1953 		}
   1954 		if (nchild->tok == MDOC_It) {
   1955 			mdoc->last = nbody;
   1956 			break;
   1957 		}
   1958 		mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line,
   1959 		    nbody->child->pos, "%s", roff_name[nbody->child->tok]);
   1960 		if (nbody->parent->prev == NULL) {
   1961 			mdoc->last = nbody->parent->parent;
   1962 			mdoc->next = ROFF_NEXT_CHILD;
   1963 		} else {
   1964 			mdoc->last = nbody->parent->prev;
   1965 			mdoc->next = ROFF_NEXT_SIBLING;
   1966 		}
   1967 		roff_node_relink(mdoc, nbody->child);
   1968 	}
   1969 
   1970 	/*
   1971 	 * We have reached the first item,
   1972 	 * so moving nodes out is no longer possible.
   1973 	 * But in .Bl -column, the first rows may be implicit,
   1974 	 * that is, they may not start with .It macros.
   1975 	 * Such rows may be followed by nodes generated on the
   1976 	 * roff level, for example .TS.
   1977 	 * Wrap such roff nodes into an implicit row.
   1978 	 */
   1979 
   1980 	while (nchild != NULL) {
   1981 		if (nchild->tok == MDOC_It) {
   1982 			nchild = roff_node_next(nchild);
   1983 			continue;
   1984 		}
   1985 		nnext = nchild->next;
   1986 		mdoc->last = nchild->prev;
   1987 		mdoc->next = ROFF_NEXT_SIBLING;
   1988 		roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
   1989 		roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
   1990 		mdoc->next = ROFF_NEXT_SIBLING;
   1991 		roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
   1992 		while (nchild->tok != MDOC_It) {
   1993 			roff_node_relink(mdoc, nchild);
   1994 			if (nnext == NULL)
   1995 				break;
   1996 			nchild = nnext;
   1997 			nnext = nchild->next;
   1998 			mdoc->next = ROFF_NEXT_SIBLING;
   1999 		}
   2000 		mdoc->last = nbody;
   2001 	}
   2002 
   2003 	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
   2004 		return;
   2005 
   2006 	prev_Er = NULL;
   2007 	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
   2008 		if (nchild->tok != MDOC_It)
   2009 			continue;
   2010 		if ((nnext = nchild->head->child) == NULL)
   2011 			continue;
   2012 		if (nnext->type == ROFFT_BLOCK)
   2013 			nnext = nnext->body->child;
   2014 		if (nnext == NULL || nnext->tok != MDOC_Er)
   2015 			continue;
   2016 		nnext = nnext->child;
   2017 		if (prev_Er != NULL) {
   2018 			order = strcmp(prev_Er, nnext->string);
   2019 			if (order > 0)
   2020 				mandoc_msg(MANDOCERR_ER_ORDER,
   2021 				    nnext->line, nnext->pos,
   2022 				    "Er %s %s (NetBSD)",
   2023 				    prev_Er, nnext->string);
   2024 			else if (order == 0)
   2025 				mandoc_msg(MANDOCERR_ER_REP,
   2026 				    nnext->line, nnext->pos,
   2027 				    "Er %s (NetBSD)", prev_Er);
   2028 		}
   2029 		prev_Er = nnext->string;
   2030 	}
   2031 }
   2032 
   2033 static void
   2034 post_bk(POST_ARGS)
   2035 {
   2036 	struct roff_node	*n;
   2037 
   2038 	n = mdoc->last;
   2039 
   2040 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
   2041 		mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
   2042 		roff_node_delete(mdoc, n);
   2043 	}
   2044 }
   2045 
   2046 static void
   2047 post_sm(POST_ARGS)
   2048 {
   2049 	struct roff_node	*nch;
   2050 
   2051 	nch = mdoc->last->child;
   2052 
   2053 	if (nch == NULL) {
   2054 		mdoc->flags ^= MDOC_SMOFF;
   2055 		return;
   2056 	}
   2057 
   2058 	assert(nch->type == ROFFT_TEXT);
   2059 
   2060 	if ( ! strcmp(nch->string, "on")) {
   2061 		mdoc->flags &= ~MDOC_SMOFF;
   2062 		return;
   2063 	}
   2064 	if ( ! strcmp(nch->string, "off")) {
   2065 		mdoc->flags |= MDOC_SMOFF;
   2066 		return;
   2067 	}
   2068 
   2069 	mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
   2070 	    "%s %s", roff_name[mdoc->last->tok], nch->string);
   2071 	roff_node_relink(mdoc, nch);
   2072 	return;
   2073 }
   2074 
   2075 static void
   2076 post_root(POST_ARGS)
   2077 {
   2078 	struct roff_node *n;
   2079 
   2080 	/* Add missing prologue data. */
   2081 
   2082 	if (mdoc->meta.date == NULL)
   2083 		mdoc->meta.date = mandoc_normdate(NULL, NULL);
   2084 
   2085 	if (mdoc->meta.title == NULL) {
   2086 		mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
   2087 		mdoc->meta.title = mandoc_strdup("UNTITLED");
   2088 	}
   2089 
   2090 	if (mdoc->meta.vol == NULL)
   2091 		mdoc->meta.vol = mandoc_strdup("LOCAL");
   2092 
   2093 	if (mdoc->meta.os == NULL) {
   2094 		mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
   2095 		mdoc->meta.os = mandoc_strdup("");
   2096 	} else if (mdoc->meta.os_e &&
   2097 	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
   2098 		mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
   2099 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
   2100 		    "(OpenBSD)" : "(NetBSD)");
   2101 
   2102 	if (mdoc->meta.arch != NULL &&
   2103 	    arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
   2104 		n = mdoc->meta.first->child;
   2105 		while (n->tok != MDOC_Dt ||
   2106 		    n->child == NULL ||
   2107 		    n->child->next == NULL ||
   2108 		    n->child->next->next == NULL)
   2109 			n = n->next;
   2110 		n = n->child->next->next;
   2111 		mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
   2112 		    "Dt ... %s %s", mdoc->meta.arch,
   2113 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
   2114 		    "(OpenBSD)" : "(NetBSD)");
   2115 	}
   2116 
   2117 	/* Check that we begin with a proper `Sh'. */
   2118 
   2119 	n = mdoc->meta.first->child;
   2120 	while (n != NULL &&
   2121 	    (n->type == ROFFT_COMMENT ||
   2122 	     (n->tok >= MDOC_Dd &&
   2123 	      mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
   2124 		n = n->next;
   2125 
   2126 	if (n == NULL)
   2127 		mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
   2128 	else if (n->tok != MDOC_Sh)
   2129 		mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
   2130 		    "%s", roff_name[n->tok]);
   2131 }
   2132 
   2133 static void
   2134 post_rs(POST_ARGS)
   2135 {
   2136 	struct roff_node *np, *nch, *next, *prev;
   2137 	int		  i, j;
   2138 
   2139 	np = mdoc->last;
   2140 
   2141 	if (np->type != ROFFT_BODY)
   2142 		return;
   2143 
   2144 	if (np->child == NULL) {
   2145 		mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
   2146 		return;
   2147 	}
   2148 
   2149 	/*
   2150 	 * The full `Rs' block needs special handling to order the
   2151 	 * sub-elements according to `rsord'.  Pick through each element
   2152 	 * and correctly order it.  This is an insertion sort.
   2153 	 */
   2154 
   2155 	next = NULL;
   2156 	for (nch = np->child->next; nch != NULL; nch = next) {
   2157 		/* Determine order number of this child. */
   2158 		for (i = 0; i < RSORD_MAX; i++)
   2159 			if (rsord[i] == nch->tok)
   2160 				break;
   2161 
   2162 		if (i == RSORD_MAX) {
   2163 			mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
   2164 			    "%s", roff_name[nch->tok]);
   2165 			i = -1;
   2166 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
   2167 			np->norm->Rs.quote_T++;
   2168 
   2169 		/*
   2170 		 * Remove this child from the chain.  This somewhat
   2171 		 * repeats roff_node_unlink(), but since we're
   2172 		 * just re-ordering, there's no need for the
   2173 		 * full unlink process.
   2174 		 */
   2175 
   2176 		if ((next = nch->next) != NULL)
   2177 			next->prev = nch->prev;
   2178 
   2179 		if ((prev = nch->prev) != NULL)
   2180 			prev->next = nch->next;
   2181 
   2182 		nch->prev = nch->next = NULL;
   2183 
   2184 		/*
   2185 		 * Scan back until we reach a node that's
   2186 		 * to be ordered before this child.
   2187 		 */
   2188 
   2189 		for ( ; prev ; prev = prev->prev) {
   2190 			/* Determine order of `prev'. */
   2191 			for (j = 0; j < RSORD_MAX; j++)
   2192 				if (rsord[j] == prev->tok)
   2193 					break;
   2194 			if (j == RSORD_MAX)
   2195 				j = -1;
   2196 
   2197 			if (j <= i)
   2198 				break;
   2199 		}
   2200 
   2201 		/*
   2202 		 * Set this child back into its correct place
   2203 		 * in front of the `prev' node.
   2204 		 */
   2205 
   2206 		nch->prev = prev;
   2207 
   2208 		if (prev == NULL) {
   2209 			np->child->prev = nch;
   2210 			nch->next = np->child;
   2211 			np->child = nch;
   2212 		} else {
   2213 			if (prev->next)
   2214 				prev->next->prev = nch;
   2215 			nch->next = prev->next;
   2216 			prev->next = nch;
   2217 		}
   2218 	}
   2219 }
   2220 
   2221 /*
   2222  * For some arguments of some macros,
   2223  * convert all breakable hyphens into ASCII_HYPH.
   2224  */
   2225 static void
   2226 post_hyph(POST_ARGS)
   2227 {
   2228 	struct roff_node	*n, *nch;
   2229 	char			*cp;
   2230 
   2231 	n = mdoc->last;
   2232 	for (nch = n->child; nch != NULL; nch = nch->next) {
   2233 		if (nch->type != ROFFT_TEXT)
   2234 			continue;
   2235 		cp = nch->string;
   2236 		if (*cp == '\0')
   2237 			continue;
   2238 		while (*(++cp) != '\0')
   2239 			if (*cp == '-' &&
   2240 			    isalpha((unsigned char)cp[-1]) &&
   2241 			    isalpha((unsigned char)cp[1])) {
   2242 				if (n->tag == NULL && n->flags & NODE_ID)
   2243 					n->tag = mandoc_strdup(nch->string);
   2244 				*cp = ASCII_HYPH;
   2245 			}
   2246 	}
   2247 }
   2248 
   2249 static void
   2250 post_ns(POST_ARGS)
   2251 {
   2252 	struct roff_node	*n;
   2253 
   2254 	n = mdoc->last;
   2255 	if (n->flags & NODE_LINE ||
   2256 	    (n->next != NULL && n->next->flags & NODE_DELIMC))
   2257 		mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
   2258 }
   2259 
   2260 static void
   2261 post_sx(POST_ARGS)
   2262 {
   2263 	post_delim(mdoc);
   2264 	post_hyph(mdoc);
   2265 }
   2266 
   2267 static void
   2268 post_sh(POST_ARGS)
   2269 {
   2270 	post_section(mdoc);
   2271 
   2272 	switch (mdoc->last->type) {
   2273 	case ROFFT_HEAD:
   2274 		post_sh_head(mdoc);
   2275 		break;
   2276 	case ROFFT_BODY:
   2277 		switch (mdoc->lastsec)  {
   2278 		case SEC_NAME:
   2279 			post_sh_name(mdoc);
   2280 			break;
   2281 		case SEC_SEE_ALSO:
   2282 			post_sh_see_also(mdoc);
   2283 			break;
   2284 		case SEC_AUTHORS:
   2285 			post_sh_authors(mdoc);
   2286 			break;
   2287 		default:
   2288 			break;
   2289 		}
   2290 		break;
   2291 	default:
   2292 		break;
   2293 	}
   2294 }
   2295 
   2296 static void
   2297 post_sh_name(POST_ARGS)
   2298 {
   2299 	struct roff_node *n;
   2300 	int hasnm, hasnd;
   2301 
   2302 	hasnm = hasnd = 0;
   2303 
   2304 	for (n = mdoc->last->child; n != NULL; n = n->next) {
   2305 		switch (n->tok) {
   2306 		case MDOC_Nm:
   2307 			if (hasnm && n->child != NULL)
   2308 				mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
   2309 				    n->line, n->pos,
   2310 				    "Nm %s", n->child->string);
   2311 			hasnm = 1;
   2312 			continue;
   2313 		case MDOC_Nd:
   2314 			hasnd = 1;
   2315 			if (n->next != NULL)
   2316 				mandoc_msg(MANDOCERR_NAMESEC_ND,
   2317 				    n->line, n->pos, NULL);
   2318 			break;
   2319 		case TOKEN_NONE:
   2320 			if (n->type == ROFFT_TEXT &&
   2321 			    n->string[0] == ',' && n->string[1] == '\0' &&
   2322 			    n->next != NULL && n->next->tok == MDOC_Nm) {
   2323 				n = n->next;
   2324 				continue;
   2325 			}
   2326 			/* FALLTHROUGH */
   2327 		default:
   2328 			mandoc_msg(MANDOCERR_NAMESEC_BAD,
   2329 			    n->line, n->pos, "%s", roff_name[n->tok]);
   2330 			continue;
   2331 		}
   2332 		break;
   2333 	}
   2334 
   2335 	if ( ! hasnm)
   2336 		mandoc_msg(MANDOCERR_NAMESEC_NONM,
   2337 		    mdoc->last->line, mdoc->last->pos, NULL);
   2338 	if ( ! hasnd)
   2339 		mandoc_msg(MANDOCERR_NAMESEC_NOND,
   2340 		    mdoc->last->line, mdoc->last->pos, NULL);
   2341 }
   2342 
   2343 static void
   2344 post_sh_see_also(POST_ARGS)
   2345 {
   2346 	const struct roff_node	*n;
   2347 	const char		*name, *sec;
   2348 	const char		*lastname, *lastsec, *lastpunct;
   2349 	int			 cmp;
   2350 
   2351 	n = mdoc->last->child;
   2352 	lastname = lastsec = lastpunct = NULL;
   2353 	while (n != NULL) {
   2354 		if (n->tok != MDOC_Xr ||
   2355 		    n->child == NULL ||
   2356 		    n->child->next == NULL)
   2357 			break;
   2358 
   2359 		/* Process one .Xr node. */
   2360 
   2361 		name = n->child->string;
   2362 		sec = n->child->next->string;
   2363 		if (lastsec != NULL) {
   2364 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
   2365 				mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
   2366 				    n->pos, "%s before %s(%s)",
   2367 				    lastpunct, name, sec);
   2368 			cmp = strcmp(lastsec, sec);
   2369 			if (cmp > 0)
   2370 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
   2371 				    n->pos, "%s(%s) after %s(%s)",
   2372 				    name, sec, lastname, lastsec);
   2373 			else if (cmp == 0 &&
   2374 			    strcasecmp(lastname, name) > 0)
   2375 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
   2376 				    n->pos, "%s after %s", name, lastname);
   2377 		}
   2378 		lastname = name;
   2379 		lastsec = sec;
   2380 
   2381 		/* Process the following node. */
   2382 
   2383 		n = n->next;
   2384 		if (n == NULL)
   2385 			break;
   2386 		if (n->tok == MDOC_Xr) {
   2387 			lastpunct = "none";
   2388 			continue;
   2389 		}
   2390 		if (n->type != ROFFT_TEXT)
   2391 			break;
   2392 		for (name = n->string; *name != '\0'; name++)
   2393 			if (isalpha((const unsigned char)*name))
   2394 				return;
   2395 		lastpunct = n->string;
   2396 		if (n->next == NULL || n->next->tok == MDOC_Rs)
   2397 			mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
   2398 			    n->pos, "%s after %s(%s)",
   2399 			    lastpunct, lastname, lastsec);
   2400 		n = n->next;
   2401 	}
   2402 }
   2403 
   2404 static int
   2405 child_an(const struct roff_node *n)
   2406 {
   2407 
   2408 	for (n = n->child; n != NULL; n = n->next)
   2409 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
   2410 			return 1;
   2411 	return 0;
   2412 }
   2413 
   2414 static void
   2415 post_sh_authors(POST_ARGS)
   2416 {
   2417 
   2418 	if ( ! child_an(mdoc->last))
   2419 		mandoc_msg(MANDOCERR_AN_MISSING,
   2420 		    mdoc->last->line, mdoc->last->pos, NULL);
   2421 }
   2422 
   2423 /*
   2424  * Return an upper bound for the string distance (allowing
   2425  * transpositions).  Not a full Levenshtein implementation
   2426  * because Levenshtein is quadratic in the string length
   2427  * and this function is called for every standard name,
   2428  * so the check for each custom name would be cubic.
   2429  * The following crude heuristics is linear, resulting
   2430  * in quadratic behaviour for checking one custom name,
   2431  * which does not cause measurable slowdown.
   2432  */
   2433 static int
   2434 similar(const char *s1, const char *s2)
   2435 {
   2436 	const int	maxdist = 3;
   2437 	int		dist = 0;
   2438 
   2439 	while (s1[0] != '\0' && s2[0] != '\0') {
   2440 		if (s1[0] == s2[0]) {
   2441 			s1++;
   2442 			s2++;
   2443 			continue;
   2444 		}
   2445 		if (++dist > maxdist)
   2446 			return INT_MAX;
   2447 		if (s1[1] == s2[1]) {  /* replacement */
   2448 			s1++;
   2449 			s2++;
   2450 		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
   2451 			s1 += 2;	/* transposition */
   2452 			s2 += 2;
   2453 		} else if (s1[0] == s2[1])  /* insertion */
   2454 			s2++;
   2455 		else if (s1[1] == s2[0])  /* deletion */
   2456 			s1++;
   2457 		else
   2458 			return INT_MAX;
   2459 	}
   2460 	dist += strlen(s1) + strlen(s2);
   2461 	return dist > maxdist ? INT_MAX : dist;
   2462 }
   2463 
   2464 static void
   2465 post_sh_head(POST_ARGS)
   2466 {
   2467 	struct roff_node	*nch;
   2468 	const char		*goodsec;
   2469 	const char *const	*testsec;
   2470 	int			 dist, mindist;
   2471 	enum roff_sec		 sec;
   2472 
   2473 	/*
   2474 	 * Process a new section.  Sections are either "named" or
   2475 	 * "custom".  Custom sections are user-defined, while named ones
   2476 	 * follow a conventional order and may only appear in certain
   2477 	 * manual sections.
   2478 	 */
   2479 
   2480 	sec = mdoc->last->sec;
   2481 
   2482 	/* The NAME should be first. */
   2483 
   2484 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
   2485 		mandoc_msg(MANDOCERR_NAMESEC_FIRST,
   2486 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
   2487 		    sec != SEC_CUSTOM ? secnames[sec] :
   2488 		    (nch = mdoc->last->child) == NULL ? "" :
   2489 		    nch->type == ROFFT_TEXT ? nch->string :
   2490 		    roff_name[nch->tok]);
   2491 
   2492 	/* The SYNOPSIS gets special attention in other areas. */
   2493 
   2494 	if (sec == SEC_SYNOPSIS) {
   2495 		roff_setreg(mdoc->roff, "nS", 1, '=');
   2496 		mdoc->flags |= MDOC_SYNOPSIS;
   2497 	} else {
   2498 		roff_setreg(mdoc->roff, "nS", 0, '=');
   2499 		mdoc->flags &= ~MDOC_SYNOPSIS;
   2500 	}
   2501 	if (sec == SEC_DESCRIPTION)
   2502 		fn_prio = TAG_STRONG;
   2503 
   2504 	/* Mark our last section. */
   2505 
   2506 	mdoc->lastsec = sec;
   2507 
   2508 	/* We don't care about custom sections after this. */
   2509 
   2510 	if (sec == SEC_CUSTOM) {
   2511 		if ((nch = mdoc->last->child) == NULL ||
   2512 		    nch->type != ROFFT_TEXT || nch->next != NULL)
   2513 			return;
   2514 		goodsec = NULL;
   2515 		mindist = INT_MAX;
   2516 		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
   2517 			dist = similar(nch->string, *testsec);
   2518 			if (dist < mindist) {
   2519 				goodsec = *testsec;
   2520 				mindist = dist;
   2521 			}
   2522 		}
   2523 		if (goodsec != NULL)
   2524 			mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
   2525 			    "Sh %s instead of %s", nch->string, goodsec);
   2526 		return;
   2527 	}
   2528 
   2529 	/*
   2530 	 * Check whether our non-custom section is being repeated or is
   2531 	 * out of order.
   2532 	 */
   2533 
   2534 	if (sec == mdoc->lastnamed)
   2535 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
   2536 		    mdoc->last->pos, "Sh %s", secnames[sec]);
   2537 
   2538 	if (sec < mdoc->lastnamed)
   2539 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
   2540 		    mdoc->last->pos, "Sh %s", secnames[sec]);
   2541 
   2542 	/* Mark the last named section. */
   2543 
   2544 	mdoc->lastnamed = sec;
   2545 
   2546 	/* Check particular section/manual conventions. */
   2547 
   2548 	if (mdoc->meta.msec == NULL)
   2549 		return;
   2550 
   2551 	goodsec = NULL;
   2552 	switch (sec) {
   2553 	case SEC_ERRORS:
   2554 		if (*mdoc->meta.msec == '4')
   2555 			break;
   2556 		goodsec = "2, 3, 4, 9";
   2557 		/* FALLTHROUGH */
   2558 	case SEC_RETURN_VALUES:
   2559 	case SEC_LIBRARY:
   2560 		if (*mdoc->meta.msec == '2')
   2561 			break;
   2562 		if (*mdoc->meta.msec == '3')
   2563 			break;
   2564 		if (NULL == goodsec)
   2565 			goodsec = "2, 3, 9";
   2566 		/* FALLTHROUGH */
   2567 	case SEC_CONTEXT:
   2568 		if (*mdoc->meta.msec == '9')
   2569 			break;
   2570 		if (NULL == goodsec)
   2571 			goodsec = "9";
   2572 		mandoc_msg(MANDOCERR_SEC_MSEC,
   2573 		    mdoc->last->line, mdoc->last->pos,
   2574 		    "Sh %s for %s only", secnames[sec], goodsec);
   2575 		break;
   2576 	default:
   2577 		break;
   2578 	}
   2579 }
   2580 
   2581 static void
   2582 post_xr(POST_ARGS)
   2583 {
   2584 	struct roff_node *n, *nch;
   2585 
   2586 	n = mdoc->last;
   2587 	nch = n->child;
   2588 	if (nch->next == NULL) {
   2589 		mandoc_msg(MANDOCERR_XR_NOSEC,
   2590 		    n->line, n->pos, "Xr %s", nch->string);
   2591 	} else {
   2592 		assert(nch->next == n->last);
   2593 		if(mandoc_xr_add(nch->next->string, nch->string,
   2594 		    nch->line, nch->pos))
   2595 			mandoc_msg(MANDOCERR_XR_SELF,
   2596 			    nch->line, nch->pos, "Xr %s %s",
   2597 			    nch->string, nch->next->string);
   2598 	}
   2599 	post_delim_nb(mdoc);
   2600 }
   2601 
   2602 static void
   2603 post_section(POST_ARGS)
   2604 {
   2605 	struct roff_node *n, *nch;
   2606 	char		 *cp, *tag;
   2607 
   2608 	n = mdoc->last;
   2609 	switch (n->type) {
   2610 	case ROFFT_BLOCK:
   2611 		post_prevpar(mdoc);
   2612 		return;
   2613 	case ROFFT_HEAD:
   2614 		tag = NULL;
   2615 		deroff(&tag, n);
   2616 		if (tag != NULL) {
   2617 			for (cp = tag; *cp != '\0'; cp++)
   2618 				if (*cp == ' ')
   2619 					*cp = '_';
   2620 			if ((nch = n->child) != NULL &&
   2621 			    nch->type == ROFFT_TEXT &&
   2622 			    strcmp(nch->string, tag) == 0)
   2623 				tag_put(NULL, TAG_STRONG, n);
   2624 			else
   2625 				tag_put(tag, TAG_FALLBACK, n);
   2626 			free(tag);
   2627 		}
   2628 		post_delim(mdoc);
   2629 		post_hyph(mdoc);
   2630 		return;
   2631 	case ROFFT_BODY:
   2632 		break;
   2633 	default:
   2634 		return;
   2635 	}
   2636 	if ((nch = n->child) != NULL &&
   2637 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br ||
   2638 	     nch->tok == ROFF_sp)) {
   2639 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
   2640 		    "%s after %s", roff_name[nch->tok],
   2641 		    roff_name[n->tok]);
   2642 		roff_node_delete(mdoc, nch);
   2643 	}
   2644 	if ((nch = n->last) != NULL &&
   2645 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) {
   2646 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
   2647 		    "%s at the end of %s", roff_name[nch->tok],
   2648 		    roff_name[n->tok]);
   2649 		roff_node_delete(mdoc, nch);
   2650 	}
   2651 }
   2652 
   2653 static void
   2654 post_prevpar(POST_ARGS)
   2655 {
   2656 	struct roff_node *n, *np;
   2657 
   2658 	n = mdoc->last;
   2659 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
   2660 		return;
   2661 	if ((np = roff_node_prev(n)) == NULL)
   2662 		return;
   2663 
   2664 	/*
   2665 	 * Don't allow `Pp' prior to a paragraph-type
   2666 	 * block: `Pp' or non-compact `Bd' or `Bl'.
   2667 	 */
   2668 
   2669 	if (np->tok != MDOC_Pp && np->tok != ROFF_br)
   2670 		return;
   2671 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
   2672 		return;
   2673 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
   2674 		return;
   2675 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
   2676 		return;
   2677 
   2678 	mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
   2679 	    "%s before %s", roff_name[np->tok], roff_name[n->tok]);
   2680 	roff_node_delete(mdoc, np);
   2681 }
   2682 
   2683 static void
   2684 post_par(POST_ARGS)
   2685 {
   2686 	struct roff_node *np;
   2687 
   2688 	fn_prio = TAG_STRONG;
   2689 	post_prevpar(mdoc);
   2690 
   2691 	np = mdoc->last;
   2692 	if (np->child != NULL)
   2693 		mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
   2694 		    "%s %s", roff_name[np->tok], np->child->string);
   2695 }
   2696 
   2697 static void
   2698 post_dd(POST_ARGS)
   2699 {
   2700 	struct roff_node *n;
   2701 
   2702 	n = mdoc->last;
   2703 	n->flags |= NODE_NOPRT;
   2704 
   2705 	if (mdoc->meta.date != NULL) {
   2706 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
   2707 		free(mdoc->meta.date);
   2708 	} else if (mdoc->flags & MDOC_PBODY)
   2709 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
   2710 	else if (mdoc->meta.title != NULL)
   2711 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
   2712 		    n->line, n->pos, "Dd after Dt");
   2713 	else if (mdoc->meta.os != NULL)
   2714 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
   2715 		    n->line, n->pos, "Dd after Os");
   2716 
   2717 	if (mdoc->quick && n != NULL)
   2718 		mdoc->meta.date = mandoc_strdup("");
   2719 	else
   2720 		mdoc->meta.date = mandoc_normdate(n->child, n);
   2721 }
   2722 
   2723 static void
   2724 post_dt(POST_ARGS)
   2725 {
   2726 	struct roff_node *nn, *n;
   2727 	const char	 *cp;
   2728 	char		 *p;
   2729 
   2730 	n = mdoc->last;
   2731 	n->flags |= NODE_NOPRT;
   2732 
   2733 	if (mdoc->flags & MDOC_PBODY) {
   2734 		mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
   2735 		return;
   2736 	}
   2737 
   2738 	if (mdoc->meta.title != NULL)
   2739 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
   2740 	else if (mdoc->meta.os != NULL)
   2741 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
   2742 		    n->line, n->pos, "Dt after Os");
   2743 
   2744 	free(mdoc->meta.title);
   2745 	free(mdoc->meta.msec);
   2746 	free(mdoc->meta.vol);
   2747 	free(mdoc->meta.arch);
   2748 
   2749 	mdoc->meta.title = NULL;
   2750 	mdoc->meta.msec = NULL;
   2751 	mdoc->meta.vol = NULL;
   2752 	mdoc->meta.arch = NULL;
   2753 
   2754 	/* Mandatory first argument: title. */
   2755 
   2756 	nn = n->child;
   2757 	if (nn == NULL || *nn->string == '\0') {
   2758 		mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
   2759 		mdoc->meta.title = mandoc_strdup("UNTITLED");
   2760 	} else {
   2761 		mdoc->meta.title = mandoc_strdup(nn->string);
   2762 
   2763 		/* Check that all characters are uppercase. */
   2764 
   2765 		for (p = nn->string; *p != '\0'; p++)
   2766 			if (islower((unsigned char)*p)) {
   2767 				mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
   2768 				    nn->pos + (int)(p - nn->string),
   2769 				    "Dt %s", nn->string);
   2770 				break;
   2771 			}
   2772 	}
   2773 
   2774 	/* Mandatory second argument: section. */
   2775 
   2776 	if (nn != NULL)
   2777 		nn = nn->next;
   2778 
   2779 	if (nn == NULL) {
   2780 		mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
   2781 		    "Dt %s", mdoc->meta.title);
   2782 		mdoc->meta.vol = mandoc_strdup("LOCAL");
   2783 		return;  /* msec and arch remain NULL. */
   2784 	}
   2785 
   2786 	mdoc->meta.msec = mandoc_strdup(nn->string);
   2787 
   2788 	/* Infer volume title from section number. */
   2789 
   2790 	cp = mandoc_a2msec(nn->string);
   2791 	if (cp == NULL) {
   2792 		mandoc_msg(MANDOCERR_MSEC_BAD,
   2793 		    nn->line, nn->pos, "Dt ... %s", nn->string);
   2794 		mdoc->meta.vol = mandoc_strdup(nn->string);
   2795 	} else {
   2796 		mdoc->meta.vol = mandoc_strdup(cp);
   2797 		if (mdoc->filesec != '\0' &&
   2798 		    mdoc->filesec != *nn->string &&
   2799 		    *nn->string >= '1' && *nn->string <= '9')
   2800 			mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos,
   2801 			    "*.%c vs Dt ... %c", mdoc->filesec, *nn->string);
   2802 	}
   2803 
   2804 	/* Optional third argument: architecture. */
   2805 
   2806 	if ((nn = nn->next) == NULL)
   2807 		return;
   2808 
   2809 	for (p = nn->string; *p != '\0'; p++)
   2810 		*p = tolower((unsigned char)*p);
   2811 	mdoc->meta.arch = mandoc_strdup(nn->string);
   2812 
   2813 	/* Ignore fourth and later arguments. */
   2814 
   2815 	if ((nn = nn->next) != NULL)
   2816 		mandoc_msg(MANDOCERR_ARG_EXCESS,
   2817 		    nn->line, nn->pos, "Dt ... %s", nn->string);
   2818 }
   2819 
   2820 static void
   2821 post_bx(POST_ARGS)
   2822 {
   2823 	struct roff_node	*n, *nch;
   2824 	const char		*macro;
   2825 
   2826 	post_delim_nb(mdoc);
   2827 
   2828 	n = mdoc->last;
   2829 	nch = n->child;
   2830 
   2831 	if (nch != NULL) {
   2832 		macro = !strcmp(nch->string, "Open") ? "Ox" :
   2833 		    !strcmp(nch->string, "Net") ? "Nx" :
   2834 		    !strcmp(nch->string, "Free") ? "Fx" :
   2835 		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
   2836 		if (macro != NULL)
   2837 			mandoc_msg(MANDOCERR_BX,
   2838 			    n->line, n->pos, "%s", macro);
   2839 		mdoc->last = nch;
   2840 		nch = nch->next;
   2841 		mdoc->next = ROFF_NEXT_SIBLING;
   2842 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
   2843 		mdoc->last->flags |= NODE_NOSRC;
   2844 		mdoc->next = ROFF_NEXT_SIBLING;
   2845 	} else
   2846 		mdoc->next = ROFF_NEXT_CHILD;
   2847 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
   2848 	mdoc->last->flags |= NODE_NOSRC;
   2849 
   2850 	if (nch == NULL) {
   2851 		mdoc->last = n;
   2852 		return;
   2853 	}
   2854 
   2855 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
   2856 	mdoc->last->flags |= NODE_NOSRC;
   2857 	mdoc->next = ROFF_NEXT_SIBLING;
   2858 	roff_word_alloc(mdoc, n->line, n->pos, "-");
   2859 	mdoc->last->flags |= NODE_NOSRC;
   2860 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
   2861 	mdoc->last->flags |= NODE_NOSRC;
   2862 	mdoc->last = n;
   2863 
   2864 	/*
   2865 	 * Make `Bx's second argument always start with an uppercase
   2866 	 * letter.  Groff checks if it's an "accepted" term, but we just
   2867 	 * uppercase blindly.
   2868 	 */
   2869 
   2870 	*nch->string = (char)toupper((unsigned char)*nch->string);
   2871 }
   2872 
   2873 static void
   2874 post_os(POST_ARGS)
   2875 {
   2876 #ifndef OSNAME
   2877 	struct utsname	  utsname;
   2878 	static char	 *defbuf;
   2879 #endif
   2880 	struct roff_node *n;
   2881 
   2882 	n = mdoc->last;
   2883 	n->flags |= NODE_NOPRT;
   2884 
   2885 	if (mdoc->meta.os != NULL)
   2886 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
   2887 	else if (mdoc->flags & MDOC_PBODY)
   2888 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
   2889 
   2890 	post_delim(mdoc);
   2891 
   2892 	/*
   2893 	 * Set the operating system by way of the `Os' macro.
   2894 	 * The order of precedence is:
   2895 	 * 1. the argument of the `Os' macro, unless empty
   2896 	 * 2. the -Ios=foo command line argument, if provided
   2897 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
   2898 	 * 4. "sysname release" from uname(3)
   2899 	 */
   2900 
   2901 	free(mdoc->meta.os);
   2902 	mdoc->meta.os = NULL;
   2903 	deroff(&mdoc->meta.os, n);
   2904 	if (mdoc->meta.os)
   2905 		goto out;
   2906 
   2907 	if (mdoc->os_s != NULL) {
   2908 		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
   2909 		goto out;
   2910 	}
   2911 
   2912 #ifdef OSNAME
   2913 	mdoc->meta.os = mandoc_strdup(OSNAME);
   2914 #else /*!OSNAME */
   2915 	if (defbuf == NULL) {
   2916 		if (uname(&utsname) == -1) {
   2917 			mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
   2918 			defbuf = mandoc_strdup("UNKNOWN");
   2919 		} else
   2920 			mandoc_asprintf(&defbuf, "%s %s",
   2921 			    utsname.sysname, utsname.release);
   2922 	}
   2923 	mdoc->meta.os = mandoc_strdup(defbuf);
   2924 #endif /*!OSNAME*/
   2925 
   2926 out:
   2927 	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
   2928 		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
   2929 			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
   2930 		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
   2931 			mdoc->meta.os_e = MANDOC_OS_NETBSD;
   2932 	}
   2933 
   2934 	/*
   2935 	 * This is the earliest point where we can check
   2936 	 * Mdocdate conventions because we don't know
   2937 	 * the operating system earlier.
   2938 	 */
   2939 
   2940 	if (n->child != NULL)
   2941 		mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
   2942 		    "Os %s (%s)", n->child->string,
   2943 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
   2944 		    "OpenBSD" : "NetBSD");
   2945 
   2946 	while (n->tok != MDOC_Dd)
   2947 		if ((n = n->prev) == NULL)
   2948 			return;
   2949 	if ((n = n->child) == NULL)
   2950 		return;
   2951 	if (strncmp(n->string, "$" "Mdocdate", 9)) {
   2952 		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
   2953 			mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
   2954 			    n->pos, "Dd %s (OpenBSD)", n->string);
   2955 	} else {
   2956 		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
   2957 			mandoc_msg(MANDOCERR_MDOCDATE, n->line,
   2958 			    n->pos, "Dd %s (NetBSD)", n->string);
   2959 	}
   2960 }
   2961 
   2962 enum roff_sec
   2963 mdoc_a2sec(const char *p)
   2964 {
   2965 	int		 i;
   2966 
   2967 	for (i = 0; i < (int)SEC__MAX; i++)
   2968 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
   2969 			return (enum roff_sec)i;
   2970 
   2971 	return SEC_CUSTOM;
   2972 }
   2973 
   2974 static size_t
   2975 macro2len(enum roff_tok macro)
   2976 {
   2977 
   2978 	switch (macro) {
   2979 	case MDOC_Ad:
   2980 		return 12;
   2981 	case MDOC_Ao:
   2982 		return 12;
   2983 	case MDOC_An:
   2984 		return 12;
   2985 	case MDOC_Aq:
   2986 		return 12;
   2987 	case MDOC_Ar:
   2988 		return 12;
   2989 	case MDOC_Bo:
   2990 		return 12;
   2991 	case MDOC_Bq:
   2992 		return 12;
   2993 	case MDOC_Cd:
   2994 		return 12;
   2995 	case MDOC_Cm:
   2996 		return 10;
   2997 	case MDOC_Do:
   2998 		return 10;
   2999 	case MDOC_Dq:
   3000 		return 12;
   3001 	case MDOC_Dv:
   3002 		return 12;
   3003 	case MDOC_Eo:
   3004 		return 12;
   3005 	case MDOC_Em:
   3006 		return 10;
   3007 	case MDOC_Er:
   3008 		return 17;
   3009 	case MDOC_Ev:
   3010 		return 15;
   3011 	case MDOC_Fa:
   3012 		return 12;
   3013 	case MDOC_Fl:
   3014 		return 10;
   3015 	case MDOC_Fo:
   3016 		return 16;
   3017 	case MDOC_Fn:
   3018 		return 16;
   3019 	case MDOC_Ic:
   3020 		return 10;
   3021 	case MDOC_Li:
   3022 		return 16;
   3023 	case MDOC_Ms:
   3024 		return 6;
   3025 	case MDOC_Nm:
   3026 		return 10;
   3027 	case MDOC_No:
   3028 		return 12;
   3029 	case MDOC_Oo:
   3030 		return 10;
   3031 	case MDOC_Op:
   3032 		return 14;
   3033 	case MDOC_Pa:
   3034 		return 32;
   3035 	case MDOC_Pf:
   3036 		return 12;
   3037 	case MDOC_Po:
   3038 		return 12;
   3039 	case MDOC_Pq:
   3040 		return 12;
   3041 	case MDOC_Ql:
   3042 		return 16;
   3043 	case MDOC_Qo:
   3044 		return 12;
   3045 	case MDOC_So:
   3046 		return 12;
   3047 	case MDOC_Sq:
   3048 		return 12;
   3049 	case MDOC_Sy:
   3050 		return 6;
   3051 	case MDOC_Sx:
   3052 		return 16;
   3053 	case MDOC_Tn:
   3054 		return 10;
   3055 	case MDOC_Va:
   3056 		return 12;
   3057 	case MDOC_Vt:
   3058 		return 12;
   3059 	case MDOC_Xr:
   3060 		return 10;
   3061 	default:
   3062 		break;
   3063 	};
   3064 	return 0;
   3065 }
   3066