xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 1d0823ad)
1*1d0823adSschwarze /*	$OpenBSD: mdoc_validate.c,v 1.191 2015/02/06 07:12:34 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
42d266539Sschwarze  * Copyright (c) 2010-2015 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  *
11a6464425Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a6464425Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 
32a35fc07aSschwarze #include "mdoc.h"
336e03d529Sschwarze #include "mandoc.h"
344f4f7972Sschwarze #include "mandoc_aux.h"
35f73abda9Skristaps #include "libmdoc.h"
36f6854d5cSschwarze #include "libmandoc.h"
37f73abda9Skristaps 
38f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
39f73abda9Skristaps 
406093755cSschwarze #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
41f73abda9Skristaps #define	POST_ARGS struct mdoc *mdoc
42f73abda9Skristaps 
437c2be9f8Sschwarze enum	check_ineq {
447c2be9f8Sschwarze 	CHECK_LT,
457c2be9f8Sschwarze 	CHECK_GT,
467c2be9f8Sschwarze 	CHECK_EQ
477c2be9f8Sschwarze };
487c2be9f8Sschwarze 
4998b8f00aSschwarze typedef	void	(*v_pre)(PRE_ARGS);
5098b8f00aSschwarze typedef	void	(*v_post)(POST_ARGS);
51f73abda9Skristaps 
52f73abda9Skristaps struct	valids {
53d52d1586Sschwarze 	v_pre	 pre;
54753701eeSschwarze 	v_post	 post;
55f73abda9Skristaps };
56f73abda9Skristaps 
5720fa2881Sschwarze static	void	 check_text(struct mdoc *, int, int, char *);
5820fa2881Sschwarze static	void	 check_argv(struct mdoc *,
5931e23753Sschwarze 			struct mdoc_node *, struct mdoc_argv *);
6020fa2881Sschwarze static	void	 check_args(struct mdoc *, struct mdoc_node *);
61cd6c268fSschwarze static	int	 child_an(const struct mdoc_node *);
6219a69263Sschwarze static	enum mdoc_sec	a2sec(const char *);
6319a69263Sschwarze static	size_t		macro2len(enum mdoct);
6490d52a15Sschwarze static	void	 rewrite_macro2len(char **);
6567c719adSschwarze 
6698b8f00aSschwarze static	void	 post_an(POST_ARGS);
6798b8f00aSschwarze static	void	 post_at(POST_ARGS);
6898b8f00aSschwarze static	void	 post_bf(POST_ARGS);
6998b8f00aSschwarze static	void	 post_bk(POST_ARGS);
7098b8f00aSschwarze static	void	 post_bl(POST_ARGS);
7198b8f00aSschwarze static	void	 post_bl_block(POST_ARGS);
7298b8f00aSschwarze static	void	 post_bl_block_tag(POST_ARGS);
7398b8f00aSschwarze static	void	 post_bl_head(POST_ARGS);
7498b8f00aSschwarze static	void	 post_bx(POST_ARGS);
7598b8f00aSschwarze static	void	 post_d1(POST_ARGS);
7698b8f00aSschwarze static	void	 post_defaults(POST_ARGS);
7798b8f00aSschwarze static	void	 post_dd(POST_ARGS);
7898b8f00aSschwarze static	void	 post_dt(POST_ARGS);
7998b8f00aSschwarze static	void	 post_en(POST_ARGS);
8098b8f00aSschwarze static	void	 post_es(POST_ARGS);
8198b8f00aSschwarze static	void	 post_eoln(POST_ARGS);
8298b8f00aSschwarze static	void	 post_ex(POST_ARGS);
8398b8f00aSschwarze static	void	 post_fa(POST_ARGS);
8498b8f00aSschwarze static	void	 post_fn(POST_ARGS);
8598b8f00aSschwarze static	void	 post_fname(POST_ARGS);
8698b8f00aSschwarze static	void	 post_fo(POST_ARGS);
8798b8f00aSschwarze static	void	 post_hyph(POST_ARGS);
8898b8f00aSschwarze static	void	 post_ignpar(POST_ARGS);
8998b8f00aSschwarze static	void	 post_it(POST_ARGS);
9098b8f00aSschwarze static	void	 post_lb(POST_ARGS);
9198b8f00aSschwarze static	void	 post_literal(POST_ARGS);
9298b8f00aSschwarze static	void	 post_nd(POST_ARGS);
9398b8f00aSschwarze static	void	 post_nm(POST_ARGS);
9498b8f00aSschwarze static	void	 post_ns(POST_ARGS);
9598b8f00aSschwarze static	void	 post_os(POST_ARGS);
9698b8f00aSschwarze static	void	 post_par(POST_ARGS);
9798b8f00aSschwarze static	void	 post_root(POST_ARGS);
9898b8f00aSschwarze static	void	 post_rs(POST_ARGS);
9998b8f00aSschwarze static	void	 post_sh(POST_ARGS);
10098b8f00aSschwarze static	void	 post_sh_head(POST_ARGS);
10198b8f00aSschwarze static	void	 post_sh_name(POST_ARGS);
10298b8f00aSschwarze static	void	 post_sh_see_also(POST_ARGS);
10398b8f00aSschwarze static	void	 post_sh_authors(POST_ARGS);
10498b8f00aSschwarze static	void	 post_sm(POST_ARGS);
10598b8f00aSschwarze static	void	 post_st(POST_ARGS);
10698b8f00aSschwarze static	void	 post_vt(POST_ARGS);
10798b8f00aSschwarze 
10898b8f00aSschwarze static	void	 pre_an(PRE_ARGS);
10998b8f00aSschwarze static	void	 pre_bd(PRE_ARGS);
11098b8f00aSschwarze static	void	 pre_bl(PRE_ARGS);
11198b8f00aSschwarze static	void	 pre_dd(PRE_ARGS);
11298b8f00aSschwarze static	void	 pre_display(PRE_ARGS);
11398b8f00aSschwarze static	void	 pre_dt(PRE_ARGS);
11498b8f00aSschwarze static	void	 pre_literal(PRE_ARGS);
11598b8f00aSschwarze static	void	 pre_obsolete(PRE_ARGS);
11698b8f00aSschwarze static	void	 pre_os(PRE_ARGS);
11798b8f00aSschwarze static	void	 pre_par(PRE_ARGS);
11898b8f00aSschwarze static	void	 pre_std(PRE_ARGS);
119f73abda9Skristaps 
12019a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
121099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
122753701eeSschwarze 	{ pre_dd, post_dd },			/* Dd */
123753701eeSschwarze 	{ pre_dt, post_dt },			/* Dt */
124753701eeSschwarze 	{ pre_os, post_os },			/* Os */
125753701eeSschwarze 	{ NULL, post_sh },			/* Sh */
126753701eeSschwarze 	{ NULL, post_ignpar },			/* Ss */
127753701eeSschwarze 	{ pre_par, post_par },			/* Pp */
128753701eeSschwarze 	{ pre_display, post_d1 },		/* D1 */
129753701eeSschwarze 	{ pre_literal, post_literal },		/* Dl */
130753701eeSschwarze 	{ pre_bd, post_literal },		/* Bd */
131f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
132753701eeSschwarze 	{ pre_bl, post_bl },			/* Bl */
133f73abda9Skristaps 	{ NULL, NULL },				/* El */
134753701eeSschwarze 	{ pre_par, post_it },			/* It */
135e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
136753701eeSschwarze 	{ pre_an, post_an },			/* An */
137753701eeSschwarze 	{ NULL, post_defaults },		/* Ar */
138e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
139f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
140f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
1414039b21cSschwarze 	{ NULL, NULL },				/* Er */
142f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
143753701eeSschwarze 	{ pre_std, post_ex },			/* Ex */
1447e92c062Sschwarze 	{ NULL, post_fa },			/* Fa */
145afcd1f03Sschwarze 	{ NULL, NULL },				/* Fd */
146f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
1470c5064e3Sschwarze 	{ NULL, post_fn },			/* Fn */
148e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
149e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
150b6df9df3Sschwarze 	{ NULL, NULL },				/* In */
151753701eeSschwarze 	{ NULL, post_defaults },		/* Li */
152753701eeSschwarze 	{ NULL, post_nd },			/* Nd */
153753701eeSschwarze 	{ NULL, post_nm },			/* Nm */
154bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
155d52d1586Sschwarze 	{ pre_obsolete, NULL },			/* Ot */
156753701eeSschwarze 	{ NULL, post_defaults },		/* Pa */
157d52d1586Sschwarze 	{ pre_std, NULL },			/* Rv */
158753701eeSschwarze 	{ NULL, post_st },			/* St */
159f73abda9Skristaps 	{ NULL, NULL },				/* Va */
160753701eeSschwarze 	{ NULL, post_vt },			/* Vt */
161b6df9df3Sschwarze 	{ NULL, NULL },				/* Xr */
162*1d0823adSschwarze 	{ NULL, NULL },				/* %A */
163*1d0823adSschwarze 	{ NULL, post_hyph },			/* %B */ /* FIXME: can be used outside Rs/Re. */
164*1d0823adSschwarze 	{ NULL, NULL },				/* %D */
165*1d0823adSschwarze 	{ NULL, NULL },				/* %I */
166*1d0823adSschwarze 	{ NULL, NULL },				/* %J */
167*1d0823adSschwarze 	{ NULL, post_hyph },			/* %N */
168*1d0823adSschwarze 	{ NULL, post_hyph },			/* %O */
169*1d0823adSschwarze 	{ NULL, NULL },				/* %P */
170*1d0823adSschwarze 	{ NULL, post_hyph },			/* %R */
171*1d0823adSschwarze 	{ NULL, post_hyph },			/* %T */ /* FIXME: can be used outside Rs/Re. */
172*1d0823adSschwarze 	{ NULL, NULL },				/* %V */
173f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
174f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
175bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
176753701eeSschwarze 	{ NULL, post_at },			/* At */
177f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
178753701eeSschwarze 	{ NULL, post_bf },			/* Bf */
179f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
180bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
181f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
182753701eeSschwarze 	{ NULL, post_bx },			/* Bx */
18378bbbab4Sschwarze 	{ pre_obsolete, NULL },			/* Db */
184f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
185f73abda9Skristaps 	{ NULL, NULL },				/* Do */
186bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
187f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
188f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
189f73abda9Skristaps 	{ NULL, NULL },				/* Em */
190f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
191f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
192e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
1936f9818f6Sschwarze 	{ NULL, NULL },				/* No */
194753701eeSschwarze 	{ NULL, post_ns },			/* Ns */
195f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
196f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
197f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
19888fc1858Sschwarze 	{ NULL, NULL },				/* Pf */
199f73abda9Skristaps 	{ NULL, NULL },				/* Po */
200bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
201f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
202bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
203f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
204bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
205f73abda9Skristaps 	{ NULL, NULL },				/* Re */
206753701eeSschwarze 	{ NULL, post_rs },			/* Rs */
207f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
208f73abda9Skristaps 	{ NULL, NULL },				/* So */
209bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
21078bbbab4Sschwarze 	{ NULL, post_sm },			/* Sm */
211753701eeSschwarze 	{ NULL, post_hyph },			/* Sx */
212e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
213e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
214f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
215f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
216f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
217753701eeSschwarze 	{ NULL, post_fo },			/* Fo */
218f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
219f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
220f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
221753701eeSschwarze 	{ NULL, post_bk },			/* Bk */
222f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
223753701eeSschwarze 	{ NULL, post_eoln },			/* Bt */
224f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
225d52d1586Sschwarze 	{ pre_obsolete, NULL },			/* Fr */
226753701eeSschwarze 	{ NULL, post_eoln },			/* Ud */
227753701eeSschwarze 	{ NULL, post_lb },			/* Lb */
228753701eeSschwarze 	{ pre_par, post_par },			/* Lp */
229e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
230753701eeSschwarze 	{ NULL, post_defaults },		/* Mt */
231bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
232f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
233f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
234*1d0823adSschwarze 	{ NULL, NULL },				/* %C */
235753701eeSschwarze 	{ pre_obsolete, post_es },		/* Es */
236753701eeSschwarze 	{ pre_obsolete, post_en },		/* En */
237f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
238*1d0823adSschwarze 	{ NULL, NULL },				/* %Q */
239753701eeSschwarze 	{ NULL, post_par },			/* br */
240753701eeSschwarze 	{ NULL, post_par },			/* sp */
241*1d0823adSschwarze 	{ NULL, NULL },				/* %U */
2426093755cSschwarze 	{ NULL, NULL },				/* Ta */
2435281506aSschwarze 	{ NULL, NULL },				/* ll */
244f73abda9Skristaps };
245f73abda9Skristaps 
24620fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
24720fa2881Sschwarze 
24820fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
24920fa2881Sschwarze 	MDOC__A,
25020fa2881Sschwarze 	MDOC__T,
25120fa2881Sschwarze 	MDOC__B,
25220fa2881Sschwarze 	MDOC__I,
25320fa2881Sschwarze 	MDOC__J,
25420fa2881Sschwarze 	MDOC__R,
25520fa2881Sschwarze 	MDOC__N,
25620fa2881Sschwarze 	MDOC__V,
2570397c682Sschwarze 	MDOC__U,
25820fa2881Sschwarze 	MDOC__P,
25920fa2881Sschwarze 	MDOC__Q,
2604e32ec8fSschwarze 	MDOC__C,
26120fa2881Sschwarze 	MDOC__D,
2624e32ec8fSschwarze 	MDOC__O
26320fa2881Sschwarze };
26420fa2881Sschwarze 
26519a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
26619a69263Sschwarze 	NULL,
26719a69263Sschwarze 	"NAME",
26819a69263Sschwarze 	"LIBRARY",
26919a69263Sschwarze 	"SYNOPSIS",
27019a69263Sschwarze 	"DESCRIPTION",
27103ab2f23Sdlg 	"CONTEXT",
27219a69263Sschwarze 	"IMPLEMENTATION NOTES",
27319a69263Sschwarze 	"RETURN VALUES",
27419a69263Sschwarze 	"ENVIRONMENT",
27519a69263Sschwarze 	"FILES",
27619a69263Sschwarze 	"EXIT STATUS",
27719a69263Sschwarze 	"EXAMPLES",
27819a69263Sschwarze 	"DIAGNOSTICS",
27919a69263Sschwarze 	"COMPATIBILITY",
28019a69263Sschwarze 	"ERRORS",
28119a69263Sschwarze 	"SEE ALSO",
28219a69263Sschwarze 	"STANDARDS",
28319a69263Sschwarze 	"HISTORY",
28419a69263Sschwarze 	"AUTHORS",
28519a69263Sschwarze 	"CAVEATS",
28619a69263Sschwarze 	"BUGS",
28719a69263Sschwarze 	"SECURITY CONSIDERATIONS",
28819a69263Sschwarze 	NULL
28919a69263Sschwarze };
290f73abda9Skristaps 
29149aff9f8Sschwarze 
29298b8f00aSschwarze void
2936093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
294f73abda9Skristaps {
295d52d1586Sschwarze 	v_pre	 p;
296f73abda9Skristaps 
2972791bd1cSschwarze 	switch (n->type) {
29849aff9f8Sschwarze 	case MDOC_TEXT:
299d52d1586Sschwarze 		check_text(mdoc, n->line, n->pos, n->string);
3002791bd1cSschwarze 		/* FALLTHROUGH */
30149aff9f8Sschwarze 	case MDOC_TBL:
3022791bd1cSschwarze 		/* FALLTHROUGH */
30349aff9f8Sschwarze 	case MDOC_EQN:
3048d973ab1Sschwarze 		/* FALLTHROUGH */
30549aff9f8Sschwarze 	case MDOC_ROOT:
30698b8f00aSschwarze 		return;
3072791bd1cSschwarze 	default:
3082791bd1cSschwarze 		break;
309f73abda9Skristaps 	}
310f73abda9Skristaps 
31120fa2881Sschwarze 	check_args(mdoc, n);
312d52d1586Sschwarze 	p = mdoc_valids[n->tok].pre;
31398b8f00aSschwarze 	if (*p)
31498b8f00aSschwarze 		(*p)(mdoc, n);
315f73abda9Skristaps }
316f73abda9Skristaps 
31798b8f00aSschwarze void
318f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc)
319f73abda9Skristaps {
320753701eeSschwarze 	struct mdoc_node *n;
321753701eeSschwarze 	v_post p;
322f73abda9Skristaps 
323753701eeSschwarze 	n = mdoc->last;
324753701eeSschwarze 	if (n->flags & MDOC_VALID)
32598b8f00aSschwarze 		return;
326753701eeSschwarze 	n->flags |= MDOC_VALID;
327f73abda9Skristaps 
328753701eeSschwarze 	switch (n->type) {
32949aff9f8Sschwarze 	case MDOC_TEXT:
3302791bd1cSschwarze 		/* FALLTHROUGH */
33149aff9f8Sschwarze 	case MDOC_EQN:
3328d973ab1Sschwarze 		/* FALLTHROUGH */
33349aff9f8Sschwarze 	case MDOC_TBL:
33498b8f00aSschwarze 		break;
33549aff9f8Sschwarze 	case MDOC_ROOT:
33698b8f00aSschwarze 		post_root(mdoc);
33798b8f00aSschwarze 		break;
3382791bd1cSschwarze 	default:
3396f9818f6Sschwarze 
3406f9818f6Sschwarze 		/*
3416f9818f6Sschwarze 		 * Closing delimiters are not special at the
3426f9818f6Sschwarze 		 * beginning of a block, opening delimiters
3436f9818f6Sschwarze 		 * are not special at the end.
3446f9818f6Sschwarze 		 */
3456f9818f6Sschwarze 
3466f9818f6Sschwarze 		if (n->child != NULL)
3476f9818f6Sschwarze 			n->child->flags &= ~MDOC_DELIMC;
3486f9818f6Sschwarze 		if (n->last != NULL)
3496f9818f6Sschwarze 			n->last->flags &= ~MDOC_DELIMO;
3506f9818f6Sschwarze 
3516f9818f6Sschwarze 		/* Call the macro's postprocessor. */
3526f9818f6Sschwarze 
353753701eeSschwarze 		p = mdoc_valids[n->tok].post;
35498b8f00aSschwarze 		if (*p)
35598b8f00aSschwarze 			(*p)(mdoc);
35698b8f00aSschwarze 		break;
3572791bd1cSschwarze 	}
358f73abda9Skristaps }
359f73abda9Skristaps 
36098b8f00aSschwarze static void
3617ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n)
362f73abda9Skristaps {
363f73abda9Skristaps 	int		 i;
364f73abda9Skristaps 
365f73abda9Skristaps 	if (NULL == n->args)
36620fa2881Sschwarze 		return;
367f73abda9Skristaps 
368f73abda9Skristaps 	assert(n->args->argc);
369f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
3707ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
371f73abda9Skristaps }
372f73abda9Skristaps 
37320fa2881Sschwarze static void
3747ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
375f73abda9Skristaps {
376f73abda9Skristaps 	int		 i;
377f73abda9Skristaps 
378f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
3797ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
380f73abda9Skristaps }
381f73abda9Skristaps 
38220fa2881Sschwarze static void
3837ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
384f73abda9Skristaps {
38504e980cbSschwarze 	char		*cp;
386769ee804Sschwarze 
3877ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
3881cdbf331Sschwarze 		return;
3891cdbf331Sschwarze 
3901cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
391dd5b31c3Sschwarze 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
392dd5b31c3Sschwarze 		    ln, pos + (int)(p - cp), NULL);
393f73abda9Skristaps }
394f73abda9Skristaps 
39598b8f00aSschwarze static void
396f73abda9Skristaps pre_display(PRE_ARGS)
397f73abda9Skristaps {
398f73abda9Skristaps 	struct mdoc_node *node;
399f73abda9Skristaps 
400f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
40198b8f00aSschwarze 		return;
402f73abda9Skristaps 
403f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
404f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
405f73abda9Skristaps 			if (MDOC_Bd == node->tok)
406f73abda9Skristaps 				break;
40720fa2881Sschwarze 
40805c39368Sschwarze 	if (node)
409b723eac2Sschwarze 		mandoc_vmsg(MANDOCERR_BD_NEST,
410b723eac2Sschwarze 		    mdoc->parse, n->line, n->pos,
411b723eac2Sschwarze 		    "%s in Bd", mdoc_macronames[n->tok]);
412f73abda9Skristaps }
413f73abda9Skristaps 
41498b8f00aSschwarze static void
415f73abda9Skristaps pre_bl(PRE_ARGS)
416f73abda9Skristaps {
417769ee804Sschwarze 	struct mdoc_node *np;
418aa99c14fSschwarze 	struct mdoc_argv *argv, *wa;
4194a9f685fSschwarze 	int		  i;
420aa99c14fSschwarze 	enum mdocargt	  mdoclt;
4214a9f685fSschwarze 	enum mdoc_list	  lt;
422f73abda9Skristaps 
4236093755cSschwarze 	if (MDOC_BLOCK != n->type) {
424769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
425769ee804Sschwarze 			assert(n->pending);
426769ee804Sschwarze 			np = n->pending->parent;
427769ee804Sschwarze 		} else
428769ee804Sschwarze 			np = n->parent;
429769ee804Sschwarze 
430769ee804Sschwarze 		assert(np);
431769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
432769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
43398b8f00aSschwarze 		return;
4346e03d529Sschwarze 	}
435f73abda9Skristaps 
4366093755cSschwarze 	/*
4376093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
4386093755cSschwarze 	 * the first mentioned list type and warn about any remaining
4396093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
4406093755cSschwarze 	 */
441f73abda9Skristaps 
442dadc3a61Sschwarze 	wa = (n->args == NULL) ? NULL : n->args->argv;
443aa99c14fSschwarze 	mdoclt = MDOC_ARG_MAX;
4446093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
4454a9f685fSschwarze 		argv = n->args->argv + i;
4466093755cSschwarze 		lt = LIST__NONE;
4474a9f685fSschwarze 		switch (argv->arg) {
4486093755cSschwarze 		/* Set list types. */
44949aff9f8Sschwarze 		case MDOC_Bullet:
4506093755cSschwarze 			lt = LIST_bullet;
4516093755cSschwarze 			break;
45249aff9f8Sschwarze 		case MDOC_Dash:
4536093755cSschwarze 			lt = LIST_dash;
4546093755cSschwarze 			break;
45549aff9f8Sschwarze 		case MDOC_Enum:
4566093755cSschwarze 			lt = LIST_enum;
4576093755cSschwarze 			break;
45849aff9f8Sschwarze 		case MDOC_Hyphen:
4596093755cSschwarze 			lt = LIST_hyphen;
4606093755cSschwarze 			break;
46149aff9f8Sschwarze 		case MDOC_Item:
4626093755cSschwarze 			lt = LIST_item;
4636093755cSschwarze 			break;
46449aff9f8Sschwarze 		case MDOC_Tag:
4656093755cSschwarze 			lt = LIST_tag;
4666093755cSschwarze 			break;
46749aff9f8Sschwarze 		case MDOC_Diag:
4686093755cSschwarze 			lt = LIST_diag;
4696093755cSschwarze 			break;
47049aff9f8Sschwarze 		case MDOC_Hang:
4716093755cSschwarze 			lt = LIST_hang;
4726093755cSschwarze 			break;
47349aff9f8Sschwarze 		case MDOC_Ohang:
4746093755cSschwarze 			lt = LIST_ohang;
4756093755cSschwarze 			break;
47649aff9f8Sschwarze 		case MDOC_Inset:
4776093755cSschwarze 			lt = LIST_inset;
4786093755cSschwarze 			break;
47949aff9f8Sschwarze 		case MDOC_Column:
4806093755cSschwarze 			lt = LIST_column;
48164d728e4Sschwarze 			break;
4826093755cSschwarze 		/* Set list arguments. */
48349aff9f8Sschwarze 		case MDOC_Compact:
4844a9f685fSschwarze 			if (n->norm->Bl.comp)
4854a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
4864a9f685fSschwarze 				    mdoc->parse, argv->line,
4874a9f685fSschwarze 				    argv->pos, "Bl -compact");
4884a9f685fSschwarze 			n->norm->Bl.comp = 1;
48950e63e03Sschwarze 			break;
49049aff9f8Sschwarze 		case MDOC_Width:
491aa99c14fSschwarze 			wa = argv;
4924a9f685fSschwarze 			if (0 == argv->sz) {
4934a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
4944a9f685fSschwarze 				    mdoc->parse, argv->line,
4954a9f685fSschwarze 				    argv->pos, "Bl -width");
4964a9f685fSschwarze 				n->norm->Bl.width = "0n";
49722972b14Sschwarze 				break;
49822972b14Sschwarze 			}
4994a9f685fSschwarze 			if (NULL != n->norm->Bl.width)
5004a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
5014a9f685fSschwarze 				    mdoc->parse, argv->line,
5024a9f685fSschwarze 				    argv->pos, "Bl -width %s",
5034a9f685fSschwarze 				    argv->value[0]);
50490d52a15Sschwarze 			rewrite_macro2len(argv->value);
5054a9f685fSschwarze 			n->norm->Bl.width = argv->value[0];
50664d728e4Sschwarze 			break;
50749aff9f8Sschwarze 		case MDOC_Offset:
5084a9f685fSschwarze 			if (0 == argv->sz) {
5094a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
5104a9f685fSschwarze 				    mdoc->parse, argv->line,
5114a9f685fSschwarze 				    argv->pos, "Bl -offset");
51231e23753Sschwarze 				break;
51331e23753Sschwarze 			}
5144a9f685fSschwarze 			if (NULL != n->norm->Bl.offs)
5154a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
5164a9f685fSschwarze 				    mdoc->parse, argv->line,
5174a9f685fSschwarze 				    argv->pos, "Bl -offset %s",
5184a9f685fSschwarze 				    argv->value[0]);
51990d52a15Sschwarze 			rewrite_macro2len(argv->value);
5204a9f685fSschwarze 			n->norm->Bl.offs = argv->value[0];
521f73abda9Skristaps 			break;
522ddce0b0cSschwarze 		default:
523ddce0b0cSschwarze 			continue;
524f73abda9Skristaps 		}
525dc0d8bb2Sschwarze 		if (LIST__NONE == lt)
526dc0d8bb2Sschwarze 			continue;
527aa99c14fSschwarze 		mdoclt = argv->arg;
528f73abda9Skristaps 
5296093755cSschwarze 		/* Check: multiple list types. */
5306093755cSschwarze 
531dc0d8bb2Sschwarze 		if (LIST__NONE != n->norm->Bl.type) {
532bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_REP,
533dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
534bd594191Sschwarze 			    "Bl -%s", mdoc_argnames[argv->arg]);
535dc0d8bb2Sschwarze 			continue;
536769ee804Sschwarze 		}
5376093755cSschwarze 
5386093755cSschwarze 		/* The list type should come first. */
5396093755cSschwarze 
5408c62fbf5Sschwarze 		if (n->norm->Bl.width ||
5418c62fbf5Sschwarze 		    n->norm->Bl.offs ||
5428c62fbf5Sschwarze 		    n->norm->Bl.comp)
543bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
544bd594191Sschwarze 			    mdoc->parse, n->line, n->pos, "Bl -%s",
54566788495Sschwarze 			    mdoc_argnames[n->args->argv[0].arg]);
546dc0d8bb2Sschwarze 
547dc0d8bb2Sschwarze 		n->norm->Bl.type = lt;
548dc0d8bb2Sschwarze 		if (LIST_column == lt) {
549dc0d8bb2Sschwarze 			n->norm->Bl.ncols = argv->sz;
550dc0d8bb2Sschwarze 			n->norm->Bl.cols = (void *)argv->value;
551dc0d8bb2Sschwarze 		}
5526093755cSschwarze 	}
5536093755cSschwarze 
5546093755cSschwarze 	/* Allow lists to default to LIST_item. */
5556093755cSschwarze 
5568c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
557bd594191Sschwarze 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
558bd594191Sschwarze 		    n->line, n->pos, "Bl");
5598c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
5606e03d529Sschwarze 	}
561f73abda9Skristaps 
56264d728e4Sschwarze 	/*
56364d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
56464d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
5655eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
5665eced068Sschwarze 	 * no warning.
56764d728e4Sschwarze 	 */
56864d728e4Sschwarze 
5698c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
57049aff9f8Sschwarze 	case LIST_tag:
5715eced068Sschwarze 		if (NULL == n->norm->Bl.width)
572bd594191Sschwarze 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
573bd594191Sschwarze 			    n->line, n->pos, "Bl -tag");
574f73abda9Skristaps 		break;
57549aff9f8Sschwarze 	case LIST_column:
5766093755cSschwarze 		/* FALLTHROUGH */
57749aff9f8Sschwarze 	case LIST_diag:
5786093755cSschwarze 		/* FALLTHROUGH */
57949aff9f8Sschwarze 	case LIST_ohang:
5806093755cSschwarze 		/* FALLTHROUGH */
58149aff9f8Sschwarze 	case LIST_inset:
5826093755cSschwarze 		/* FALLTHROUGH */
58349aff9f8Sschwarze 	case LIST_item:
5848c62fbf5Sschwarze 		if (n->norm->Bl.width)
585aa99c14fSschwarze 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
586aa99c14fSschwarze 			    wa->line, wa->pos, "Bl -%s",
587aa99c14fSschwarze 			    mdoc_argnames[mdoclt]);
5886093755cSschwarze 		break;
58949aff9f8Sschwarze 	case LIST_bullet:
5905eced068Sschwarze 		/* FALLTHROUGH */
59149aff9f8Sschwarze 	case LIST_dash:
5925eced068Sschwarze 		/* FALLTHROUGH */
59349aff9f8Sschwarze 	case LIST_hyphen:
5945eced068Sschwarze 		if (NULL == n->norm->Bl.width)
5955eced068Sschwarze 			n->norm->Bl.width = "2n";
5965eced068Sschwarze 		break;
59749aff9f8Sschwarze 	case LIST_enum:
5985eced068Sschwarze 		if (NULL == n->norm->Bl.width)
5995eced068Sschwarze 			n->norm->Bl.width = "3n";
6005eced068Sschwarze 		break;
60164d728e4Sschwarze 	default:
602f73abda9Skristaps 		break;
60364d728e4Sschwarze 	}
60498b8f00aSschwarze 	pre_par(mdoc, n);
605f73abda9Skristaps }
606f73abda9Skristaps 
60798b8f00aSschwarze static void
608f73abda9Skristaps pre_bd(PRE_ARGS)
609f73abda9Skristaps {
610769ee804Sschwarze 	struct mdoc_node *np;
6114a9f685fSschwarze 	struct mdoc_argv *argv;
6124a9f685fSschwarze 	int		  i;
6134a9f685fSschwarze 	enum mdoc_disp	  dt;
614f73abda9Skristaps 
615d52d1586Sschwarze 	pre_literal(mdoc, n);
616d52d1586Sschwarze 
61731e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
618769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
619769ee804Sschwarze 			assert(n->pending);
620769ee804Sschwarze 			np = n->pending->parent;
621769ee804Sschwarze 		} else
622769ee804Sschwarze 			np = n->parent;
623769ee804Sschwarze 
624769ee804Sschwarze 		assert(np);
625769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
626769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
62798b8f00aSschwarze 		return;
6286e03d529Sschwarze 	}
629f73abda9Skristaps 
63031e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
6314a9f685fSschwarze 		argv = n->args->argv + i;
63231e23753Sschwarze 		dt = DISP__NONE;
63331e23753Sschwarze 
6344a9f685fSschwarze 		switch (argv->arg) {
63549aff9f8Sschwarze 		case MDOC_Centred:
6362065e47aSschwarze 			dt = DISP_centered;
63731e23753Sschwarze 			break;
63849aff9f8Sschwarze 		case MDOC_Ragged:
63931e23753Sschwarze 			dt = DISP_ragged;
64031e23753Sschwarze 			break;
64149aff9f8Sschwarze 		case MDOC_Unfilled:
64231e23753Sschwarze 			dt = DISP_unfilled;
64331e23753Sschwarze 			break;
64449aff9f8Sschwarze 		case MDOC_Filled:
64531e23753Sschwarze 			dt = DISP_filled;
64631e23753Sschwarze 			break;
64749aff9f8Sschwarze 		case MDOC_Literal:
64831e23753Sschwarze 			dt = DISP_literal;
649f73abda9Skristaps 			break;
65049aff9f8Sschwarze 		case MDOC_File:
651bd594191Sschwarze 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
652bd594191Sschwarze 			    n->line, n->pos, NULL);
6531164a325Sschwarze 			break;
65449aff9f8Sschwarze 		case MDOC_Offset:
6554a9f685fSschwarze 			if (0 == argv->sz) {
6564a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
6574a9f685fSschwarze 				    mdoc->parse, argv->line,
6584a9f685fSschwarze 				    argv->pos, "Bd -offset");
659f73abda9Skristaps 				break;
660f73abda9Skristaps 			}
6614a9f685fSschwarze 			if (NULL != n->norm->Bd.offs)
6624a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
6634a9f685fSschwarze 				    mdoc->parse, argv->line,
6644a9f685fSschwarze 				    argv->pos, "Bd -offset %s",
6654a9f685fSschwarze 				    argv->value[0]);
66690d52a15Sschwarze 			rewrite_macro2len(argv->value);
6674a9f685fSschwarze 			n->norm->Bd.offs = argv->value[0];
66831e23753Sschwarze 			break;
66949aff9f8Sschwarze 		case MDOC_Compact:
6704a9f685fSschwarze 			if (n->norm->Bd.comp)
6714a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
6724a9f685fSschwarze 				    mdoc->parse, argv->line,
6734a9f685fSschwarze 				    argv->pos, "Bd -compact");
6744a9f685fSschwarze 			n->norm->Bd.comp = 1;
67531e23753Sschwarze 			break;
67631e23753Sschwarze 		default:
67731e23753Sschwarze 			abort();
67831e23753Sschwarze 			/* NOTREACHED */
67931e23753Sschwarze 		}
680dc0d8bb2Sschwarze 		if (DISP__NONE == dt)
681dc0d8bb2Sschwarze 			continue;
68231e23753Sschwarze 
683dc0d8bb2Sschwarze 		if (DISP__NONE == n->norm->Bd.type)
6848c62fbf5Sschwarze 			n->norm->Bd.type = dt;
685dc0d8bb2Sschwarze 		else
686bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BD_REP,
687dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
688bd594191Sschwarze 			    "Bd -%s", mdoc_argnames[argv->arg]);
68931e23753Sschwarze 	}
69031e23753Sschwarze 
6918c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
692bd594191Sschwarze 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
693bd594191Sschwarze 		    n->line, n->pos, "Bd");
6948c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
69531e23753Sschwarze 	}
69698b8f00aSschwarze 	pre_par(mdoc, n);
697f73abda9Skristaps }
698f73abda9Skristaps 
69998b8f00aSschwarze static void
700f73abda9Skristaps pre_an(PRE_ARGS)
701f73abda9Skristaps {
702aa99c14fSschwarze 	struct mdoc_argv *argv;
703aa99c14fSschwarze 	size_t	 i;
704f73abda9Skristaps 
705aa99c14fSschwarze 	if (n->args == NULL)
70698b8f00aSschwarze 		return;
707769ee804Sschwarze 
708aa99c14fSschwarze 	for (i = 1; i < n->args->argc; i++) {
709aa99c14fSschwarze 		argv = n->args->argv + i;
710aa99c14fSschwarze 		mandoc_vmsg(MANDOCERR_AN_REP,
711aa99c14fSschwarze 		    mdoc->parse, argv->line, argv->pos,
712aa99c14fSschwarze 		    "An -%s", mdoc_argnames[argv->arg]);
713aa99c14fSschwarze 	}
7147c2be9f8Sschwarze 
715aa99c14fSschwarze 	argv = n->args->argv;
716aa99c14fSschwarze 	if (argv->arg == MDOC_Split)
7178c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
718aa99c14fSschwarze 	else if (argv->arg == MDOC_Nosplit)
7198c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
720769ee804Sschwarze 	else
721769ee804Sschwarze 		abort();
722f73abda9Skristaps }
723f73abda9Skristaps 
72498b8f00aSschwarze static void
72520fa2881Sschwarze pre_std(PRE_ARGS)
726f73abda9Skristaps {
727f73abda9Skristaps 
72820fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
72920fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
73098b8f00aSschwarze 			return;
731f73abda9Skristaps 
73266788495Sschwarze 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
73366788495Sschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
7346093755cSschwarze }
7356093755cSschwarze 
73698b8f00aSschwarze static void
737551cd4a8Sschwarze pre_obsolete(PRE_ARGS)
738551cd4a8Sschwarze {
739551cd4a8Sschwarze 
740551cd4a8Sschwarze 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
741551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
742551cd4a8Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
743551cd4a8Sschwarze }
744551cd4a8Sschwarze 
74598b8f00aSschwarze static void
746f73abda9Skristaps pre_dt(PRE_ARGS)
747f73abda9Skristaps {
748f73abda9Skristaps 
7493fdead0cSschwarze 	if (mdoc->meta.title != NULL)
75051fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
75151fcab2fSschwarze 		    n->line, n->pos, "Dt");
7523fdead0cSschwarze 	else if (mdoc->meta.os != NULL)
7533fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
7543fdead0cSschwarze 		    n->line, n->pos, "Dt after Os");
755f73abda9Skristaps }
756f73abda9Skristaps 
75798b8f00aSschwarze static void
758f73abda9Skristaps pre_os(PRE_ARGS)
759f73abda9Skristaps {
760f73abda9Skristaps 
7613fdead0cSschwarze 	if (mdoc->meta.os != NULL)
76251fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
76351fcab2fSschwarze 		    n->line, n->pos, "Os");
7643fdead0cSschwarze 	else if (mdoc->flags & MDOC_PBODY)
7653fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
7663fdead0cSschwarze 		    n->line, n->pos, "Os");
767f73abda9Skristaps }
768f73abda9Skristaps 
76998b8f00aSschwarze static void
770f73abda9Skristaps pre_dd(PRE_ARGS)
771f73abda9Skristaps {
772f73abda9Skristaps 
7733fdead0cSschwarze 	if (mdoc->meta.date != NULL)
77451fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
77551fcab2fSschwarze 		    n->line, n->pos, "Dd");
7763fdead0cSschwarze 	else if (mdoc->flags & MDOC_PBODY)
7773fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
7783fdead0cSschwarze 		    n->line, n->pos, "Dd");
7793fdead0cSschwarze 	else if (mdoc->meta.title != NULL)
7803fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
7813fdead0cSschwarze 		    n->line, n->pos, "Dd after Dt");
7823fdead0cSschwarze 	else if (mdoc->meta.os != NULL)
7833fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
7843fdead0cSschwarze 		    n->line, n->pos, "Dd after Os");
785f73abda9Skristaps }
786f73abda9Skristaps 
78798b8f00aSschwarze static void
788f73abda9Skristaps post_bf(POST_ARGS)
789f73abda9Skristaps {
790ecb10c32Sschwarze 	struct mdoc_node *np, *nch;
791ddce0b0cSschwarze 	enum mdocargt	  arg;
792f73abda9Skristaps 
793769ee804Sschwarze 	/*
794769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
795769ee804Sschwarze 	 * element, which contains the goods.
796769ee804Sschwarze 	 */
797769ee804Sschwarze 
798769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
799769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
800769ee804Sschwarze 			assert(mdoc->last->pending);
801769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
802769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
803769ee804Sschwarze 			np = mdoc->last->parent->head;
804769ee804Sschwarze 		} else
805769ee804Sschwarze 			np = mdoc->last->head;
806769ee804Sschwarze 
807769ee804Sschwarze 		assert(np);
808769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
809769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
81098b8f00aSschwarze 		return;
8116e03d529Sschwarze 	}
812f73abda9Skristaps 
813769ee804Sschwarze 	np = mdoc->last;
814769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
815769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
81650d41253Sschwarze 
817ecb10c32Sschwarze 	/* Check the number of arguments. */
818f73abda9Skristaps 
819ecb10c32Sschwarze 	nch = np->child;
820ecb10c32Sschwarze 	if (NULL == np->parent->args) {
821ecb10c32Sschwarze 		if (NULL == nch) {
822bd594191Sschwarze 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
823bd594191Sschwarze 			    np->line, np->pos, "Bf");
82498b8f00aSschwarze 			return;
82520fa2881Sschwarze 		}
826ecb10c32Sschwarze 		nch = nch->next;
827ecb10c32Sschwarze 	}
828ecb10c32Sschwarze 	if (NULL != nch)
829ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
830ecb10c32Sschwarze 		    nch->line, nch->pos, "Bf ... %s", nch->string);
831769ee804Sschwarze 
832769ee804Sschwarze 	/* Extract argument into data. */
833769ee804Sschwarze 
834769ee804Sschwarze 	if (np->parent->args) {
835769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
836769ee804Sschwarze 		if (MDOC_Emphasis == arg)
8378c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
838769ee804Sschwarze 		else if (MDOC_Literal == arg)
8398c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
840769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
8418c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
842769ee804Sschwarze 		else
843769ee804Sschwarze 			abort();
84498b8f00aSschwarze 		return;
845769ee804Sschwarze 	}
846769ee804Sschwarze 
847769ee804Sschwarze 	/* Extract parameter into data. */
848769ee804Sschwarze 
849769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
8508c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
851769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
8528c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
853769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
8548c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
85520fa2881Sschwarze 	else
856ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
857ecb10c32Sschwarze 		    np->child->line, np->child->pos,
858ecb10c32Sschwarze 		    "Bf %s", np->child->string);
859f73abda9Skristaps }
860f73abda9Skristaps 
86198b8f00aSschwarze static void
86271719887Sschwarze post_lb(POST_ARGS)
86371719887Sschwarze {
86477e000ffSschwarze 	struct mdoc_node	*n;
86577e000ffSschwarze 	char			*libname;
86671719887Sschwarze 
86777e000ffSschwarze 	n = mdoc->last->child;
86877e000ffSschwarze 	assert(MDOC_TEXT == n->type);
869aaad0dcfSschwarze 	mandoc_asprintf(&libname, "library \\(lq%s\\(rq", n->string);
87077e000ffSschwarze 	free(n->string);
87177e000ffSschwarze 	n->string = libname;
87220fa2881Sschwarze }
87371719887Sschwarze 
87498b8f00aSschwarze static void
875b31af00dSschwarze post_eoln(POST_ARGS)
876b31af00dSschwarze {
877ecb10c32Sschwarze 	const struct mdoc_node *n;
878b31af00dSschwarze 
879ecb10c32Sschwarze 	n = mdoc->last;
880ecb10c32Sschwarze 	if (n->child)
881ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
882ecb10c32Sschwarze 		    mdoc->parse, n->line, n->pos,
883ecb10c32Sschwarze 		    "%s %s", mdoc_macronames[n->tok],
884ecb10c32Sschwarze 		    n->child->string);
885b31af00dSschwarze }
886b31af00dSschwarze 
88798b8f00aSschwarze static void
8880c5064e3Sschwarze post_fname(POST_ARGS)
8890c5064e3Sschwarze {
8900c5064e3Sschwarze 	const struct mdoc_node	*n;
8910ff14c71Sschwarze 	const char		*cp;
8920c5064e3Sschwarze 	size_t			 pos;
8930c5064e3Sschwarze 
8940c5064e3Sschwarze 	n = mdoc->last->child;
8950c5064e3Sschwarze 	pos = strcspn(n->string, "()");
8960ff14c71Sschwarze 	cp = n->string + pos;
8970ff14c71Sschwarze 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
8980c5064e3Sschwarze 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
8990c5064e3Sschwarze 		    n->line, n->pos + pos, n->string);
9000c5064e3Sschwarze }
9010c5064e3Sschwarze 
90298b8f00aSschwarze static void
9030c5064e3Sschwarze post_fn(POST_ARGS)
9040c5064e3Sschwarze {
9050c5064e3Sschwarze 
9060c5064e3Sschwarze 	post_fname(mdoc);
9070c5064e3Sschwarze 	post_fa(mdoc);
9080c5064e3Sschwarze }
9090c5064e3Sschwarze 
91098b8f00aSschwarze static void
911753701eeSschwarze post_fo(POST_ARGS)
912753701eeSschwarze {
913afcd1f03Sschwarze 	const struct mdoc_node	*n;
914753701eeSschwarze 
915afcd1f03Sschwarze 	n = mdoc->last;
916afcd1f03Sschwarze 
917afcd1f03Sschwarze 	if (n->type != MDOC_HEAD)
918afcd1f03Sschwarze 		return;
919afcd1f03Sschwarze 
920afcd1f03Sschwarze 	if (n->child == NULL) {
921afcd1f03Sschwarze 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
922afcd1f03Sschwarze 		    n->line, n->pos, "Fo");
923afcd1f03Sschwarze 		return;
924afcd1f03Sschwarze 	}
925afcd1f03Sschwarze 	if (n->child != n->last) {
926afcd1f03Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
927afcd1f03Sschwarze 		    n->child->next->line, n->child->next->pos,
928afcd1f03Sschwarze 		    "Fo ... %s", n->child->next->string);
929afcd1f03Sschwarze 		while (n->child != n->last)
930afcd1f03Sschwarze 			mdoc_node_delete(mdoc, n->last);
931afcd1f03Sschwarze 	}
932afcd1f03Sschwarze 
9330c5064e3Sschwarze 	post_fname(mdoc);
934753701eeSschwarze }
935753701eeSschwarze 
93698b8f00aSschwarze static void
9377e92c062Sschwarze post_fa(POST_ARGS)
9387e92c062Sschwarze {
9397e92c062Sschwarze 	const struct mdoc_node *n;
9407e92c062Sschwarze 	const char *cp;
9417e92c062Sschwarze 
9427e92c062Sschwarze 	for (n = mdoc->last->child; n != NULL; n = n->next) {
9437e92c062Sschwarze 		for (cp = n->string; *cp != '\0'; cp++) {
9447e92c062Sschwarze 			/* Ignore callbacks and alterations. */
9457e92c062Sschwarze 			if (*cp == '(' || *cp == '{')
9467e92c062Sschwarze 				break;
9477e92c062Sschwarze 			if (*cp != ',')
9487e92c062Sschwarze 				continue;
9497e92c062Sschwarze 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
9507e92c062Sschwarze 			    n->line, n->pos + (cp - n->string),
9517e92c062Sschwarze 			    n->string);
9527e92c062Sschwarze 			break;
9537e92c062Sschwarze 		}
9547e92c062Sschwarze 	}
9557e92c062Sschwarze }
9567e92c062Sschwarze 
95798b8f00aSschwarze static void
9588521b0bcSschwarze post_vt(POST_ARGS)
9598521b0bcSschwarze {
9608521b0bcSschwarze 	const struct mdoc_node *n;
9618521b0bcSschwarze 
9628521b0bcSschwarze 	/*
9638521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
9648521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
965e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
966e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
9678521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
9688521b0bcSschwarze 	 */
9698521b0bcSschwarze 
9708521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
97198b8f00aSschwarze 		return;
9728521b0bcSschwarze 
9738521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
974bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
975dd25b57cSschwarze 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
976dd25b57cSschwarze 			    n->line, n->pos, mdoc_macronames[n->tok]);
9778521b0bcSschwarze }
9788521b0bcSschwarze 
97998b8f00aSschwarze static void
980f73abda9Skristaps post_nm(POST_ARGS)
981f73abda9Skristaps {
9822d266539Sschwarze 	struct mdoc_node	*n;
9832d266539Sschwarze 
9842d266539Sschwarze 	n = mdoc->last;
9852d266539Sschwarze 
9862d266539Sschwarze 	if (n->last != NULL &&
9872d266539Sschwarze 	    (n->last->tok == MDOC_Pp ||
9882d266539Sschwarze 	     n->last->tok == MDOC_Lp))
9892d266539Sschwarze 		mdoc_node_relink(mdoc, n->last);
99020fa2881Sschwarze 
991160ac481Sschwarze 	if (NULL != mdoc->meta.name)
99298b8f00aSschwarze 		return;
99320fa2881Sschwarze 
9942d266539Sschwarze 	mdoc_deroff(&mdoc->meta.name, n);
99520fa2881Sschwarze 
996e214f641Sschwarze 	if (NULL == mdoc->meta.name)
997bd594191Sschwarze 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
9982d266539Sschwarze 		    n->line, n->pos, "Nm");
99920fa2881Sschwarze }
100020fa2881Sschwarze 
100198b8f00aSschwarze static void
1002753701eeSschwarze post_nd(POST_ARGS)
1003753701eeSschwarze {
10041570daf1Sschwarze 	struct mdoc_node	*n;
1005753701eeSschwarze 
10061570daf1Sschwarze 	n = mdoc->last;
10071570daf1Sschwarze 
10081570daf1Sschwarze 	if (n->type != MDOC_BODY)
10091570daf1Sschwarze 		return;
10101570daf1Sschwarze 
10111570daf1Sschwarze 	if (n->child == NULL)
10121570daf1Sschwarze 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
10131570daf1Sschwarze 		    n->line, n->pos, "Nd");
10141570daf1Sschwarze 
101598b8f00aSschwarze 	post_hyph(mdoc);
1016753701eeSschwarze }
1017753701eeSschwarze 
101898b8f00aSschwarze static void
1019753701eeSschwarze post_d1(POST_ARGS)
1020753701eeSschwarze {
1021b7530f2fSschwarze 	struct mdoc_node	*n;
1022753701eeSschwarze 
1023b7530f2fSschwarze 	n = mdoc->last;
1024b7530f2fSschwarze 
1025b7530f2fSschwarze 	if (n->type != MDOC_BODY)
1026b7530f2fSschwarze 		return;
1027b7530f2fSschwarze 
1028b7530f2fSschwarze 	if (n->child == NULL)
1029*1d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1030b7530f2fSschwarze 		    n->line, n->pos, "D1");
1031b7530f2fSschwarze 
103298b8f00aSschwarze 	post_hyph(mdoc);
1033753701eeSschwarze }
1034753701eeSschwarze 
103598b8f00aSschwarze static void
103620fa2881Sschwarze post_literal(POST_ARGS)
103720fa2881Sschwarze {
1038b7530f2fSschwarze 	struct mdoc_node	*n;
103920fa2881Sschwarze 
1040b7530f2fSschwarze 	n = mdoc->last;
1041753701eeSschwarze 
1042b7530f2fSschwarze 	if (n->type != MDOC_BODY)
1043b7530f2fSschwarze 		return;
104420fa2881Sschwarze 
1045b7530f2fSschwarze 	if (n->child == NULL)
1046*1d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1047b7530f2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1048b7530f2fSschwarze 
1049b7530f2fSschwarze 	if (n->tok == MDOC_Bd &&
1050b7530f2fSschwarze 	    n->norm->Bd.type != DISP_literal &&
1051b7530f2fSschwarze 	    n->norm->Bd.type != DISP_unfilled)
1052b7530f2fSschwarze 		return;
1053b7530f2fSschwarze 
105420fa2881Sschwarze 	mdoc->flags &= ~MDOC_LITERAL;
105520fa2881Sschwarze }
105620fa2881Sschwarze 
105798b8f00aSschwarze static void
105820fa2881Sschwarze post_defaults(POST_ARGS)
105920fa2881Sschwarze {
106020fa2881Sschwarze 	struct mdoc_node *nn;
106120fa2881Sschwarze 
106220fa2881Sschwarze 	/*
106320fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
106420fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
106520fa2881Sschwarze 	 * gets an empty string.
106620fa2881Sschwarze 	 */
1067f73abda9Skristaps 
1068f73abda9Skristaps 	if (mdoc->last->child)
106998b8f00aSschwarze 		return;
107020fa2881Sschwarze 
107120fa2881Sschwarze 	nn = mdoc->last;
107220fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
107320fa2881Sschwarze 
107420fa2881Sschwarze 	switch (nn->tok) {
107549aff9f8Sschwarze 	case MDOC_Ar:
107698b8f00aSschwarze 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
107798b8f00aSschwarze 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
107820fa2881Sschwarze 		break;
107949aff9f8Sschwarze 	case MDOC_Pa:
108020fa2881Sschwarze 		/* FALLTHROUGH */
108149aff9f8Sschwarze 	case MDOC_Mt:
108298b8f00aSschwarze 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
108320fa2881Sschwarze 		break;
108420fa2881Sschwarze 	default:
108520fa2881Sschwarze 		abort();
108620fa2881Sschwarze 		/* NOTREACHED */
1087f73abda9Skristaps 	}
108820fa2881Sschwarze 	mdoc->last = nn;
108920fa2881Sschwarze }
1090f73abda9Skristaps 
109198b8f00aSschwarze static void
1092f73abda9Skristaps post_at(POST_ARGS)
1093f73abda9Skristaps {
10940b2f1307Sschwarze 	struct mdoc_node	*n;
10950b2f1307Sschwarze 	const char		*std_att;
10960b2f1307Sschwarze 	char			*att;
109720fa2881Sschwarze 
1098753701eeSschwarze 	n = mdoc->last;
1099753701eeSschwarze 	if (n->child == NULL) {
1100753701eeSschwarze 		mdoc->next = MDOC_NEXT_CHILD;
110198b8f00aSschwarze 		mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1102753701eeSschwarze 		mdoc->last = n;
110398b8f00aSschwarze 		return;
1104753701eeSschwarze 	}
1105753701eeSschwarze 
110620fa2881Sschwarze 	/*
110720fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
110820fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
110920fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
111020fa2881Sschwarze 	 */
1111f73abda9Skristaps 
1112753701eeSschwarze 	n = n->child;
11130b2f1307Sschwarze 	assert(MDOC_TEXT == n->type);
11140b2f1307Sschwarze 	if (NULL == (std_att = mdoc_a2att(n->string))) {
1115bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1116bd594191Sschwarze 		    n->line, n->pos, "At %s", n->string);
11170b2f1307Sschwarze 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
11180b2f1307Sschwarze 	} else
11190b2f1307Sschwarze 		att = mandoc_strdup(std_att);
1120f73abda9Skristaps 
11210b2f1307Sschwarze 	free(n->string);
11220b2f1307Sschwarze 	n->string = att;
112320fa2881Sschwarze }
1124f73abda9Skristaps 
112598b8f00aSschwarze static void
1126f73abda9Skristaps post_an(POST_ARGS)
1127f73abda9Skristaps {
11283798fb25Sschwarze 	struct mdoc_node *np, *nch;
1129f73abda9Skristaps 
1130769ee804Sschwarze 	np = mdoc->last;
1131cba50636Sschwarze 	nch = np->child;
1132cba50636Sschwarze 	if (np->norm->An.auth == AUTH__NONE) {
1133cba50636Sschwarze 		if (nch == NULL)
1134cba50636Sschwarze 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1135cba50636Sschwarze 			    np->line, np->pos, "An");
1136cba50636Sschwarze 	} else if (nch != NULL)
11373798fb25Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
11383798fb25Sschwarze 		    nch->line, nch->pos, "An ... %s", nch->string);
1139f73abda9Skristaps }
1140f73abda9Skristaps 
114198b8f00aSschwarze static void
1142551cd4a8Sschwarze post_en(POST_ARGS)
1143551cd4a8Sschwarze {
1144551cd4a8Sschwarze 
1145551cd4a8Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
1146551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1147551cd4a8Sschwarze }
1148551cd4a8Sschwarze 
114998b8f00aSschwarze static void
1150551cd4a8Sschwarze post_es(POST_ARGS)
1151551cd4a8Sschwarze {
1152551cd4a8Sschwarze 
1153551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1154551cd4a8Sschwarze }
1155551cd4a8Sschwarze 
115698b8f00aSschwarze static void
1157f73abda9Skristaps post_it(POST_ARGS)
1158f73abda9Skristaps {
115919a69263Sschwarze 	int		  i, cols;
11606093755cSschwarze 	enum mdoc_list	  lt;
11619530682eSschwarze 	struct mdoc_node *nbl, *nit, *nch;
1162f73abda9Skristaps 
11639530682eSschwarze 	nit = mdoc->last;
1164d26e35c2Sschwarze 	if (nit->type != MDOC_BLOCK)
116598b8f00aSschwarze 		return;
1166f73abda9Skristaps 
11679530682eSschwarze 	nbl = nit->parent->parent;
11689530682eSschwarze 	lt = nbl->norm->Bl.type;
11696093755cSschwarze 
11706093755cSschwarze 	switch (lt) {
117149aff9f8Sschwarze 	case LIST_tag:
11729530682eSschwarze 		/* FALLTHROUGH */
117349aff9f8Sschwarze 	case LIST_hang:
1174f73abda9Skristaps 		/* FALLTHROUGH */
117549aff9f8Sschwarze 	case LIST_ohang:
1176f73abda9Skristaps 		/* FALLTHROUGH */
117749aff9f8Sschwarze 	case LIST_inset:
1178f73abda9Skristaps 		/* FALLTHROUGH */
117949aff9f8Sschwarze 	case LIST_diag:
1180d26e35c2Sschwarze 		if (nit->head->child == NULL)
1181bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
11829530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
1183bd594191Sschwarze 			    "Bl -%s It",
11849530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1185f73abda9Skristaps 		break;
118649aff9f8Sschwarze 	case LIST_bullet:
1187f73abda9Skristaps 		/* FALLTHROUGH */
118849aff9f8Sschwarze 	case LIST_dash:
1189f73abda9Skristaps 		/* FALLTHROUGH */
119049aff9f8Sschwarze 	case LIST_enum:
1191f73abda9Skristaps 		/* FALLTHROUGH */
119249aff9f8Sschwarze 	case LIST_hyphen:
1193d26e35c2Sschwarze 		if (nit->body == NULL || nit->body->child == NULL)
1194bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
119566788495Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1196bd594191Sschwarze 			    "Bl -%s It",
119766788495Sschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1198f73abda9Skristaps 		/* FALLTHROUGH */
119949aff9f8Sschwarze 	case LIST_item:
1200d26e35c2Sschwarze 		if (nit->head->child != NULL)
1201ecb10c32Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1202ecb10c32Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1203ecb10c32Sschwarze 			    "It %s", nit->head->child->string);
1204f73abda9Skristaps 		break;
120549aff9f8Sschwarze 	case LIST_column:
12069530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
12076093755cSschwarze 
1208d26e35c2Sschwarze 		assert(nit->head->child == NULL);
12096093755cSschwarze 
12109530682eSschwarze 		for (i = 0, nch = nit->child; nch; nch = nch->next)
1211d26e35c2Sschwarze 			if (nch->type == MDOC_BODY)
1212f73abda9Skristaps 				i++;
121353292e81Sschwarze 
1214e14c4c11Sschwarze 		if (i < cols || i > cols + 1)
1215e14c4c11Sschwarze 			mandoc_vmsg(MANDOCERR_ARGCOUNT,
1216e14c4c11Sschwarze 			    mdoc->parse, nit->line, nit->pos,
12176e03d529Sschwarze 			    "columns == %d (have %d)", cols, i);
1218e14c4c11Sschwarze 		break;
1219f73abda9Skristaps 	default:
122066788495Sschwarze 		abort();
1221f73abda9Skristaps 	}
1222f73abda9Skristaps }
1223f73abda9Skristaps 
122498b8f00aSschwarze static void
122520fa2881Sschwarze post_bl_block(POST_ARGS)
122620fa2881Sschwarze {
1227bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
122820fa2881Sschwarze 
122920fa2881Sschwarze 	/*
123020fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
123120fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
123220fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
123320fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
123420fa2881Sschwarze 	 * rewritten into real lengths).
123520fa2881Sschwarze 	 */
123620fa2881Sschwarze 
123720fa2881Sschwarze 	n = mdoc->last;
123820fa2881Sschwarze 
12398c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
12408c62fbf5Sschwarze 	    NULL == n->norm->Bl.width) {
124198b8f00aSschwarze 		post_bl_block_tag(mdoc);
1242bb99f0faSschwarze 		assert(n->norm->Bl.width);
1243bb99f0faSschwarze 	}
1244bb99f0faSschwarze 
1245bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1246bb99f0faSschwarze 		if (NULL == ni->body)
1247bb99f0faSschwarze 			continue;
1248bb99f0faSschwarze 		nc = ni->body->last;
1249bb99f0faSschwarze 		while (NULL != nc) {
1250bb99f0faSschwarze 			switch (nc->tok) {
125149aff9f8Sschwarze 			case MDOC_Pp:
1252bb99f0faSschwarze 				/* FALLTHROUGH */
125349aff9f8Sschwarze 			case MDOC_Lp:
1254bb99f0faSschwarze 				/* FALLTHROUGH */
125549aff9f8Sschwarze 			case MDOC_br:
1256bb99f0faSschwarze 				break;
1257bb99f0faSschwarze 			default:
1258bb99f0faSschwarze 				nc = NULL;
1259bb99f0faSschwarze 				continue;
1260bb99f0faSschwarze 			}
1261bb99f0faSschwarze 			if (NULL == ni->next) {
126220369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
126320369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
126420369664Sschwarze 				    mdoc_macronames[nc->tok]);
126598b8f00aSschwarze 				mdoc_node_relink(mdoc, nc);
1266bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1267bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
126820369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
126920369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
127020369664Sschwarze 				    "%s before It",
127120369664Sschwarze 				    mdoc_macronames[nc->tok]);
1272bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1273bb99f0faSschwarze 			} else
1274bb99f0faSschwarze 				break;
1275bb99f0faSschwarze 			nc = ni->body->last;
1276bb99f0faSschwarze 		}
1277bb99f0faSschwarze 	}
127820fa2881Sschwarze }
127920fa2881Sschwarze 
128090d52a15Sschwarze /*
128190d52a15Sschwarze  * If the argument of -offset or -width is a macro,
128290d52a15Sschwarze  * replace it with the associated default width.
128390d52a15Sschwarze  */
128490d52a15Sschwarze void
128590d52a15Sschwarze rewrite_macro2len(char **arg)
128620fa2881Sschwarze {
128720fa2881Sschwarze 	size_t		  width;
128820fa2881Sschwarze 	enum mdoct	  tok;
128920fa2881Sschwarze 
129090d52a15Sschwarze 	if (*arg == NULL)
129190d52a15Sschwarze 		return;
129290d52a15Sschwarze 	else if ( ! strcmp(*arg, "Ds"))
129320fa2881Sschwarze 		width = 6;
129490d52a15Sschwarze 	else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
129590d52a15Sschwarze 		return;
1296dc0d8bb2Sschwarze 	else
1297dc0d8bb2Sschwarze 		width = macro2len(tok);
129820fa2881Sschwarze 
129990d52a15Sschwarze 	free(*arg);
130090d52a15Sschwarze 	mandoc_asprintf(arg, "%zun", width);
130120fa2881Sschwarze }
130220fa2881Sschwarze 
130398b8f00aSschwarze static void
130420fa2881Sschwarze post_bl_block_tag(POST_ARGS)
130520fa2881Sschwarze {
130620fa2881Sschwarze 	struct mdoc_node *n, *nn;
130720fa2881Sschwarze 	size_t		  sz, ssz;
130820fa2881Sschwarze 	int		  i;
130947813146Sschwarze 	char		  buf[24];
131020fa2881Sschwarze 
131120fa2881Sschwarze 	/*
131220fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
131320fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
131420fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
131590d52a15Sschwarze 	 * rewrite_macro2len() for converting the -width string.
131620fa2881Sschwarze 	 */
131720fa2881Sschwarze 
131820fa2881Sschwarze 	sz = 10;
131920fa2881Sschwarze 	n = mdoc->last;
132020fa2881Sschwarze 
132120fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
132220fa2881Sschwarze 		if (MDOC_It != nn->tok)
132320fa2881Sschwarze 			continue;
132420fa2881Sschwarze 
132520fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
132620fa2881Sschwarze 		nn = nn->head->child;
132720fa2881Sschwarze 
132820fa2881Sschwarze 		if (nn == NULL)
132920fa2881Sschwarze 			break;
133020fa2881Sschwarze 
133120fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
133220fa2881Sschwarze 			sz = strlen(nn->string) + 1;
133320fa2881Sschwarze 			break;
133420fa2881Sschwarze 		}
133520fa2881Sschwarze 
133619a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
133720fa2881Sschwarze 			sz = ssz;
133820fa2881Sschwarze 
133920fa2881Sschwarze 		break;
134020fa2881Sschwarze 	}
134120fa2881Sschwarze 
134220fa2881Sschwarze 	/* Defaults to ten ens. */
134320fa2881Sschwarze 
134447813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
134520fa2881Sschwarze 
134620fa2881Sschwarze 	/*
134720fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
134820fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
134920fa2881Sschwarze 	 */
135020fa2881Sschwarze 
135120fa2881Sschwarze 	assert(n->args);
135220fa2881Sschwarze 	i = (int)(n->args->argc)++;
135320fa2881Sschwarze 
13548286bf36Sschwarze 	n->args->argv = mandoc_reallocarray(n->args->argv,
13558286bf36Sschwarze 	    n->args->argc, sizeof(struct mdoc_argv));
135620fa2881Sschwarze 
135720fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
135820fa2881Sschwarze 	n->args->argv[i].line = n->line;
135920fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
136020fa2881Sschwarze 	n->args->argv[i].sz = 1;
136120fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
136220fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
136320fa2881Sschwarze 
136420fa2881Sschwarze 	/* Set our width! */
13658c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
136620fa2881Sschwarze }
136720fa2881Sschwarze 
136898b8f00aSschwarze static void
1369395185ccSschwarze post_bl_head(POST_ARGS)
1370395185ccSschwarze {
13712588c917Sschwarze 	struct mdoc_node *nbl, *nh, *nch, *nnext;
1372f5174743Sschwarze 	struct mdoc_argv *argv;
137320fa2881Sschwarze 	int		  i, j;
1374395185ccSschwarze 
13752588c917Sschwarze 	nh = mdoc->last;
13762588c917Sschwarze 
13772588c917Sschwarze 	if (nh->norm->Bl.type != LIST_column) {
13782588c917Sschwarze 		if ((nch = nh->child) == NULL)
13792588c917Sschwarze 			return;
13802588c917Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
13812588c917Sschwarze 		    nch->line, nch->pos, "Bl ... %s", nch->string);
13822588c917Sschwarze 		while (nch != NULL) {
13832588c917Sschwarze 			mdoc_node_delete(mdoc, nch);
13842588c917Sschwarze 			nch = nh->child;
13852588c917Sschwarze 		}
138698b8f00aSschwarze 		return;
138798b8f00aSschwarze 	}
1388395185ccSschwarze 
138920fa2881Sschwarze 	/*
1390f5174743Sschwarze 	 * Append old-style lists, where the column width specifiers
139120fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
139220fa2881Sschwarze 	 * lists where they're argument values following -column.
139320fa2881Sschwarze 	 */
139420fa2881Sschwarze 
13952588c917Sschwarze 	if (nh->child == NULL)
139698b8f00aSschwarze 		return;
139720fa2881Sschwarze 
13982588c917Sschwarze 	nbl = nh->parent;
13992588c917Sschwarze 	for (j = 0; j < (int)nbl->args->argc; j++)
14002588c917Sschwarze 		if (nbl->args->argv[j].arg == MDOC_Column)
140120fa2881Sschwarze 			break;
140220fa2881Sschwarze 
14032588c917Sschwarze 	assert(j < (int)nbl->args->argc);
140420fa2881Sschwarze 
140520fa2881Sschwarze 	/*
1406a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
140720fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
140820fa2881Sschwarze 	 * column field.  Then, delete the head children.
140920fa2881Sschwarze 	 */
141020fa2881Sschwarze 
14112588c917Sschwarze 	argv = nbl->args->argv + j;
1412f5174743Sschwarze 	i = argv->sz;
14132588c917Sschwarze 	argv->sz += nh->nchild;
1414f5174743Sschwarze 	argv->value = mandoc_reallocarray(argv->value,
1415f5174743Sschwarze 	    argv->sz, sizeof(char *));
141620fa2881Sschwarze 
14172588c917Sschwarze 	nh->norm->Bl.ncols = argv->sz;
14182588c917Sschwarze 	nh->norm->Bl.cols = (void *)argv->value;
141920fa2881Sschwarze 
14202588c917Sschwarze 	for (nch = nh->child; nch != NULL; nch = nnext) {
14212588c917Sschwarze 		argv->value[i++] = nch->string;
14222588c917Sschwarze 		nch->string = NULL;
14232588c917Sschwarze 		nnext = nch->next;
14242588c917Sschwarze 		mdoc_node_delete(NULL, nch);
1425395185ccSschwarze 	}
14262588c917Sschwarze 	nh->nchild = 0;
14272588c917Sschwarze 	nh->child = NULL;
1428b16e7ddfSschwarze }
1429b16e7ddfSschwarze 
143098b8f00aSschwarze static void
1431f73abda9Skristaps post_bl(POST_ARGS)
1432f73abda9Skristaps {
14332a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
14342a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
14352a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1436f73abda9Skristaps 
14372a427d60Sschwarze 	nbody = mdoc->last;
14382a427d60Sschwarze 	switch (nbody->type) {
143949aff9f8Sschwarze 	case MDOC_BLOCK:
144098b8f00aSschwarze 		post_bl_block(mdoc);
144198b8f00aSschwarze 		return;
144249aff9f8Sschwarze 	case MDOC_HEAD:
144398b8f00aSschwarze 		post_bl_head(mdoc);
144498b8f00aSschwarze 		return;
144549aff9f8Sschwarze 	case MDOC_BODY:
1446f6127a73Sschwarze 		break;
14472a427d60Sschwarze 	default:
144898b8f00aSschwarze 		return;
1449f6127a73Sschwarze 	}
1450f6127a73Sschwarze 
14512a427d60Sschwarze 	nchild = nbody->child;
1452b7530f2fSschwarze 	if (nchild == NULL) {
1453*1d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1454b7530f2fSschwarze 		    nbody->line, nbody->pos, "Bl");
1455b7530f2fSschwarze 		return;
1456b7530f2fSschwarze 	}
1457b7530f2fSschwarze 	while (nchild != NULL) {
1458204684a7Sschwarze 		if (nchild->tok == MDOC_It ||
1459204684a7Sschwarze 		    (nchild->tok == MDOC_Sm &&
1460204684a7Sschwarze 		     nchild->next != NULL &&
1461204684a7Sschwarze 		     nchild->next->tok == MDOC_It)) {
14622a427d60Sschwarze 			nchild = nchild->next;
14632a427d60Sschwarze 			continue;
14642a427d60Sschwarze 		}
14652a427d60Sschwarze 
1466dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1467dd25b57cSschwarze 		    nchild->line, nchild->pos,
1468dd25b57cSschwarze 		    mdoc_macronames[nchild->tok]);
14692a427d60Sschwarze 
14702a427d60Sschwarze 		/*
14712a427d60Sschwarze 		 * Move the node out of the Bl block.
14722a427d60Sschwarze 		 * First, collect all required node pointers.
14732a427d60Sschwarze 		 */
14742a427d60Sschwarze 
14752a427d60Sschwarze 		nblock  = nbody->parent;
14762a427d60Sschwarze 		nprev   = nblock->prev;
14772a427d60Sschwarze 		nparent = nblock->parent;
14782a427d60Sschwarze 		nnext   = nchild->next;
14792a427d60Sschwarze 
14802a427d60Sschwarze 		/*
14812a427d60Sschwarze 		 * Unlink this child.
14822a427d60Sschwarze 		 */
14832a427d60Sschwarze 
14842a427d60Sschwarze 		assert(NULL == nchild->prev);
14852a427d60Sschwarze 		if (0 == --nbody->nchild) {
14862a427d60Sschwarze 			nbody->child = NULL;
14872a427d60Sschwarze 			nbody->last  = NULL;
14882a427d60Sschwarze 			assert(NULL == nnext);
14892a427d60Sschwarze 		} else {
14902a427d60Sschwarze 			nbody->child = nnext;
14912a427d60Sschwarze 			nnext->prev = NULL;
14922a427d60Sschwarze 		}
14932a427d60Sschwarze 
14942a427d60Sschwarze 		/*
14952a427d60Sschwarze 		 * Relink this child.
14962a427d60Sschwarze 		 */
14972a427d60Sschwarze 
14982a427d60Sschwarze 		nchild->parent = nparent;
14992a427d60Sschwarze 		nchild->prev   = nprev;
15002a427d60Sschwarze 		nchild->next   = nblock;
15012a427d60Sschwarze 
15022a427d60Sschwarze 		nblock->prev = nchild;
15032a427d60Sschwarze 		nparent->nchild++;
15042a427d60Sschwarze 		if (NULL == nprev)
15052a427d60Sschwarze 			nparent->child = nchild;
15062a427d60Sschwarze 		else
15072a427d60Sschwarze 			nprev->next = nchild;
15082a427d60Sschwarze 
15092a427d60Sschwarze 		nchild = nnext;
1510f73abda9Skristaps 	}
1511f73abda9Skristaps }
1512f73abda9Skristaps 
151398b8f00aSschwarze static void
1514753701eeSschwarze post_bk(POST_ARGS)
1515753701eeSschwarze {
15162588c917Sschwarze 	struct mdoc_node	*n;
1517753701eeSschwarze 
15182588c917Sschwarze 	n = mdoc->last;
15192588c917Sschwarze 
15202588c917Sschwarze 	if (n->type == MDOC_BLOCK && n->body->child == NULL) {
1521*1d0823adSschwarze 		mandoc_msg(MANDOCERR_BLK_EMPTY,
15222588c917Sschwarze 		    mdoc->parse, n->line, n->pos, "Bk");
15232588c917Sschwarze 		mdoc_node_delete(mdoc, n);
15242588c917Sschwarze 	}
1525753701eeSschwarze }
1526753701eeSschwarze 
152798b8f00aSschwarze static void
152878bbbab4Sschwarze post_sm(struct mdoc *mdoc)
1529f73abda9Skristaps {
1530dc0d8bb2Sschwarze 	struct mdoc_node	*nch;
1531f73abda9Skristaps 
1532dc0d8bb2Sschwarze 	nch = mdoc->last->child;
1533dc0d8bb2Sschwarze 
153478bbbab4Sschwarze 	if (nch == NULL) {
1535f9e7bf99Sschwarze 		mdoc->flags ^= MDOC_SMOFF;
153698b8f00aSschwarze 		return;
1537bb648afaSschwarze 	}
1538f9e7bf99Sschwarze 
153978bbbab4Sschwarze 	assert(nch->type == MDOC_TEXT);
154020fa2881Sschwarze 
154178bbbab4Sschwarze 	if ( ! strcmp(nch->string, "on")) {
1542ec2beb53Sschwarze 		mdoc->flags &= ~MDOC_SMOFF;
154398b8f00aSschwarze 		return;
1544ec2beb53Sschwarze 	}
154578bbbab4Sschwarze 	if ( ! strcmp(nch->string, "off")) {
1546ec2beb53Sschwarze 		mdoc->flags |= MDOC_SMOFF;
154798b8f00aSschwarze 		return;
1548ec2beb53Sschwarze 	}
154920fa2881Sschwarze 
1550dc0d8bb2Sschwarze 	mandoc_vmsg(MANDOCERR_SM_BAD,
1551dc0d8bb2Sschwarze 	    mdoc->parse, nch->line, nch->pos,
155278bbbab4Sschwarze 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
155398b8f00aSschwarze 	mdoc_node_relink(mdoc, nch);
155498b8f00aSschwarze 	return;
155520fa2881Sschwarze }
1556f73abda9Skristaps 
155798b8f00aSschwarze static void
1558f73abda9Skristaps post_root(POST_ARGS)
1559f73abda9Skristaps {
156020fa2881Sschwarze 	struct mdoc_node *n;
1561f73abda9Skristaps 
1562ac1f49d0Sschwarze 	/* Add missing prologue data. */
156320fa2881Sschwarze 
1564ac1f49d0Sschwarze 	if (mdoc->meta.date == NULL)
1565ac1f49d0Sschwarze 		mdoc->meta.date = mdoc->quick ?
1566ac1f49d0Sschwarze 		    mandoc_strdup("") :
1567ac1f49d0Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
15683fdead0cSschwarze 
15693fdead0cSschwarze 	if (mdoc->meta.title == NULL) {
15703fdead0cSschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
15713fdead0cSschwarze 		    mdoc->parse, 0, 0, "EOF");
15723fdead0cSschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
15733fdead0cSschwarze 	}
15743fdead0cSschwarze 
1575ac1f49d0Sschwarze 	if (mdoc->meta.vol == NULL)
1576ac1f49d0Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
15773fdead0cSschwarze 
15783fdead0cSschwarze 	if (mdoc->meta.os == NULL) {
15793fdead0cSschwarze 		mandoc_msg(MANDOCERR_OS_MISSING,
15803fdead0cSschwarze 		    mdoc->parse, 0, 0, NULL);
15813fdead0cSschwarze 		mdoc->meta.os = mandoc_strdup("");
1582f73abda9Skristaps 	}
1583f73abda9Skristaps 
158420fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
158520fa2881Sschwarze 
1586e20417bdSschwarze 	n = mdoc->first->child;
1587e20417bdSschwarze 	while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1588e20417bdSschwarze 		n = n->next;
1589e20417bdSschwarze 
1590e20417bdSschwarze 	if (n == NULL)
1591e20417bdSschwarze 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1592e20417bdSschwarze 	else if (n->tok != MDOC_Sh)
159351fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1594e20417bdSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
159520fa2881Sschwarze }
1596f73abda9Skristaps 
159798b8f00aSschwarze static void
1598f73abda9Skristaps post_st(POST_ARGS)
1599f73abda9Skristaps {
1600dc0d8bb2Sschwarze 	struct mdoc_node	 *n, *nch;
160120fa2881Sschwarze 	const char		 *p;
1602f73abda9Skristaps 
1603dc0d8bb2Sschwarze 	n = mdoc->last;
1604dc0d8bb2Sschwarze 	nch = n->child;
1605dc0d8bb2Sschwarze 
1606dc0d8bb2Sschwarze 	assert(MDOC_TEXT == nch->type);
160720fa2881Sschwarze 
1608dc0d8bb2Sschwarze 	if (NULL == (p = mdoc_a2st(nch->string))) {
1609bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1610bd594191Sschwarze 		    nch->line, nch->pos, "St %s", nch->string);
1611dc0d8bb2Sschwarze 		mdoc_node_delete(mdoc, n);
161220fa2881Sschwarze 	} else {
1613dc0d8bb2Sschwarze 		free(nch->string);
1614dc0d8bb2Sschwarze 		nch->string = mandoc_strdup(p);
1615f73abda9Skristaps 	}
161620fa2881Sschwarze }
1617f73abda9Skristaps 
161898b8f00aSschwarze static void
1619011fe33bSschwarze post_rs(POST_ARGS)
1620011fe33bSschwarze {
16216e96429aSschwarze 	struct mdoc_node *np, *nch, *next, *prev;
162220fa2881Sschwarze 	int		  i, j;
1623011fe33bSschwarze 
16246e96429aSschwarze 	np = mdoc->last;
16256e96429aSschwarze 
16266e96429aSschwarze 	if (np->type != MDOC_BODY)
162798b8f00aSschwarze 		return;
16286e96429aSschwarze 
16296e96429aSschwarze 	if (np->child == NULL) {
16306e96429aSschwarze 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
16316e96429aSschwarze 		    np->line, np->pos, "Rs");
163298b8f00aSschwarze 		return;
1633bb648afaSschwarze 	}
1634011fe33bSschwarze 
163520fa2881Sschwarze 	/*
163620fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
163720fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
1638b538baa5Sschwarze 	 * and correctly order it.  This is an insertion sort.
163920fa2881Sschwarze 	 */
164020fa2881Sschwarze 
164120fa2881Sschwarze 	next = NULL;
16426e96429aSschwarze 	for (nch = np->child->next; nch != NULL; nch = next) {
16436e96429aSschwarze 		/* Determine order number of this child. */
164420fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
16456e96429aSschwarze 			if (rsord[i] == nch->tok)
164620fa2881Sschwarze 				break;
164720fa2881Sschwarze 
1648b538baa5Sschwarze 		if (i == RSORD_MAX) {
1649b538baa5Sschwarze 			mandoc_msg(MANDOCERR_RS_BAD,
16506e96429aSschwarze 			    mdoc->parse, nch->line, nch->pos,
16516e96429aSschwarze 			    mdoc_macronames[nch->tok]);
1652b538baa5Sschwarze 			i = -1;
16536e96429aSschwarze 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
16546e96429aSschwarze 			np->norm->Rs.quote_T++;
1655b538baa5Sschwarze 
165620fa2881Sschwarze 		/*
16576e96429aSschwarze 		 * Remove this child from the chain.  This somewhat
165820fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
165920fa2881Sschwarze 		 * just re-ordering, there's no need for the
166020fa2881Sschwarze 		 * full unlink process.
166120fa2881Sschwarze 		 */
166220fa2881Sschwarze 
16636e96429aSschwarze 		if ((next = nch->next) != NULL)
16646e96429aSschwarze 			next->prev = nch->prev;
166520fa2881Sschwarze 
16666e96429aSschwarze 		if ((prev = nch->prev) != NULL)
16676e96429aSschwarze 			prev->next = nch->next;
166820fa2881Sschwarze 
16696e96429aSschwarze 		nch->prev = nch->next = NULL;
167020fa2881Sschwarze 
167120fa2881Sschwarze 		/*
167220fa2881Sschwarze 		 * Scan back until we reach a node that's
16736e96429aSschwarze 		 * to be ordered before this child.
167420fa2881Sschwarze 		 */
167520fa2881Sschwarze 
167620fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
167720fa2881Sschwarze 			/* Determine order of `prev'. */
167820fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
167920fa2881Sschwarze 				if (rsord[j] == prev->tok)
168020fa2881Sschwarze 					break;
1681b538baa5Sschwarze 			if (j == RSORD_MAX)
1682b538baa5Sschwarze 				j = -1;
168320fa2881Sschwarze 
168420fa2881Sschwarze 			if (j <= i)
168520fa2881Sschwarze 				break;
168620fa2881Sschwarze 		}
168720fa2881Sschwarze 
168820fa2881Sschwarze 		/*
16896e96429aSschwarze 		 * Set this child back into its correct place
16906e96429aSschwarze 		 * in front of the `prev' node.
169120fa2881Sschwarze 		 */
169220fa2881Sschwarze 
16936e96429aSschwarze 		nch->prev = prev;
169420fa2881Sschwarze 
16956e96429aSschwarze 		if (prev == NULL) {
16966e96429aSschwarze 			np->child->prev = nch;
16976e96429aSschwarze 			nch->next = np->child;
16986e96429aSschwarze 			np->child = nch;
169920fa2881Sschwarze 		} else {
17006e96429aSschwarze 			if (prev->next)
17016e96429aSschwarze 				prev->next->prev = nch;
17026e96429aSschwarze 			nch->next = prev->next;
17036e96429aSschwarze 			prev->next = nch;
170420fa2881Sschwarze 		}
1705011fe33bSschwarze 	}
1706011fe33bSschwarze }
1707011fe33bSschwarze 
17084039b21cSschwarze /*
17094039b21cSschwarze  * For some arguments of some macros,
17104039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
17114039b21cSschwarze  */
171298b8f00aSschwarze static void
17134039b21cSschwarze post_hyph(POST_ARGS)
17144039b21cSschwarze {
1715b7530f2fSschwarze 	struct mdoc_node	*nch;
17164039b21cSschwarze 	char			*cp;
17174039b21cSschwarze 
1718b7530f2fSschwarze 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1719b7530f2fSschwarze 		if (nch->type != MDOC_TEXT)
17204039b21cSschwarze 			continue;
17214039b21cSschwarze 		cp = nch->string;
1722b7530f2fSschwarze 		if (*cp == '\0')
17234039b21cSschwarze 			continue;
1724b7530f2fSschwarze 		while (*(++cp) != '\0')
1725b7530f2fSschwarze 			if (*cp == '-' &&
17264039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
17274039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
17284039b21cSschwarze 				*cp = ASCII_HYPH;
17294039b21cSschwarze 	}
17304039b21cSschwarze }
17314039b21cSschwarze 
173298b8f00aSschwarze static void
1733af216717Sschwarze post_ns(POST_ARGS)
1734af216717Sschwarze {
1735af216717Sschwarze 
1736af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
173728153913Sschwarze 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
173828153913Sschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
1739af216717Sschwarze }
1740af216717Sschwarze 
174198b8f00aSschwarze static void
1742f73abda9Skristaps post_sh(POST_ARGS)
1743f73abda9Skristaps {
1744f73abda9Skristaps 
1745753701eeSschwarze 	post_ignpar(mdoc);
1746753701eeSschwarze 
1747cd6c268fSschwarze 	switch (mdoc->last->type) {
1748cd6c268fSschwarze 	case MDOC_HEAD:
174998b8f00aSschwarze 		post_sh_head(mdoc);
175098b8f00aSschwarze 		break;
1751cd6c268fSschwarze 	case MDOC_BODY:
1752cd6c268fSschwarze 		switch (mdoc->lastsec)  {
1753cd6c268fSschwarze 		case SEC_NAME:
175498b8f00aSschwarze 			post_sh_name(mdoc);
175598b8f00aSschwarze 			break;
17567c384856Sschwarze 		case SEC_SEE_ALSO:
175798b8f00aSschwarze 			post_sh_see_also(mdoc);
175898b8f00aSschwarze 			break;
1759cd6c268fSschwarze 		case SEC_AUTHORS:
176098b8f00aSschwarze 			post_sh_authors(mdoc);
176198b8f00aSschwarze 			break;
1762cd6c268fSschwarze 		default:
1763cd6c268fSschwarze 			break;
1764cd6c268fSschwarze 		}
1765cd6c268fSschwarze 		break;
1766cd6c268fSschwarze 	default:
1767cd6c268fSschwarze 		break;
1768cd6c268fSschwarze 	}
1769f73abda9Skristaps }
1770f73abda9Skristaps 
177198b8f00aSschwarze static void
1772cd6c268fSschwarze post_sh_name(POST_ARGS)
1773f73abda9Skristaps {
1774f73abda9Skristaps 	struct mdoc_node *n;
1775f73abda9Skristaps 
1776f73abda9Skristaps 	/*
1777f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1778f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1779f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1780f73abda9Skristaps 	 */
1781f73abda9Skristaps 
178220fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
178351fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
178451fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, "empty");
178598b8f00aSschwarze 		return;
178620fa2881Sschwarze 	}
1787f73abda9Skristaps 
1788f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1789f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1790f73abda9Skristaps 			continue;
1791f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1792f73abda9Skristaps 			continue;
179351fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
179451fcab2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1795f73abda9Skristaps 	}
1796f73abda9Skristaps 
179749d529b5Sschwarze 	assert(n);
17984602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
179998b8f00aSschwarze 		return;
1800f73abda9Skristaps 
180151fcab2fSschwarze 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
180251fcab2fSschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
180320fa2881Sschwarze }
1804f73abda9Skristaps 
180598b8f00aSschwarze static void
18067c384856Sschwarze post_sh_see_also(POST_ARGS)
18077c384856Sschwarze {
18087c384856Sschwarze 	const struct mdoc_node	*n;
18097c384856Sschwarze 	const char 		*name, *sec;
18107c384856Sschwarze 	const char		*lastname, *lastsec, *lastpunct;
18117c384856Sschwarze 	int			 cmp;
18127c384856Sschwarze 
18137c384856Sschwarze 	n = mdoc->last->child;
18147c384856Sschwarze 	lastname = lastsec = lastpunct = NULL;
18157c384856Sschwarze 	while (n != NULL) {
18167c384856Sschwarze 		if (n->tok != MDOC_Xr || n->nchild < 2)
18177c384856Sschwarze 			break;
18187c384856Sschwarze 
18197c384856Sschwarze 		/* Process one .Xr node. */
18207c384856Sschwarze 
18217c384856Sschwarze 		name = n->child->string;
18227c384856Sschwarze 		sec = n->child->next->string;
18237c384856Sschwarze 		if (lastsec != NULL) {
18247c384856Sschwarze 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
18257c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
18267c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
18277c384856Sschwarze 				    "%s before %s(%s)", lastpunct,
18287c384856Sschwarze 				    name, sec);
18297c384856Sschwarze 			cmp = strcmp(lastsec, sec);
18307c384856Sschwarze 			if (cmp > 0)
18317c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
18327c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
18337c384856Sschwarze 				    "%s(%s) after %s(%s)", name,
18347c384856Sschwarze 				    sec, lastname, lastsec);
18357c384856Sschwarze 			else if (cmp == 0 &&
18367c384856Sschwarze 			    strcasecmp(lastname, name) > 0)
18377c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
18387c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
18397c384856Sschwarze 				    "%s after %s", name, lastname);
18407c384856Sschwarze 		}
18417c384856Sschwarze 		lastname = name;
18427c384856Sschwarze 		lastsec = sec;
18437c384856Sschwarze 
18447c384856Sschwarze 		/* Process the following node. */
18457c384856Sschwarze 
18467c384856Sschwarze 		n = n->next;
18477c384856Sschwarze 		if (n == NULL)
18487c384856Sschwarze 			break;
18497c384856Sschwarze 		if (n->tok == MDOC_Xr) {
18507c384856Sschwarze 			lastpunct = "none";
18517c384856Sschwarze 			continue;
18527c384856Sschwarze 		}
18537c384856Sschwarze 		if (n->type != MDOC_TEXT)
18547c384856Sschwarze 			break;
18557c384856Sschwarze 		for (name = n->string; *name != '\0'; name++)
18567c384856Sschwarze 			if (isalpha((const unsigned char)*name))
185798b8f00aSschwarze 				return;
18587c384856Sschwarze 		lastpunct = n->string;
18597c384856Sschwarze 		if (n->next == NULL)
18607c384856Sschwarze 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
18617c384856Sschwarze 			    n->line, n->pos, "%s after %s(%s)",
18627c384856Sschwarze 			    lastpunct, lastname, lastsec);
18637c384856Sschwarze 		n = n->next;
18647c384856Sschwarze 	}
18657c384856Sschwarze }
18667c384856Sschwarze 
18677c384856Sschwarze static int
1868cd6c268fSschwarze child_an(const struct mdoc_node *n)
1869cd6c268fSschwarze {
1870cd6c268fSschwarze 
1871cd6c268fSschwarze 	for (n = n->child; n != NULL; n = n->next)
1872cd6c268fSschwarze 		if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1873cd6c268fSschwarze 			return(1);
1874cd6c268fSschwarze 	return(0);
1875cd6c268fSschwarze }
1876cd6c268fSschwarze 
187798b8f00aSschwarze static void
1878cd6c268fSschwarze post_sh_authors(POST_ARGS)
1879cd6c268fSschwarze {
1880cd6c268fSschwarze 
1881cd6c268fSschwarze 	if ( ! child_an(mdoc->last))
1882cd6c268fSschwarze 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1883cd6c268fSschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
1884cd6c268fSschwarze }
1885cd6c268fSschwarze 
188698b8f00aSschwarze static void
1887f73abda9Skristaps post_sh_head(POST_ARGS)
1888f73abda9Skristaps {
1889a2cff342Sschwarze 	struct mdoc_node *n;
189051fcab2fSschwarze 	const char	*goodsec;
189146133849Sschwarze 	char		*secname;
1892f73abda9Skristaps 	enum mdoc_sec	 sec;
1893f73abda9Skristaps 
1894f73abda9Skristaps 	/*
1895f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
189620fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
189720fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
189820fa2881Sschwarze 	 * manual sections.
1899f73abda9Skristaps 	 */
1900f73abda9Skristaps 
190183af2bccSschwarze 	secname = NULL;
190204e980cbSschwarze 	sec = SEC_CUSTOM;
190346133849Sschwarze 	mdoc_deroff(&secname, mdoc->last);
190446133849Sschwarze 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1905f73abda9Skristaps 
190620fa2881Sschwarze 	/* The NAME should be first. */
1907f73abda9Skristaps 
1908fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1909bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1910bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
1911bd594191Sschwarze 		    "Sh %s", secname);
191220fa2881Sschwarze 
191320fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
191420fa2881Sschwarze 
191522881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
191675088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
191720fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
191822881299Sschwarze 	} else {
191975088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
192020fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
192122881299Sschwarze 	}
192220fa2881Sschwarze 
192320fa2881Sschwarze 	/* Mark our last section. */
192420fa2881Sschwarze 
192520fa2881Sschwarze 	mdoc->lastsec = sec;
19261eccdf28Sschwarze 
19271eccdf28Sschwarze 	/*
19281eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
19291eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
19301eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
19311eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
19321eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
19331eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
19341eccdf28Sschwarze 	 */
19351eccdf28Sschwarze 
1936a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
1937a2cff342Sschwarze 	mdoc->last->sec = sec;
1938a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
1939a2cff342Sschwarze 		n->sec = sec;
194020fa2881Sschwarze 
194120fa2881Sschwarze 	/* We don't care about custom sections after this. */
1942fccfce9dSschwarze 
194346133849Sschwarze 	if (SEC_CUSTOM == sec) {
194446133849Sschwarze 		free(secname);
194598b8f00aSschwarze 		return;
194646133849Sschwarze 	}
1947fccfce9dSschwarze 
19486be99f77Sschwarze 	/*
194920fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
195020fa2881Sschwarze 	 * out of order.
19516be99f77Sschwarze 	 */
1952f73abda9Skristaps 
195320fa2881Sschwarze 	if (sec == mdoc->lastnamed)
1954bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1955bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
1956bd594191Sschwarze 		    "Sh %s", secname);
195720fa2881Sschwarze 
195820fa2881Sschwarze 	if (sec < mdoc->lastnamed)
1959bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1960bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
1961bd594191Sschwarze 		    "Sh %s", secname);
196220fa2881Sschwarze 
196320fa2881Sschwarze 	/* Mark the last named section. */
196420fa2881Sschwarze 
196520fa2881Sschwarze 	mdoc->lastnamed = sec;
196620fa2881Sschwarze 
196720fa2881Sschwarze 	/* Check particular section/manual conventions. */
196820fa2881Sschwarze 
19693fdead0cSschwarze 	if (mdoc->meta.msec == NULL) {
19703fdead0cSschwarze 		free(secname);
197198b8f00aSschwarze 		return;
19723fdead0cSschwarze 	}
197320fa2881Sschwarze 
197451fcab2fSschwarze 	goodsec = NULL;
197520fa2881Sschwarze 	switch (sec) {
197649aff9f8Sschwarze 	case SEC_ERRORS:
1977be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
1978be89e780Sschwarze 			break;
197951fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
1980be89e780Sschwarze 		/* FALLTHROUGH */
198149aff9f8Sschwarze 	case SEC_RETURN_VALUES:
198220fa2881Sschwarze 		/* FALLTHROUGH */
198349aff9f8Sschwarze 	case SEC_LIBRARY:
198492c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
1985f73abda9Skristaps 			break;
198692c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
198792c0ca7fSschwarze 			break;
198851fcab2fSschwarze 		if (NULL == goodsec)
198951fcab2fSschwarze 			goodsec = "2, 3, 9";
199003ab2f23Sdlg 		/* FALLTHROUGH */
199149aff9f8Sschwarze 	case SEC_CONTEXT:
199292c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
199392c0ca7fSschwarze 			break;
199451fcab2fSschwarze 		if (NULL == goodsec)
199551fcab2fSschwarze 			goodsec = "9";
199651fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
199751fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
1998bd594191Sschwarze 		    "Sh %s for %s only", secname, goodsec);
199920fa2881Sschwarze 		break;
2000f73abda9Skristaps 	default:
2001f73abda9Skristaps 		break;
2002f73abda9Skristaps 	}
200346133849Sschwarze 	free(secname);
2004f73abda9Skristaps }
2005d39b9a9cSschwarze 
200698b8f00aSschwarze static void
2007f6127a73Sschwarze post_ignpar(POST_ARGS)
2008f6127a73Sschwarze {
2009f6127a73Sschwarze 	struct mdoc_node *np;
2010f6127a73Sschwarze 
2011b7530f2fSschwarze 	switch (mdoc->last->type) {
2012b7530f2fSschwarze 	case MDOC_HEAD:
2013753701eeSschwarze 		post_hyph(mdoc);
201498b8f00aSschwarze 		return;
2015b7530f2fSschwarze 	case MDOC_BODY:
2016b7530f2fSschwarze 		break;
2017b7530f2fSschwarze 	default:
2018b7530f2fSschwarze 		return;
2019b7530f2fSschwarze 	}
2020f6127a73Sschwarze 
2021f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2022f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
202320369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
202420369664Sschwarze 			    mdoc->parse, np->line, np->pos,
202520369664Sschwarze 			    "%s after %s", mdoc_macronames[np->tok],
202620369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2027f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2028f6127a73Sschwarze 		}
2029f6127a73Sschwarze 
2030f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2031f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
203220369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
203320369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
203420369664Sschwarze 			    mdoc_macronames[np->tok],
203520369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2036f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2037f6127a73Sschwarze 		}
2038f6127a73Sschwarze }
2039f6127a73Sschwarze 
204098b8f00aSschwarze static void
204120fa2881Sschwarze pre_par(PRE_ARGS)
2042d39b9a9cSschwarze {
2043d39b9a9cSschwarze 
2044d39b9a9cSschwarze 	if (NULL == mdoc->last)
204598b8f00aSschwarze 		return;
2046f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
204798b8f00aSschwarze 		return;
2048d39b9a9cSschwarze 
204920fa2881Sschwarze 	/*
205020fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
205120fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
205220fa2881Sschwarze 	 */
2053d39b9a9cSschwarze 
2054e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2055e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2056e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
205798b8f00aSschwarze 		return;
20588c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
205998b8f00aSschwarze 		return;
20608c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
206198b8f00aSschwarze 		return;
20628c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
206398b8f00aSschwarze 		return;
2064d39b9a9cSschwarze 
206520369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
206620369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
206720369664Sschwarze 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
206820369664Sschwarze 	    mdoc_macronames[n->tok]);
2069d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2070d39b9a9cSschwarze }
207120fa2881Sschwarze 
207298b8f00aSschwarze static void
2073e0dd4c9cSschwarze post_par(POST_ARGS)
2074e0dd4c9cSschwarze {
207520369664Sschwarze 	struct mdoc_node *np;
2076e0dd4c9cSschwarze 
20773798fb25Sschwarze 	np = mdoc->last;
2078753701eeSschwarze 
20793798fb25Sschwarze 	if (np->tok == MDOC_sp) {
20803798fb25Sschwarze 		if (np->nchild > 1)
20813798fb25Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
20823798fb25Sschwarze 			    np->child->next->line, np->child->next->pos,
20833798fb25Sschwarze 			    "sp ... %s", np->child->next->string);
20843798fb25Sschwarze 	} else if (np->child != NULL)
20853798fb25Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
20863798fb25Sschwarze 		    mdoc->parse, np->line, np->pos, "%s %s",
20873798fb25Sschwarze 		    mdoc_macronames[np->tok], np->child->string);
2088e0dd4c9cSschwarze 
208920369664Sschwarze 	if (NULL == (np = mdoc->last->prev)) {
209020369664Sschwarze 		np = mdoc->last->parent;
209120369664Sschwarze 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
209298b8f00aSschwarze 			return;
209398b8f00aSschwarze 	} else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2094e0dd4c9cSschwarze 	    (MDOC_br != mdoc->last->tok ||
209520369664Sschwarze 	     (MDOC_sp != np->tok && MDOC_br != np->tok)))
209698b8f00aSschwarze 		return;
2097e0dd4c9cSschwarze 
209820369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
209920369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
210020369664Sschwarze 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
210120369664Sschwarze 	    mdoc_macronames[np->tok]);
2102e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2103e0dd4c9cSschwarze }
2104e0dd4c9cSschwarze 
210598b8f00aSschwarze static void
210620fa2881Sschwarze pre_literal(PRE_ARGS)
210720fa2881Sschwarze {
210820fa2881Sschwarze 
2109d52d1586Sschwarze 	pre_display(mdoc, n);
2110d52d1586Sschwarze 
211120fa2881Sschwarze 	if (MDOC_BODY != n->type)
211298b8f00aSschwarze 		return;
211320fa2881Sschwarze 
211420fa2881Sschwarze 	/*
211520fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
211620fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
211720fa2881Sschwarze 	 */
211820fa2881Sschwarze 
211920fa2881Sschwarze 	switch (n->tok) {
212049aff9f8Sschwarze 	case MDOC_Dl:
212120fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
212220fa2881Sschwarze 		break;
212349aff9f8Sschwarze 	case MDOC_Bd:
21248c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
212520fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
21268c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
212720fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
212820fa2881Sschwarze 		break;
212920fa2881Sschwarze 	default:
213020fa2881Sschwarze 		abort();
213120fa2881Sschwarze 		/* NOTREACHED */
213220fa2881Sschwarze 	}
213320fa2881Sschwarze }
213420fa2881Sschwarze 
213598b8f00aSschwarze static void
213620fa2881Sschwarze post_dd(POST_ARGS)
213720fa2881Sschwarze {
213820fa2881Sschwarze 	struct mdoc_node *n;
213983af2bccSschwarze 	char		 *datestr;
214020fa2881Sschwarze 
2141b058e777Sschwarze 	if (mdoc->meta.date)
2142b058e777Sschwarze 		free(mdoc->meta.date);
214320fa2881Sschwarze 
2144b058e777Sschwarze 	n = mdoc->last;
2145b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2146231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2147231c7061Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
21483fdead0cSschwarze 		goto out;
214920fa2881Sschwarze 	}
215020fa2881Sschwarze 
215183af2bccSschwarze 	datestr = NULL;
215283af2bccSschwarze 	mdoc_deroff(&datestr, n);
215383af2bccSschwarze 	if (mdoc->quick)
215483af2bccSschwarze 		mdoc->meta.date = datestr;
215583af2bccSschwarze 	else {
215683af2bccSschwarze 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
215783af2bccSschwarze 		    datestr, n->line, n->pos);
215883af2bccSschwarze 		free(datestr);
215904e980cbSschwarze 	}
21603fdead0cSschwarze out:
21613fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
216220fa2881Sschwarze }
216320fa2881Sschwarze 
216498b8f00aSschwarze static void
216520fa2881Sschwarze post_dt(POST_ARGS)
216620fa2881Sschwarze {
216720fa2881Sschwarze 	struct mdoc_node *nn, *n;
216820fa2881Sschwarze 	const char	 *cp;
216920fa2881Sschwarze 	char		 *p;
217020fa2881Sschwarze 
217120fa2881Sschwarze 	n = mdoc->last;
217220fa2881Sschwarze 
217320fa2881Sschwarze 	free(mdoc->meta.title);
21743fdead0cSschwarze 	free(mdoc->meta.msec);
217520fa2881Sschwarze 	free(mdoc->meta.vol);
217620fa2881Sschwarze 	free(mdoc->meta.arch);
217720fa2881Sschwarze 
21783fdead0cSschwarze 	mdoc->meta.title = NULL;
21793fdead0cSschwarze 	mdoc->meta.msec = NULL;
21803fdead0cSschwarze 	mdoc->meta.vol = NULL;
21813fdead0cSschwarze 	mdoc->meta.arch = NULL;
218220fa2881Sschwarze 
218351fcab2fSschwarze 	/* First check that all characters are uppercase. */
218420fa2881Sschwarze 
218520fa2881Sschwarze 	if (NULL != (nn = n->child))
218620fa2881Sschwarze 		for (p = nn->string; *p; p++) {
218704e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
218820fa2881Sschwarze 				continue;
2189bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_TITLE_CASE,
219051fcab2fSschwarze 			    mdoc->parse, nn->line,
219151fcab2fSschwarze 			    nn->pos + (p - nn->string),
2192bd594191Sschwarze 			    "Dt %s", nn->string);
219320fa2881Sschwarze 			break;
219420fa2881Sschwarze 		}
219520fa2881Sschwarze 
21963fdead0cSschwarze 	/* No argument: msec and arch remain NULL. */
219720fa2881Sschwarze 
219820fa2881Sschwarze 	if (NULL == (nn = n->child)) {
21993fdead0cSschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
22003fdead0cSschwarze 		    mdoc->parse, n->line, n->pos, "Dt");
22013fdead0cSschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
220220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
22033fdead0cSschwarze 		goto out;
220420fa2881Sschwarze 	}
220520fa2881Sschwarze 
22063fdead0cSschwarze 	/* One argument: msec and arch remain NULL. */
220720fa2881Sschwarze 
220849aff9f8Sschwarze 	mdoc->meta.title = mandoc_strdup(
22093fdead0cSschwarze 	    '\0' == nn->string[0] ? "UNTITLED" : nn->string);
221020fa2881Sschwarze 
221120fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
22123fdead0cSschwarze 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
22133fdead0cSschwarze 		    mdoc->parse, n->line, n->pos,
22143fdead0cSschwarze 		    "Dt %s", mdoc->meta.title);
221520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
22163fdead0cSschwarze 		goto out;
221720fa2881Sschwarze 	}
221820fa2881Sschwarze 
221920fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
222049aff9f8Sschwarze 	 * title = TITLE,
222149aff9f8Sschwarze 	 * volume = SEC is msec ? format(msec) : SEC,
222220fa2881Sschwarze 	 * msec = SEC is msec ? atoi(msec) : 0,
222320fa2881Sschwarze 	 * arch = NULL
222420fa2881Sschwarze 	 */
222520fa2881Sschwarze 
222688ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
222720fa2881Sschwarze 	if (cp) {
222820fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
222920fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
223020fa2881Sschwarze 	} else {
2231bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2232bd594191Sschwarze 		    nn->line, nn->pos, "Dt ... %s", nn->string);
223320fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
223420fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
223520fa2881Sschwarze 	}
223620fa2881Sschwarze 
2237a47bc3bcSschwarze 	/* Handle an optional architecture */
223820fa2881Sschwarze 
2239a47bc3bcSschwarze 	if ((nn = nn->next) != NULL) {
2240b94f27c5Sschwarze 		for (p = nn->string; *p; p++)
2241b94f27c5Sschwarze 			*p = tolower((unsigned char)*p);
2242b94f27c5Sschwarze 		mdoc->meta.arch = mandoc_strdup(nn->string);
224320fa2881Sschwarze 	}
224420fa2881Sschwarze 
224520fa2881Sschwarze 	/* Ignore any subsequent parameters... */
224620fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
22473fdead0cSschwarze out:
22483fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
224920fa2881Sschwarze }
225020fa2881Sschwarze 
225198b8f00aSschwarze static void
2252992063deSschwarze post_bx(POST_ARGS)
2253992063deSschwarze {
2254992063deSschwarze 	struct mdoc_node	*n;
2255992063deSschwarze 
2256992063deSschwarze 	/*
2257992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2258992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2259992063deSschwarze 	 * uppercase blindly.
2260992063deSschwarze 	 */
2261992063deSschwarze 
2262992063deSschwarze 	n = mdoc->last->child;
2263992063deSschwarze 	if (n && NULL != (n = n->next))
226449aff9f8Sschwarze 		*n->string = (char)toupper((unsigned char)*n->string);
2265992063deSschwarze }
2266992063deSschwarze 
226798b8f00aSschwarze static void
226820fa2881Sschwarze post_os(POST_ARGS)
226920fa2881Sschwarze {
227020fa2881Sschwarze #ifndef OSNAME
227120fa2881Sschwarze 	struct utsname	  utsname;
22724c468128Sschwarze 	static char	 *defbuf;
227320fa2881Sschwarze #endif
22744c468128Sschwarze 	struct mdoc_node *n;
227520fa2881Sschwarze 
227620fa2881Sschwarze 	n = mdoc->last;
227720fa2881Sschwarze 
227820fa2881Sschwarze 	/*
2279353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2280353fa9ecSschwarze 	 * The order of precedence is:
2281353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2282353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2283353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2284353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
228520fa2881Sschwarze 	 */
228620fa2881Sschwarze 
228720fa2881Sschwarze 	free(mdoc->meta.os);
228883af2bccSschwarze 	mdoc->meta.os = NULL;
228983af2bccSschwarze 	mdoc_deroff(&mdoc->meta.os, n);
229083af2bccSschwarze 	if (mdoc->meta.os)
22913fdead0cSschwarze 		goto out;
22924c468128Sschwarze 
2293353fa9ecSschwarze 	if (mdoc->defos) {
2294353fa9ecSschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
22953fdead0cSschwarze 		goto out;
2296353fa9ecSschwarze 	}
22974c468128Sschwarze 
229820fa2881Sschwarze #ifdef OSNAME
22994c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
230020fa2881Sschwarze #else /*!OSNAME */
23014c468128Sschwarze 	if (NULL == defbuf) {
2302a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
2303f79e7afeSschwarze 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2304f79e7afeSschwarze 			    n->line, n->pos, "Os");
23054c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2306a450f7c4Sschwarze 		} else
2307a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2308a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
230920fa2881Sschwarze 	}
23104c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
231120fa2881Sschwarze #endif /*!OSNAME*/
23123fdead0cSschwarze 
23133fdead0cSschwarze out:
23143fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
231520fa2881Sschwarze }
231620fa2881Sschwarze 
2317e214f641Sschwarze /*
2318e214f641Sschwarze  * If no argument is provided,
2319e214f641Sschwarze  * fill in the name of the current manual page.
2320e214f641Sschwarze  */
232198b8f00aSschwarze static void
2322e214f641Sschwarze post_ex(POST_ARGS)
232320fa2881Sschwarze {
2324e214f641Sschwarze 	struct mdoc_node *n;
232520fa2881Sschwarze 
232620fa2881Sschwarze 	n = mdoc->last;
232720fa2881Sschwarze 
232820fa2881Sschwarze 	if (n->child)
232998b8f00aSschwarze 		return;
233020fa2881Sschwarze 
2331e214f641Sschwarze 	if (mdoc->meta.name == NULL) {
2332bd594191Sschwarze 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2333bd594191Sschwarze 		    n->line, n->pos, "Ex");
233498b8f00aSschwarze 		return;
2335e214f641Sschwarze 	}
233620fa2881Sschwarze 
233720fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
233898b8f00aSschwarze 	mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2339e214f641Sschwarze 	mdoc->last = n;
234020fa2881Sschwarze }
234120fa2881Sschwarze 
234219a69263Sschwarze static enum mdoc_sec
234319a69263Sschwarze a2sec(const char *p)
234419a69263Sschwarze {
234519a69263Sschwarze 	int		 i;
234619a69263Sschwarze 
234719a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
234819a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
234919a69263Sschwarze 			return((enum mdoc_sec)i);
235019a69263Sschwarze 
235119a69263Sschwarze 	return(SEC_CUSTOM);
235219a69263Sschwarze }
235319a69263Sschwarze 
235419a69263Sschwarze static size_t
235519a69263Sschwarze macro2len(enum mdoct macro)
235619a69263Sschwarze {
235719a69263Sschwarze 
235819a69263Sschwarze 	switch (macro) {
235949aff9f8Sschwarze 	case MDOC_Ad:
236019a69263Sschwarze 		return(12);
236149aff9f8Sschwarze 	case MDOC_Ao:
236219a69263Sschwarze 		return(12);
236349aff9f8Sschwarze 	case MDOC_An:
236419a69263Sschwarze 		return(12);
236549aff9f8Sschwarze 	case MDOC_Aq:
236619a69263Sschwarze 		return(12);
236749aff9f8Sschwarze 	case MDOC_Ar:
236819a69263Sschwarze 		return(12);
236949aff9f8Sschwarze 	case MDOC_Bo:
237019a69263Sschwarze 		return(12);
237149aff9f8Sschwarze 	case MDOC_Bq:
237219a69263Sschwarze 		return(12);
237349aff9f8Sschwarze 	case MDOC_Cd:
237419a69263Sschwarze 		return(12);
237549aff9f8Sschwarze 	case MDOC_Cm:
237619a69263Sschwarze 		return(10);
237749aff9f8Sschwarze 	case MDOC_Do:
237819a69263Sschwarze 		return(10);
237949aff9f8Sschwarze 	case MDOC_Dq:
238019a69263Sschwarze 		return(12);
238149aff9f8Sschwarze 	case MDOC_Dv:
238219a69263Sschwarze 		return(12);
238349aff9f8Sschwarze 	case MDOC_Eo:
238419a69263Sschwarze 		return(12);
238549aff9f8Sschwarze 	case MDOC_Em:
238619a69263Sschwarze 		return(10);
238749aff9f8Sschwarze 	case MDOC_Er:
238819a69263Sschwarze 		return(17);
238949aff9f8Sschwarze 	case MDOC_Ev:
239019a69263Sschwarze 		return(15);
239149aff9f8Sschwarze 	case MDOC_Fa:
239219a69263Sschwarze 		return(12);
239349aff9f8Sschwarze 	case MDOC_Fl:
239419a69263Sschwarze 		return(10);
239549aff9f8Sschwarze 	case MDOC_Fo:
239619a69263Sschwarze 		return(16);
239749aff9f8Sschwarze 	case MDOC_Fn:
239819a69263Sschwarze 		return(16);
239949aff9f8Sschwarze 	case MDOC_Ic:
240019a69263Sschwarze 		return(10);
240149aff9f8Sschwarze 	case MDOC_Li:
240219a69263Sschwarze 		return(16);
240349aff9f8Sschwarze 	case MDOC_Ms:
240419a69263Sschwarze 		return(6);
240549aff9f8Sschwarze 	case MDOC_Nm:
240619a69263Sschwarze 		return(10);
240749aff9f8Sschwarze 	case MDOC_No:
240819a69263Sschwarze 		return(12);
240949aff9f8Sschwarze 	case MDOC_Oo:
241019a69263Sschwarze 		return(10);
241149aff9f8Sschwarze 	case MDOC_Op:
241219a69263Sschwarze 		return(14);
241349aff9f8Sschwarze 	case MDOC_Pa:
241419a69263Sschwarze 		return(32);
241549aff9f8Sschwarze 	case MDOC_Pf:
241619a69263Sschwarze 		return(12);
241749aff9f8Sschwarze 	case MDOC_Po:
241819a69263Sschwarze 		return(12);
241949aff9f8Sschwarze 	case MDOC_Pq:
242019a69263Sschwarze 		return(12);
242149aff9f8Sschwarze 	case MDOC_Ql:
242219a69263Sschwarze 		return(16);
242349aff9f8Sschwarze 	case MDOC_Qo:
242419a69263Sschwarze 		return(12);
242549aff9f8Sschwarze 	case MDOC_So:
242619a69263Sschwarze 		return(12);
242749aff9f8Sschwarze 	case MDOC_Sq:
242819a69263Sschwarze 		return(12);
242949aff9f8Sschwarze 	case MDOC_Sy:
243019a69263Sschwarze 		return(6);
243149aff9f8Sschwarze 	case MDOC_Sx:
243219a69263Sschwarze 		return(16);
243349aff9f8Sschwarze 	case MDOC_Tn:
243419a69263Sschwarze 		return(10);
243549aff9f8Sschwarze 	case MDOC_Va:
243619a69263Sschwarze 		return(12);
243749aff9f8Sschwarze 	case MDOC_Vt:
243819a69263Sschwarze 		return(12);
243949aff9f8Sschwarze 	case MDOC_Xr:
244019a69263Sschwarze 		return(10);
244119a69263Sschwarze 	default:
244219a69263Sschwarze 		break;
244319a69263Sschwarze 	};
244419a69263Sschwarze 	return(0);
244519a69263Sschwarze }
2446