xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 55818fdc)
1*55818fdcSschwarze /*	$Id: mdoc_validate.c,v 1.108 2012/11/16 22:20:40 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4353fa9ecSschwarze  * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
5f73abda9Skristaps  *
6f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
7a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
8a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
9f73abda9Skristaps  *
10a6464425Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a6464425Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f73abda9Skristaps  */
1820fa2881Sschwarze #ifndef        OSNAME
1920fa2881Sschwarze #include <sys/utsname.h>
2020fa2881Sschwarze #endif
2120fa2881Sschwarze 
22f73abda9Skristaps #include <sys/types.h>
23f73abda9Skristaps 
24f73abda9Skristaps #include <assert.h>
25f73abda9Skristaps #include <ctype.h>
26d92dc4efSschwarze #include <limits.h>
273216dddfSschwarze #include <stdio.h>
28f73abda9Skristaps #include <stdlib.h>
29f73abda9Skristaps #include <string.h>
3020fa2881Sschwarze #include <time.h>
31f73abda9Skristaps 
32a35fc07aSschwarze #include "mdoc.h"
336e03d529Sschwarze #include "mandoc.h"
34f73abda9Skristaps #include "libmdoc.h"
35f6854d5cSschwarze #include "libmandoc.h"
36f73abda9Skristaps 
37f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
38f73abda9Skristaps 
396093755cSschwarze #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
40f73abda9Skristaps #define	POST_ARGS struct mdoc *mdoc
41f73abda9Skristaps 
4220fa2881Sschwarze #define	NUMSIZ	  32
4320fa2881Sschwarze #define	DATESIZE  32
4420fa2881Sschwarze 
457c2be9f8Sschwarze enum	check_ineq {
467c2be9f8Sschwarze 	CHECK_LT,
477c2be9f8Sschwarze 	CHECK_GT,
487c2be9f8Sschwarze 	CHECK_EQ
497c2be9f8Sschwarze };
507c2be9f8Sschwarze 
517c2be9f8Sschwarze enum	check_lvl {
527c2be9f8Sschwarze 	CHECK_WARN,
537c2be9f8Sschwarze 	CHECK_ERROR,
547c2be9f8Sschwarze };
557c2be9f8Sschwarze 
56f73abda9Skristaps typedef	int	(*v_pre)(PRE_ARGS);
57f73abda9Skristaps typedef	int	(*v_post)(POST_ARGS);
58f73abda9Skristaps 
59f73abda9Skristaps struct	valids {
60f73abda9Skristaps 	v_pre	*pre;
61f73abda9Skristaps 	v_post	*post;
62f73abda9Skristaps };
63f73abda9Skristaps 
647c2be9f8Sschwarze static	int	 check_count(struct mdoc *, enum mdoc_type,
657c2be9f8Sschwarze 			enum check_lvl, enum check_ineq, int);
66dd94fa3aSschwarze static	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
6720fa2881Sschwarze static	void	 check_text(struct mdoc *, int, int, char *);
6820fa2881Sschwarze static	void	 check_argv(struct mdoc *,
6931e23753Sschwarze 			struct mdoc_node *, struct mdoc_argv *);
7020fa2881Sschwarze static	void	 check_args(struct mdoc *, struct mdoc_node *);
7104e980cbSschwarze static	int	 concat(char *, const struct mdoc_node *, size_t);
7219a69263Sschwarze static	enum mdoc_sec	a2sec(const char *);
7319a69263Sschwarze static	size_t		macro2len(enum mdoct);
7467c719adSschwarze 
757c2be9f8Sschwarze static	int	 ebool(POST_ARGS);
7667c719adSschwarze static	int	 berr_ge1(POST_ARGS);
7767c719adSschwarze static	int	 bwarn_ge1(POST_ARGS);
78da272f5eSschwarze static	int	 ewarn_eq0(POST_ARGS);
79bb648afaSschwarze static	int	 ewarn_eq1(POST_ARGS);
8067c719adSschwarze static	int	 ewarn_ge1(POST_ARGS);
81bb648afaSschwarze static	int	 ewarn_le1(POST_ARGS);
82b16e7ddfSschwarze static	int	 hwarn_eq0(POST_ARGS);
837c2be9f8Sschwarze static	int	 hwarn_eq1(POST_ARGS);
84bb648afaSschwarze static	int	 hwarn_ge1(POST_ARGS);
8567c719adSschwarze static	int	 hwarn_le1(POST_ARGS);
8667c719adSschwarze 
8767c719adSschwarze static	int	 post_an(POST_ARGS);
8867c719adSschwarze static	int	 post_at(POST_ARGS);
8967c719adSschwarze static	int	 post_bf(POST_ARGS);
9067c719adSschwarze static	int	 post_bl(POST_ARGS);
9120fa2881Sschwarze static	int	 post_bl_block(POST_ARGS);
9220fa2881Sschwarze static	int	 post_bl_block_width(POST_ARGS);
9320fa2881Sschwarze static	int	 post_bl_block_tag(POST_ARGS);
9467c719adSschwarze static	int	 post_bl_head(POST_ARGS);
95992063deSschwarze static	int	 post_bx(POST_ARGS);
9620fa2881Sschwarze static	int	 post_dd(POST_ARGS);
976093755cSschwarze static	int	 post_dt(POST_ARGS);
9820fa2881Sschwarze static	int	 post_defaults(POST_ARGS);
9920fa2881Sschwarze static	int	 post_literal(POST_ARGS);
10020fa2881Sschwarze static	int	 post_eoln(POST_ARGS);
10167c719adSschwarze static	int	 post_it(POST_ARGS);
10267c719adSschwarze static	int	 post_lb(POST_ARGS);
10367c719adSschwarze static	int	 post_nm(POST_ARGS);
104af216717Sschwarze static	int	 post_ns(POST_ARGS);
10520fa2881Sschwarze static	int	 post_os(POST_ARGS);
106e0dd4c9cSschwarze static	int	 post_par(POST_ARGS);
107f6127a73Sschwarze static	int	 post_ignpar(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);
11520fa2881Sschwarze static	int	 post_std(POST_ARGS);
1168521b0bcSschwarze static	int	 post_vt(POST_ARGS);
117f73abda9Skristaps static	int	 pre_an(PRE_ARGS);
118f73abda9Skristaps static	int	 pre_bd(PRE_ARGS);
119f73abda9Skristaps static	int	 pre_bl(PRE_ARGS);
120f73abda9Skristaps static	int	 pre_dd(PRE_ARGS);
121f73abda9Skristaps static	int	 pre_display(PRE_ARGS);
122f73abda9Skristaps static	int	 pre_dt(PRE_ARGS);
123f73abda9Skristaps static	int	 pre_it(PRE_ARGS);
12420fa2881Sschwarze static	int	 pre_literal(PRE_ARGS);
125f73abda9Skristaps static	int	 pre_os(PRE_ARGS);
12620fa2881Sschwarze static	int	 pre_par(PRE_ARGS);
127f73abda9Skristaps static	int	 pre_sh(PRE_ARGS);
128f73abda9Skristaps static	int	 pre_ss(PRE_ARGS);
12920fa2881Sschwarze static	int	 pre_std(PRE_ARGS);
130f73abda9Skristaps 
13167c719adSschwarze static	v_post	 posts_an[] = { post_an, NULL };
13220fa2881Sschwarze static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
13320fa2881Sschwarze static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
13467c719adSschwarze static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
13520fa2881Sschwarze static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
13667c719adSschwarze static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
137992063deSschwarze static	v_post	 posts_bx[] = { post_bx, NULL };
138bb648afaSschwarze static	v_post	 posts_bool[] = { ebool, NULL };
139b31af00dSschwarze static	v_post	 posts_eoln[] = { post_eoln, NULL };
14020fa2881Sschwarze static	v_post	 posts_defaults[] = { post_defaults, NULL };
141b058e777Sschwarze static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
142bb648afaSschwarze static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
14320fa2881Sschwarze static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
14467c719adSschwarze static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
14567c719adSschwarze static	v_post	 posts_it[] = { post_it, NULL };
146bb648afaSschwarze static	v_post	 posts_lb[] = { post_lb, NULL };
14767c719adSschwarze static	v_post	 posts_nd[] = { berr_ge1, NULL };
14867c719adSschwarze static	v_post	 posts_nm[] = { post_nm, NULL };
149da272f5eSschwarze static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
150af216717Sschwarze static	v_post	 posts_ns[] = { post_ns, NULL };
15120fa2881Sschwarze static	v_post	 posts_os[] = { post_os, post_prol, NULL };
152e0dd4c9cSschwarze static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
153bb648afaSschwarze static	v_post	 posts_rs[] = { post_rs, NULL };
154a35fc07aSschwarze static	v_post	 posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL };
155e0dd4c9cSschwarze static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
156a35fc07aSschwarze static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, NULL };
157bb648afaSschwarze static	v_post	 posts_st[] = { post_st, NULL };
15820fa2881Sschwarze static	v_post	 posts_std[] = { post_std, NULL };
159e7a93ef3Sschwarze static	v_post	 posts_text[] = { ewarn_ge1, NULL };
160bb648afaSschwarze static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
1618521b0bcSschwarze static	v_post	 posts_vt[] = { post_vt, NULL };
162bb648afaSschwarze static	v_post	 posts_wline[] = { bwarn_ge1, NULL };
163f73abda9Skristaps static	v_pre	 pres_an[] = { pre_an, NULL };
16420fa2881Sschwarze static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
16520fa2881Sschwarze static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
166f73abda9Skristaps static	v_pre	 pres_d1[] = { pre_display, NULL };
16720fa2881Sschwarze static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
16867c719adSschwarze static	v_pre	 pres_dd[] = { pre_dd, NULL };
1696be99f77Sschwarze static	v_pre	 pres_dt[] = { pre_dt, NULL };
170b16e7ddfSschwarze static	v_pre	 pres_er[] = { NULL, NULL };
171974b5ecdSschwarze static	v_pre	 pres_fd[] = { NULL, NULL };
172f6127a73Sschwarze static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
1736be99f77Sschwarze static	v_pre	 pres_os[] = { pre_os, NULL };
17420fa2881Sschwarze static	v_pre	 pres_pp[] = { pre_par, NULL };
175f73abda9Skristaps static	v_pre	 pres_sh[] = { pre_sh, NULL };
176f73abda9Skristaps static	v_pre	 pres_ss[] = { pre_ss, NULL };
17720fa2881Sschwarze static	v_pre	 pres_std[] = { pre_std, NULL };
178f73abda9Skristaps 
17919a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
180099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
18120fa2881Sschwarze 	{ pres_dd, posts_dd },			/* Dd */
1826093755cSschwarze 	{ pres_dt, posts_dt },			/* Dt */
18320fa2881Sschwarze 	{ pres_os, posts_os },			/* Os */
184f73abda9Skristaps 	{ pres_sh, posts_sh },			/* Sh */
185f73abda9Skristaps 	{ pres_ss, posts_ss },			/* Ss */
186e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Pp */
187f73abda9Skristaps 	{ pres_d1, posts_wline },		/* D1 */
18820fa2881Sschwarze 	{ pres_dl, posts_dl },			/* Dl */
18920fa2881Sschwarze 	{ pres_bd, posts_bd },			/* Bd */
190f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
191f73abda9Skristaps 	{ pres_bl, posts_bl },			/* Bl */
192f73abda9Skristaps 	{ NULL, NULL },				/* El */
193f73abda9Skristaps 	{ pres_it, posts_it },			/* It */
194e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
195f73abda9Skristaps 	{ pres_an, posts_an },			/* An */
19620fa2881Sschwarze 	{ NULL, posts_defaults },		/* Ar */
197e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
198f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
199f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
200e7a93ef3Sschwarze 	{ pres_er, NULL },			/* Er */
201f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
20220fa2881Sschwarze 	{ pres_std, posts_std },		/* Ex */
203f73abda9Skristaps 	{ NULL, NULL },				/* Fa */
204e7a93ef3Sschwarze 	{ pres_fd, posts_text },		/* Fd */
205f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
206e7a93ef3Sschwarze 	{ NULL, NULL },				/* Fn */
207e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
208e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
209b822ca0dSschwarze 	{ NULL, posts_text1 },			/* In */
21020fa2881Sschwarze 	{ NULL, posts_defaults },		/* Li */
2114602e85cSschwarze 	{ NULL, posts_nd },			/* Nd */
212f73abda9Skristaps 	{ NULL, posts_nm },			/* Nm */
213bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
214f73abda9Skristaps 	{ NULL, NULL },				/* Ot */
21520fa2881Sschwarze 	{ NULL, posts_defaults },		/* Pa */
21620fa2881Sschwarze 	{ pres_std, posts_std },		/* Rv */
217f73abda9Skristaps 	{ NULL, posts_st },			/* St */
218f73abda9Skristaps 	{ NULL, NULL },				/* Va */
2198521b0bcSschwarze 	{ NULL, posts_vt },			/* Vt */
220e7a93ef3Sschwarze 	{ NULL, posts_text },			/* Xr */
221f73abda9Skristaps 	{ NULL, posts_text },			/* %A */
2224175bdabSschwarze 	{ NULL, posts_text },			/* %B */ /* FIXME: can be used outside Rs/Re. */
223b058e777Sschwarze 	{ NULL, posts_text },			/* %D */
224f73abda9Skristaps 	{ NULL, posts_text },			/* %I */
225f73abda9Skristaps 	{ NULL, posts_text },			/* %J */
226f73abda9Skristaps 	{ NULL, posts_text },			/* %N */
227f73abda9Skristaps 	{ NULL, posts_text },			/* %O */
228f73abda9Skristaps 	{ NULL, posts_text },			/* %P */
229f73abda9Skristaps 	{ NULL, posts_text },			/* %R */
2304175bdabSschwarze 	{ NULL, posts_text },			/* %T */ /* FIXME: can be used outside Rs/Re. */
231f73abda9Skristaps 	{ NULL, posts_text },			/* %V */
232f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
233f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
234bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
235f73abda9Skristaps 	{ NULL, posts_at },			/* At */
236f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
237f73abda9Skristaps 	{ NULL, posts_bf },			/* Bf */
238f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
239bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
240f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
241992063deSschwarze 	{ NULL, posts_bx },			/* Bx */
242f73abda9Skristaps 	{ NULL, posts_bool },			/* Db */
243f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
244f73abda9Skristaps 	{ NULL, NULL },				/* Do */
245bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
246f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
247f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
248f73abda9Skristaps 	{ NULL, NULL },				/* Em */
249f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
250f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
251e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
252f73abda9Skristaps 	{ NULL, posts_notext },			/* No */
253af216717Sschwarze 	{ NULL, posts_ns },			/* Ns */
254f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
255f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
256f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
257b822ca0dSschwarze 	{ NULL, posts_text1 },			/* Pf */
258f73abda9Skristaps 	{ NULL, NULL },				/* Po */
259bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
260f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
261bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
262f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
263bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
264f73abda9Skristaps 	{ NULL, NULL },				/* Re */
2658c62fbf5Sschwarze 	{ NULL, posts_rs },			/* Rs */
266f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
267f73abda9Skristaps 	{ NULL, NULL },				/* So */
268bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
269f73abda9Skristaps 	{ NULL, posts_bool },			/* Sm */
270e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sx */
271e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
272e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
273f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
274f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
275f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
276f73abda9Skristaps 	{ NULL, posts_fo },			/* Fo */
277f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
278f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
279f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
28020fa2881Sschwarze 	{ NULL, posts_bk },			/* Bk */
281f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
282b31af00dSschwarze 	{ NULL, posts_eoln },			/* Bt */
283f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
284f73abda9Skristaps 	{ NULL, NULL },				/* Fr */
285b31af00dSschwarze 	{ NULL, posts_eoln },			/* Ud */
286b31af00dSschwarze 	{ NULL, posts_lb },			/* Lb */
287e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Lp */
288e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
28920fa2881Sschwarze 	{ NULL, posts_defaults },		/* Mt */
290bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
291f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
292f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
293f73abda9Skristaps 	{ NULL, posts_text },			/* %C */
294f73abda9Skristaps 	{ NULL, NULL },				/* Es */
295f73abda9Skristaps 	{ NULL, NULL },				/* En */
296f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
297f73abda9Skristaps 	{ NULL, posts_text },			/* %Q */
298e0dd4c9cSschwarze 	{ NULL, posts_pp },			/* br */
299e0dd4c9cSschwarze 	{ NULL, posts_sp },			/* sp */
300b822ca0dSschwarze 	{ NULL, posts_text1 },			/* %U */
3016093755cSschwarze 	{ NULL, NULL },				/* Ta */
302f73abda9Skristaps };
303f73abda9Skristaps 
30420fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
30520fa2881Sschwarze 
30620fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
30720fa2881Sschwarze 	MDOC__A,
30820fa2881Sschwarze 	MDOC__T,
30920fa2881Sschwarze 	MDOC__B,
31020fa2881Sschwarze 	MDOC__I,
31120fa2881Sschwarze 	MDOC__J,
31220fa2881Sschwarze 	MDOC__R,
31320fa2881Sschwarze 	MDOC__N,
31420fa2881Sschwarze 	MDOC__V,
3150397c682Sschwarze 	MDOC__U,
31620fa2881Sschwarze 	MDOC__P,
31720fa2881Sschwarze 	MDOC__Q,
31820fa2881Sschwarze 	MDOC__D,
31920fa2881Sschwarze 	MDOC__O,
3200397c682Sschwarze 	MDOC__C
32120fa2881Sschwarze };
32220fa2881Sschwarze 
32319a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
32419a69263Sschwarze 	NULL,
32519a69263Sschwarze 	"NAME",
32619a69263Sschwarze 	"LIBRARY",
32719a69263Sschwarze 	"SYNOPSIS",
32819a69263Sschwarze 	"DESCRIPTION",
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 
348f73abda9Skristaps int
3496093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
350f73abda9Skristaps {
351f73abda9Skristaps 	v_pre		*p;
352f73abda9Skristaps 	int		 line, pos;
35331e23753Sschwarze 	char		*tp;
354f73abda9Skristaps 
3552791bd1cSschwarze 	switch (n->type) {
3562791bd1cSschwarze 	case (MDOC_TEXT):
357f73abda9Skristaps 		tp = n->string;
358f73abda9Skristaps 		line = n->line;
359f73abda9Skristaps 		pos = n->pos;
36020fa2881Sschwarze 		check_text(mdoc, line, pos, tp);
3612791bd1cSschwarze 		/* FALLTHROUGH */
3622791bd1cSschwarze 	case (MDOC_TBL):
3632791bd1cSschwarze 		/* FALLTHROUGH */
3648d973ab1Sschwarze 	case (MDOC_EQN):
3658d973ab1Sschwarze 		/* FALLTHROUGH */
3662791bd1cSschwarze 	case (MDOC_ROOT):
36720fa2881Sschwarze 		return(1);
3682791bd1cSschwarze 	default:
3692791bd1cSschwarze 		break;
370f73abda9Skristaps 	}
371f73abda9Skristaps 
37220fa2881Sschwarze 	check_args(mdoc, n);
37320fa2881Sschwarze 
374f73abda9Skristaps 	if (NULL == mdoc_valids[n->tok].pre)
375f73abda9Skristaps 		return(1);
376f73abda9Skristaps 	for (p = mdoc_valids[n->tok].pre; *p; p++)
377f73abda9Skristaps 		if ( ! (*p)(mdoc, n))
378f73abda9Skristaps 			return(0);
379f73abda9Skristaps 	return(1);
380f73abda9Skristaps }
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) {
3932791bd1cSschwarze 	case (MDOC_TEXT):
3942791bd1cSschwarze 		/* FALLTHROUGH */
3958d973ab1Sschwarze 	case (MDOC_EQN):
3968d973ab1Sschwarze 		/* FALLTHROUGH */
3972791bd1cSschwarze 	case (MDOC_TBL):
398f73abda9Skristaps 		return(1);
3992791bd1cSschwarze 	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
4157c2be9f8Sschwarze check_count(struct mdoc *m, 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 
4217c2be9f8Sschwarze 	if (m->last->type != type)
4227c2be9f8Sschwarze 		return(1);
4237c2be9f8Sschwarze 
4247c2be9f8Sschwarze 	switch (ineq) {
4257c2be9f8Sschwarze 	case (CHECK_LT):
4267c2be9f8Sschwarze 		p = "less than ";
4277c2be9f8Sschwarze 		if (m->last->nchild < val)
4287c2be9f8Sschwarze 			return(1);
4297c2be9f8Sschwarze 		break;
4307c2be9f8Sschwarze 	case (CHECK_GT):
431bb648afaSschwarze 		p = "more than ";
4327c2be9f8Sschwarze 		if (m->last->nchild > val)
4337c2be9f8Sschwarze 			return(1);
4347c2be9f8Sschwarze 		break;
4357c2be9f8Sschwarze 	case (CHECK_EQ):
4367c2be9f8Sschwarze 		p = "";
4377c2be9f8Sschwarze 		if (val == m->last->nchild)
4387c2be9f8Sschwarze 			return(1);
4397c2be9f8Sschwarze 		break;
44020fa2881Sschwarze 	default:
44120fa2881Sschwarze 		abort();
44220fa2881Sschwarze 		/* NOTREACHED */
4437c2be9f8Sschwarze 	}
4447c2be9f8Sschwarze 
445bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
446a35fc07aSschwarze 	mandoc_vmsg(t, m->parse, m->last->line, m->last->pos,
4477c2be9f8Sschwarze 			"want %s%d children (have %d)",
44819a69263Sschwarze 			p, val, m->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 
507bb648afaSschwarze static int
5087c2be9f8Sschwarze hwarn_le1(POST_ARGS)
5097c2be9f8Sschwarze {
5107c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
5117c2be9f8Sschwarze }
512f73abda9Skristaps 
51320fa2881Sschwarze static void
51431e23753Sschwarze check_args(struct mdoc *m, struct mdoc_node *n)
515f73abda9Skristaps {
516f73abda9Skristaps 	int		 i;
517f73abda9Skristaps 
518f73abda9Skristaps 	if (NULL == n->args)
51920fa2881Sschwarze 		return;
520f73abda9Skristaps 
521f73abda9Skristaps 	assert(n->args->argc);
522f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
52320fa2881Sschwarze 		check_argv(m, n, &n->args->argv[i]);
524f73abda9Skristaps }
525f73abda9Skristaps 
52620fa2881Sschwarze static void
52731e23753Sschwarze check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
528f73abda9Skristaps {
529f73abda9Skristaps 	int		 i;
530f73abda9Skristaps 
531f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
53220fa2881Sschwarze 		check_text(m, v->line, v->pos, v->value[i]);
533f73abda9Skristaps 
53420fa2881Sschwarze 	/* FIXME: move to post_std(). */
53520fa2881Sschwarze 
53620fa2881Sschwarze 	if (MDOC_Std == v->arg)
53720fa2881Sschwarze 		if ( ! (v->sz || m->meta.name))
53820fa2881Sschwarze 			mdoc_nmsg(m, n, MANDOCERR_NONAME);
539f73abda9Skristaps }
540f73abda9Skristaps 
54120fa2881Sschwarze static void
542ddce0b0cSschwarze check_text(struct mdoc *m, int ln, int pos, char *p)
543f73abda9Skristaps {
54404e980cbSschwarze 	char		*cp;
545769ee804Sschwarze 
54604e980cbSschwarze 	if (MDOC_LITERAL & m->flags)
5471cdbf331Sschwarze 		return;
5481cdbf331Sschwarze 
5491cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
55004e980cbSschwarze 		mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
551f73abda9Skristaps }
552f73abda9Skristaps 
553f73abda9Skristaps static int
554dd94fa3aSschwarze check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
555f73abda9Skristaps {
556f73abda9Skristaps 
557f73abda9Skristaps 	assert(n->parent);
558f73abda9Skristaps 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
559f73abda9Skristaps 			(t == n->parent->type))
560f73abda9Skristaps 		return(1);
561f73abda9Skristaps 
562a35fc07aSschwarze 	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
563a35fc07aSschwarze 			n->pos, "want parent %s", MDOC_ROOT == t ?
564a35fc07aSschwarze 			"<root>" : mdoc_macronames[tok]);
5656e03d529Sschwarze 	return(0);
566f73abda9Skristaps }
567f73abda9Skristaps 
568f73abda9Skristaps 
569f73abda9Skristaps static int
570f73abda9Skristaps pre_display(PRE_ARGS)
571f73abda9Skristaps {
572f73abda9Skristaps 	struct mdoc_node *node;
573f73abda9Skristaps 
574f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
575f73abda9Skristaps 		return(1);
576f73abda9Skristaps 
577f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
578f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
579f73abda9Skristaps 			if (MDOC_Bd == node->tok)
580f73abda9Skristaps 				break;
58120fa2881Sschwarze 
58205c39368Sschwarze 	if (node)
5836e03d529Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
58405c39368Sschwarze 
58505c39368Sschwarze 	return(1);
586f73abda9Skristaps }
587f73abda9Skristaps 
588f73abda9Skristaps 
589f73abda9Skristaps static int
590f73abda9Skristaps pre_bl(PRE_ARGS)
591f73abda9Skristaps {
59231e23753Sschwarze 	int		  i, comp, dup;
59331e23753Sschwarze 	const char	 *offs, *width;
5946093755cSschwarze 	enum mdoc_list	  lt;
595769ee804Sschwarze 	struct mdoc_node *np;
596f73abda9Skristaps 
5976093755cSschwarze 	if (MDOC_BLOCK != n->type) {
598769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
599769ee804Sschwarze 			assert(n->pending);
600769ee804Sschwarze 			np = n->pending->parent;
601769ee804Sschwarze 		} else
602769ee804Sschwarze 			np = n->parent;
603769ee804Sschwarze 
604769ee804Sschwarze 		assert(np);
605769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
606769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
607f73abda9Skristaps 		return(1);
6086e03d529Sschwarze 	}
609f73abda9Skristaps 
6106093755cSschwarze 	/*
6116093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
6126093755cSschwarze 	 * the first mentioned list type and warn about any remaining
6136093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
6146093755cSschwarze 	 */
615f73abda9Skristaps 
616f73abda9Skristaps 	/* LINTED */
6176093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
6186093755cSschwarze 		lt = LIST__NONE;
61931e23753Sschwarze 		dup = comp = 0;
62031e23753Sschwarze 		width = offs = NULL;
6216093755cSschwarze 		switch (n->args->argv[i].arg) {
6226093755cSschwarze 		/* Set list types. */
623f73abda9Skristaps 		case (MDOC_Bullet):
6246093755cSschwarze 			lt = LIST_bullet;
6256093755cSschwarze 			break;
626f73abda9Skristaps 		case (MDOC_Dash):
6276093755cSschwarze 			lt = LIST_dash;
6286093755cSschwarze 			break;
629f73abda9Skristaps 		case (MDOC_Enum):
6306093755cSschwarze 			lt = LIST_enum;
6316093755cSschwarze 			break;
632f73abda9Skristaps 		case (MDOC_Hyphen):
6336093755cSschwarze 			lt = LIST_hyphen;
6346093755cSschwarze 			break;
635f73abda9Skristaps 		case (MDOC_Item):
6366093755cSschwarze 			lt = LIST_item;
6376093755cSschwarze 			break;
638f73abda9Skristaps 		case (MDOC_Tag):
6396093755cSschwarze 			lt = LIST_tag;
6406093755cSschwarze 			break;
641f73abda9Skristaps 		case (MDOC_Diag):
6426093755cSschwarze 			lt = LIST_diag;
6436093755cSschwarze 			break;
644f73abda9Skristaps 		case (MDOC_Hang):
6456093755cSschwarze 			lt = LIST_hang;
6466093755cSschwarze 			break;
647f73abda9Skristaps 		case (MDOC_Ohang):
6486093755cSschwarze 			lt = LIST_ohang;
6496093755cSschwarze 			break;
650f73abda9Skristaps 		case (MDOC_Inset):
6516093755cSschwarze 			lt = LIST_inset;
6526093755cSschwarze 			break;
653f73abda9Skristaps 		case (MDOC_Column):
6546093755cSschwarze 			lt = LIST_column;
65564d728e4Sschwarze 			break;
6566093755cSschwarze 		/* Set list arguments. */
65750e63e03Sschwarze 		case (MDOC_Compact):
6588c62fbf5Sschwarze 			dup = n->norm->Bl.comp;
65931e23753Sschwarze 			comp = 1;
66050e63e03Sschwarze 			break;
661f73abda9Skristaps 		case (MDOC_Width):
66222972b14Sschwarze 			/* NB: this can be empty! */
66322972b14Sschwarze 			if (n->args->argv[i].sz) {
66431e23753Sschwarze 				width = n->args->argv[i].value[0];
66522972b14Sschwarze 				dup = (NULL != n->norm->Bl.width);
66622972b14Sschwarze 				break;
66722972b14Sschwarze 			}
66822972b14Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
66964d728e4Sschwarze 			break;
670f73abda9Skristaps 		case (MDOC_Offset):
67131e23753Sschwarze 			/* NB: this can be empty! */
67231e23753Sschwarze 			if (n->args->argv[i].sz) {
67331e23753Sschwarze 				offs = n->args->argv[i].value[0];
6748c62fbf5Sschwarze 				dup = (NULL != n->norm->Bl.offs);
67531e23753Sschwarze 				break;
67631e23753Sschwarze 			}
67720fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
678f73abda9Skristaps 			break;
679ddce0b0cSschwarze 		default:
680ddce0b0cSschwarze 			continue;
681f73abda9Skristaps 		}
682f73abda9Skristaps 
6836093755cSschwarze 		/* Check: duplicate auxiliary arguments. */
6846093755cSschwarze 
68520fa2881Sschwarze 		if (dup)
68620fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
6876093755cSschwarze 
68831e23753Sschwarze 		if (comp && ! dup)
6898c62fbf5Sschwarze 			n->norm->Bl.comp = comp;
69031e23753Sschwarze 		if (offs && ! dup)
6918c62fbf5Sschwarze 			n->norm->Bl.offs = offs;
69231e23753Sschwarze 		if (width && ! dup)
6938c62fbf5Sschwarze 			n->norm->Bl.width = width;
69431e23753Sschwarze 
6956093755cSschwarze 		/* Check: multiple list types. */
6966093755cSschwarze 
6978c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
69820fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
6996093755cSschwarze 
7006093755cSschwarze 		/* Assign list type. */
7016093755cSschwarze 
7028c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
7038c62fbf5Sschwarze 			n->norm->Bl.type = lt;
704769ee804Sschwarze 			/* Set column information, too. */
705769ee804Sschwarze 			if (LIST_column == lt) {
7068c62fbf5Sschwarze 				n->norm->Bl.ncols =
707769ee804Sschwarze 					n->args->argv[i].sz;
70804e980cbSschwarze 				n->norm->Bl.cols = (void *)
709769ee804Sschwarze 					n->args->argv[i].value;
710769ee804Sschwarze 			}
711769ee804Sschwarze 		}
7126093755cSschwarze 
7136093755cSschwarze 		/* The list type should come first. */
7146093755cSschwarze 
7158c62fbf5Sschwarze 		if (n->norm->Bl.type == LIST__NONE)
7168c62fbf5Sschwarze 			if (n->norm->Bl.width ||
7178c62fbf5Sschwarze 					n->norm->Bl.offs ||
7188c62fbf5Sschwarze 					n->norm->Bl.comp)
71920fa2881Sschwarze 				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
7206093755cSschwarze 
7216093755cSschwarze 		continue;
7226093755cSschwarze 	}
7236093755cSschwarze 
7246093755cSschwarze 	/* Allow lists to default to LIST_item. */
7256093755cSschwarze 
7268c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
72720fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
7288c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
7296e03d529Sschwarze 	}
730f73abda9Skristaps 
73164d728e4Sschwarze 	/*
73264d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
73364d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
7345eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
7355eced068Sschwarze 	 * no warning.
73664d728e4Sschwarze 	 */
73764d728e4Sschwarze 
7388c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
7396093755cSschwarze 	case (LIST_tag):
7405eced068Sschwarze 		if (NULL == n->norm->Bl.width)
74120fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
742f73abda9Skristaps 		break;
7436093755cSschwarze 	case (LIST_column):
7446093755cSschwarze 		/* FALLTHROUGH */
7456093755cSschwarze 	case (LIST_diag):
7466093755cSschwarze 		/* FALLTHROUGH */
7476093755cSschwarze 	case (LIST_ohang):
7486093755cSschwarze 		/* FALLTHROUGH */
7496093755cSschwarze 	case (LIST_inset):
7506093755cSschwarze 		/* FALLTHROUGH */
7516093755cSschwarze 	case (LIST_item):
7528c62fbf5Sschwarze 		if (n->norm->Bl.width)
753817ac90bSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
7546093755cSschwarze 		break;
7555eced068Sschwarze 	case (LIST_bullet):
7565eced068Sschwarze 		/* FALLTHROUGH */
7575eced068Sschwarze 	case (LIST_dash):
7585eced068Sschwarze 		/* FALLTHROUGH */
7595eced068Sschwarze 	case (LIST_hyphen):
7605eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7615eced068Sschwarze 			n->norm->Bl.width = "2n";
7625eced068Sschwarze 		break;
7635eced068Sschwarze 	case (LIST_enum):
7645eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7655eced068Sschwarze 			n->norm->Bl.width = "3n";
7665eced068Sschwarze 		break;
76764d728e4Sschwarze 	default:
768f73abda9Skristaps 		break;
76964d728e4Sschwarze 	}
77064d728e4Sschwarze 
771f73abda9Skristaps 	return(1);
772f73abda9Skristaps }
773f73abda9Skristaps 
774f73abda9Skristaps 
775f73abda9Skristaps static int
776f73abda9Skristaps pre_bd(PRE_ARGS)
777f73abda9Skristaps {
77831e23753Sschwarze 	int		  i, dup, comp;
77931e23753Sschwarze 	enum mdoc_disp 	  dt;
78031e23753Sschwarze 	const char	 *offs;
781769ee804Sschwarze 	struct mdoc_node *np;
782f73abda9Skristaps 
78331e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
784769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
785769ee804Sschwarze 			assert(n->pending);
786769ee804Sschwarze 			np = n->pending->parent;
787769ee804Sschwarze 		} else
788769ee804Sschwarze 			np = n->parent;
789769ee804Sschwarze 
790769ee804Sschwarze 		assert(np);
791769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
792769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
793f73abda9Skristaps 		return(1);
7946e03d529Sschwarze 	}
795f73abda9Skristaps 
796f73abda9Skristaps 	/* LINTED */
79731e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
79831e23753Sschwarze 		dt = DISP__NONE;
79931e23753Sschwarze 		dup = comp = 0;
80031e23753Sschwarze 		offs = NULL;
80131e23753Sschwarze 
802f73abda9Skristaps 		switch (n->args->argv[i].arg) {
803b822ca0dSschwarze 		case (MDOC_Centred):
80431e23753Sschwarze 			dt = DISP_centred;
80531e23753Sschwarze 			break;
806f73abda9Skristaps 		case (MDOC_Ragged):
80731e23753Sschwarze 			dt = DISP_ragged;
80831e23753Sschwarze 			break;
809f73abda9Skristaps 		case (MDOC_Unfilled):
81031e23753Sschwarze 			dt = DISP_unfilled;
81131e23753Sschwarze 			break;
812f73abda9Skristaps 		case (MDOC_Filled):
81331e23753Sschwarze 			dt = DISP_filled;
81431e23753Sschwarze 			break;
815f73abda9Skristaps 		case (MDOC_Literal):
81631e23753Sschwarze 			dt = DISP_literal;
817f73abda9Skristaps 			break;
81831e23753Sschwarze 		case (MDOC_File):
81931e23753Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
8206e03d529Sschwarze 			return(0);
82131e23753Sschwarze 		case (MDOC_Offset):
82231e23753Sschwarze 			/* NB: this can be empty! */
82331e23753Sschwarze 			if (n->args->argv[i].sz) {
82431e23753Sschwarze 				offs = n->args->argv[i].value[0];
8258c62fbf5Sschwarze 				dup = (NULL != n->norm->Bd.offs);
826f73abda9Skristaps 				break;
827f73abda9Skristaps 			}
82820fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
82931e23753Sschwarze 			break;
83031e23753Sschwarze 		case (MDOC_Compact):
83131e23753Sschwarze 			comp = 1;
8328c62fbf5Sschwarze 			dup = n->norm->Bd.comp;
83331e23753Sschwarze 			break;
83431e23753Sschwarze 		default:
83531e23753Sschwarze 			abort();
83631e23753Sschwarze 			/* NOTREACHED */
83731e23753Sschwarze 		}
83831e23753Sschwarze 
83931e23753Sschwarze 		/* Check whether we have duplicates. */
84031e23753Sschwarze 
84120fa2881Sschwarze 		if (dup)
84220fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
84331e23753Sschwarze 
84431e23753Sschwarze 		/* Make our auxiliary assignments. */
84531e23753Sschwarze 
84631e23753Sschwarze 		if (offs && ! dup)
8478c62fbf5Sschwarze 			n->norm->Bd.offs = offs;
84831e23753Sschwarze 		if (comp && ! dup)
8498c62fbf5Sschwarze 			n->norm->Bd.comp = comp;
85031e23753Sschwarze 
85131e23753Sschwarze 		/* Check whether a type has already been assigned. */
85231e23753Sschwarze 
8538c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
85420fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
85531e23753Sschwarze 
85631e23753Sschwarze 		/* Make our type assignment. */
85731e23753Sschwarze 
8588c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
8598c62fbf5Sschwarze 			n->norm->Bd.type = dt;
86031e23753Sschwarze 	}
86131e23753Sschwarze 
8628c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
86320fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
8648c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
86531e23753Sschwarze 	}
86631e23753Sschwarze 
86731e23753Sschwarze 	return(1);
868f73abda9Skristaps }
869f73abda9Skristaps 
870f73abda9Skristaps 
871f73abda9Skristaps static int
872f73abda9Skristaps pre_ss(PRE_ARGS)
873f73abda9Skristaps {
874f73abda9Skristaps 
875f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
876f73abda9Skristaps 		return(1);
877f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
878f73abda9Skristaps }
879f73abda9Skristaps 
880f73abda9Skristaps 
881f73abda9Skristaps static int
882f73abda9Skristaps pre_sh(PRE_ARGS)
883f73abda9Skristaps {
884f73abda9Skristaps 
885f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
886f73abda9Skristaps 		return(1);
8879830bf9fSschwarze 
888f8618d99Sschwarze 	roff_regunset(mdoc->roff, REG_nS);
889a4c002ecSschwarze 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
890f73abda9Skristaps }
891f73abda9Skristaps 
892f73abda9Skristaps 
893f73abda9Skristaps static int
894f73abda9Skristaps pre_it(PRE_ARGS)
895f73abda9Skristaps {
896f73abda9Skristaps 
897f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
898f73abda9Skristaps 		return(1);
89920fa2881Sschwarze 
900f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
901f73abda9Skristaps }
902f73abda9Skristaps 
903f73abda9Skristaps 
904f73abda9Skristaps static int
905f73abda9Skristaps pre_an(PRE_ARGS)
906f73abda9Skristaps {
9076475d5b0Sschwarze 	int		 i;
908f73abda9Skristaps 
909769ee804Sschwarze 	if (NULL == n->args)
910f73abda9Skristaps 		return(1);
911769ee804Sschwarze 
9126475d5b0Sschwarze 	for (i = 1; i < (int)n->args->argc; i++)
91320fa2881Sschwarze 		mdoc_pmsg(mdoc, n->args->argv[i].line,
91420fa2881Sschwarze 			n->args->argv[i].pos, MANDOCERR_IGNARGV);
9157c2be9f8Sschwarze 
916769ee804Sschwarze 	if (MDOC_Split == n->args->argv[0].arg)
9178c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
918769ee804Sschwarze 	else if (MDOC_Nosplit == n->args->argv[0].arg)
9198c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
920769ee804Sschwarze 	else
921769ee804Sschwarze 		abort();
922769ee804Sschwarze 
923769ee804Sschwarze 	return(1);
924f73abda9Skristaps }
925f73abda9Skristaps 
926f73abda9Skristaps static int
92720fa2881Sschwarze pre_std(PRE_ARGS)
928f73abda9Skristaps {
929f73abda9Skristaps 
93020fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
93120fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
93220fa2881Sschwarze 			return(1);
933f73abda9Skristaps 
93420fa2881Sschwarze 	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
9356093755cSschwarze 	return(1);
9366093755cSschwarze }
9376093755cSschwarze 
9386093755cSschwarze static int
939f73abda9Skristaps pre_dt(PRE_ARGS)
940f73abda9Skristaps {
941f73abda9Skristaps 
942b058e777Sschwarze 	if (NULL == mdoc->meta.date || mdoc->meta.os)
94320fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
94420fa2881Sschwarze 
945f73abda9Skristaps 	if (mdoc->meta.title)
94620fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
94720fa2881Sschwarze 
948f73abda9Skristaps 	return(1);
949f73abda9Skristaps }
950f73abda9Skristaps 
951f73abda9Skristaps static int
952f73abda9Skristaps pre_os(PRE_ARGS)
953f73abda9Skristaps {
954f73abda9Skristaps 
955b058e777Sschwarze 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
95620fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
95720fa2881Sschwarze 
958f73abda9Skristaps 	if (mdoc->meta.os)
95920fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
96020fa2881Sschwarze 
961f73abda9Skristaps 	return(1);
962f73abda9Skristaps }
963f73abda9Skristaps 
964f73abda9Skristaps static int
965f73abda9Skristaps pre_dd(PRE_ARGS)
966f73abda9Skristaps {
967f73abda9Skristaps 
968f73abda9Skristaps 	if (mdoc->meta.title || mdoc->meta.os)
96920fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
97020fa2881Sschwarze 
971f73abda9Skristaps 	if (mdoc->meta.date)
97220fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
97320fa2881Sschwarze 
974f73abda9Skristaps 	return(1);
975f73abda9Skristaps }
976f73abda9Skristaps 
977f73abda9Skristaps 
978f73abda9Skristaps static int
979f73abda9Skristaps post_bf(POST_ARGS)
980f73abda9Skristaps {
981769ee804Sschwarze 	struct mdoc_node *np;
982ddce0b0cSschwarze 	enum mdocargt	  arg;
983f73abda9Skristaps 
984769ee804Sschwarze 	/*
985769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
986769ee804Sschwarze 	 * element, which contains the goods.
987769ee804Sschwarze 	 */
988769ee804Sschwarze 
989769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
990769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
991769ee804Sschwarze 			assert(mdoc->last->pending);
992769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
993769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
994769ee804Sschwarze 			np = mdoc->last->parent->head;
995769ee804Sschwarze 		} else
996769ee804Sschwarze 			np = mdoc->last->head;
997769ee804Sschwarze 
998769ee804Sschwarze 		assert(np);
999769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
1000769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
1001f73abda9Skristaps 		return(1);
10026e03d529Sschwarze 	}
1003f73abda9Skristaps 
1004769ee804Sschwarze 	np = mdoc->last;
1005769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
1006769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
100750d41253Sschwarze 
1008769ee804Sschwarze 	/*
1009769ee804Sschwarze 	 * Cannot have both argument and parameter.
1010769ee804Sschwarze 	 * If neither is specified, let it through with a warning.
1011769ee804Sschwarze 	 */
1012f73abda9Skristaps 
1013769ee804Sschwarze 	if (np->parent->args && np->child) {
1014769ee804Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
10156e03d529Sschwarze 		return(0);
101620fa2881Sschwarze 	} else if (NULL == np->parent->args && NULL == np->child) {
101720fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
101820fa2881Sschwarze 		return(1);
101920fa2881Sschwarze 	}
1020769ee804Sschwarze 
1021769ee804Sschwarze 	/* Extract argument into data. */
1022769ee804Sschwarze 
1023769ee804Sschwarze 	if (np->parent->args) {
1024769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
1025769ee804Sschwarze 		if (MDOC_Emphasis == arg)
10268c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
1027769ee804Sschwarze 		else if (MDOC_Literal == arg)
10288c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
1029769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
10308c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
1031769ee804Sschwarze 		else
1032769ee804Sschwarze 			abort();
1033769ee804Sschwarze 		return(1);
1034769ee804Sschwarze 	}
1035769ee804Sschwarze 
1036769ee804Sschwarze 	/* Extract parameter into data. */
1037769ee804Sschwarze 
1038769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
10398c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
1040769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
10418c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
1042769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
10438c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
104420fa2881Sschwarze 	else
104520fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1046769ee804Sschwarze 
1047769ee804Sschwarze 	return(1);
1048f73abda9Skristaps }
1049f73abda9Skristaps 
1050f73abda9Skristaps static int
105171719887Sschwarze post_lb(POST_ARGS)
105271719887Sschwarze {
105320fa2881Sschwarze 	const char	*p;
105420fa2881Sschwarze 	char		*buf;
105520fa2881Sschwarze 	size_t		 sz;
105671719887Sschwarze 
1057bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1058bb648afaSschwarze 
105920fa2881Sschwarze 	assert(mdoc->last->child);
106020fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
106120fa2881Sschwarze 
106220fa2881Sschwarze 	p = mdoc_a2lib(mdoc->last->child->string);
106320fa2881Sschwarze 
106420fa2881Sschwarze 	/* If lookup ok, replace with table value. */
106520fa2881Sschwarze 
106620fa2881Sschwarze 	if (p) {
106720fa2881Sschwarze 		free(mdoc->last->child->string);
106820fa2881Sschwarze 		mdoc->last->child->string = mandoc_strdup(p);
106971719887Sschwarze 		return(1);
107071719887Sschwarze 	}
107171719887Sschwarze 
107220fa2881Sschwarze 	/* If not, use "library ``xxxx''. */
107320fa2881Sschwarze 
107420fa2881Sschwarze 	sz = strlen(mdoc->last->child->string) +
107520fa2881Sschwarze 		2 + strlen("\\(lqlibrary\\(rq");
107620fa2881Sschwarze 	buf = mandoc_malloc(sz);
107720fa2881Sschwarze 	snprintf(buf, sz, "library \\(lq%s\\(rq",
107820fa2881Sschwarze 			mdoc->last->child->string);
107920fa2881Sschwarze 	free(mdoc->last->child->string);
108020fa2881Sschwarze 	mdoc->last->child->string = buf;
108120fa2881Sschwarze 	return(1);
108220fa2881Sschwarze }
108371719887Sschwarze 
108471719887Sschwarze static int
1085b31af00dSschwarze post_eoln(POST_ARGS)
1086b31af00dSschwarze {
1087b31af00dSschwarze 
108820fa2881Sschwarze 	if (mdoc->last->child)
108920fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1090b31af00dSschwarze 	return(1);
1091b31af00dSschwarze }
1092b31af00dSschwarze 
1093b31af00dSschwarze 
1094b31af00dSschwarze static int
10958521b0bcSschwarze post_vt(POST_ARGS)
10968521b0bcSschwarze {
10978521b0bcSschwarze 	const struct mdoc_node *n;
10988521b0bcSschwarze 
10998521b0bcSschwarze 	/*
11008521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
11018521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1102e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1103e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
11048521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
11058521b0bcSschwarze 	 */
11068521b0bcSschwarze 
11078521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
11088521b0bcSschwarze 		return(1);
11098521b0bcSschwarze 
11108521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1111bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
111220fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
11138521b0bcSschwarze 
11148521b0bcSschwarze 	return(1);
11158521b0bcSschwarze }
11168521b0bcSschwarze 
11178521b0bcSschwarze 
11188521b0bcSschwarze static int
1119f73abda9Skristaps post_nm(POST_ARGS)
1120f73abda9Skristaps {
112120fa2881Sschwarze 	char		 buf[BUFSIZ];
112204e980cbSschwarze 	int		 c;
112320fa2881Sschwarze 
1124160ac481Sschwarze 	if (NULL != mdoc->meta.name)
112520fa2881Sschwarze 		return(1);
112620fa2881Sschwarze 
1127160ac481Sschwarze 	/* Try to use our children for setting the meta name. */
112820fa2881Sschwarze 
1129160ac481Sschwarze 	if (NULL != mdoc->last->child) {
113004e980cbSschwarze 		buf[0] = '\0';
1131160ac481Sschwarze 		c = concat(buf, mdoc->last->child, BUFSIZ);
1132160ac481Sschwarze 	} else
1133160ac481Sschwarze 		c = 0;
1134160ac481Sschwarze 
1135160ac481Sschwarze 	switch (c) {
1136160ac481Sschwarze 	case (-1):
113704e980cbSschwarze 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
113820fa2881Sschwarze 		return(0);
1139160ac481Sschwarze 	case (0):
1140160ac481Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1141160ac481Sschwarze 		mdoc->meta.name = mandoc_strdup("UNKNOWN");
1142160ac481Sschwarze 		break;
1143160ac481Sschwarze 	default:
114420fa2881Sschwarze 		mdoc->meta.name = mandoc_strdup(buf);
1145160ac481Sschwarze 		break;
1146160ac481Sschwarze 	}
114720fa2881Sschwarze 	return(1);
114820fa2881Sschwarze }
114920fa2881Sschwarze 
115020fa2881Sschwarze static int
115120fa2881Sschwarze post_literal(POST_ARGS)
115220fa2881Sschwarze {
115320fa2881Sschwarze 
115420fa2881Sschwarze 	/*
115520fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
115620fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
115720fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
115820fa2881Sschwarze 	 * off in general since displays can't be nested.
115920fa2881Sschwarze 	 */
116020fa2881Sschwarze 
116120fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
116220fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
116320fa2881Sschwarze 
116420fa2881Sschwarze 	return(1);
116520fa2881Sschwarze }
116620fa2881Sschwarze 
116720fa2881Sschwarze static int
116820fa2881Sschwarze post_defaults(POST_ARGS)
116920fa2881Sschwarze {
117020fa2881Sschwarze 	struct mdoc_node *nn;
117120fa2881Sschwarze 
117220fa2881Sschwarze 	/*
117320fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
117420fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
117520fa2881Sschwarze 	 * gets an empty string.
117620fa2881Sschwarze 	 */
1177f73abda9Skristaps 
1178f73abda9Skristaps 	if (mdoc->last->child)
1179f73abda9Skristaps 		return(1);
118020fa2881Sschwarze 
118120fa2881Sschwarze 	nn = mdoc->last;
118220fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
118320fa2881Sschwarze 
118420fa2881Sschwarze 	switch (nn->tok) {
118520fa2881Sschwarze 	case (MDOC_Ar):
118620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
118720fa2881Sschwarze 			return(0);
118820fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
118920fa2881Sschwarze 			return(0);
119020fa2881Sschwarze 		break;
119120fa2881Sschwarze 	case (MDOC_At):
119220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
119320fa2881Sschwarze 			return(0);
119420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
119520fa2881Sschwarze 			return(0);
119620fa2881Sschwarze 		break;
119720fa2881Sschwarze 	case (MDOC_Li):
119820fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
119920fa2881Sschwarze 			return(0);
120020fa2881Sschwarze 		break;
120120fa2881Sschwarze 	case (MDOC_Pa):
120220fa2881Sschwarze 		/* FALLTHROUGH */
120320fa2881Sschwarze 	case (MDOC_Mt):
120420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
120520fa2881Sschwarze 			return(0);
120620fa2881Sschwarze 		break;
120720fa2881Sschwarze 	default:
120820fa2881Sschwarze 		abort();
120920fa2881Sschwarze 		/* NOTREACHED */
1210f73abda9Skristaps 	}
1211f73abda9Skristaps 
121220fa2881Sschwarze 	mdoc->last = nn;
121320fa2881Sschwarze 	return(1);
121420fa2881Sschwarze }
1215f73abda9Skristaps 
1216f73abda9Skristaps static int
1217f73abda9Skristaps post_at(POST_ARGS)
1218f73abda9Skristaps {
121920fa2881Sschwarze 	const char	 *p, *q;
122020fa2881Sschwarze 	char		 *buf;
122120fa2881Sschwarze 	size_t		  sz;
122220fa2881Sschwarze 
122320fa2881Sschwarze 	/*
122420fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
122520fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
122620fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
122720fa2881Sschwarze 	 */
1228f73abda9Skristaps 
1229f73abda9Skristaps 	if (NULL == mdoc->last->child)
1230f73abda9Skristaps 		return(1);
123120fa2881Sschwarze 
12326e03d529Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
123320fa2881Sschwarze 	p = mdoc_a2att(mdoc->last->child->string);
123420fa2881Sschwarze 
123520fa2881Sschwarze 	if (p) {
123620fa2881Sschwarze 		free(mdoc->last->child->string);
123720fa2881Sschwarze 		mdoc->last->child->string = mandoc_strdup(p);
123820fa2881Sschwarze 	} else {
123920fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
124020fa2881Sschwarze 		p = "AT&T UNIX ";
124120fa2881Sschwarze 		q = mdoc->last->child->string;
124220fa2881Sschwarze 		sz = strlen(p) + strlen(q) + 1;
124320fa2881Sschwarze 		buf = mandoc_malloc(sz);
124420fa2881Sschwarze 		strlcpy(buf, p, sz);
124520fa2881Sschwarze 		strlcat(buf, q, sz);
124620fa2881Sschwarze 		free(mdoc->last->child->string);
124720fa2881Sschwarze 		mdoc->last->child->string = buf;
1248f73abda9Skristaps 	}
1249f73abda9Skristaps 
125020fa2881Sschwarze 	return(1);
125120fa2881Sschwarze }
1252f73abda9Skristaps 
1253f73abda9Skristaps static int
1254f73abda9Skristaps post_an(POST_ARGS)
1255f73abda9Skristaps {
1256769ee804Sschwarze 	struct mdoc_node *np;
1257f73abda9Skristaps 
1258769ee804Sschwarze 	np = mdoc->last;
1259e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1260e7a93ef3Sschwarze 		if (0 == np->child)
1261e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1262e7a93ef3Sschwarze 	} else if (np->child)
1263bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
126420fa2881Sschwarze 
126520fa2881Sschwarze 	return(1);
1266f73abda9Skristaps }
1267f73abda9Skristaps 
1268f73abda9Skristaps 
1269f73abda9Skristaps static int
1270f73abda9Skristaps post_it(POST_ARGS)
1271f73abda9Skristaps {
127219a69263Sschwarze 	int		  i, cols;
12736093755cSschwarze 	enum mdoc_list	  lt;
1274f73abda9Skristaps 	struct mdoc_node *n, *c;
12756093755cSschwarze 	enum mandocerr	  er;
1276f73abda9Skristaps 
1277f73abda9Skristaps 	if (MDOC_BLOCK != mdoc->last->type)
1278f73abda9Skristaps 		return(1);
1279f73abda9Skristaps 
1280f73abda9Skristaps 	n = mdoc->last->parent->parent;
12818c62fbf5Sschwarze 	lt = n->norm->Bl.type;
12826093755cSschwarze 
12836093755cSschwarze 	if (LIST__NONE == lt) {
12846e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
128520fa2881Sschwarze 		return(1);
12866e03d529Sschwarze 	}
1287f73abda9Skristaps 
12886093755cSschwarze 	switch (lt) {
12896093755cSschwarze 	case (LIST_tag):
12906093755cSschwarze 		if (mdoc->last->head->child)
1291f73abda9Skristaps 			break;
12926093755cSschwarze 		/* FIXME: give this a dummy value. */
129320fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1294f73abda9Skristaps 		break;
12956093755cSschwarze 	case (LIST_hang):
1296f73abda9Skristaps 		/* FALLTHROUGH */
12976093755cSschwarze 	case (LIST_ohang):
1298f73abda9Skristaps 		/* FALLTHROUGH */
12996093755cSschwarze 	case (LIST_inset):
1300f73abda9Skristaps 		/* FALLTHROUGH */
13016093755cSschwarze 	case (LIST_diag):
1302f73abda9Skristaps 		if (NULL == mdoc->last->head->child)
130320fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1304f73abda9Skristaps 		break;
13056093755cSschwarze 	case (LIST_bullet):
1306f73abda9Skristaps 		/* FALLTHROUGH */
13076093755cSschwarze 	case (LIST_dash):
1308f73abda9Skristaps 		/* FALLTHROUGH */
13096093755cSschwarze 	case (LIST_enum):
1310f73abda9Skristaps 		/* FALLTHROUGH */
13116093755cSschwarze 	case (LIST_hyphen):
13129f373962Sschwarze 		if (NULL == mdoc->last->body->child)
131320fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1314f73abda9Skristaps 		/* FALLTHROUGH */
13156093755cSschwarze 	case (LIST_item):
1316f73abda9Skristaps 		if (mdoc->last->head->child)
131720fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1318f73abda9Skristaps 		break;
13196093755cSschwarze 	case (LIST_column):
13208c62fbf5Sschwarze 		cols = (int)n->norm->Bl.ncols;
13216093755cSschwarze 
13226093755cSschwarze 		assert(NULL == mdoc->last->head->child);
13236093755cSschwarze 
13246093755cSschwarze 		if (NULL == mdoc->last->body->child)
132520fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
13266093755cSschwarze 
13276093755cSschwarze 		for (i = 0, c = mdoc->last->child; c; c = c->next)
13286093755cSschwarze 			if (MDOC_BODY == c->type)
1329f73abda9Skristaps 				i++;
133053292e81Sschwarze 
13316093755cSschwarze 		if (i < cols)
13326093755cSschwarze 			er = MANDOCERR_ARGCOUNT;
13336093755cSschwarze 		else if (i == cols || i == cols + 1)
1334f73abda9Skristaps 			break;
13356093755cSschwarze 		else
13366093755cSschwarze 			er = MANDOCERR_SYNTARGCOUNT;
133753292e81Sschwarze 
1338a35fc07aSschwarze 		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1339a35fc07aSschwarze 				mdoc->last->pos,
13406e03d529Sschwarze 				"columns == %d (have %d)", cols, i);
134119a69263Sschwarze 		return(MANDOCERR_ARGCOUNT == er);
1342f73abda9Skristaps 	default:
1343f73abda9Skristaps 		break;
1344f73abda9Skristaps 	}
1345f73abda9Skristaps 
1346f73abda9Skristaps 	return(1);
1347f73abda9Skristaps }
1348f73abda9Skristaps 
134920fa2881Sschwarze static int
135020fa2881Sschwarze post_bl_block(POST_ARGS)
135120fa2881Sschwarze {
1352bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
135320fa2881Sschwarze 
135420fa2881Sschwarze 	/*
135520fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
135620fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
135720fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
135820fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
135920fa2881Sschwarze 	 * rewritten into real lengths).
136020fa2881Sschwarze 	 */
136120fa2881Sschwarze 
136220fa2881Sschwarze 	n = mdoc->last;
136320fa2881Sschwarze 
13648c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
13658c62fbf5Sschwarze 			NULL == n->norm->Bl.width) {
136620fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
136720fa2881Sschwarze 			return(0);
1368bb99f0faSschwarze 		assert(n->norm->Bl.width);
13698c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
137020fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
137120fa2881Sschwarze 			return(0);
13728c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1373bb99f0faSschwarze 	}
1374bb99f0faSschwarze 
1375bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1376bb99f0faSschwarze 		if (NULL == ni->body)
1377bb99f0faSschwarze 			continue;
1378bb99f0faSschwarze 		nc = ni->body->last;
1379bb99f0faSschwarze 		while (NULL != nc) {
1380bb99f0faSschwarze 			switch (nc->tok) {
1381bb99f0faSschwarze 			case (MDOC_Pp):
1382bb99f0faSschwarze 				/* FALLTHROUGH */
1383bb99f0faSschwarze 			case (MDOC_Lp):
1384bb99f0faSschwarze 				/* FALLTHROUGH */
1385bb99f0faSschwarze 			case (MDOC_br):
1386bb99f0faSschwarze 				break;
1387bb99f0faSschwarze 			default:
1388bb99f0faSschwarze 				nc = NULL;
1389bb99f0faSschwarze 				continue;
1390bb99f0faSschwarze 			}
1391bb99f0faSschwarze 			if (NULL == ni->next) {
1392bb99f0faSschwarze 				mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1393bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1394bb99f0faSschwarze 					return(0);
1395bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1396bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
1397bb99f0faSschwarze 				mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1398bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1399bb99f0faSschwarze 			} else
1400bb99f0faSschwarze 				break;
1401bb99f0faSschwarze 			nc = ni->body->last;
1402bb99f0faSschwarze 		}
1403bb99f0faSschwarze 	}
140420fa2881Sschwarze 	return(1);
140520fa2881Sschwarze }
140620fa2881Sschwarze 
140720fa2881Sschwarze static int
140820fa2881Sschwarze post_bl_block_width(POST_ARGS)
140920fa2881Sschwarze {
141020fa2881Sschwarze 	size_t		  width;
141120fa2881Sschwarze 	int		  i;
141220fa2881Sschwarze 	enum mdoct	  tok;
141320fa2881Sschwarze 	struct mdoc_node *n;
141420fa2881Sschwarze 	char		  buf[NUMSIZ];
141520fa2881Sschwarze 
141620fa2881Sschwarze 	n = mdoc->last;
141720fa2881Sschwarze 
141820fa2881Sschwarze 	/*
141920fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
142020fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
142120fa2881Sschwarze 	 * literal string, or a scaling width.
142220fa2881Sschwarze 	 *
142320fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
142420fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
142520fa2881Sschwarze 	 */
142620fa2881Sschwarze 
14278c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
142820fa2881Sschwarze 		width = 6;
14298c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
143020fa2881Sschwarze 		return(1);
143119a69263Sschwarze 	else if (0 == (width = macro2len(tok)))  {
143220fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
143320fa2881Sschwarze 		return(1);
143420fa2881Sschwarze 	}
143520fa2881Sschwarze 
143620fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
143720fa2881Sschwarze 
143820fa2881Sschwarze 	assert(n->args);
143920fa2881Sschwarze 
144020fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
144120fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
144220fa2881Sschwarze 			break;
144320fa2881Sschwarze 
144420fa2881Sschwarze 	assert(i < (int)n->args->argc);
144520fa2881Sschwarze 
144604e980cbSschwarze 	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
144720fa2881Sschwarze 	free(n->args->argv[i].value[0]);
144820fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
144920fa2881Sschwarze 
145020fa2881Sschwarze 	/* Set our width! */
14518c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
145220fa2881Sschwarze 	return(1);
145320fa2881Sschwarze }
145420fa2881Sschwarze 
145520fa2881Sschwarze static int
145620fa2881Sschwarze post_bl_block_tag(POST_ARGS)
145720fa2881Sschwarze {
145820fa2881Sschwarze 	struct mdoc_node *n, *nn;
145920fa2881Sschwarze 	size_t		  sz, ssz;
146020fa2881Sschwarze 	int		  i;
146120fa2881Sschwarze 	char		  buf[NUMSIZ];
146220fa2881Sschwarze 
146320fa2881Sschwarze 	/*
146420fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
146520fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
146620fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
146720fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
146820fa2881Sschwarze 	 */
146920fa2881Sschwarze 
147020fa2881Sschwarze 	sz = 10;
147120fa2881Sschwarze 	n = mdoc->last;
147220fa2881Sschwarze 
147320fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
147420fa2881Sschwarze 		if (MDOC_It != nn->tok)
147520fa2881Sschwarze 			continue;
147620fa2881Sschwarze 
147720fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
147820fa2881Sschwarze 		nn = nn->head->child;
147920fa2881Sschwarze 
148020fa2881Sschwarze 		if (nn == NULL)
148120fa2881Sschwarze 			break;
148220fa2881Sschwarze 
148320fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
148420fa2881Sschwarze 			sz = strlen(nn->string) + 1;
148520fa2881Sschwarze 			break;
148620fa2881Sschwarze 		}
148720fa2881Sschwarze 
148819a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
148920fa2881Sschwarze 			sz = ssz;
149020fa2881Sschwarze 
149120fa2881Sschwarze 		break;
149220fa2881Sschwarze 	}
149320fa2881Sschwarze 
149420fa2881Sschwarze 	/* Defaults to ten ens. */
149520fa2881Sschwarze 
149604e980cbSschwarze 	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
149720fa2881Sschwarze 
149820fa2881Sschwarze 	/*
149920fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
150020fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
150120fa2881Sschwarze 	 */
150220fa2881Sschwarze 
150320fa2881Sschwarze 	assert(n->args);
150420fa2881Sschwarze 	i = (int)(n->args->argc)++;
150520fa2881Sschwarze 
150620fa2881Sschwarze 	n->args->argv = mandoc_realloc(n->args->argv,
150720fa2881Sschwarze 			n->args->argc * sizeof(struct mdoc_argv));
150820fa2881Sschwarze 
150920fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
151020fa2881Sschwarze 	n->args->argv[i].line = n->line;
151120fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
151220fa2881Sschwarze 	n->args->argv[i].sz = 1;
151320fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
151420fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
151520fa2881Sschwarze 
151620fa2881Sschwarze 	/* Set our width! */
15178c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
151820fa2881Sschwarze 	return(1);
151920fa2881Sschwarze }
152020fa2881Sschwarze 
1521f73abda9Skristaps 
1522f73abda9Skristaps static int
1523395185ccSschwarze post_bl_head(POST_ARGS)
1524395185ccSschwarze {
152520fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
152620fa2881Sschwarze 	int		  i, j;
1527395185ccSschwarze 
15288c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
152920fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
153020fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1531395185ccSschwarze 
153220fa2881Sschwarze 	/*
153320fa2881Sschwarze 	 * Convert old-style lists, where the column width specifiers
153420fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
153520fa2881Sschwarze 	 * lists where they're argument values following -column.
153620fa2881Sschwarze 	 */
153720fa2881Sschwarze 
153820fa2881Sschwarze 	/* First, disallow both types and allow normal-form. */
153920fa2881Sschwarze 
154020fa2881Sschwarze 	/*
154120fa2881Sschwarze 	 * TODO: technically, we can accept both and just merge the two
154220fa2881Sschwarze 	 * lists, but I'll leave that for another day.
154320fa2881Sschwarze 	 */
154420fa2881Sschwarze 
15458c62fbf5Sschwarze 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
154620fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
15476093755cSschwarze 		return(0);
154820fa2881Sschwarze 	} else if (NULL == mdoc->last->child)
154920fa2881Sschwarze 		return(1);
155020fa2881Sschwarze 
155120fa2881Sschwarze 	np = mdoc->last->parent;
155220fa2881Sschwarze 	assert(np->args);
155320fa2881Sschwarze 
155420fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
155520fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
155620fa2881Sschwarze 			break;
155720fa2881Sschwarze 
155820fa2881Sschwarze 	assert(j < (int)np->args->argc);
155920fa2881Sschwarze 	assert(0 == np->args->argv[j].sz);
156020fa2881Sschwarze 
156120fa2881Sschwarze 	/*
1562a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
156320fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
156420fa2881Sschwarze 	 * column field.  Then, delete the head children.
156520fa2881Sschwarze 	 */
156620fa2881Sschwarze 
156720fa2881Sschwarze 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
156820fa2881Sschwarze 	np->args->argv[j].value = mandoc_malloc
156920fa2881Sschwarze 		((size_t)mdoc->last->nchild * sizeof(char *));
157020fa2881Sschwarze 
15718c62fbf5Sschwarze 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
157204e980cbSschwarze 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
157320fa2881Sschwarze 
157420fa2881Sschwarze 	for (i = 0, nn = mdoc->last->child; nn; i++) {
157520fa2881Sschwarze 		np->args->argv[j].value[i] = nn->string;
157620fa2881Sschwarze 		nn->string = NULL;
157720fa2881Sschwarze 		nnp = nn;
157820fa2881Sschwarze 		nn = nn->next;
157920fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1580395185ccSschwarze 	}
158120fa2881Sschwarze 
158220fa2881Sschwarze 	mdoc->last->nchild = 0;
158320fa2881Sschwarze 	mdoc->last->child = NULL;
158420fa2881Sschwarze 
15856093755cSschwarze 	return(1);
1586b16e7ddfSschwarze }
1587b16e7ddfSschwarze 
1588395185ccSschwarze static int
1589f73abda9Skristaps post_bl(POST_ARGS)
1590f73abda9Skristaps {
1591f73abda9Skristaps 	struct mdoc_node	*n;
1592f73abda9Skristaps 
1593395185ccSschwarze 	if (MDOC_HEAD == mdoc->last->type)
1594395185ccSschwarze 		return(post_bl_head(mdoc));
159520fa2881Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
159620fa2881Sschwarze 		return(post_bl_block(mdoc));
1597f73abda9Skristaps 	if (MDOC_BODY != mdoc->last->type)
1598f73abda9Skristaps 		return(1);
1599f73abda9Skristaps 
1600f73abda9Skristaps 	for (n = mdoc->last->child; n; n = n->next) {
1601f6127a73Sschwarze 		switch (n->tok) {
1602f6127a73Sschwarze 		case (MDOC_Lp):
1603f6127a73Sschwarze 			/* FALLTHROUGH */
1604f6127a73Sschwarze 		case (MDOC_Pp):
1605f6127a73Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1606f6127a73Sschwarze 			/* FALLTHROUGH */
1607f6127a73Sschwarze 		case (MDOC_It):
1608f6127a73Sschwarze 			/* FALLTHROUGH */
1609f6127a73Sschwarze 		case (MDOC_Sm):
161039bb0097Sschwarze 			continue;
1611f6127a73Sschwarze 		default:
1612f6127a73Sschwarze 			break;
1613f6127a73Sschwarze 		}
1614f6127a73Sschwarze 
16156e03d529Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
16166e03d529Sschwarze 		return(0);
1617f73abda9Skristaps 	}
1618f73abda9Skristaps 
1619f73abda9Skristaps 	return(1);
1620f73abda9Skristaps }
1621f73abda9Skristaps 
1622f73abda9Skristaps static int
1623f73abda9Skristaps ebool(struct mdoc *mdoc)
1624f73abda9Skristaps {
1625f73abda9Skristaps 
1626bb648afaSschwarze 	if (NULL == mdoc->last->child) {
1627bb648afaSschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1628bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1629f73abda9Skristaps 		return(1);
1630bb648afaSschwarze 	}
1631bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1632f73abda9Skristaps 
163320fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
163420fa2881Sschwarze 
163520fa2881Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "on"))
163620fa2881Sschwarze 		return(1);
163720fa2881Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "off"))
163820fa2881Sschwarze 		return(1);
163920fa2881Sschwarze 
164020fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
164120fa2881Sschwarze 	return(1);
164220fa2881Sschwarze }
1643f73abda9Skristaps 
1644f73abda9Skristaps static int
1645f73abda9Skristaps post_root(POST_ARGS)
1646f73abda9Skristaps {
164720fa2881Sschwarze 	int		  erc;
164820fa2881Sschwarze 	struct mdoc_node *n;
1649f73abda9Skristaps 
165020fa2881Sschwarze 	erc = 0;
165120fa2881Sschwarze 
165220fa2881Sschwarze 	/* Check that we have a finished prologue. */
165320fa2881Sschwarze 
165420fa2881Sschwarze 	if ( ! (MDOC_PBODY & mdoc->flags)) {
165520fa2881Sschwarze 		erc++;
16566e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1657f73abda9Skristaps 	}
1658f73abda9Skristaps 
165920fa2881Sschwarze 	n = mdoc->first;
166020fa2881Sschwarze 	assert(n);
166120fa2881Sschwarze 
166220fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
166320fa2881Sschwarze 
166420fa2881Sschwarze 	if (NULL == n->child) {
166520fa2881Sschwarze 		erc++;
166620fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
166720fa2881Sschwarze 	} else if (MDOC_BLOCK != n->child->type ||
166820fa2881Sschwarze 			MDOC_Sh != n->child->tok) {
166920fa2881Sschwarze 		erc++;
167020fa2881Sschwarze 		/* Can this be lifted?  See rxdebug.1 for example. */
167120fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
167220fa2881Sschwarze 	}
167320fa2881Sschwarze 
167420fa2881Sschwarze 	return(erc ? 0 : 1);
167520fa2881Sschwarze }
1676f73abda9Skristaps 
1677f73abda9Skristaps static int
1678f73abda9Skristaps post_st(POST_ARGS)
1679f73abda9Skristaps {
1680bb648afaSschwarze 	struct mdoc_node	 *ch;
168120fa2881Sschwarze 	const char		 *p;
1682f73abda9Skristaps 
1683bb648afaSschwarze 	if (NULL == (ch = mdoc->last->child)) {
1684bb648afaSschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1685bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1686bb648afaSschwarze 		return(1);
1687bb648afaSschwarze 	}
168820fa2881Sschwarze 
1689bb648afaSschwarze 	assert(MDOC_TEXT == ch->type);
169020fa2881Sschwarze 
1691bb648afaSschwarze 	if (NULL == (p = mdoc_a2st(ch->string))) {
169220fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
169320fa2881Sschwarze 		mdoc_node_delete(mdoc, mdoc->last);
169420fa2881Sschwarze 	} else {
1695bb648afaSschwarze 		free(ch->string);
1696bb648afaSschwarze 		ch->string = mandoc_strdup(p);
1697f73abda9Skristaps 	}
1698f73abda9Skristaps 
169920fa2881Sschwarze 	return(1);
170020fa2881Sschwarze }
1701f73abda9Skristaps 
1702f73abda9Skristaps static int
1703011fe33bSschwarze post_rs(POST_ARGS)
1704011fe33bSschwarze {
170520fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
170620fa2881Sschwarze 	int		  i, j;
1707011fe33bSschwarze 
1708bb648afaSschwarze 	switch (mdoc->last->type) {
1709bb648afaSschwarze 	case (MDOC_HEAD):
1710bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1711011fe33bSschwarze 		return(1);
1712bb648afaSschwarze 	case (MDOC_BODY):
1713bb648afaSschwarze 		if (mdoc->last->child)
1714bb648afaSschwarze 			break;
1715bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1716bb648afaSschwarze 		return(1);
1717bb648afaSschwarze 	default:
1718bb648afaSschwarze 		return(1);
1719bb648afaSschwarze 	}
1720011fe33bSschwarze 
172120fa2881Sschwarze 	/*
172220fa2881Sschwarze 	 * Make sure only certain types of nodes are allowed within the
172320fa2881Sschwarze 	 * the `Rs' body.  Delete offending nodes and raise a warning.
172420fa2881Sschwarze 	 * Do this before re-ordering for the sake of clarity.
172520fa2881Sschwarze 	 */
172620fa2881Sschwarze 
172720fa2881Sschwarze 	next = NULL;
172820fa2881Sschwarze 	for (nn = mdoc->last->child; nn; nn = next) {
172920fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
173020fa2881Sschwarze 			if (nn->tok == rsord[i])
1731011fe33bSschwarze 				break;
173220fa2881Sschwarze 
173320fa2881Sschwarze 		if (i < RSORD_MAX) {
17345d273f35Sschwarze 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
17355d273f35Sschwarze 				mdoc->last->norm->Rs.quote_T++;
173620fa2881Sschwarze 			next = nn->next;
173720fa2881Sschwarze 			continue;
173820fa2881Sschwarze 		}
173920fa2881Sschwarze 
174020fa2881Sschwarze 		next = nn->next;
174120fa2881Sschwarze 		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
174220fa2881Sschwarze 		mdoc_node_delete(mdoc, nn);
174320fa2881Sschwarze 	}
174420fa2881Sschwarze 
174520fa2881Sschwarze 	/*
1746c6176538Sschwarze 	 * Nothing to sort if only invalid nodes were found
1747c6176538Sschwarze 	 * inside the `Rs' body.
1748c6176538Sschwarze 	 */
1749c6176538Sschwarze 
1750c6176538Sschwarze 	if (NULL == mdoc->last->child)
1751c6176538Sschwarze 		return(1);
1752c6176538Sschwarze 
1753c6176538Sschwarze 	/*
175420fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
175520fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
175620fa2881Sschwarze 	 * and correctly order it.  This is a insertion sort.
175720fa2881Sschwarze 	 */
175820fa2881Sschwarze 
175920fa2881Sschwarze 	next = NULL;
176020fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
176120fa2881Sschwarze 		/* Determine order of `nn'. */
176220fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
176320fa2881Sschwarze 			if (rsord[i] == nn->tok)
176420fa2881Sschwarze 				break;
176520fa2881Sschwarze 
176620fa2881Sschwarze 		/*
176720fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
176820fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
176920fa2881Sschwarze 		 * just re-ordering, there's no need for the
177020fa2881Sschwarze 		 * full unlink process.
177120fa2881Sschwarze 		 */
177220fa2881Sschwarze 
177320fa2881Sschwarze 		if (NULL != (next = nn->next))
177420fa2881Sschwarze 			next->prev = nn->prev;
177520fa2881Sschwarze 
177620fa2881Sschwarze 		if (NULL != (prev = nn->prev))
177720fa2881Sschwarze 			prev->next = nn->next;
177820fa2881Sschwarze 
177920fa2881Sschwarze 		nn->prev = nn->next = NULL;
178020fa2881Sschwarze 
178120fa2881Sschwarze 		/*
178220fa2881Sschwarze 		 * Scan back until we reach a node that's
178320fa2881Sschwarze 		 * ordered before `nn'.
178420fa2881Sschwarze 		 */
178520fa2881Sschwarze 
178620fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
178720fa2881Sschwarze 			/* Determine order of `prev'. */
178820fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
178920fa2881Sschwarze 				if (rsord[j] == prev->tok)
179020fa2881Sschwarze 					break;
179120fa2881Sschwarze 
179220fa2881Sschwarze 			if (j <= i)
179320fa2881Sschwarze 				break;
179420fa2881Sschwarze 		}
179520fa2881Sschwarze 
179620fa2881Sschwarze 		/*
179720fa2881Sschwarze 		 * Set `nn' back into its correct place in front
179820fa2881Sschwarze 		 * of the `prev' node.
179920fa2881Sschwarze 		 */
180020fa2881Sschwarze 
180120fa2881Sschwarze 		nn->prev = prev;
180220fa2881Sschwarze 
180320fa2881Sschwarze 		if (prev) {
180420fa2881Sschwarze 			if (prev->next)
180520fa2881Sschwarze 				prev->next->prev = nn;
180620fa2881Sschwarze 			nn->next = prev->next;
180720fa2881Sschwarze 			prev->next = nn;
180820fa2881Sschwarze 		} else {
180920fa2881Sschwarze 			mdoc->last->child->prev = nn;
181020fa2881Sschwarze 			nn->next = mdoc->last->child;
181120fa2881Sschwarze 			mdoc->last->child = nn;
181220fa2881Sschwarze 		}
1813011fe33bSschwarze 	}
1814011fe33bSschwarze 
1815011fe33bSschwarze 	return(1);
1816011fe33bSschwarze }
1817011fe33bSschwarze 
1818011fe33bSschwarze static int
1819af216717Sschwarze post_ns(POST_ARGS)
1820af216717Sschwarze {
1821af216717Sschwarze 
1822af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
1823af216717Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
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)) {
185420fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
185520fa2881Sschwarze 		return(1);
185620fa2881Sschwarze 	}
1857f73abda9Skristaps 
1858f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1859f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1860f73abda9Skristaps 			continue;
1861f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1862f73abda9Skristaps 			continue;
186320fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1864f73abda9Skristaps 	}
1865f73abda9Skristaps 
186649d529b5Sschwarze 	assert(n);
18674602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1868f73abda9Skristaps 		return(1);
1869f73abda9Skristaps 
187020fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
187120fa2881Sschwarze 	return(1);
187220fa2881Sschwarze }
1873f73abda9Skristaps 
1874f73abda9Skristaps static int
1875f73abda9Skristaps post_sh_head(POST_ARGS)
1876f73abda9Skristaps {
18773216dddfSschwarze 	char		 buf[BUFSIZ];
1878a2cff342Sschwarze 	struct mdoc_node *n;
1879f73abda9Skristaps 	enum mdoc_sec	 sec;
188004e980cbSschwarze 	int		 c;
1881f73abda9Skristaps 
1882f73abda9Skristaps 	/*
1883f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
188420fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
188520fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
188620fa2881Sschwarze 	 * manual sections.
1887f73abda9Skristaps 	 */
1888f73abda9Skristaps 
188904e980cbSschwarze 	sec = SEC_CUSTOM;
189004e980cbSschwarze 	buf[0] = '\0';
189104e980cbSschwarze 	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
189204e980cbSschwarze 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
18936e03d529Sschwarze 		return(0);
189404e980cbSschwarze 	} else if (1 == c)
189519a69263Sschwarze 		sec = a2sec(buf);
1896f73abda9Skristaps 
189720fa2881Sschwarze 	/* The NAME should be first. */
1898f73abda9Skristaps 
1899fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
190020fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
190120fa2881Sschwarze 
190220fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
190320fa2881Sschwarze 
190420fa2881Sschwarze 	if (SEC_SYNOPSIS == sec)
190520fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
190620fa2881Sschwarze 	else
190720fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
190820fa2881Sschwarze 
190920fa2881Sschwarze 	/* Mark our last section. */
191020fa2881Sschwarze 
191120fa2881Sschwarze 	mdoc->lastsec = sec;
19121eccdf28Sschwarze 
19131eccdf28Sschwarze 	/*
19141eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
19151eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
19161eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
19171eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
19181eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
19191eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
19201eccdf28Sschwarze 	 */
19211eccdf28Sschwarze 
1922a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
1923a2cff342Sschwarze 	mdoc->last->sec = sec;
1924a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
1925a2cff342Sschwarze 		n->sec = sec;
192620fa2881Sschwarze 
192720fa2881Sschwarze 	/* We don't care about custom sections after this. */
1928fccfce9dSschwarze 
1929f73abda9Skristaps 	if (SEC_CUSTOM == sec)
1930f73abda9Skristaps 		return(1);
1931fccfce9dSschwarze 
19326be99f77Sschwarze 	/*
193320fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
193420fa2881Sschwarze 	 * out of order.
19356be99f77Sschwarze 	 */
1936f73abda9Skristaps 
193720fa2881Sschwarze 	if (sec == mdoc->lastnamed)
193820fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
193920fa2881Sschwarze 
194020fa2881Sschwarze 	if (sec < mdoc->lastnamed)
194120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
194220fa2881Sschwarze 
194320fa2881Sschwarze 	/* Mark the last named section. */
194420fa2881Sschwarze 
194520fa2881Sschwarze 	mdoc->lastnamed = sec;
194620fa2881Sschwarze 
194720fa2881Sschwarze 	/* Check particular section/manual conventions. */
194820fa2881Sschwarze 
194992c0ca7fSschwarze 	assert(mdoc->meta.msec);
195020fa2881Sschwarze 
195120fa2881Sschwarze 	switch (sec) {
195220fa2881Sschwarze 	case (SEC_RETURN_VALUES):
195320fa2881Sschwarze 		/* FALLTHROUGH */
195420fa2881Sschwarze 	case (SEC_ERRORS):
195520fa2881Sschwarze 		/* FALLTHROUGH */
195620fa2881Sschwarze 	case (SEC_LIBRARY):
195792c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
1958f73abda9Skristaps 			break;
195992c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
196092c0ca7fSschwarze 			break;
196192c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
196292c0ca7fSschwarze 			break;
196320fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
196420fa2881Sschwarze 		break;
1965f73abda9Skristaps 	default:
1966f73abda9Skristaps 		break;
1967f73abda9Skristaps 	}
1968f73abda9Skristaps 
1969f73abda9Skristaps 	return(1);
1970f73abda9Skristaps }
1971d39b9a9cSschwarze 
197220fa2881Sschwarze static int
1973f6127a73Sschwarze post_ignpar(POST_ARGS)
1974f6127a73Sschwarze {
1975f6127a73Sschwarze 	struct mdoc_node *np;
1976f6127a73Sschwarze 
1977f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
1978f6127a73Sschwarze 		return(1);
1979f6127a73Sschwarze 
1980f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
1981f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1982f6127a73Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1983f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
1984f6127a73Sschwarze 		}
1985f6127a73Sschwarze 
1986f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
1987f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1988f6127a73Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1989f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
1990f6127a73Sschwarze 		}
1991f6127a73Sschwarze 
1992f6127a73Sschwarze 	return(1);
1993f6127a73Sschwarze }
1994f6127a73Sschwarze 
1995f6127a73Sschwarze static int
199620fa2881Sschwarze pre_par(PRE_ARGS)
1997d39b9a9cSschwarze {
1998d39b9a9cSschwarze 
1999d39b9a9cSschwarze 	if (NULL == mdoc->last)
2000d39b9a9cSschwarze 		return(1);
2001f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2002f6127a73Sschwarze 		return(1);
2003d39b9a9cSschwarze 
200420fa2881Sschwarze 	/*
200520fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
200620fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
200720fa2881Sschwarze 	 */
2008d39b9a9cSschwarze 
2009e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2010e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2011e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2012d39b9a9cSschwarze 		return(1);
20138c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2014d39b9a9cSschwarze 		return(1);
20158c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2016d39b9a9cSschwarze 		return(1);
20178c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2018f6127a73Sschwarze 		return(1);
2019d39b9a9cSschwarze 
2020d39b9a9cSschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2021d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2022d39b9a9cSschwarze 	return(1);
2023d39b9a9cSschwarze }
202420fa2881Sschwarze 
202520fa2881Sschwarze static int
2026e0dd4c9cSschwarze post_par(POST_ARGS)
2027e0dd4c9cSschwarze {
2028e0dd4c9cSschwarze 
2029e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2030e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2031e0dd4c9cSschwarze 		return(1);
2032e0dd4c9cSschwarze 
2033e0dd4c9cSschwarze 	if (NULL == mdoc->last->prev) {
2034e0dd4c9cSschwarze 		if (MDOC_Sh != mdoc->last->parent->tok &&
2035e0dd4c9cSschwarze 		    MDOC_Ss != mdoc->last->parent->tok)
2036e0dd4c9cSschwarze 			return(1);
2037e0dd4c9cSschwarze 	} else {
2038e0dd4c9cSschwarze 		if (MDOC_Pp != mdoc->last->prev->tok &&
2039e0dd4c9cSschwarze 		    MDOC_Lp != mdoc->last->prev->tok &&
2040e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
2041e0dd4c9cSschwarze 		     (MDOC_sp != mdoc->last->prev->tok &&
2042e0dd4c9cSschwarze 		      MDOC_br != mdoc->last->prev->tok)))
2043e0dd4c9cSschwarze 			return(1);
2044e0dd4c9cSschwarze 	}
2045e0dd4c9cSschwarze 
2046e0dd4c9cSschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2047e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2048e0dd4c9cSschwarze 	return(1);
2049e0dd4c9cSschwarze }
2050e0dd4c9cSschwarze 
2051e0dd4c9cSschwarze static int
205220fa2881Sschwarze pre_literal(PRE_ARGS)
205320fa2881Sschwarze {
205420fa2881Sschwarze 
205520fa2881Sschwarze 	if (MDOC_BODY != n->type)
205620fa2881Sschwarze 		return(1);
205720fa2881Sschwarze 
205820fa2881Sschwarze 	/*
205920fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
206020fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
206120fa2881Sschwarze 	 */
206220fa2881Sschwarze 
206320fa2881Sschwarze 	switch (n->tok) {
206420fa2881Sschwarze 	case (MDOC_Dl):
206520fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
206620fa2881Sschwarze 		break;
206720fa2881Sschwarze 	case (MDOC_Bd):
20688c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
206920fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
20708c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
207120fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
207220fa2881Sschwarze 		break;
207320fa2881Sschwarze 	default:
207420fa2881Sschwarze 		abort();
207520fa2881Sschwarze 		/* NOTREACHED */
207620fa2881Sschwarze 	}
207720fa2881Sschwarze 
207820fa2881Sschwarze 	return(1);
207920fa2881Sschwarze }
208020fa2881Sschwarze 
208120fa2881Sschwarze static int
208220fa2881Sschwarze post_dd(POST_ARGS)
208320fa2881Sschwarze {
208420fa2881Sschwarze 	char		  buf[DATESIZE];
208520fa2881Sschwarze 	struct mdoc_node *n;
208604e980cbSschwarze 	int		  c;
208720fa2881Sschwarze 
2088b058e777Sschwarze 	if (mdoc->meta.date)
2089b058e777Sschwarze 		free(mdoc->meta.date);
209020fa2881Sschwarze 
2091b058e777Sschwarze 	n = mdoc->last;
2092b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2093a35fc07aSschwarze 		mdoc->meta.date = mandoc_normdate
2094a35fc07aSschwarze 			(mdoc->parse, NULL, n->line, n->pos);
209520fa2881Sschwarze 		return(1);
209620fa2881Sschwarze 	}
209720fa2881Sschwarze 
209804e980cbSschwarze 	buf[0] = '\0';
209904e980cbSschwarze 	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
210004e980cbSschwarze 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
210120fa2881Sschwarze 		return(0);
210204e980cbSschwarze 	}
210320fa2881Sschwarze 
210404e980cbSschwarze 	assert(c);
2105a35fc07aSschwarze 	mdoc->meta.date = mandoc_normdate
2106a35fc07aSschwarze 		(mdoc->parse, buf, n->line, n->pos);
210720fa2881Sschwarze 
210820fa2881Sschwarze 	return(1);
210920fa2881Sschwarze }
211020fa2881Sschwarze 
211120fa2881Sschwarze static int
211220fa2881Sschwarze post_dt(POST_ARGS)
211320fa2881Sschwarze {
211420fa2881Sschwarze 	struct mdoc_node *nn, *n;
211520fa2881Sschwarze 	const char	 *cp;
211620fa2881Sschwarze 	char		 *p;
211720fa2881Sschwarze 
211820fa2881Sschwarze 	n = mdoc->last;
211920fa2881Sschwarze 
212020fa2881Sschwarze 	if (mdoc->meta.title)
212120fa2881Sschwarze 		free(mdoc->meta.title);
212220fa2881Sschwarze 	if (mdoc->meta.vol)
212320fa2881Sschwarze 		free(mdoc->meta.vol);
212420fa2881Sschwarze 	if (mdoc->meta.arch)
212520fa2881Sschwarze 		free(mdoc->meta.arch);
212620fa2881Sschwarze 
212720fa2881Sschwarze 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
212820fa2881Sschwarze 
212920fa2881Sschwarze 	/* First make all characters uppercase. */
213020fa2881Sschwarze 
213120fa2881Sschwarze 	if (NULL != (nn = n->child))
213220fa2881Sschwarze 		for (p = nn->string; *p; p++) {
213304e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
213420fa2881Sschwarze 				continue;
213520fa2881Sschwarze 
213620fa2881Sschwarze 			/*
213720fa2881Sschwarze 			 * FIXME: don't be lazy: have this make all
213820fa2881Sschwarze 			 * characters be uppercase and just warn once.
213920fa2881Sschwarze 			 */
214020fa2881Sschwarze 			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
214120fa2881Sschwarze 			break;
214220fa2881Sschwarze 		}
214320fa2881Sschwarze 
214420fa2881Sschwarze 	/* Handles: `.Dt'
214520fa2881Sschwarze 	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
214620fa2881Sschwarze 	 */
214720fa2881Sschwarze 
214820fa2881Sschwarze 	if (NULL == (nn = n->child)) {
214920fa2881Sschwarze 		/* XXX: make these macro values. */
215020fa2881Sschwarze 		/* FIXME: warn about missing values. */
215120fa2881Sschwarze 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
215220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
215320fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
215420fa2881Sschwarze 		return(1);
215520fa2881Sschwarze 	}
215620fa2881Sschwarze 
215720fa2881Sschwarze 	/* Handles: `.Dt TITLE'
215820fa2881Sschwarze 	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
215920fa2881Sschwarze 	 */
216020fa2881Sschwarze 
216120fa2881Sschwarze 	mdoc->meta.title = mandoc_strdup
216220fa2881Sschwarze 		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
216320fa2881Sschwarze 
216420fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
216520fa2881Sschwarze 		/* FIXME: warn about missing msec. */
216620fa2881Sschwarze 		/* XXX: make this a macro value. */
216720fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
216820fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
216920fa2881Sschwarze 		return(1);
217020fa2881Sschwarze 	}
217120fa2881Sschwarze 
217220fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
217320fa2881Sschwarze 	 *   --> title = TITLE, volume = SEC is msec ?
217420fa2881Sschwarze 	 *           format(msec) : SEC,
217520fa2881Sschwarze 	 *       msec = SEC is msec ? atoi(msec) : 0,
217620fa2881Sschwarze 	 *       arch = NULL
217720fa2881Sschwarze 	 */
217820fa2881Sschwarze 
217988ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
218020fa2881Sschwarze 	if (cp) {
218120fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
218220fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
218320fa2881Sschwarze 	} else {
218420fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
218520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
218620fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
218720fa2881Sschwarze 	}
218820fa2881Sschwarze 
218920fa2881Sschwarze 	if (NULL == (nn = nn->next))
219020fa2881Sschwarze 		return(1);
219120fa2881Sschwarze 
219220fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
219320fa2881Sschwarze 	 *   --> title = TITLE, volume = VOL is vol ?
219420fa2881Sschwarze 	 *       format(VOL) :
219520fa2881Sschwarze 	 *           VOL is arch ? format(arch) :
219620fa2881Sschwarze 	 *               VOL
219720fa2881Sschwarze 	 */
219820fa2881Sschwarze 
219920fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
220020fa2881Sschwarze 	if (cp) {
220120fa2881Sschwarze 		free(mdoc->meta.vol);
220220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
220320fa2881Sschwarze 	} else {
220420fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
220520fa2881Sschwarze 		if (NULL == cp) {
2206*55818fdcSschwarze 			mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
220720fa2881Sschwarze 			free(mdoc->meta.vol);
220820fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
220920fa2881Sschwarze 		} else
221020fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
221120fa2881Sschwarze 	}
221220fa2881Sschwarze 
221320fa2881Sschwarze 	/* Ignore any subsequent parameters... */
221420fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
221520fa2881Sschwarze 
221620fa2881Sschwarze 	return(1);
221720fa2881Sschwarze }
221820fa2881Sschwarze 
221920fa2881Sschwarze static int
222020fa2881Sschwarze post_prol(POST_ARGS)
222120fa2881Sschwarze {
222220fa2881Sschwarze 	/*
222320fa2881Sschwarze 	 * Remove prologue macros from the document after they're
222420fa2881Sschwarze 	 * processed.  The final document uses mdoc_meta for these
222520fa2881Sschwarze 	 * values and discards the originals.
222620fa2881Sschwarze 	 */
222720fa2881Sschwarze 
222820fa2881Sschwarze 	mdoc_node_delete(mdoc, mdoc->last);
222920fa2881Sschwarze 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
223020fa2881Sschwarze 		mdoc->flags |= MDOC_PBODY;
223120fa2881Sschwarze 
223220fa2881Sschwarze 	return(1);
223320fa2881Sschwarze }
223420fa2881Sschwarze 
223520fa2881Sschwarze static int
2236992063deSschwarze post_bx(POST_ARGS)
2237992063deSschwarze {
2238992063deSschwarze 	struct mdoc_node	*n;
2239992063deSschwarze 
2240992063deSschwarze 	/*
2241992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2242992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2243992063deSschwarze 	 * uppercase blindly.
2244992063deSschwarze 	 */
2245992063deSschwarze 
2246992063deSschwarze 	n = mdoc->last->child;
2247992063deSschwarze 	if (n && NULL != (n = n->next))
224819a69263Sschwarze 		*n->string = (char)toupper
224919a69263Sschwarze 			((unsigned char)*n->string);
2250992063deSschwarze 
2251992063deSschwarze 	return(1);
2252992063deSschwarze }
2253992063deSschwarze 
2254992063deSschwarze static int
225520fa2881Sschwarze post_os(POST_ARGS)
225620fa2881Sschwarze {
225720fa2881Sschwarze 	struct mdoc_node *n;
225820fa2881Sschwarze 	char		  buf[BUFSIZ];
225904e980cbSschwarze 	int		  c;
226020fa2881Sschwarze #ifndef OSNAME
226120fa2881Sschwarze 	struct utsname	  utsname;
226220fa2881Sschwarze #endif
226320fa2881Sschwarze 
226420fa2881Sschwarze 	n = mdoc->last;
226520fa2881Sschwarze 
226620fa2881Sschwarze 	/*
2267353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2268353fa9ecSschwarze 	 * The order of precedence is:
2269353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2270353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2271353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2272353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
227320fa2881Sschwarze  	 */
227420fa2881Sschwarze 
227520fa2881Sschwarze 	free(mdoc->meta.os);
227620fa2881Sschwarze 
227704e980cbSschwarze 	buf[0] = '\0';
227804e980cbSschwarze 	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
227904e980cbSschwarze 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
228020fa2881Sschwarze 		return(0);
228104e980cbSschwarze 	}
228204e980cbSschwarze 
228304e980cbSschwarze 	assert(c);
228420fa2881Sschwarze 
228520fa2881Sschwarze 	if ('\0' == buf[0]) {
2286353fa9ecSschwarze 		if (mdoc->defos) {
2287353fa9ecSschwarze 			mdoc->meta.os = mandoc_strdup(mdoc->defos);
2288353fa9ecSschwarze 			return(1);
2289353fa9ecSschwarze 		}
229020fa2881Sschwarze #ifdef OSNAME
229120fa2881Sschwarze 		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
229220fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
229320fa2881Sschwarze 			return(0);
229420fa2881Sschwarze 		}
229520fa2881Sschwarze #else /*!OSNAME */
2296a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
229720fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
229820fa2881Sschwarze                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
229920fa2881Sschwarze                         return(post_prol(mdoc));
230020fa2881Sschwarze                 }
230120fa2881Sschwarze 
230220fa2881Sschwarze 		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
230320fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
230420fa2881Sschwarze 			return(0);
230520fa2881Sschwarze 		}
230620fa2881Sschwarze 		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
230720fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
230820fa2881Sschwarze 			return(0);
230920fa2881Sschwarze 		}
231020fa2881Sschwarze 		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
231120fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
231220fa2881Sschwarze 			return(0);
231320fa2881Sschwarze 		}
231420fa2881Sschwarze #endif /*!OSNAME*/
231520fa2881Sschwarze 	}
231620fa2881Sschwarze 
231720fa2881Sschwarze 	mdoc->meta.os = mandoc_strdup(buf);
231820fa2881Sschwarze 	return(1);
231920fa2881Sschwarze }
232020fa2881Sschwarze 
232120fa2881Sschwarze static int
232220fa2881Sschwarze post_std(POST_ARGS)
232320fa2881Sschwarze {
232420fa2881Sschwarze 	struct mdoc_node *nn, *n;
232520fa2881Sschwarze 
232620fa2881Sschwarze 	n = mdoc->last;
232720fa2881Sschwarze 
232820fa2881Sschwarze 	/*
232920fa2881Sschwarze 	 * Macros accepting `-std' as an argument have the name of the
233020fa2881Sschwarze 	 * current document (`Nm') filled in as the argument if it's not
233120fa2881Sschwarze 	 * provided.
233220fa2881Sschwarze 	 */
233320fa2881Sschwarze 
233420fa2881Sschwarze 	if (n->child)
233520fa2881Sschwarze 		return(1);
233620fa2881Sschwarze 
233720fa2881Sschwarze 	if (NULL == mdoc->meta.name)
233820fa2881Sschwarze 		return(1);
233920fa2881Sschwarze 
234020fa2881Sschwarze 	nn = n;
234120fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
234220fa2881Sschwarze 
234320fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
234420fa2881Sschwarze 		return(0);
234520fa2881Sschwarze 
234620fa2881Sschwarze 	mdoc->last = nn;
234720fa2881Sschwarze 	return(1);
234820fa2881Sschwarze }
234920fa2881Sschwarze 
235004e980cbSschwarze /*
235104e980cbSschwarze  * Concatenate a node, stopping at the first non-text.
235204e980cbSschwarze  * Concatenation is separated by a single whitespace.
235304e980cbSschwarze  * Returns -1 on fatal (string overrun) error, 0 if child nodes were
235404e980cbSschwarze  * encountered, 1 otherwise.
235504e980cbSschwarze  */
235620fa2881Sschwarze static int
235704e980cbSschwarze concat(char *p, const struct mdoc_node *n, size_t sz)
235820fa2881Sschwarze {
235920fa2881Sschwarze 
236004e980cbSschwarze 	for ( ; NULL != n; n = n->next) {
236104e980cbSschwarze 		if (MDOC_TEXT != n->type)
236220fa2881Sschwarze 			return(0);
236304e980cbSschwarze 		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
236404e980cbSschwarze 			return(-1);
236504e980cbSschwarze 		if (strlcat(p, n->string, sz) >= sz)
236604e980cbSschwarze 			return(-1);
236704e980cbSschwarze 		concat(p, n->child, sz);
236820fa2881Sschwarze 	}
236920fa2881Sschwarze 
237020fa2881Sschwarze 	return(1);
237120fa2881Sschwarze }
237220fa2881Sschwarze 
237319a69263Sschwarze static enum mdoc_sec
237419a69263Sschwarze a2sec(const char *p)
237519a69263Sschwarze {
237619a69263Sschwarze 	int		 i;
237719a69263Sschwarze 
237819a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
237919a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
238019a69263Sschwarze 			return((enum mdoc_sec)i);
238119a69263Sschwarze 
238219a69263Sschwarze 	return(SEC_CUSTOM);
238319a69263Sschwarze }
238419a69263Sschwarze 
238519a69263Sschwarze static size_t
238619a69263Sschwarze macro2len(enum mdoct macro)
238719a69263Sschwarze {
238819a69263Sschwarze 
238919a69263Sschwarze 	switch (macro) {
239019a69263Sschwarze 	case(MDOC_Ad):
239119a69263Sschwarze 		return(12);
239219a69263Sschwarze 	case(MDOC_Ao):
239319a69263Sschwarze 		return(12);
239419a69263Sschwarze 	case(MDOC_An):
239519a69263Sschwarze 		return(12);
239619a69263Sschwarze 	case(MDOC_Aq):
239719a69263Sschwarze 		return(12);
239819a69263Sschwarze 	case(MDOC_Ar):
239919a69263Sschwarze 		return(12);
240019a69263Sschwarze 	case(MDOC_Bo):
240119a69263Sschwarze 		return(12);
240219a69263Sschwarze 	case(MDOC_Bq):
240319a69263Sschwarze 		return(12);
240419a69263Sschwarze 	case(MDOC_Cd):
240519a69263Sschwarze 		return(12);
240619a69263Sschwarze 	case(MDOC_Cm):
240719a69263Sschwarze 		return(10);
240819a69263Sschwarze 	case(MDOC_Do):
240919a69263Sschwarze 		return(10);
241019a69263Sschwarze 	case(MDOC_Dq):
241119a69263Sschwarze 		return(12);
241219a69263Sschwarze 	case(MDOC_Dv):
241319a69263Sschwarze 		return(12);
241419a69263Sschwarze 	case(MDOC_Eo):
241519a69263Sschwarze 		return(12);
241619a69263Sschwarze 	case(MDOC_Em):
241719a69263Sschwarze 		return(10);
241819a69263Sschwarze 	case(MDOC_Er):
241919a69263Sschwarze 		return(17);
242019a69263Sschwarze 	case(MDOC_Ev):
242119a69263Sschwarze 		return(15);
242219a69263Sschwarze 	case(MDOC_Fa):
242319a69263Sschwarze 		return(12);
242419a69263Sschwarze 	case(MDOC_Fl):
242519a69263Sschwarze 		return(10);
242619a69263Sschwarze 	case(MDOC_Fo):
242719a69263Sschwarze 		return(16);
242819a69263Sschwarze 	case(MDOC_Fn):
242919a69263Sschwarze 		return(16);
243019a69263Sschwarze 	case(MDOC_Ic):
243119a69263Sschwarze 		return(10);
243219a69263Sschwarze 	case(MDOC_Li):
243319a69263Sschwarze 		return(16);
243419a69263Sschwarze 	case(MDOC_Ms):
243519a69263Sschwarze 		return(6);
243619a69263Sschwarze 	case(MDOC_Nm):
243719a69263Sschwarze 		return(10);
243819a69263Sschwarze 	case(MDOC_No):
243919a69263Sschwarze 		return(12);
244019a69263Sschwarze 	case(MDOC_Oo):
244119a69263Sschwarze 		return(10);
244219a69263Sschwarze 	case(MDOC_Op):
244319a69263Sschwarze 		return(14);
244419a69263Sschwarze 	case(MDOC_Pa):
244519a69263Sschwarze 		return(32);
244619a69263Sschwarze 	case(MDOC_Pf):
244719a69263Sschwarze 		return(12);
244819a69263Sschwarze 	case(MDOC_Po):
244919a69263Sschwarze 		return(12);
245019a69263Sschwarze 	case(MDOC_Pq):
245119a69263Sschwarze 		return(12);
245219a69263Sschwarze 	case(MDOC_Ql):
245319a69263Sschwarze 		return(16);
245419a69263Sschwarze 	case(MDOC_Qo):
245519a69263Sschwarze 		return(12);
245619a69263Sschwarze 	case(MDOC_So):
245719a69263Sschwarze 		return(12);
245819a69263Sschwarze 	case(MDOC_Sq):
245919a69263Sschwarze 		return(12);
246019a69263Sschwarze 	case(MDOC_Sy):
246119a69263Sschwarze 		return(6);
246219a69263Sschwarze 	case(MDOC_Sx):
246319a69263Sschwarze 		return(16);
246419a69263Sschwarze 	case(MDOC_Tn):
246519a69263Sschwarze 		return(10);
246619a69263Sschwarze 	case(MDOC_Va):
246719a69263Sschwarze 		return(12);
246819a69263Sschwarze 	case(MDOC_Vt):
246919a69263Sschwarze 		return(12);
247019a69263Sschwarze 	case(MDOC_Xr):
247119a69263Sschwarze 		return(10);
247219a69263Sschwarze 	default:
247319a69263Sschwarze 		break;
247419a69263Sschwarze 	};
247519a69263Sschwarze 	return(0);
247619a69263Sschwarze }
2477