xref: /illumos-gate/usr/src/cmd/mandoc/mdoc_validate.c (revision 371584c2eae4cf827fd406ba26c14f021adaaa70)
1*371584c2SYuri Pankov /*	$Id: mdoc_validate.c,v 1.301 2016/01/08 17:48:09 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*371584c2SYuri Pankov  * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org>
5260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
695c635efSGarrett D'Amore  *
795c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
895c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
995c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1095c635efSGarrett D'Amore  *
11*371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1295c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1495c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1595c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1695c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1795c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1895c635efSGarrett D'Amore  */
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore 
21260e9a87SYuri Pankov #include <sys/types.h>
22698f87a4SGarrett D'Amore #ifndef OSNAME
2395c635efSGarrett D'Amore #include <sys/utsname.h>
2495c635efSGarrett D'Amore #endif
2595c635efSGarrett D'Amore 
2695c635efSGarrett D'Amore #include <assert.h>
2795c635efSGarrett D'Amore #include <ctype.h>
2895c635efSGarrett D'Amore #include <limits.h>
2995c635efSGarrett D'Amore #include <stdio.h>
3095c635efSGarrett D'Amore #include <stdlib.h>
3195c635efSGarrett D'Amore #include <string.h>
3295c635efSGarrett D'Amore #include <time.h>
3395c635efSGarrett D'Amore 
34260e9a87SYuri Pankov #include "mandoc_aux.h"
35*371584c2SYuri Pankov #include "mandoc.h"
36*371584c2SYuri Pankov #include "roff.h"
37*371584c2SYuri Pankov #include "mdoc.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
39*371584c2SYuri Pankov #include "roff_int.h"
40*371584c2SYuri Pankov #include "libmdoc.h"
4195c635efSGarrett D'Amore 
4295c635efSGarrett D'Amore /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4395c635efSGarrett D'Amore 
44*371584c2SYuri Pankov #define	POST_ARGS struct roff_man *mdoc
4595c635efSGarrett D'Amore 
4695c635efSGarrett D'Amore enum	check_ineq {
4795c635efSGarrett D'Amore 	CHECK_LT,
4895c635efSGarrett D'Amore 	CHECK_GT,
4995c635efSGarrett D'Amore 	CHECK_EQ
5095c635efSGarrett D'Amore };
5195c635efSGarrett D'Amore 
52260e9a87SYuri Pankov typedef	void	(*v_post)(POST_ARGS);
5395c635efSGarrett D'Amore 
54*371584c2SYuri Pankov static	void	 check_text(struct roff_man *, int, int, char *);
55*371584c2SYuri Pankov static	void	 check_argv(struct roff_man *,
56*371584c2SYuri Pankov 			struct roff_node *, struct mdoc_argv *);
57*371584c2SYuri Pankov static	void	 check_args(struct roff_man *, struct roff_node *);
58*371584c2SYuri Pankov static	int	 child_an(const struct roff_node *);
59*371584c2SYuri Pankov static	size_t		macro2len(int);
60260e9a87SYuri Pankov static	void	 rewrite_macro2len(char **);
61260e9a87SYuri Pankov 
62260e9a87SYuri Pankov static	void	 post_an(POST_ARGS);
63*371584c2SYuri Pankov static	void	 post_an_norm(POST_ARGS);
64260e9a87SYuri Pankov static	void	 post_at(POST_ARGS);
65*371584c2SYuri Pankov static	void	 post_bd(POST_ARGS);
66260e9a87SYuri Pankov static	void	 post_bf(POST_ARGS);
67260e9a87SYuri Pankov static	void	 post_bk(POST_ARGS);
68260e9a87SYuri Pankov static	void	 post_bl(POST_ARGS);
69260e9a87SYuri Pankov static	void	 post_bl_block(POST_ARGS);
70260e9a87SYuri Pankov static	void	 post_bl_block_tag(POST_ARGS);
71260e9a87SYuri Pankov static	void	 post_bl_head(POST_ARGS);
72*371584c2SYuri Pankov static	void	 post_bl_norm(POST_ARGS);
73260e9a87SYuri Pankov static	void	 post_bx(POST_ARGS);
74260e9a87SYuri Pankov static	void	 post_defaults(POST_ARGS);
75*371584c2SYuri Pankov static	void	 post_display(POST_ARGS);
76260e9a87SYuri Pankov static	void	 post_dd(POST_ARGS);
77260e9a87SYuri Pankov static	void	 post_dt(POST_ARGS);
78260e9a87SYuri Pankov static	void	 post_en(POST_ARGS);
79260e9a87SYuri Pankov static	void	 post_es(POST_ARGS);
80260e9a87SYuri Pankov static	void	 post_eoln(POST_ARGS);
81260e9a87SYuri Pankov static	void	 post_ex(POST_ARGS);
82260e9a87SYuri Pankov static	void	 post_fa(POST_ARGS);
83260e9a87SYuri Pankov static	void	 post_fn(POST_ARGS);
84260e9a87SYuri Pankov static	void	 post_fname(POST_ARGS);
85260e9a87SYuri Pankov static	void	 post_fo(POST_ARGS);
86260e9a87SYuri Pankov static	void	 post_hyph(POST_ARGS);
87260e9a87SYuri Pankov static	void	 post_ignpar(POST_ARGS);
88260e9a87SYuri Pankov static	void	 post_it(POST_ARGS);
89260e9a87SYuri Pankov static	void	 post_lb(POST_ARGS);
90260e9a87SYuri Pankov static	void	 post_nd(POST_ARGS);
91260e9a87SYuri Pankov static	void	 post_nm(POST_ARGS);
92260e9a87SYuri Pankov static	void	 post_ns(POST_ARGS);
93*371584c2SYuri Pankov static	void	 post_obsolete(POST_ARGS);
94260e9a87SYuri Pankov static	void	 post_os(POST_ARGS);
95260e9a87SYuri Pankov static	void	 post_par(POST_ARGS);
96*371584c2SYuri Pankov static	void	 post_prevpar(POST_ARGS);
97260e9a87SYuri Pankov static	void	 post_root(POST_ARGS);
98260e9a87SYuri Pankov static	void	 post_rs(POST_ARGS);
99260e9a87SYuri Pankov static	void	 post_sh(POST_ARGS);
100260e9a87SYuri Pankov static	void	 post_sh_head(POST_ARGS);
101260e9a87SYuri Pankov static	void	 post_sh_name(POST_ARGS);
102260e9a87SYuri Pankov static	void	 post_sh_see_also(POST_ARGS);
103260e9a87SYuri Pankov static	void	 post_sh_authors(POST_ARGS);
104260e9a87SYuri Pankov static	void	 post_sm(POST_ARGS);
105260e9a87SYuri Pankov static	void	 post_st(POST_ARGS);
106*371584c2SYuri Pankov static	void	 post_std(POST_ARGS);
107*371584c2SYuri Pankov 
108*371584c2SYuri Pankov static	v_post mdoc_valids[MDOC_MAX] = {
109*371584c2SYuri Pankov 	NULL,		/* Ap */
110*371584c2SYuri Pankov 	post_dd,	/* Dd */
111*371584c2SYuri Pankov 	post_dt,	/* Dt */
112*371584c2SYuri Pankov 	post_os,	/* Os */
113*371584c2SYuri Pankov 	post_sh,	/* Sh */
114*371584c2SYuri Pankov 	post_ignpar,	/* Ss */
115*371584c2SYuri Pankov 	post_par,	/* Pp */
116*371584c2SYuri Pankov 	post_display,	/* D1 */
117*371584c2SYuri Pankov 	post_display,	/* Dl */
118*371584c2SYuri Pankov 	post_display,	/* Bd */
119*371584c2SYuri Pankov 	NULL,		/* Ed */
120*371584c2SYuri Pankov 	post_bl,	/* Bl */
121*371584c2SYuri Pankov 	NULL,		/* El */
122*371584c2SYuri Pankov 	post_it,	/* It */
123*371584c2SYuri Pankov 	NULL,		/* Ad */
124*371584c2SYuri Pankov 	post_an,	/* An */
125*371584c2SYuri Pankov 	post_defaults,	/* Ar */
126*371584c2SYuri Pankov 	NULL,		/* Cd */
127*371584c2SYuri Pankov 	NULL,		/* Cm */
128*371584c2SYuri Pankov 	NULL,		/* Dv */
129*371584c2SYuri Pankov 	NULL,		/* Er */
130*371584c2SYuri Pankov 	NULL,		/* Ev */
131*371584c2SYuri Pankov 	post_ex,	/* Ex */
132*371584c2SYuri Pankov 	post_fa,	/* Fa */
133*371584c2SYuri Pankov 	NULL,		/* Fd */
134*371584c2SYuri Pankov 	NULL,		/* Fl */
135*371584c2SYuri Pankov 	post_fn,	/* Fn */
136*371584c2SYuri Pankov 	NULL,		/* Ft */
137*371584c2SYuri Pankov 	NULL,		/* Ic */
138*371584c2SYuri Pankov 	NULL,		/* In */
139*371584c2SYuri Pankov 	post_defaults,	/* Li */
140*371584c2SYuri Pankov 	post_nd,	/* Nd */
141*371584c2SYuri Pankov 	post_nm,	/* Nm */
142*371584c2SYuri Pankov 	NULL,		/* Op */
143*371584c2SYuri Pankov 	post_obsolete,	/* Ot */
144*371584c2SYuri Pankov 	post_defaults,	/* Pa */
145*371584c2SYuri Pankov 	post_std,	/* Rv */
146*371584c2SYuri Pankov 	post_st,	/* St */
147*371584c2SYuri Pankov 	NULL,		/* Va */
148*371584c2SYuri Pankov 	NULL,		/* Vt */
149*371584c2SYuri Pankov 	NULL,		/* Xr */
150*371584c2SYuri Pankov 	NULL,		/* %A */
151*371584c2SYuri Pankov 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
152*371584c2SYuri Pankov 	NULL,		/* %D */
153*371584c2SYuri Pankov 	NULL,		/* %I */
154*371584c2SYuri Pankov 	NULL,		/* %J */
155*371584c2SYuri Pankov 	post_hyph,	/* %N */
156*371584c2SYuri Pankov 	post_hyph,	/* %O */
157*371584c2SYuri Pankov 	NULL,		/* %P */
158*371584c2SYuri Pankov 	post_hyph,	/* %R */
159*371584c2SYuri Pankov 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
160*371584c2SYuri Pankov 	NULL,		/* %V */
161*371584c2SYuri Pankov 	NULL,		/* Ac */
162*371584c2SYuri Pankov 	NULL,		/* Ao */
163*371584c2SYuri Pankov 	NULL,		/* Aq */
164*371584c2SYuri Pankov 	post_at,	/* At */
165*371584c2SYuri Pankov 	NULL,		/* Bc */
166*371584c2SYuri Pankov 	post_bf,	/* Bf */
167*371584c2SYuri Pankov 	NULL,		/* Bo */
168*371584c2SYuri Pankov 	NULL,		/* Bq */
169*371584c2SYuri Pankov 	NULL,		/* Bsx */
170*371584c2SYuri Pankov 	post_bx,	/* Bx */
171*371584c2SYuri Pankov 	post_obsolete,	/* Db */
172*371584c2SYuri Pankov 	NULL,		/* Dc */
173*371584c2SYuri Pankov 	NULL,		/* Do */
174*371584c2SYuri Pankov 	NULL,		/* Dq */
175*371584c2SYuri Pankov 	NULL,		/* Ec */
176*371584c2SYuri Pankov 	NULL,		/* Ef */
177*371584c2SYuri Pankov 	NULL,		/* Em */
178*371584c2SYuri Pankov 	NULL,		/* Eo */
179*371584c2SYuri Pankov 	NULL,		/* Fx */
180*371584c2SYuri Pankov 	NULL,		/* Ms */
181*371584c2SYuri Pankov 	NULL,		/* No */
182*371584c2SYuri Pankov 	post_ns,	/* Ns */
183*371584c2SYuri Pankov 	NULL,		/* Nx */
184*371584c2SYuri Pankov 	NULL,		/* Ox */
185*371584c2SYuri Pankov 	NULL,		/* Pc */
186*371584c2SYuri Pankov 	NULL,		/* Pf */
187*371584c2SYuri Pankov 	NULL,		/* Po */
188*371584c2SYuri Pankov 	NULL,		/* Pq */
189*371584c2SYuri Pankov 	NULL,		/* Qc */
190*371584c2SYuri Pankov 	NULL,		/* Ql */
191*371584c2SYuri Pankov 	NULL,		/* Qo */
192*371584c2SYuri Pankov 	NULL,		/* Qq */
193*371584c2SYuri Pankov 	NULL,		/* Re */
194*371584c2SYuri Pankov 	post_rs,	/* Rs */
195*371584c2SYuri Pankov 	NULL,		/* Sc */
196*371584c2SYuri Pankov 	NULL,		/* So */
197*371584c2SYuri Pankov 	NULL,		/* Sq */
198*371584c2SYuri Pankov 	post_sm,	/* Sm */
199*371584c2SYuri Pankov 	post_hyph,	/* Sx */
200*371584c2SYuri Pankov 	NULL,		/* Sy */
201*371584c2SYuri Pankov 	NULL,		/* Tn */
202*371584c2SYuri Pankov 	NULL,		/* Ux */
203*371584c2SYuri Pankov 	NULL,		/* Xc */
204*371584c2SYuri Pankov 	NULL,		/* Xo */
205*371584c2SYuri Pankov 	post_fo,	/* Fo */
206*371584c2SYuri Pankov 	NULL,		/* Fc */
207*371584c2SYuri Pankov 	NULL,		/* Oo */
208*371584c2SYuri Pankov 	NULL,		/* Oc */
209*371584c2SYuri Pankov 	post_bk,	/* Bk */
210*371584c2SYuri Pankov 	NULL,		/* Ek */
211*371584c2SYuri Pankov 	post_eoln,	/* Bt */
212*371584c2SYuri Pankov 	NULL,		/* Hf */
213*371584c2SYuri Pankov 	post_obsolete,	/* Fr */
214*371584c2SYuri Pankov 	post_eoln,	/* Ud */
215*371584c2SYuri Pankov 	post_lb,	/* Lb */
216*371584c2SYuri Pankov 	post_par,	/* Lp */
217*371584c2SYuri Pankov 	NULL,		/* Lk */
218*371584c2SYuri Pankov 	post_defaults,	/* Mt */
219*371584c2SYuri Pankov 	NULL,		/* Brq */
220*371584c2SYuri Pankov 	NULL,		/* Bro */
221*371584c2SYuri Pankov 	NULL,		/* Brc */
222*371584c2SYuri Pankov 	NULL,		/* %C */
223*371584c2SYuri Pankov 	post_es,	/* Es */
224*371584c2SYuri Pankov 	post_en,	/* En */
225*371584c2SYuri Pankov 	NULL,		/* Dx */
226*371584c2SYuri Pankov 	NULL,		/* %Q */
227*371584c2SYuri Pankov 	post_par,	/* br */
228*371584c2SYuri Pankov 	post_par,	/* sp */
229*371584c2SYuri Pankov 	NULL,		/* %U */
230*371584c2SYuri Pankov 	NULL,		/* Ta */
231*371584c2SYuri Pankov 	NULL,		/* ll */
23295c635efSGarrett D'Amore };
23395c635efSGarrett D'Amore 
23495c635efSGarrett D'Amore #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
23595c635efSGarrett D'Amore 
236*371584c2SYuri Pankov static	const int rsord[RSORD_MAX] = {
23795c635efSGarrett D'Amore 	MDOC__A,
23895c635efSGarrett D'Amore 	MDOC__T,
23995c635efSGarrett D'Amore 	MDOC__B,
24095c635efSGarrett D'Amore 	MDOC__I,
24195c635efSGarrett D'Amore 	MDOC__J,
24295c635efSGarrett D'Amore 	MDOC__R,
24395c635efSGarrett D'Amore 	MDOC__N,
24495c635efSGarrett D'Amore 	MDOC__V,
245698f87a4SGarrett D'Amore 	MDOC__U,
24695c635efSGarrett D'Amore 	MDOC__P,
24795c635efSGarrett D'Amore 	MDOC__Q,
24895c635efSGarrett D'Amore 	MDOC__C,
249698f87a4SGarrett D'Amore 	MDOC__D,
250698f87a4SGarrett D'Amore 	MDOC__O
25195c635efSGarrett D'Amore };
25295c635efSGarrett D'Amore 
25395c635efSGarrett D'Amore static	const char * const secnames[SEC__MAX] = {
25495c635efSGarrett D'Amore 	NULL,
25595c635efSGarrett D'Amore 	"NAME",
25695c635efSGarrett D'Amore 	"LIBRARY",
25795c635efSGarrett D'Amore 	"SYNOPSIS",
25895c635efSGarrett D'Amore 	"DESCRIPTION",
259260e9a87SYuri Pankov 	"CONTEXT",
26095c635efSGarrett D'Amore 	"IMPLEMENTATION NOTES",
26195c635efSGarrett D'Amore 	"RETURN VALUES",
26295c635efSGarrett D'Amore 	"ENVIRONMENT",
26395c635efSGarrett D'Amore 	"FILES",
26495c635efSGarrett D'Amore 	"EXIT STATUS",
26595c635efSGarrett D'Amore 	"EXAMPLES",
26695c635efSGarrett D'Amore 	"DIAGNOSTICS",
26795c635efSGarrett D'Amore 	"COMPATIBILITY",
26895c635efSGarrett D'Amore 	"ERRORS",
26995c635efSGarrett D'Amore 	"SEE ALSO",
27095c635efSGarrett D'Amore 	"STANDARDS",
27195c635efSGarrett D'Amore 	"HISTORY",
27295c635efSGarrett D'Amore 	"AUTHORS",
27395c635efSGarrett D'Amore 	"CAVEATS",
27495c635efSGarrett D'Amore 	"BUGS",
27595c635efSGarrett D'Amore 	"SECURITY CONSIDERATIONS",
27695c635efSGarrett D'Amore 	NULL
27795c635efSGarrett D'Amore };
27895c635efSGarrett D'Amore 
279260e9a87SYuri Pankov 
280260e9a87SYuri Pankov void
281*371584c2SYuri Pankov mdoc_node_validate(struct roff_man *mdoc)
28295c635efSGarrett D'Amore {
283*371584c2SYuri Pankov 	struct roff_node *n;
284*371584c2SYuri Pankov 	v_post *p;
28595c635efSGarrett D'Amore 
286*371584c2SYuri Pankov 	n = mdoc->last;
287*371584c2SYuri Pankov 	mdoc->last = mdoc->last->child;
288*371584c2SYuri Pankov 	while (mdoc->last != NULL) {
289*371584c2SYuri Pankov 		mdoc_node_validate(mdoc);
290*371584c2SYuri Pankov 		if (mdoc->last == n)
291*371584c2SYuri Pankov 			mdoc->last = mdoc->last->child;
292*371584c2SYuri Pankov 		else
293*371584c2SYuri Pankov 			mdoc->last = mdoc->last->next;
294*371584c2SYuri Pankov 	}
295*371584c2SYuri Pankov 
296*371584c2SYuri Pankov 	mdoc->last = n;
297*371584c2SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
29895c635efSGarrett D'Amore 	switch (n->type) {
299*371584c2SYuri Pankov 	case ROFFT_TEXT:
300260e9a87SYuri Pankov 		if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
301260e9a87SYuri Pankov 			check_text(mdoc, n->line, n->pos, n->string);
30295c635efSGarrett D'Amore 		break;
303*371584c2SYuri Pankov 	case ROFFT_EQN:
304*371584c2SYuri Pankov 	case ROFFT_TBL:
30595c635efSGarrett D'Amore 		break;
306*371584c2SYuri Pankov 	case ROFFT_ROOT:
307260e9a87SYuri Pankov 		post_root(mdoc);
30895c635efSGarrett D'Amore 		break;
30995c635efSGarrett D'Amore 	default:
310*371584c2SYuri Pankov 		check_args(mdoc, mdoc->last);
31195c635efSGarrett D'Amore 
312260e9a87SYuri Pankov 		/*
313260e9a87SYuri Pankov 		 * Closing delimiters are not special at the
314260e9a87SYuri Pankov 		 * beginning of a block, opening delimiters
315260e9a87SYuri Pankov 		 * are not special at the end.
316260e9a87SYuri Pankov 		 */
31795c635efSGarrett D'Amore 
318260e9a87SYuri Pankov 		if (n->child != NULL)
319260e9a87SYuri Pankov 			n->child->flags &= ~MDOC_DELIMC;
320260e9a87SYuri Pankov 		if (n->last != NULL)
321260e9a87SYuri Pankov 			n->last->flags &= ~MDOC_DELIMO;
32295c635efSGarrett D'Amore 
323260e9a87SYuri Pankov 		/* Call the macro's postprocessor. */
32495c635efSGarrett D'Amore 
325*371584c2SYuri Pankov 		p = mdoc_valids + n->tok;
326260e9a87SYuri Pankov 		if (*p)
327260e9a87SYuri Pankov 			(*p)(mdoc);
328*371584c2SYuri Pankov 		if (mdoc->last == n)
329*371584c2SYuri Pankov 			mdoc_state(mdoc, n);
330260e9a87SYuri Pankov 		break;
331260e9a87SYuri Pankov 	}
33295c635efSGarrett D'Amore }
33395c635efSGarrett D'Amore 
33495c635efSGarrett D'Amore static void
335*371584c2SYuri Pankov check_args(struct roff_man *mdoc, struct roff_node *n)
33695c635efSGarrett D'Amore {
33795c635efSGarrett D'Amore 	int		 i;
33895c635efSGarrett D'Amore 
33995c635efSGarrett D'Amore 	if (NULL == n->args)
34095c635efSGarrett D'Amore 		return;
34195c635efSGarrett D'Amore 
34295c635efSGarrett D'Amore 	assert(n->args->argc);
34395c635efSGarrett D'Amore 	for (i = 0; i < (int)n->args->argc; i++)
344698f87a4SGarrett D'Amore 		check_argv(mdoc, n, &n->args->argv[i]);
34595c635efSGarrett D'Amore }
34695c635efSGarrett D'Amore 
34795c635efSGarrett D'Amore static void
348*371584c2SYuri Pankov check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
34995c635efSGarrett D'Amore {
35095c635efSGarrett D'Amore 	int		 i;
35195c635efSGarrett D'Amore 
35295c635efSGarrett D'Amore 	for (i = 0; i < (int)v->sz; i++)
353698f87a4SGarrett D'Amore 		check_text(mdoc, v->line, v->pos, v->value[i]);
35495c635efSGarrett D'Amore }
35595c635efSGarrett D'Amore 
35695c635efSGarrett D'Amore static void
357*371584c2SYuri Pankov check_text(struct roff_man *mdoc, int ln, int pos, char *p)
35895c635efSGarrett D'Amore {
35995c635efSGarrett D'Amore 	char		*cp;
36095c635efSGarrett D'Amore 
361698f87a4SGarrett D'Amore 	if (MDOC_LITERAL & mdoc->flags)
36295c635efSGarrett D'Amore 		return;
36395c635efSGarrett D'Amore 
36495c635efSGarrett D'Amore 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
365260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
366260e9a87SYuri Pankov 		    ln, pos + (int)(p - cp), NULL);
36795c635efSGarrett D'Amore }
36895c635efSGarrett D'Amore 
369260e9a87SYuri Pankov static void
370*371584c2SYuri Pankov post_bl_norm(POST_ARGS)
37195c635efSGarrett D'Amore {
372*371584c2SYuri Pankov 	struct roff_node *n;
373260e9a87SYuri Pankov 	struct mdoc_argv *argv, *wa;
374260e9a87SYuri Pankov 	int		  i;
375260e9a87SYuri Pankov 	enum mdocargt	  mdoclt;
37695c635efSGarrett D'Amore 	enum mdoc_list	  lt;
37795c635efSGarrett D'Amore 
378*371584c2SYuri Pankov 	n = mdoc->last->parent;
379*371584c2SYuri Pankov 	n->norm->Bl.type = LIST__NONE;
38095c635efSGarrett D'Amore 
381260e9a87SYuri Pankov 	/*
38295c635efSGarrett D'Amore 	 * First figure out which kind of list to use: bind ourselves to
38395c635efSGarrett D'Amore 	 * the first mentioned list type and warn about any remaining
38495c635efSGarrett D'Amore 	 * ones.  If we find no list type, we default to LIST_item.
38595c635efSGarrett D'Amore 	 */
38695c635efSGarrett D'Amore 
387260e9a87SYuri Pankov 	wa = (n->args == NULL) ? NULL : n->args->argv;
388260e9a87SYuri Pankov 	mdoclt = MDOC_ARG_MAX;
38995c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
390260e9a87SYuri Pankov 		argv = n->args->argv + i;
39195c635efSGarrett D'Amore 		lt = LIST__NONE;
392260e9a87SYuri Pankov 		switch (argv->arg) {
39395c635efSGarrett D'Amore 		/* Set list types. */
394260e9a87SYuri Pankov 		case MDOC_Bullet:
39595c635efSGarrett D'Amore 			lt = LIST_bullet;
39695c635efSGarrett D'Amore 			break;
397260e9a87SYuri Pankov 		case MDOC_Dash:
39895c635efSGarrett D'Amore 			lt = LIST_dash;
39995c635efSGarrett D'Amore 			break;
400260e9a87SYuri Pankov 		case MDOC_Enum:
40195c635efSGarrett D'Amore 			lt = LIST_enum;
40295c635efSGarrett D'Amore 			break;
403260e9a87SYuri Pankov 		case MDOC_Hyphen:
40495c635efSGarrett D'Amore 			lt = LIST_hyphen;
40595c635efSGarrett D'Amore 			break;
406260e9a87SYuri Pankov 		case MDOC_Item:
40795c635efSGarrett D'Amore 			lt = LIST_item;
40895c635efSGarrett D'Amore 			break;
409260e9a87SYuri Pankov 		case MDOC_Tag:
41095c635efSGarrett D'Amore 			lt = LIST_tag;
41195c635efSGarrett D'Amore 			break;
412260e9a87SYuri Pankov 		case MDOC_Diag:
41395c635efSGarrett D'Amore 			lt = LIST_diag;
41495c635efSGarrett D'Amore 			break;
415260e9a87SYuri Pankov 		case MDOC_Hang:
41695c635efSGarrett D'Amore 			lt = LIST_hang;
41795c635efSGarrett D'Amore 			break;
418260e9a87SYuri Pankov 		case MDOC_Ohang:
41995c635efSGarrett D'Amore 			lt = LIST_ohang;
42095c635efSGarrett D'Amore 			break;
421260e9a87SYuri Pankov 		case MDOC_Inset:
42295c635efSGarrett D'Amore 			lt = LIST_inset;
42395c635efSGarrett D'Amore 			break;
424260e9a87SYuri Pankov 		case MDOC_Column:
42595c635efSGarrett D'Amore 			lt = LIST_column;
42695c635efSGarrett D'Amore 			break;
42795c635efSGarrett D'Amore 		/* Set list arguments. */
428260e9a87SYuri Pankov 		case MDOC_Compact:
429260e9a87SYuri Pankov 			if (n->norm->Bl.comp)
430260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
431260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
432260e9a87SYuri Pankov 				    argv->pos, "Bl -compact");
433260e9a87SYuri Pankov 			n->norm->Bl.comp = 1;
43495c635efSGarrett D'Amore 			break;
435260e9a87SYuri Pankov 		case MDOC_Width:
436260e9a87SYuri Pankov 			wa = argv;
437260e9a87SYuri Pankov 			if (0 == argv->sz) {
438260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
439260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
440260e9a87SYuri Pankov 				    argv->pos, "Bl -width");
441260e9a87SYuri Pankov 				n->norm->Bl.width = "0n";
44295c635efSGarrett D'Amore 				break;
44395c635efSGarrett D'Amore 			}
444260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.width)
445260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
446260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
447260e9a87SYuri Pankov 				    argv->pos, "Bl -width %s",
448260e9a87SYuri Pankov 				    argv->value[0]);
449260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
450260e9a87SYuri Pankov 			n->norm->Bl.width = argv->value[0];
45195c635efSGarrett D'Amore 			break;
452260e9a87SYuri Pankov 		case MDOC_Offset:
453260e9a87SYuri Pankov 			if (0 == argv->sz) {
454260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
455260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
456260e9a87SYuri Pankov 				    argv->pos, "Bl -offset");
45795c635efSGarrett D'Amore 				break;
45895c635efSGarrett D'Amore 			}
459260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.offs)
460260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
461260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
462260e9a87SYuri Pankov 				    argv->pos, "Bl -offset %s",
463260e9a87SYuri Pankov 				    argv->value[0]);
464260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
465260e9a87SYuri Pankov 			n->norm->Bl.offs = argv->value[0];
46695c635efSGarrett D'Amore 			break;
46795c635efSGarrett D'Amore 		default:
46895c635efSGarrett D'Amore 			continue;
46995c635efSGarrett D'Amore 		}
470260e9a87SYuri Pankov 		if (LIST__NONE == lt)
471260e9a87SYuri Pankov 			continue;
472260e9a87SYuri Pankov 		mdoclt = argv->arg;
47395c635efSGarrett D'Amore 
47495c635efSGarrett D'Amore 		/* Check: multiple list types. */
47595c635efSGarrett D'Amore 
476260e9a87SYuri Pankov 		if (LIST__NONE != n->norm->Bl.type) {
477260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_REP,
478260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
479260e9a87SYuri Pankov 			    "Bl -%s", mdoc_argnames[argv->arg]);
480260e9a87SYuri Pankov 			continue;
48195c635efSGarrett D'Amore 		}
48295c635efSGarrett D'Amore 
48395c635efSGarrett D'Amore 		/* The list type should come first. */
48495c635efSGarrett D'Amore 
485260e9a87SYuri Pankov 		if (n->norm->Bl.width ||
486260e9a87SYuri Pankov 		    n->norm->Bl.offs ||
487260e9a87SYuri Pankov 		    n->norm->Bl.comp)
488260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
489260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos, "Bl -%s",
490260e9a87SYuri Pankov 			    mdoc_argnames[n->args->argv[0].arg]);
491260e9a87SYuri Pankov 
492260e9a87SYuri Pankov 		n->norm->Bl.type = lt;
493260e9a87SYuri Pankov 		if (LIST_column == lt) {
494260e9a87SYuri Pankov 			n->norm->Bl.ncols = argv->sz;
495260e9a87SYuri Pankov 			n->norm->Bl.cols = (void *)argv->value;
496260e9a87SYuri Pankov 		}
49795c635efSGarrett D'Amore 	}
49895c635efSGarrett D'Amore 
49995c635efSGarrett D'Amore 	/* Allow lists to default to LIST_item. */
50095c635efSGarrett D'Amore 
50195c635efSGarrett D'Amore 	if (LIST__NONE == n->norm->Bl.type) {
502260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
503260e9a87SYuri Pankov 		    n->line, n->pos, "Bl");
50495c635efSGarrett D'Amore 		n->norm->Bl.type = LIST_item;
50595c635efSGarrett D'Amore 	}
50695c635efSGarrett D'Amore 
507260e9a87SYuri Pankov 	/*
50895c635efSGarrett D'Amore 	 * Validate the width field.  Some list types don't need width
50995c635efSGarrett D'Amore 	 * types and should be warned about them.  Others should have it
510698f87a4SGarrett D'Amore 	 * and must also be warned.  Yet others have a default and need
511698f87a4SGarrett D'Amore 	 * no warning.
51295c635efSGarrett D'Amore 	 */
51395c635efSGarrett D'Amore 
51495c635efSGarrett D'Amore 	switch (n->norm->Bl.type) {
515260e9a87SYuri Pankov 	case LIST_tag:
516698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
517260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
518260e9a87SYuri Pankov 			    n->line, n->pos, "Bl -tag");
51995c635efSGarrett D'Amore 		break;
520260e9a87SYuri Pankov 	case LIST_column:
521260e9a87SYuri Pankov 	case LIST_diag:
522260e9a87SYuri Pankov 	case LIST_ohang:
523260e9a87SYuri Pankov 	case LIST_inset:
524260e9a87SYuri Pankov 	case LIST_item:
52595c635efSGarrett D'Amore 		if (n->norm->Bl.width)
526260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
527260e9a87SYuri Pankov 			    wa->line, wa->pos, "Bl -%s",
528260e9a87SYuri Pankov 			    mdoc_argnames[mdoclt]);
52995c635efSGarrett D'Amore 		break;
530260e9a87SYuri Pankov 	case LIST_bullet:
531260e9a87SYuri Pankov 	case LIST_dash:
532260e9a87SYuri Pankov 	case LIST_hyphen:
533698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
534698f87a4SGarrett D'Amore 			n->norm->Bl.width = "2n";
535698f87a4SGarrett D'Amore 		break;
536260e9a87SYuri Pankov 	case LIST_enum:
537698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
538698f87a4SGarrett D'Amore 			n->norm->Bl.width = "3n";
539698f87a4SGarrett D'Amore 		break;
54095c635efSGarrett D'Amore 	default:
54195c635efSGarrett D'Amore 		break;
54295c635efSGarrett D'Amore 	}
54395c635efSGarrett D'Amore }
54495c635efSGarrett D'Amore 
545260e9a87SYuri Pankov static void
546*371584c2SYuri Pankov post_bd(POST_ARGS)
54795c635efSGarrett D'Amore {
548*371584c2SYuri Pankov 	struct roff_node *n;
549260e9a87SYuri Pankov 	struct mdoc_argv *argv;
550260e9a87SYuri Pankov 	int		  i;
551260e9a87SYuri Pankov 	enum mdoc_disp	  dt;
55295c635efSGarrett D'Amore 
553*371584c2SYuri Pankov 	n = mdoc->last;
55495c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
555260e9a87SYuri Pankov 		argv = n->args->argv + i;
55695c635efSGarrett D'Amore 		dt = DISP__NONE;
55795c635efSGarrett D'Amore 
558260e9a87SYuri Pankov 		switch (argv->arg) {
559260e9a87SYuri Pankov 		case MDOC_Centred:
560260e9a87SYuri Pankov 			dt = DISP_centered;
56195c635efSGarrett D'Amore 			break;
562260e9a87SYuri Pankov 		case MDOC_Ragged:
56395c635efSGarrett D'Amore 			dt = DISP_ragged;
56495c635efSGarrett D'Amore 			break;
565260e9a87SYuri Pankov 		case MDOC_Unfilled:
56695c635efSGarrett D'Amore 			dt = DISP_unfilled;
56795c635efSGarrett D'Amore 			break;
568260e9a87SYuri Pankov 		case MDOC_Filled:
56995c635efSGarrett D'Amore 			dt = DISP_filled;
57095c635efSGarrett D'Amore 			break;
571260e9a87SYuri Pankov 		case MDOC_Literal:
57295c635efSGarrett D'Amore 			dt = DISP_literal;
57395c635efSGarrett D'Amore 			break;
574260e9a87SYuri Pankov 		case MDOC_File:
575260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
576260e9a87SYuri Pankov 			    n->line, n->pos, NULL);
577260e9a87SYuri Pankov 			break;
578260e9a87SYuri Pankov 		case MDOC_Offset:
579260e9a87SYuri Pankov 			if (0 == argv->sz) {
580260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
581260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
582260e9a87SYuri Pankov 				    argv->pos, "Bd -offset");
58395c635efSGarrett D'Amore 				break;
58495c635efSGarrett D'Amore 			}
585260e9a87SYuri Pankov 			if (NULL != n->norm->Bd.offs)
586260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
587260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
588260e9a87SYuri Pankov 				    argv->pos, "Bd -offset %s",
589260e9a87SYuri Pankov 				    argv->value[0]);
590260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
591260e9a87SYuri Pankov 			n->norm->Bd.offs = argv->value[0];
59295c635efSGarrett D'Amore 			break;
593260e9a87SYuri Pankov 		case MDOC_Compact:
594260e9a87SYuri Pankov 			if (n->norm->Bd.comp)
595260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
596260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
597260e9a87SYuri Pankov 				    argv->pos, "Bd -compact");
598260e9a87SYuri Pankov 			n->norm->Bd.comp = 1;
59995c635efSGarrett D'Amore 			break;
60095c635efSGarrett D'Amore 		default:
60195c635efSGarrett D'Amore 			abort();
60295c635efSGarrett D'Amore 		}
603260e9a87SYuri Pankov 		if (DISP__NONE == dt)
604260e9a87SYuri Pankov 			continue;
60595c635efSGarrett D'Amore 
606260e9a87SYuri Pankov 		if (DISP__NONE == n->norm->Bd.type)
60795c635efSGarrett D'Amore 			n->norm->Bd.type = dt;
608260e9a87SYuri Pankov 		else
609260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BD_REP,
610260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
611260e9a87SYuri Pankov 			    "Bd -%s", mdoc_argnames[argv->arg]);
61295c635efSGarrett D'Amore 	}
61395c635efSGarrett D'Amore 
61495c635efSGarrett D'Amore 	if (DISP__NONE == n->norm->Bd.type) {
615260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
616260e9a87SYuri Pankov 		    n->line, n->pos, "Bd");
61795c635efSGarrett D'Amore 		n->norm->Bd.type = DISP_ragged;
61895c635efSGarrett D'Amore 	}
61995c635efSGarrett D'Amore }
62095c635efSGarrett D'Amore 
621260e9a87SYuri Pankov static void
622*371584c2SYuri Pankov post_an_norm(POST_ARGS)
62395c635efSGarrett D'Amore {
624*371584c2SYuri Pankov 	struct roff_node *n;
625260e9a87SYuri Pankov 	struct mdoc_argv *argv;
626260e9a87SYuri Pankov 	size_t	 i;
62795c635efSGarrett D'Amore 
628*371584c2SYuri Pankov 	n = mdoc->last;
629260e9a87SYuri Pankov 	if (n->args == NULL)
630260e9a87SYuri Pankov 		return;
63195c635efSGarrett D'Amore 
632260e9a87SYuri Pankov 	for (i = 1; i < n->args->argc; i++) {
633260e9a87SYuri Pankov 		argv = n->args->argv + i;
634260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AN_REP,
635260e9a87SYuri Pankov 		    mdoc->parse, argv->line, argv->pos,
636260e9a87SYuri Pankov 		    "An -%s", mdoc_argnames[argv->arg]);
637260e9a87SYuri Pankov 	}
638260e9a87SYuri Pankov 
639260e9a87SYuri Pankov 	argv = n->args->argv;
640260e9a87SYuri Pankov 	if (argv->arg == MDOC_Split)
64195c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_split;
642260e9a87SYuri Pankov 	else if (argv->arg == MDOC_Nosplit)
64395c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_nosplit;
64495c635efSGarrett D'Amore 	else
64595c635efSGarrett D'Amore 		abort();
64695c635efSGarrett D'Amore }
64795c635efSGarrett D'Amore 
648260e9a87SYuri Pankov static void
649*371584c2SYuri Pankov post_std(POST_ARGS)
65095c635efSGarrett D'Amore {
651*371584c2SYuri Pankov 	struct roff_node *n;
65295c635efSGarrett D'Amore 
653*371584c2SYuri Pankov 	n = mdoc->last;
654*371584c2SYuri Pankov 	if (n->args && n->args->argc == 1)
655*371584c2SYuri Pankov 		if (n->args->argv[0].arg == MDOC_Std)
656260e9a87SYuri Pankov 			return;
65795c635efSGarrett D'Amore 
658260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
659260e9a87SYuri Pankov 	    n->line, n->pos, mdoc_macronames[n->tok]);
66095c635efSGarrett D'Amore }
66195c635efSGarrett D'Amore 
662260e9a87SYuri Pankov static void
663*371584c2SYuri Pankov post_obsolete(POST_ARGS)
66495c635efSGarrett D'Amore {
665*371584c2SYuri Pankov 	struct roff_node *n;
66695c635efSGarrett D'Amore 
667*371584c2SYuri Pankov 	n = mdoc->last;
668*371584c2SYuri Pankov 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
669260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
670260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
671260e9a87SYuri Pankov }
67295c635efSGarrett D'Amore 
673260e9a87SYuri Pankov static void
67495c635efSGarrett D'Amore post_bf(POST_ARGS)
67595c635efSGarrett D'Amore {
676*371584c2SYuri Pankov 	struct roff_node *np, *nch;
67795c635efSGarrett D'Amore 
67895c635efSGarrett D'Amore 	/*
67995c635efSGarrett D'Amore 	 * Unlike other data pointers, these are "housed" by the HEAD
68095c635efSGarrett D'Amore 	 * element, which contains the goods.
68195c635efSGarrett D'Amore 	 */
68295c635efSGarrett D'Amore 
68395c635efSGarrett D'Amore 	np = mdoc->last;
684*371584c2SYuri Pankov 	if (np->type != ROFFT_HEAD)
685260e9a87SYuri Pankov 		return;
686260e9a87SYuri Pankov 
687*371584c2SYuri Pankov 	assert(np->parent->type == ROFFT_BLOCK);
688*371584c2SYuri Pankov 	assert(np->parent->tok == MDOC_Bf);
68995c635efSGarrett D'Amore 
690260e9a87SYuri Pankov 	/* Check the number of arguments. */
69195c635efSGarrett D'Amore 
692260e9a87SYuri Pankov 	nch = np->child;
693*371584c2SYuri Pankov 	if (np->parent->args == NULL) {
694*371584c2SYuri Pankov 		if (nch == NULL) {
695260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
696260e9a87SYuri Pankov 			    np->line, np->pos, "Bf");
697260e9a87SYuri Pankov 			return;
698260e9a87SYuri Pankov 		}
699260e9a87SYuri Pankov 		nch = nch->next;
70095c635efSGarrett D'Amore 	}
701*371584c2SYuri Pankov 	if (nch != NULL)
702260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
703260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bf ... %s", nch->string);
70495c635efSGarrett D'Amore 
70595c635efSGarrett D'Amore 	/* Extract argument into data. */
706260e9a87SYuri Pankov 
707*371584c2SYuri Pankov 	if (np->parent->args != NULL) {
708*371584c2SYuri Pankov 		switch (np->parent->args->argv[0].arg) {
709*371584c2SYuri Pankov 		case MDOC_Emphasis:
71095c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Em;
711*371584c2SYuri Pankov 			break;
712*371584c2SYuri Pankov 		case MDOC_Literal:
71395c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Li;
714*371584c2SYuri Pankov 			break;
715*371584c2SYuri Pankov 		case MDOC_Symbolic:
71695c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Sy;
717*371584c2SYuri Pankov 			break;
718*371584c2SYuri Pankov 		default:
71995c635efSGarrett D'Amore 			abort();
720*371584c2SYuri Pankov 		}
721260e9a87SYuri Pankov 		return;
72295c635efSGarrett D'Amore 	}
72395c635efSGarrett D'Amore 
72495c635efSGarrett D'Amore 	/* Extract parameter into data. */
72595c635efSGarrett D'Amore 
726*371584c2SYuri Pankov 	if ( ! strcmp(np->child->string, "Em"))
72795c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Em;
728*371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Li"))
72995c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Li;
730*371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Sy"))
73195c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Sy;
732260e9a87SYuri Pankov 	else
733260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
734260e9a87SYuri Pankov 		    np->child->line, np->child->pos,
735260e9a87SYuri Pankov 		    "Bf %s", np->child->string);
73695c635efSGarrett D'Amore }
73795c635efSGarrett D'Amore 
738260e9a87SYuri Pankov static void
73995c635efSGarrett D'Amore post_lb(POST_ARGS)
74095c635efSGarrett D'Amore {
741*371584c2SYuri Pankov 	struct roff_node	*n;
742260e9a87SYuri Pankov 	const char		*stdlibname;
743260e9a87SYuri Pankov 	char			*libname;
74495c635efSGarrett D'Amore 
745260e9a87SYuri Pankov 	n = mdoc->last->child;
746*371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
74795c635efSGarrett D'Amore 
748260e9a87SYuri Pankov 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
749260e9a87SYuri Pankov 		mandoc_asprintf(&libname,
750260e9a87SYuri Pankov 		    "library \\(Lq%s\\(Rq", n->string);
751260e9a87SYuri Pankov 	else
752260e9a87SYuri Pankov 		libname = mandoc_strdup(stdlibname);
75395c635efSGarrett D'Amore 
754260e9a87SYuri Pankov 	free(n->string);
755260e9a87SYuri Pankov 	n->string = libname;
756260e9a87SYuri Pankov }
75795c635efSGarrett D'Amore 
758260e9a87SYuri Pankov static void
759260e9a87SYuri Pankov post_eoln(POST_ARGS)
760260e9a87SYuri Pankov {
761*371584c2SYuri Pankov 	const struct roff_node *n;
76295c635efSGarrett D'Amore 
763260e9a87SYuri Pankov 	n = mdoc->last;
764*371584c2SYuri Pankov 	if (n->child != NULL)
765260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
766260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
767260e9a87SYuri Pankov 		    "%s %s", mdoc_macronames[n->tok],
768260e9a87SYuri Pankov 		    n->child->string);
769260e9a87SYuri Pankov }
77095c635efSGarrett D'Amore 
771260e9a87SYuri Pankov static void
772260e9a87SYuri Pankov post_fname(POST_ARGS)
773260e9a87SYuri Pankov {
774*371584c2SYuri Pankov 	const struct roff_node	*n;
775260e9a87SYuri Pankov 	const char		*cp;
776260e9a87SYuri Pankov 	size_t			 pos;
77795c635efSGarrett D'Amore 
778260e9a87SYuri Pankov 	n = mdoc->last->child;
779260e9a87SYuri Pankov 	pos = strcspn(n->string, "()");
780260e9a87SYuri Pankov 	cp = n->string + pos;
781260e9a87SYuri Pankov 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
782260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
783260e9a87SYuri Pankov 		    n->line, n->pos + pos, n->string);
78495c635efSGarrett D'Amore }
78595c635efSGarrett D'Amore 
786260e9a87SYuri Pankov static void
787260e9a87SYuri Pankov post_fn(POST_ARGS)
78895c635efSGarrett D'Amore {
78995c635efSGarrett D'Amore 
790260e9a87SYuri Pankov 	post_fname(mdoc);
791260e9a87SYuri Pankov 	post_fa(mdoc);
79295c635efSGarrett D'Amore }
79395c635efSGarrett D'Amore 
794260e9a87SYuri Pankov static void
795260e9a87SYuri Pankov post_fo(POST_ARGS)
796260e9a87SYuri Pankov {
797*371584c2SYuri Pankov 	const struct roff_node	*n;
79895c635efSGarrett D'Amore 
799260e9a87SYuri Pankov 	n = mdoc->last;
800260e9a87SYuri Pankov 
801*371584c2SYuri Pankov 	if (n->type != ROFFT_HEAD)
802260e9a87SYuri Pankov 		return;
803260e9a87SYuri Pankov 
804260e9a87SYuri Pankov 	if (n->child == NULL) {
805260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
806260e9a87SYuri Pankov 		    n->line, n->pos, "Fo");
807260e9a87SYuri Pankov 		return;
808260e9a87SYuri Pankov 	}
809260e9a87SYuri Pankov 	if (n->child != n->last) {
810260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
811260e9a87SYuri Pankov 		    n->child->next->line, n->child->next->pos,
812260e9a87SYuri Pankov 		    "Fo ... %s", n->child->next->string);
813260e9a87SYuri Pankov 		while (n->child != n->last)
814*371584c2SYuri Pankov 			roff_node_delete(mdoc, n->last);
815260e9a87SYuri Pankov 	}
816260e9a87SYuri Pankov 
817260e9a87SYuri Pankov 	post_fname(mdoc);
818260e9a87SYuri Pankov }
819260e9a87SYuri Pankov 
820260e9a87SYuri Pankov static void
821260e9a87SYuri Pankov post_fa(POST_ARGS)
822260e9a87SYuri Pankov {
823*371584c2SYuri Pankov 	const struct roff_node *n;
824260e9a87SYuri Pankov 	const char *cp;
825260e9a87SYuri Pankov 
826260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
827260e9a87SYuri Pankov 		for (cp = n->string; *cp != '\0'; cp++) {
828260e9a87SYuri Pankov 			/* Ignore callbacks and alterations. */
829260e9a87SYuri Pankov 			if (*cp == '(' || *cp == '{')
830260e9a87SYuri Pankov 				break;
831260e9a87SYuri Pankov 			if (*cp != ',')
832260e9a87SYuri Pankov 				continue;
833260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
834260e9a87SYuri Pankov 			    n->line, n->pos + (cp - n->string),
835260e9a87SYuri Pankov 			    n->string);
836260e9a87SYuri Pankov 			break;
837260e9a87SYuri Pankov 		}
838260e9a87SYuri Pankov 	}
839260e9a87SYuri Pankov }
840260e9a87SYuri Pankov 
841260e9a87SYuri Pankov static void
84295c635efSGarrett D'Amore post_nm(POST_ARGS)
84395c635efSGarrett D'Amore {
844*371584c2SYuri Pankov 	struct roff_node	*n;
845260e9a87SYuri Pankov 
846260e9a87SYuri Pankov 	n = mdoc->last;
847260e9a87SYuri Pankov 
848260e9a87SYuri Pankov 	if (n->last != NULL &&
849260e9a87SYuri Pankov 	    (n->last->tok == MDOC_Pp ||
850260e9a87SYuri Pankov 	     n->last->tok == MDOC_Lp))
851260e9a87SYuri Pankov 		mdoc_node_relink(mdoc, n->last);
85295c635efSGarrett D'Amore 
853*371584c2SYuri Pankov 	if (mdoc->meta.name != NULL)
854260e9a87SYuri Pankov 		return;
85595c635efSGarrett D'Amore 
856*371584c2SYuri Pankov 	deroff(&mdoc->meta.name, n);
85795c635efSGarrett D'Amore 
858*371584c2SYuri Pankov 	if (mdoc->meta.name == NULL)
859260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
860260e9a87SYuri Pankov 		    n->line, n->pos, "Nm");
86195c635efSGarrett D'Amore }
86295c635efSGarrett D'Amore 
863260e9a87SYuri Pankov static void
864260e9a87SYuri Pankov post_nd(POST_ARGS)
865260e9a87SYuri Pankov {
866*371584c2SYuri Pankov 	struct roff_node	*n;
867260e9a87SYuri Pankov 
868260e9a87SYuri Pankov 	n = mdoc->last;
869260e9a87SYuri Pankov 
870*371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
871260e9a87SYuri Pankov 		return;
872260e9a87SYuri Pankov 
873260e9a87SYuri Pankov 	if (n->child == NULL)
874260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
875260e9a87SYuri Pankov 		    n->line, n->pos, "Nd");
876260e9a87SYuri Pankov 
877260e9a87SYuri Pankov 	post_hyph(mdoc);
878260e9a87SYuri Pankov }
879260e9a87SYuri Pankov 
880260e9a87SYuri Pankov static void
881*371584c2SYuri Pankov post_display(POST_ARGS)
882260e9a87SYuri Pankov {
883*371584c2SYuri Pankov 	struct roff_node *n, *np;
884260e9a87SYuri Pankov 
885260e9a87SYuri Pankov 	n = mdoc->last;
886*371584c2SYuri Pankov 	switch (n->type) {
887*371584c2SYuri Pankov 	case ROFFT_BODY:
888*371584c2SYuri Pankov 		if (n->end != ENDBODY_NOT)
889*371584c2SYuri Pankov 			break;
890*371584c2SYuri Pankov 		if (n->child == NULL)
891*371584c2SYuri Pankov 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
892*371584c2SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
893*371584c2SYuri Pankov 		else if (n->tok == MDOC_D1)
894*371584c2SYuri Pankov 			post_hyph(mdoc);
895*371584c2SYuri Pankov 		break;
896*371584c2SYuri Pankov 	case ROFFT_BLOCK:
897*371584c2SYuri Pankov 		if (n->tok == MDOC_Bd) {
898*371584c2SYuri Pankov 			if (n->args == NULL) {
899*371584c2SYuri Pankov 				mandoc_msg(MANDOCERR_BD_NOARG,
900*371584c2SYuri Pankov 				    mdoc->parse, n->line, n->pos, "Bd");
901*371584c2SYuri Pankov 				mdoc->next = ROFF_NEXT_SIBLING;
902*371584c2SYuri Pankov 				while (n->body->child != NULL)
903*371584c2SYuri Pankov 					mdoc_node_relink(mdoc,
904*371584c2SYuri Pankov 					    n->body->child);
905*371584c2SYuri Pankov 				roff_node_delete(mdoc, n);
906*371584c2SYuri Pankov 				break;
907*371584c2SYuri Pankov 			}
908*371584c2SYuri Pankov 			post_bd(mdoc);
909*371584c2SYuri Pankov 			post_prevpar(mdoc);
910*371584c2SYuri Pankov 		}
911*371584c2SYuri Pankov 		for (np = n->parent; np != NULL; np = np->parent) {
912*371584c2SYuri Pankov 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
913*371584c2SYuri Pankov 				mandoc_vmsg(MANDOCERR_BD_NEST,
914*371584c2SYuri Pankov 				    mdoc->parse, n->line, n->pos,
915*371584c2SYuri Pankov 				    "%s in Bd", mdoc_macronames[n->tok]);
916*371584c2SYuri Pankov 				break;
917*371584c2SYuri Pankov 			}
918*371584c2SYuri Pankov 		}
919*371584c2SYuri Pankov 		break;
920*371584c2SYuri Pankov 	default:
921*371584c2SYuri Pankov 		break;
922*371584c2SYuri Pankov 	}
92395c635efSGarrett D'Amore }
92495c635efSGarrett D'Amore 
925260e9a87SYuri Pankov static void
92695c635efSGarrett D'Amore post_defaults(POST_ARGS)
92795c635efSGarrett D'Amore {
928*371584c2SYuri Pankov 	struct roff_node *nn;
92995c635efSGarrett D'Amore 
93095c635efSGarrett D'Amore 	/*
93195c635efSGarrett D'Amore 	 * The `Ar' defaults to "file ..." if no value is provided as an
93295c635efSGarrett D'Amore 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
93395c635efSGarrett D'Amore 	 * gets an empty string.
93495c635efSGarrett D'Amore 	 */
93595c635efSGarrett D'Amore 
936*371584c2SYuri Pankov 	if (mdoc->last->child != NULL)
937260e9a87SYuri Pankov 		return;
938260e9a87SYuri Pankov 
93995c635efSGarrett D'Amore 	nn = mdoc->last;
94095c635efSGarrett D'Amore 
94195c635efSGarrett D'Amore 	switch (nn->tok) {
942260e9a87SYuri Pankov 	case MDOC_Ar:
943*371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
944*371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
945*371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
94695c635efSGarrett D'Amore 		break;
947260e9a87SYuri Pankov 	case MDOC_Pa:
948260e9a87SYuri Pankov 	case MDOC_Mt:
949*371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
950*371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
95195c635efSGarrett D'Amore 		break;
95295c635efSGarrett D'Amore 	default:
95395c635efSGarrett D'Amore 		abort();
954260e9a87SYuri Pankov 	}
95595c635efSGarrett D'Amore 	mdoc->last = nn;
95695c635efSGarrett D'Amore }
95795c635efSGarrett D'Amore 
958260e9a87SYuri Pankov static void
95995c635efSGarrett D'Amore post_at(POST_ARGS)
96095c635efSGarrett D'Amore {
961*371584c2SYuri Pankov 	struct roff_node	*n;
962260e9a87SYuri Pankov 	const char		*std_att;
963260e9a87SYuri Pankov 	char			*att;
964260e9a87SYuri Pankov 
965260e9a87SYuri Pankov 	n = mdoc->last;
966260e9a87SYuri Pankov 	if (n->child == NULL) {
967*371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
968*371584c2SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
969260e9a87SYuri Pankov 		mdoc->last = n;
970260e9a87SYuri Pankov 		return;
971260e9a87SYuri Pankov 	}
97295c635efSGarrett D'Amore 
97395c635efSGarrett D'Amore 	/*
97495c635efSGarrett D'Amore 	 * If we have a child, look it up in the standard keys.  If a
97595c635efSGarrett D'Amore 	 * key exist, use that instead of the child; if it doesn't,
97695c635efSGarrett D'Amore 	 * prefix "AT&T UNIX " to the existing data.
97795c635efSGarrett D'Amore 	 */
97895c635efSGarrett D'Amore 
979260e9a87SYuri Pankov 	n = n->child;
980*371584c2SYuri Pankov 	assert(n->type == ROFFT_TEXT);
981*371584c2SYuri Pankov 	if ((std_att = mdoc_a2att(n->string)) == NULL) {
982260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
983260e9a87SYuri Pankov 		    n->line, n->pos, "At %s", n->string);
984260e9a87SYuri Pankov 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
985260e9a87SYuri Pankov 	} else
986260e9a87SYuri Pankov 		att = mandoc_strdup(std_att);
98795c635efSGarrett D'Amore 
988260e9a87SYuri Pankov 	free(n->string);
989260e9a87SYuri Pankov 	n->string = att;
99095c635efSGarrett D'Amore }
99195c635efSGarrett D'Amore 
992260e9a87SYuri Pankov static void
99395c635efSGarrett D'Amore post_an(POST_ARGS)
99495c635efSGarrett D'Amore {
995*371584c2SYuri Pankov 	struct roff_node *np, *nch;
996*371584c2SYuri Pankov 
997*371584c2SYuri Pankov 	post_an_norm(mdoc);
99895c635efSGarrett D'Amore 
99995c635efSGarrett D'Amore 	np = mdoc->last;
1000260e9a87SYuri Pankov 	nch = np->child;
1001260e9a87SYuri Pankov 	if (np->norm->An.auth == AUTH__NONE) {
1002260e9a87SYuri Pankov 		if (nch == NULL)
1003260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1004260e9a87SYuri Pankov 			    np->line, np->pos, "An");
1005260e9a87SYuri Pankov 	} else if (nch != NULL)
1006260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1007260e9a87SYuri Pankov 		    nch->line, nch->pos, "An ... %s", nch->string);
1008260e9a87SYuri Pankov }
1009260e9a87SYuri Pankov 
1010260e9a87SYuri Pankov static void
1011260e9a87SYuri Pankov post_en(POST_ARGS)
1012260e9a87SYuri Pankov {
101395c635efSGarrett D'Amore 
1014*371584c2SYuri Pankov 	post_obsolete(mdoc);
1015*371584c2SYuri Pankov 	if (mdoc->last->type == ROFFT_BLOCK)
1016260e9a87SYuri Pankov 		mdoc->last->norm->Es = mdoc->last_es;
101795c635efSGarrett D'Amore }
101895c635efSGarrett D'Amore 
1019260e9a87SYuri Pankov static void
1020260e9a87SYuri Pankov post_es(POST_ARGS)
1021260e9a87SYuri Pankov {
102295c635efSGarrett D'Amore 
1023*371584c2SYuri Pankov 	post_obsolete(mdoc);
1024260e9a87SYuri Pankov 	mdoc->last_es = mdoc->last;
1025260e9a87SYuri Pankov }
1026260e9a87SYuri Pankov 
1027260e9a87SYuri Pankov static void
102895c635efSGarrett D'Amore post_it(POST_ARGS)
102995c635efSGarrett D'Amore {
1030*371584c2SYuri Pankov 	struct roff_node *nbl, *nit, *nch;
103195c635efSGarrett D'Amore 	int		  i, cols;
103295c635efSGarrett D'Amore 	enum mdoc_list	  lt;
1033*371584c2SYuri Pankov 
1034*371584c2SYuri Pankov 	post_prevpar(mdoc);
103595c635efSGarrett D'Amore 
1036260e9a87SYuri Pankov 	nit = mdoc->last;
1037*371584c2SYuri Pankov 	if (nit->type != ROFFT_BLOCK)
1038260e9a87SYuri Pankov 		return;
103995c635efSGarrett D'Amore 
1040260e9a87SYuri Pankov 	nbl = nit->parent->parent;
1041260e9a87SYuri Pankov 	lt = nbl->norm->Bl.type;
104295c635efSGarrett D'Amore 
104395c635efSGarrett D'Amore 	switch (lt) {
1044260e9a87SYuri Pankov 	case LIST_tag:
1045260e9a87SYuri Pankov 	case LIST_hang:
1046260e9a87SYuri Pankov 	case LIST_ohang:
1047260e9a87SYuri Pankov 	case LIST_inset:
1048260e9a87SYuri Pankov 	case LIST_diag:
1049260e9a87SYuri Pankov 		if (nit->head->child == NULL)
1050260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1051260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1052260e9a87SYuri Pankov 			    "Bl -%s It",
1053260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
105495c635efSGarrett D'Amore 		break;
1055260e9a87SYuri Pankov 	case LIST_bullet:
1056260e9a87SYuri Pankov 	case LIST_dash:
1057260e9a87SYuri Pankov 	case LIST_enum:
1058260e9a87SYuri Pankov 	case LIST_hyphen:
1059260e9a87SYuri Pankov 		if (nit->body == NULL || nit->body->child == NULL)
1060260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1061260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1062260e9a87SYuri Pankov 			    "Bl -%s It",
1063260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
106495c635efSGarrett D'Amore 		/* FALLTHROUGH */
1065260e9a87SYuri Pankov 	case LIST_item:
1066260e9a87SYuri Pankov 		if (nit->head->child != NULL)
1067260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1068260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1069260e9a87SYuri Pankov 			    "It %s", nit->head->child->string);
107095c635efSGarrett D'Amore 		break;
1071260e9a87SYuri Pankov 	case LIST_column:
1072260e9a87SYuri Pankov 		cols = (int)nbl->norm->Bl.ncols;
107395c635efSGarrett D'Amore 
1074260e9a87SYuri Pankov 		assert(nit->head->child == NULL);
107595c635efSGarrett D'Amore 
1076*371584c2SYuri Pankov 		i = 0;
1077*371584c2SYuri Pankov 		for (nch = nit->child; nch != NULL; nch = nch->next)
1078*371584c2SYuri Pankov 			if (nch->type == ROFFT_BODY)
107995c635efSGarrett D'Amore 				i++;
108095c635efSGarrett D'Amore 
1081260e9a87SYuri Pankov 		if (i < cols || i > cols + 1)
1082260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_COL,
1083260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1084260e9a87SYuri Pankov 			    "%d columns, %d cells", cols, i);
108595c635efSGarrett D'Amore 		break;
1086260e9a87SYuri Pankov 	default:
1087260e9a87SYuri Pankov 		abort();
108895c635efSGarrett D'Amore 	}
108995c635efSGarrett D'Amore }
109095c635efSGarrett D'Amore 
1091260e9a87SYuri Pankov static void
1092260e9a87SYuri Pankov post_bl_block(POST_ARGS)
109395c635efSGarrett D'Amore {
1094*371584c2SYuri Pankov 	struct roff_node *n, *ni, *nc;
1095*371584c2SYuri Pankov 
1096*371584c2SYuri Pankov 	post_prevpar(mdoc);
109795c635efSGarrett D'Amore 
109895c635efSGarrett D'Amore 	/*
109995c635efSGarrett D'Amore 	 * These are fairly complicated, so we've broken them into two
110095c635efSGarrett D'Amore 	 * functions.  post_bl_block_tag() is called when a -tag is
110195c635efSGarrett D'Amore 	 * specified, but no -width (it must be guessed).  The second
110295c635efSGarrett D'Amore 	 * when a -width is specified (macro indicators must be
110395c635efSGarrett D'Amore 	 * rewritten into real lengths).
110495c635efSGarrett D'Amore 	 */
110595c635efSGarrett D'Amore 
110695c635efSGarrett D'Amore 	n = mdoc->last;
110795c635efSGarrett D'Amore 
1108*371584c2SYuri Pankov 	if (n->norm->Bl.type == LIST_tag &&
1109*371584c2SYuri Pankov 	    n->norm->Bl.width == NULL) {
1110260e9a87SYuri Pankov 		post_bl_block_tag(mdoc);
1111*371584c2SYuri Pankov 		assert(n->norm->Bl.width != NULL);
1112698f87a4SGarrett D'Amore 	}
111395c635efSGarrett D'Amore 
1114*371584c2SYuri Pankov 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1115*371584c2SYuri Pankov 		if (ni->body == NULL)
1116698f87a4SGarrett D'Amore 			continue;
1117698f87a4SGarrett D'Amore 		nc = ni->body->last;
1118*371584c2SYuri Pankov 		while (nc != NULL) {
1119698f87a4SGarrett D'Amore 			switch (nc->tok) {
1120260e9a87SYuri Pankov 			case MDOC_Pp:
1121260e9a87SYuri Pankov 			case MDOC_Lp:
1122260e9a87SYuri Pankov 			case MDOC_br:
1123698f87a4SGarrett D'Amore 				break;
1124698f87a4SGarrett D'Amore 			default:
1125698f87a4SGarrett D'Amore 				nc = NULL;
1126698f87a4SGarrett D'Amore 				continue;
1127698f87a4SGarrett D'Amore 			}
1128*371584c2SYuri Pankov 			if (ni->next == NULL) {
1129260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_PAR_MOVE,
1130260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1131260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1132260e9a87SYuri Pankov 				mdoc_node_relink(mdoc, nc);
1133*371584c2SYuri Pankov 			} else if (n->norm->Bl.comp == 0 &&
1134*371584c2SYuri Pankov 			    n->norm->Bl.type != LIST_column) {
1135260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1136260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1137260e9a87SYuri Pankov 				    "%s before It",
1138260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1139*371584c2SYuri Pankov 				roff_node_delete(mdoc, nc);
1140698f87a4SGarrett D'Amore 			} else
1141698f87a4SGarrett D'Amore 				break;
1142698f87a4SGarrett D'Amore 			nc = ni->body->last;
1143698f87a4SGarrett D'Amore 		}
1144698f87a4SGarrett D'Amore 	}
114595c635efSGarrett D'Amore }
114695c635efSGarrett D'Amore 
1147260e9a87SYuri Pankov /*
1148260e9a87SYuri Pankov  * If the argument of -offset or -width is a macro,
1149260e9a87SYuri Pankov  * replace it with the associated default width.
1150260e9a87SYuri Pankov  */
1151260e9a87SYuri Pankov void
1152260e9a87SYuri Pankov rewrite_macro2len(char **arg)
115395c635efSGarrett D'Amore {
115495c635efSGarrett D'Amore 	size_t		  width;
1155*371584c2SYuri Pankov 	int		  tok;
115695c635efSGarrett D'Amore 
1157260e9a87SYuri Pankov 	if (*arg == NULL)
1158260e9a87SYuri Pankov 		return;
1159260e9a87SYuri Pankov 	else if ( ! strcmp(*arg, "Ds"))
116095c635efSGarrett D'Amore 		width = 6;
1161*371584c2SYuri Pankov 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1162260e9a87SYuri Pankov 		return;
1163260e9a87SYuri Pankov 	else
1164260e9a87SYuri Pankov 		width = macro2len(tok);
116595c635efSGarrett D'Amore 
1166260e9a87SYuri Pankov 	free(*arg);
1167260e9a87SYuri Pankov 	mandoc_asprintf(arg, "%zun", width);
116895c635efSGarrett D'Amore }
116995c635efSGarrett D'Amore 
1170260e9a87SYuri Pankov static void
117195c635efSGarrett D'Amore post_bl_block_tag(POST_ARGS)
117295c635efSGarrett D'Amore {
1173*371584c2SYuri Pankov 	struct roff_node *n, *nn;
117495c635efSGarrett D'Amore 	size_t		  sz, ssz;
117595c635efSGarrett D'Amore 	int		  i;
1176260e9a87SYuri Pankov 	char		  buf[24];
117795c635efSGarrett D'Amore 
117895c635efSGarrett D'Amore 	/*
117995c635efSGarrett D'Amore 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
118095c635efSGarrett D'Amore 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
118195c635efSGarrett D'Amore 	 * ONLY if the -width argument has NOT been provided.  See
1182260e9a87SYuri Pankov 	 * rewrite_macro2len() for converting the -width string.
118395c635efSGarrett D'Amore 	 */
118495c635efSGarrett D'Amore 
118595c635efSGarrett D'Amore 	sz = 10;
118695c635efSGarrett D'Amore 	n = mdoc->last;
118795c635efSGarrett D'Amore 
1188*371584c2SYuri Pankov 	for (nn = n->body->child; nn != NULL; nn = nn->next) {
1189*371584c2SYuri Pankov 		if (nn->tok != MDOC_It)
119095c635efSGarrett D'Amore 			continue;
119195c635efSGarrett D'Amore 
1192*371584c2SYuri Pankov 		assert(nn->type == ROFFT_BLOCK);
119395c635efSGarrett D'Amore 		nn = nn->head->child;
119495c635efSGarrett D'Amore 
119595c635efSGarrett D'Amore 		if (nn == NULL)
119695c635efSGarrett D'Amore 			break;
119795c635efSGarrett D'Amore 
1198*371584c2SYuri Pankov 		if (nn->type == ROFFT_TEXT) {
119995c635efSGarrett D'Amore 			sz = strlen(nn->string) + 1;
120095c635efSGarrett D'Amore 			break;
120195c635efSGarrett D'Amore 		}
120295c635efSGarrett D'Amore 
120395c635efSGarrett D'Amore 		if (0 != (ssz = macro2len(nn->tok)))
120495c635efSGarrett D'Amore 			sz = ssz;
120595c635efSGarrett D'Amore 
120695c635efSGarrett D'Amore 		break;
1207260e9a87SYuri Pankov 	}
120895c635efSGarrett D'Amore 
120995c635efSGarrett D'Amore 	/* Defaults to ten ens. */
121095c635efSGarrett D'Amore 
1211260e9a87SYuri Pankov 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
121295c635efSGarrett D'Amore 
121395c635efSGarrett D'Amore 	/*
121495c635efSGarrett D'Amore 	 * We have to dynamically add this to the macro's argument list.
121595c635efSGarrett D'Amore 	 * We're guaranteed that a MDOC_Width doesn't already exist.
121695c635efSGarrett D'Amore 	 */
121795c635efSGarrett D'Amore 
1218*371584c2SYuri Pankov 	assert(n->args != NULL);
121995c635efSGarrett D'Amore 	i = (int)(n->args->argc)++;
122095c635efSGarrett D'Amore 
1221260e9a87SYuri Pankov 	n->args->argv = mandoc_reallocarray(n->args->argv,
1222260e9a87SYuri Pankov 	    n->args->argc, sizeof(struct mdoc_argv));
122395c635efSGarrett D'Amore 
122495c635efSGarrett D'Amore 	n->args->argv[i].arg = MDOC_Width;
122595c635efSGarrett D'Amore 	n->args->argv[i].line = n->line;
122695c635efSGarrett D'Amore 	n->args->argv[i].pos = n->pos;
122795c635efSGarrett D'Amore 	n->args->argv[i].sz = 1;
122895c635efSGarrett D'Amore 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
122995c635efSGarrett D'Amore 	n->args->argv[i].value[0] = mandoc_strdup(buf);
123095c635efSGarrett D'Amore 
123195c635efSGarrett D'Amore 	/* Set our width! */
123295c635efSGarrett D'Amore 	n->norm->Bl.width = n->args->argv[i].value[0];
123395c635efSGarrett D'Amore }
123495c635efSGarrett D'Amore 
1235260e9a87SYuri Pankov static void
1236260e9a87SYuri Pankov post_bl_head(POST_ARGS)
123795c635efSGarrett D'Amore {
1238*371584c2SYuri Pankov 	struct roff_node *nbl, *nh, *nch, *nnext;
1239260e9a87SYuri Pankov 	struct mdoc_argv *argv;
124095c635efSGarrett D'Amore 	int		  i, j;
124195c635efSGarrett D'Amore 
1242*371584c2SYuri Pankov 	post_bl_norm(mdoc);
1243260e9a87SYuri Pankov 
1244*371584c2SYuri Pankov 	nh = mdoc->last;
1245260e9a87SYuri Pankov 	if (nh->norm->Bl.type != LIST_column) {
1246260e9a87SYuri Pankov 		if ((nch = nh->child) == NULL)
1247260e9a87SYuri Pankov 			return;
1248260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1249260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1250260e9a87SYuri Pankov 		while (nch != NULL) {
1251*371584c2SYuri Pankov 			roff_node_delete(mdoc, nch);
1252260e9a87SYuri Pankov 			nch = nh->child;
1253260e9a87SYuri Pankov 		}
1254260e9a87SYuri Pankov 		return;
1255260e9a87SYuri Pankov 	}
125695c635efSGarrett D'Amore 
125795c635efSGarrett D'Amore 	/*
1258260e9a87SYuri Pankov 	 * Append old-style lists, where the column width specifiers
125995c635efSGarrett D'Amore 	 * trail as macro parameters, to the new-style ("normal-form")
126095c635efSGarrett D'Amore 	 * lists where they're argument values following -column.
126195c635efSGarrett D'Amore 	 */
126295c635efSGarrett D'Amore 
1263260e9a87SYuri Pankov 	if (nh->child == NULL)
1264260e9a87SYuri Pankov 		return;
126595c635efSGarrett D'Amore 
1266260e9a87SYuri Pankov 	nbl = nh->parent;
1267260e9a87SYuri Pankov 	for (j = 0; j < (int)nbl->args->argc; j++)
1268260e9a87SYuri Pankov 		if (nbl->args->argv[j].arg == MDOC_Column)
126995c635efSGarrett D'Amore 			break;
127095c635efSGarrett D'Amore 
1271260e9a87SYuri Pankov 	assert(j < (int)nbl->args->argc);
127295c635efSGarrett D'Amore 
127395c635efSGarrett D'Amore 	/*
127495c635efSGarrett D'Amore 	 * Accommodate for new-style groff column syntax.  Shuffle the
127595c635efSGarrett D'Amore 	 * child nodes, all of which must be TEXT, as arguments for the
127695c635efSGarrett D'Amore 	 * column field.  Then, delete the head children.
127795c635efSGarrett D'Amore 	 */
127895c635efSGarrett D'Amore 
1279260e9a87SYuri Pankov 	argv = nbl->args->argv + j;
1280260e9a87SYuri Pankov 	i = argv->sz;
1281*371584c2SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nch->next)
1282*371584c2SYuri Pankov 		argv->sz++;
1283260e9a87SYuri Pankov 	argv->value = mandoc_reallocarray(argv->value,
1284260e9a87SYuri Pankov 	    argv->sz, sizeof(char *));
128595c635efSGarrett D'Amore 
1286260e9a87SYuri Pankov 	nh->norm->Bl.ncols = argv->sz;
1287260e9a87SYuri Pankov 	nh->norm->Bl.cols = (void *)argv->value;
128895c635efSGarrett D'Amore 
1289260e9a87SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nnext) {
1290260e9a87SYuri Pankov 		argv->value[i++] = nch->string;
1291260e9a87SYuri Pankov 		nch->string = NULL;
1292260e9a87SYuri Pankov 		nnext = nch->next;
1293*371584c2SYuri Pankov 		roff_node_delete(NULL, nch);
129495c635efSGarrett D'Amore 	}
1295260e9a87SYuri Pankov 	nh->child = NULL;
129695c635efSGarrett D'Amore }
129795c635efSGarrett D'Amore 
1298260e9a87SYuri Pankov static void
129995c635efSGarrett D'Amore post_bl(POST_ARGS)
130095c635efSGarrett D'Amore {
1301*371584c2SYuri Pankov 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1302*371584c2SYuri Pankov 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1303*371584c2SYuri Pankov 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
130495c635efSGarrett D'Amore 
1305698f87a4SGarrett D'Amore 	nbody = mdoc->last;
1306698f87a4SGarrett D'Amore 	switch (nbody->type) {
1307*371584c2SYuri Pankov 	case ROFFT_BLOCK:
1308260e9a87SYuri Pankov 		post_bl_block(mdoc);
1309260e9a87SYuri Pankov 		return;
1310*371584c2SYuri Pankov 	case ROFFT_HEAD:
1311260e9a87SYuri Pankov 		post_bl_head(mdoc);
1312260e9a87SYuri Pankov 		return;
1313*371584c2SYuri Pankov 	case ROFFT_BODY:
1314698f87a4SGarrett D'Amore 		break;
1315698f87a4SGarrett D'Amore 	default:
1316260e9a87SYuri Pankov 		return;
1317698f87a4SGarrett D'Amore 	}
1318*371584c2SYuri Pankov 	if (nbody->end != ENDBODY_NOT)
1319*371584c2SYuri Pankov 		return;
132095c635efSGarrett D'Amore 
1321698f87a4SGarrett D'Amore 	nchild = nbody->child;
1322260e9a87SYuri Pankov 	if (nchild == NULL) {
1323260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1324260e9a87SYuri Pankov 		    nbody->line, nbody->pos, "Bl");
1325260e9a87SYuri Pankov 		return;
1326260e9a87SYuri Pankov 	}
1327260e9a87SYuri Pankov 	while (nchild != NULL) {
1328260e9a87SYuri Pankov 		if (nchild->tok == MDOC_It ||
1329260e9a87SYuri Pankov 		    (nchild->tok == MDOC_Sm &&
1330260e9a87SYuri Pankov 		     nchild->next != NULL &&
1331260e9a87SYuri Pankov 		     nchild->next->tok == MDOC_It)) {
1332698f87a4SGarrett D'Amore 			nchild = nchild->next;
133395c635efSGarrett D'Amore 			continue;
133495c635efSGarrett D'Amore 		}
133595c635efSGarrett D'Amore 
1336260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1337260e9a87SYuri Pankov 		    nchild->line, nchild->pos,
1338260e9a87SYuri Pankov 		    mdoc_macronames[nchild->tok]);
1339698f87a4SGarrett D'Amore 
1340698f87a4SGarrett D'Amore 		/*
1341698f87a4SGarrett D'Amore 		 * Move the node out of the Bl block.
1342698f87a4SGarrett D'Amore 		 * First, collect all required node pointers.
1343698f87a4SGarrett D'Amore 		 */
1344698f87a4SGarrett D'Amore 
1345698f87a4SGarrett D'Amore 		nblock  = nbody->parent;
1346698f87a4SGarrett D'Amore 		nprev   = nblock->prev;
1347698f87a4SGarrett D'Amore 		nparent = nblock->parent;
1348698f87a4SGarrett D'Amore 		nnext   = nchild->next;
1349698f87a4SGarrett D'Amore 
1350698f87a4SGarrett D'Amore 		/*
1351698f87a4SGarrett D'Amore 		 * Unlink this child.
1352698f87a4SGarrett D'Amore 		 */
1353698f87a4SGarrett D'Amore 
1354*371584c2SYuri Pankov 		assert(nchild->prev == NULL);
1355*371584c2SYuri Pankov 		nbody->child = nnext;
1356*371584c2SYuri Pankov 		if (nnext == NULL)
1357698f87a4SGarrett D'Amore 			nbody->last  = NULL;
1358*371584c2SYuri Pankov 		else
1359698f87a4SGarrett D'Amore 			nnext->prev = NULL;
1360698f87a4SGarrett D'Amore 
1361698f87a4SGarrett D'Amore 		/*
1362698f87a4SGarrett D'Amore 		 * Relink this child.
1363698f87a4SGarrett D'Amore 		 */
1364698f87a4SGarrett D'Amore 
1365698f87a4SGarrett D'Amore 		nchild->parent = nparent;
1366698f87a4SGarrett D'Amore 		nchild->prev   = nprev;
1367698f87a4SGarrett D'Amore 		nchild->next   = nblock;
1368698f87a4SGarrett D'Amore 
1369698f87a4SGarrett D'Amore 		nblock->prev = nchild;
1370*371584c2SYuri Pankov 		if (nprev == NULL)
1371698f87a4SGarrett D'Amore 			nparent->child = nchild;
1372698f87a4SGarrett D'Amore 		else
1373698f87a4SGarrett D'Amore 			nprev->next = nchild;
1374698f87a4SGarrett D'Amore 
1375698f87a4SGarrett D'Amore 		nchild = nnext;
137695c635efSGarrett D'Amore 	}
1377260e9a87SYuri Pankov }
1378260e9a87SYuri Pankov 
1379260e9a87SYuri Pankov static void
1380260e9a87SYuri Pankov post_bk(POST_ARGS)
1381260e9a87SYuri Pankov {
1382*371584c2SYuri Pankov 	struct roff_node	*n;
1383260e9a87SYuri Pankov 
1384260e9a87SYuri Pankov 	n = mdoc->last;
138595c635efSGarrett D'Amore 
1386*371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1387260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1388260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Bk");
1389*371584c2SYuri Pankov 		roff_node_delete(mdoc, n);
1390260e9a87SYuri Pankov 	}
139195c635efSGarrett D'Amore }
139295c635efSGarrett D'Amore 
1393260e9a87SYuri Pankov static void
1394*371584c2SYuri Pankov post_sm(POST_ARGS)
139595c635efSGarrett D'Amore {
1396*371584c2SYuri Pankov 	struct roff_node	*nch;
139795c635efSGarrett D'Amore 
1398260e9a87SYuri Pankov 	nch = mdoc->last->child;
1399260e9a87SYuri Pankov 
1400260e9a87SYuri Pankov 	if (nch == NULL) {
1401260e9a87SYuri Pankov 		mdoc->flags ^= MDOC_SMOFF;
1402260e9a87SYuri Pankov 		return;
140395c635efSGarrett D'Amore 	}
140495c635efSGarrett D'Amore 
1405*371584c2SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
140695c635efSGarrett D'Amore 
1407260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "on")) {
1408260e9a87SYuri Pankov 		mdoc->flags &= ~MDOC_SMOFF;
1409260e9a87SYuri Pankov 		return;
1410698f87a4SGarrett D'Amore 	}
1411260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "off")) {
1412260e9a87SYuri Pankov 		mdoc->flags |= MDOC_SMOFF;
1413260e9a87SYuri Pankov 		return;
1414698f87a4SGarrett D'Amore 	}
141595c635efSGarrett D'Amore 
1416260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_SM_BAD,
1417260e9a87SYuri Pankov 	    mdoc->parse, nch->line, nch->pos,
1418260e9a87SYuri Pankov 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1419260e9a87SYuri Pankov 	mdoc_node_relink(mdoc, nch);
1420260e9a87SYuri Pankov 	return;
142195c635efSGarrett D'Amore }
142295c635efSGarrett D'Amore 
1423260e9a87SYuri Pankov static void
142495c635efSGarrett D'Amore post_root(POST_ARGS)
142595c635efSGarrett D'Amore {
1426*371584c2SYuri Pankov 	struct roff_node *n;
142795c635efSGarrett D'Amore 
1428260e9a87SYuri Pankov 	/* Add missing prologue data. */
142995c635efSGarrett D'Amore 
1430260e9a87SYuri Pankov 	if (mdoc->meta.date == NULL)
1431260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ?
1432260e9a87SYuri Pankov 		    mandoc_strdup("") :
1433260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
143495c635efSGarrett D'Amore 
1435260e9a87SYuri Pankov 	if (mdoc->meta.title == NULL) {
1436260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1437260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, "EOF");
1438260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
143995c635efSGarrett D'Amore 	}
144095c635efSGarrett D'Amore 
1441260e9a87SYuri Pankov 	if (mdoc->meta.vol == NULL)
1442260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup("LOCAL");
144395c635efSGarrett D'Amore 
1444260e9a87SYuri Pankov 	if (mdoc->meta.os == NULL) {
1445260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_OS_MISSING,
1446260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, NULL);
1447260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup("");
144895c635efSGarrett D'Amore 	}
144995c635efSGarrett D'Amore 
1450260e9a87SYuri Pankov 	/* Check that we begin with a proper `Sh'. */
1451260e9a87SYuri Pankov 
1452260e9a87SYuri Pankov 	n = mdoc->first->child;
1453*371584c2SYuri Pankov 	while (n != NULL && n->tok != TOKEN_NONE &&
1454*371584c2SYuri Pankov 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1455260e9a87SYuri Pankov 		n = n->next;
1456260e9a87SYuri Pankov 
1457260e9a87SYuri Pankov 	if (n == NULL)
1458260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1459260e9a87SYuri Pankov 	else if (n->tok != MDOC_Sh)
1460260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1461260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
146295c635efSGarrett D'Amore }
146395c635efSGarrett D'Amore 
1464260e9a87SYuri Pankov static void
146595c635efSGarrett D'Amore post_st(POST_ARGS)
146695c635efSGarrett D'Amore {
1467*371584c2SYuri Pankov 	struct roff_node	 *n, *nch;
146895c635efSGarrett D'Amore 	const char		 *p;
146995c635efSGarrett D'Amore 
1470260e9a87SYuri Pankov 	n = mdoc->last;
1471260e9a87SYuri Pankov 	nch = n->child;
147295c635efSGarrett D'Amore 
1473*371584c2SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
147495c635efSGarrett D'Amore 
1475*371584c2SYuri Pankov 	if ((p = mdoc_a2st(nch->string)) == NULL) {
1476260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1477260e9a87SYuri Pankov 		    nch->line, nch->pos, "St %s", nch->string);
1478*371584c2SYuri Pankov 		roff_node_delete(mdoc, n);
147995c635efSGarrett D'Amore 	} else {
1480260e9a87SYuri Pankov 		free(nch->string);
1481260e9a87SYuri Pankov 		nch->string = mandoc_strdup(p);
148295c635efSGarrett D'Amore 	}
148395c635efSGarrett D'Amore }
148495c635efSGarrett D'Amore 
1485260e9a87SYuri Pankov static void
148695c635efSGarrett D'Amore post_rs(POST_ARGS)
148795c635efSGarrett D'Amore {
1488*371584c2SYuri Pankov 	struct roff_node *np, *nch, *next, *prev;
148995c635efSGarrett D'Amore 	int		  i, j;
149095c635efSGarrett D'Amore 
1491260e9a87SYuri Pankov 	np = mdoc->last;
149295c635efSGarrett D'Amore 
1493*371584c2SYuri Pankov 	if (np->type != ROFFT_BODY)
1494260e9a87SYuri Pankov 		return;
149595c635efSGarrett D'Amore 
1496260e9a87SYuri Pankov 	if (np->child == NULL) {
1497260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1498260e9a87SYuri Pankov 		    np->line, np->pos, "Rs");
1499260e9a87SYuri Pankov 		return;
150095c635efSGarrett D'Amore 	}
150195c635efSGarrett D'Amore 
150295c635efSGarrett D'Amore 	/*
150395c635efSGarrett D'Amore 	 * The full `Rs' block needs special handling to order the
150495c635efSGarrett D'Amore 	 * sub-elements according to `rsord'.  Pick through each element
1505260e9a87SYuri Pankov 	 * and correctly order it.  This is an insertion sort.
150695c635efSGarrett D'Amore 	 */
150795c635efSGarrett D'Amore 
150895c635efSGarrett D'Amore 	next = NULL;
1509260e9a87SYuri Pankov 	for (nch = np->child->next; nch != NULL; nch = next) {
1510260e9a87SYuri Pankov 		/* Determine order number of this child. */
151195c635efSGarrett D'Amore 		for (i = 0; i < RSORD_MAX; i++)
1512260e9a87SYuri Pankov 			if (rsord[i] == nch->tok)
151395c635efSGarrett D'Amore 				break;
151495c635efSGarrett D'Amore 
1515260e9a87SYuri Pankov 		if (i == RSORD_MAX) {
1516260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_RS_BAD,
1517260e9a87SYuri Pankov 			    mdoc->parse, nch->line, nch->pos,
1518260e9a87SYuri Pankov 			    mdoc_macronames[nch->tok]);
1519260e9a87SYuri Pankov 			i = -1;
1520260e9a87SYuri Pankov 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1521260e9a87SYuri Pankov 			np->norm->Rs.quote_T++;
1522260e9a87SYuri Pankov 
1523260e9a87SYuri Pankov 		/*
1524260e9a87SYuri Pankov 		 * Remove this child from the chain.  This somewhat
1525*371584c2SYuri Pankov 		 * repeats roff_node_unlink(), but since we're
152695c635efSGarrett D'Amore 		 * just re-ordering, there's no need for the
152795c635efSGarrett D'Amore 		 * full unlink process.
152895c635efSGarrett D'Amore 		 */
152995c635efSGarrett D'Amore 
1530260e9a87SYuri Pankov 		if ((next = nch->next) != NULL)
1531260e9a87SYuri Pankov 			next->prev = nch->prev;
153295c635efSGarrett D'Amore 
1533260e9a87SYuri Pankov 		if ((prev = nch->prev) != NULL)
1534260e9a87SYuri Pankov 			prev->next = nch->next;
153595c635efSGarrett D'Amore 
1536260e9a87SYuri Pankov 		nch->prev = nch->next = NULL;
1537260e9a87SYuri Pankov 
1538260e9a87SYuri Pankov 		/*
153995c635efSGarrett D'Amore 		 * Scan back until we reach a node that's
1540260e9a87SYuri Pankov 		 * to be ordered before this child.
154195c635efSGarrett D'Amore 		 */
154295c635efSGarrett D'Amore 
154395c635efSGarrett D'Amore 		for ( ; prev ; prev = prev->prev) {
154495c635efSGarrett D'Amore 			/* Determine order of `prev'. */
154595c635efSGarrett D'Amore 			for (j = 0; j < RSORD_MAX; j++)
154695c635efSGarrett D'Amore 				if (rsord[j] == prev->tok)
154795c635efSGarrett D'Amore 					break;
1548260e9a87SYuri Pankov 			if (j == RSORD_MAX)
1549260e9a87SYuri Pankov 				j = -1;
155095c635efSGarrett D'Amore 
155195c635efSGarrett D'Amore 			if (j <= i)
155295c635efSGarrett D'Amore 				break;
155395c635efSGarrett D'Amore 		}
155495c635efSGarrett D'Amore 
155595c635efSGarrett D'Amore 		/*
1556260e9a87SYuri Pankov 		 * Set this child back into its correct place
1557260e9a87SYuri Pankov 		 * in front of the `prev' node.
155895c635efSGarrett D'Amore 		 */
155995c635efSGarrett D'Amore 
1560260e9a87SYuri Pankov 		nch->prev = prev;
156195c635efSGarrett D'Amore 
1562260e9a87SYuri Pankov 		if (prev == NULL) {
1563260e9a87SYuri Pankov 			np->child->prev = nch;
1564260e9a87SYuri Pankov 			nch->next = np->child;
1565260e9a87SYuri Pankov 			np->child = nch;
156695c635efSGarrett D'Amore 		} else {
1567260e9a87SYuri Pankov 			if (prev->next)
1568260e9a87SYuri Pankov 				prev->next->prev = nch;
1569260e9a87SYuri Pankov 			nch->next = prev->next;
1570260e9a87SYuri Pankov 			prev->next = nch;
157195c635efSGarrett D'Amore 		}
157295c635efSGarrett D'Amore 	}
157395c635efSGarrett D'Amore }
157495c635efSGarrett D'Amore 
1575698f87a4SGarrett D'Amore /*
1576698f87a4SGarrett D'Amore  * For some arguments of some macros,
1577698f87a4SGarrett D'Amore  * convert all breakable hyphens into ASCII_HYPH.
1578698f87a4SGarrett D'Amore  */
1579260e9a87SYuri Pankov static void
1580698f87a4SGarrett D'Amore post_hyph(POST_ARGS)
1581698f87a4SGarrett D'Amore {
1582*371584c2SYuri Pankov 	struct roff_node	*nch;
1583698f87a4SGarrett D'Amore 	char			*cp;
1584698f87a4SGarrett D'Amore 
1585260e9a87SYuri Pankov 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1586*371584c2SYuri Pankov 		if (nch->type != ROFFT_TEXT)
1587698f87a4SGarrett D'Amore 			continue;
1588698f87a4SGarrett D'Amore 		cp = nch->string;
1589260e9a87SYuri Pankov 		if (*cp == '\0')
1590698f87a4SGarrett D'Amore 			continue;
1591260e9a87SYuri Pankov 		while (*(++cp) != '\0')
1592260e9a87SYuri Pankov 			if (*cp == '-' &&
1593698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[-1]) &&
1594698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[1]))
1595698f87a4SGarrett D'Amore 				*cp = ASCII_HYPH;
1596698f87a4SGarrett D'Amore 	}
1597698f87a4SGarrett D'Amore }
1598698f87a4SGarrett D'Amore 
1599260e9a87SYuri Pankov static void
160095c635efSGarrett D'Amore post_ns(POST_ARGS)
160195c635efSGarrett D'Amore {
160295c635efSGarrett D'Amore 
1603*371584c2SYuri Pankov 	if (mdoc->last->flags & MDOC_LINE)
1604260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1605260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
160695c635efSGarrett D'Amore }
160795c635efSGarrett D'Amore 
1608260e9a87SYuri Pankov static void
160995c635efSGarrett D'Amore post_sh(POST_ARGS)
161095c635efSGarrett D'Amore {
161195c635efSGarrett D'Amore 
1612260e9a87SYuri Pankov 	post_ignpar(mdoc);
161395c635efSGarrett D'Amore 
1614260e9a87SYuri Pankov 	switch (mdoc->last->type) {
1615*371584c2SYuri Pankov 	case ROFFT_HEAD:
1616260e9a87SYuri Pankov 		post_sh_head(mdoc);
1617260e9a87SYuri Pankov 		break;
1618*371584c2SYuri Pankov 	case ROFFT_BODY:
1619260e9a87SYuri Pankov 		switch (mdoc->lastsec)  {
1620260e9a87SYuri Pankov 		case SEC_NAME:
1621260e9a87SYuri Pankov 			post_sh_name(mdoc);
1622260e9a87SYuri Pankov 			break;
1623260e9a87SYuri Pankov 		case SEC_SEE_ALSO:
1624260e9a87SYuri Pankov 			post_sh_see_also(mdoc);
1625260e9a87SYuri Pankov 			break;
1626260e9a87SYuri Pankov 		case SEC_AUTHORS:
1627260e9a87SYuri Pankov 			post_sh_authors(mdoc);
1628260e9a87SYuri Pankov 			break;
1629260e9a87SYuri Pankov 		default:
1630260e9a87SYuri Pankov 			break;
1631260e9a87SYuri Pankov 		}
1632260e9a87SYuri Pankov 		break;
1633260e9a87SYuri Pankov 	default:
1634260e9a87SYuri Pankov 		break;
1635260e9a87SYuri Pankov 	}
163695c635efSGarrett D'Amore }
163795c635efSGarrett D'Amore 
1638260e9a87SYuri Pankov static void
1639260e9a87SYuri Pankov post_sh_name(POST_ARGS)
164095c635efSGarrett D'Amore {
1641*371584c2SYuri Pankov 	struct roff_node *n;
1642260e9a87SYuri Pankov 	int hasnm, hasnd;
164395c635efSGarrett D'Amore 
1644260e9a87SYuri Pankov 	hasnm = hasnd = 0;
164595c635efSGarrett D'Amore 
1646260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1647260e9a87SYuri Pankov 		switch (n->tok) {
1648260e9a87SYuri Pankov 		case MDOC_Nm:
1649260e9a87SYuri Pankov 			hasnm = 1;
1650260e9a87SYuri Pankov 			break;
1651260e9a87SYuri Pankov 		case MDOC_Nd:
1652260e9a87SYuri Pankov 			hasnd = 1;
1653260e9a87SYuri Pankov 			if (n->next != NULL)
1654260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1655260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos, NULL);
1656260e9a87SYuri Pankov 			break;
1657*371584c2SYuri Pankov 		case TOKEN_NONE:
1658260e9a87SYuri Pankov 			if (hasnm)
1659260e9a87SYuri Pankov 				break;
1660260e9a87SYuri Pankov 			/* FALLTHROUGH */
1661260e9a87SYuri Pankov 		default:
1662260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1663260e9a87SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
1664260e9a87SYuri Pankov 			break;
1665260e9a87SYuri Pankov 		}
166695c635efSGarrett D'Amore 	}
166795c635efSGarrett D'Amore 
1668260e9a87SYuri Pankov 	if ( ! hasnm)
1669260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1670260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1671260e9a87SYuri Pankov 	if ( ! hasnd)
1672260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1673260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1674260e9a87SYuri Pankov }
1675260e9a87SYuri Pankov 
1676260e9a87SYuri Pankov static void
1677260e9a87SYuri Pankov post_sh_see_also(POST_ARGS)
1678260e9a87SYuri Pankov {
1679*371584c2SYuri Pankov 	const struct roff_node	*n;
1680260e9a87SYuri Pankov 	const char		*name, *sec;
1681260e9a87SYuri Pankov 	const char		*lastname, *lastsec, *lastpunct;
1682260e9a87SYuri Pankov 	int			 cmp;
1683260e9a87SYuri Pankov 
1684260e9a87SYuri Pankov 	n = mdoc->last->child;
1685260e9a87SYuri Pankov 	lastname = lastsec = lastpunct = NULL;
1686260e9a87SYuri Pankov 	while (n != NULL) {
1687*371584c2SYuri Pankov 		if (n->tok != MDOC_Xr ||
1688*371584c2SYuri Pankov 		    n->child == NULL ||
1689*371584c2SYuri Pankov 		    n->child->next == NULL)
1690260e9a87SYuri Pankov 			break;
1691260e9a87SYuri Pankov 
1692260e9a87SYuri Pankov 		/* Process one .Xr node. */
1693260e9a87SYuri Pankov 
1694260e9a87SYuri Pankov 		name = n->child->string;
1695260e9a87SYuri Pankov 		sec = n->child->next->string;
1696260e9a87SYuri Pankov 		if (lastsec != NULL) {
1697260e9a87SYuri Pankov 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1698260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1699260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1700260e9a87SYuri Pankov 				    "%s before %s(%s)", lastpunct,
1701260e9a87SYuri Pankov 				    name, sec);
1702260e9a87SYuri Pankov 			cmp = strcmp(lastsec, sec);
1703260e9a87SYuri Pankov 			if (cmp > 0)
1704260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1705260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1706260e9a87SYuri Pankov 				    "%s(%s) after %s(%s)", name,
1707260e9a87SYuri Pankov 				    sec, lastname, lastsec);
1708260e9a87SYuri Pankov 			else if (cmp == 0 &&
1709260e9a87SYuri Pankov 			    strcasecmp(lastname, name) > 0)
1710260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1711260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1712260e9a87SYuri Pankov 				    "%s after %s", name, lastname);
1713260e9a87SYuri Pankov 		}
1714260e9a87SYuri Pankov 		lastname = name;
1715260e9a87SYuri Pankov 		lastsec = sec;
1716260e9a87SYuri Pankov 
1717260e9a87SYuri Pankov 		/* Process the following node. */
1718260e9a87SYuri Pankov 
1719260e9a87SYuri Pankov 		n = n->next;
1720260e9a87SYuri Pankov 		if (n == NULL)
1721260e9a87SYuri Pankov 			break;
1722260e9a87SYuri Pankov 		if (n->tok == MDOC_Xr) {
1723260e9a87SYuri Pankov 			lastpunct = "none";
172495c635efSGarrett D'Amore 			continue;
1725260e9a87SYuri Pankov 		}
1726*371584c2SYuri Pankov 		if (n->type != ROFFT_TEXT)
1727260e9a87SYuri Pankov 			break;
1728260e9a87SYuri Pankov 		for (name = n->string; *name != '\0'; name++)
1729260e9a87SYuri Pankov 			if (isalpha((const unsigned char)*name))
1730260e9a87SYuri Pankov 				return;
1731260e9a87SYuri Pankov 		lastpunct = n->string;
1732260e9a87SYuri Pankov 		if (n->next == NULL)
1733260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1734260e9a87SYuri Pankov 			    n->line, n->pos, "%s after %s(%s)",
1735260e9a87SYuri Pankov 			    lastpunct, lastname, lastsec);
1736260e9a87SYuri Pankov 		n = n->next;
173795c635efSGarrett D'Amore 	}
1738260e9a87SYuri Pankov }
173995c635efSGarrett D'Amore 
1740260e9a87SYuri Pankov static int
1741*371584c2SYuri Pankov child_an(const struct roff_node *n)
1742260e9a87SYuri Pankov {
174395c635efSGarrett D'Amore 
1744260e9a87SYuri Pankov 	for (n = n->child; n != NULL; n = n->next)
1745*371584c2SYuri Pankov 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1746*371584c2SYuri Pankov 			return 1;
1747*371584c2SYuri Pankov 	return 0;
174895c635efSGarrett D'Amore }
174995c635efSGarrett D'Amore 
1750260e9a87SYuri Pankov static void
1751260e9a87SYuri Pankov post_sh_authors(POST_ARGS)
1752260e9a87SYuri Pankov {
1753260e9a87SYuri Pankov 
1754260e9a87SYuri Pankov 	if ( ! child_an(mdoc->last))
1755260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1756260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1757260e9a87SYuri Pankov }
1758260e9a87SYuri Pankov 
1759260e9a87SYuri Pankov static void
176095c635efSGarrett D'Amore post_sh_head(POST_ARGS)
176195c635efSGarrett D'Amore {
1762260e9a87SYuri Pankov 	const char	*goodsec;
1763*371584c2SYuri Pankov 	enum roff_sec	 sec;
176495c635efSGarrett D'Amore 
176595c635efSGarrett D'Amore 	/*
176695c635efSGarrett D'Amore 	 * Process a new section.  Sections are either "named" or
176795c635efSGarrett D'Amore 	 * "custom".  Custom sections are user-defined, while named ones
176895c635efSGarrett D'Amore 	 * follow a conventional order and may only appear in certain
176995c635efSGarrett D'Amore 	 * manual sections.
177095c635efSGarrett D'Amore 	 */
177195c635efSGarrett D'Amore 
1772*371584c2SYuri Pankov 	sec = mdoc->last->sec;
177395c635efSGarrett D'Amore 
177495c635efSGarrett D'Amore 	/* The NAME should be first. */
177595c635efSGarrett D'Amore 
177695c635efSGarrett D'Amore 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1777260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1778260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1779*371584c2SYuri Pankov 		    "Sh %s", secnames[sec]);
178095c635efSGarrett D'Amore 
178195c635efSGarrett D'Amore 	/* The SYNOPSIS gets special attention in other areas. */
178295c635efSGarrett D'Amore 
1783*371584c2SYuri Pankov 	if (sec == SEC_SYNOPSIS) {
1784698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 1, '=');
178595c635efSGarrett D'Amore 		mdoc->flags |= MDOC_SYNOPSIS;
1786698f87a4SGarrett D'Amore 	} else {
1787698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 0, '=');
178895c635efSGarrett D'Amore 		mdoc->flags &= ~MDOC_SYNOPSIS;
1789698f87a4SGarrett D'Amore 	}
179095c635efSGarrett D'Amore 
179195c635efSGarrett D'Amore 	/* Mark our last section. */
179295c635efSGarrett D'Amore 
179395c635efSGarrett D'Amore 	mdoc->lastsec = sec;
179495c635efSGarrett D'Amore 
179595c635efSGarrett D'Amore 	/* We don't care about custom sections after this. */
179695c635efSGarrett D'Amore 
1797*371584c2SYuri Pankov 	if (sec == SEC_CUSTOM)
1798260e9a87SYuri Pankov 		return;
179995c635efSGarrett D'Amore 
180095c635efSGarrett D'Amore 	/*
180195c635efSGarrett D'Amore 	 * Check whether our non-custom section is being repeated or is
180295c635efSGarrett D'Amore 	 * out of order.
180395c635efSGarrett D'Amore 	 */
180495c635efSGarrett D'Amore 
180595c635efSGarrett D'Amore 	if (sec == mdoc->lastnamed)
1806260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1807260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1808*371584c2SYuri Pankov 		    "Sh %s", secnames[sec]);
180995c635efSGarrett D'Amore 
181095c635efSGarrett D'Amore 	if (sec < mdoc->lastnamed)
1811260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1812260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1813*371584c2SYuri Pankov 		    "Sh %s", secnames[sec]);
181495c635efSGarrett D'Amore 
181595c635efSGarrett D'Amore 	/* Mark the last named section. */
181695c635efSGarrett D'Amore 
181795c635efSGarrett D'Amore 	mdoc->lastnamed = sec;
181895c635efSGarrett D'Amore 
181995c635efSGarrett D'Amore 	/* Check particular section/manual conventions. */
182095c635efSGarrett D'Amore 
1821*371584c2SYuri Pankov 	if (mdoc->meta.msec == NULL)
1822260e9a87SYuri Pankov 		return;
182395c635efSGarrett D'Amore 
1824260e9a87SYuri Pankov 	goodsec = NULL;
182595c635efSGarrett D'Amore 	switch (sec) {
1826260e9a87SYuri Pankov 	case SEC_ERRORS:
1827260e9a87SYuri Pankov 		if (*mdoc->meta.msec == '4')
1828260e9a87SYuri Pankov 			break;
1829260e9a87SYuri Pankov 		goodsec = "2, 3, 4, 9";
183095c635efSGarrett D'Amore 		/* FALLTHROUGH */
1831260e9a87SYuri Pankov 	case SEC_RETURN_VALUES:
1832260e9a87SYuri Pankov 	case SEC_LIBRARY:
183395c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '2')
183495c635efSGarrett D'Amore 			break;
183595c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '3')
183695c635efSGarrett D'Amore 			break;
1837260e9a87SYuri Pankov 		if (NULL == goodsec)
1838260e9a87SYuri Pankov 			goodsec = "2, 3, 9";
1839260e9a87SYuri Pankov 		/* FALLTHROUGH */
1840260e9a87SYuri Pankov 	case SEC_CONTEXT:
184195c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '9')
184295c635efSGarrett D'Amore 			break;
1843260e9a87SYuri Pankov 		if (NULL == goodsec)
1844260e9a87SYuri Pankov 			goodsec = "9";
1845260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1846260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1847*371584c2SYuri Pankov 		    "Sh %s for %s only", secnames[sec], goodsec);
184895c635efSGarrett D'Amore 		break;
184995c635efSGarrett D'Amore 	default:
185095c635efSGarrett D'Amore 		break;
185195c635efSGarrett D'Amore 	}
185295c635efSGarrett D'Amore }
185395c635efSGarrett D'Amore 
1854260e9a87SYuri Pankov static void
185595c635efSGarrett D'Amore post_ignpar(POST_ARGS)
185695c635efSGarrett D'Amore {
1857*371584c2SYuri Pankov 	struct roff_node *np;
185895c635efSGarrett D'Amore 
1859260e9a87SYuri Pankov 	switch (mdoc->last->type) {
1860*371584c2SYuri Pankov 	case ROFFT_HEAD:
1861260e9a87SYuri Pankov 		post_hyph(mdoc);
1862260e9a87SYuri Pankov 		return;
1863*371584c2SYuri Pankov 	case ROFFT_BODY:
1864260e9a87SYuri Pankov 		break;
1865260e9a87SYuri Pankov 	default:
1866260e9a87SYuri Pankov 		return;
1867260e9a87SYuri Pankov 	}
186895c635efSGarrett D'Amore 
1869*371584c2SYuri Pankov 	if ((np = mdoc->last->child) != NULL)
1870*371584c2SYuri Pankov 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1871260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
1872260e9a87SYuri Pankov 			    mdoc->parse, np->line, np->pos,
1873260e9a87SYuri Pankov 			    "%s after %s", mdoc_macronames[np->tok],
1874260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
1875*371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
187695c635efSGarrett D'Amore 		}
187795c635efSGarrett D'Amore 
1878*371584c2SYuri Pankov 	if ((np = mdoc->last->last) != NULL)
1879*371584c2SYuri Pankov 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
1880260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1881260e9a87SYuri Pankov 			    np->line, np->pos, "%s at the end of %s",
1882260e9a87SYuri Pankov 			    mdoc_macronames[np->tok],
1883260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
1884*371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
188595c635efSGarrett D'Amore 		}
188695c635efSGarrett D'Amore }
188795c635efSGarrett D'Amore 
1888260e9a87SYuri Pankov static void
1889*371584c2SYuri Pankov post_prevpar(POST_ARGS)
189095c635efSGarrett D'Amore {
1891*371584c2SYuri Pankov 	struct roff_node *n;
189295c635efSGarrett D'Amore 
1893*371584c2SYuri Pankov 	n = mdoc->last;
1894*371584c2SYuri Pankov 	if (NULL == n->prev)
1895260e9a87SYuri Pankov 		return;
1896*371584c2SYuri Pankov 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
1897260e9a87SYuri Pankov 		return;
189895c635efSGarrett D'Amore 
1899260e9a87SYuri Pankov 	/*
190095c635efSGarrett D'Amore 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
190195c635efSGarrett D'Amore 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
190295c635efSGarrett D'Amore 	 */
190395c635efSGarrett D'Amore 
1904*371584c2SYuri Pankov 	if (n->prev->tok != MDOC_Pp &&
1905*371584c2SYuri Pankov 	    n->prev->tok != MDOC_Lp &&
1906*371584c2SYuri Pankov 	    n->prev->tok != MDOC_br)
1907260e9a87SYuri Pankov 		return;
1908*371584c2SYuri Pankov 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
1909260e9a87SYuri Pankov 		return;
1910*371584c2SYuri Pankov 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
1911260e9a87SYuri Pankov 		return;
1912*371584c2SYuri Pankov 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
1913260e9a87SYuri Pankov 		return;
191495c635efSGarrett D'Amore 
1915260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1916*371584c2SYuri Pankov 	    n->prev->line, n->prev->pos,
1917*371584c2SYuri Pankov 	    "%s before %s", mdoc_macronames[n->prev->tok],
1918260e9a87SYuri Pankov 	    mdoc_macronames[n->tok]);
1919*371584c2SYuri Pankov 	roff_node_delete(mdoc, n->prev);
192095c635efSGarrett D'Amore }
192195c635efSGarrett D'Amore 
1922260e9a87SYuri Pankov static void
1923698f87a4SGarrett D'Amore post_par(POST_ARGS)
1924698f87a4SGarrett D'Amore {
1925*371584c2SYuri Pankov 	struct roff_node *np;
1926698f87a4SGarrett D'Amore 
1927260e9a87SYuri Pankov 	np = mdoc->last;
1928*371584c2SYuri Pankov 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
1929*371584c2SYuri Pankov 		post_prevpar(mdoc);
1930698f87a4SGarrett D'Amore 
1931260e9a87SYuri Pankov 	if (np->tok == MDOC_sp) {
1932*371584c2SYuri Pankov 		if (np->child != NULL && np->child->next != NULL)
1933260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1934260e9a87SYuri Pankov 			    np->child->next->line, np->child->next->pos,
1935260e9a87SYuri Pankov 			    "sp ... %s", np->child->next->string);
1936260e9a87SYuri Pankov 	} else if (np->child != NULL)
1937260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
1938260e9a87SYuri Pankov 		    mdoc->parse, np->line, np->pos, "%s %s",
1939260e9a87SYuri Pankov 		    mdoc_macronames[np->tok], np->child->string);
1940260e9a87SYuri Pankov 
1941*371584c2SYuri Pankov 	if ((np = mdoc->last->prev) == NULL) {
1942260e9a87SYuri Pankov 		np = mdoc->last->parent;
1943*371584c2SYuri Pankov 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
1944260e9a87SYuri Pankov 			return;
1945*371584c2SYuri Pankov 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
1946*371584c2SYuri Pankov 	    (mdoc->last->tok != MDOC_br ||
1947*371584c2SYuri Pankov 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
1948260e9a87SYuri Pankov 		return;
1949698f87a4SGarrett D'Amore 
1950260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1951260e9a87SYuri Pankov 	    mdoc->last->line, mdoc->last->pos,
1952260e9a87SYuri Pankov 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
1953260e9a87SYuri Pankov 	    mdoc_macronames[np->tok]);
1954*371584c2SYuri Pankov 	roff_node_delete(mdoc, mdoc->last);
195595c635efSGarrett D'Amore }
195695c635efSGarrett D'Amore 
1957260e9a87SYuri Pankov static void
195895c635efSGarrett D'Amore post_dd(POST_ARGS)
195995c635efSGarrett D'Amore {
1960*371584c2SYuri Pankov 	struct roff_node *n;
1961260e9a87SYuri Pankov 	char		 *datestr;
196295c635efSGarrett D'Amore 
1963*371584c2SYuri Pankov 	n = mdoc->last;
1964*371584c2SYuri Pankov 	if (mdoc->meta.date != NULL) {
1965*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
1966*371584c2SYuri Pankov 		    n->line, n->pos, "Dd");
196795c635efSGarrett D'Amore 		free(mdoc->meta.date);
1968*371584c2SYuri Pankov 	} else if (mdoc->flags & MDOC_PBODY)
1969*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
1970*371584c2SYuri Pankov 		    n->line, n->pos, "Dd");
1971*371584c2SYuri Pankov 	else if (mdoc->meta.title != NULL)
1972*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
1973*371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Dt");
1974*371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
1975*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
1976*371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Os");
197795c635efSGarrett D'Amore 
1978*371584c2SYuri Pankov 	if (n->child == NULL || n->child->string[0] == '\0') {
1979260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1980260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
1981260e9a87SYuri Pankov 		goto out;
198295c635efSGarrett D'Amore 	}
198395c635efSGarrett D'Amore 
1984260e9a87SYuri Pankov 	datestr = NULL;
1985*371584c2SYuri Pankov 	deroff(&datestr, n);
1986260e9a87SYuri Pankov 	if (mdoc->quick)
1987260e9a87SYuri Pankov 		mdoc->meta.date = datestr;
1988260e9a87SYuri Pankov 	else {
1989260e9a87SYuri Pankov 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
1990260e9a87SYuri Pankov 		    datestr, n->line, n->pos);
1991260e9a87SYuri Pankov 		free(datestr);
199295c635efSGarrett D'Amore 	}
1993260e9a87SYuri Pankov out:
1994*371584c2SYuri Pankov 	roff_node_delete(mdoc, n);
199595c635efSGarrett D'Amore }
199695c635efSGarrett D'Amore 
1997260e9a87SYuri Pankov static void
199895c635efSGarrett D'Amore post_dt(POST_ARGS)
199995c635efSGarrett D'Amore {
2000*371584c2SYuri Pankov 	struct roff_node *nn, *n;
200195c635efSGarrett D'Amore 	const char	 *cp;
200295c635efSGarrett D'Amore 	char		 *p;
200395c635efSGarrett D'Amore 
200495c635efSGarrett D'Amore 	n = mdoc->last;
2005*371584c2SYuri Pankov 	if (mdoc->flags & MDOC_PBODY) {
2006*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2007*371584c2SYuri Pankov 		    n->line, n->pos, "Dt");
2008*371584c2SYuri Pankov 		goto out;
2009*371584c2SYuri Pankov 	}
2010*371584c2SYuri Pankov 
2011*371584c2SYuri Pankov 	if (mdoc->meta.title != NULL)
2012*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2013*371584c2SYuri Pankov 		    n->line, n->pos, "Dt");
2014*371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
2015*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2016*371584c2SYuri Pankov 		    n->line, n->pos, "Dt after Os");
201795c635efSGarrett D'Amore 
2018260e9a87SYuri Pankov 	free(mdoc->meta.title);
2019260e9a87SYuri Pankov 	free(mdoc->meta.msec);
2020260e9a87SYuri Pankov 	free(mdoc->meta.vol);
2021260e9a87SYuri Pankov 	free(mdoc->meta.arch);
202295c635efSGarrett D'Amore 
2023260e9a87SYuri Pankov 	mdoc->meta.title = NULL;
2024260e9a87SYuri Pankov 	mdoc->meta.msec = NULL;
2025260e9a87SYuri Pankov 	mdoc->meta.vol = NULL;
2026260e9a87SYuri Pankov 	mdoc->meta.arch = NULL;
202795c635efSGarrett D'Amore 
2028260e9a87SYuri Pankov 	/* Mandatory first argument: title. */
202995c635efSGarrett D'Amore 
2030260e9a87SYuri Pankov 	nn = n->child;
2031260e9a87SYuri Pankov 	if (nn == NULL || *nn->string == '\0') {
2032260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2033260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Dt");
2034260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2035260e9a87SYuri Pankov 	} else {
2036260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup(nn->string);
203795c635efSGarrett D'Amore 
2038260e9a87SYuri Pankov 		/* Check that all characters are uppercase. */
203995c635efSGarrett D'Amore 
2040260e9a87SYuri Pankov 		for (p = nn->string; *p != '\0'; p++)
2041260e9a87SYuri Pankov 			if (islower((unsigned char)*p)) {
2042260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2043260e9a87SYuri Pankov 				    mdoc->parse, nn->line,
2044260e9a87SYuri Pankov 				    nn->pos + (p - nn->string),
2045260e9a87SYuri Pankov 				    "Dt %s", nn->string);
2046260e9a87SYuri Pankov 				break;
2047260e9a87SYuri Pankov 			}
204895c635efSGarrett D'Amore 	}
204995c635efSGarrett D'Amore 
2050260e9a87SYuri Pankov 	/* Mandatory second argument: section.�*/
205195c635efSGarrett D'Amore 
2052260e9a87SYuri Pankov 	if (nn != NULL)
2053260e9a87SYuri Pankov 		nn = nn->next;
205495c635efSGarrett D'Amore 
2055260e9a87SYuri Pankov 	if (nn == NULL) {
2056260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2057260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
2058260e9a87SYuri Pankov 		    "Dt %s", mdoc->meta.title);
205995c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2060260e9a87SYuri Pankov 		goto out;  /* msec and arch remain NULL. */
206195c635efSGarrett D'Amore 	}
206295c635efSGarrett D'Amore 
2063260e9a87SYuri Pankov 	mdoc->meta.msec = mandoc_strdup(nn->string);
2064260e9a87SYuri Pankov 
2065260e9a87SYuri Pankov 	/* Infer volume title from section number. */
206695c635efSGarrett D'Amore 
206795c635efSGarrett D'Amore 	cp = mandoc_a2msec(nn->string);
2068260e9a87SYuri Pankov 	if (cp == NULL) {
2069260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2070260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
207195c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup(nn->string);
2072260e9a87SYuri Pankov 	} else
2073260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup(cp);
207495c635efSGarrett D'Amore 
2075260e9a87SYuri Pankov 	/* Optional third argument: architecture. */
207695c635efSGarrett D'Amore 
2077260e9a87SYuri Pankov 	if ((nn = nn->next) == NULL)
2078260e9a87SYuri Pankov 		goto out;
207995c635efSGarrett D'Amore 
2080260e9a87SYuri Pankov 	for (p = nn->string; *p != '\0'; p++)
2081260e9a87SYuri Pankov 		*p = tolower((unsigned char)*p);
2082260e9a87SYuri Pankov 	mdoc->meta.arch = mandoc_strdup(nn->string);
208395c635efSGarrett D'Amore 
2084260e9a87SYuri Pankov 	/* Ignore fourth and later arguments. */
208595c635efSGarrett D'Amore 
2086260e9a87SYuri Pankov 	if ((nn = nn->next) != NULL)
2087260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2088260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
208995c635efSGarrett D'Amore 
2090260e9a87SYuri Pankov out:
2091*371584c2SYuri Pankov 	roff_node_delete(mdoc, n);
209295c635efSGarrett D'Amore }
209395c635efSGarrett D'Amore 
2094260e9a87SYuri Pankov static void
209595c635efSGarrett D'Amore post_bx(POST_ARGS)
209695c635efSGarrett D'Amore {
2097*371584c2SYuri Pankov 	struct roff_node	*n;
209895c635efSGarrett D'Amore 
2099260e9a87SYuri Pankov 	/*
210095c635efSGarrett D'Amore 	 * Make `Bx's second argument always start with an uppercase
210195c635efSGarrett D'Amore 	 * letter.  Groff checks if it's an "accepted" term, but we just
210295c635efSGarrett D'Amore 	 * uppercase blindly.
210395c635efSGarrett D'Amore 	 */
210495c635efSGarrett D'Amore 
2105*371584c2SYuri Pankov 	if ((n = mdoc->last->child) != NULL && (n = n->next) != NULL)
2106260e9a87SYuri Pankov 		*n->string = (char)toupper((unsigned char)*n->string);
210795c635efSGarrett D'Amore }
210895c635efSGarrett D'Amore 
2109260e9a87SYuri Pankov static void
211095c635efSGarrett D'Amore post_os(POST_ARGS)
211195c635efSGarrett D'Amore {
211295c635efSGarrett D'Amore #ifndef OSNAME
211395c635efSGarrett D'Amore 	struct utsname	  utsname;
2114260e9a87SYuri Pankov 	static char	 *defbuf;
211595c635efSGarrett D'Amore #endif
2116*371584c2SYuri Pankov 	struct roff_node *n;
211795c635efSGarrett D'Amore 
211895c635efSGarrett D'Amore 	n = mdoc->last;
2119*371584c2SYuri Pankov 	if (mdoc->meta.os != NULL)
2120*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2121*371584c2SYuri Pankov 		    n->line, n->pos, "Os");
2122*371584c2SYuri Pankov 	else if (mdoc->flags & MDOC_PBODY)
2123*371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2124*371584c2SYuri Pankov 		    n->line, n->pos, "Os");
212595c635efSGarrett D'Amore 
212695c635efSGarrett D'Amore 	/*
2127698f87a4SGarrett D'Amore 	 * Set the operating system by way of the `Os' macro.
2128698f87a4SGarrett D'Amore 	 * The order of precedence is:
2129698f87a4SGarrett D'Amore 	 * 1. the argument of the `Os' macro, unless empty
2130698f87a4SGarrett D'Amore 	 * 2. the -Ios=foo command line argument, if provided
2131698f87a4SGarrett D'Amore 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2132698f87a4SGarrett D'Amore 	 * 4. "sysname release" from uname(3)
2133260e9a87SYuri Pankov 	 */
213495c635efSGarrett D'Amore 
2135698f87a4SGarrett D'Amore 	free(mdoc->meta.os);
2136260e9a87SYuri Pankov 	mdoc->meta.os = NULL;
2137*371584c2SYuri Pankov 	deroff(&mdoc->meta.os, n);
2138260e9a87SYuri Pankov 	if (mdoc->meta.os)
2139260e9a87SYuri Pankov 		goto out;
214095c635efSGarrett D'Amore 
2141260e9a87SYuri Pankov 	if (mdoc->defos) {
2142260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2143260e9a87SYuri Pankov 		goto out;
214495c635efSGarrett D'Amore 	}
214595c635efSGarrett D'Amore 
214695c635efSGarrett D'Amore #ifdef OSNAME
2147260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(OSNAME);
214895c635efSGarrett D'Amore #else /*!OSNAME */
2149*371584c2SYuri Pankov 	if (defbuf == NULL) {
2150*371584c2SYuri Pankov 		if (uname(&utsname) == -1) {
2151260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2152260e9a87SYuri Pankov 			    n->line, n->pos, "Os");
2153260e9a87SYuri Pankov 			defbuf = mandoc_strdup("UNKNOWN");
2154260e9a87SYuri Pankov 		} else
2155260e9a87SYuri Pankov 			mandoc_asprintf(&defbuf, "%s %s",
2156260e9a87SYuri Pankov 			    utsname.sysname, utsname.release);
215795c635efSGarrett D'Amore 	}
2158260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(defbuf);
2159260e9a87SYuri Pankov #endif /*!OSNAME*/
216095c635efSGarrett D'Amore 
2161260e9a87SYuri Pankov out:
2162*371584c2SYuri Pankov 	roff_node_delete(mdoc, n);
216395c635efSGarrett D'Amore }
216495c635efSGarrett D'Amore 
2165260e9a87SYuri Pankov /*
2166260e9a87SYuri Pankov  * If no argument is provided,
2167260e9a87SYuri Pankov  * fill in the name of the current manual page.
2168260e9a87SYuri Pankov  */
2169260e9a87SYuri Pankov static void
2170260e9a87SYuri Pankov post_ex(POST_ARGS)
217195c635efSGarrett D'Amore {
2172*371584c2SYuri Pankov 	struct roff_node *n;
217395c635efSGarrett D'Amore 
2174*371584c2SYuri Pankov 	post_std(mdoc);
217595c635efSGarrett D'Amore 
2176*371584c2SYuri Pankov 	n = mdoc->last;
2177*371584c2SYuri Pankov 	if (n->child != NULL)
2178260e9a87SYuri Pankov 		return;
217995c635efSGarrett D'Amore 
2180260e9a87SYuri Pankov 	if (mdoc->meta.name == NULL) {
2181260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2182260e9a87SYuri Pankov 		    n->line, n->pos, "Ex");
2183260e9a87SYuri Pankov 		return;
218495c635efSGarrett D'Amore 	}
218595c635efSGarrett D'Amore 
2186*371584c2SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
2187*371584c2SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2188260e9a87SYuri Pankov 	mdoc->last = n;
218995c635efSGarrett D'Amore }
219095c635efSGarrett D'Amore 
2191*371584c2SYuri Pankov enum roff_sec
2192*371584c2SYuri Pankov mdoc_a2sec(const char *p)
219395c635efSGarrett D'Amore {
219495c635efSGarrett D'Amore 	int		 i;
219595c635efSGarrett D'Amore 
2196260e9a87SYuri Pankov 	for (i = 0; i < (int)SEC__MAX; i++)
219795c635efSGarrett D'Amore 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2198*371584c2SYuri Pankov 			return (enum roff_sec)i;
219995c635efSGarrett D'Amore 
2200*371584c2SYuri Pankov 	return SEC_CUSTOM;
220195c635efSGarrett D'Amore }
220295c635efSGarrett D'Amore 
220395c635efSGarrett D'Amore static size_t
2204*371584c2SYuri Pankov macro2len(int macro)
220595c635efSGarrett D'Amore {
220695c635efSGarrett D'Amore 
220795c635efSGarrett D'Amore 	switch (macro) {
2208260e9a87SYuri Pankov 	case MDOC_Ad:
2209*371584c2SYuri Pankov 		return 12;
2210260e9a87SYuri Pankov 	case MDOC_Ao:
2211*371584c2SYuri Pankov 		return 12;
2212260e9a87SYuri Pankov 	case MDOC_An:
2213*371584c2SYuri Pankov 		return 12;
2214260e9a87SYuri Pankov 	case MDOC_Aq:
2215*371584c2SYuri Pankov 		return 12;
2216260e9a87SYuri Pankov 	case MDOC_Ar:
2217*371584c2SYuri Pankov 		return 12;
2218260e9a87SYuri Pankov 	case MDOC_Bo:
2219*371584c2SYuri Pankov 		return 12;
2220260e9a87SYuri Pankov 	case MDOC_Bq:
2221*371584c2SYuri Pankov 		return 12;
2222260e9a87SYuri Pankov 	case MDOC_Cd:
2223*371584c2SYuri Pankov 		return 12;
2224260e9a87SYuri Pankov 	case MDOC_Cm:
2225*371584c2SYuri Pankov 		return 10;
2226260e9a87SYuri Pankov 	case MDOC_Do:
2227*371584c2SYuri Pankov 		return 10;
2228260e9a87SYuri Pankov 	case MDOC_Dq:
2229*371584c2SYuri Pankov 		return 12;
2230260e9a87SYuri Pankov 	case MDOC_Dv:
2231*371584c2SYuri Pankov 		return 12;
2232260e9a87SYuri Pankov 	case MDOC_Eo:
2233*371584c2SYuri Pankov 		return 12;
2234260e9a87SYuri Pankov 	case MDOC_Em:
2235*371584c2SYuri Pankov 		return 10;
2236260e9a87SYuri Pankov 	case MDOC_Er:
2237*371584c2SYuri Pankov 		return 17;
2238260e9a87SYuri Pankov 	case MDOC_Ev:
2239*371584c2SYuri Pankov 		return 15;
2240260e9a87SYuri Pankov 	case MDOC_Fa:
2241*371584c2SYuri Pankov 		return 12;
2242260e9a87SYuri Pankov 	case MDOC_Fl:
2243*371584c2SYuri Pankov 		return 10;
2244260e9a87SYuri Pankov 	case MDOC_Fo:
2245*371584c2SYuri Pankov 		return 16;
2246260e9a87SYuri Pankov 	case MDOC_Fn:
2247*371584c2SYuri Pankov 		return 16;
2248260e9a87SYuri Pankov 	case MDOC_Ic:
2249*371584c2SYuri Pankov 		return 10;
2250260e9a87SYuri Pankov 	case MDOC_Li:
2251*371584c2SYuri Pankov 		return 16;
2252260e9a87SYuri Pankov 	case MDOC_Ms:
2253*371584c2SYuri Pankov 		return 6;
2254260e9a87SYuri Pankov 	case MDOC_Nm:
2255*371584c2SYuri Pankov 		return 10;
2256260e9a87SYuri Pankov 	case MDOC_No:
2257*371584c2SYuri Pankov 		return 12;
2258260e9a87SYuri Pankov 	case MDOC_Oo:
2259*371584c2SYuri Pankov 		return 10;
2260260e9a87SYuri Pankov 	case MDOC_Op:
2261*371584c2SYuri Pankov 		return 14;
2262260e9a87SYuri Pankov 	case MDOC_Pa:
2263*371584c2SYuri Pankov 		return 32;
2264260e9a87SYuri Pankov 	case MDOC_Pf:
2265*371584c2SYuri Pankov 		return 12;
2266260e9a87SYuri Pankov 	case MDOC_Po:
2267*371584c2SYuri Pankov 		return 12;
2268260e9a87SYuri Pankov 	case MDOC_Pq:
2269*371584c2SYuri Pankov 		return 12;
2270260e9a87SYuri Pankov 	case MDOC_Ql:
2271*371584c2SYuri Pankov 		return 16;
2272260e9a87SYuri Pankov 	case MDOC_Qo:
2273*371584c2SYuri Pankov 		return 12;
2274260e9a87SYuri Pankov 	case MDOC_So:
2275*371584c2SYuri Pankov 		return 12;
2276260e9a87SYuri Pankov 	case MDOC_Sq:
2277*371584c2SYuri Pankov 		return 12;
2278260e9a87SYuri Pankov 	case MDOC_Sy:
2279*371584c2SYuri Pankov 		return 6;
2280260e9a87SYuri Pankov 	case MDOC_Sx:
2281*371584c2SYuri Pankov 		return 16;
2282260e9a87SYuri Pankov 	case MDOC_Tn:
2283*371584c2SYuri Pankov 		return 10;
2284260e9a87SYuri Pankov 	case MDOC_Va:
2285*371584c2SYuri Pankov 		return 12;
2286260e9a87SYuri Pankov 	case MDOC_Vt:
2287*371584c2SYuri Pankov 		return 12;
2288260e9a87SYuri Pankov 	case MDOC_Xr:
2289*371584c2SYuri Pankov 		return 10;
229095c635efSGarrett D'Amore 	default:
229195c635efSGarrett D'Amore 		break;
229295c635efSGarrett D'Amore 	};
2293*371584c2SYuri Pankov 	return 0;
229495c635efSGarrett D'Amore }
2295