xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 7c539ecb)
1*7c539ecbSschwarze /*	$OpenBSD: mdoc_validate.c,v 1.278 2018/12/03 21:00:06 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
46dc98fe5Sschwarze  * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
539c2a57eSschwarze  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6f73abda9Skristaps  *
7f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps  *
11d1982c71Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d1982c71Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f73abda9Skristaps  */
19dadc3a61Sschwarze #include <sys/types.h>
2020fa2881Sschwarze #ifndef OSNAME
2120fa2881Sschwarze #include <sys/utsname.h>
2220fa2881Sschwarze #endif
2320fa2881Sschwarze 
24f73abda9Skristaps #include <assert.h>
25f73abda9Skristaps #include <ctype.h>
26d92dc4efSschwarze #include <limits.h>
273216dddfSschwarze #include <stdio.h>
28f73abda9Skristaps #include <stdlib.h>
29f73abda9Skristaps #include <string.h>
3020fa2881Sschwarze #include <time.h>
31f73abda9Skristaps 
324f4f7972Sschwarze #include "mandoc_aux.h"
33d1982c71Sschwarze #include "mandoc.h"
3419b6bef7Sschwarze #include "mandoc_xr.h"
35d1982c71Sschwarze #include "roff.h"
36d1982c71Sschwarze #include "mdoc.h"
37f6854d5cSschwarze #include "libmandoc.h"
38fa2127f9Sschwarze #include "roff_int.h"
39d1982c71Sschwarze #include "libmdoc.h"
40f73abda9Skristaps 
41f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42f73abda9Skristaps 
43ede1b9d0Sschwarze #define	POST_ARGS struct roff_man *mdoc
44f73abda9Skristaps 
457c2be9f8Sschwarze enum	check_ineq {
467c2be9f8Sschwarze 	CHECK_LT,
477c2be9f8Sschwarze 	CHECK_GT,
487c2be9f8Sschwarze 	CHECK_EQ
497c2be9f8Sschwarze };
507c2be9f8Sschwarze 
5198b8f00aSschwarze typedef	void	(*v_post)(POST_ARGS);
52f73abda9Skristaps 
538ccddcd3Sschwarze static	int	 build_list(struct roff_man *, int);
54ede1b9d0Sschwarze static	void	 check_argv(struct roff_man *,
553a0d07afSschwarze 			struct roff_node *, struct mdoc_argv *);
56ede1b9d0Sschwarze static	void	 check_args(struct roff_man *, struct roff_node *);
576dc98fe5Sschwarze static	void	 check_text(struct roff_man *, int, int, char *);
586dc98fe5Sschwarze static	void	 check_text_em(struct roff_man *, int, int, char *);
5948497dd5Sschwarze static	void	 check_toptext(struct roff_man *, int, int, const char *);
603a0d07afSschwarze static	int	 child_an(const struct roff_node *);
6114a309e3Sschwarze static	size_t		macro2len(enum roff_tok);
626050a3daSschwarze static	void	 rewrite_macro2len(struct roff_man *, char **);
634482121fSschwarze static	int	 similar(const char *, const char *);
6467c719adSschwarze 
65*7c539ecbSschwarze static	void	 post_abort(POST_ARGS);
6698b8f00aSschwarze static	void	 post_an(POST_ARGS);
673e642ba0Sschwarze static	void	 post_an_norm(POST_ARGS);
6898b8f00aSschwarze static	void	 post_at(POST_ARGS);
693e642ba0Sschwarze static	void	 post_bd(POST_ARGS);
7098b8f00aSschwarze static	void	 post_bf(POST_ARGS);
7198b8f00aSschwarze static	void	 post_bk(POST_ARGS);
7298b8f00aSschwarze static	void	 post_bl(POST_ARGS);
7398b8f00aSschwarze static	void	 post_bl_block(POST_ARGS);
7498b8f00aSschwarze static	void	 post_bl_head(POST_ARGS);
753e642ba0Sschwarze static	void	 post_bl_norm(POST_ARGS);
7698b8f00aSschwarze static	void	 post_bx(POST_ARGS);
7798b8f00aSschwarze static	void	 post_defaults(POST_ARGS);
783e642ba0Sschwarze static	void	 post_display(POST_ARGS);
7998b8f00aSschwarze static	void	 post_dd(POST_ARGS);
8004fbb99fSschwarze static	void	 post_delim(POST_ARGS);
81fe8e59edSschwarze static	void	 post_delim_nb(POST_ARGS);
8298b8f00aSschwarze static	void	 post_dt(POST_ARGS);
8398b8f00aSschwarze static	void	 post_en(POST_ARGS);
8498b8f00aSschwarze static	void	 post_es(POST_ARGS);
8598b8f00aSschwarze static	void	 post_eoln(POST_ARGS);
8698b8f00aSschwarze static	void	 post_ex(POST_ARGS);
8798b8f00aSschwarze static	void	 post_fa(POST_ARGS);
8898b8f00aSschwarze static	void	 post_fn(POST_ARGS);
8998b8f00aSschwarze static	void	 post_fname(POST_ARGS);
9098b8f00aSschwarze static	void	 post_fo(POST_ARGS);
9198b8f00aSschwarze static	void	 post_hyph(POST_ARGS);
9298b8f00aSschwarze static	void	 post_ignpar(POST_ARGS);
9398b8f00aSschwarze static	void	 post_it(POST_ARGS);
9498b8f00aSschwarze static	void	 post_lb(POST_ARGS);
9598b8f00aSschwarze static	void	 post_nd(POST_ARGS);
9698b8f00aSschwarze static	void	 post_nm(POST_ARGS);
9798b8f00aSschwarze static	void	 post_ns(POST_ARGS);
983e642ba0Sschwarze static	void	 post_obsolete(POST_ARGS);
9998b8f00aSschwarze static	void	 post_os(POST_ARGS);
10098b8f00aSschwarze static	void	 post_par(POST_ARGS);
1013e642ba0Sschwarze static	void	 post_prevpar(POST_ARGS);
10298b8f00aSschwarze static	void	 post_root(POST_ARGS);
10398b8f00aSschwarze static	void	 post_rs(POST_ARGS);
1048ccddcd3Sschwarze static	void	 post_rv(POST_ARGS);
10598b8f00aSschwarze static	void	 post_sh(POST_ARGS);
10698b8f00aSschwarze static	void	 post_sh_head(POST_ARGS);
10798b8f00aSschwarze static	void	 post_sh_name(POST_ARGS);
10898b8f00aSschwarze static	void	 post_sh_see_also(POST_ARGS);
10998b8f00aSschwarze static	void	 post_sh_authors(POST_ARGS);
11098b8f00aSschwarze static	void	 post_sm(POST_ARGS);
11198b8f00aSschwarze static	void	 post_st(POST_ARGS);
1123e642ba0Sschwarze static	void	 post_std(POST_ARGS);
113fe8e59edSschwarze static	void	 post_sx(POST_ARGS);
114bc205043Sschwarze static	void	 post_useless(POST_ARGS);
1155ae08040Sschwarze static	void	 post_xr(POST_ARGS);
116816c3c54Sschwarze static	void	 post_xx(POST_ARGS);
11798b8f00aSschwarze 
118b3f54129Sschwarze static	const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
1193e642ba0Sschwarze 	post_dd,	/* Dd */
1203e642ba0Sschwarze 	post_dt,	/* Dt */
1213e642ba0Sschwarze 	post_os,	/* Os */
1223e642ba0Sschwarze 	post_sh,	/* Sh */
1233e642ba0Sschwarze 	post_ignpar,	/* Ss */
1243e642ba0Sschwarze 	post_par,	/* Pp */
1253e642ba0Sschwarze 	post_display,	/* D1 */
1263e642ba0Sschwarze 	post_display,	/* Dl */
1273e642ba0Sschwarze 	post_display,	/* Bd */
1283e642ba0Sschwarze 	NULL,		/* Ed */
1293e642ba0Sschwarze 	post_bl,	/* Bl */
1303e642ba0Sschwarze 	NULL,		/* El */
1313e642ba0Sschwarze 	post_it,	/* It */
132fe8e59edSschwarze 	post_delim_nb,	/* Ad */
1333e642ba0Sschwarze 	post_an,	/* An */
13414a309e3Sschwarze 	NULL,		/* Ap */
1353e642ba0Sschwarze 	post_defaults,	/* Ar */
1363e642ba0Sschwarze 	NULL,		/* Cd */
137fe8e59edSschwarze 	post_delim_nb,	/* Cm */
138fe8e59edSschwarze 	post_delim_nb,	/* Dv */
139fe8e59edSschwarze 	post_delim_nb,	/* Er */
140fe8e59edSschwarze 	post_delim_nb,	/* Ev */
1413e642ba0Sschwarze 	post_ex,	/* Ex */
1423e642ba0Sschwarze 	post_fa,	/* Fa */
1433e642ba0Sschwarze 	NULL,		/* Fd */
144fe8e59edSschwarze 	post_delim_nb,	/* Fl */
1453e642ba0Sschwarze 	post_fn,	/* Fn */
146fe8e59edSschwarze 	post_delim_nb,	/* Ft */
147fe8e59edSschwarze 	post_delim_nb,	/* Ic */
148fe8e59edSschwarze 	post_delim_nb,	/* In */
1493e642ba0Sschwarze 	post_defaults,	/* Li */
1503e642ba0Sschwarze 	post_nd,	/* Nd */
1513e642ba0Sschwarze 	post_nm,	/* Nm */
152fe8e59edSschwarze 	post_delim_nb,	/* Op */
153*7c539ecbSschwarze 	post_abort,	/* Ot */
1543e642ba0Sschwarze 	post_defaults,	/* Pa */
1558ccddcd3Sschwarze 	post_rv,	/* Rv */
1563e642ba0Sschwarze 	post_st,	/* St */
157fe8e59edSschwarze 	post_delim_nb,	/* Va */
158fe8e59edSschwarze 	post_delim_nb,	/* Vt */
1595ae08040Sschwarze 	post_xr,	/* Xr */
1603e642ba0Sschwarze 	NULL,		/* %A */
1613e642ba0Sschwarze 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
1623e642ba0Sschwarze 	NULL,		/* %D */
1633e642ba0Sschwarze 	NULL,		/* %I */
1643e642ba0Sschwarze 	NULL,		/* %J */
1653e642ba0Sschwarze 	post_hyph,	/* %N */
1663e642ba0Sschwarze 	post_hyph,	/* %O */
1673e642ba0Sschwarze 	NULL,		/* %P */
1683e642ba0Sschwarze 	post_hyph,	/* %R */
1693e642ba0Sschwarze 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
1703e642ba0Sschwarze 	NULL,		/* %V */
1713e642ba0Sschwarze 	NULL,		/* Ac */
1721abead14Sschwarze 	NULL,		/* Ao */
173fe8e59edSschwarze 	post_delim_nb,	/* Aq */
1743e642ba0Sschwarze 	post_at,	/* At */
1753e642ba0Sschwarze 	NULL,		/* Bc */
1763e642ba0Sschwarze 	post_bf,	/* Bf */
1771abead14Sschwarze 	NULL,		/* Bo */
1783e642ba0Sschwarze 	NULL,		/* Bq */
179816c3c54Sschwarze 	post_xx,	/* Bsx */
1803e642ba0Sschwarze 	post_bx,	/* Bx */
1813e642ba0Sschwarze 	post_obsolete,	/* Db */
1823e642ba0Sschwarze 	NULL,		/* Dc */
1833e642ba0Sschwarze 	NULL,		/* Do */
1843e642ba0Sschwarze 	NULL,		/* Dq */
1853e642ba0Sschwarze 	NULL,		/* Ec */
1863e642ba0Sschwarze 	NULL,		/* Ef */
187fe8e59edSschwarze 	post_delim_nb,	/* Em */
1883e642ba0Sschwarze 	NULL,		/* Eo */
189816c3c54Sschwarze 	post_xx,	/* Fx */
190fe8e59edSschwarze 	post_delim_nb,	/* Ms */
191abcca917Sschwarze 	NULL,		/* No */
1923e642ba0Sschwarze 	post_ns,	/* Ns */
193816c3c54Sschwarze 	post_xx,	/* Nx */
194816c3c54Sschwarze 	post_xx,	/* Ox */
1953e642ba0Sschwarze 	NULL,		/* Pc */
1963e642ba0Sschwarze 	NULL,		/* Pf */
1971abead14Sschwarze 	NULL,		/* Po */
198fe8e59edSschwarze 	post_delim_nb,	/* Pq */
1993e642ba0Sschwarze 	NULL,		/* Qc */
200fe8e59edSschwarze 	post_delim_nb,	/* Ql */
2011abead14Sschwarze 	NULL,		/* Qo */
202fe8e59edSschwarze 	post_delim_nb,	/* Qq */
2033e642ba0Sschwarze 	NULL,		/* Re */
2043e642ba0Sschwarze 	post_rs,	/* Rs */
2053e642ba0Sschwarze 	NULL,		/* Sc */
2061abead14Sschwarze 	NULL,		/* So */
207fe8e59edSschwarze 	post_delim_nb,	/* Sq */
2083e642ba0Sschwarze 	post_sm,	/* Sm */
209fe8e59edSschwarze 	post_sx,	/* Sx */
210fe8e59edSschwarze 	post_delim_nb,	/* Sy */
211bc205043Sschwarze 	post_useless,	/* Tn */
212816c3c54Sschwarze 	post_xx,	/* Ux */
2133e642ba0Sschwarze 	NULL,		/* Xc */
2143e642ba0Sschwarze 	NULL,		/* Xo */
2153e642ba0Sschwarze 	post_fo,	/* Fo */
2163e642ba0Sschwarze 	NULL,		/* Fc */
2171abead14Sschwarze 	NULL,		/* Oo */
2183e642ba0Sschwarze 	NULL,		/* Oc */
2193e642ba0Sschwarze 	post_bk,	/* Bk */
2203e642ba0Sschwarze 	NULL,		/* Ek */
2213e642ba0Sschwarze 	post_eoln,	/* Bt */
22204fbb99fSschwarze 	post_obsolete,	/* Hf */
2233e642ba0Sschwarze 	post_obsolete,	/* Fr */
2243e642ba0Sschwarze 	post_eoln,	/* Ud */
2253e642ba0Sschwarze 	post_lb,	/* Lb */
226*7c539ecbSschwarze 	post_abort,	/* Lp */
227fe8e59edSschwarze 	post_delim_nb,	/* Lk */
2283e642ba0Sschwarze 	post_defaults,	/* Mt */
229fe8e59edSschwarze 	post_delim_nb,	/* Brq */
2301abead14Sschwarze 	NULL,		/* Bro */
2313e642ba0Sschwarze 	NULL,		/* Brc */
2323e642ba0Sschwarze 	NULL,		/* %C */
2333e642ba0Sschwarze 	post_es,	/* Es */
2343e642ba0Sschwarze 	post_en,	/* En */
235816c3c54Sschwarze 	post_xx,	/* Dx */
2363e642ba0Sschwarze 	NULL,		/* %Q */
2373e642ba0Sschwarze 	NULL,		/* %U */
2383e642ba0Sschwarze 	NULL,		/* Ta */
239f73abda9Skristaps };
240f73abda9Skristaps 
24120fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
24220fa2881Sschwarze 
24314a309e3Sschwarze static	const enum roff_tok rsord[RSORD_MAX] = {
24420fa2881Sschwarze 	MDOC__A,
24520fa2881Sschwarze 	MDOC__T,
24620fa2881Sschwarze 	MDOC__B,
24720fa2881Sschwarze 	MDOC__I,
24820fa2881Sschwarze 	MDOC__J,
24920fa2881Sschwarze 	MDOC__R,
25020fa2881Sschwarze 	MDOC__N,
25120fa2881Sschwarze 	MDOC__V,
2520397c682Sschwarze 	MDOC__U,
25320fa2881Sschwarze 	MDOC__P,
25420fa2881Sschwarze 	MDOC__Q,
2554e32ec8fSschwarze 	MDOC__C,
25620fa2881Sschwarze 	MDOC__D,
2574e32ec8fSschwarze 	MDOC__O
25820fa2881Sschwarze };
25920fa2881Sschwarze 
26019a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
26119a69263Sschwarze 	NULL,
26219a69263Sschwarze 	"NAME",
26319a69263Sschwarze 	"LIBRARY",
26419a69263Sschwarze 	"SYNOPSIS",
26519a69263Sschwarze 	"DESCRIPTION",
26603ab2f23Sdlg 	"CONTEXT",
26719a69263Sschwarze 	"IMPLEMENTATION NOTES",
26819a69263Sschwarze 	"RETURN VALUES",
26919a69263Sschwarze 	"ENVIRONMENT",
27019a69263Sschwarze 	"FILES",
27119a69263Sschwarze 	"EXIT STATUS",
27219a69263Sschwarze 	"EXAMPLES",
27319a69263Sschwarze 	"DIAGNOSTICS",
27419a69263Sschwarze 	"COMPATIBILITY",
27519a69263Sschwarze 	"ERRORS",
27619a69263Sschwarze 	"SEE ALSO",
27719a69263Sschwarze 	"STANDARDS",
27819a69263Sschwarze 	"HISTORY",
27919a69263Sschwarze 	"AUTHORS",
28019a69263Sschwarze 	"CAVEATS",
28119a69263Sschwarze 	"BUGS",
28219a69263Sschwarze 	"SECURITY CONSIDERATIONS",
28319a69263Sschwarze 	NULL
28419a69263Sschwarze };
285f73abda9Skristaps 
28649aff9f8Sschwarze 
287*7c539ecbSschwarze /* Validate the subtree rooted at mdoc->last. */
28898b8f00aSschwarze void
289396853b5Sschwarze mdoc_node_validate(struct roff_man *mdoc)
290f73abda9Skristaps {
2916dc98fe5Sschwarze 	struct roff_node *n, *np;
29214a309e3Sschwarze 	const v_post *p;
293f73abda9Skristaps 
294*7c539ecbSschwarze 	/*
295*7c539ecbSschwarze 	 * Translate obsolete macros to modern macros first
296*7c539ecbSschwarze 	 * such that later code does not need to look
297*7c539ecbSschwarze 	 * for the obsolete versions.
298*7c539ecbSschwarze 	 */
299*7c539ecbSschwarze 
300753701eeSschwarze 	n = mdoc->last;
301*7c539ecbSschwarze 	switch (n->tok) {
302*7c539ecbSschwarze 	case MDOC_Lp:
303*7c539ecbSschwarze 		n->tok = MDOC_Pp;
304*7c539ecbSschwarze 		break;
305*7c539ecbSschwarze 	case MDOC_Ot:
306*7c539ecbSschwarze 		post_obsolete(mdoc);
307*7c539ecbSschwarze 		n->tok = MDOC_Ft;
308*7c539ecbSschwarze 		break;
309*7c539ecbSschwarze 	default:
310*7c539ecbSschwarze 		break;
311*7c539ecbSschwarze 	}
312*7c539ecbSschwarze 
313*7c539ecbSschwarze 	/*
314*7c539ecbSschwarze 	 * Iterate over all children, recursing into each one
315*7c539ecbSschwarze 	 * in turn, depth-first.
316*7c539ecbSschwarze 	 */
317*7c539ecbSschwarze 
318396853b5Sschwarze 	mdoc->last = mdoc->last->child;
319396853b5Sschwarze 	while (mdoc->last != NULL) {
320396853b5Sschwarze 		mdoc_node_validate(mdoc);
321396853b5Sschwarze 		if (mdoc->last == n)
322396853b5Sschwarze 			mdoc->last = mdoc->last->child;
323396853b5Sschwarze 		else
324396853b5Sschwarze 			mdoc->last = mdoc->last->next;
325396853b5Sschwarze 	}
326f73abda9Skristaps 
327*7c539ecbSschwarze 	/* Finally validate the macro itself. */
328*7c539ecbSschwarze 
329396853b5Sschwarze 	mdoc->last = n;
330396853b5Sschwarze 	mdoc->next = ROFF_NEXT_SIBLING;
331753701eeSschwarze 	switch (n->type) {
332d1982c71Sschwarze 	case ROFFT_TEXT:
3336dc98fe5Sschwarze 		np = n->parent;
33479ca1811Sschwarze 		if (n->sec != SEC_SYNOPSIS ||
3356dc98fe5Sschwarze 		    (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
3363e642ba0Sschwarze 			check_text(mdoc, n->line, n->pos, n->string);
3376dc98fe5Sschwarze 		if (np->tok != MDOC_Ql && np->tok != MDOC_Dl &&
3386dc98fe5Sschwarze 		    (np->tok != MDOC_Bd ||
3396dc98fe5Sschwarze 		     (mdoc->flags & MDOC_LITERAL) == 0) &&
3406dc98fe5Sschwarze 		    (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
3416dc98fe5Sschwarze 		     np->parent->parent->norm->Bl.type != LIST_diag))
3426dc98fe5Sschwarze 			check_text_em(mdoc, n->line, n->pos, n->string);
3436dc98fe5Sschwarze 		if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
3446dc98fe5Sschwarze 		    (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
34548497dd5Sschwarze 			check_toptext(mdoc, n->line, n->pos, n->string);
3463e642ba0Sschwarze 		break;
3474c293873Sschwarze 	case ROFFT_COMMENT:
348d1982c71Sschwarze 	case ROFFT_EQN:
349d1982c71Sschwarze 	case ROFFT_TBL:
35098b8f00aSschwarze 		break;
351d1982c71Sschwarze 	case ROFFT_ROOT:
35298b8f00aSschwarze 		post_root(mdoc);
35398b8f00aSschwarze 		break;
3542791bd1cSschwarze 	default:
3553e642ba0Sschwarze 		check_args(mdoc, mdoc->last);
3566f9818f6Sschwarze 
3576f9818f6Sschwarze 		/*
3586f9818f6Sschwarze 		 * Closing delimiters are not special at the
3596f9818f6Sschwarze 		 * beginning of a block, opening delimiters
3606f9818f6Sschwarze 		 * are not special at the end.
3616f9818f6Sschwarze 		 */
3626f9818f6Sschwarze 
3636f9818f6Sschwarze 		if (n->child != NULL)
364c4b0939cSschwarze 			n->child->flags &= ~NODE_DELIMC;
3656f9818f6Sschwarze 		if (n->last != NULL)
366c4b0939cSschwarze 			n->last->flags &= ~NODE_DELIMO;
3676f9818f6Sschwarze 
3686f9818f6Sschwarze 		/* Call the macro's postprocessor. */
3696f9818f6Sschwarze 
37029478532Sschwarze 		if (n->tok < ROFF_MAX) {
37129478532Sschwarze 			switch(n->tok) {
37229478532Sschwarze 			case ROFF_br:
3736561cb23Sschwarze 			case ROFF_sp:
37429478532Sschwarze 				post_par(mdoc);
37529478532Sschwarze 				break;
37629478532Sschwarze 			default:
377c4d3fa85Sschwarze 				roff_validate(mdoc);
378c4d3fa85Sschwarze 				break;
37929478532Sschwarze 			}
38029478532Sschwarze 			break;
38129478532Sschwarze 		}
38229478532Sschwarze 
38329478532Sschwarze 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
384b3f54129Sschwarze 		p = mdoc_valids + (n->tok - MDOC_Dd);
38598b8f00aSschwarze 		if (*p)
38698b8f00aSschwarze 			(*p)(mdoc);
387396853b5Sschwarze 		if (mdoc->last == n)
388396853b5Sschwarze 			mdoc_state(mdoc, n);
38998b8f00aSschwarze 		break;
3902791bd1cSschwarze 	}
391f73abda9Skristaps }
392f73abda9Skristaps 
39398b8f00aSschwarze static void
394ede1b9d0Sschwarze check_args(struct roff_man *mdoc, struct roff_node *n)
395f73abda9Skristaps {
396f73abda9Skristaps 	int		 i;
397f73abda9Skristaps 
398f73abda9Skristaps 	if (NULL == n->args)
39920fa2881Sschwarze 		return;
400f73abda9Skristaps 
401f73abda9Skristaps 	assert(n->args->argc);
402f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
4037ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
404f73abda9Skristaps }
405f73abda9Skristaps 
40620fa2881Sschwarze static void
407ede1b9d0Sschwarze check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
408f73abda9Skristaps {
409f73abda9Skristaps 	int		 i;
410f73abda9Skristaps 
411f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
4127ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
413f73abda9Skristaps }
414f73abda9Skristaps 
41520fa2881Sschwarze static void
416ede1b9d0Sschwarze check_text(struct roff_man *mdoc, int ln, int pos, char *p)
417f73abda9Skristaps {
41804e980cbSschwarze 	char		*cp;
419769ee804Sschwarze 
4207ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
4211cdbf331Sschwarze 		return;
4221cdbf331Sschwarze 
4231cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
424dd5b31c3Sschwarze 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
425dd5b31c3Sschwarze 		    ln, pos + (int)(p - cp), NULL);
426f73abda9Skristaps }
427f73abda9Skristaps 
42898b8f00aSschwarze static void
4296dc98fe5Sschwarze check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
4306dc98fe5Sschwarze {
4316dc98fe5Sschwarze 	const struct roff_node	*np, *nn;
4326dc98fe5Sschwarze 	char			*cp;
4336dc98fe5Sschwarze 
4346dc98fe5Sschwarze 	np = mdoc->last->prev;
4356dc98fe5Sschwarze 	nn = mdoc->last->next;
4366dc98fe5Sschwarze 
4376dc98fe5Sschwarze 	/* Look for em-dashes wrongly encoded as "--". */
4386dc98fe5Sschwarze 
4396dc98fe5Sschwarze 	for (cp = p; *cp != '\0'; cp++) {
440dfdd4af9Sschwarze 		if (cp[0] != '-' || cp[1] != '-')
4416dc98fe5Sschwarze 			continue;
442dfdd4af9Sschwarze 		cp++;
4436dc98fe5Sschwarze 
4446dc98fe5Sschwarze 		/* Skip input sequences of more than two '-'. */
4456dc98fe5Sschwarze 
4466dc98fe5Sschwarze 		if (cp[1] == '-') {
4476dc98fe5Sschwarze 			while (cp[1] == '-')
4486dc98fe5Sschwarze 				cp++;
4496dc98fe5Sschwarze 			continue;
4506dc98fe5Sschwarze 		}
4516dc98fe5Sschwarze 
4526dc98fe5Sschwarze 		/* Skip "--" directly attached to something else. */
4536dc98fe5Sschwarze 
4546dc98fe5Sschwarze 		if ((cp - p > 1 && cp[-2] != ' ') ||
4556dc98fe5Sschwarze 		    (cp[1] != '\0' && cp[1] != ' '))
4566dc98fe5Sschwarze 			continue;
4576dc98fe5Sschwarze 
4586dc98fe5Sschwarze 		/* Require a letter right before or right afterwards. */
4596dc98fe5Sschwarze 
4606dc98fe5Sschwarze 		if ((cp - p > 2 ?
4616dc98fe5Sschwarze 		     isalpha((unsigned char)cp[-3]) :
4626dc98fe5Sschwarze 		     np != NULL &&
4636dc98fe5Sschwarze 		     np->type == ROFFT_TEXT &&
46489db6ba1Sschwarze 		     *np->string != '\0' &&
4656dc98fe5Sschwarze 		     isalpha((unsigned char)np->string[
4666dc98fe5Sschwarze 		       strlen(np->string) - 1])) ||
46700e80a2aSschwarze 		    (cp[1] != '\0' && cp[2] != '\0' ?
4686dc98fe5Sschwarze 		     isalpha((unsigned char)cp[2]) :
4696dc98fe5Sschwarze 		     nn != NULL &&
4706dc98fe5Sschwarze 		     nn->type == ROFFT_TEXT &&
4716dc98fe5Sschwarze 		     isalpha((unsigned char)*nn->string))) {
4726dc98fe5Sschwarze 			mandoc_msg(MANDOCERR_DASHDASH, mdoc->parse,
4736dc98fe5Sschwarze 			    ln, pos + (int)(cp - p) - 1, NULL);
4746dc98fe5Sschwarze 			break;
4756dc98fe5Sschwarze 		}
4766dc98fe5Sschwarze 	}
4776dc98fe5Sschwarze }
4786dc98fe5Sschwarze 
4796dc98fe5Sschwarze static void
48048497dd5Sschwarze check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
481f0c18971Sschwarze {
48248497dd5Sschwarze 	const char	*cp, *cpr;
48348497dd5Sschwarze 
48448497dd5Sschwarze 	if (*p == '\0')
48548497dd5Sschwarze 		return;
486f0c18971Sschwarze 
487f0c18971Sschwarze 	if ((cp = strstr(p, "OpenBSD")) != NULL)
488f0c18971Sschwarze 		mandoc_msg(MANDOCERR_BX, mdoc->parse,
489f0c18971Sschwarze 		    ln, pos + (cp - p), "Ox");
490f0c18971Sschwarze 	if ((cp = strstr(p, "NetBSD")) != NULL)
491f0c18971Sschwarze 		mandoc_msg(MANDOCERR_BX, mdoc->parse,
492f0c18971Sschwarze 		    ln, pos + (cp - p), "Nx");
493f0c18971Sschwarze 	if ((cp = strstr(p, "FreeBSD")) != NULL)
494f0c18971Sschwarze 		mandoc_msg(MANDOCERR_BX, mdoc->parse,
495f0c18971Sschwarze 		    ln, pos + (cp - p), "Fx");
496f0c18971Sschwarze 	if ((cp = strstr(p, "DragonFly")) != NULL)
497f0c18971Sschwarze 		mandoc_msg(MANDOCERR_BX, mdoc->parse,
498f0c18971Sschwarze 		    ln, pos + (cp - p), "Dx");
49948497dd5Sschwarze 
50048497dd5Sschwarze 	cp = p;
50148497dd5Sschwarze 	while ((cp = strstr(cp + 1, "()")) != NULL) {
50248497dd5Sschwarze 		for (cpr = cp - 1; cpr >= p; cpr--)
50348497dd5Sschwarze 			if (*cpr != '_' && !isalnum((unsigned char)*cpr))
50448497dd5Sschwarze 				break;
50548497dd5Sschwarze 		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
50648497dd5Sschwarze 			cpr++;
50748497dd5Sschwarze 			mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse,
50848497dd5Sschwarze 			    ln, pos + (cpr - p),
50948497dd5Sschwarze 			    "%.*s()", (int)(cp - cpr), cpr);
51048497dd5Sschwarze 		}
51148497dd5Sschwarze 	}
512f0c18971Sschwarze }
513f0c18971Sschwarze 
514f0c18971Sschwarze static void
515*7c539ecbSschwarze post_abort(POST_ARGS)
516*7c539ecbSschwarze {
517*7c539ecbSschwarze 	abort();
518*7c539ecbSschwarze }
519*7c539ecbSschwarze 
520*7c539ecbSschwarze static void
52104fbb99fSschwarze post_delim(POST_ARGS)
52204fbb99fSschwarze {
52304fbb99fSschwarze 	const struct roff_node	*nch;
524fe8e59edSschwarze 	const char		*lc;
525fe8e59edSschwarze 	enum mdelim		 delim;
526fe8e59edSschwarze 	enum roff_tok		 tok;
527fe8e59edSschwarze 
528fe8e59edSschwarze 	tok = mdoc->last->tok;
529fe8e59edSschwarze 	nch = mdoc->last->last;
530fe8e59edSschwarze 	if (nch == NULL || nch->type != ROFFT_TEXT)
531fe8e59edSschwarze 		return;
532fe8e59edSschwarze 	lc = strchr(nch->string, '\0') - 1;
533fe8e59edSschwarze 	if (lc < nch->string)
534fe8e59edSschwarze 		return;
535fe8e59edSschwarze 	delim = mdoc_isdelim(lc);
536fe8e59edSschwarze 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
537fe8e59edSschwarze 		return;
538fe8e59edSschwarze 	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
539fe8e59edSschwarze 	    tok == MDOC_Ss || tok == MDOC_Fo))
540fe8e59edSschwarze 		return;
541fe8e59edSschwarze 
542fe8e59edSschwarze 	mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse,
543fe8e59edSschwarze 	    nch->line, nch->pos + (lc - nch->string),
544fe8e59edSschwarze 	    "%s%s %s", roff_name[tok],
545fe8e59edSschwarze 	    nch == mdoc->last->child ? "" : " ...", nch->string);
546fe8e59edSschwarze }
547fe8e59edSschwarze 
548fe8e59edSschwarze static void
549fe8e59edSschwarze post_delim_nb(POST_ARGS)
550fe8e59edSschwarze {
551fe8e59edSschwarze 	const struct roff_node	*nch;
552b7c50e87Sschwarze 	const char		*lc, *cp;
553b7c50e87Sschwarze 	int			 nw;
55404fbb99fSschwarze 	enum mdelim		 delim;
555b7c50e87Sschwarze 	enum roff_tok		 tok;
55604fbb99fSschwarze 
557b7c50e87Sschwarze 	/*
558b7c50e87Sschwarze 	 * Find candidates: at least two bytes,
559b7c50e87Sschwarze 	 * the last one a closing or middle delimiter.
560b7c50e87Sschwarze 	 */
561b7c50e87Sschwarze 
562b7c50e87Sschwarze 	tok = mdoc->last->tok;
56304fbb99fSschwarze 	nch = mdoc->last->last;
56404fbb99fSschwarze 	if (nch == NULL || nch->type != ROFFT_TEXT)
56504fbb99fSschwarze 		return;
56604fbb99fSschwarze 	lc = strchr(nch->string, '\0') - 1;
56704fbb99fSschwarze 	if (lc <= nch->string)
56804fbb99fSschwarze 		return;
56904fbb99fSschwarze 	delim = mdoc_isdelim(lc);
57004fbb99fSschwarze 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
57104fbb99fSschwarze 		return;
572b7c50e87Sschwarze 
573b7c50e87Sschwarze 	/*
574b7c50e87Sschwarze 	 * Reduce false positives by allowing various cases.
575b7c50e87Sschwarze 	 */
576b7c50e87Sschwarze 
577b7c50e87Sschwarze 	/* Escaped delimiters. */
578b7c50e87Sschwarze 	if (lc > nch->string + 1 && lc[-2] == '\\' &&
579b7c50e87Sschwarze 	    (lc[-1] == '&' || lc[-1] == 'e'))
580b7c50e87Sschwarze 		return;
581b7c50e87Sschwarze 
582b7c50e87Sschwarze 	/* Specific byte sequences. */
583b7c50e87Sschwarze 	switch (*lc) {
584b7c50e87Sschwarze 	case ')':
585b7c50e87Sschwarze 		for (cp = lc; cp >= nch->string; cp--)
586b7c50e87Sschwarze 			if (*cp == '(')
587b7c50e87Sschwarze 				return;
588b7c50e87Sschwarze 		break;
589b7c50e87Sschwarze 	case '.':
590b7c50e87Sschwarze 		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
591b7c50e87Sschwarze 			return;
592b7c50e87Sschwarze 		if (lc[-1] == '.')
593b7c50e87Sschwarze 			return;
594b7c50e87Sschwarze 		break;
595b7c50e87Sschwarze 	case ';':
596b7c50e87Sschwarze 		if (tok == MDOC_Vt)
597b7c50e87Sschwarze 			return;
598b7c50e87Sschwarze 		break;
599b7c50e87Sschwarze 	case '?':
600b7c50e87Sschwarze 		if (lc[-1] == '?')
601b7c50e87Sschwarze 			return;
602b7c50e87Sschwarze 		break;
603b7c50e87Sschwarze 	case ']':
604b7c50e87Sschwarze 		for (cp = lc; cp >= nch->string; cp--)
605b7c50e87Sschwarze 			if (*cp == '[')
606b7c50e87Sschwarze 				return;
607b7c50e87Sschwarze 		break;
608b7c50e87Sschwarze 	case '|':
609b7c50e87Sschwarze 		if (lc == nch->string + 1 && lc[-1] == '|')
610b7c50e87Sschwarze 			return;
611b7c50e87Sschwarze 	default:
612b7c50e87Sschwarze 		break;
613b7c50e87Sschwarze 	}
614b7c50e87Sschwarze 
615b7c50e87Sschwarze 	/* Exactly two non-alphanumeric bytes. */
616b7c50e87Sschwarze 	if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
617b7c50e87Sschwarze 		return;
618b7c50e87Sschwarze 
619b7c50e87Sschwarze 	/* At least three alphabetic words with a sentence ending. */
620b7c50e87Sschwarze 	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
6211abead14Sschwarze 	    tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
622b7c50e87Sschwarze 		nw = 0;
623b7c50e87Sschwarze 		for (cp = lc - 1; cp >= nch->string; cp--) {
624b7c50e87Sschwarze 			if (*cp == ' ') {
625b7c50e87Sschwarze 				nw++;
626b7c50e87Sschwarze 				if (cp > nch->string && cp[-1] == ',')
627b7c50e87Sschwarze 					cp--;
628b7c50e87Sschwarze 			} else if (isalpha((unsigned int)*cp)) {
629b7c50e87Sschwarze 				if (nw > 1)
630b7c50e87Sschwarze 					return;
631b7c50e87Sschwarze 			} else
632b7c50e87Sschwarze 				break;
633b7c50e87Sschwarze 		}
634b7c50e87Sschwarze 	}
635b7c50e87Sschwarze 
636fe8e59edSschwarze 	mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
63704fbb99fSschwarze 	    nch->line, nch->pos + (lc - nch->string),
638b7c50e87Sschwarze 	    "%s%s %s", roff_name[tok],
63904fbb99fSschwarze 	    nch == mdoc->last->child ? "" : " ...", nch->string);
64004fbb99fSschwarze }
64104fbb99fSschwarze 
64204fbb99fSschwarze static void
6433e642ba0Sschwarze post_bl_norm(POST_ARGS)
644f73abda9Skristaps {
6453e642ba0Sschwarze 	struct roff_node *n;
646aa99c14fSschwarze 	struct mdoc_argv *argv, *wa;
6474a9f685fSschwarze 	int		  i;
648aa99c14fSschwarze 	enum mdocargt	  mdoclt;
6494a9f685fSschwarze 	enum mdoc_list	  lt;
650f73abda9Skristaps 
6513e642ba0Sschwarze 	n = mdoc->last->parent;
6523e642ba0Sschwarze 	n->norm->Bl.type = LIST__NONE;
653f73abda9Skristaps 
6546093755cSschwarze 	/*
6556093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
6566093755cSschwarze 	 * the first mentioned list type and warn about any remaining
6576093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
6586093755cSschwarze 	 */
659f73abda9Skristaps 
660dadc3a61Sschwarze 	wa = (n->args == NULL) ? NULL : n->args->argv;
661aa99c14fSschwarze 	mdoclt = MDOC_ARG_MAX;
6626093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
6634a9f685fSschwarze 		argv = n->args->argv + i;
6646093755cSschwarze 		lt = LIST__NONE;
6654a9f685fSschwarze 		switch (argv->arg) {
6666093755cSschwarze 		/* Set list types. */
66749aff9f8Sschwarze 		case MDOC_Bullet:
6686093755cSschwarze 			lt = LIST_bullet;
6696093755cSschwarze 			break;
67049aff9f8Sschwarze 		case MDOC_Dash:
6716093755cSschwarze 			lt = LIST_dash;
6726093755cSschwarze 			break;
67349aff9f8Sschwarze 		case MDOC_Enum:
6746093755cSschwarze 			lt = LIST_enum;
6756093755cSschwarze 			break;
67649aff9f8Sschwarze 		case MDOC_Hyphen:
6776093755cSschwarze 			lt = LIST_hyphen;
6786093755cSschwarze 			break;
67949aff9f8Sschwarze 		case MDOC_Item:
6806093755cSschwarze 			lt = LIST_item;
6816093755cSschwarze 			break;
68249aff9f8Sschwarze 		case MDOC_Tag:
6836093755cSschwarze 			lt = LIST_tag;
6846093755cSschwarze 			break;
68549aff9f8Sschwarze 		case MDOC_Diag:
6866093755cSschwarze 			lt = LIST_diag;
6876093755cSschwarze 			break;
68849aff9f8Sschwarze 		case MDOC_Hang:
6896093755cSschwarze 			lt = LIST_hang;
6906093755cSschwarze 			break;
69149aff9f8Sschwarze 		case MDOC_Ohang:
6926093755cSschwarze 			lt = LIST_ohang;
6936093755cSschwarze 			break;
69449aff9f8Sschwarze 		case MDOC_Inset:
6956093755cSschwarze 			lt = LIST_inset;
6966093755cSschwarze 			break;
69749aff9f8Sschwarze 		case MDOC_Column:
6986093755cSschwarze 			lt = LIST_column;
69964d728e4Sschwarze 			break;
7006093755cSschwarze 		/* Set list arguments. */
70149aff9f8Sschwarze 		case MDOC_Compact:
7024a9f685fSschwarze 			if (n->norm->Bl.comp)
7034a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
7044a9f685fSschwarze 				    mdoc->parse, argv->line,
7054a9f685fSschwarze 				    argv->pos, "Bl -compact");
7064a9f685fSschwarze 			n->norm->Bl.comp = 1;
70750e63e03Sschwarze 			break;
70849aff9f8Sschwarze 		case MDOC_Width:
709aa99c14fSschwarze 			wa = argv;
7104a9f685fSschwarze 			if (0 == argv->sz) {
7114a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7124a9f685fSschwarze 				    mdoc->parse, argv->line,
7134a9f685fSschwarze 				    argv->pos, "Bl -width");
7144a9f685fSschwarze 				n->norm->Bl.width = "0n";
71522972b14Sschwarze 				break;
71622972b14Sschwarze 			}
7174a9f685fSschwarze 			if (NULL != n->norm->Bl.width)
7184a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
7194a9f685fSschwarze 				    mdoc->parse, argv->line,
7204a9f685fSschwarze 				    argv->pos, "Bl -width %s",
7214a9f685fSschwarze 				    argv->value[0]);
7226050a3daSschwarze 			rewrite_macro2len(mdoc, argv->value);
7234a9f685fSschwarze 			n->norm->Bl.width = argv->value[0];
72464d728e4Sschwarze 			break;
72549aff9f8Sschwarze 		case MDOC_Offset:
7264a9f685fSschwarze 			if (0 == argv->sz) {
7274a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7284a9f685fSschwarze 				    mdoc->parse, argv->line,
7294a9f685fSschwarze 				    argv->pos, "Bl -offset");
73031e23753Sschwarze 				break;
73131e23753Sschwarze 			}
7324a9f685fSschwarze 			if (NULL != n->norm->Bl.offs)
7334a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
7344a9f685fSschwarze 				    mdoc->parse, argv->line,
7354a9f685fSschwarze 				    argv->pos, "Bl -offset %s",
7364a9f685fSschwarze 				    argv->value[0]);
7376050a3daSschwarze 			rewrite_macro2len(mdoc, argv->value);
7384a9f685fSschwarze 			n->norm->Bl.offs = argv->value[0];
739f73abda9Skristaps 			break;
740ddce0b0cSschwarze 		default:
741ddce0b0cSschwarze 			continue;
742f73abda9Skristaps 		}
743dc0d8bb2Sschwarze 		if (LIST__NONE == lt)
744dc0d8bb2Sschwarze 			continue;
745aa99c14fSschwarze 		mdoclt = argv->arg;
746f73abda9Skristaps 
7476093755cSschwarze 		/* Check: multiple list types. */
7486093755cSschwarze 
749dc0d8bb2Sschwarze 		if (LIST__NONE != n->norm->Bl.type) {
750bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_REP,
751dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
752bd594191Sschwarze 			    "Bl -%s", mdoc_argnames[argv->arg]);
753dc0d8bb2Sschwarze 			continue;
754769ee804Sschwarze 		}
7556093755cSschwarze 
7566093755cSschwarze 		/* The list type should come first. */
7576093755cSschwarze 
7588c62fbf5Sschwarze 		if (n->norm->Bl.width ||
7598c62fbf5Sschwarze 		    n->norm->Bl.offs ||
7608c62fbf5Sschwarze 		    n->norm->Bl.comp)
761bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
762bd594191Sschwarze 			    mdoc->parse, n->line, n->pos, "Bl -%s",
76366788495Sschwarze 			    mdoc_argnames[n->args->argv[0].arg]);
764dc0d8bb2Sschwarze 
765dc0d8bb2Sschwarze 		n->norm->Bl.type = lt;
766dc0d8bb2Sschwarze 		if (LIST_column == lt) {
767dc0d8bb2Sschwarze 			n->norm->Bl.ncols = argv->sz;
768dc0d8bb2Sschwarze 			n->norm->Bl.cols = (void *)argv->value;
769dc0d8bb2Sschwarze 		}
7706093755cSschwarze 	}
7716093755cSschwarze 
7726093755cSschwarze 	/* Allow lists to default to LIST_item. */
7736093755cSschwarze 
7748c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
775bd594191Sschwarze 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
776bd594191Sschwarze 		    n->line, n->pos, "Bl");
7778c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
7782c7eb483Sschwarze 		mdoclt = MDOC_Item;
7796e03d529Sschwarze 	}
780f73abda9Skristaps 
78164d728e4Sschwarze 	/*
78264d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
78364d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
7845eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
7855eced068Sschwarze 	 * no warning.
78664d728e4Sschwarze 	 */
78764d728e4Sschwarze 
7888c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
78949aff9f8Sschwarze 	case LIST_tag:
790162c3bafSschwarze 		if (n->norm->Bl.width == NULL)
791bd594191Sschwarze 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
792bd594191Sschwarze 			    n->line, n->pos, "Bl -tag");
793f73abda9Skristaps 		break;
79449aff9f8Sschwarze 	case LIST_column:
79549aff9f8Sschwarze 	case LIST_diag:
79649aff9f8Sschwarze 	case LIST_ohang:
79749aff9f8Sschwarze 	case LIST_inset:
79849aff9f8Sschwarze 	case LIST_item:
799162c3bafSschwarze 		if (n->norm->Bl.width != NULL)
800aa99c14fSschwarze 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
801aa99c14fSschwarze 			    wa->line, wa->pos, "Bl -%s",
802aa99c14fSschwarze 			    mdoc_argnames[mdoclt]);
803162c3bafSschwarze 		n->norm->Bl.width = NULL;
8046093755cSschwarze 		break;
80549aff9f8Sschwarze 	case LIST_bullet:
80649aff9f8Sschwarze 	case LIST_dash:
80749aff9f8Sschwarze 	case LIST_hyphen:
808162c3bafSschwarze 		if (n->norm->Bl.width == NULL)
8095eced068Sschwarze 			n->norm->Bl.width = "2n";
8105eced068Sschwarze 		break;
81149aff9f8Sschwarze 	case LIST_enum:
812162c3bafSschwarze 		if (n->norm->Bl.width == NULL)
8135eced068Sschwarze 			n->norm->Bl.width = "3n";
8145eced068Sschwarze 		break;
81564d728e4Sschwarze 	default:
816f73abda9Skristaps 		break;
81764d728e4Sschwarze 	}
818f73abda9Skristaps }
819f73abda9Skristaps 
82098b8f00aSschwarze static void
8213e642ba0Sschwarze post_bd(POST_ARGS)
822f73abda9Skristaps {
8233e642ba0Sschwarze 	struct roff_node *n;
8244a9f685fSschwarze 	struct mdoc_argv *argv;
8254a9f685fSschwarze 	int		  i;
8264a9f685fSschwarze 	enum mdoc_disp	  dt;
827f73abda9Skristaps 
8283e642ba0Sschwarze 	n = mdoc->last;
82931e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
8304a9f685fSschwarze 		argv = n->args->argv + i;
83131e23753Sschwarze 		dt = DISP__NONE;
83231e23753Sschwarze 
8334a9f685fSschwarze 		switch (argv->arg) {
83449aff9f8Sschwarze 		case MDOC_Centred:
8352065e47aSschwarze 			dt = DISP_centered;
83631e23753Sschwarze 			break;
83749aff9f8Sschwarze 		case MDOC_Ragged:
83831e23753Sschwarze 			dt = DISP_ragged;
83931e23753Sschwarze 			break;
84049aff9f8Sschwarze 		case MDOC_Unfilled:
84131e23753Sschwarze 			dt = DISP_unfilled;
84231e23753Sschwarze 			break;
84349aff9f8Sschwarze 		case MDOC_Filled:
84431e23753Sschwarze 			dt = DISP_filled;
84531e23753Sschwarze 			break;
84649aff9f8Sschwarze 		case MDOC_Literal:
84731e23753Sschwarze 			dt = DISP_literal;
848f73abda9Skristaps 			break;
84949aff9f8Sschwarze 		case MDOC_File:
850bd594191Sschwarze 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
851bd594191Sschwarze 			    n->line, n->pos, NULL);
8521164a325Sschwarze 			break;
85349aff9f8Sschwarze 		case MDOC_Offset:
8544a9f685fSschwarze 			if (0 == argv->sz) {
8554a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
8564a9f685fSschwarze 				    mdoc->parse, argv->line,
8574a9f685fSschwarze 				    argv->pos, "Bd -offset");
858f73abda9Skristaps 				break;
859f73abda9Skristaps 			}
8604a9f685fSschwarze 			if (NULL != n->norm->Bd.offs)
8614a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
8624a9f685fSschwarze 				    mdoc->parse, argv->line,
8634a9f685fSschwarze 				    argv->pos, "Bd -offset %s",
8644a9f685fSschwarze 				    argv->value[0]);
8656050a3daSschwarze 			rewrite_macro2len(mdoc, argv->value);
8664a9f685fSschwarze 			n->norm->Bd.offs = argv->value[0];
86731e23753Sschwarze 			break;
86849aff9f8Sschwarze 		case MDOC_Compact:
8694a9f685fSschwarze 			if (n->norm->Bd.comp)
8704a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
8714a9f685fSschwarze 				    mdoc->parse, argv->line,
8724a9f685fSschwarze 				    argv->pos, "Bd -compact");
8734a9f685fSschwarze 			n->norm->Bd.comp = 1;
87431e23753Sschwarze 			break;
87531e23753Sschwarze 		default:
87631e23753Sschwarze 			abort();
87731e23753Sschwarze 		}
878dc0d8bb2Sschwarze 		if (DISP__NONE == dt)
879dc0d8bb2Sschwarze 			continue;
88031e23753Sschwarze 
881dc0d8bb2Sschwarze 		if (DISP__NONE == n->norm->Bd.type)
8828c62fbf5Sschwarze 			n->norm->Bd.type = dt;
883dc0d8bb2Sschwarze 		else
884bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BD_REP,
885dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
886bd594191Sschwarze 			    "Bd -%s", mdoc_argnames[argv->arg]);
88731e23753Sschwarze 	}
88831e23753Sschwarze 
8898c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
890bd594191Sschwarze 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
891bd594191Sschwarze 		    n->line, n->pos, "Bd");
8928c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
89331e23753Sschwarze 	}
894f73abda9Skristaps }
895f73abda9Skristaps 
8968ccddcd3Sschwarze /*
8978ccddcd3Sschwarze  * Stand-alone line macros.
8988ccddcd3Sschwarze  */
8998ccddcd3Sschwarze 
90098b8f00aSschwarze static void
9013e642ba0Sschwarze post_an_norm(POST_ARGS)
902f73abda9Skristaps {
9033e642ba0Sschwarze 	struct roff_node *n;
904aa99c14fSschwarze 	struct mdoc_argv *argv;
905aa99c14fSschwarze 	size_t	 i;
906f73abda9Skristaps 
9073e642ba0Sschwarze 	n = mdoc->last;
908aa99c14fSschwarze 	if (n->args == NULL)
90998b8f00aSschwarze 		return;
910769ee804Sschwarze 
911aa99c14fSschwarze 	for (i = 1; i < n->args->argc; i++) {
912aa99c14fSschwarze 		argv = n->args->argv + i;
913aa99c14fSschwarze 		mandoc_vmsg(MANDOCERR_AN_REP,
914aa99c14fSschwarze 		    mdoc->parse, argv->line, argv->pos,
915aa99c14fSschwarze 		    "An -%s", mdoc_argnames[argv->arg]);
916aa99c14fSschwarze 	}
9177c2be9f8Sschwarze 
918aa99c14fSschwarze 	argv = n->args->argv;
919aa99c14fSschwarze 	if (argv->arg == MDOC_Split)
9208c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
921aa99c14fSschwarze 	else if (argv->arg == MDOC_Nosplit)
9228c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
923769ee804Sschwarze 	else
924769ee804Sschwarze 		abort();
925f73abda9Skristaps }
926f73abda9Skristaps 
92798b8f00aSschwarze static void
9288ccddcd3Sschwarze post_eoln(POST_ARGS)
9298ccddcd3Sschwarze {
9308ccddcd3Sschwarze 	struct roff_node	*n;
9318ccddcd3Sschwarze 
932bc205043Sschwarze 	post_useless(mdoc);
9338ccddcd3Sschwarze 	n = mdoc->last;
9348ccddcd3Sschwarze 	if (n->child != NULL)
93514a309e3Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line,
93614a309e3Sschwarze 		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
9378ccddcd3Sschwarze 
9388ccddcd3Sschwarze 	while (n->child != NULL)
9398ccddcd3Sschwarze 		roff_node_delete(mdoc, n->child);
9408ccddcd3Sschwarze 
9418ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
9428ccddcd3Sschwarze 	    "is currently in beta test." : "currently under development.");
9438ccddcd3Sschwarze 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
9448ccddcd3Sschwarze 	mdoc->last = n;
9458ccddcd3Sschwarze }
9468ccddcd3Sschwarze 
9478ccddcd3Sschwarze static int
9488ccddcd3Sschwarze build_list(struct roff_man *mdoc, int tok)
9498ccddcd3Sschwarze {
9508ccddcd3Sschwarze 	struct roff_node	*n;
9518ccddcd3Sschwarze 	int			 ic;
9528ccddcd3Sschwarze 
9538ccddcd3Sschwarze 	n = mdoc->last->next;
9548ccddcd3Sschwarze 	for (ic = 1;; ic++) {
9558ccddcd3Sschwarze 		roff_elem_alloc(mdoc, n->line, n->pos, tok);
9568ccddcd3Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
9578ccddcd3Sschwarze 		mdoc_node_relink(mdoc, n);
9588ccddcd3Sschwarze 		n = mdoc->last = mdoc->last->parent;
9598ccddcd3Sschwarze 		mdoc->next = ROFF_NEXT_SIBLING;
9608ccddcd3Sschwarze 		if (n->next == NULL)
9618ccddcd3Sschwarze 			return ic;
9628ccddcd3Sschwarze 		if (ic > 1 || n->next->next != NULL) {
9638ccddcd3Sschwarze 			roff_word_alloc(mdoc, n->line, n->pos, ",");
9648ccddcd3Sschwarze 			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
9658ccddcd3Sschwarze 		}
9668ccddcd3Sschwarze 		n = mdoc->last->next;
9678ccddcd3Sschwarze 		if (n->next == NULL) {
9688ccddcd3Sschwarze 			roff_word_alloc(mdoc, n->line, n->pos, "and");
9698ccddcd3Sschwarze 			mdoc->last->flags |= NODE_NOSRC;
9708ccddcd3Sschwarze 		}
9718ccddcd3Sschwarze 	}
9728ccddcd3Sschwarze }
9738ccddcd3Sschwarze 
9748ccddcd3Sschwarze static void
9758ccddcd3Sschwarze post_ex(POST_ARGS)
9768ccddcd3Sschwarze {
9778ccddcd3Sschwarze 	struct roff_node	*n;
9788ccddcd3Sschwarze 	int			 ic;
9798ccddcd3Sschwarze 
9808ccddcd3Sschwarze 	post_std(mdoc);
9818ccddcd3Sschwarze 
9828ccddcd3Sschwarze 	n = mdoc->last;
9838ccddcd3Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
9848ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "The");
9858ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
9868ccddcd3Sschwarze 
9878ccddcd3Sschwarze 	if (mdoc->last->next != NULL)
9888ccddcd3Sschwarze 		ic = build_list(mdoc, MDOC_Nm);
9898ccddcd3Sschwarze 	else if (mdoc->meta.name != NULL) {
9908ccddcd3Sschwarze 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
9918ccddcd3Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
9928ccddcd3Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
9938ccddcd3Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
9948ccddcd3Sschwarze 		mdoc->last = mdoc->last->parent;
9958ccddcd3Sschwarze 		mdoc->next = ROFF_NEXT_SIBLING;
9968ccddcd3Sschwarze 		ic = 1;
9978ccddcd3Sschwarze 	} else {
9988ccddcd3Sschwarze 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
9998ccddcd3Sschwarze 		    n->line, n->pos, "Ex");
10008ccddcd3Sschwarze 		ic = 0;
10018ccddcd3Sschwarze 	}
10028ccddcd3Sschwarze 
10038ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos,
10048ccddcd3Sschwarze 	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
10058ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
10068ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos,
10078ccddcd3Sschwarze 	    "on success, and\\~>0 if an error occurs.");
10088ccddcd3Sschwarze 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
10098ccddcd3Sschwarze 	mdoc->last = n;
10108ccddcd3Sschwarze }
10118ccddcd3Sschwarze 
10128ccddcd3Sschwarze static void
10138ccddcd3Sschwarze post_lb(POST_ARGS)
10148ccddcd3Sschwarze {
10158ccddcd3Sschwarze 	struct roff_node	*n;
10168ccddcd3Sschwarze 
1017fe8e59edSschwarze 	post_delim_nb(mdoc);
101804fbb99fSschwarze 
10198ccddcd3Sschwarze 	n = mdoc->last;
10208ccddcd3Sschwarze 	assert(n->child->type == ROFFT_TEXT);
10218ccddcd3Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
10228ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "library");
10238ccddcd3Sschwarze 	mdoc->last->flags = NODE_NOSRC;
1024965f5c87Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
10258ccddcd3Sschwarze 	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
10268ccddcd3Sschwarze 	mdoc->last = mdoc->last->next;
1027965f5c87Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
10288ccddcd3Sschwarze 	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
10298ccddcd3Sschwarze 	mdoc->last = n;
10308ccddcd3Sschwarze }
10318ccddcd3Sschwarze 
10328ccddcd3Sschwarze static void
10338ccddcd3Sschwarze post_rv(POST_ARGS)
10348ccddcd3Sschwarze {
10358ccddcd3Sschwarze 	struct roff_node	*n;
10368ccddcd3Sschwarze 	int			 ic;
10378ccddcd3Sschwarze 
10388ccddcd3Sschwarze 	post_std(mdoc);
10398ccddcd3Sschwarze 
10408ccddcd3Sschwarze 	n = mdoc->last;
10418ccddcd3Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
10428ccddcd3Sschwarze 	if (n->child != NULL) {
10438ccddcd3Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos, "The");
10448ccddcd3Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
10458ccddcd3Sschwarze 		ic = build_list(mdoc, MDOC_Fn);
10468ccddcd3Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos,
10478ccddcd3Sschwarze 		    ic > 1 ? "functions return" : "function returns");
10488ccddcd3Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
10498ccddcd3Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos,
10508ccddcd3Sschwarze 		    "the value\\~0 if successful;");
10518ccddcd3Sschwarze 	} else
10528ccddcd3Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
10538ccddcd3Sschwarze 		    "completion, the value\\~0 is returned;");
10548ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
10558ccddcd3Sschwarze 
10568ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
10578ccddcd3Sschwarze 	    "the value\\~\\-1 is returned and the global variable");
10588ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
10598ccddcd3Sschwarze 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
10608ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
10618ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "errno");
10628ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
10638ccddcd3Sschwarze 	mdoc->last = mdoc->last->parent;
10648ccddcd3Sschwarze 	mdoc->next = ROFF_NEXT_SIBLING;
10658ccddcd3Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos,
10668ccddcd3Sschwarze 	    "is set to indicate the error.");
10678ccddcd3Sschwarze 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
10688ccddcd3Sschwarze 	mdoc->last = n;
10698ccddcd3Sschwarze }
10708ccddcd3Sschwarze 
10718ccddcd3Sschwarze static void
10723e642ba0Sschwarze post_std(POST_ARGS)
1073f73abda9Skristaps {
10743e642ba0Sschwarze 	struct roff_node *n;
1075f73abda9Skristaps 
1076fe8e59edSschwarze 	post_delim(mdoc);
1077fe8e59edSschwarze 
10783e642ba0Sschwarze 	n = mdoc->last;
10793e642ba0Sschwarze 	if (n->args && n->args->argc == 1)
10803e642ba0Sschwarze 		if (n->args->argv[0].arg == MDOC_Std)
108198b8f00aSschwarze 			return;
1082f73abda9Skristaps 
108366788495Sschwarze 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
108414a309e3Sschwarze 	    n->line, n->pos, roff_name[n->tok]);
10856093755cSschwarze }
10866093755cSschwarze 
108798b8f00aSschwarze static void
10888ccddcd3Sschwarze post_st(POST_ARGS)
10898ccddcd3Sschwarze {
10908ccddcd3Sschwarze 	struct roff_node	 *n, *nch;
10918ccddcd3Sschwarze 	const char		 *p;
10928ccddcd3Sschwarze 
10938ccddcd3Sschwarze 	n = mdoc->last;
10948ccddcd3Sschwarze 	nch = n->child;
10958ccddcd3Sschwarze 	assert(nch->type == ROFFT_TEXT);
10968ccddcd3Sschwarze 
10978ccddcd3Sschwarze 	if ((p = mdoc_a2st(nch->string)) == NULL) {
10988ccddcd3Sschwarze 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
10998ccddcd3Sschwarze 		    nch->line, nch->pos, "St %s", nch->string);
11008ccddcd3Sschwarze 		roff_node_delete(mdoc, n);
11018ccddcd3Sschwarze 		return;
11028ccddcd3Sschwarze 	}
11038ccddcd3Sschwarze 
11048ccddcd3Sschwarze 	nch->flags |= NODE_NOPRT;
11058ccddcd3Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
11068ccddcd3Sschwarze 	roff_word_alloc(mdoc, nch->line, nch->pos, p);
11078ccddcd3Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
11088ccddcd3Sschwarze 	mdoc->last= n;
11098ccddcd3Sschwarze }
11108ccddcd3Sschwarze 
11118ccddcd3Sschwarze static void
11123e642ba0Sschwarze post_obsolete(POST_ARGS)
1113551cd4a8Sschwarze {
11143e642ba0Sschwarze 	struct roff_node *n;
1115551cd4a8Sschwarze 
11163e642ba0Sschwarze 	n = mdoc->last;
1117d1982c71Sschwarze 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1118551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
111914a309e3Sschwarze 		    n->line, n->pos, roff_name[n->tok]);
1120551cd4a8Sschwarze }
1121551cd4a8Sschwarze 
1122bc205043Sschwarze static void
1123bc205043Sschwarze post_useless(POST_ARGS)
1124bc205043Sschwarze {
1125bc205043Sschwarze 	struct roff_node *n;
1126bc205043Sschwarze 
1127bc205043Sschwarze 	n = mdoc->last;
1128bc205043Sschwarze 	mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse,
1129bc205043Sschwarze 	    n->line, n->pos, roff_name[n->tok]);
1130bc205043Sschwarze }
1131bc205043Sschwarze 
11328ccddcd3Sschwarze /*
11338ccddcd3Sschwarze  * Block macros.
11348ccddcd3Sschwarze  */
11358ccddcd3Sschwarze 
113698b8f00aSschwarze static void
1137f73abda9Skristaps post_bf(POST_ARGS)
1138f73abda9Skristaps {
11393a0d07afSschwarze 	struct roff_node *np, *nch;
1140f73abda9Skristaps 
1141769ee804Sschwarze 	/*
1142769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
1143769ee804Sschwarze 	 * element, which contains the goods.
1144769ee804Sschwarze 	 */
1145769ee804Sschwarze 
1146769ee804Sschwarze 	np = mdoc->last;
1147d1982c71Sschwarze 	if (np->type != ROFFT_HEAD)
1148ae2efdd8Sschwarze 		return;
1149ae2efdd8Sschwarze 
1150d1982c71Sschwarze 	assert(np->parent->type == ROFFT_BLOCK);
1151f051602aSschwarze 	assert(np->parent->tok == MDOC_Bf);
115250d41253Sschwarze 
1153ecb10c32Sschwarze 	/* Check the number of arguments. */
1154f73abda9Skristaps 
1155ecb10c32Sschwarze 	nch = np->child;
1156f051602aSschwarze 	if (np->parent->args == NULL) {
1157f051602aSschwarze 		if (nch == NULL) {
1158bd594191Sschwarze 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
1159bd594191Sschwarze 			    np->line, np->pos, "Bf");
116098b8f00aSschwarze 			return;
116120fa2881Sschwarze 		}
1162ecb10c32Sschwarze 		nch = nch->next;
1163ecb10c32Sschwarze 	}
1164f051602aSschwarze 	if (nch != NULL)
1165ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1166ecb10c32Sschwarze 		    nch->line, nch->pos, "Bf ... %s", nch->string);
1167769ee804Sschwarze 
1168769ee804Sschwarze 	/* Extract argument into data. */
1169769ee804Sschwarze 
1170f051602aSschwarze 	if (np->parent->args != NULL) {
1171f051602aSschwarze 		switch (np->parent->args->argv[0].arg) {
1172f051602aSschwarze 		case MDOC_Emphasis:
11738c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
1174f051602aSschwarze 			break;
1175f051602aSschwarze 		case MDOC_Literal:
11768c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
1177f051602aSschwarze 			break;
1178f051602aSschwarze 		case MDOC_Symbolic:
11798c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
1180f051602aSschwarze 			break;
1181f051602aSschwarze 		default:
1182769ee804Sschwarze 			abort();
1183f051602aSschwarze 		}
118498b8f00aSschwarze 		return;
1185769ee804Sschwarze 	}
1186769ee804Sschwarze 
1187769ee804Sschwarze 	/* Extract parameter into data. */
1188769ee804Sschwarze 
1189f051602aSschwarze 	if ( ! strcmp(np->child->string, "Em"))
11908c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
1191f051602aSschwarze 	else if ( ! strcmp(np->child->string, "Li"))
11928c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
1193f051602aSschwarze 	else if ( ! strcmp(np->child->string, "Sy"))
11948c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
119520fa2881Sschwarze 	else
1196ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1197ecb10c32Sschwarze 		    np->child->line, np->child->pos,
1198ecb10c32Sschwarze 		    "Bf %s", np->child->string);
1199f73abda9Skristaps }
1200f73abda9Skristaps 
120198b8f00aSschwarze static void
12020c5064e3Sschwarze post_fname(POST_ARGS)
12030c5064e3Sschwarze {
12043a0d07afSschwarze 	const struct roff_node	*n;
12050ff14c71Sschwarze 	const char		*cp;
12060c5064e3Sschwarze 	size_t			 pos;
12070c5064e3Sschwarze 
12080c5064e3Sschwarze 	n = mdoc->last->child;
12090c5064e3Sschwarze 	pos = strcspn(n->string, "()");
12100ff14c71Sschwarze 	cp = n->string + pos;
12110ff14c71Sschwarze 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
12120c5064e3Sschwarze 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
12130c5064e3Sschwarze 		    n->line, n->pos + pos, n->string);
12140c5064e3Sschwarze }
12150c5064e3Sschwarze 
121698b8f00aSschwarze static void
12170c5064e3Sschwarze post_fn(POST_ARGS)
12180c5064e3Sschwarze {
12190c5064e3Sschwarze 
12200c5064e3Sschwarze 	post_fname(mdoc);
12210c5064e3Sschwarze 	post_fa(mdoc);
12220c5064e3Sschwarze }
12230c5064e3Sschwarze 
122498b8f00aSschwarze static void
1225753701eeSschwarze post_fo(POST_ARGS)
1226753701eeSschwarze {
12273a0d07afSschwarze 	const struct roff_node	*n;
1228753701eeSschwarze 
1229afcd1f03Sschwarze 	n = mdoc->last;
1230afcd1f03Sschwarze 
1231d1982c71Sschwarze 	if (n->type != ROFFT_HEAD)
1232afcd1f03Sschwarze 		return;
1233afcd1f03Sschwarze 
1234afcd1f03Sschwarze 	if (n->child == NULL) {
1235afcd1f03Sschwarze 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
1236afcd1f03Sschwarze 		    n->line, n->pos, "Fo");
1237afcd1f03Sschwarze 		return;
1238afcd1f03Sschwarze 	}
1239afcd1f03Sschwarze 	if (n->child != n->last) {
1240afcd1f03Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1241afcd1f03Sschwarze 		    n->child->next->line, n->child->next->pos,
1242afcd1f03Sschwarze 		    "Fo ... %s", n->child->next->string);
1243afcd1f03Sschwarze 		while (n->child != n->last)
1244fa2127f9Sschwarze 			roff_node_delete(mdoc, n->last);
1245fe8e59edSschwarze 	} else
1246fe8e59edSschwarze 		post_delim(mdoc);
1247afcd1f03Sschwarze 
12480c5064e3Sschwarze 	post_fname(mdoc);
1249753701eeSschwarze }
1250753701eeSschwarze 
125198b8f00aSschwarze static void
12527e92c062Sschwarze post_fa(POST_ARGS)
12537e92c062Sschwarze {
12543a0d07afSschwarze 	const struct roff_node *n;
12557e92c062Sschwarze 	const char *cp;
12567e92c062Sschwarze 
12577e92c062Sschwarze 	for (n = mdoc->last->child; n != NULL; n = n->next) {
12587e92c062Sschwarze 		for (cp = n->string; *cp != '\0'; cp++) {
12597e92c062Sschwarze 			/* Ignore callbacks and alterations. */
12607e92c062Sschwarze 			if (*cp == '(' || *cp == '{')
12617e92c062Sschwarze 				break;
12627e92c062Sschwarze 			if (*cp != ',')
12637e92c062Sschwarze 				continue;
12647e92c062Sschwarze 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
12657e92c062Sschwarze 			    n->line, n->pos + (cp - n->string),
12667e92c062Sschwarze 			    n->string);
12677e92c062Sschwarze 			break;
12687e92c062Sschwarze 		}
12697e92c062Sschwarze 	}
1270fe8e59edSschwarze 	post_delim_nb(mdoc);
12717e92c062Sschwarze }
12727e92c062Sschwarze 
127398b8f00aSschwarze static void
1274f73abda9Skristaps post_nm(POST_ARGS)
1275f73abda9Skristaps {
12763a0d07afSschwarze 	struct roff_node	*n;
12772d266539Sschwarze 
12782d266539Sschwarze 	n = mdoc->last;
12792d266539Sschwarze 
12802ab19127Sschwarze 	if (n->sec == SEC_NAME && n->child != NULL &&
12812ab19127Sschwarze 	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
128252d11c96Sschwarze 		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
128352d11c96Sschwarze 
1284*7c539ecbSschwarze 	if (n->last != NULL && n->last->tok == MDOC_Pp)
12852d266539Sschwarze 		mdoc_node_relink(mdoc, n->last);
128620fa2881Sschwarze 
1287f27faaccSschwarze 	if (mdoc->meta.name == NULL)
1288423631c9Sschwarze 		deroff(&mdoc->meta.name, n);
128920fa2881Sschwarze 
1290f27faaccSschwarze 	if (mdoc->meta.name == NULL ||
1291f27faaccSschwarze 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1292bd594191Sschwarze 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
12932d266539Sschwarze 		    n->line, n->pos, "Nm");
129490957cf5Sschwarze 
1295fe8e59edSschwarze 	switch (n->type) {
1296fe8e59edSschwarze 	case ROFFT_ELEM:
1297fe8e59edSschwarze 		post_delim_nb(mdoc);
1298fe8e59edSschwarze 		break;
1299fe8e59edSschwarze 	case ROFFT_HEAD:
130004fbb99fSschwarze 		post_delim(mdoc);
1301fe8e59edSschwarze 		break;
1302fe8e59edSschwarze 	default:
1303fe8e59edSschwarze 		return;
1304fe8e59edSschwarze 	}
130504fbb99fSschwarze 
1306fe8e59edSschwarze 	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
130790957cf5Sschwarze 	    mdoc->meta.name == NULL)
130890957cf5Sschwarze 		return;
130990957cf5Sschwarze 
131090957cf5Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
131190957cf5Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
131290957cf5Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
131390957cf5Sschwarze 	mdoc->last = n;
131420fa2881Sschwarze }
131520fa2881Sschwarze 
131698b8f00aSschwarze static void
1317753701eeSschwarze post_nd(POST_ARGS)
1318753701eeSschwarze {
13193a0d07afSschwarze 	struct roff_node	*n;
1320753701eeSschwarze 
13211570daf1Sschwarze 	n = mdoc->last;
13221570daf1Sschwarze 
1323d1982c71Sschwarze 	if (n->type != ROFFT_BODY)
13241570daf1Sschwarze 		return;
13251570daf1Sschwarze 
132656e9e976Sschwarze 	if (n->sec != SEC_NAME)
132756e9e976Sschwarze 		mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
132856e9e976Sschwarze 		    n->line, n->pos, "Nd");
132956e9e976Sschwarze 
13301570daf1Sschwarze 	if (n->child == NULL)
13311570daf1Sschwarze 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
13321570daf1Sschwarze 		    n->line, n->pos, "Nd");
1333fe8e59edSschwarze 	else
1334fe8e59edSschwarze 		post_delim(mdoc);
13351570daf1Sschwarze 
133698b8f00aSschwarze 	post_hyph(mdoc);
1337753701eeSschwarze }
1338753701eeSschwarze 
133998b8f00aSschwarze static void
13403e642ba0Sschwarze post_display(POST_ARGS)
1341753701eeSschwarze {
13423e642ba0Sschwarze 	struct roff_node *n, *np;
1343753701eeSschwarze 
1344b7530f2fSschwarze 	n = mdoc->last;
13453e642ba0Sschwarze 	switch (n->type) {
13463e642ba0Sschwarze 	case ROFFT_BODY:
13474b5c4138Sschwarze 		if (n->end != ENDBODY_NOT) {
1348611d9138Sschwarze 			if (n->tok == MDOC_Bd &&
1349611d9138Sschwarze 			    n->body->parent->args == NULL)
13504b5c4138Sschwarze 				roff_node_delete(mdoc, n);
13514b5c4138Sschwarze 		} else if (n->child == NULL)
13521d0823adSschwarze 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
135314a309e3Sschwarze 			    n->line, n->pos, roff_name[n->tok]);
13543e642ba0Sschwarze 		else if (n->tok == MDOC_D1)
13553e642ba0Sschwarze 			post_hyph(mdoc);
13563e642ba0Sschwarze 		break;
13573e642ba0Sschwarze 	case ROFFT_BLOCK:
13583e642ba0Sschwarze 		if (n->tok == MDOC_Bd) {
1359a43e24e2Sschwarze 			if (n->args == NULL) {
1360a43e24e2Sschwarze 				mandoc_msg(MANDOCERR_BD_NOARG,
1361a43e24e2Sschwarze 				    mdoc->parse, n->line, n->pos, "Bd");
1362a43e24e2Sschwarze 				mdoc->next = ROFF_NEXT_SIBLING;
1363a43e24e2Sschwarze 				while (n->body->child != NULL)
1364a43e24e2Sschwarze 					mdoc_node_relink(mdoc,
1365a43e24e2Sschwarze 					    n->body->child);
1366a43e24e2Sschwarze 				roff_node_delete(mdoc, n);
1367a43e24e2Sschwarze 				break;
1368a43e24e2Sschwarze 			}
13693e642ba0Sschwarze 			post_bd(mdoc);
13703e642ba0Sschwarze 			post_prevpar(mdoc);
13713e642ba0Sschwarze 		}
13723e642ba0Sschwarze 		for (np = n->parent; np != NULL; np = np->parent) {
13733e642ba0Sschwarze 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
13743e642ba0Sschwarze 				mandoc_vmsg(MANDOCERR_BD_NEST,
13753e642ba0Sschwarze 				    mdoc->parse, n->line, n->pos,
137614a309e3Sschwarze 				    "%s in Bd", roff_name[n->tok]);
13773e642ba0Sschwarze 				break;
13783e642ba0Sschwarze 			}
13793e642ba0Sschwarze 		}
13803e642ba0Sschwarze 		break;
13813e642ba0Sschwarze 	default:
13823e642ba0Sschwarze 		break;
13833e642ba0Sschwarze 	}
138420fa2881Sschwarze }
138520fa2881Sschwarze 
138698b8f00aSschwarze static void
138720fa2881Sschwarze post_defaults(POST_ARGS)
138820fa2881Sschwarze {
13893a0d07afSschwarze 	struct roff_node *nn;
139020fa2881Sschwarze 
139104fbb99fSschwarze 	if (mdoc->last->child != NULL) {
1392fe8e59edSschwarze 		post_delim_nb(mdoc);
139304fbb99fSschwarze 		return;
139404fbb99fSschwarze 	}
139504fbb99fSschwarze 
139620fa2881Sschwarze 	/*
139720fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
139820fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
139920fa2881Sschwarze 	 * gets an empty string.
140020fa2881Sschwarze 	 */
1401f73abda9Skristaps 
140220fa2881Sschwarze 	nn = mdoc->last;
140320fa2881Sschwarze 	switch (nn->tok) {
140449aff9f8Sschwarze 	case MDOC_Ar:
1405396853b5Sschwarze 		mdoc->next = ROFF_NEXT_CHILD;
140669c34eaaSschwarze 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
140743808411Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
140869c34eaaSschwarze 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
140943808411Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
141020fa2881Sschwarze 		break;
141149aff9f8Sschwarze 	case MDOC_Pa:
141249aff9f8Sschwarze 	case MDOC_Mt:
1413396853b5Sschwarze 		mdoc->next = ROFF_NEXT_CHILD;
141469c34eaaSschwarze 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
141543808411Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
141620fa2881Sschwarze 		break;
141720fa2881Sschwarze 	default:
141820fa2881Sschwarze 		abort();
1419f73abda9Skristaps 	}
142020fa2881Sschwarze 	mdoc->last = nn;
142120fa2881Sschwarze }
1422f73abda9Skristaps 
142398b8f00aSschwarze static void
1424f73abda9Skristaps post_at(POST_ARGS)
1425f73abda9Skristaps {
14263af8e8d7Sschwarze 	struct roff_node	*n, *nch;
14273af8e8d7Sschwarze 	const char		*att;
142820fa2881Sschwarze 
1429753701eeSschwarze 	n = mdoc->last;
14303af8e8d7Sschwarze 	nch = n->child;
1431753701eeSschwarze 
143220fa2881Sschwarze 	/*
143320fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
143420fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
143520fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
143620fa2881Sschwarze 	 */
1437f73abda9Skristaps 
14383af8e8d7Sschwarze 	att = NULL;
14393af8e8d7Sschwarze 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1440bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
14413af8e8d7Sschwarze 		    nch->line, nch->pos, "At %s", nch->string);
1442f73abda9Skristaps 
14433af8e8d7Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
14443af8e8d7Sschwarze 	if (att != NULL) {
14453af8e8d7Sschwarze 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
14463af8e8d7Sschwarze 		nch->flags |= NODE_NOPRT;
14473af8e8d7Sschwarze 	} else
14483af8e8d7Sschwarze 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
14493af8e8d7Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
14503af8e8d7Sschwarze 	mdoc->last = n;
145120fa2881Sschwarze }
1452f73abda9Skristaps 
145398b8f00aSschwarze static void
1454f73abda9Skristaps post_an(POST_ARGS)
1455f73abda9Skristaps {
14563a0d07afSschwarze 	struct roff_node *np, *nch;
1457f73abda9Skristaps 
14583e642ba0Sschwarze 	post_an_norm(mdoc);
14593e642ba0Sschwarze 
1460769ee804Sschwarze 	np = mdoc->last;
1461cba50636Sschwarze 	nch = np->child;
1462cba50636Sschwarze 	if (np->norm->An.auth == AUTH__NONE) {
1463cba50636Sschwarze 		if (nch == NULL)
1464cba50636Sschwarze 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1465cba50636Sschwarze 			    np->line, np->pos, "An");
146604fbb99fSschwarze 		else
1467fe8e59edSschwarze 			post_delim_nb(mdoc);
1468cba50636Sschwarze 	} else if (nch != NULL)
14693798fb25Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
14703798fb25Sschwarze 		    nch->line, nch->pos, "An ... %s", nch->string);
1471f73abda9Skristaps }
1472f73abda9Skristaps 
147398b8f00aSschwarze static void
1474551cd4a8Sschwarze post_en(POST_ARGS)
1475551cd4a8Sschwarze {
1476551cd4a8Sschwarze 
14773e642ba0Sschwarze 	post_obsolete(mdoc);
1478d1982c71Sschwarze 	if (mdoc->last->type == ROFFT_BLOCK)
1479551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1480551cd4a8Sschwarze }
1481551cd4a8Sschwarze 
148298b8f00aSschwarze static void
1483551cd4a8Sschwarze post_es(POST_ARGS)
1484551cd4a8Sschwarze {
1485551cd4a8Sschwarze 
14863e642ba0Sschwarze 	post_obsolete(mdoc);
1487551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1488551cd4a8Sschwarze }
1489551cd4a8Sschwarze 
149098b8f00aSschwarze static void
1491816c3c54Sschwarze post_xx(POST_ARGS)
1492816c3c54Sschwarze {
1493816c3c54Sschwarze 	struct roff_node	*n;
1494816c3c54Sschwarze 	const char		*os;
149507ff2819Sschwarze 	char			*v;
1496816c3c54Sschwarze 
1497fe8e59edSschwarze 	post_delim_nb(mdoc);
149804fbb99fSschwarze 
1499816c3c54Sschwarze 	n = mdoc->last;
1500816c3c54Sschwarze 	switch (n->tok) {
1501816c3c54Sschwarze 	case MDOC_Bsx:
1502816c3c54Sschwarze 		os = "BSD/OS";
1503816c3c54Sschwarze 		break;
1504816c3c54Sschwarze 	case MDOC_Dx:
1505816c3c54Sschwarze 		os = "DragonFly";
1506816c3c54Sschwarze 		break;
1507816c3c54Sschwarze 	case MDOC_Fx:
1508816c3c54Sschwarze 		os = "FreeBSD";
1509816c3c54Sschwarze 		break;
1510816c3c54Sschwarze 	case MDOC_Nx:
1511816c3c54Sschwarze 		os = "NetBSD";
151207ff2819Sschwarze 		if (n->child == NULL)
151307ff2819Sschwarze 			break;
151407ff2819Sschwarze 		v = n->child->string;
151507ff2819Sschwarze 		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
151607ff2819Sschwarze 		    v[2] < '0' || v[2] > '9' ||
151707ff2819Sschwarze 		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
151807ff2819Sschwarze 			break;
151907ff2819Sschwarze 		n->child->flags |= NODE_NOPRT;
152007ff2819Sschwarze 		mdoc->next = ROFF_NEXT_CHILD;
152107ff2819Sschwarze 		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
152207ff2819Sschwarze 		v = mdoc->last->string;
152307ff2819Sschwarze 		v[3] = toupper((unsigned char)v[3]);
152407ff2819Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
152507ff2819Sschwarze 		mdoc->last = n;
1526816c3c54Sschwarze 		break;
1527816c3c54Sschwarze 	case MDOC_Ox:
1528816c3c54Sschwarze 		os = "OpenBSD";
1529816c3c54Sschwarze 		break;
1530816c3c54Sschwarze 	case MDOC_Ux:
1531816c3c54Sschwarze 		os = "UNIX";
1532816c3c54Sschwarze 		break;
1533816c3c54Sschwarze 	default:
1534816c3c54Sschwarze 		abort();
1535816c3c54Sschwarze 	}
1536816c3c54Sschwarze 	mdoc->next = ROFF_NEXT_CHILD;
1537816c3c54Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, os);
1538816c3c54Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
1539816c3c54Sschwarze 	mdoc->last = n;
1540816c3c54Sschwarze }
1541816c3c54Sschwarze 
1542816c3c54Sschwarze static void
1543f73abda9Skristaps post_it(POST_ARGS)
1544f73abda9Skristaps {
15453a0d07afSschwarze 	struct roff_node *nbl, *nit, *nch;
154619a69263Sschwarze 	int		  i, cols;
15476093755cSschwarze 	enum mdoc_list	  lt;
1548f73abda9Skristaps 
15493e642ba0Sschwarze 	post_prevpar(mdoc);
15503e642ba0Sschwarze 
15519530682eSschwarze 	nit = mdoc->last;
1552d1982c71Sschwarze 	if (nit->type != ROFFT_BLOCK)
155398b8f00aSschwarze 		return;
1554f73abda9Skristaps 
15559530682eSschwarze 	nbl = nit->parent->parent;
15569530682eSschwarze 	lt = nbl->norm->Bl.type;
15576093755cSschwarze 
15586093755cSschwarze 	switch (lt) {
155949aff9f8Sschwarze 	case LIST_tag:
156049aff9f8Sschwarze 	case LIST_hang:
156149aff9f8Sschwarze 	case LIST_ohang:
156249aff9f8Sschwarze 	case LIST_inset:
156349aff9f8Sschwarze 	case LIST_diag:
1564d26e35c2Sschwarze 		if (nit->head->child == NULL)
1565bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
15669530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
1567bd594191Sschwarze 			    "Bl -%s It",
15689530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1569f73abda9Skristaps 		break;
157049aff9f8Sschwarze 	case LIST_bullet:
157149aff9f8Sschwarze 	case LIST_dash:
157249aff9f8Sschwarze 	case LIST_enum:
157349aff9f8Sschwarze 	case LIST_hyphen:
1574d26e35c2Sschwarze 		if (nit->body == NULL || nit->body->child == NULL)
1575bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
157666788495Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1577bd594191Sschwarze 			    "Bl -%s It",
157866788495Sschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1579f73abda9Skristaps 		/* FALLTHROUGH */
158049aff9f8Sschwarze 	case LIST_item:
158125f8eeb1Sschwarze 		if ((nch = nit->head->child) != NULL)
158214a309e3Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
158314a309e3Sschwarze 			    nit->line, nit->pos, "It %s",
158414a309e3Sschwarze 			    nch->string == NULL ? roff_name[nch->tok] :
158514a309e3Sschwarze 			    nch->string);
1586f73abda9Skristaps 		break;
158749aff9f8Sschwarze 	case LIST_column:
15889530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
15896093755cSschwarze 
1590d26e35c2Sschwarze 		assert(nit->head->child == NULL);
15916093755cSschwarze 
159280f58981Sschwarze 		if (nit->head->next->child == NULL &&
159380f58981Sschwarze 		    nit->head->next->next == NULL) {
159480f58981Sschwarze 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
159580f58981Sschwarze 			    nit->line, nit->pos, "It");
159680f58981Sschwarze 			roff_node_delete(mdoc, nit);
159780f58981Sschwarze 			break;
159880f58981Sschwarze 		}
159953292e81Sschwarze 
160080f58981Sschwarze 		i = 0;
160180f58981Sschwarze 		for (nch = nit->child; nch != NULL; nch = nch->next) {
160280f58981Sschwarze 			if (nch->type != ROFFT_BODY)
160380f58981Sschwarze 				continue;
160480f58981Sschwarze 			if (i++ && nch->flags & NODE_LINE)
160580f58981Sschwarze 				mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse,
160680f58981Sschwarze 				    nch->line, nch->pos, "Ta");
160780f58981Sschwarze 		}
1608e14c4c11Sschwarze 		if (i < cols || i > cols + 1)
1609bce599dfSschwarze 			mandoc_vmsg(MANDOCERR_BL_COL,
1610e14c4c11Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1611bce599dfSschwarze 			    "%d columns, %d cells", cols, i);
161280f58981Sschwarze 		else if (nit->head->next->child != NULL &&
161380f58981Sschwarze 		    nit->head->next->child->line > nit->line)
161480f58981Sschwarze 			mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse,
161580f58981Sschwarze 			    nit->line, nit->pos, "Bl -column It");
1616e14c4c11Sschwarze 		break;
1617f73abda9Skristaps 	default:
161866788495Sschwarze 		abort();
1619f73abda9Skristaps 	}
1620f73abda9Skristaps }
1621f73abda9Skristaps 
162298b8f00aSschwarze static void
162320fa2881Sschwarze post_bl_block(POST_ARGS)
162420fa2881Sschwarze {
16253a0d07afSschwarze 	struct roff_node *n, *ni, *nc;
162620fa2881Sschwarze 
16273e642ba0Sschwarze 	post_prevpar(mdoc);
16283e642ba0Sschwarze 
162920fa2881Sschwarze 	n = mdoc->last;
1630f051602aSschwarze 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1631f051602aSschwarze 		if (ni->body == NULL)
1632bb99f0faSschwarze 			continue;
1633bb99f0faSschwarze 		nc = ni->body->last;
1634f051602aSschwarze 		while (nc != NULL) {
1635bb99f0faSschwarze 			switch (nc->tok) {
163649aff9f8Sschwarze 			case MDOC_Pp:
163729478532Sschwarze 			case ROFF_br:
1638bb99f0faSschwarze 				break;
1639bb99f0faSschwarze 			default:
1640bb99f0faSschwarze 				nc = NULL;
1641bb99f0faSschwarze 				continue;
1642bb99f0faSschwarze 			}
1643f051602aSschwarze 			if (ni->next == NULL) {
164420369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
164520369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
164614a309e3Sschwarze 				    roff_name[nc->tok]);
164798b8f00aSschwarze 				mdoc_node_relink(mdoc, nc);
1648f051602aSschwarze 			} else if (n->norm->Bl.comp == 0 &&
1649f051602aSschwarze 			    n->norm->Bl.type != LIST_column) {
165020369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
165120369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
165214a309e3Sschwarze 				    "%s before It", roff_name[nc->tok]);
1653fa2127f9Sschwarze 				roff_node_delete(mdoc, nc);
1654bb99f0faSschwarze 			} else
1655bb99f0faSschwarze 				break;
1656bb99f0faSschwarze 			nc = ni->body->last;
1657bb99f0faSschwarze 		}
1658bb99f0faSschwarze 	}
165920fa2881Sschwarze }
166020fa2881Sschwarze 
166190d52a15Sschwarze /*
166290d52a15Sschwarze  * If the argument of -offset or -width is a macro,
166390d52a15Sschwarze  * replace it with the associated default width.
166490d52a15Sschwarze  */
16656050a3daSschwarze static void
16666050a3daSschwarze rewrite_macro2len(struct roff_man *mdoc, char **arg)
166720fa2881Sschwarze {
166820fa2881Sschwarze 	size_t		  width;
166914a309e3Sschwarze 	enum roff_tok	  tok;
167020fa2881Sschwarze 
167190d52a15Sschwarze 	if (*arg == NULL)
167290d52a15Sschwarze 		return;
167390d52a15Sschwarze 	else if ( ! strcmp(*arg, "Ds"))
167420fa2881Sschwarze 		width = 6;
16756050a3daSschwarze 	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
167690d52a15Sschwarze 		return;
1677dc0d8bb2Sschwarze 	else
1678dc0d8bb2Sschwarze 		width = macro2len(tok);
167920fa2881Sschwarze 
168090d52a15Sschwarze 	free(*arg);
168190d52a15Sschwarze 	mandoc_asprintf(arg, "%zun", width);
168220fa2881Sschwarze }
168320fa2881Sschwarze 
168498b8f00aSschwarze static void
1685395185ccSschwarze post_bl_head(POST_ARGS)
1686395185ccSschwarze {
16873a0d07afSschwarze 	struct roff_node *nbl, *nh, *nch, *nnext;
1688f5174743Sschwarze 	struct mdoc_argv *argv;
168920fa2881Sschwarze 	int		  i, j;
1690395185ccSschwarze 
16913e642ba0Sschwarze 	post_bl_norm(mdoc);
16922588c917Sschwarze 
16933e642ba0Sschwarze 	nh = mdoc->last;
16942588c917Sschwarze 	if (nh->norm->Bl.type != LIST_column) {
16952588c917Sschwarze 		if ((nch = nh->child) == NULL)
16962588c917Sschwarze 			return;
16972588c917Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
16982588c917Sschwarze 		    nch->line, nch->pos, "Bl ... %s", nch->string);
16992588c917Sschwarze 		while (nch != NULL) {
1700fa2127f9Sschwarze 			roff_node_delete(mdoc, nch);
17012588c917Sschwarze 			nch = nh->child;
17022588c917Sschwarze 		}
170398b8f00aSschwarze 		return;
170498b8f00aSschwarze 	}
1705395185ccSschwarze 
170620fa2881Sschwarze 	/*
1707f5174743Sschwarze 	 * Append old-style lists, where the column width specifiers
170820fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
170920fa2881Sschwarze 	 * lists where they're argument values following -column.
171020fa2881Sschwarze 	 */
171120fa2881Sschwarze 
17122588c917Sschwarze 	if (nh->child == NULL)
171398b8f00aSschwarze 		return;
171420fa2881Sschwarze 
17152588c917Sschwarze 	nbl = nh->parent;
17162588c917Sschwarze 	for (j = 0; j < (int)nbl->args->argc; j++)
17172588c917Sschwarze 		if (nbl->args->argv[j].arg == MDOC_Column)
171820fa2881Sschwarze 			break;
171920fa2881Sschwarze 
17202588c917Sschwarze 	assert(j < (int)nbl->args->argc);
172120fa2881Sschwarze 
172220fa2881Sschwarze 	/*
1723a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
172420fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
172520fa2881Sschwarze 	 * column field.  Then, delete the head children.
172620fa2881Sschwarze 	 */
172720fa2881Sschwarze 
17282588c917Sschwarze 	argv = nbl->args->argv + j;
1729f5174743Sschwarze 	i = argv->sz;
173030e5ee06Sschwarze 	for (nch = nh->child; nch != NULL; nch = nch->next)
173130e5ee06Sschwarze 		argv->sz++;
1732f5174743Sschwarze 	argv->value = mandoc_reallocarray(argv->value,
1733f5174743Sschwarze 	    argv->sz, sizeof(char *));
173420fa2881Sschwarze 
17352588c917Sschwarze 	nh->norm->Bl.ncols = argv->sz;
17362588c917Sschwarze 	nh->norm->Bl.cols = (void *)argv->value;
173720fa2881Sschwarze 
17382588c917Sschwarze 	for (nch = nh->child; nch != NULL; nch = nnext) {
17392588c917Sschwarze 		argv->value[i++] = nch->string;
17402588c917Sschwarze 		nch->string = NULL;
17412588c917Sschwarze 		nnext = nch->next;
1742fa2127f9Sschwarze 		roff_node_delete(NULL, nch);
1743395185ccSschwarze 	}
17442588c917Sschwarze 	nh->child = NULL;
1745b16e7ddfSschwarze }
1746b16e7ddfSschwarze 
174798b8f00aSschwarze static void
1748f73abda9Skristaps post_bl(POST_ARGS)
1749f73abda9Skristaps {
17503a0d07afSschwarze 	struct roff_node	*nparent, *nprev; /* of the Bl block */
17513a0d07afSschwarze 	struct roff_node	*nblock, *nbody;  /* of the Bl */
17523a0d07afSschwarze 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1753ce0ef847Sschwarze 	const char		*prev_Er;
1754ce0ef847Sschwarze 	int			 order;
1755f73abda9Skristaps 
17562a427d60Sschwarze 	nbody = mdoc->last;
17572a427d60Sschwarze 	switch (nbody->type) {
1758d1982c71Sschwarze 	case ROFFT_BLOCK:
175998b8f00aSschwarze 		post_bl_block(mdoc);
176098b8f00aSschwarze 		return;
1761d1982c71Sschwarze 	case ROFFT_HEAD:
176298b8f00aSschwarze 		post_bl_head(mdoc);
176398b8f00aSschwarze 		return;
1764d1982c71Sschwarze 	case ROFFT_BODY:
1765f6127a73Sschwarze 		break;
17662a427d60Sschwarze 	default:
176798b8f00aSschwarze 		return;
1768f6127a73Sschwarze 	}
1769396853b5Sschwarze 	if (nbody->end != ENDBODY_NOT)
1770396853b5Sschwarze 		return;
1771f6127a73Sschwarze 
17722a427d60Sschwarze 	nchild = nbody->child;
1773b7530f2fSschwarze 	if (nchild == NULL) {
17741d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1775b7530f2fSschwarze 		    nbody->line, nbody->pos, "Bl");
1776b7530f2fSschwarze 		return;
1777b7530f2fSschwarze 	}
1778b7530f2fSschwarze 	while (nchild != NULL) {
1779ad7fa6e5Sschwarze 		nnext = nchild->next;
1780204684a7Sschwarze 		if (nchild->tok == MDOC_It ||
1781204684a7Sschwarze 		    (nchild->tok == MDOC_Sm &&
1782ad7fa6e5Sschwarze 		     nnext != NULL && nnext->tok == MDOC_It)) {
1783ad7fa6e5Sschwarze 			nchild = nnext;
1784ad7fa6e5Sschwarze 			continue;
1785ad7fa6e5Sschwarze 		}
1786ad7fa6e5Sschwarze 
1787ad7fa6e5Sschwarze 		/*
1788ad7fa6e5Sschwarze 		 * In .Bl -column, the first rows may be implicit,
1789ad7fa6e5Sschwarze 		 * that is, they may not start with .It macros.
1790ad7fa6e5Sschwarze 		 * Such rows may be followed by nodes generated on the
1791ad7fa6e5Sschwarze 		 * roff level, for example .TS, which cannot be moved
1792ad7fa6e5Sschwarze 		 * out of the list.  In that case, wrap such roff nodes
1793ad7fa6e5Sschwarze 		 * into an implicit row.
1794ad7fa6e5Sschwarze 		 */
1795ad7fa6e5Sschwarze 
1796ad7fa6e5Sschwarze 		if (nchild->prev != NULL) {
1797ad7fa6e5Sschwarze 			mdoc->last = nchild;
1798ad7fa6e5Sschwarze 			mdoc->next = ROFF_NEXT_SIBLING;
1799ad7fa6e5Sschwarze 			roff_block_alloc(mdoc, nchild->line,
1800ad7fa6e5Sschwarze 			    nchild->pos, MDOC_It);
1801ad7fa6e5Sschwarze 			roff_head_alloc(mdoc, nchild->line,
1802ad7fa6e5Sschwarze 			    nchild->pos, MDOC_It);
1803ad7fa6e5Sschwarze 			mdoc->next = ROFF_NEXT_SIBLING;
1804ad7fa6e5Sschwarze 			roff_body_alloc(mdoc, nchild->line,
1805ad7fa6e5Sschwarze 			    nchild->pos, MDOC_It);
1806ad7fa6e5Sschwarze 			while (nchild->tok != MDOC_It) {
1807ad7fa6e5Sschwarze 				mdoc_node_relink(mdoc, nchild);
1808ad7fa6e5Sschwarze 				if ((nchild = nnext) == NULL)
1809ad7fa6e5Sschwarze 					break;
1810ad7fa6e5Sschwarze 				nnext = nchild->next;
1811ad7fa6e5Sschwarze 				mdoc->next = ROFF_NEXT_SIBLING;
1812ad7fa6e5Sschwarze 			}
1813ad7fa6e5Sschwarze 			mdoc->last = nbody;
18142a427d60Sschwarze 			continue;
18152a427d60Sschwarze 		}
18162a427d60Sschwarze 
1817dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
181814a309e3Sschwarze 		    nchild->line, nchild->pos, roff_name[nchild->tok]);
18192a427d60Sschwarze 
18202a427d60Sschwarze 		/*
18212a427d60Sschwarze 		 * Move the node out of the Bl block.
18222a427d60Sschwarze 		 * First, collect all required node pointers.
18232a427d60Sschwarze 		 */
18242a427d60Sschwarze 
18252a427d60Sschwarze 		nblock  = nbody->parent;
18262a427d60Sschwarze 		nprev   = nblock->prev;
18272a427d60Sschwarze 		nparent = nblock->parent;
18282a427d60Sschwarze 
18292a427d60Sschwarze 		/*
18302a427d60Sschwarze 		 * Unlink this child.
18312a427d60Sschwarze 		 */
18322a427d60Sschwarze 
18332a427d60Sschwarze 		nbody->child = nnext;
183430e5ee06Sschwarze 		if (nnext == NULL)
183530e5ee06Sschwarze 			nbody->last  = NULL;
183630e5ee06Sschwarze 		else
18372a427d60Sschwarze 			nnext->prev = NULL;
18382a427d60Sschwarze 
18392a427d60Sschwarze 		/*
18402a427d60Sschwarze 		 * Relink this child.
18412a427d60Sschwarze 		 */
18422a427d60Sschwarze 
18432a427d60Sschwarze 		nchild->parent = nparent;
18442a427d60Sschwarze 		nchild->prev   = nprev;
18452a427d60Sschwarze 		nchild->next   = nblock;
18462a427d60Sschwarze 
18472a427d60Sschwarze 		nblock->prev = nchild;
1848f051602aSschwarze 		if (nprev == NULL)
18492a427d60Sschwarze 			nparent->child = nchild;
18502a427d60Sschwarze 		else
18512a427d60Sschwarze 			nprev->next = nchild;
18522a427d60Sschwarze 
18532a427d60Sschwarze 		nchild = nnext;
1854f73abda9Skristaps 	}
1855ce0ef847Sschwarze 
1856f3476b07Sschwarze 	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1857ce0ef847Sschwarze 		return;
1858ce0ef847Sschwarze 
1859ce0ef847Sschwarze 	prev_Er = NULL;
1860ce0ef847Sschwarze 	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1861ce0ef847Sschwarze 		if (nchild->tok != MDOC_It)
1862ce0ef847Sschwarze 			continue;
1863ce0ef847Sschwarze 		if ((nnext = nchild->head->child) == NULL)
1864ce0ef847Sschwarze 			continue;
1865ce0ef847Sschwarze 		if (nnext->type == ROFFT_BLOCK)
1866ce0ef847Sschwarze 			nnext = nnext->body->child;
1867ce0ef847Sschwarze 		if (nnext == NULL || nnext->tok != MDOC_Er)
1868ce0ef847Sschwarze 			continue;
1869ce0ef847Sschwarze 		nnext = nnext->child;
1870ce0ef847Sschwarze 		if (prev_Er != NULL) {
1871ce0ef847Sschwarze 			order = strcmp(prev_Er, nnext->string);
1872ce0ef847Sschwarze 			if (order > 0)
1873ce0ef847Sschwarze 				mandoc_vmsg(MANDOCERR_ER_ORDER,
1874ce0ef847Sschwarze 				    mdoc->parse, nnext->line, nnext->pos,
1875f3476b07Sschwarze 				    "Er %s %s (NetBSD)",
1876f3476b07Sschwarze 				    prev_Er, nnext->string);
1877ce0ef847Sschwarze 			else if (order == 0)
1878ce0ef847Sschwarze 				mandoc_vmsg(MANDOCERR_ER_REP,
1879ce0ef847Sschwarze 				    mdoc->parse, nnext->line, nnext->pos,
1880f3476b07Sschwarze 				    "Er %s (NetBSD)", prev_Er);
1881ce0ef847Sschwarze 		}
1882ce0ef847Sschwarze 		prev_Er = nnext->string;
1883ce0ef847Sschwarze 	}
1884f73abda9Skristaps }
1885f73abda9Skristaps 
188698b8f00aSschwarze static void
1887753701eeSschwarze post_bk(POST_ARGS)
1888753701eeSschwarze {
18893a0d07afSschwarze 	struct roff_node	*n;
1890753701eeSschwarze 
18912588c917Sschwarze 	n = mdoc->last;
18922588c917Sschwarze 
1893d1982c71Sschwarze 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
18941d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY,
18952588c917Sschwarze 		    mdoc->parse, n->line, n->pos, "Bk");
1896fa2127f9Sschwarze 		roff_node_delete(mdoc, n);
18972588c917Sschwarze 	}
1898753701eeSschwarze }
1899753701eeSschwarze 
190098b8f00aSschwarze static void
1901f051602aSschwarze post_sm(POST_ARGS)
1902f73abda9Skristaps {
19033a0d07afSschwarze 	struct roff_node	*nch;
1904f73abda9Skristaps 
1905dc0d8bb2Sschwarze 	nch = mdoc->last->child;
1906dc0d8bb2Sschwarze 
190778bbbab4Sschwarze 	if (nch == NULL) {
1908f9e7bf99Sschwarze 		mdoc->flags ^= MDOC_SMOFF;
190998b8f00aSschwarze 		return;
1910bb648afaSschwarze 	}
1911f9e7bf99Sschwarze 
1912d1982c71Sschwarze 	assert(nch->type == ROFFT_TEXT);
191320fa2881Sschwarze 
191478bbbab4Sschwarze 	if ( ! strcmp(nch->string, "on")) {
1915ec2beb53Sschwarze 		mdoc->flags &= ~MDOC_SMOFF;
191698b8f00aSschwarze 		return;
1917ec2beb53Sschwarze 	}
191878bbbab4Sschwarze 	if ( ! strcmp(nch->string, "off")) {
1919ec2beb53Sschwarze 		mdoc->flags |= MDOC_SMOFF;
192098b8f00aSschwarze 		return;
1921ec2beb53Sschwarze 	}
192220fa2881Sschwarze 
1923dc0d8bb2Sschwarze 	mandoc_vmsg(MANDOCERR_SM_BAD,
1924dc0d8bb2Sschwarze 	    mdoc->parse, nch->line, nch->pos,
192514a309e3Sschwarze 	    "%s %s", roff_name[mdoc->last->tok], nch->string);
192698b8f00aSschwarze 	mdoc_node_relink(mdoc, nch);
192798b8f00aSschwarze 	return;
192820fa2881Sschwarze }
1929f73abda9Skristaps 
193098b8f00aSschwarze static void
1931f73abda9Skristaps post_root(POST_ARGS)
1932f73abda9Skristaps {
19338fd2959dSschwarze 	const char *openbsd_arch[] = {
19348fd2959dSschwarze 		"alpha", "amd64", "arm64", "armv7", "hppa", "i386",
19358fd2959dSschwarze 		"landisk", "loongson", "luna88k", "macppc", "mips64",
19368fd2959dSschwarze 		"octeon", "sgi", "socppc", "sparc64", NULL
19378fd2959dSschwarze 	};
19388fd2959dSschwarze 	const char *netbsd_arch[] = {
19398fd2959dSschwarze 		"acorn26", "acorn32", "algor", "alpha", "amiga",
19408fd2959dSschwarze 		"arc", "atari",
19418fd2959dSschwarze 		"bebox", "cats", "cesfic", "cobalt", "dreamcast",
19428fd2959dSschwarze 		"emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
19438fd2959dSschwarze 		"hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
19448fd2959dSschwarze 		"i386", "ibmnws", "luna68k",
19458fd2959dSschwarze 		"mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
19468fd2959dSschwarze 		"netwinder", "news68k", "newsmips", "next68k",
19478fd2959dSschwarze 		"pc532", "playstation2", "pmax", "pmppc", "prep",
19488fd2959dSschwarze 		"sandpoint", "sbmips", "sgimips", "shark",
19498fd2959dSschwarze 		"sparc", "sparc64", "sun2", "sun3",
19508fd2959dSschwarze 		"vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
19518fd2959dSschwarze         };
19528fd2959dSschwarze 	const char **arches[] = { NULL, netbsd_arch, openbsd_arch };
19538fd2959dSschwarze 
19543a0d07afSschwarze 	struct roff_node *n;
19558fd2959dSschwarze 	const char **arch;
1956f73abda9Skristaps 
1957ac1f49d0Sschwarze 	/* Add missing prologue data. */
195820fa2881Sschwarze 
1959ac1f49d0Sschwarze 	if (mdoc->meta.date == NULL)
19603427e516Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
19613427e516Sschwarze 		    mandoc_normdate(mdoc, NULL, 0, 0);
19623fdead0cSschwarze 
19633fdead0cSschwarze 	if (mdoc->meta.title == NULL) {
19643fdead0cSschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
19653fdead0cSschwarze 		    mdoc->parse, 0, 0, "EOF");
19663fdead0cSschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
19673fdead0cSschwarze 	}
19683fdead0cSschwarze 
1969ac1f49d0Sschwarze 	if (mdoc->meta.vol == NULL)
1970ac1f49d0Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
19713fdead0cSschwarze 
19723fdead0cSschwarze 	if (mdoc->meta.os == NULL) {
19733fdead0cSschwarze 		mandoc_msg(MANDOCERR_OS_MISSING,
19743fdead0cSschwarze 		    mdoc->parse, 0, 0, NULL);
19753fdead0cSschwarze 		mdoc->meta.os = mandoc_strdup("");
1976172864f7Sschwarze 	} else if (mdoc->meta.os_e &&
1977172864f7Sschwarze 	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1978f3476b07Sschwarze 		mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1979f3476b07Sschwarze 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1980f3476b07Sschwarze 		    "(OpenBSD)" : "(NetBSD)");
1981f73abda9Skristaps 
19828fd2959dSschwarze 	if (mdoc->meta.arch != NULL &&
19838fd2959dSschwarze 	    (arch = arches[mdoc->meta.os_e]) != NULL) {
19848fd2959dSschwarze 		while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
19858fd2959dSschwarze 			arch++;
19868fd2959dSschwarze 		if (*arch == NULL) {
19878fd2959dSschwarze 			n = mdoc->first->child;
19883f450c8cSschwarze 			while (n->tok != MDOC_Dt ||
19893f450c8cSschwarze 			    n->child == NULL ||
19903f450c8cSschwarze 			    n->child->next == NULL ||
19913f450c8cSschwarze 			    n->child->next->next == NULL)
19928fd2959dSschwarze 				n = n->next;
19938fd2959dSschwarze 			n = n->child->next->next;
19948fd2959dSschwarze 			mandoc_vmsg(MANDOCERR_ARCH_BAD,
19958fd2959dSschwarze 			    mdoc->parse, n->line, n->pos,
19968fd2959dSschwarze 			    "Dt ... %s %s", mdoc->meta.arch,
19978fd2959dSschwarze 			    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
19988fd2959dSschwarze 			    "(OpenBSD)" : "(NetBSD)");
19998fd2959dSschwarze 		}
20008fd2959dSschwarze 	}
20018fd2959dSschwarze 
200220fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
200320fa2881Sschwarze 
2004e20417bdSschwarze 	n = mdoc->first->child;
20054c293873Sschwarze 	while (n != NULL &&
20064c293873Sschwarze 	    (n->type == ROFFT_COMMENT ||
20074c293873Sschwarze 	     (n->tok >= MDOC_Dd &&
200816fe0cfcSschwarze 	      mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
2009e20417bdSschwarze 		n = n->next;
2010e20417bdSschwarze 
2011e20417bdSschwarze 	if (n == NULL)
2012e20417bdSschwarze 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
2013e20417bdSschwarze 	else if (n->tok != MDOC_Sh)
201451fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
201514a309e3Sschwarze 		    n->line, n->pos, roff_name[n->tok]);
201620fa2881Sschwarze }
2017f73abda9Skristaps 
201898b8f00aSschwarze static void
2019011fe33bSschwarze post_rs(POST_ARGS)
2020011fe33bSschwarze {
20213a0d07afSschwarze 	struct roff_node *np, *nch, *next, *prev;
202220fa2881Sschwarze 	int		  i, j;
2023011fe33bSschwarze 
20246e96429aSschwarze 	np = mdoc->last;
20256e96429aSschwarze 
2026d1982c71Sschwarze 	if (np->type != ROFFT_BODY)
202798b8f00aSschwarze 		return;
20286e96429aSschwarze 
20296e96429aSschwarze 	if (np->child == NULL) {
20306e96429aSschwarze 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
20316e96429aSschwarze 		    np->line, np->pos, "Rs");
203298b8f00aSschwarze 		return;
2033bb648afaSschwarze 	}
2034011fe33bSschwarze 
203520fa2881Sschwarze 	/*
203620fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
203720fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
2038b538baa5Sschwarze 	 * and correctly order it.  This is an insertion sort.
203920fa2881Sschwarze 	 */
204020fa2881Sschwarze 
204120fa2881Sschwarze 	next = NULL;
20426e96429aSschwarze 	for (nch = np->child->next; nch != NULL; nch = next) {
20436e96429aSschwarze 		/* Determine order number of this child. */
204420fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
20456e96429aSschwarze 			if (rsord[i] == nch->tok)
204620fa2881Sschwarze 				break;
204720fa2881Sschwarze 
2048b538baa5Sschwarze 		if (i == RSORD_MAX) {
204914a309e3Sschwarze 			mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
205014a309e3Sschwarze 			    nch->line, nch->pos, roff_name[nch->tok]);
2051b538baa5Sschwarze 			i = -1;
20526e96429aSschwarze 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
20536e96429aSschwarze 			np->norm->Rs.quote_T++;
2054b538baa5Sschwarze 
205520fa2881Sschwarze 		/*
20566e96429aSschwarze 		 * Remove this child from the chain.  This somewhat
2057fa2127f9Sschwarze 		 * repeats roff_node_unlink(), but since we're
205820fa2881Sschwarze 		 * just re-ordering, there's no need for the
205920fa2881Sschwarze 		 * full unlink process.
206020fa2881Sschwarze 		 */
206120fa2881Sschwarze 
20626e96429aSschwarze 		if ((next = nch->next) != NULL)
20636e96429aSschwarze 			next->prev = nch->prev;
206420fa2881Sschwarze 
20656e96429aSschwarze 		if ((prev = nch->prev) != NULL)
20666e96429aSschwarze 			prev->next = nch->next;
206720fa2881Sschwarze 
20686e96429aSschwarze 		nch->prev = nch->next = NULL;
206920fa2881Sschwarze 
207020fa2881Sschwarze 		/*
207120fa2881Sschwarze 		 * Scan back until we reach a node that's
20726e96429aSschwarze 		 * to be ordered before this child.
207320fa2881Sschwarze 		 */
207420fa2881Sschwarze 
207520fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
207620fa2881Sschwarze 			/* Determine order of `prev'. */
207720fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
207820fa2881Sschwarze 				if (rsord[j] == prev->tok)
207920fa2881Sschwarze 					break;
2080b538baa5Sschwarze 			if (j == RSORD_MAX)
2081b538baa5Sschwarze 				j = -1;
208220fa2881Sschwarze 
208320fa2881Sschwarze 			if (j <= i)
208420fa2881Sschwarze 				break;
208520fa2881Sschwarze 		}
208620fa2881Sschwarze 
208720fa2881Sschwarze 		/*
20886e96429aSschwarze 		 * Set this child back into its correct place
20896e96429aSschwarze 		 * in front of the `prev' node.
209020fa2881Sschwarze 		 */
209120fa2881Sschwarze 
20926e96429aSschwarze 		nch->prev = prev;
209320fa2881Sschwarze 
20946e96429aSschwarze 		if (prev == NULL) {
20956e96429aSschwarze 			np->child->prev = nch;
20966e96429aSschwarze 			nch->next = np->child;
20976e96429aSschwarze 			np->child = nch;
209820fa2881Sschwarze 		} else {
20996e96429aSschwarze 			if (prev->next)
21006e96429aSschwarze 				prev->next->prev = nch;
21016e96429aSschwarze 			nch->next = prev->next;
21026e96429aSschwarze 			prev->next = nch;
210320fa2881Sschwarze 		}
2104011fe33bSschwarze 	}
2105011fe33bSschwarze }
2106011fe33bSschwarze 
21074039b21cSschwarze /*
21084039b21cSschwarze  * For some arguments of some macros,
21094039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
21104039b21cSschwarze  */
211198b8f00aSschwarze static void
21124039b21cSschwarze post_hyph(POST_ARGS)
21134039b21cSschwarze {
21143a0d07afSschwarze 	struct roff_node	*nch;
21154039b21cSschwarze 	char			*cp;
21164039b21cSschwarze 
2117b7530f2fSschwarze 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2118d1982c71Sschwarze 		if (nch->type != ROFFT_TEXT)
21194039b21cSschwarze 			continue;
21204039b21cSschwarze 		cp = nch->string;
2121b7530f2fSschwarze 		if (*cp == '\0')
21224039b21cSschwarze 			continue;
2123b7530f2fSschwarze 		while (*(++cp) != '\0')
2124b7530f2fSschwarze 			if (*cp == '-' &&
21254039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
21264039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
21274039b21cSschwarze 				*cp = ASCII_HYPH;
21284039b21cSschwarze 	}
21294039b21cSschwarze }
21304039b21cSschwarze 
213198b8f00aSschwarze static void
2132af216717Sschwarze post_ns(POST_ARGS)
2133af216717Sschwarze {
2134676013e4Sschwarze 	struct roff_node	*n;
2135af216717Sschwarze 
2136676013e4Sschwarze 	n = mdoc->last;
2137676013e4Sschwarze 	if (n->flags & NODE_LINE ||
2138676013e4Sschwarze 	    (n->next != NULL && n->next->flags & NODE_DELIMC))
213928153913Sschwarze 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
2140676013e4Sschwarze 		    n->line, n->pos, NULL);
2141af216717Sschwarze }
2142af216717Sschwarze 
214398b8f00aSschwarze static void
2144fe8e59edSschwarze post_sx(POST_ARGS)
2145fe8e59edSschwarze {
2146fe8e59edSschwarze 	post_delim(mdoc);
2147fe8e59edSschwarze 	post_hyph(mdoc);
2148fe8e59edSschwarze }
2149fe8e59edSschwarze 
2150fe8e59edSschwarze static void
2151f73abda9Skristaps post_sh(POST_ARGS)
2152f73abda9Skristaps {
2153f73abda9Skristaps 
2154753701eeSschwarze 	post_ignpar(mdoc);
2155753701eeSschwarze 
2156cd6c268fSschwarze 	switch (mdoc->last->type) {
2157d1982c71Sschwarze 	case ROFFT_HEAD:
215898b8f00aSschwarze 		post_sh_head(mdoc);
215998b8f00aSschwarze 		break;
2160d1982c71Sschwarze 	case ROFFT_BODY:
2161cd6c268fSschwarze 		switch (mdoc->lastsec)  {
2162cd6c268fSschwarze 		case SEC_NAME:
216398b8f00aSschwarze 			post_sh_name(mdoc);
216498b8f00aSschwarze 			break;
21657c384856Sschwarze 		case SEC_SEE_ALSO:
216698b8f00aSschwarze 			post_sh_see_also(mdoc);
216798b8f00aSschwarze 			break;
2168cd6c268fSschwarze 		case SEC_AUTHORS:
216998b8f00aSschwarze 			post_sh_authors(mdoc);
217098b8f00aSschwarze 			break;
2171cd6c268fSschwarze 		default:
2172cd6c268fSschwarze 			break;
2173cd6c268fSschwarze 		}
2174cd6c268fSschwarze 		break;
2175cd6c268fSschwarze 	default:
2176cd6c268fSschwarze 		break;
2177cd6c268fSschwarze 	}
2178f73abda9Skristaps }
2179f73abda9Skristaps 
218098b8f00aSschwarze static void
2181cd6c268fSschwarze post_sh_name(POST_ARGS)
2182f73abda9Skristaps {
21833a0d07afSschwarze 	struct roff_node *n;
218420e2cf25Sschwarze 	int hasnm, hasnd;
2185f73abda9Skristaps 
218620e2cf25Sschwarze 	hasnm = hasnd = 0;
2187f73abda9Skristaps 
218820e2cf25Sschwarze 	for (n = mdoc->last->child; n != NULL; n = n->next) {
218920e2cf25Sschwarze 		switch (n->tok) {
219020e2cf25Sschwarze 		case MDOC_Nm:
2191f27faaccSschwarze 			if (hasnm && n->child != NULL)
2192f27faaccSschwarze 				mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
2193f27faaccSschwarze 				    mdoc->parse, n->line, n->pos,
2194f27faaccSschwarze 				    "Nm %s", n->child->string);
219520e2cf25Sschwarze 			hasnm = 1;
2196f27faaccSschwarze 			continue;
219720e2cf25Sschwarze 		case MDOC_Nd:
219820e2cf25Sschwarze 			hasnd = 1;
219920e2cf25Sschwarze 			if (n->next != NULL)
220020e2cf25Sschwarze 				mandoc_msg(MANDOCERR_NAMESEC_ND,
220120e2cf25Sschwarze 				    mdoc->parse, n->line, n->pos, NULL);
220220e2cf25Sschwarze 			break;
22032d6f95d3Sschwarze 		case TOKEN_NONE:
2204f27faaccSschwarze 			if (n->type == ROFFT_TEXT &&
2205f27faaccSschwarze 			    n->string[0] == ',' && n->string[1] == '\0' &&
2206f27faaccSschwarze 			    n->next != NULL && n->next->tok == MDOC_Nm) {
2207f27faaccSschwarze 				n = n->next;
2208f27faaccSschwarze 				continue;
2209f27faaccSschwarze 			}
2210fa072f7fSschwarze 			/* FALLTHROUGH */
221120e2cf25Sschwarze 		default:
221251fcab2fSschwarze 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
221314a309e3Sschwarze 			    n->line, n->pos, roff_name[n->tok]);
2214f27faaccSschwarze 			continue;
221520e2cf25Sschwarze 		}
2216f27faaccSschwarze 		break;
2217f73abda9Skristaps 	}
2218f73abda9Skristaps 
221920e2cf25Sschwarze 	if ( ! hasnm)
222020e2cf25Sschwarze 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
222120e2cf25Sschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
222220e2cf25Sschwarze 	if ( ! hasnd)
222320e2cf25Sschwarze 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
222420e2cf25Sschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
222520fa2881Sschwarze }
2226f73abda9Skristaps 
222798b8f00aSschwarze static void
22287c384856Sschwarze post_sh_see_also(POST_ARGS)
22297c384856Sschwarze {
22303a0d07afSschwarze 	const struct roff_node	*n;
22317c384856Sschwarze 	const char		*name, *sec;
22327c384856Sschwarze 	const char		*lastname, *lastsec, *lastpunct;
22337c384856Sschwarze 	int			 cmp;
22347c384856Sschwarze 
22357c384856Sschwarze 	n = mdoc->last->child;
22367c384856Sschwarze 	lastname = lastsec = lastpunct = NULL;
22377c384856Sschwarze 	while (n != NULL) {
223830e5ee06Sschwarze 		if (n->tok != MDOC_Xr ||
223930e5ee06Sschwarze 		    n->child == NULL ||
224030e5ee06Sschwarze 		    n->child->next == NULL)
22417c384856Sschwarze 			break;
22427c384856Sschwarze 
22437c384856Sschwarze 		/* Process one .Xr node. */
22447c384856Sschwarze 
22457c384856Sschwarze 		name = n->child->string;
22467c384856Sschwarze 		sec = n->child->next->string;
22477c384856Sschwarze 		if (lastsec != NULL) {
22487c384856Sschwarze 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
22497c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
22507c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
22517c384856Sschwarze 				    "%s before %s(%s)", lastpunct,
22527c384856Sschwarze 				    name, sec);
22537c384856Sschwarze 			cmp = strcmp(lastsec, sec);
22547c384856Sschwarze 			if (cmp > 0)
22557c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
22567c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
22577c384856Sschwarze 				    "%s(%s) after %s(%s)", name,
22587c384856Sschwarze 				    sec, lastname, lastsec);
22597c384856Sschwarze 			else if (cmp == 0 &&
22607c384856Sschwarze 			    strcasecmp(lastname, name) > 0)
22617c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
22627c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
22637c384856Sschwarze 				    "%s after %s", name, lastname);
22647c384856Sschwarze 		}
22657c384856Sschwarze 		lastname = name;
22667c384856Sschwarze 		lastsec = sec;
22677c384856Sschwarze 
22687c384856Sschwarze 		/* Process the following node. */
22697c384856Sschwarze 
22707c384856Sschwarze 		n = n->next;
22717c384856Sschwarze 		if (n == NULL)
22727c384856Sschwarze 			break;
22737c384856Sschwarze 		if (n->tok == MDOC_Xr) {
22747c384856Sschwarze 			lastpunct = "none";
22757c384856Sschwarze 			continue;
22767c384856Sschwarze 		}
2277d1982c71Sschwarze 		if (n->type != ROFFT_TEXT)
22787c384856Sschwarze 			break;
22797c384856Sschwarze 		for (name = n->string; *name != '\0'; name++)
22807c384856Sschwarze 			if (isalpha((const unsigned char)*name))
228198b8f00aSschwarze 				return;
22827c384856Sschwarze 		lastpunct = n->string;
2283c7098240Sschwarze 		if (n->next == NULL || n->next->tok == MDOC_Rs)
22847c384856Sschwarze 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
22857c384856Sschwarze 			    n->line, n->pos, "%s after %s(%s)",
22867c384856Sschwarze 			    lastpunct, lastname, lastsec);
22877c384856Sschwarze 		n = n->next;
22887c384856Sschwarze 	}
22897c384856Sschwarze }
22907c384856Sschwarze 
22917c384856Sschwarze static int
22923a0d07afSschwarze child_an(const struct roff_node *n)
2293cd6c268fSschwarze {
2294cd6c268fSschwarze 
2295cd6c268fSschwarze 	for (n = n->child; n != NULL; n = n->next)
229630e5ee06Sschwarze 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2297526e306bSschwarze 			return 1;
2298526e306bSschwarze 	return 0;
2299cd6c268fSschwarze }
2300cd6c268fSschwarze 
230198b8f00aSschwarze static void
2302cd6c268fSschwarze post_sh_authors(POST_ARGS)
2303cd6c268fSschwarze {
2304cd6c268fSschwarze 
2305cd6c268fSschwarze 	if ( ! child_an(mdoc->last))
2306cd6c268fSschwarze 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2307cd6c268fSschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
2308cd6c268fSschwarze }
2309cd6c268fSschwarze 
23104482121fSschwarze /*
23114482121fSschwarze  * Return an upper bound for the string distance (allowing
23124482121fSschwarze  * transpositions).  Not a full Levenshtein implementation
23134482121fSschwarze  * because Levenshtein is quadratic in the string length
23144482121fSschwarze  * and this function is called for every standard name,
23154482121fSschwarze  * so the check for each custom name would be cubic.
23164482121fSschwarze  * The following crude heuristics is linear, resulting
23174482121fSschwarze  * in quadratic behaviour for checking one custom name,
23184482121fSschwarze  * which does not cause measurable slowdown.
23194482121fSschwarze  */
23204482121fSschwarze static int
23214482121fSschwarze similar(const char *s1, const char *s2)
23224482121fSschwarze {
23234482121fSschwarze 	const int	maxdist = 3;
23244482121fSschwarze 	int		dist = 0;
23254482121fSschwarze 
23264482121fSschwarze 	while (s1[0] != '\0' && s2[0] != '\0') {
23274482121fSschwarze 		if (s1[0] == s2[0]) {
23284482121fSschwarze 			s1++;
23294482121fSschwarze 			s2++;
23304482121fSschwarze 			continue;
23314482121fSschwarze 		}
23324482121fSschwarze 		if (++dist > maxdist)
23334482121fSschwarze 			return INT_MAX;
23344482121fSschwarze 		if (s1[1] == s2[1]) {  /* replacement */
23354482121fSschwarze 			s1++;
23364482121fSschwarze 			s2++;
23374482121fSschwarze 		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
23384482121fSschwarze 			s1 += 2;	/* transposition */
23394482121fSschwarze 			s2 += 2;
23404482121fSschwarze 		} else if (s1[0] == s2[1])  /* insertion */
23414482121fSschwarze 			s2++;
23424482121fSschwarze 		else if (s1[1] == s2[0])  /* deletion */
23434482121fSschwarze 			s1++;
23444482121fSschwarze 		else
23454482121fSschwarze 			return INT_MAX;
23464482121fSschwarze 	}
23474482121fSschwarze 	dist += strlen(s1) + strlen(s2);
23484482121fSschwarze 	return dist > maxdist ? INT_MAX : dist;
23494482121fSschwarze }
23504482121fSschwarze 
235198b8f00aSschwarze static void
2352f73abda9Skristaps post_sh_head(POST_ARGS)
2353f73abda9Skristaps {
2354cf0e2075Sschwarze 	struct roff_node	*nch;
235551fcab2fSschwarze 	const char		*goodsec;
23564482121fSschwarze 	const char *const	*testsec;
23574482121fSschwarze 	int			 dist, mindist;
23583a0d07afSschwarze 	enum roff_sec		 sec;
2359f73abda9Skristaps 
2360f73abda9Skristaps 	/*
2361f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
236220fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
236320fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
236420fa2881Sschwarze 	 * manual sections.
2365f73abda9Skristaps 	 */
2366f73abda9Skristaps 
2367396853b5Sschwarze 	sec = mdoc->last->sec;
2368f73abda9Skristaps 
236920fa2881Sschwarze 	/* The NAME should be first. */
2370f73abda9Skristaps 
2371d37754b9Sschwarze 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2372bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2373d37754b9Sschwarze 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
2374cf0e2075Sschwarze 		    sec != SEC_CUSTOM ? secnames[sec] :
2375cf0e2075Sschwarze 		    (nch = mdoc->last->child) == NULL ? "" :
2376cf0e2075Sschwarze 		    nch->type == ROFFT_TEXT ? nch->string :
237714a309e3Sschwarze 		    roff_name[nch->tok]);
237820fa2881Sschwarze 
237920fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
238020fa2881Sschwarze 
2381396853b5Sschwarze 	if (sec == SEC_SYNOPSIS) {
238275088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
238320fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
238422881299Sschwarze 	} else {
238575088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
238620fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
238722881299Sschwarze 	}
238820fa2881Sschwarze 
238920fa2881Sschwarze 	/* Mark our last section. */
239020fa2881Sschwarze 
239120fa2881Sschwarze 	mdoc->lastsec = sec;
23921eccdf28Sschwarze 
239320fa2881Sschwarze 	/* We don't care about custom sections after this. */
2394fccfce9dSschwarze 
23954482121fSschwarze 	if (sec == SEC_CUSTOM) {
23964482121fSschwarze 		if ((nch = mdoc->last->child) == NULL ||
23974482121fSschwarze 		    nch->type != ROFFT_TEXT || nch->next != NULL)
239898b8f00aSschwarze 			return;
23994482121fSschwarze 		goodsec = NULL;
24004482121fSschwarze 		mindist = INT_MAX;
24014482121fSschwarze 		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
24024482121fSschwarze 			dist = similar(nch->string, *testsec);
24034482121fSschwarze 			if (dist < mindist) {
24044482121fSschwarze 				goodsec = *testsec;
24054482121fSschwarze 				mindist = dist;
24064482121fSschwarze 			}
24074482121fSschwarze 		}
24084482121fSschwarze 		if (goodsec != NULL)
24094482121fSschwarze 			mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse,
24104482121fSschwarze 			    nch->line, nch->pos, "Sh %s instead of %s",
24114482121fSschwarze 			    nch->string, goodsec);
24124482121fSschwarze 		return;
24134482121fSschwarze 	}
2414fccfce9dSschwarze 
24156be99f77Sschwarze 	/*
241620fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
241720fa2881Sschwarze 	 * out of order.
24186be99f77Sschwarze 	 */
2419f73abda9Skristaps 
242020fa2881Sschwarze 	if (sec == mdoc->lastnamed)
2421bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2422bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
2423396853b5Sschwarze 		    "Sh %s", secnames[sec]);
242420fa2881Sschwarze 
242520fa2881Sschwarze 	if (sec < mdoc->lastnamed)
2426bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2427bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
2428396853b5Sschwarze 		    "Sh %s", secnames[sec]);
242920fa2881Sschwarze 
243020fa2881Sschwarze 	/* Mark the last named section. */
243120fa2881Sschwarze 
243220fa2881Sschwarze 	mdoc->lastnamed = sec;
243320fa2881Sschwarze 
243420fa2881Sschwarze 	/* Check particular section/manual conventions. */
243520fa2881Sschwarze 
2436396853b5Sschwarze 	if (mdoc->meta.msec == NULL)
243798b8f00aSschwarze 		return;
243820fa2881Sschwarze 
243951fcab2fSschwarze 	goodsec = NULL;
244020fa2881Sschwarze 	switch (sec) {
244149aff9f8Sschwarze 	case SEC_ERRORS:
2442be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
2443be89e780Sschwarze 			break;
244451fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
2445be89e780Sschwarze 		/* FALLTHROUGH */
244649aff9f8Sschwarze 	case SEC_RETURN_VALUES:
244749aff9f8Sschwarze 	case SEC_LIBRARY:
244892c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
2449f73abda9Skristaps 			break;
245092c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
245192c0ca7fSschwarze 			break;
245251fcab2fSschwarze 		if (NULL == goodsec)
245351fcab2fSschwarze 			goodsec = "2, 3, 9";
245403ab2f23Sdlg 		/* FALLTHROUGH */
245549aff9f8Sschwarze 	case SEC_CONTEXT:
245692c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
245792c0ca7fSschwarze 			break;
245851fcab2fSschwarze 		if (NULL == goodsec)
245951fcab2fSschwarze 			goodsec = "9";
246051fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
246151fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
2462396853b5Sschwarze 		    "Sh %s for %s only", secnames[sec], goodsec);
246320fa2881Sschwarze 		break;
2464f73abda9Skristaps 	default:
2465f73abda9Skristaps 		break;
2466f73abda9Skristaps 	}
2467f73abda9Skristaps }
2468d39b9a9cSschwarze 
246998b8f00aSschwarze static void
24705ae08040Sschwarze post_xr(POST_ARGS)
24715ae08040Sschwarze {
24725ae08040Sschwarze 	struct roff_node *n, *nch;
24735ae08040Sschwarze 
24745ae08040Sschwarze 	n = mdoc->last;
24755ae08040Sschwarze 	nch = n->child;
24765ae08040Sschwarze 	if (nch->next == NULL) {
24775ae08040Sschwarze 		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
24785ae08040Sschwarze 		    n->line, n->pos, "Xr %s", nch->string);
247919b6bef7Sschwarze 	} else {
24805ae08040Sschwarze 		assert(nch->next == n->last);
248152d11c96Sschwarze 		if(mandoc_xr_add(nch->next->string, nch->string,
248252d11c96Sschwarze 		    nch->line, nch->pos))
248352d11c96Sschwarze 			mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse,
248452d11c96Sschwarze 			    nch->line, nch->pos, "Xr %s %s",
248552d11c96Sschwarze 			    nch->string, nch->next->string);
248619b6bef7Sschwarze 	}
2487fe8e59edSschwarze 	post_delim_nb(mdoc);
24885ae08040Sschwarze }
24895ae08040Sschwarze 
24905ae08040Sschwarze static void
2491f6127a73Sschwarze post_ignpar(POST_ARGS)
2492f6127a73Sschwarze {
24933a0d07afSschwarze 	struct roff_node *np;
2494f6127a73Sschwarze 
2495b7530f2fSschwarze 	switch (mdoc->last->type) {
24969c7c6a1fSschwarze 	case ROFFT_BLOCK:
24979c7c6a1fSschwarze 		post_prevpar(mdoc);
24989c7c6a1fSschwarze 		return;
2499d1982c71Sschwarze 	case ROFFT_HEAD:
2500fe8e59edSschwarze 		post_delim(mdoc);
2501753701eeSschwarze 		post_hyph(mdoc);
250298b8f00aSschwarze 		return;
2503d1982c71Sschwarze 	case ROFFT_BODY:
2504b7530f2fSschwarze 		break;
2505b7530f2fSschwarze 	default:
2506b7530f2fSschwarze 		return;
2507b7530f2fSschwarze 	}
2508f6127a73Sschwarze 
2509f051602aSschwarze 	if ((np = mdoc->last->child) != NULL)
2510*7c539ecbSschwarze 		if (np->tok == MDOC_Pp) {
251120369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
251220369664Sschwarze 			    mdoc->parse, np->line, np->pos,
251314a309e3Sschwarze 			    "%s after %s", roff_name[np->tok],
251414a309e3Sschwarze 			    roff_name[mdoc->last->tok]);
2515fa2127f9Sschwarze 			roff_node_delete(mdoc, np);
2516f6127a73Sschwarze 		}
2517f6127a73Sschwarze 
2518f051602aSschwarze 	if ((np = mdoc->last->last) != NULL)
2519*7c539ecbSschwarze 		if (np->tok == MDOC_Pp) {
252020369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
252120369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
252214a309e3Sschwarze 			    roff_name[np->tok],
252314a309e3Sschwarze 			    roff_name[mdoc->last->tok]);
2524fa2127f9Sschwarze 			roff_node_delete(mdoc, np);
2525f6127a73Sschwarze 		}
2526f6127a73Sschwarze }
2527f6127a73Sschwarze 
252898b8f00aSschwarze static void
25293e642ba0Sschwarze post_prevpar(POST_ARGS)
2530d39b9a9cSschwarze {
25313e642ba0Sschwarze 	struct roff_node *n;
2532d39b9a9cSschwarze 
25333e642ba0Sschwarze 	n = mdoc->last;
25343e642ba0Sschwarze 	if (NULL == n->prev)
253598b8f00aSschwarze 		return;
2536d1982c71Sschwarze 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
253798b8f00aSschwarze 		return;
2538d39b9a9cSschwarze 
253920fa2881Sschwarze 	/*
2540*7c539ecbSschwarze 	 * Don't allow `Pp' prior to a paragraph-type
2541*7c539ecbSschwarze 	 * block: `Pp' or non-compact `Bd' or `Bl'.
254220fa2881Sschwarze 	 */
2543d39b9a9cSschwarze 
2544*7c539ecbSschwarze 	if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br)
254598b8f00aSschwarze 		return;
25463e642ba0Sschwarze 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
254798b8f00aSschwarze 		return;
25483e642ba0Sschwarze 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
254998b8f00aSschwarze 		return;
25503e642ba0Sschwarze 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
255198b8f00aSschwarze 		return;
2552d39b9a9cSschwarze 
255320369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
255414a309e3Sschwarze 	    n->prev->line, n->prev->pos, "%s before %s",
255514a309e3Sschwarze 	    roff_name[n->prev->tok], roff_name[n->tok]);
25563e642ba0Sschwarze 	roff_node_delete(mdoc, n->prev);
2557d39b9a9cSschwarze }
255820fa2881Sschwarze 
255998b8f00aSschwarze static void
2560e0dd4c9cSschwarze post_par(POST_ARGS)
2561e0dd4c9cSschwarze {
25623a0d07afSschwarze 	struct roff_node *np;
2563e0dd4c9cSschwarze 
25643798fb25Sschwarze 	np = mdoc->last;
25656561cb23Sschwarze 	if (np->tok != ROFF_br && np->tok != ROFF_sp)
25663e642ba0Sschwarze 		post_prevpar(mdoc);
2567753701eeSschwarze 
25686561cb23Sschwarze 	if (np->tok == ROFF_sp) {
256930e5ee06Sschwarze 		if (np->child != NULL && np->child->next != NULL)
25703798fb25Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
25713798fb25Sschwarze 			    np->child->next->line, np->child->next->pos,
25723798fb25Sschwarze 			    "sp ... %s", np->child->next->string);
25733798fb25Sschwarze 	} else if (np->child != NULL)
25743798fb25Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
25753798fb25Sschwarze 		    mdoc->parse, np->line, np->pos, "%s %s",
257614a309e3Sschwarze 		    roff_name[np->tok], np->child->string);
2577e0dd4c9cSschwarze 
2578f051602aSschwarze 	if ((np = mdoc->last->prev) == NULL) {
257920369664Sschwarze 		np = mdoc->last->parent;
2580f051602aSschwarze 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
258198b8f00aSschwarze 			return;
2582*7c539ecbSschwarze 	} else if (np->tok != MDOC_Pp &&
258329478532Sschwarze 	    (mdoc->last->tok != ROFF_br ||
25846561cb23Sschwarze 	     (np->tok != ROFF_sp && np->tok != ROFF_br)))
258598b8f00aSschwarze 		return;
2586e0dd4c9cSschwarze 
258720369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
258814a309e3Sschwarze 	    mdoc->last->line, mdoc->last->pos, "%s after %s",
258914a309e3Sschwarze 	    roff_name[mdoc->last->tok], roff_name[np->tok]);
2590fa2127f9Sschwarze 	roff_node_delete(mdoc, mdoc->last);
2591e0dd4c9cSschwarze }
2592e0dd4c9cSschwarze 
259398b8f00aSschwarze static void
259420fa2881Sschwarze post_dd(POST_ARGS)
259520fa2881Sschwarze {
25963a0d07afSschwarze 	struct roff_node *n;
259783af2bccSschwarze 	char		 *datestr;
259820fa2881Sschwarze 
2599b058e777Sschwarze 	n = mdoc->last;
260043808411Sschwarze 	n->flags |= NODE_NOPRT;
260143808411Sschwarze 
2602396853b5Sschwarze 	if (mdoc->meta.date != NULL) {
2603396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2604396853b5Sschwarze 		    n->line, n->pos, "Dd");
2605396853b5Sschwarze 		free(mdoc->meta.date);
2606396853b5Sschwarze 	} else if (mdoc->flags & MDOC_PBODY)
2607396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2608396853b5Sschwarze 		    n->line, n->pos, "Dd");
2609396853b5Sschwarze 	else if (mdoc->meta.title != NULL)
2610396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2611396853b5Sschwarze 		    n->line, n->pos, "Dd after Dt");
2612396853b5Sschwarze 	else if (mdoc->meta.os != NULL)
2613396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2614396853b5Sschwarze 		    n->line, n->pos, "Dd after Os");
2615396853b5Sschwarze 
2616f051602aSschwarze 	if (n->child == NULL || n->child->string[0] == '\0') {
2617231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
26183427e516Sschwarze 		    mandoc_normdate(mdoc, NULL, n->line, n->pos);
261943808411Sschwarze 		return;
262020fa2881Sschwarze 	}
262120fa2881Sschwarze 
262283af2bccSschwarze 	datestr = NULL;
2623423631c9Sschwarze 	deroff(&datestr, n);
262483af2bccSschwarze 	if (mdoc->quick)
262583af2bccSschwarze 		mdoc->meta.date = datestr;
262683af2bccSschwarze 	else {
26273427e516Sschwarze 		mdoc->meta.date = mandoc_normdate(mdoc,
262883af2bccSschwarze 		    datestr, n->line, n->pos);
262983af2bccSschwarze 		free(datestr);
263004e980cbSschwarze 	}
263120fa2881Sschwarze }
263220fa2881Sschwarze 
263398b8f00aSschwarze static void
263420fa2881Sschwarze post_dt(POST_ARGS)
263520fa2881Sschwarze {
26363a0d07afSschwarze 	struct roff_node *nn, *n;
263720fa2881Sschwarze 	const char	 *cp;
263820fa2881Sschwarze 	char		 *p;
263920fa2881Sschwarze 
264020fa2881Sschwarze 	n = mdoc->last;
264143808411Sschwarze 	n->flags |= NODE_NOPRT;
264243808411Sschwarze 
2643396853b5Sschwarze 	if (mdoc->flags & MDOC_PBODY) {
2644396853b5Sschwarze 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2645396853b5Sschwarze 		    n->line, n->pos, "Dt");
264643808411Sschwarze 		return;
2647396853b5Sschwarze 	}
2648396853b5Sschwarze 
2649396853b5Sschwarze 	if (mdoc->meta.title != NULL)
2650396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2651396853b5Sschwarze 		    n->line, n->pos, "Dt");
2652396853b5Sschwarze 	else if (mdoc->meta.os != NULL)
2653396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2654396853b5Sschwarze 		    n->line, n->pos, "Dt after Os");
265520fa2881Sschwarze 
265620fa2881Sschwarze 	free(mdoc->meta.title);
26573fdead0cSschwarze 	free(mdoc->meta.msec);
265820fa2881Sschwarze 	free(mdoc->meta.vol);
265920fa2881Sschwarze 	free(mdoc->meta.arch);
266020fa2881Sschwarze 
26613fdead0cSschwarze 	mdoc->meta.title = NULL;
26623fdead0cSschwarze 	mdoc->meta.msec = NULL;
26633fdead0cSschwarze 	mdoc->meta.vol = NULL;
26643fdead0cSschwarze 	mdoc->meta.arch = NULL;
266520fa2881Sschwarze 
26669a928a59Sschwarze 	/* Mandatory first argument: title. */
266720fa2881Sschwarze 
26689a928a59Sschwarze 	nn = n->child;
26699a928a59Sschwarze 	if (nn == NULL || *nn->string == '\0') {
26709a928a59Sschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
26719a928a59Sschwarze 		    mdoc->parse, n->line, n->pos, "Dt");
26729a928a59Sschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
26739a928a59Sschwarze 	} else {
26749a928a59Sschwarze 		mdoc->meta.title = mandoc_strdup(nn->string);
26759a928a59Sschwarze 
26769a928a59Sschwarze 		/* Check that all characters are uppercase. */
26779a928a59Sschwarze 
26789a928a59Sschwarze 		for (p = nn->string; *p != '\0'; p++)
26799a928a59Sschwarze 			if (islower((unsigned char)*p)) {
2680bd594191Sschwarze 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
268151fcab2fSschwarze 				    mdoc->parse, nn->line,
268251fcab2fSschwarze 				    nn->pos + (p - nn->string),
2683bd594191Sschwarze 				    "Dt %s", nn->string);
268420fa2881Sschwarze 				break;
268520fa2881Sschwarze 			}
268620fa2881Sschwarze 	}
268720fa2881Sschwarze 
26885ae08040Sschwarze 	/* Mandatory second argument: section. */
268920fa2881Sschwarze 
26909a928a59Sschwarze 	if (nn != NULL)
26919a928a59Sschwarze 		nn = nn->next;
269220fa2881Sschwarze 
26939a928a59Sschwarze 	if (nn == NULL) {
26943fdead0cSschwarze 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
26953fdead0cSschwarze 		    mdoc->parse, n->line, n->pos,
26963fdead0cSschwarze 		    "Dt %s", mdoc->meta.title);
269720fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
269843808411Sschwarze 		return;  /* msec and arch remain NULL. */
269920fa2881Sschwarze 	}
270020fa2881Sschwarze 
27019a928a59Sschwarze 	mdoc->meta.msec = mandoc_strdup(nn->string);
27029a928a59Sschwarze 
27039a928a59Sschwarze 	/* Infer volume title from section number. */
270420fa2881Sschwarze 
270588ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
27069a928a59Sschwarze 	if (cp == NULL) {
2707bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2708bd594191Sschwarze 		    nn->line, nn->pos, "Dt ... %s", nn->string);
270920fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
27109a928a59Sschwarze 	} else
27119a928a59Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
271220fa2881Sschwarze 
27139a928a59Sschwarze 	/* Optional third argument: architecture. */
271420fa2881Sschwarze 
27159a928a59Sschwarze 	if ((nn = nn->next) == NULL)
271643808411Sschwarze 		return;
27179a928a59Sschwarze 
27189a928a59Sschwarze 	for (p = nn->string; *p != '\0'; p++)
2719b94f27c5Sschwarze 		*p = tolower((unsigned char)*p);
2720b94f27c5Sschwarze 	mdoc->meta.arch = mandoc_strdup(nn->string);
272120fa2881Sschwarze 
27229a928a59Sschwarze 	/* Ignore fourth and later arguments. */
27239a928a59Sschwarze 
27249a928a59Sschwarze 	if ((nn = nn->next) != NULL)
27259a928a59Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
27269a928a59Sschwarze 		    nn->line, nn->pos, "Dt ... %s", nn->string);
272720fa2881Sschwarze }
272820fa2881Sschwarze 
272998b8f00aSschwarze static void
2730992063deSschwarze post_bx(POST_ARGS)
2731992063deSschwarze {
27323af8e8d7Sschwarze 	struct roff_node	*n, *nch;
2733f0c18971Sschwarze 	const char		*macro;
27343af8e8d7Sschwarze 
2735fe8e59edSschwarze 	post_delim_nb(mdoc);
273604fbb99fSschwarze 
27373af8e8d7Sschwarze 	n = mdoc->last;
27383af8e8d7Sschwarze 	nch = n->child;
27393af8e8d7Sschwarze 
27403af8e8d7Sschwarze 	if (nch != NULL) {
2741f0c18971Sschwarze 		macro = !strcmp(nch->string, "Open") ? "Ox" :
2742f0c18971Sschwarze 		    !strcmp(nch->string, "Net") ? "Nx" :
2743f0c18971Sschwarze 		    !strcmp(nch->string, "Free") ? "Fx" :
2744f0c18971Sschwarze 		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2745f0c18971Sschwarze 		if (macro != NULL)
2746f0c18971Sschwarze 			mandoc_msg(MANDOCERR_BX, mdoc->parse,
2747f0c18971Sschwarze 			    n->line, n->pos, macro);
27483af8e8d7Sschwarze 		mdoc->last = nch;
27493af8e8d7Sschwarze 		nch = nch->next;
27503af8e8d7Sschwarze 		mdoc->next = ROFF_NEXT_SIBLING;
27513af8e8d7Sschwarze 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
27523af8e8d7Sschwarze 		mdoc->last->flags |= NODE_NOSRC;
27533af8e8d7Sschwarze 		mdoc->next = ROFF_NEXT_SIBLING;
27543af8e8d7Sschwarze 	} else
27553af8e8d7Sschwarze 		mdoc->next = ROFF_NEXT_CHILD;
27563af8e8d7Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
27573af8e8d7Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
27583af8e8d7Sschwarze 
27593af8e8d7Sschwarze 	if (nch == NULL) {
27603af8e8d7Sschwarze 		mdoc->last = n;
27613af8e8d7Sschwarze 		return;
27623af8e8d7Sschwarze 	}
27633af8e8d7Sschwarze 
27643af8e8d7Sschwarze 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
27653af8e8d7Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
27663af8e8d7Sschwarze 	mdoc->next = ROFF_NEXT_SIBLING;
27673af8e8d7Sschwarze 	roff_word_alloc(mdoc, n->line, n->pos, "-");
27683af8e8d7Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
27693af8e8d7Sschwarze 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
27703af8e8d7Sschwarze 	mdoc->last->flags |= NODE_NOSRC;
27713af8e8d7Sschwarze 	mdoc->last = n;
2772992063deSschwarze 
2773992063deSschwarze 	/*
2774992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2775992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2776992063deSschwarze 	 * uppercase blindly.
2777992063deSschwarze 	 */
2778992063deSschwarze 
27793af8e8d7Sschwarze 	*nch->string = (char)toupper((unsigned char)*nch->string);
2780992063deSschwarze }
2781992063deSschwarze 
278298b8f00aSschwarze static void
278320fa2881Sschwarze post_os(POST_ARGS)
278420fa2881Sschwarze {
278520fa2881Sschwarze #ifndef OSNAME
278620fa2881Sschwarze 	struct utsname	  utsname;
27874c468128Sschwarze 	static char	 *defbuf;
278820fa2881Sschwarze #endif
27893a0d07afSschwarze 	struct roff_node *n;
279020fa2881Sschwarze 
279120fa2881Sschwarze 	n = mdoc->last;
279243808411Sschwarze 	n->flags |= NODE_NOPRT;
279343808411Sschwarze 
2794396853b5Sschwarze 	if (mdoc->meta.os != NULL)
2795396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2796396853b5Sschwarze 		    n->line, n->pos, "Os");
2797396853b5Sschwarze 	else if (mdoc->flags & MDOC_PBODY)
2798396853b5Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2799396853b5Sschwarze 		    n->line, n->pos, "Os");
280020fa2881Sschwarze 
2801fe8e59edSschwarze 	post_delim(mdoc);
2802fe8e59edSschwarze 
280320fa2881Sschwarze 	/*
2804353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2805353fa9ecSschwarze 	 * The order of precedence is:
2806353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2807353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2808353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2809353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
281020fa2881Sschwarze 	 */
281120fa2881Sschwarze 
281220fa2881Sschwarze 	free(mdoc->meta.os);
281383af2bccSschwarze 	mdoc->meta.os = NULL;
2814423631c9Sschwarze 	deroff(&mdoc->meta.os, n);
281583af2bccSschwarze 	if (mdoc->meta.os)
2816ce0ef847Sschwarze 		goto out;
28174c468128Sschwarze 
2818f3476b07Sschwarze 	if (mdoc->os_s != NULL) {
2819f3476b07Sschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2820ce0ef847Sschwarze 		goto out;
2821353fa9ecSschwarze 	}
28224c468128Sschwarze 
282320fa2881Sschwarze #ifdef OSNAME
28244c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
282520fa2881Sschwarze #else /*!OSNAME */
2826f051602aSschwarze 	if (defbuf == NULL) {
2827f051602aSschwarze 		if (uname(&utsname) == -1) {
2828f79e7afeSschwarze 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2829f79e7afeSschwarze 			    n->line, n->pos, "Os");
28304c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2831a450f7c4Sschwarze 		} else
2832a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2833a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
283420fa2881Sschwarze 	}
28354c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
283620fa2881Sschwarze #endif /*!OSNAME*/
2837ce0ef847Sschwarze 
2838f3476b07Sschwarze out:
2839f3476b07Sschwarze 	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2840f3476b07Sschwarze 		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2841f3476b07Sschwarze 			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2842f3476b07Sschwarze 		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2843f3476b07Sschwarze 			mdoc->meta.os_e = MANDOC_OS_NETBSD;
2844f3476b07Sschwarze 	}
28453427e516Sschwarze 
28463427e516Sschwarze 	/*
28473427e516Sschwarze 	 * This is the earliest point where we can check
28483427e516Sschwarze 	 * Mdocdate conventions because we don't know
28493427e516Sschwarze 	 * the operating system earlier.
28503427e516Sschwarze 	 */
28513427e516Sschwarze 
28522f84042eSschwarze 	if (n->child != NULL)
28532f84042eSschwarze 		mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
28542f84042eSschwarze 		    n->child->line, n->child->pos,
28552f84042eSschwarze 		    "Os %s (%s)", n->child->string,
28562f84042eSschwarze 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
28572f84042eSschwarze 		    "OpenBSD" : "NetBSD");
28582f84042eSschwarze 
28593427e516Sschwarze 	while (n->tok != MDOC_Dd)
28603427e516Sschwarze 		if ((n = n->prev) == NULL)
28613427e516Sschwarze 			return;
28623427e516Sschwarze 	if ((n = n->child) == NULL)
28633427e516Sschwarze 		return;
286407ec32d0Sschwarze 	if (strncmp(n->string, "$" "Mdocdate", 9)) {
2865f3476b07Sschwarze 		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
28663427e516Sschwarze 			mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
28673427e516Sschwarze 			    mdoc->parse, n->line, n->pos,
2868f3476b07Sschwarze 			    "Dd %s (OpenBSD)", n->string);
28693427e516Sschwarze 	} else {
2870f3476b07Sschwarze 		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
28713427e516Sschwarze 			mandoc_vmsg(MANDOCERR_MDOCDATE,
28723427e516Sschwarze 			    mdoc->parse, n->line, n->pos,
2873f3476b07Sschwarze 			    "Dd %s (NetBSD)", n->string);
28743427e516Sschwarze 	}
287520fa2881Sschwarze }
287620fa2881Sschwarze 
2877396853b5Sschwarze enum roff_sec
2878396853b5Sschwarze mdoc_a2sec(const char *p)
287919a69263Sschwarze {
288019a69263Sschwarze 	int		 i;
288119a69263Sschwarze 
288219a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
288319a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2884526e306bSschwarze 			return (enum roff_sec)i;
288519a69263Sschwarze 
2886526e306bSschwarze 	return SEC_CUSTOM;
288719a69263Sschwarze }
288819a69263Sschwarze 
288919a69263Sschwarze static size_t
289014a309e3Sschwarze macro2len(enum roff_tok macro)
289119a69263Sschwarze {
289219a69263Sschwarze 
289319a69263Sschwarze 	switch (macro) {
289449aff9f8Sschwarze 	case MDOC_Ad:
2895526e306bSschwarze 		return 12;
289649aff9f8Sschwarze 	case MDOC_Ao:
2897526e306bSschwarze 		return 12;
289849aff9f8Sschwarze 	case MDOC_An:
2899526e306bSschwarze 		return 12;
290049aff9f8Sschwarze 	case MDOC_Aq:
2901526e306bSschwarze 		return 12;
290249aff9f8Sschwarze 	case MDOC_Ar:
2903526e306bSschwarze 		return 12;
290449aff9f8Sschwarze 	case MDOC_Bo:
2905526e306bSschwarze 		return 12;
290649aff9f8Sschwarze 	case MDOC_Bq:
2907526e306bSschwarze 		return 12;
290849aff9f8Sschwarze 	case MDOC_Cd:
2909526e306bSschwarze 		return 12;
291049aff9f8Sschwarze 	case MDOC_Cm:
2911526e306bSschwarze 		return 10;
291249aff9f8Sschwarze 	case MDOC_Do:
2913526e306bSschwarze 		return 10;
291449aff9f8Sschwarze 	case MDOC_Dq:
2915526e306bSschwarze 		return 12;
291649aff9f8Sschwarze 	case MDOC_Dv:
2917526e306bSschwarze 		return 12;
291849aff9f8Sschwarze 	case MDOC_Eo:
2919526e306bSschwarze 		return 12;
292049aff9f8Sschwarze 	case MDOC_Em:
2921526e306bSschwarze 		return 10;
292249aff9f8Sschwarze 	case MDOC_Er:
2923526e306bSschwarze 		return 17;
292449aff9f8Sschwarze 	case MDOC_Ev:
2925526e306bSschwarze 		return 15;
292649aff9f8Sschwarze 	case MDOC_Fa:
2927526e306bSschwarze 		return 12;
292849aff9f8Sschwarze 	case MDOC_Fl:
2929526e306bSschwarze 		return 10;
293049aff9f8Sschwarze 	case MDOC_Fo:
2931526e306bSschwarze 		return 16;
293249aff9f8Sschwarze 	case MDOC_Fn:
2933526e306bSschwarze 		return 16;
293449aff9f8Sschwarze 	case MDOC_Ic:
2935526e306bSschwarze 		return 10;
293649aff9f8Sschwarze 	case MDOC_Li:
2937526e306bSschwarze 		return 16;
293849aff9f8Sschwarze 	case MDOC_Ms:
2939526e306bSschwarze 		return 6;
294049aff9f8Sschwarze 	case MDOC_Nm:
2941526e306bSschwarze 		return 10;
294249aff9f8Sschwarze 	case MDOC_No:
2943526e306bSschwarze 		return 12;
294449aff9f8Sschwarze 	case MDOC_Oo:
2945526e306bSschwarze 		return 10;
294649aff9f8Sschwarze 	case MDOC_Op:
2947526e306bSschwarze 		return 14;
294849aff9f8Sschwarze 	case MDOC_Pa:
2949526e306bSschwarze 		return 32;
295049aff9f8Sschwarze 	case MDOC_Pf:
2951526e306bSschwarze 		return 12;
295249aff9f8Sschwarze 	case MDOC_Po:
2953526e306bSschwarze 		return 12;
295449aff9f8Sschwarze 	case MDOC_Pq:
2955526e306bSschwarze 		return 12;
295649aff9f8Sschwarze 	case MDOC_Ql:
2957526e306bSschwarze 		return 16;
295849aff9f8Sschwarze 	case MDOC_Qo:
2959526e306bSschwarze 		return 12;
296049aff9f8Sschwarze 	case MDOC_So:
2961526e306bSschwarze 		return 12;
296249aff9f8Sschwarze 	case MDOC_Sq:
2963526e306bSschwarze 		return 12;
296449aff9f8Sschwarze 	case MDOC_Sy:
2965526e306bSschwarze 		return 6;
296649aff9f8Sschwarze 	case MDOC_Sx:
2967526e306bSschwarze 		return 16;
296849aff9f8Sschwarze 	case MDOC_Tn:
2969526e306bSschwarze 		return 10;
297049aff9f8Sschwarze 	case MDOC_Va:
2971526e306bSschwarze 		return 12;
297249aff9f8Sschwarze 	case MDOC_Vt:
2973526e306bSschwarze 		return 12;
297449aff9f8Sschwarze 	case MDOC_Xr:
2975526e306bSschwarze 		return 10;
297619a69263Sschwarze 	default:
297719a69263Sschwarze 		break;
297819a69263Sschwarze 	};
2979526e306bSschwarze 	return 0;
298019a69263Sschwarze }
2981