xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision f5174743)
1*f5174743Sschwarze /*	$Id: mdoc_validate.c,v 1.155 2014/08/08 15:42:39 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4231c7061Sschwarze  * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
539c2a57eSschwarze  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6f73abda9Skristaps  *
7f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps  *
11a6464425Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a6464425Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f73abda9Skristaps  */
1920fa2881Sschwarze #ifndef OSNAME
2020fa2881Sschwarze #include <sys/utsname.h>
2120fa2881Sschwarze #endif
2220fa2881Sschwarze 
23f73abda9Skristaps #include <sys/types.h>
24f73abda9Skristaps 
25f73abda9Skristaps #include <assert.h>
26f73abda9Skristaps #include <ctype.h>
27d92dc4efSschwarze #include <limits.h>
283216dddfSschwarze #include <stdio.h>
29f73abda9Skristaps #include <stdlib.h>
30f73abda9Skristaps #include <string.h>
3120fa2881Sschwarze #include <time.h>
32f73abda9Skristaps 
33a35fc07aSschwarze #include "mdoc.h"
346e03d529Sschwarze #include "mandoc.h"
354f4f7972Sschwarze #include "mandoc_aux.h"
36f73abda9Skristaps #include "libmdoc.h"
37f6854d5cSschwarze #include "libmandoc.h"
38f73abda9Skristaps 
39f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
40f73abda9Skristaps 
416093755cSschwarze #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
42f73abda9Skristaps #define	POST_ARGS struct mdoc *mdoc
43f73abda9Skristaps 
447c2be9f8Sschwarze enum	check_ineq {
457c2be9f8Sschwarze 	CHECK_LT,
467c2be9f8Sschwarze 	CHECK_GT,
477c2be9f8Sschwarze 	CHECK_EQ
487c2be9f8Sschwarze };
497c2be9f8Sschwarze 
507c2be9f8Sschwarze enum	check_lvl {
517c2be9f8Sschwarze 	CHECK_WARN,
527c2be9f8Sschwarze 	CHECK_ERROR,
537c2be9f8Sschwarze };
547c2be9f8Sschwarze 
55f73abda9Skristaps typedef	int	(*v_pre)(PRE_ARGS);
56f73abda9Skristaps typedef	int	(*v_post)(POST_ARGS);
57f73abda9Skristaps 
58f73abda9Skristaps struct	valids {
59f73abda9Skristaps 	v_pre	*pre;
60f73abda9Skristaps 	v_post	*post;
61f73abda9Skristaps };
62f73abda9Skristaps 
637c2be9f8Sschwarze static	int	 check_count(struct mdoc *, enum mdoc_type,
647c2be9f8Sschwarze 			enum check_lvl, enum check_ineq, int);
6520fa2881Sschwarze static	void	 check_text(struct mdoc *, int, int, char *);
6620fa2881Sschwarze static	void	 check_argv(struct mdoc *,
6731e23753Sschwarze 			struct mdoc_node *, struct mdoc_argv *);
6820fa2881Sschwarze static	void	 check_args(struct mdoc *, struct mdoc_node *);
6919a69263Sschwarze static	enum mdoc_sec	a2sec(const char *);
7019a69263Sschwarze static	size_t		macro2len(enum mdoct);
7167c719adSschwarze 
727c2be9f8Sschwarze static	int	 ebool(POST_ARGS);
7367c719adSschwarze static	int	 berr_ge1(POST_ARGS);
7467c719adSschwarze static	int	 bwarn_ge1(POST_ARGS);
75da272f5eSschwarze static	int	 ewarn_eq0(POST_ARGS);
76bb648afaSschwarze static	int	 ewarn_eq1(POST_ARGS);
7767c719adSschwarze static	int	 ewarn_ge1(POST_ARGS);
78bb648afaSschwarze static	int	 ewarn_le1(POST_ARGS);
79b16e7ddfSschwarze static	int	 hwarn_eq0(POST_ARGS);
807c2be9f8Sschwarze static	int	 hwarn_eq1(POST_ARGS);
81bb648afaSschwarze static	int	 hwarn_ge1(POST_ARGS);
8267c719adSschwarze 
8367c719adSschwarze static	int	 post_an(POST_ARGS);
8467c719adSschwarze static	int	 post_at(POST_ARGS);
8567c719adSschwarze static	int	 post_bf(POST_ARGS);
8667c719adSschwarze static	int	 post_bl(POST_ARGS);
8720fa2881Sschwarze static	int	 post_bl_block(POST_ARGS);
8820fa2881Sschwarze static	int	 post_bl_block_width(POST_ARGS);
8920fa2881Sschwarze static	int	 post_bl_block_tag(POST_ARGS);
9067c719adSschwarze static	int	 post_bl_head(POST_ARGS);
91992063deSschwarze static	int	 post_bx(POST_ARGS);
924039b21cSschwarze static	int	 post_defaults(POST_ARGS);
9320fa2881Sschwarze static	int	 post_dd(POST_ARGS);
946093755cSschwarze static	int	 post_dt(POST_ARGS);
95551cd4a8Sschwarze static	int	 post_en(POST_ARGS);
96551cd4a8Sschwarze static	int	 post_es(POST_ARGS);
9720fa2881Sschwarze static	int	 post_eoln(POST_ARGS);
98e214f641Sschwarze static	int	 post_ex(POST_ARGS);
994039b21cSschwarze static	int	 post_hyph(POST_ARGS);
1004039b21cSschwarze static	int	 post_ignpar(POST_ARGS);
10167c719adSschwarze static	int	 post_it(POST_ARGS);
10267c719adSschwarze static	int	 post_lb(POST_ARGS);
1034039b21cSschwarze static	int	 post_literal(POST_ARGS);
10467c719adSschwarze static	int	 post_nm(POST_ARGS);
105af216717Sschwarze static	int	 post_ns(POST_ARGS);
10620fa2881Sschwarze static	int	 post_os(POST_ARGS);
107e0dd4c9cSschwarze static	int	 post_par(POST_ARGS);
10820fa2881Sschwarze static	int	 post_prol(POST_ARGS);
10967c719adSschwarze static	int	 post_root(POST_ARGS);
110011fe33bSschwarze static	int	 post_rs(POST_ARGS);
11167c719adSschwarze static	int	 post_sh(POST_ARGS);
11267c719adSschwarze static	int	 post_sh_body(POST_ARGS);
11367c719adSschwarze static	int	 post_sh_head(POST_ARGS);
11467c719adSschwarze static	int	 post_st(POST_ARGS);
1158521b0bcSschwarze static	int	 post_vt(POST_ARGS);
116f73abda9Skristaps static	int	 pre_an(PRE_ARGS);
117f73abda9Skristaps static	int	 pre_bd(PRE_ARGS);
118f73abda9Skristaps static	int	 pre_bl(PRE_ARGS);
119f73abda9Skristaps static	int	 pre_dd(PRE_ARGS);
120f73abda9Skristaps static	int	 pre_display(PRE_ARGS);
121f73abda9Skristaps static	int	 pre_dt(PRE_ARGS);
12220fa2881Sschwarze static	int	 pre_literal(PRE_ARGS);
123551cd4a8Sschwarze static	int	 pre_obsolete(PRE_ARGS);
124f73abda9Skristaps static	int	 pre_os(PRE_ARGS);
12520fa2881Sschwarze static	int	 pre_par(PRE_ARGS);
12620fa2881Sschwarze static	int	 pre_std(PRE_ARGS);
127f73abda9Skristaps 
12867c719adSschwarze static	v_post	 posts_an[] = { post_an, NULL };
12920fa2881Sschwarze static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
13020fa2881Sschwarze static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
131ecb10c32Sschwarze static	v_post	 posts_bf[] = { post_bf, NULL };
13220fa2881Sschwarze static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
13367c719adSschwarze static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
134992063deSschwarze static	v_post	 posts_bx[] = { post_bx, NULL };
135bb648afaSschwarze static	v_post	 posts_bool[] = { ebool, NULL };
136b31af00dSschwarze static	v_post	 posts_eoln[] = { post_eoln, NULL };
13720fa2881Sschwarze static	v_post	 posts_defaults[] = { post_defaults, NULL };
1384039b21cSschwarze static	v_post	 posts_d1[] = { bwarn_ge1, post_hyph, NULL };
139b058e777Sschwarze static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
140bb648afaSschwarze static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
14120fa2881Sschwarze static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
142551cd4a8Sschwarze static	v_post	 posts_en[] = { post_en, NULL };
143551cd4a8Sschwarze static	v_post	 posts_es[] = { post_es, NULL };
144e214f641Sschwarze static	v_post	 posts_ex[] = { post_ex, NULL };
14567c719adSschwarze static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
1464039b21cSschwarze static	v_post	 posts_hyph[] = { post_hyph, NULL };
1474039b21cSschwarze static	v_post	 posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
14867c719adSschwarze static	v_post	 posts_it[] = { post_it, NULL };
149bb648afaSschwarze static	v_post	 posts_lb[] = { post_lb, NULL };
1504039b21cSschwarze static	v_post	 posts_nd[] = { berr_ge1, post_hyph, NULL };
15167c719adSschwarze static	v_post	 posts_nm[] = { post_nm, NULL };
152da272f5eSschwarze static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
153af216717Sschwarze static	v_post	 posts_ns[] = { post_ns, NULL };
15420fa2881Sschwarze static	v_post	 posts_os[] = { post_os, post_prol, NULL };
155e0dd4c9cSschwarze static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
156bb648afaSschwarze static	v_post	 posts_rs[] = { post_rs, NULL };
1574039b21cSschwarze static	v_post	 posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
158e0dd4c9cSschwarze static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
1594039b21cSschwarze static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
160bb648afaSschwarze static	v_post	 posts_st[] = { post_st, NULL };
161e7a93ef3Sschwarze static	v_post	 posts_text[] = { ewarn_ge1, NULL };
162bb648afaSschwarze static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
1638521b0bcSschwarze static	v_post	 posts_vt[] = { post_vt, NULL };
164f73abda9Skristaps static	v_pre	 pres_an[] = { pre_an, NULL };
16520fa2881Sschwarze static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
16620fa2881Sschwarze static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
167f73abda9Skristaps static	v_pre	 pres_d1[] = { pre_display, NULL };
16820fa2881Sschwarze static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
16967c719adSschwarze static	v_pre	 pres_dd[] = { pre_dd, NULL };
1706be99f77Sschwarze static	v_pre	 pres_dt[] = { pre_dt, NULL };
1718d5916b5Sschwarze static	v_pre	 pres_it[] = { pre_par, NULL };
172551cd4a8Sschwarze static	v_pre	 pres_obsolete[] = { pre_obsolete, NULL };
1736be99f77Sschwarze static	v_pre	 pres_os[] = { pre_os, NULL };
17420fa2881Sschwarze static	v_pre	 pres_pp[] = { pre_par, NULL };
17520fa2881Sschwarze static	v_pre	 pres_std[] = { pre_std, NULL };
176f73abda9Skristaps 
17719a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
178099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
17920fa2881Sschwarze 	{ pres_dd, posts_dd },			/* Dd */
1806093755cSschwarze 	{ pres_dt, posts_dt },			/* Dt */
18120fa2881Sschwarze 	{ pres_os, posts_os },			/* Os */
1828d5916b5Sschwarze 	{ NULL, posts_sh },			/* Sh */
1838d5916b5Sschwarze 	{ NULL, posts_ss },			/* Ss */
184e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Pp */
1854039b21cSschwarze 	{ pres_d1, posts_d1 },			/* D1 */
18620fa2881Sschwarze 	{ pres_dl, posts_dl },			/* Dl */
18720fa2881Sschwarze 	{ pres_bd, posts_bd },			/* Bd */
188f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
189f73abda9Skristaps 	{ pres_bl, posts_bl },			/* Bl */
190f73abda9Skristaps 	{ NULL, NULL },				/* El */
191f73abda9Skristaps 	{ pres_it, posts_it },			/* It */
192e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
193f73abda9Skristaps 	{ pres_an, posts_an },			/* An */
19420fa2881Sschwarze 	{ NULL, posts_defaults },		/* Ar */
195e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
196f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
197f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
1984039b21cSschwarze 	{ NULL, NULL },				/* Er */
199f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
200e214f641Sschwarze 	{ pres_std, posts_ex },			/* Ex */
201f73abda9Skristaps 	{ NULL, NULL },				/* Fa */
2024039b21cSschwarze 	{ NULL, posts_text },			/* Fd */
203f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
204e7a93ef3Sschwarze 	{ NULL, NULL },				/* Fn */
205e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
206e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
207b822ca0dSschwarze 	{ NULL, posts_text1 },			/* In */
20820fa2881Sschwarze 	{ NULL, posts_defaults },		/* Li */
2094602e85cSschwarze 	{ NULL, posts_nd },			/* Nd */
210f73abda9Skristaps 	{ NULL, posts_nm },			/* Nm */
211bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
212551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Ot */
21320fa2881Sschwarze 	{ NULL, posts_defaults },		/* Pa */
214e214f641Sschwarze 	{ pres_std, NULL },			/* Rv */
215f73abda9Skristaps 	{ NULL, posts_st },			/* St */
216f73abda9Skristaps 	{ NULL, NULL },				/* Va */
2178521b0bcSschwarze 	{ NULL, posts_vt },			/* Vt */
218e7a93ef3Sschwarze 	{ NULL, posts_text },			/* Xr */
219f73abda9Skristaps 	{ NULL, posts_text },			/* %A */
2204039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
221b058e777Sschwarze 	{ NULL, posts_text },			/* %D */
222f73abda9Skristaps 	{ NULL, posts_text },			/* %I */
223f73abda9Skristaps 	{ NULL, posts_text },			/* %J */
2244039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %N */
2254039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %O */
226f73abda9Skristaps 	{ NULL, posts_text },			/* %P */
2274039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %R */
2284039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
229f73abda9Skristaps 	{ NULL, posts_text },			/* %V */
230f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
231f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
232bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
233f73abda9Skristaps 	{ NULL, posts_at },			/* At */
234f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
235f73abda9Skristaps 	{ NULL, posts_bf },			/* Bf */
236f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
237bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
238f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
239992063deSschwarze 	{ NULL, posts_bx },			/* Bx */
240f73abda9Skristaps 	{ NULL, posts_bool },			/* Db */
241f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
242f73abda9Skristaps 	{ NULL, NULL },				/* Do */
243bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
244f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
245f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
246f73abda9Skristaps 	{ NULL, NULL },				/* Em */
247f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
248f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
249e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
250f73abda9Skristaps 	{ NULL, posts_notext },			/* No */
251af216717Sschwarze 	{ NULL, posts_ns },			/* Ns */
252f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
253f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
254f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
255b822ca0dSschwarze 	{ NULL, posts_text1 },			/* Pf */
256f73abda9Skristaps 	{ NULL, NULL },				/* Po */
257bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
258f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
259bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
260f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
261bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
262f73abda9Skristaps 	{ NULL, NULL },				/* Re */
2638c62fbf5Sschwarze 	{ NULL, posts_rs },			/* Rs */
264f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
265f73abda9Skristaps 	{ NULL, NULL },				/* So */
266bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
267f73abda9Skristaps 	{ NULL, posts_bool },			/* Sm */
2684039b21cSschwarze 	{ NULL, posts_hyph },			/* Sx */
269e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
270e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
271f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
272f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
273f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
274f73abda9Skristaps 	{ NULL, posts_fo },			/* Fo */
275f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
276f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
277f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
27820fa2881Sschwarze 	{ NULL, posts_bk },			/* Bk */
279f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
280b31af00dSschwarze 	{ NULL, posts_eoln },			/* Bt */
281f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
282551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Fr */
283b31af00dSschwarze 	{ NULL, posts_eoln },			/* Ud */
284b31af00dSschwarze 	{ NULL, posts_lb },			/* Lb */
285e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Lp */
286e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
28720fa2881Sschwarze 	{ NULL, posts_defaults },		/* Mt */
288bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
289f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
290f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
291f73abda9Skristaps 	{ NULL, posts_text },			/* %C */
292551cd4a8Sschwarze 	{ pres_obsolete, posts_es },		/* Es */
293551cd4a8Sschwarze 	{ pres_obsolete, posts_en },		/* En */
294f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
295f73abda9Skristaps 	{ NULL, posts_text },			/* %Q */
296e0dd4c9cSschwarze 	{ NULL, posts_pp },			/* br */
297e0dd4c9cSschwarze 	{ NULL, posts_sp },			/* sp */
298b822ca0dSschwarze 	{ NULL, posts_text1 },			/* %U */
2996093755cSschwarze 	{ NULL, NULL },				/* Ta */
3005281506aSschwarze 	{ NULL, NULL },				/* ll */
301f73abda9Skristaps };
302f73abda9Skristaps 
30320fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
30420fa2881Sschwarze 
30520fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
30620fa2881Sschwarze 	MDOC__A,
30720fa2881Sschwarze 	MDOC__T,
30820fa2881Sschwarze 	MDOC__B,
30920fa2881Sschwarze 	MDOC__I,
31020fa2881Sschwarze 	MDOC__J,
31120fa2881Sschwarze 	MDOC__R,
31220fa2881Sschwarze 	MDOC__N,
31320fa2881Sschwarze 	MDOC__V,
3140397c682Sschwarze 	MDOC__U,
31520fa2881Sschwarze 	MDOC__P,
31620fa2881Sschwarze 	MDOC__Q,
3174e32ec8fSschwarze 	MDOC__C,
31820fa2881Sschwarze 	MDOC__D,
3194e32ec8fSschwarze 	MDOC__O
32020fa2881Sschwarze };
32120fa2881Sschwarze 
32219a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
32319a69263Sschwarze 	NULL,
32419a69263Sschwarze 	"NAME",
32519a69263Sschwarze 	"LIBRARY",
32619a69263Sschwarze 	"SYNOPSIS",
32719a69263Sschwarze 	"DESCRIPTION",
32803ab2f23Sdlg 	"CONTEXT",
32919a69263Sschwarze 	"IMPLEMENTATION NOTES",
33019a69263Sschwarze 	"RETURN VALUES",
33119a69263Sschwarze 	"ENVIRONMENT",
33219a69263Sschwarze 	"FILES",
33319a69263Sschwarze 	"EXIT STATUS",
33419a69263Sschwarze 	"EXAMPLES",
33519a69263Sschwarze 	"DIAGNOSTICS",
33619a69263Sschwarze 	"COMPATIBILITY",
33719a69263Sschwarze 	"ERRORS",
33819a69263Sschwarze 	"SEE ALSO",
33919a69263Sschwarze 	"STANDARDS",
34019a69263Sschwarze 	"HISTORY",
34119a69263Sschwarze 	"AUTHORS",
34219a69263Sschwarze 	"CAVEATS",
34319a69263Sschwarze 	"BUGS",
34419a69263Sschwarze 	"SECURITY CONSIDERATIONS",
34519a69263Sschwarze 	NULL
34619a69263Sschwarze };
347f73abda9Skristaps 
34849aff9f8Sschwarze 
349f73abda9Skristaps int
3506093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
351f73abda9Skristaps {
352f73abda9Skristaps 	v_pre		*p;
353f73abda9Skristaps 	int		 line, pos;
35431e23753Sschwarze 	char		*tp;
355f73abda9Skristaps 
3562791bd1cSschwarze 	switch (n->type) {
35749aff9f8Sschwarze 	case MDOC_TEXT:
358f73abda9Skristaps 		tp = n->string;
359f73abda9Skristaps 		line = n->line;
360f73abda9Skristaps 		pos = n->pos;
36120fa2881Sschwarze 		check_text(mdoc, line, pos, tp);
3622791bd1cSschwarze 		/* FALLTHROUGH */
36349aff9f8Sschwarze 	case MDOC_TBL:
3642791bd1cSschwarze 		/* FALLTHROUGH */
36549aff9f8Sschwarze 	case MDOC_EQN:
3668d973ab1Sschwarze 		/* FALLTHROUGH */
36749aff9f8Sschwarze 	case MDOC_ROOT:
36820fa2881Sschwarze 		return(1);
3692791bd1cSschwarze 	default:
3702791bd1cSschwarze 		break;
371f73abda9Skristaps 	}
372f73abda9Skristaps 
37320fa2881Sschwarze 	check_args(mdoc, n);
37420fa2881Sschwarze 
375f73abda9Skristaps 	if (NULL == mdoc_valids[n->tok].pre)
376f73abda9Skristaps 		return(1);
377f73abda9Skristaps 	for (p = mdoc_valids[n->tok].pre; *p; p++)
378f73abda9Skristaps 		if ( ! (*p)(mdoc, n))
379f73abda9Skristaps 			return(0);
380f73abda9Skristaps 	return(1);
381f73abda9Skristaps }
382f73abda9Skristaps 
383f73abda9Skristaps int
384f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc)
385f73abda9Skristaps {
386f73abda9Skristaps 	v_post		*p;
387f73abda9Skristaps 
388f73abda9Skristaps 	if (MDOC_VALID & mdoc->last->flags)
389f73abda9Skristaps 		return(1);
390f73abda9Skristaps 	mdoc->last->flags |= MDOC_VALID;
391f73abda9Skristaps 
3922791bd1cSschwarze 	switch (mdoc->last->type) {
39349aff9f8Sschwarze 	case MDOC_TEXT:
3942791bd1cSschwarze 		/* FALLTHROUGH */
39549aff9f8Sschwarze 	case MDOC_EQN:
3968d973ab1Sschwarze 		/* FALLTHROUGH */
39749aff9f8Sschwarze 	case MDOC_TBL:
398f73abda9Skristaps 		return(1);
39949aff9f8Sschwarze 	case MDOC_ROOT:
400f73abda9Skristaps 		return(post_root(mdoc));
4012791bd1cSschwarze 	default:
4022791bd1cSschwarze 		break;
4032791bd1cSschwarze 	}
404f73abda9Skristaps 
405f73abda9Skristaps 	if (NULL == mdoc_valids[mdoc->last->tok].post)
406f73abda9Skristaps 		return(1);
407f73abda9Skristaps 	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
408f73abda9Skristaps 		if ( ! (*p)(mdoc))
409f73abda9Skristaps 			return(0);
410f73abda9Skristaps 
411f73abda9Skristaps 	return(1);
412f73abda9Skristaps }
413f73abda9Skristaps 
4147c2be9f8Sschwarze static int
4157ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type,
4167c2be9f8Sschwarze 		enum check_lvl lvl, enum check_ineq ineq, int val)
417f73abda9Skristaps {
4187c2be9f8Sschwarze 	const char	*p;
419bb648afaSschwarze 	enum mandocerr	 t;
4207c2be9f8Sschwarze 
4217ead8a4eSschwarze 	if (mdoc->last->type != type)
4227c2be9f8Sschwarze 		return(1);
4237c2be9f8Sschwarze 
4247c2be9f8Sschwarze 	switch (ineq) {
42549aff9f8Sschwarze 	case CHECK_LT:
4267c2be9f8Sschwarze 		p = "less than ";
4277ead8a4eSschwarze 		if (mdoc->last->nchild < val)
4287c2be9f8Sschwarze 			return(1);
4297c2be9f8Sschwarze 		break;
43049aff9f8Sschwarze 	case CHECK_GT:
431bb648afaSschwarze 		p = "more than ";
4327ead8a4eSschwarze 		if (mdoc->last->nchild > val)
4337c2be9f8Sschwarze 			return(1);
4347c2be9f8Sschwarze 		break;
43549aff9f8Sschwarze 	case CHECK_EQ:
4367c2be9f8Sschwarze 		p = "";
4377ead8a4eSschwarze 		if (val == mdoc->last->nchild)
4387c2be9f8Sschwarze 			return(1);
4397c2be9f8Sschwarze 		break;
44020fa2881Sschwarze 	default:
44120fa2881Sschwarze 		abort();
44220fa2881Sschwarze 		/* NOTREACHED */
4437c2be9f8Sschwarze 	}
4447c2be9f8Sschwarze 
445bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
44649aff9f8Sschwarze 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
44749aff9f8Sschwarze 	    mdoc->last->pos, "want %s%d children (have %d)",
4487ead8a4eSschwarze 	    p, val, mdoc->last->nchild);
44919a69263Sschwarze 	return(1);
4507c2be9f8Sschwarze }
451f73abda9Skristaps 
4527c2be9f8Sschwarze static int
4537c2be9f8Sschwarze berr_ge1(POST_ARGS)
454f73abda9Skristaps {
455f73abda9Skristaps 
456bb648afaSschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
457f73abda9Skristaps }
458f73abda9Skristaps 
4597c2be9f8Sschwarze static int
4607c2be9f8Sschwarze bwarn_ge1(POST_ARGS)
4617c2be9f8Sschwarze {
4627c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
463f73abda9Skristaps }
464f73abda9Skristaps 
4657c2be9f8Sschwarze static int
4667c2be9f8Sschwarze ewarn_eq0(POST_ARGS)
4677c2be9f8Sschwarze {
4687c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
4697c2be9f8Sschwarze }
4707c2be9f8Sschwarze 
4717c2be9f8Sschwarze static int
472bb648afaSschwarze ewarn_eq1(POST_ARGS)
473bb648afaSschwarze {
474bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
475bb648afaSschwarze }
476bb648afaSschwarze 
477bb648afaSschwarze static int
4787c2be9f8Sschwarze ewarn_ge1(POST_ARGS)
4797c2be9f8Sschwarze {
4807c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
4817c2be9f8Sschwarze }
4827c2be9f8Sschwarze 
4837c2be9f8Sschwarze static int
484bb648afaSschwarze ewarn_le1(POST_ARGS)
4857c2be9f8Sschwarze {
486bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
4877c2be9f8Sschwarze }
4887c2be9f8Sschwarze 
4897c2be9f8Sschwarze static int
4907c2be9f8Sschwarze hwarn_eq0(POST_ARGS)
4917c2be9f8Sschwarze {
4927c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
4937c2be9f8Sschwarze }
4947c2be9f8Sschwarze 
4957c2be9f8Sschwarze static int
4967c2be9f8Sschwarze hwarn_eq1(POST_ARGS)
4977c2be9f8Sschwarze {
4987c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
4997c2be9f8Sschwarze }
5007c2be9f8Sschwarze 
5017c2be9f8Sschwarze static int
502bb648afaSschwarze hwarn_ge1(POST_ARGS)
503bb648afaSschwarze {
504bb648afaSschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
505bb648afaSschwarze }
506bb648afaSschwarze 
50720fa2881Sschwarze static void
5087ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n)
509f73abda9Skristaps {
510f73abda9Skristaps 	int		 i;
511f73abda9Skristaps 
512f73abda9Skristaps 	if (NULL == n->args)
51320fa2881Sschwarze 		return;
514f73abda9Skristaps 
515f73abda9Skristaps 	assert(n->args->argc);
516f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
5177ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
518f73abda9Skristaps }
519f73abda9Skristaps 
52020fa2881Sschwarze static void
5217ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
522f73abda9Skristaps {
523f73abda9Skristaps 	int		 i;
524f73abda9Skristaps 
525f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
5267ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
527f73abda9Skristaps }
528f73abda9Skristaps 
52920fa2881Sschwarze static void
5307ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
531f73abda9Skristaps {
53204e980cbSschwarze 	char		*cp;
533769ee804Sschwarze 
5347ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
5351cdbf331Sschwarze 		return;
5361cdbf331Sschwarze 
5371cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
538dd5b31c3Sschwarze 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
539dd5b31c3Sschwarze 		    ln, pos + (int)(p - cp), NULL);
540f73abda9Skristaps }
541f73abda9Skristaps 
542f73abda9Skristaps static int
543f73abda9Skristaps pre_display(PRE_ARGS)
544f73abda9Skristaps {
545f73abda9Skristaps 	struct mdoc_node *node;
546f73abda9Skristaps 
547f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
548f73abda9Skristaps 		return(1);
549f73abda9Skristaps 
550f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
551f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
552f73abda9Skristaps 			if (MDOC_Bd == node->tok)
553f73abda9Skristaps 				break;
55420fa2881Sschwarze 
55505c39368Sschwarze 	if (node)
556b723eac2Sschwarze 		mandoc_vmsg(MANDOCERR_BD_NEST,
557b723eac2Sschwarze 		    mdoc->parse, n->line, n->pos,
558b723eac2Sschwarze 		    "%s in Bd", mdoc_macronames[n->tok]);
55905c39368Sschwarze 
56005c39368Sschwarze 	return(1);
561f73abda9Skristaps }
562f73abda9Skristaps 
563f73abda9Skristaps static int
564f73abda9Skristaps pre_bl(PRE_ARGS)
565f73abda9Skristaps {
566769ee804Sschwarze 	struct mdoc_node *np;
5674a9f685fSschwarze 	struct mdoc_argv *argv;
5684a9f685fSschwarze 	int		  i;
5694a9f685fSschwarze 	enum mdoc_list	  lt;
570f73abda9Skristaps 
5716093755cSschwarze 	if (MDOC_BLOCK != n->type) {
572769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
573769ee804Sschwarze 			assert(n->pending);
574769ee804Sschwarze 			np = n->pending->parent;
575769ee804Sschwarze 		} else
576769ee804Sschwarze 			np = n->parent;
577769ee804Sschwarze 
578769ee804Sschwarze 		assert(np);
579769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
580769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
581f73abda9Skristaps 		return(1);
5826e03d529Sschwarze 	}
583f73abda9Skristaps 
5846093755cSschwarze 	/*
5856093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
5866093755cSschwarze 	 * the first mentioned list type and warn about any remaining
5876093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
5886093755cSschwarze 	 */
589f73abda9Skristaps 
5906093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
5914a9f685fSschwarze 		argv = n->args->argv + i;
5926093755cSschwarze 		lt = LIST__NONE;
5934a9f685fSschwarze 		switch (argv->arg) {
5946093755cSschwarze 		/* Set list types. */
59549aff9f8Sschwarze 		case MDOC_Bullet:
5966093755cSschwarze 			lt = LIST_bullet;
5976093755cSschwarze 			break;
59849aff9f8Sschwarze 		case MDOC_Dash:
5996093755cSschwarze 			lt = LIST_dash;
6006093755cSschwarze 			break;
60149aff9f8Sschwarze 		case MDOC_Enum:
6026093755cSschwarze 			lt = LIST_enum;
6036093755cSschwarze 			break;
60449aff9f8Sschwarze 		case MDOC_Hyphen:
6056093755cSschwarze 			lt = LIST_hyphen;
6066093755cSschwarze 			break;
60749aff9f8Sschwarze 		case MDOC_Item:
6086093755cSschwarze 			lt = LIST_item;
6096093755cSschwarze 			break;
61049aff9f8Sschwarze 		case MDOC_Tag:
6116093755cSschwarze 			lt = LIST_tag;
6126093755cSschwarze 			break;
61349aff9f8Sschwarze 		case MDOC_Diag:
6146093755cSschwarze 			lt = LIST_diag;
6156093755cSschwarze 			break;
61649aff9f8Sschwarze 		case MDOC_Hang:
6176093755cSschwarze 			lt = LIST_hang;
6186093755cSschwarze 			break;
61949aff9f8Sschwarze 		case MDOC_Ohang:
6206093755cSschwarze 			lt = LIST_ohang;
6216093755cSschwarze 			break;
62249aff9f8Sschwarze 		case MDOC_Inset:
6236093755cSschwarze 			lt = LIST_inset;
6246093755cSschwarze 			break;
62549aff9f8Sschwarze 		case MDOC_Column:
6266093755cSschwarze 			lt = LIST_column;
62764d728e4Sschwarze 			break;
6286093755cSschwarze 		/* Set list arguments. */
62949aff9f8Sschwarze 		case MDOC_Compact:
6304a9f685fSschwarze 			if (n->norm->Bl.comp)
6314a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
6324a9f685fSschwarze 				    mdoc->parse, argv->line,
6334a9f685fSschwarze 				    argv->pos, "Bl -compact");
6344a9f685fSschwarze 			n->norm->Bl.comp = 1;
63550e63e03Sschwarze 			break;
63649aff9f8Sschwarze 		case MDOC_Width:
6374a9f685fSschwarze 			if (0 == argv->sz) {
6384a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
6394a9f685fSschwarze 				    mdoc->parse, argv->line,
6404a9f685fSschwarze 				    argv->pos, "Bl -width");
6414a9f685fSschwarze 				n->norm->Bl.width = "0n";
64222972b14Sschwarze 				break;
64322972b14Sschwarze 			}
6444a9f685fSschwarze 			if (NULL != n->norm->Bl.width)
6454a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
6464a9f685fSschwarze 				    mdoc->parse, argv->line,
6474a9f685fSschwarze 				    argv->pos, "Bl -width %s",
6484a9f685fSschwarze 				    argv->value[0]);
6494a9f685fSschwarze 			n->norm->Bl.width = argv->value[0];
65064d728e4Sschwarze 			break;
65149aff9f8Sschwarze 		case MDOC_Offset:
6524a9f685fSschwarze 			if (0 == argv->sz) {
6534a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
6544a9f685fSschwarze 				    mdoc->parse, argv->line,
6554a9f685fSschwarze 				    argv->pos, "Bl -offset");
65631e23753Sschwarze 				break;
65731e23753Sschwarze 			}
6584a9f685fSschwarze 			if (NULL != n->norm->Bl.offs)
6594a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
6604a9f685fSschwarze 				    mdoc->parse, argv->line,
6614a9f685fSschwarze 				    argv->pos, "Bl -offset %s",
6624a9f685fSschwarze 				    argv->value[0]);
6634a9f685fSschwarze 			n->norm->Bl.offs = argv->value[0];
664f73abda9Skristaps 			break;
665ddce0b0cSschwarze 		default:
666ddce0b0cSschwarze 			continue;
667f73abda9Skristaps 		}
668dc0d8bb2Sschwarze 		if (LIST__NONE == lt)
669dc0d8bb2Sschwarze 			continue;
670f73abda9Skristaps 
6716093755cSschwarze 		/* Check: multiple list types. */
6726093755cSschwarze 
673dc0d8bb2Sschwarze 		if (LIST__NONE != n->norm->Bl.type) {
674dc0d8bb2Sschwarze 			mandoc_msg(MANDOCERR_BL_REP,
675dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
676dc0d8bb2Sschwarze 			    mdoc_argnames[argv->arg]);
677dc0d8bb2Sschwarze 			continue;
678769ee804Sschwarze 		}
6796093755cSschwarze 
6806093755cSschwarze 		/* The list type should come first. */
6816093755cSschwarze 
6828c62fbf5Sschwarze 		if (n->norm->Bl.width ||
6838c62fbf5Sschwarze 		    n->norm->Bl.offs ||
6848c62fbf5Sschwarze 		    n->norm->Bl.comp)
68566788495Sschwarze 			mandoc_msg(MANDOCERR_BL_LATETYPE,
68666788495Sschwarze 			    mdoc->parse, n->line, n->pos,
68766788495Sschwarze 			    mdoc_argnames[n->args->argv[0].arg]);
688dc0d8bb2Sschwarze 
689dc0d8bb2Sschwarze 		n->norm->Bl.type = lt;
690dc0d8bb2Sschwarze 		if (LIST_column == lt) {
691dc0d8bb2Sschwarze 			n->norm->Bl.ncols = argv->sz;
692dc0d8bb2Sschwarze 			n->norm->Bl.cols = (void *)argv->value;
693dc0d8bb2Sschwarze 		}
6946093755cSschwarze 	}
6956093755cSschwarze 
6966093755cSschwarze 	/* Allow lists to default to LIST_item. */
6976093755cSschwarze 
6988c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
69966788495Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BL_NOTYPE);
7008c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
7016e03d529Sschwarze 	}
702f73abda9Skristaps 
70364d728e4Sschwarze 	/*
70464d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
70564d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
7065eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
7075eced068Sschwarze 	 * no warning.
70864d728e4Sschwarze 	 */
70964d728e4Sschwarze 
7108c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
71149aff9f8Sschwarze 	case LIST_tag:
7125eced068Sschwarze 		if (NULL == n->norm->Bl.width)
713dc0d8bb2Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BL_NOWIDTH);
714f73abda9Skristaps 		break;
71549aff9f8Sschwarze 	case LIST_column:
7166093755cSschwarze 		/* FALLTHROUGH */
71749aff9f8Sschwarze 	case LIST_diag:
7186093755cSschwarze 		/* FALLTHROUGH */
71949aff9f8Sschwarze 	case LIST_ohang:
7206093755cSschwarze 		/* FALLTHROUGH */
72149aff9f8Sschwarze 	case LIST_inset:
7226093755cSschwarze 		/* FALLTHROUGH */
72349aff9f8Sschwarze 	case LIST_item:
7248c62fbf5Sschwarze 		if (n->norm->Bl.width)
725817ac90bSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
7266093755cSschwarze 		break;
72749aff9f8Sschwarze 	case LIST_bullet:
7285eced068Sschwarze 		/* FALLTHROUGH */
72949aff9f8Sschwarze 	case LIST_dash:
7305eced068Sschwarze 		/* FALLTHROUGH */
73149aff9f8Sschwarze 	case LIST_hyphen:
7325eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7335eced068Sschwarze 			n->norm->Bl.width = "2n";
7345eced068Sschwarze 		break;
73549aff9f8Sschwarze 	case LIST_enum:
7365eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7375eced068Sschwarze 			n->norm->Bl.width = "3n";
7385eced068Sschwarze 		break;
73964d728e4Sschwarze 	default:
740f73abda9Skristaps 		break;
74164d728e4Sschwarze 	}
74264d728e4Sschwarze 
743f73abda9Skristaps 	return(1);
744f73abda9Skristaps }
745f73abda9Skristaps 
746f73abda9Skristaps static int
747f73abda9Skristaps pre_bd(PRE_ARGS)
748f73abda9Skristaps {
749769ee804Sschwarze 	struct mdoc_node *np;
7504a9f685fSschwarze 	struct mdoc_argv *argv;
7514a9f685fSschwarze 	int		  i;
7524a9f685fSschwarze 	enum mdoc_disp	  dt;
753f73abda9Skristaps 
75431e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
755769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
756769ee804Sschwarze 			assert(n->pending);
757769ee804Sschwarze 			np = n->pending->parent;
758769ee804Sschwarze 		} else
759769ee804Sschwarze 			np = n->parent;
760769ee804Sschwarze 
761769ee804Sschwarze 		assert(np);
762769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
763769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
764f73abda9Skristaps 		return(1);
7656e03d529Sschwarze 	}
766f73abda9Skristaps 
76731e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
7684a9f685fSschwarze 		argv = n->args->argv + i;
76931e23753Sschwarze 		dt = DISP__NONE;
77031e23753Sschwarze 
7714a9f685fSschwarze 		switch (argv->arg) {
77249aff9f8Sschwarze 		case MDOC_Centred:
7732065e47aSschwarze 			dt = DISP_centered;
77431e23753Sschwarze 			break;
77549aff9f8Sschwarze 		case MDOC_Ragged:
77631e23753Sschwarze 			dt = DISP_ragged;
77731e23753Sschwarze 			break;
77849aff9f8Sschwarze 		case MDOC_Unfilled:
77931e23753Sschwarze 			dt = DISP_unfilled;
78031e23753Sschwarze 			break;
78149aff9f8Sschwarze 		case MDOC_Filled:
78231e23753Sschwarze 			dt = DISP_filled;
78331e23753Sschwarze 			break;
78449aff9f8Sschwarze 		case MDOC_Literal:
78531e23753Sschwarze 			dt = DISP_literal;
786f73abda9Skristaps 			break;
78749aff9f8Sschwarze 		case MDOC_File:
78831e23753Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
7896e03d529Sschwarze 			return(0);
79049aff9f8Sschwarze 		case MDOC_Offset:
7914a9f685fSschwarze 			if (0 == argv->sz) {
7924a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7934a9f685fSschwarze 				    mdoc->parse, argv->line,
7944a9f685fSschwarze 				    argv->pos, "Bd -offset");
795f73abda9Skristaps 				break;
796f73abda9Skristaps 			}
7974a9f685fSschwarze 			if (NULL != n->norm->Bd.offs)
7984a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
7994a9f685fSschwarze 				    mdoc->parse, argv->line,
8004a9f685fSschwarze 				    argv->pos, "Bd -offset %s",
8014a9f685fSschwarze 				    argv->value[0]);
8024a9f685fSschwarze 			n->norm->Bd.offs = argv->value[0];
80331e23753Sschwarze 			break;
80449aff9f8Sschwarze 		case MDOC_Compact:
8054a9f685fSschwarze 			if (n->norm->Bd.comp)
8064a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
8074a9f685fSschwarze 				    mdoc->parse, argv->line,
8084a9f685fSschwarze 				    argv->pos, "Bd -compact");
8094a9f685fSschwarze 			n->norm->Bd.comp = 1;
81031e23753Sschwarze 			break;
81131e23753Sschwarze 		default:
81231e23753Sschwarze 			abort();
81331e23753Sschwarze 			/* NOTREACHED */
81431e23753Sschwarze 		}
815dc0d8bb2Sschwarze 		if (DISP__NONE == dt)
816dc0d8bb2Sschwarze 			continue;
81731e23753Sschwarze 
818dc0d8bb2Sschwarze 		if (DISP__NONE == n->norm->Bd.type)
8198c62fbf5Sschwarze 			n->norm->Bd.type = dt;
820dc0d8bb2Sschwarze 		else
821dc0d8bb2Sschwarze 			mandoc_msg(MANDOCERR_BD_REP,
822dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
823dc0d8bb2Sschwarze 			    mdoc_argnames[argv->arg]);
82431e23753Sschwarze 	}
82531e23753Sschwarze 
8268c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
82766788495Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BD_NOTYPE);
8288c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
82931e23753Sschwarze 	}
83031e23753Sschwarze 
83131e23753Sschwarze 	return(1);
832f73abda9Skristaps }
833f73abda9Skristaps 
834f73abda9Skristaps static int
835f73abda9Skristaps pre_an(PRE_ARGS)
836f73abda9Skristaps {
8376475d5b0Sschwarze 	int		 i;
838f73abda9Skristaps 
839769ee804Sschwarze 	if (NULL == n->args)
840f73abda9Skristaps 		return(1);
841769ee804Sschwarze 
8426475d5b0Sschwarze 	for (i = 1; i < (int)n->args->argc; i++)
84320fa2881Sschwarze 		mdoc_pmsg(mdoc, n->args->argv[i].line,
84420fa2881Sschwarze 		    n->args->argv[i].pos, MANDOCERR_IGNARGV);
8457c2be9f8Sschwarze 
846769ee804Sschwarze 	if (MDOC_Split == n->args->argv[0].arg)
8478c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
848769ee804Sschwarze 	else if (MDOC_Nosplit == n->args->argv[0].arg)
8498c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
850769ee804Sschwarze 	else
851769ee804Sschwarze 		abort();
852769ee804Sschwarze 
853769ee804Sschwarze 	return(1);
854f73abda9Skristaps }
855f73abda9Skristaps 
856f73abda9Skristaps static int
85720fa2881Sschwarze pre_std(PRE_ARGS)
858f73abda9Skristaps {
859f73abda9Skristaps 
86020fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
86120fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
86220fa2881Sschwarze 			return(1);
863f73abda9Skristaps 
86466788495Sschwarze 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
86566788495Sschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
8666093755cSschwarze 	return(1);
8676093755cSschwarze }
8686093755cSschwarze 
8696093755cSschwarze static int
870551cd4a8Sschwarze pre_obsolete(PRE_ARGS)
871551cd4a8Sschwarze {
872551cd4a8Sschwarze 
873551cd4a8Sschwarze 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
874551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
875551cd4a8Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
876551cd4a8Sschwarze 	return(1);
877551cd4a8Sschwarze }
878551cd4a8Sschwarze 
879551cd4a8Sschwarze static int
880f73abda9Skristaps pre_dt(PRE_ARGS)
881f73abda9Skristaps {
882f73abda9Skristaps 
883b058e777Sschwarze 	if (NULL == mdoc->meta.date || mdoc->meta.os)
88451fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
88551fcab2fSschwarze 		    n->line, n->pos, "Dt");
88620fa2881Sschwarze 
887f73abda9Skristaps 	if (mdoc->meta.title)
88851fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
88951fcab2fSschwarze 		    n->line, n->pos, "Dt");
89020fa2881Sschwarze 
891f73abda9Skristaps 	return(1);
892f73abda9Skristaps }
893f73abda9Skristaps 
894f73abda9Skristaps static int
895f73abda9Skristaps pre_os(PRE_ARGS)
896f73abda9Skristaps {
897f73abda9Skristaps 
898b058e777Sschwarze 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
89951fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
90051fcab2fSschwarze 		    n->line, n->pos, "Os");
90120fa2881Sschwarze 
902f73abda9Skristaps 	if (mdoc->meta.os)
90351fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
90451fcab2fSschwarze 		    n->line, n->pos, "Os");
90520fa2881Sschwarze 
906f73abda9Skristaps 	return(1);
907f73abda9Skristaps }
908f73abda9Skristaps 
909f73abda9Skristaps static int
910f73abda9Skristaps pre_dd(PRE_ARGS)
911f73abda9Skristaps {
912f73abda9Skristaps 
913f73abda9Skristaps 	if (mdoc->meta.title || mdoc->meta.os)
91451fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
91551fcab2fSschwarze 		    n->line, n->pos, "Dd");
91620fa2881Sschwarze 
917f73abda9Skristaps 	if (mdoc->meta.date)
91851fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
91951fcab2fSschwarze 		    n->line, n->pos, "Dd");
92020fa2881Sschwarze 
921f73abda9Skristaps 	return(1);
922f73abda9Skristaps }
923f73abda9Skristaps 
924f73abda9Skristaps static int
925f73abda9Skristaps post_bf(POST_ARGS)
926f73abda9Skristaps {
927ecb10c32Sschwarze 	struct mdoc_node *np, *nch;
928ddce0b0cSschwarze 	enum mdocargt	  arg;
929f73abda9Skristaps 
930769ee804Sschwarze 	/*
931769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
932769ee804Sschwarze 	 * element, which contains the goods.
933769ee804Sschwarze 	 */
934769ee804Sschwarze 
935769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
936769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
937769ee804Sschwarze 			assert(mdoc->last->pending);
938769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
939769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
940769ee804Sschwarze 			np = mdoc->last->parent->head;
941769ee804Sschwarze 		} else
942769ee804Sschwarze 			np = mdoc->last->head;
943769ee804Sschwarze 
944769ee804Sschwarze 		assert(np);
945769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
946769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
947f73abda9Skristaps 		return(1);
9486e03d529Sschwarze 	}
949f73abda9Skristaps 
950769ee804Sschwarze 	np = mdoc->last;
951769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
952769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
95350d41253Sschwarze 
954ecb10c32Sschwarze 	/* Check the number of arguments. */
955f73abda9Skristaps 
956ecb10c32Sschwarze 	nch = np->child;
957ecb10c32Sschwarze 	if (NULL == np->parent->args) {
958ecb10c32Sschwarze 		if (NULL == nch) {
959ecb10c32Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_BF_NOFONT);
96020fa2881Sschwarze 			return(1);
96120fa2881Sschwarze 		}
962ecb10c32Sschwarze 		nch = nch->next;
963ecb10c32Sschwarze 	}
964ecb10c32Sschwarze 	if (NULL != nch)
965ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
966ecb10c32Sschwarze 		    nch->line, nch->pos, "Bf ... %s", nch->string);
967769ee804Sschwarze 
968769ee804Sschwarze 	/* Extract argument into data. */
969769ee804Sschwarze 
970769ee804Sschwarze 	if (np->parent->args) {
971769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
972769ee804Sschwarze 		if (MDOC_Emphasis == arg)
9738c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
974769ee804Sschwarze 		else if (MDOC_Literal == arg)
9758c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
976769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
9778c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
978769ee804Sschwarze 		else
979769ee804Sschwarze 			abort();
980769ee804Sschwarze 		return(1);
981769ee804Sschwarze 	}
982769ee804Sschwarze 
983769ee804Sschwarze 	/* Extract parameter into data. */
984769ee804Sschwarze 
985769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
9868c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
987769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
9888c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
989769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
9908c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
99120fa2881Sschwarze 	else
992ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
993ecb10c32Sschwarze 		    np->child->line, np->child->pos,
994ecb10c32Sschwarze 		    "Bf %s", np->child->string);
995769ee804Sschwarze 
996769ee804Sschwarze 	return(1);
997f73abda9Skristaps }
998f73abda9Skristaps 
999f73abda9Skristaps static int
100071719887Sschwarze post_lb(POST_ARGS)
100171719887Sschwarze {
100277e000ffSschwarze 	struct mdoc_node	*n;
100377e000ffSschwarze 	const char		*stdlibname;
100477e000ffSschwarze 	char			*libname;
100571719887Sschwarze 
1006bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1007bb648afaSschwarze 
100877e000ffSschwarze 	n = mdoc->last->child;
100920fa2881Sschwarze 
101077e000ffSschwarze 	assert(n);
101177e000ffSschwarze 	assert(MDOC_TEXT == n->type);
101220fa2881Sschwarze 
101377e000ffSschwarze 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
101477e000ffSschwarze 		mandoc_asprintf(&libname,
101577e000ffSschwarze 		    "library \\(lq%s\\(rq", n->string);
101677e000ffSschwarze 	else
101777e000ffSschwarze 		libname = mandoc_strdup(stdlibname);
101820fa2881Sschwarze 
101977e000ffSschwarze 	free(n->string);
102077e000ffSschwarze 	n->string = libname;
102120fa2881Sschwarze 	return(1);
102220fa2881Sschwarze }
102371719887Sschwarze 
102471719887Sschwarze static int
1025b31af00dSschwarze post_eoln(POST_ARGS)
1026b31af00dSschwarze {
1027ecb10c32Sschwarze 	const struct mdoc_node *n;
1028b31af00dSschwarze 
1029ecb10c32Sschwarze 	n = mdoc->last;
1030ecb10c32Sschwarze 	if (n->child)
1031ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
1032ecb10c32Sschwarze 		    mdoc->parse, n->line, n->pos,
1033ecb10c32Sschwarze 		    "%s %s", mdoc_macronames[n->tok],
1034ecb10c32Sschwarze 		    n->child->string);
1035b31af00dSschwarze 	return(1);
1036b31af00dSschwarze }
1037b31af00dSschwarze 
1038b31af00dSschwarze static int
10398521b0bcSschwarze post_vt(POST_ARGS)
10408521b0bcSschwarze {
10418521b0bcSschwarze 	const struct mdoc_node *n;
10428521b0bcSschwarze 
10438521b0bcSschwarze 	/*
10448521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
10458521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1046e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1047e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
10488521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
10498521b0bcSschwarze 	 */
10508521b0bcSschwarze 
10518521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
10528521b0bcSschwarze 		return(1);
10538521b0bcSschwarze 
10548521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1055bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
1056dd25b57cSschwarze 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1057dd25b57cSschwarze 			    n->line, n->pos, mdoc_macronames[n->tok]);
10588521b0bcSschwarze 
10598521b0bcSschwarze 	return(1);
10608521b0bcSschwarze }
10618521b0bcSschwarze 
10628521b0bcSschwarze static int
1063f73abda9Skristaps post_nm(POST_ARGS)
1064f73abda9Skristaps {
106520fa2881Sschwarze 
1066160ac481Sschwarze 	if (NULL != mdoc->meta.name)
106720fa2881Sschwarze 		return(1);
106820fa2881Sschwarze 
106983af2bccSschwarze 	mdoc_deroff(&mdoc->meta.name, mdoc->last);
107020fa2881Sschwarze 
1071e214f641Sschwarze 	if (NULL == mdoc->meta.name)
1072e214f641Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NM_NONAME);
107320fa2881Sschwarze 	return(1);
107420fa2881Sschwarze }
107520fa2881Sschwarze 
107620fa2881Sschwarze static int
107720fa2881Sschwarze post_literal(POST_ARGS)
107820fa2881Sschwarze {
107920fa2881Sschwarze 
108020fa2881Sschwarze 	/*
108120fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
108220fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
108320fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
108420fa2881Sschwarze 	 * off in general since displays can't be nested.
108520fa2881Sschwarze 	 */
108620fa2881Sschwarze 
108720fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
108820fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
108920fa2881Sschwarze 
109020fa2881Sschwarze 	return(1);
109120fa2881Sschwarze }
109220fa2881Sschwarze 
109320fa2881Sschwarze static int
109420fa2881Sschwarze post_defaults(POST_ARGS)
109520fa2881Sschwarze {
109620fa2881Sschwarze 	struct mdoc_node *nn;
109720fa2881Sschwarze 
109820fa2881Sschwarze 	/*
109920fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
110020fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
110120fa2881Sschwarze 	 * gets an empty string.
110220fa2881Sschwarze 	 */
1103f73abda9Skristaps 
1104f73abda9Skristaps 	if (mdoc->last->child)
1105f73abda9Skristaps 		return(1);
110620fa2881Sschwarze 
110720fa2881Sschwarze 	nn = mdoc->last;
110820fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
110920fa2881Sschwarze 
111020fa2881Sschwarze 	switch (nn->tok) {
111149aff9f8Sschwarze 	case MDOC_Ar:
111220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
111320fa2881Sschwarze 			return(0);
111420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
111520fa2881Sschwarze 			return(0);
111620fa2881Sschwarze 		break;
111749aff9f8Sschwarze 	case MDOC_At:
111820fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
111920fa2881Sschwarze 			return(0);
112020fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
112120fa2881Sschwarze 			return(0);
112220fa2881Sschwarze 		break;
112349aff9f8Sschwarze 	case MDOC_Li:
112420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
112520fa2881Sschwarze 			return(0);
112620fa2881Sschwarze 		break;
112749aff9f8Sschwarze 	case MDOC_Pa:
112820fa2881Sschwarze 		/* FALLTHROUGH */
112949aff9f8Sschwarze 	case MDOC_Mt:
113020fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
113120fa2881Sschwarze 			return(0);
113220fa2881Sschwarze 		break;
113320fa2881Sschwarze 	default:
113420fa2881Sschwarze 		abort();
113520fa2881Sschwarze 		/* NOTREACHED */
1136f73abda9Skristaps 	}
1137f73abda9Skristaps 
113820fa2881Sschwarze 	mdoc->last = nn;
113920fa2881Sschwarze 	return(1);
114020fa2881Sschwarze }
1141f73abda9Skristaps 
1142f73abda9Skristaps static int
1143f73abda9Skristaps post_at(POST_ARGS)
1144f73abda9Skristaps {
11450b2f1307Sschwarze 	struct mdoc_node	*n;
11460b2f1307Sschwarze 	const char		*std_att;
11470b2f1307Sschwarze 	char			*att;
114820fa2881Sschwarze 
114920fa2881Sschwarze 	/*
115020fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
115120fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
115220fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
115320fa2881Sschwarze 	 */
1154f73abda9Skristaps 
11550b2f1307Sschwarze 	if (NULL == (n = mdoc->last->child))
1156f73abda9Skristaps 		return(1);
115720fa2881Sschwarze 
11580b2f1307Sschwarze 	assert(MDOC_TEXT == n->type);
11590b2f1307Sschwarze 	if (NULL == (std_att = mdoc_a2att(n->string))) {
1160dc0d8bb2Sschwarze 		mandoc_msg(MANDOCERR_AT_BAD, mdoc->parse,
1161dc0d8bb2Sschwarze 		    n->line, n->pos, n->string);
11620b2f1307Sschwarze 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
11630b2f1307Sschwarze 	} else
11640b2f1307Sschwarze 		att = mandoc_strdup(std_att);
1165f73abda9Skristaps 
11660b2f1307Sschwarze 	free(n->string);
11670b2f1307Sschwarze 	n->string = att;
116820fa2881Sschwarze 	return(1);
116920fa2881Sschwarze }
1170f73abda9Skristaps 
1171f73abda9Skristaps static int
1172f73abda9Skristaps post_an(POST_ARGS)
1173f73abda9Skristaps {
1174769ee804Sschwarze 	struct mdoc_node *np;
1175f73abda9Skristaps 
1176769ee804Sschwarze 	np = mdoc->last;
1177e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1178e7a93ef3Sschwarze 		if (0 == np->child)
1179e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1180e7a93ef3Sschwarze 	} else if (np->child)
1181bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
118220fa2881Sschwarze 
118320fa2881Sschwarze 	return(1);
1184f73abda9Skristaps }
1185f73abda9Skristaps 
1186f73abda9Skristaps static int
1187551cd4a8Sschwarze post_en(POST_ARGS)
1188551cd4a8Sschwarze {
1189551cd4a8Sschwarze 
1190551cd4a8Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
1191551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1192551cd4a8Sschwarze 	return(1);
1193551cd4a8Sschwarze }
1194551cd4a8Sschwarze 
1195551cd4a8Sschwarze static int
1196551cd4a8Sschwarze post_es(POST_ARGS)
1197551cd4a8Sschwarze {
1198551cd4a8Sschwarze 
1199551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1200551cd4a8Sschwarze 	return(1);
1201551cd4a8Sschwarze }
1202551cd4a8Sschwarze 
1203551cd4a8Sschwarze static int
1204f73abda9Skristaps post_it(POST_ARGS)
1205f73abda9Skristaps {
120619a69263Sschwarze 	int		  i, cols;
12076093755cSschwarze 	enum mdoc_list	  lt;
12089530682eSschwarze 	struct mdoc_node *nbl, *nit, *nch;
1209f73abda9Skristaps 
12109530682eSschwarze 	nit = mdoc->last;
12119530682eSschwarze 	if (MDOC_BLOCK != nit->type)
1212f73abda9Skristaps 		return(1);
1213f73abda9Skristaps 
12149530682eSschwarze 	nbl = nit->parent->parent;
12159530682eSschwarze 	lt = nbl->norm->Bl.type;
12166093755cSschwarze 
12176093755cSschwarze 	switch (lt) {
121849aff9f8Sschwarze 	case LIST_tag:
12199530682eSschwarze 		/* FALLTHROUGH */
122049aff9f8Sschwarze 	case LIST_hang:
1221f73abda9Skristaps 		/* FALLTHROUGH */
122249aff9f8Sschwarze 	case LIST_ohang:
1223f73abda9Skristaps 		/* FALLTHROUGH */
122449aff9f8Sschwarze 	case LIST_inset:
1225f73abda9Skristaps 		/* FALLTHROUGH */
122649aff9f8Sschwarze 	case LIST_diag:
12279530682eSschwarze 		if (NULL == nit->head->child)
12289530682eSschwarze 			mandoc_msg(MANDOCERR_IT_NOHEAD,
12299530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
12309530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1231f73abda9Skristaps 		break;
123249aff9f8Sschwarze 	case LIST_bullet:
1233f73abda9Skristaps 		/* FALLTHROUGH */
123449aff9f8Sschwarze 	case LIST_dash:
1235f73abda9Skristaps 		/* FALLTHROUGH */
123649aff9f8Sschwarze 	case LIST_enum:
1237f73abda9Skristaps 		/* FALLTHROUGH */
123849aff9f8Sschwarze 	case LIST_hyphen:
12399530682eSschwarze 		if (NULL == nit->body->child)
124066788495Sschwarze 			mandoc_msg(MANDOCERR_IT_NOBODY,
124166788495Sschwarze 			    mdoc->parse, nit->line, nit->pos,
124266788495Sschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1243f73abda9Skristaps 		/* FALLTHROUGH */
124449aff9f8Sschwarze 	case LIST_item:
12459530682eSschwarze 		if (NULL != nit->head->child)
1246ecb10c32Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1247ecb10c32Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1248ecb10c32Sschwarze 			    "It %s", nit->head->child->string);
1249f73abda9Skristaps 		break;
125049aff9f8Sschwarze 	case LIST_column:
12519530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
12526093755cSschwarze 
12539530682eSschwarze 		assert(NULL == nit->head->child);
12546093755cSschwarze 
12559530682eSschwarze 		for (i = 0, nch = nit->child; nch; nch = nch->next)
12569530682eSschwarze 			if (MDOC_BODY == nch->type)
1257f73abda9Skristaps 				i++;
125853292e81Sschwarze 
1259e14c4c11Sschwarze 		if (i < cols || i > cols + 1)
1260e14c4c11Sschwarze 			mandoc_vmsg(MANDOCERR_ARGCOUNT,
1261e14c4c11Sschwarze 			    mdoc->parse, nit->line, nit->pos,
12626e03d529Sschwarze 			    "columns == %d (have %d)", cols, i);
1263e14c4c11Sschwarze 		break;
1264f73abda9Skristaps 	default:
126566788495Sschwarze 		abort();
1266f73abda9Skristaps 	}
1267f73abda9Skristaps 
1268f73abda9Skristaps 	return(1);
1269f73abda9Skristaps }
1270f73abda9Skristaps 
127120fa2881Sschwarze static int
127220fa2881Sschwarze post_bl_block(POST_ARGS)
127320fa2881Sschwarze {
1274bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
127520fa2881Sschwarze 
127620fa2881Sschwarze 	/*
127720fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
127820fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
127920fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
128020fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
128120fa2881Sschwarze 	 * rewritten into real lengths).
128220fa2881Sschwarze 	 */
128320fa2881Sschwarze 
128420fa2881Sschwarze 	n = mdoc->last;
128520fa2881Sschwarze 
12868c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
12878c62fbf5Sschwarze 	    NULL == n->norm->Bl.width) {
128820fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
128920fa2881Sschwarze 			return(0);
1290bb99f0faSschwarze 		assert(n->norm->Bl.width);
12918c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
129220fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
129320fa2881Sschwarze 			return(0);
12948c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1295bb99f0faSschwarze 	}
1296bb99f0faSschwarze 
1297bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1298bb99f0faSschwarze 		if (NULL == ni->body)
1299bb99f0faSschwarze 			continue;
1300bb99f0faSschwarze 		nc = ni->body->last;
1301bb99f0faSschwarze 		while (NULL != nc) {
1302bb99f0faSschwarze 			switch (nc->tok) {
130349aff9f8Sschwarze 			case MDOC_Pp:
1304bb99f0faSschwarze 				/* FALLTHROUGH */
130549aff9f8Sschwarze 			case MDOC_Lp:
1306bb99f0faSschwarze 				/* FALLTHROUGH */
130749aff9f8Sschwarze 			case MDOC_br:
1308bb99f0faSschwarze 				break;
1309bb99f0faSschwarze 			default:
1310bb99f0faSschwarze 				nc = NULL;
1311bb99f0faSschwarze 				continue;
1312bb99f0faSschwarze 			}
1313bb99f0faSschwarze 			if (NULL == ni->next) {
131420369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
131520369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
131620369664Sschwarze 				    mdoc_macronames[nc->tok]);
1317bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1318bb99f0faSschwarze 					return(0);
1319bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1320bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
132120369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
132220369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
132320369664Sschwarze 				    "%s before It",
132420369664Sschwarze 				    mdoc_macronames[nc->tok]);
1325bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1326bb99f0faSschwarze 			} else
1327bb99f0faSschwarze 				break;
1328bb99f0faSschwarze 			nc = ni->body->last;
1329bb99f0faSschwarze 		}
1330bb99f0faSschwarze 	}
133120fa2881Sschwarze 	return(1);
133220fa2881Sschwarze }
133320fa2881Sschwarze 
133420fa2881Sschwarze static int
133520fa2881Sschwarze post_bl_block_width(POST_ARGS)
133620fa2881Sschwarze {
133720fa2881Sschwarze 	size_t		  width;
133820fa2881Sschwarze 	int		  i;
133920fa2881Sschwarze 	enum mdoct	  tok;
134020fa2881Sschwarze 	struct mdoc_node *n;
134147813146Sschwarze 	char		  buf[24];
134220fa2881Sschwarze 
134320fa2881Sschwarze 	n = mdoc->last;
134420fa2881Sschwarze 
134520fa2881Sschwarze 	/*
134620fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
134720fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
134820fa2881Sschwarze 	 * literal string, or a scaling width.
134920fa2881Sschwarze 	 *
135020fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
135120fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
135220fa2881Sschwarze 	 */
135320fa2881Sschwarze 
13548c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
135520fa2881Sschwarze 		width = 6;
13568c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
135720fa2881Sschwarze 		return(1);
1358dc0d8bb2Sschwarze 	else
1359dc0d8bb2Sschwarze 		width = macro2len(tok);
136020fa2881Sschwarze 
136120fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
136220fa2881Sschwarze 
136320fa2881Sschwarze 	assert(n->args);
136420fa2881Sschwarze 
136520fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
136620fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
136720fa2881Sschwarze 			break;
136820fa2881Sschwarze 
136920fa2881Sschwarze 	assert(i < (int)n->args->argc);
137020fa2881Sschwarze 
137147813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
137220fa2881Sschwarze 	free(n->args->argv[i].value[0]);
137320fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
137420fa2881Sschwarze 
137520fa2881Sschwarze 	/* Set our width! */
13768c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
137720fa2881Sschwarze 	return(1);
137820fa2881Sschwarze }
137920fa2881Sschwarze 
138020fa2881Sschwarze static int
138120fa2881Sschwarze post_bl_block_tag(POST_ARGS)
138220fa2881Sschwarze {
138320fa2881Sschwarze 	struct mdoc_node *n, *nn;
138420fa2881Sschwarze 	size_t		  sz, ssz;
138520fa2881Sschwarze 	int		  i;
138647813146Sschwarze 	char		  buf[24];
138720fa2881Sschwarze 
138820fa2881Sschwarze 	/*
138920fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
139020fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
139120fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
139220fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
139320fa2881Sschwarze 	 */
139420fa2881Sschwarze 
139520fa2881Sschwarze 	sz = 10;
139620fa2881Sschwarze 	n = mdoc->last;
139720fa2881Sschwarze 
139820fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
139920fa2881Sschwarze 		if (MDOC_It != nn->tok)
140020fa2881Sschwarze 			continue;
140120fa2881Sschwarze 
140220fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
140320fa2881Sschwarze 		nn = nn->head->child;
140420fa2881Sschwarze 
140520fa2881Sschwarze 		if (nn == NULL)
140620fa2881Sschwarze 			break;
140720fa2881Sschwarze 
140820fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
140920fa2881Sschwarze 			sz = strlen(nn->string) + 1;
141020fa2881Sschwarze 			break;
141120fa2881Sschwarze 		}
141220fa2881Sschwarze 
141319a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
141420fa2881Sschwarze 			sz = ssz;
141520fa2881Sschwarze 
141620fa2881Sschwarze 		break;
141720fa2881Sschwarze 	}
141820fa2881Sschwarze 
141920fa2881Sschwarze 	/* Defaults to ten ens. */
142020fa2881Sschwarze 
142147813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
142220fa2881Sschwarze 
142320fa2881Sschwarze 	/*
142420fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
142520fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
142620fa2881Sschwarze 	 */
142720fa2881Sschwarze 
142820fa2881Sschwarze 	assert(n->args);
142920fa2881Sschwarze 	i = (int)(n->args->argc)++;
143020fa2881Sschwarze 
14318286bf36Sschwarze 	n->args->argv = mandoc_reallocarray(n->args->argv,
14328286bf36Sschwarze 	    n->args->argc, sizeof(struct mdoc_argv));
143320fa2881Sschwarze 
143420fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
143520fa2881Sschwarze 	n->args->argv[i].line = n->line;
143620fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
143720fa2881Sschwarze 	n->args->argv[i].sz = 1;
143820fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
143920fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
144020fa2881Sschwarze 
144120fa2881Sschwarze 	/* Set our width! */
14428c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
144320fa2881Sschwarze 	return(1);
144420fa2881Sschwarze }
144520fa2881Sschwarze 
1446f73abda9Skristaps static int
1447395185ccSschwarze post_bl_head(POST_ARGS)
1448395185ccSschwarze {
144920fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
1450*f5174743Sschwarze 	struct mdoc_argv *argv;
145120fa2881Sschwarze 	int		  i, j;
1452395185ccSschwarze 
14538c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
145420fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
145520fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1456395185ccSschwarze 
145720fa2881Sschwarze 	/*
1458*f5174743Sschwarze 	 * Append old-style lists, where the column width specifiers
145920fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
146020fa2881Sschwarze 	 * lists where they're argument values following -column.
146120fa2881Sschwarze 	 */
146220fa2881Sschwarze 
1463*f5174743Sschwarze 	if (mdoc->last->child == NULL)
146420fa2881Sschwarze 		return(1);
146520fa2881Sschwarze 
146620fa2881Sschwarze 	np = mdoc->last->parent;
146720fa2881Sschwarze 	assert(np->args);
146820fa2881Sschwarze 
146920fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
147020fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
147120fa2881Sschwarze 			break;
147220fa2881Sschwarze 
147320fa2881Sschwarze 	assert(j < (int)np->args->argc);
147420fa2881Sschwarze 
147520fa2881Sschwarze 	/*
1476a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
147720fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
147820fa2881Sschwarze 	 * column field.  Then, delete the head children.
147920fa2881Sschwarze 	 */
148020fa2881Sschwarze 
1481*f5174743Sschwarze 	argv = np->args->argv + j;
1482*f5174743Sschwarze 	i = argv->sz;
1483*f5174743Sschwarze 	argv->sz += mdoc->last->nchild;
1484*f5174743Sschwarze 	argv->value = mandoc_reallocarray(argv->value,
1485*f5174743Sschwarze 	    argv->sz, sizeof(char *));
148620fa2881Sschwarze 
1487*f5174743Sschwarze 	mdoc->last->norm->Bl.ncols = argv->sz;
1488*f5174743Sschwarze 	mdoc->last->norm->Bl.cols = (void *)argv->value;
148920fa2881Sschwarze 
1490*f5174743Sschwarze 	for (nn = mdoc->last->child; nn; i++) {
1491*f5174743Sschwarze 		argv->value[i] = nn->string;
149220fa2881Sschwarze 		nn->string = NULL;
149320fa2881Sschwarze 		nnp = nn;
149420fa2881Sschwarze 		nn = nn->next;
149520fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1496395185ccSschwarze 	}
149720fa2881Sschwarze 
149820fa2881Sschwarze 	mdoc->last->nchild = 0;
149920fa2881Sschwarze 	mdoc->last->child = NULL;
150020fa2881Sschwarze 
15016093755cSschwarze 	return(1);
1502b16e7ddfSschwarze }
1503b16e7ddfSschwarze 
1504395185ccSschwarze static int
1505f73abda9Skristaps post_bl(POST_ARGS)
1506f73abda9Skristaps {
15072a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
15082a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
15092a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1510f73abda9Skristaps 
15112a427d60Sschwarze 	nbody = mdoc->last;
15122a427d60Sschwarze 	switch (nbody->type) {
151349aff9f8Sschwarze 	case MDOC_BLOCK:
151420fa2881Sschwarze 		return(post_bl_block(mdoc));
151549aff9f8Sschwarze 	case MDOC_HEAD:
15162a427d60Sschwarze 		return(post_bl_head(mdoc));
151749aff9f8Sschwarze 	case MDOC_BODY:
1518f6127a73Sschwarze 		break;
15192a427d60Sschwarze 	default:
15202a427d60Sschwarze 		return(1);
1521f6127a73Sschwarze 	}
1522f6127a73Sschwarze 
15232a427d60Sschwarze 	nchild = nbody->child;
15242a427d60Sschwarze 	while (NULL != nchild) {
15252a427d60Sschwarze 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
15262a427d60Sschwarze 			nchild = nchild->next;
15272a427d60Sschwarze 			continue;
15282a427d60Sschwarze 		}
15292a427d60Sschwarze 
1530dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1531dd25b57cSschwarze 		    nchild->line, nchild->pos,
1532dd25b57cSschwarze 		    mdoc_macronames[nchild->tok]);
15332a427d60Sschwarze 
15342a427d60Sschwarze 		/*
15352a427d60Sschwarze 		 * Move the node out of the Bl block.
15362a427d60Sschwarze 		 * First, collect all required node pointers.
15372a427d60Sschwarze 		 */
15382a427d60Sschwarze 
15392a427d60Sschwarze 		nblock  = nbody->parent;
15402a427d60Sschwarze 		nprev   = nblock->prev;
15412a427d60Sschwarze 		nparent = nblock->parent;
15422a427d60Sschwarze 		nnext   = nchild->next;
15432a427d60Sschwarze 
15442a427d60Sschwarze 		/*
15452a427d60Sschwarze 		 * Unlink this child.
15462a427d60Sschwarze 		 */
15472a427d60Sschwarze 
15482a427d60Sschwarze 		assert(NULL == nchild->prev);
15492a427d60Sschwarze 		if (0 == --nbody->nchild) {
15502a427d60Sschwarze 			nbody->child = NULL;
15512a427d60Sschwarze 			nbody->last  = NULL;
15522a427d60Sschwarze 			assert(NULL == nnext);
15532a427d60Sschwarze 		} else {
15542a427d60Sschwarze 			nbody->child = nnext;
15552a427d60Sschwarze 			nnext->prev = NULL;
15562a427d60Sschwarze 		}
15572a427d60Sschwarze 
15582a427d60Sschwarze 		/*
15592a427d60Sschwarze 		 * Relink this child.
15602a427d60Sschwarze 		 */
15612a427d60Sschwarze 
15622a427d60Sschwarze 		nchild->parent = nparent;
15632a427d60Sschwarze 		nchild->prev   = nprev;
15642a427d60Sschwarze 		nchild->next   = nblock;
15652a427d60Sschwarze 
15662a427d60Sschwarze 		nblock->prev = nchild;
15672a427d60Sschwarze 		nparent->nchild++;
15682a427d60Sschwarze 		if (NULL == nprev)
15692a427d60Sschwarze 			nparent->child = nchild;
15702a427d60Sschwarze 		else
15712a427d60Sschwarze 			nprev->next = nchild;
15722a427d60Sschwarze 
15732a427d60Sschwarze 		nchild = nnext;
1574f73abda9Skristaps 	}
1575f73abda9Skristaps 
1576f73abda9Skristaps 	return(1);
1577f73abda9Skristaps }
1578f73abda9Skristaps 
1579f73abda9Skristaps static int
1580f73abda9Skristaps ebool(struct mdoc *mdoc)
1581f73abda9Skristaps {
1582dc0d8bb2Sschwarze 	struct mdoc_node	*nch;
1583dc0d8bb2Sschwarze 	enum mdoct		 tok;
1584f73abda9Skristaps 
1585dc0d8bb2Sschwarze 	tok = mdoc->last->tok;
1586dc0d8bb2Sschwarze 	nch = mdoc->last->child;
1587dc0d8bb2Sschwarze 
1588dc0d8bb2Sschwarze 	if (NULL == nch) {
1589dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1590f9e7bf99Sschwarze 			mdoc->flags ^= MDOC_SMOFF;
1591f73abda9Skristaps 		return(1);
1592bb648afaSschwarze 	}
1593f9e7bf99Sschwarze 
1594f9e7bf99Sschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1595f73abda9Skristaps 
1596dc0d8bb2Sschwarze 	assert(MDOC_TEXT == nch->type);
159720fa2881Sschwarze 
1598dc0d8bb2Sschwarze 	if (0 == strcmp(nch->string, "on")) {
1599dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1600ec2beb53Sschwarze 			mdoc->flags &= ~MDOC_SMOFF;
160120fa2881Sschwarze 		return(1);
1602ec2beb53Sschwarze 	}
1603dc0d8bb2Sschwarze 	if (0 == strcmp(nch->string, "off")) {
1604dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1605ec2beb53Sschwarze 			mdoc->flags |= MDOC_SMOFF;
160620fa2881Sschwarze 		return(1);
1607ec2beb53Sschwarze 	}
160820fa2881Sschwarze 
1609dc0d8bb2Sschwarze 	mandoc_vmsg(MANDOCERR_SM_BAD,
1610dc0d8bb2Sschwarze 	    mdoc->parse, nch->line, nch->pos,
1611dc0d8bb2Sschwarze 	    "%s %s", mdoc_macronames[tok], nch->string);
1612dc0d8bb2Sschwarze 	return(mdoc_node_relink(mdoc, nch));
161320fa2881Sschwarze }
1614f73abda9Skristaps 
1615f73abda9Skristaps static int
1616f73abda9Skristaps post_root(POST_ARGS)
1617f73abda9Skristaps {
161820fa2881Sschwarze 	struct mdoc_node *n;
1619f73abda9Skristaps 
1620ac1f49d0Sschwarze 	/* Add missing prologue data. */
162120fa2881Sschwarze 
162220fa2881Sschwarze 	if ( ! (MDOC_PBODY & mdoc->flags)) {
1623ac1f49d0Sschwarze 		mandoc_msg(MANDOCERR_PROLOG_BAD, mdoc->parse, 0, 0, "EOF");
1624ac1f49d0Sschwarze 		if (mdoc->meta.date == NULL)
1625ac1f49d0Sschwarze 			mdoc->meta.date = mdoc->quick ?
1626ac1f49d0Sschwarze 			    mandoc_strdup("") :
1627ac1f49d0Sschwarze 			    mandoc_normdate(mdoc->parse, NULL, 0, 0);
1628ac1f49d0Sschwarze 		if (mdoc->meta.title == NULL)
1629ac1f49d0Sschwarze 			mdoc->meta.title = mandoc_strdup("UNKNOWN");
1630ac1f49d0Sschwarze 		if (mdoc->meta.vol == NULL)
1631ac1f49d0Sschwarze 			mdoc->meta.vol = mandoc_strdup("LOCAL");
1632ac1f49d0Sschwarze 		if (mdoc->meta.arch == NULL)
1633ac1f49d0Sschwarze 			mdoc->meta.msec = mandoc_strdup("1");
1634ac1f49d0Sschwarze 		if (mdoc->meta.os == NULL)
1635ac1f49d0Sschwarze 			mdoc->meta.os = mandoc_strdup("UNKNOWN");
1636f73abda9Skristaps 	}
1637f73abda9Skristaps 
163820fa2881Sschwarze 	n = mdoc->first;
163920fa2881Sschwarze 	assert(n);
164020fa2881Sschwarze 
164120fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
164220fa2881Sschwarze 
164343edbcc8Sschwarze 	if (NULL == n->child)
164443edbcc8Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY);
164551fcab2fSschwarze 	else if (MDOC_Sh != n->child->tok)
164651fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
164751fcab2fSschwarze 		    n->child->line, n->child->pos,
164851fcab2fSschwarze 		    mdoc_macronames[n->child->tok]);
164920fa2881Sschwarze 
1650ac1f49d0Sschwarze 	return(1);
165120fa2881Sschwarze }
1652f73abda9Skristaps 
1653f73abda9Skristaps static int
1654f73abda9Skristaps post_st(POST_ARGS)
1655f73abda9Skristaps {
1656dc0d8bb2Sschwarze 	struct mdoc_node	 *n, *nch;
165720fa2881Sschwarze 	const char		 *p;
1658f73abda9Skristaps 
1659dc0d8bb2Sschwarze 	n = mdoc->last;
1660dc0d8bb2Sschwarze 	nch = n->child;
1661dc0d8bb2Sschwarze 
1662dc0d8bb2Sschwarze 	if (NULL == nch) {
1663307e5a07Sschwarze 		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1664dc0d8bb2Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1665dc0d8bb2Sschwarze 		mdoc_node_delete(mdoc, n);
1666bb648afaSschwarze 		return(1);
1667bb648afaSschwarze 	}
166820fa2881Sschwarze 
1669dc0d8bb2Sschwarze 	assert(MDOC_TEXT == nch->type);
167020fa2881Sschwarze 
1671dc0d8bb2Sschwarze 	if (NULL == (p = mdoc_a2st(nch->string))) {
1672dc0d8bb2Sschwarze 		mandoc_msg(MANDOCERR_ST_BAD, mdoc->parse,
1673dc0d8bb2Sschwarze 		    nch->line, nch->pos, nch->string);
1674dc0d8bb2Sschwarze 		mdoc_node_delete(mdoc, n);
167520fa2881Sschwarze 	} else {
1676dc0d8bb2Sschwarze 		free(nch->string);
1677dc0d8bb2Sschwarze 		nch->string = mandoc_strdup(p);
1678f73abda9Skristaps 	}
1679f73abda9Skristaps 
168020fa2881Sschwarze 	return(1);
168120fa2881Sschwarze }
1682f73abda9Skristaps 
1683f73abda9Skristaps static int
1684011fe33bSschwarze post_rs(POST_ARGS)
1685011fe33bSschwarze {
168620fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
168720fa2881Sschwarze 	int		  i, j;
1688011fe33bSschwarze 
1689bb648afaSschwarze 	switch (mdoc->last->type) {
169049aff9f8Sschwarze 	case MDOC_HEAD:
1691bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1692011fe33bSschwarze 		return(1);
169349aff9f8Sschwarze 	case MDOC_BODY:
1694bb648afaSschwarze 		if (mdoc->last->child)
1695bb648afaSschwarze 			break;
1696bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1697bb648afaSschwarze 		return(1);
1698bb648afaSschwarze 	default:
1699bb648afaSschwarze 		return(1);
1700bb648afaSschwarze 	}
1701011fe33bSschwarze 
170220fa2881Sschwarze 	/*
170320fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
170420fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
1705b538baa5Sschwarze 	 * and correctly order it.  This is an insertion sort.
170620fa2881Sschwarze 	 */
170720fa2881Sschwarze 
170820fa2881Sschwarze 	next = NULL;
170920fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
171020fa2881Sschwarze 		/* Determine order of `nn'. */
171120fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
171220fa2881Sschwarze 			if (rsord[i] == nn->tok)
171320fa2881Sschwarze 				break;
171420fa2881Sschwarze 
1715b538baa5Sschwarze 		if (i == RSORD_MAX) {
1716b538baa5Sschwarze 			mandoc_msg(MANDOCERR_RS_BAD,
1717b538baa5Sschwarze 			    mdoc->parse, nn->line, nn->pos,
1718b538baa5Sschwarze 			    mdoc_macronames[nn->tok]);
1719b538baa5Sschwarze 			i = -1;
1720b538baa5Sschwarze 		} else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1721b538baa5Sschwarze 			mdoc->last->norm->Rs.quote_T++;
1722b538baa5Sschwarze 
172320fa2881Sschwarze 		/*
172420fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
172520fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
172620fa2881Sschwarze 		 * just re-ordering, there's no need for the
172720fa2881Sschwarze 		 * full unlink process.
172820fa2881Sschwarze 		 */
172920fa2881Sschwarze 
173020fa2881Sschwarze 		if (NULL != (next = nn->next))
173120fa2881Sschwarze 			next->prev = nn->prev;
173220fa2881Sschwarze 
173320fa2881Sschwarze 		if (NULL != (prev = nn->prev))
173420fa2881Sschwarze 			prev->next = nn->next;
173520fa2881Sschwarze 
173620fa2881Sschwarze 		nn->prev = nn->next = NULL;
173720fa2881Sschwarze 
173820fa2881Sschwarze 		/*
173920fa2881Sschwarze 		 * Scan back until we reach a node that's
174020fa2881Sschwarze 		 * ordered before `nn'.
174120fa2881Sschwarze 		 */
174220fa2881Sschwarze 
174320fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
174420fa2881Sschwarze 			/* Determine order of `prev'. */
174520fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
174620fa2881Sschwarze 				if (rsord[j] == prev->tok)
174720fa2881Sschwarze 					break;
1748b538baa5Sschwarze 			if (j == RSORD_MAX)
1749b538baa5Sschwarze 				j = -1;
175020fa2881Sschwarze 
175120fa2881Sschwarze 			if (j <= i)
175220fa2881Sschwarze 				break;
175320fa2881Sschwarze 		}
175420fa2881Sschwarze 
175520fa2881Sschwarze 		/*
175620fa2881Sschwarze 		 * Set `nn' back into its correct place in front
175720fa2881Sschwarze 		 * of the `prev' node.
175820fa2881Sschwarze 		 */
175920fa2881Sschwarze 
176020fa2881Sschwarze 		nn->prev = prev;
176120fa2881Sschwarze 
176220fa2881Sschwarze 		if (prev) {
176320fa2881Sschwarze 			if (prev->next)
176420fa2881Sschwarze 				prev->next->prev = nn;
176520fa2881Sschwarze 			nn->next = prev->next;
176620fa2881Sschwarze 			prev->next = nn;
176720fa2881Sschwarze 		} else {
176820fa2881Sschwarze 			mdoc->last->child->prev = nn;
176920fa2881Sschwarze 			nn->next = mdoc->last->child;
177020fa2881Sschwarze 			mdoc->last->child = nn;
177120fa2881Sschwarze 		}
1772011fe33bSschwarze 	}
1773011fe33bSschwarze 
1774011fe33bSschwarze 	return(1);
1775011fe33bSschwarze }
1776011fe33bSschwarze 
17774039b21cSschwarze /*
17784039b21cSschwarze  * For some arguments of some macros,
17794039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
17804039b21cSschwarze  */
17814039b21cSschwarze static int
17824039b21cSschwarze post_hyph(POST_ARGS)
17834039b21cSschwarze {
17844039b21cSschwarze 	struct mdoc_node	*n, *nch;
17854039b21cSschwarze 	char			*cp;
17864039b21cSschwarze 
17874039b21cSschwarze 	n = mdoc->last;
17884039b21cSschwarze 	switch (n->type) {
178949aff9f8Sschwarze 	case MDOC_HEAD:
17904039b21cSschwarze 		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
17914039b21cSschwarze 			break;
17924039b21cSschwarze 		return(1);
179349aff9f8Sschwarze 	case MDOC_BODY:
17944039b21cSschwarze 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
17954039b21cSschwarze 			break;
17964039b21cSschwarze 		return(1);
179749aff9f8Sschwarze 	case MDOC_ELEM:
17984039b21cSschwarze 		break;
17994039b21cSschwarze 	default:
18004039b21cSschwarze 		return(1);
18014039b21cSschwarze 	}
18024039b21cSschwarze 
18034039b21cSschwarze 	for (nch = n->child; nch; nch = nch->next) {
18044039b21cSschwarze 		if (MDOC_TEXT != nch->type)
18054039b21cSschwarze 			continue;
18064039b21cSschwarze 		cp = nch->string;
1807b7e2b14eSschwarze 		if ('\0' == *cp)
18084039b21cSschwarze 			continue;
18094039b21cSschwarze 		while ('\0' != *(++cp))
18104039b21cSschwarze 			if ('-' == *cp &&
18114039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
18124039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
18134039b21cSschwarze 				*cp = ASCII_HYPH;
18144039b21cSschwarze 	}
18154039b21cSschwarze 	return(1);
18164039b21cSschwarze }
18174039b21cSschwarze 
1818011fe33bSschwarze static int
1819af216717Sschwarze post_ns(POST_ARGS)
1820af216717Sschwarze {
1821af216717Sschwarze 
1822af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
1823b723eac2Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP);
1824af216717Sschwarze 	return(1);
1825af216717Sschwarze }
1826af216717Sschwarze 
1827af216717Sschwarze static int
1828f73abda9Skristaps post_sh(POST_ARGS)
1829f73abda9Skristaps {
1830f73abda9Skristaps 
1831f73abda9Skristaps 	if (MDOC_HEAD == mdoc->last->type)
1832f73abda9Skristaps 		return(post_sh_head(mdoc));
1833f73abda9Skristaps 	if (MDOC_BODY == mdoc->last->type)
1834f73abda9Skristaps 		return(post_sh_body(mdoc));
1835f73abda9Skristaps 
1836f73abda9Skristaps 	return(1);
1837f73abda9Skristaps }
1838f73abda9Skristaps 
1839f73abda9Skristaps static int
1840f73abda9Skristaps post_sh_body(POST_ARGS)
1841f73abda9Skristaps {
1842f73abda9Skristaps 	struct mdoc_node *n;
1843f73abda9Skristaps 
1844f8c9d6f2Sschwarze 	if (SEC_NAME != mdoc->lastsec)
1845f73abda9Skristaps 		return(1);
1846f73abda9Skristaps 
1847f73abda9Skristaps 	/*
1848f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1849f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1850f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1851f73abda9Skristaps 	 */
1852f73abda9Skristaps 
185320fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
185451fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
185551fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, "empty");
185620fa2881Sschwarze 		return(1);
185720fa2881Sschwarze 	}
1858f73abda9Skristaps 
1859f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1860f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1861f73abda9Skristaps 			continue;
1862f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1863f73abda9Skristaps 			continue;
186451fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
186551fcab2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1866f73abda9Skristaps 	}
1867f73abda9Skristaps 
186849d529b5Sschwarze 	assert(n);
18694602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1870f73abda9Skristaps 		return(1);
1871f73abda9Skristaps 
187251fcab2fSschwarze 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
187351fcab2fSschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
187420fa2881Sschwarze 	return(1);
187520fa2881Sschwarze }
1876f73abda9Skristaps 
1877f73abda9Skristaps static int
1878f73abda9Skristaps post_sh_head(POST_ARGS)
1879f73abda9Skristaps {
1880a2cff342Sschwarze 	struct mdoc_node *n;
188151fcab2fSschwarze 	const char	*goodsec;
188246133849Sschwarze 	char		*secname;
1883f73abda9Skristaps 	enum mdoc_sec	 sec;
1884f73abda9Skristaps 
1885f73abda9Skristaps 	/*
1886f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
188720fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
188820fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
188920fa2881Sschwarze 	 * manual sections.
1890f73abda9Skristaps 	 */
1891f73abda9Skristaps 
189283af2bccSschwarze 	secname = NULL;
189304e980cbSschwarze 	sec = SEC_CUSTOM;
189446133849Sschwarze 	mdoc_deroff(&secname, mdoc->last);
189546133849Sschwarze 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1896f73abda9Skristaps 
189720fa2881Sschwarze 	/* The NAME should be first. */
1898f73abda9Skristaps 
1899fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
190051fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
190151fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
190220fa2881Sschwarze 
190320fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
190420fa2881Sschwarze 
190522881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
190675088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
190720fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
190822881299Sschwarze 	} else {
190975088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
191020fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
191122881299Sschwarze 	}
191220fa2881Sschwarze 
191320fa2881Sschwarze 	/* Mark our last section. */
191420fa2881Sschwarze 
191520fa2881Sschwarze 	mdoc->lastsec = sec;
19161eccdf28Sschwarze 
19171eccdf28Sschwarze 	/*
19181eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
19191eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
19201eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
19211eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
19221eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
19231eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
19241eccdf28Sschwarze 	 */
19251eccdf28Sschwarze 
1926a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
1927a2cff342Sschwarze 	mdoc->last->sec = sec;
1928a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
1929a2cff342Sschwarze 		n->sec = sec;
193020fa2881Sschwarze 
193120fa2881Sschwarze 	/* We don't care about custom sections after this. */
1932fccfce9dSschwarze 
193346133849Sschwarze 	if (SEC_CUSTOM == sec) {
193446133849Sschwarze 		free(secname);
1935f73abda9Skristaps 		return(1);
193646133849Sschwarze 	}
1937fccfce9dSschwarze 
19386be99f77Sschwarze 	/*
193920fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
194020fa2881Sschwarze 	 * out of order.
19416be99f77Sschwarze 	 */
1942f73abda9Skristaps 
194320fa2881Sschwarze 	if (sec == mdoc->lastnamed)
194451fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse,
194551fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
194620fa2881Sschwarze 
194720fa2881Sschwarze 	if (sec < mdoc->lastnamed)
194851fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse,
194951fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
195020fa2881Sschwarze 
195120fa2881Sschwarze 	/* Mark the last named section. */
195220fa2881Sschwarze 
195320fa2881Sschwarze 	mdoc->lastnamed = sec;
195420fa2881Sschwarze 
195520fa2881Sschwarze 	/* Check particular section/manual conventions. */
195620fa2881Sschwarze 
195792c0ca7fSschwarze 	assert(mdoc->meta.msec);
195820fa2881Sschwarze 
195951fcab2fSschwarze 	goodsec = NULL;
196020fa2881Sschwarze 	switch (sec) {
196149aff9f8Sschwarze 	case SEC_ERRORS:
1962be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
1963be89e780Sschwarze 			break;
196451fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
1965be89e780Sschwarze 		/* FALLTHROUGH */
196649aff9f8Sschwarze 	case SEC_RETURN_VALUES:
196720fa2881Sschwarze 		/* FALLTHROUGH */
196849aff9f8Sschwarze 	case SEC_LIBRARY:
196992c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
1970f73abda9Skristaps 			break;
197192c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
197292c0ca7fSschwarze 			break;
197351fcab2fSschwarze 		if (NULL == goodsec)
197451fcab2fSschwarze 			goodsec = "2, 3, 9";
197503ab2f23Sdlg 		/* FALLTHROUGH */
197649aff9f8Sschwarze 	case SEC_CONTEXT:
197792c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
197892c0ca7fSschwarze 			break;
197951fcab2fSschwarze 		if (NULL == goodsec)
198051fcab2fSschwarze 			goodsec = "9";
198151fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
198251fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
198351fcab2fSschwarze 		    "%s for %s only", secname, goodsec);
198420fa2881Sschwarze 		break;
1985f73abda9Skristaps 	default:
1986f73abda9Skristaps 		break;
1987f73abda9Skristaps 	}
1988f73abda9Skristaps 
198946133849Sschwarze 	free(secname);
1990f73abda9Skristaps 	return(1);
1991f73abda9Skristaps }
1992d39b9a9cSschwarze 
199320fa2881Sschwarze static int
1994f6127a73Sschwarze post_ignpar(POST_ARGS)
1995f6127a73Sschwarze {
1996f6127a73Sschwarze 	struct mdoc_node *np;
1997f6127a73Sschwarze 
1998f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
1999f6127a73Sschwarze 		return(1);
2000f6127a73Sschwarze 
2001f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2002f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
200320369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
200420369664Sschwarze 			    mdoc->parse, np->line, np->pos,
200520369664Sschwarze 			    "%s after %s", mdoc_macronames[np->tok],
200620369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2007f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2008f6127a73Sschwarze 		}
2009f6127a73Sschwarze 
2010f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2011f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
201220369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
201320369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
201420369664Sschwarze 			    mdoc_macronames[np->tok],
201520369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2016f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2017f6127a73Sschwarze 		}
2018f6127a73Sschwarze 
2019f6127a73Sschwarze 	return(1);
2020f6127a73Sschwarze }
2021f6127a73Sschwarze 
2022f6127a73Sschwarze static int
202320fa2881Sschwarze pre_par(PRE_ARGS)
2024d39b9a9cSschwarze {
2025d39b9a9cSschwarze 
2026d39b9a9cSschwarze 	if (NULL == mdoc->last)
2027d39b9a9cSschwarze 		return(1);
2028f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2029f6127a73Sschwarze 		return(1);
2030d39b9a9cSschwarze 
203120fa2881Sschwarze 	/*
203220fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
203320fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
203420fa2881Sschwarze 	 */
2035d39b9a9cSschwarze 
2036e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2037e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2038e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2039d39b9a9cSschwarze 		return(1);
20408c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2041d39b9a9cSschwarze 		return(1);
20428c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2043d39b9a9cSschwarze 		return(1);
20448c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2045f6127a73Sschwarze 		return(1);
2046d39b9a9cSschwarze 
204720369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
204820369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
204920369664Sschwarze 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
205020369664Sschwarze 	    mdoc_macronames[n->tok]);
2051d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2052d39b9a9cSschwarze 	return(1);
2053d39b9a9cSschwarze }
205420fa2881Sschwarze 
205520fa2881Sschwarze static int
2056e0dd4c9cSschwarze post_par(POST_ARGS)
2057e0dd4c9cSschwarze {
205820369664Sschwarze 	struct mdoc_node *np;
2059e0dd4c9cSschwarze 
2060e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2061e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2062e0dd4c9cSschwarze 		return(1);
2063e0dd4c9cSschwarze 
206420369664Sschwarze 	if (NULL == (np = mdoc->last->prev)) {
206520369664Sschwarze 		np = mdoc->last->parent;
206620369664Sschwarze 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2067e0dd4c9cSschwarze 			return(1);
2068e0dd4c9cSschwarze 	} else {
206920369664Sschwarze 		if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2070e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
207120369664Sschwarze 		     (MDOC_sp != np->tok && MDOC_br != np->tok)))
2072e0dd4c9cSschwarze 			return(1);
2073e0dd4c9cSschwarze 	}
2074e0dd4c9cSschwarze 
207520369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
207620369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
207720369664Sschwarze 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
207820369664Sschwarze 	    mdoc_macronames[np->tok]);
2079e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2080e0dd4c9cSschwarze 	return(1);
2081e0dd4c9cSschwarze }
2082e0dd4c9cSschwarze 
2083e0dd4c9cSschwarze static int
208420fa2881Sschwarze pre_literal(PRE_ARGS)
208520fa2881Sschwarze {
208620fa2881Sschwarze 
208720fa2881Sschwarze 	if (MDOC_BODY != n->type)
208820fa2881Sschwarze 		return(1);
208920fa2881Sschwarze 
209020fa2881Sschwarze 	/*
209120fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
209220fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
209320fa2881Sschwarze 	 */
209420fa2881Sschwarze 
209520fa2881Sschwarze 	switch (n->tok) {
209649aff9f8Sschwarze 	case MDOC_Dl:
209720fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
209820fa2881Sschwarze 		break;
209949aff9f8Sschwarze 	case MDOC_Bd:
21008c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
210120fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
21028c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
210320fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
210420fa2881Sschwarze 		break;
210520fa2881Sschwarze 	default:
210620fa2881Sschwarze 		abort();
210720fa2881Sschwarze 		/* NOTREACHED */
210820fa2881Sschwarze 	}
210920fa2881Sschwarze 
211020fa2881Sschwarze 	return(1);
211120fa2881Sschwarze }
211220fa2881Sschwarze 
211320fa2881Sschwarze static int
211420fa2881Sschwarze post_dd(POST_ARGS)
211520fa2881Sschwarze {
211620fa2881Sschwarze 	struct mdoc_node *n;
211783af2bccSschwarze 	char		 *datestr;
211820fa2881Sschwarze 
2119b058e777Sschwarze 	if (mdoc->meta.date)
2120b058e777Sschwarze 		free(mdoc->meta.date);
212120fa2881Sschwarze 
2122b058e777Sschwarze 	n = mdoc->last;
2123b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2124231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2125231c7061Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
212620fa2881Sschwarze 		return(1);
212720fa2881Sschwarze 	}
212820fa2881Sschwarze 
212983af2bccSschwarze 	datestr = NULL;
213083af2bccSschwarze 	mdoc_deroff(&datestr, n);
213183af2bccSschwarze 	if (mdoc->quick)
213283af2bccSschwarze 		mdoc->meta.date = datestr;
213383af2bccSschwarze 	else {
213483af2bccSschwarze 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
213583af2bccSschwarze 		    datestr, n->line, n->pos);
213683af2bccSschwarze 		free(datestr);
213704e980cbSschwarze 	}
213820fa2881Sschwarze 	return(1);
213920fa2881Sschwarze }
214020fa2881Sschwarze 
214120fa2881Sschwarze static int
214220fa2881Sschwarze post_dt(POST_ARGS)
214320fa2881Sschwarze {
214420fa2881Sschwarze 	struct mdoc_node *nn, *n;
214520fa2881Sschwarze 	const char	 *cp;
214620fa2881Sschwarze 	char		 *p;
214720fa2881Sschwarze 
214820fa2881Sschwarze 	n = mdoc->last;
214920fa2881Sschwarze 
215020fa2881Sschwarze 	if (mdoc->meta.title)
215120fa2881Sschwarze 		free(mdoc->meta.title);
215220fa2881Sschwarze 	if (mdoc->meta.vol)
215320fa2881Sschwarze 		free(mdoc->meta.vol);
215420fa2881Sschwarze 	if (mdoc->meta.arch)
215520fa2881Sschwarze 		free(mdoc->meta.arch);
215620fa2881Sschwarze 
215720fa2881Sschwarze 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
215820fa2881Sschwarze 
215951fcab2fSschwarze 	/* First check that all characters are uppercase. */
216020fa2881Sschwarze 
216120fa2881Sschwarze 	if (NULL != (nn = n->child))
216220fa2881Sschwarze 		for (p = nn->string; *p; p++) {
216304e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
216420fa2881Sschwarze 				continue;
216551fcab2fSschwarze 			mandoc_msg(MANDOCERR_TITLE_CASE,
216651fcab2fSschwarze 			    mdoc->parse, nn->line,
216751fcab2fSschwarze 			    nn->pos + (p - nn->string),
216851fcab2fSschwarze 			    nn->string);
216920fa2881Sschwarze 			break;
217020fa2881Sschwarze 		}
217120fa2881Sschwarze 
217220fa2881Sschwarze 	/* Handles: `.Dt'
217349aff9f8Sschwarze 	 * title = unknown, volume = local, msec = 0, arch = NULL
217420fa2881Sschwarze 	 */
217520fa2881Sschwarze 
217620fa2881Sschwarze 	if (NULL == (nn = n->child)) {
217720fa2881Sschwarze 		/* XXX: make these macro values. */
217820fa2881Sschwarze 		/* FIXME: warn about missing values. */
217920fa2881Sschwarze 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
218020fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
218120fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
218220fa2881Sschwarze 		return(1);
218320fa2881Sschwarze 	}
218420fa2881Sschwarze 
218520fa2881Sschwarze 	/* Handles: `.Dt TITLE'
218649aff9f8Sschwarze 	 * title = TITLE, volume = local, msec = 0, arch = NULL
218720fa2881Sschwarze 	 */
218820fa2881Sschwarze 
218949aff9f8Sschwarze 	mdoc->meta.title = mandoc_strdup(
219049aff9f8Sschwarze 	    '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
219120fa2881Sschwarze 
219220fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
219320fa2881Sschwarze 		/* FIXME: warn about missing msec. */
219420fa2881Sschwarze 		/* XXX: make this a macro value. */
219520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
219620fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
219720fa2881Sschwarze 		return(1);
219820fa2881Sschwarze 	}
219920fa2881Sschwarze 
220020fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
220149aff9f8Sschwarze 	 * title = TITLE,
220249aff9f8Sschwarze 	 * volume = SEC is msec ? format(msec) : SEC,
220320fa2881Sschwarze 	 * msec = SEC is msec ? atoi(msec) : 0,
220420fa2881Sschwarze 	 * arch = NULL
220520fa2881Sschwarze 	 */
220620fa2881Sschwarze 
220788ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
220820fa2881Sschwarze 	if (cp) {
220920fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
221020fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
221120fa2881Sschwarze 	} else {
221251fcab2fSschwarze 		mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
221351fcab2fSschwarze 		    nn->line, nn->pos, nn->string);
221420fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
221520fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
221620fa2881Sschwarze 	}
221720fa2881Sschwarze 
221820fa2881Sschwarze 	if (NULL == (nn = nn->next))
221920fa2881Sschwarze 		return(1);
222020fa2881Sschwarze 
222120fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
222249aff9f8Sschwarze 	 * title = TITLE,
222349aff9f8Sschwarze 	 * volume = VOL is vol ? format(VOL) :
222420fa2881Sschwarze 	 *	    VOL is arch ? format(arch) :
222520fa2881Sschwarze 	 *	    VOL
222620fa2881Sschwarze 	 */
222720fa2881Sschwarze 
222820fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
222920fa2881Sschwarze 	if (cp) {
223020fa2881Sschwarze 		free(mdoc->meta.vol);
223120fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
223220fa2881Sschwarze 	} else {
223320fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
223420fa2881Sschwarze 		if (NULL == cp) {
223551fcab2fSschwarze 			mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
223651fcab2fSschwarze 			    nn->line, nn->pos, nn->string);
223720fa2881Sschwarze 			free(mdoc->meta.vol);
223820fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
223920fa2881Sschwarze 		} else
224020fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
224120fa2881Sschwarze 	}
224220fa2881Sschwarze 
224320fa2881Sschwarze 	/* Ignore any subsequent parameters... */
224420fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
224520fa2881Sschwarze 
224620fa2881Sschwarze 	return(1);
224720fa2881Sschwarze }
224820fa2881Sschwarze 
224920fa2881Sschwarze static int
225020fa2881Sschwarze post_prol(POST_ARGS)
225120fa2881Sschwarze {
225220fa2881Sschwarze 	/*
225320fa2881Sschwarze 	 * Remove prologue macros from the document after they're
225420fa2881Sschwarze 	 * processed.  The final document uses mdoc_meta for these
225520fa2881Sschwarze 	 * values and discards the originals.
225620fa2881Sschwarze 	 */
225720fa2881Sschwarze 
225820fa2881Sschwarze 	mdoc_node_delete(mdoc, mdoc->last);
225920fa2881Sschwarze 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
226020fa2881Sschwarze 		mdoc->flags |= MDOC_PBODY;
226120fa2881Sschwarze 
226220fa2881Sschwarze 	return(1);
226320fa2881Sschwarze }
226420fa2881Sschwarze 
226520fa2881Sschwarze static int
2266992063deSschwarze post_bx(POST_ARGS)
2267992063deSschwarze {
2268992063deSschwarze 	struct mdoc_node	*n;
2269992063deSschwarze 
2270992063deSschwarze 	/*
2271992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2272992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2273992063deSschwarze 	 * uppercase blindly.
2274992063deSschwarze 	 */
2275992063deSschwarze 
2276992063deSschwarze 	n = mdoc->last->child;
2277992063deSschwarze 	if (n && NULL != (n = n->next))
227849aff9f8Sschwarze 		*n->string = (char)toupper((unsigned char)*n->string);
2279992063deSschwarze 
2280992063deSschwarze 	return(1);
2281992063deSschwarze }
2282992063deSschwarze 
2283992063deSschwarze static int
228420fa2881Sschwarze post_os(POST_ARGS)
228520fa2881Sschwarze {
228620fa2881Sschwarze #ifndef OSNAME
228720fa2881Sschwarze 	struct utsname	  utsname;
22884c468128Sschwarze 	static char	 *defbuf;
228920fa2881Sschwarze #endif
22904c468128Sschwarze 	struct mdoc_node *n;
229120fa2881Sschwarze 
229220fa2881Sschwarze 	n = mdoc->last;
229320fa2881Sschwarze 
229420fa2881Sschwarze 	/*
2295353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2296353fa9ecSschwarze 	 * The order of precedence is:
2297353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2298353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2299353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2300353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
230120fa2881Sschwarze 	 */
230220fa2881Sschwarze 
230320fa2881Sschwarze 	free(mdoc->meta.os);
230483af2bccSschwarze 	mdoc->meta.os = NULL;
230583af2bccSschwarze 	mdoc_deroff(&mdoc->meta.os, n);
230683af2bccSschwarze 	if (mdoc->meta.os)
23074c468128Sschwarze 		return(1);
23084c468128Sschwarze 
2309353fa9ecSschwarze 	if (mdoc->defos) {
2310353fa9ecSschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2311353fa9ecSschwarze 		return(1);
2312353fa9ecSschwarze 	}
23134c468128Sschwarze 
231420fa2881Sschwarze #ifdef OSNAME
23154c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
231620fa2881Sschwarze #else /*!OSNAME */
23174c468128Sschwarze 	if (NULL == defbuf) {
2318a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
2319f79e7afeSschwarze 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2320f79e7afeSschwarze 			    n->line, n->pos, "Os");
23214c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2322a450f7c4Sschwarze 		} else
2323a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2324a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
232520fa2881Sschwarze 	}
23264c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
232720fa2881Sschwarze #endif /*!OSNAME*/
232820fa2881Sschwarze 	return(1);
232920fa2881Sschwarze }
233020fa2881Sschwarze 
2331e214f641Sschwarze /*
2332e214f641Sschwarze  * If no argument is provided,
2333e214f641Sschwarze  * fill in the name of the current manual page.
2334e214f641Sschwarze  */
233520fa2881Sschwarze static int
2336e214f641Sschwarze post_ex(POST_ARGS)
233720fa2881Sschwarze {
2338e214f641Sschwarze 	struct mdoc_node *n;
233920fa2881Sschwarze 
234020fa2881Sschwarze 	n = mdoc->last;
234120fa2881Sschwarze 
234220fa2881Sschwarze 	if (n->child)
234320fa2881Sschwarze 		return(1);
234420fa2881Sschwarze 
2345e214f641Sschwarze 	if (mdoc->meta.name == NULL) {
2346e214f641Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_EX_NONAME);
234720fa2881Sschwarze 		return(1);
2348e214f641Sschwarze 	}
234920fa2881Sschwarze 
235020fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
235120fa2881Sschwarze 
235220fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
235320fa2881Sschwarze 		return(0);
235420fa2881Sschwarze 
2355e214f641Sschwarze 	mdoc->last = n;
235620fa2881Sschwarze 	return(1);
235720fa2881Sschwarze }
235820fa2881Sschwarze 
235919a69263Sschwarze static enum mdoc_sec
236019a69263Sschwarze a2sec(const char *p)
236119a69263Sschwarze {
236219a69263Sschwarze 	int		 i;
236319a69263Sschwarze 
236419a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
236519a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
236619a69263Sschwarze 			return((enum mdoc_sec)i);
236719a69263Sschwarze 
236819a69263Sschwarze 	return(SEC_CUSTOM);
236919a69263Sschwarze }
237019a69263Sschwarze 
237119a69263Sschwarze static size_t
237219a69263Sschwarze macro2len(enum mdoct macro)
237319a69263Sschwarze {
237419a69263Sschwarze 
237519a69263Sschwarze 	switch (macro) {
237649aff9f8Sschwarze 	case MDOC_Ad:
237719a69263Sschwarze 		return(12);
237849aff9f8Sschwarze 	case MDOC_Ao:
237919a69263Sschwarze 		return(12);
238049aff9f8Sschwarze 	case MDOC_An:
238119a69263Sschwarze 		return(12);
238249aff9f8Sschwarze 	case MDOC_Aq:
238319a69263Sschwarze 		return(12);
238449aff9f8Sschwarze 	case MDOC_Ar:
238519a69263Sschwarze 		return(12);
238649aff9f8Sschwarze 	case MDOC_Bo:
238719a69263Sschwarze 		return(12);
238849aff9f8Sschwarze 	case MDOC_Bq:
238919a69263Sschwarze 		return(12);
239049aff9f8Sschwarze 	case MDOC_Cd:
239119a69263Sschwarze 		return(12);
239249aff9f8Sschwarze 	case MDOC_Cm:
239319a69263Sschwarze 		return(10);
239449aff9f8Sschwarze 	case MDOC_Do:
239519a69263Sschwarze 		return(10);
239649aff9f8Sschwarze 	case MDOC_Dq:
239719a69263Sschwarze 		return(12);
239849aff9f8Sschwarze 	case MDOC_Dv:
239919a69263Sschwarze 		return(12);
240049aff9f8Sschwarze 	case MDOC_Eo:
240119a69263Sschwarze 		return(12);
240249aff9f8Sschwarze 	case MDOC_Em:
240319a69263Sschwarze 		return(10);
240449aff9f8Sschwarze 	case MDOC_Er:
240519a69263Sschwarze 		return(17);
240649aff9f8Sschwarze 	case MDOC_Ev:
240719a69263Sschwarze 		return(15);
240849aff9f8Sschwarze 	case MDOC_Fa:
240919a69263Sschwarze 		return(12);
241049aff9f8Sschwarze 	case MDOC_Fl:
241119a69263Sschwarze 		return(10);
241249aff9f8Sschwarze 	case MDOC_Fo:
241319a69263Sschwarze 		return(16);
241449aff9f8Sschwarze 	case MDOC_Fn:
241519a69263Sschwarze 		return(16);
241649aff9f8Sschwarze 	case MDOC_Ic:
241719a69263Sschwarze 		return(10);
241849aff9f8Sschwarze 	case MDOC_Li:
241919a69263Sschwarze 		return(16);
242049aff9f8Sschwarze 	case MDOC_Ms:
242119a69263Sschwarze 		return(6);
242249aff9f8Sschwarze 	case MDOC_Nm:
242319a69263Sschwarze 		return(10);
242449aff9f8Sschwarze 	case MDOC_No:
242519a69263Sschwarze 		return(12);
242649aff9f8Sschwarze 	case MDOC_Oo:
242719a69263Sschwarze 		return(10);
242849aff9f8Sschwarze 	case MDOC_Op:
242919a69263Sschwarze 		return(14);
243049aff9f8Sschwarze 	case MDOC_Pa:
243119a69263Sschwarze 		return(32);
243249aff9f8Sschwarze 	case MDOC_Pf:
243319a69263Sschwarze 		return(12);
243449aff9f8Sschwarze 	case MDOC_Po:
243519a69263Sschwarze 		return(12);
243649aff9f8Sschwarze 	case MDOC_Pq:
243719a69263Sschwarze 		return(12);
243849aff9f8Sschwarze 	case MDOC_Ql:
243919a69263Sschwarze 		return(16);
244049aff9f8Sschwarze 	case MDOC_Qo:
244119a69263Sschwarze 		return(12);
244249aff9f8Sschwarze 	case MDOC_So:
244319a69263Sschwarze 		return(12);
244449aff9f8Sschwarze 	case MDOC_Sq:
244519a69263Sschwarze 		return(12);
244649aff9f8Sschwarze 	case MDOC_Sy:
244719a69263Sschwarze 		return(6);
244849aff9f8Sschwarze 	case MDOC_Sx:
244919a69263Sschwarze 		return(16);
245049aff9f8Sschwarze 	case MDOC_Tn:
245119a69263Sschwarze 		return(10);
245249aff9f8Sschwarze 	case MDOC_Va:
245319a69263Sschwarze 		return(12);
245449aff9f8Sschwarze 	case MDOC_Vt:
245519a69263Sschwarze 		return(12);
245649aff9f8Sschwarze 	case MDOC_Xr:
245719a69263Sschwarze 		return(10);
245819a69263Sschwarze 	default:
245919a69263Sschwarze 		break;
246019a69263Sschwarze 	};
246119a69263Sschwarze 	return(0);
246219a69263Sschwarze }
2463