xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 2a427d60)
1*2a427d60Sschwarze /*	$Id: mdoc_validate.c,v 1.113 2013/10/06 13:27:47 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
422881299Sschwarze  * Copyright (c) 2010, 2011, 2012, 2013 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,
3184e32ec8fSschwarze 	MDOC__C,
31920fa2881Sschwarze 	MDOC__D,
3204e32ec8fSschwarze 	MDOC__O
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
4157ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type,
4167c2be9f8Sschwarze 		enum check_lvl lvl, enum check_ineq ineq, int val)
417f73abda9Skristaps {
4187c2be9f8Sschwarze 	const char	*p;
419bb648afaSschwarze 	enum mandocerr	 t;
4207c2be9f8Sschwarze 
4217ead8a4eSschwarze 	if (mdoc->last->type != type)
4227c2be9f8Sschwarze 		return(1);
4237c2be9f8Sschwarze 
4247c2be9f8Sschwarze 	switch (ineq) {
4257c2be9f8Sschwarze 	case (CHECK_LT):
4267c2be9f8Sschwarze 		p = "less than ";
4277ead8a4eSschwarze 		if (mdoc->last->nchild < val)
4287c2be9f8Sschwarze 			return(1);
4297c2be9f8Sschwarze 		break;
4307c2be9f8Sschwarze 	case (CHECK_GT):
431bb648afaSschwarze 		p = "more than ";
4327ead8a4eSschwarze 		if (mdoc->last->nchild > val)
4337c2be9f8Sschwarze 			return(1);
4347c2be9f8Sschwarze 		break;
4357c2be9f8Sschwarze 	case (CHECK_EQ):
4367c2be9f8Sschwarze 		p = "";
4377ead8a4eSschwarze 		if (val == mdoc->last->nchild)
4387c2be9f8Sschwarze 			return(1);
4397c2be9f8Sschwarze 		break;
44020fa2881Sschwarze 	default:
44120fa2881Sschwarze 		abort();
44220fa2881Sschwarze 		/* NOTREACHED */
4437c2be9f8Sschwarze 	}
4447c2be9f8Sschwarze 
445bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
4467ead8a4eSschwarze 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line, mdoc->last->pos,
4477c2be9f8Sschwarze 			"want %s%d children (have %d)",
4487ead8a4eSschwarze 			p, val, mdoc->last->nchild);
44919a69263Sschwarze 	return(1);
4507c2be9f8Sschwarze }
451f73abda9Skristaps 
4527c2be9f8Sschwarze static int
4537c2be9f8Sschwarze berr_ge1(POST_ARGS)
454f73abda9Skristaps {
455f73abda9Skristaps 
456bb648afaSschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
457f73abda9Skristaps }
458f73abda9Skristaps 
4597c2be9f8Sschwarze static int
4607c2be9f8Sschwarze bwarn_ge1(POST_ARGS)
4617c2be9f8Sschwarze {
4627c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
463f73abda9Skristaps }
464f73abda9Skristaps 
4657c2be9f8Sschwarze static int
4667c2be9f8Sschwarze ewarn_eq0(POST_ARGS)
4677c2be9f8Sschwarze {
4687c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
4697c2be9f8Sschwarze }
4707c2be9f8Sschwarze 
4717c2be9f8Sschwarze static int
472bb648afaSschwarze ewarn_eq1(POST_ARGS)
473bb648afaSschwarze {
474bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
475bb648afaSschwarze }
476bb648afaSschwarze 
477bb648afaSschwarze static int
4787c2be9f8Sschwarze ewarn_ge1(POST_ARGS)
4797c2be9f8Sschwarze {
4807c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
4817c2be9f8Sschwarze }
4827c2be9f8Sschwarze 
4837c2be9f8Sschwarze static int
484bb648afaSschwarze ewarn_le1(POST_ARGS)
4857c2be9f8Sschwarze {
486bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
4877c2be9f8Sschwarze }
4887c2be9f8Sschwarze 
4897c2be9f8Sschwarze static int
4907c2be9f8Sschwarze hwarn_eq0(POST_ARGS)
4917c2be9f8Sschwarze {
4927c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
4937c2be9f8Sschwarze }
4947c2be9f8Sschwarze 
4957c2be9f8Sschwarze static int
4967c2be9f8Sschwarze hwarn_eq1(POST_ARGS)
4977c2be9f8Sschwarze {
4987c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
4997c2be9f8Sschwarze }
5007c2be9f8Sschwarze 
5017c2be9f8Sschwarze static int
502bb648afaSschwarze hwarn_ge1(POST_ARGS)
503bb648afaSschwarze {
504bb648afaSschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
505bb648afaSschwarze }
506bb648afaSschwarze 
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
5147ead8a4eSschwarze check_args(struct mdoc *mdoc, 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++)
5237ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
524f73abda9Skristaps }
525f73abda9Skristaps 
52620fa2881Sschwarze static void
5277ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
528f73abda9Skristaps {
529f73abda9Skristaps 	int		 i;
530f73abda9Skristaps 
531f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
5327ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
533f73abda9Skristaps 
53420fa2881Sschwarze 	/* FIXME: move to post_std(). */
53520fa2881Sschwarze 
53620fa2881Sschwarze 	if (MDOC_Std == v->arg)
5377ead8a4eSschwarze 		if ( ! (v->sz || mdoc->meta.name))
5387ead8a4eSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
539f73abda9Skristaps }
540f73abda9Skristaps 
54120fa2881Sschwarze static void
5427ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
543f73abda9Skristaps {
54404e980cbSschwarze 	char		*cp;
545769ee804Sschwarze 
5467ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
5471cdbf331Sschwarze 		return;
5481cdbf331Sschwarze 
5491cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
5507ead8a4eSschwarze 		mdoc_pmsg(mdoc, 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);
887a4c002ecSschwarze 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
888f73abda9Skristaps }
889f73abda9Skristaps 
890f73abda9Skristaps 
891f73abda9Skristaps static int
892f73abda9Skristaps pre_it(PRE_ARGS)
893f73abda9Skristaps {
894f73abda9Skristaps 
895f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
896f73abda9Skristaps 		return(1);
89720fa2881Sschwarze 
898f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
899f73abda9Skristaps }
900f73abda9Skristaps 
901f73abda9Skristaps 
902f73abda9Skristaps static int
903f73abda9Skristaps pre_an(PRE_ARGS)
904f73abda9Skristaps {
9056475d5b0Sschwarze 	int		 i;
906f73abda9Skristaps 
907769ee804Sschwarze 	if (NULL == n->args)
908f73abda9Skristaps 		return(1);
909769ee804Sschwarze 
9106475d5b0Sschwarze 	for (i = 1; i < (int)n->args->argc; i++)
91120fa2881Sschwarze 		mdoc_pmsg(mdoc, n->args->argv[i].line,
91220fa2881Sschwarze 			n->args->argv[i].pos, MANDOCERR_IGNARGV);
9137c2be9f8Sschwarze 
914769ee804Sschwarze 	if (MDOC_Split == n->args->argv[0].arg)
9158c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
916769ee804Sschwarze 	else if (MDOC_Nosplit == n->args->argv[0].arg)
9178c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
918769ee804Sschwarze 	else
919769ee804Sschwarze 		abort();
920769ee804Sschwarze 
921769ee804Sschwarze 	return(1);
922f73abda9Skristaps }
923f73abda9Skristaps 
924f73abda9Skristaps static int
92520fa2881Sschwarze pre_std(PRE_ARGS)
926f73abda9Skristaps {
927f73abda9Skristaps 
92820fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
92920fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
93020fa2881Sschwarze 			return(1);
931f73abda9Skristaps 
93220fa2881Sschwarze 	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
9336093755cSschwarze 	return(1);
9346093755cSschwarze }
9356093755cSschwarze 
9366093755cSschwarze static int
937f73abda9Skristaps pre_dt(PRE_ARGS)
938f73abda9Skristaps {
939f73abda9Skristaps 
940b058e777Sschwarze 	if (NULL == mdoc->meta.date || mdoc->meta.os)
94120fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
94220fa2881Sschwarze 
943f73abda9Skristaps 	if (mdoc->meta.title)
94420fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
94520fa2881Sschwarze 
946f73abda9Skristaps 	return(1);
947f73abda9Skristaps }
948f73abda9Skristaps 
949f73abda9Skristaps static int
950f73abda9Skristaps pre_os(PRE_ARGS)
951f73abda9Skristaps {
952f73abda9Skristaps 
953b058e777Sschwarze 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
95420fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
95520fa2881Sschwarze 
956f73abda9Skristaps 	if (mdoc->meta.os)
95720fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
95820fa2881Sschwarze 
959f73abda9Skristaps 	return(1);
960f73abda9Skristaps }
961f73abda9Skristaps 
962f73abda9Skristaps static int
963f73abda9Skristaps pre_dd(PRE_ARGS)
964f73abda9Skristaps {
965f73abda9Skristaps 
966f73abda9Skristaps 	if (mdoc->meta.title || mdoc->meta.os)
96720fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
96820fa2881Sschwarze 
969f73abda9Skristaps 	if (mdoc->meta.date)
97020fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
97120fa2881Sschwarze 
972f73abda9Skristaps 	return(1);
973f73abda9Skristaps }
974f73abda9Skristaps 
975f73abda9Skristaps 
976f73abda9Skristaps static int
977f73abda9Skristaps post_bf(POST_ARGS)
978f73abda9Skristaps {
979769ee804Sschwarze 	struct mdoc_node *np;
980ddce0b0cSschwarze 	enum mdocargt	  arg;
981f73abda9Skristaps 
982769ee804Sschwarze 	/*
983769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
984769ee804Sschwarze 	 * element, which contains the goods.
985769ee804Sschwarze 	 */
986769ee804Sschwarze 
987769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
988769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
989769ee804Sschwarze 			assert(mdoc->last->pending);
990769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
991769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
992769ee804Sschwarze 			np = mdoc->last->parent->head;
993769ee804Sschwarze 		} else
994769ee804Sschwarze 			np = mdoc->last->head;
995769ee804Sschwarze 
996769ee804Sschwarze 		assert(np);
997769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
998769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
999f73abda9Skristaps 		return(1);
10006e03d529Sschwarze 	}
1001f73abda9Skristaps 
1002769ee804Sschwarze 	np = mdoc->last;
1003769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
1004769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
100550d41253Sschwarze 
1006769ee804Sschwarze 	/*
1007769ee804Sschwarze 	 * Cannot have both argument and parameter.
1008769ee804Sschwarze 	 * If neither is specified, let it through with a warning.
1009769ee804Sschwarze 	 */
1010f73abda9Skristaps 
1011769ee804Sschwarze 	if (np->parent->args && np->child) {
1012769ee804Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
10136e03d529Sschwarze 		return(0);
101420fa2881Sschwarze 	} else if (NULL == np->parent->args && NULL == np->child) {
101520fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
101620fa2881Sschwarze 		return(1);
101720fa2881Sschwarze 	}
1018769ee804Sschwarze 
1019769ee804Sschwarze 	/* Extract argument into data. */
1020769ee804Sschwarze 
1021769ee804Sschwarze 	if (np->parent->args) {
1022769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
1023769ee804Sschwarze 		if (MDOC_Emphasis == arg)
10248c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
1025769ee804Sschwarze 		else if (MDOC_Literal == arg)
10268c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
1027769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
10288c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
1029769ee804Sschwarze 		else
1030769ee804Sschwarze 			abort();
1031769ee804Sschwarze 		return(1);
1032769ee804Sschwarze 	}
1033769ee804Sschwarze 
1034769ee804Sschwarze 	/* Extract parameter into data. */
1035769ee804Sschwarze 
1036769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
10378c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
1038769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
10398c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
1040769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
10418c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
104220fa2881Sschwarze 	else
104320fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1044769ee804Sschwarze 
1045769ee804Sschwarze 	return(1);
1046f73abda9Skristaps }
1047f73abda9Skristaps 
1048f73abda9Skristaps static int
104971719887Sschwarze post_lb(POST_ARGS)
105071719887Sschwarze {
105120fa2881Sschwarze 	const char	*p;
105220fa2881Sschwarze 	char		*buf;
105320fa2881Sschwarze 	size_t		 sz;
105471719887Sschwarze 
1055bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1056bb648afaSschwarze 
105720fa2881Sschwarze 	assert(mdoc->last->child);
105820fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
105920fa2881Sschwarze 
106020fa2881Sschwarze 	p = mdoc_a2lib(mdoc->last->child->string);
106120fa2881Sschwarze 
106220fa2881Sschwarze 	/* If lookup ok, replace with table value. */
106320fa2881Sschwarze 
106420fa2881Sschwarze 	if (p) {
106520fa2881Sschwarze 		free(mdoc->last->child->string);
106620fa2881Sschwarze 		mdoc->last->child->string = mandoc_strdup(p);
106771719887Sschwarze 		return(1);
106871719887Sschwarze 	}
106971719887Sschwarze 
107020fa2881Sschwarze 	/* If not, use "library ``xxxx''. */
107120fa2881Sschwarze 
107220fa2881Sschwarze 	sz = strlen(mdoc->last->child->string) +
107320fa2881Sschwarze 		2 + strlen("\\(lqlibrary\\(rq");
107420fa2881Sschwarze 	buf = mandoc_malloc(sz);
107520fa2881Sschwarze 	snprintf(buf, sz, "library \\(lq%s\\(rq",
107620fa2881Sschwarze 			mdoc->last->child->string);
107720fa2881Sschwarze 	free(mdoc->last->child->string);
107820fa2881Sschwarze 	mdoc->last->child->string = buf;
107920fa2881Sschwarze 	return(1);
108020fa2881Sschwarze }
108171719887Sschwarze 
108271719887Sschwarze static int
1083b31af00dSschwarze post_eoln(POST_ARGS)
1084b31af00dSschwarze {
1085b31af00dSschwarze 
108620fa2881Sschwarze 	if (mdoc->last->child)
108720fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1088b31af00dSschwarze 	return(1);
1089b31af00dSschwarze }
1090b31af00dSschwarze 
1091b31af00dSschwarze 
1092b31af00dSschwarze static int
10938521b0bcSschwarze post_vt(POST_ARGS)
10948521b0bcSschwarze {
10958521b0bcSschwarze 	const struct mdoc_node *n;
10968521b0bcSschwarze 
10978521b0bcSschwarze 	/*
10988521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
10998521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1100e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1101e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
11028521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
11038521b0bcSschwarze 	 */
11048521b0bcSschwarze 
11058521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
11068521b0bcSschwarze 		return(1);
11078521b0bcSschwarze 
11088521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1109bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
111020fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
11118521b0bcSschwarze 
11128521b0bcSschwarze 	return(1);
11138521b0bcSschwarze }
11148521b0bcSschwarze 
11158521b0bcSschwarze 
11168521b0bcSschwarze static int
1117f73abda9Skristaps post_nm(POST_ARGS)
1118f73abda9Skristaps {
111920fa2881Sschwarze 	char		 buf[BUFSIZ];
112004e980cbSschwarze 	int		 c;
112120fa2881Sschwarze 
1122160ac481Sschwarze 	if (NULL != mdoc->meta.name)
112320fa2881Sschwarze 		return(1);
112420fa2881Sschwarze 
1125160ac481Sschwarze 	/* Try to use our children for setting the meta name. */
112620fa2881Sschwarze 
1127160ac481Sschwarze 	if (NULL != mdoc->last->child) {
112804e980cbSschwarze 		buf[0] = '\0';
1129160ac481Sschwarze 		c = concat(buf, mdoc->last->child, BUFSIZ);
1130160ac481Sschwarze 	} else
1131160ac481Sschwarze 		c = 0;
1132160ac481Sschwarze 
1133160ac481Sschwarze 	switch (c) {
1134160ac481Sschwarze 	case (-1):
113504e980cbSschwarze 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
113620fa2881Sschwarze 		return(0);
1137160ac481Sschwarze 	case (0):
1138160ac481Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1139160ac481Sschwarze 		mdoc->meta.name = mandoc_strdup("UNKNOWN");
1140160ac481Sschwarze 		break;
1141160ac481Sschwarze 	default:
114220fa2881Sschwarze 		mdoc->meta.name = mandoc_strdup(buf);
1143160ac481Sschwarze 		break;
1144160ac481Sschwarze 	}
114520fa2881Sschwarze 	return(1);
114620fa2881Sschwarze }
114720fa2881Sschwarze 
114820fa2881Sschwarze static int
114920fa2881Sschwarze post_literal(POST_ARGS)
115020fa2881Sschwarze {
115120fa2881Sschwarze 
115220fa2881Sschwarze 	/*
115320fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
115420fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
115520fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
115620fa2881Sschwarze 	 * off in general since displays can't be nested.
115720fa2881Sschwarze 	 */
115820fa2881Sschwarze 
115920fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
116020fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
116120fa2881Sschwarze 
116220fa2881Sschwarze 	return(1);
116320fa2881Sschwarze }
116420fa2881Sschwarze 
116520fa2881Sschwarze static int
116620fa2881Sschwarze post_defaults(POST_ARGS)
116720fa2881Sschwarze {
116820fa2881Sschwarze 	struct mdoc_node *nn;
116920fa2881Sschwarze 
117020fa2881Sschwarze 	/*
117120fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
117220fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
117320fa2881Sschwarze 	 * gets an empty string.
117420fa2881Sschwarze 	 */
1175f73abda9Skristaps 
1176f73abda9Skristaps 	if (mdoc->last->child)
1177f73abda9Skristaps 		return(1);
117820fa2881Sschwarze 
117920fa2881Sschwarze 	nn = mdoc->last;
118020fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
118120fa2881Sschwarze 
118220fa2881Sschwarze 	switch (nn->tok) {
118320fa2881Sschwarze 	case (MDOC_Ar):
118420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
118520fa2881Sschwarze 			return(0);
118620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
118720fa2881Sschwarze 			return(0);
118820fa2881Sschwarze 		break;
118920fa2881Sschwarze 	case (MDOC_At):
119020fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
119120fa2881Sschwarze 			return(0);
119220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
119320fa2881Sschwarze 			return(0);
119420fa2881Sschwarze 		break;
119520fa2881Sschwarze 	case (MDOC_Li):
119620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
119720fa2881Sschwarze 			return(0);
119820fa2881Sschwarze 		break;
119920fa2881Sschwarze 	case (MDOC_Pa):
120020fa2881Sschwarze 		/* FALLTHROUGH */
120120fa2881Sschwarze 	case (MDOC_Mt):
120220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
120320fa2881Sschwarze 			return(0);
120420fa2881Sschwarze 		break;
120520fa2881Sschwarze 	default:
120620fa2881Sschwarze 		abort();
120720fa2881Sschwarze 		/* NOTREACHED */
1208f73abda9Skristaps 	}
1209f73abda9Skristaps 
121020fa2881Sschwarze 	mdoc->last = nn;
121120fa2881Sschwarze 	return(1);
121220fa2881Sschwarze }
1213f73abda9Skristaps 
1214f73abda9Skristaps static int
1215f73abda9Skristaps post_at(POST_ARGS)
1216f73abda9Skristaps {
121720fa2881Sschwarze 	const char	 *p, *q;
121820fa2881Sschwarze 	char		 *buf;
121920fa2881Sschwarze 	size_t		  sz;
122020fa2881Sschwarze 
122120fa2881Sschwarze 	/*
122220fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
122320fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
122420fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
122520fa2881Sschwarze 	 */
1226f73abda9Skristaps 
1227f73abda9Skristaps 	if (NULL == mdoc->last->child)
1228f73abda9Skristaps 		return(1);
122920fa2881Sschwarze 
12306e03d529Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
123120fa2881Sschwarze 	p = mdoc_a2att(mdoc->last->child->string);
123220fa2881Sschwarze 
123320fa2881Sschwarze 	if (p) {
123420fa2881Sschwarze 		free(mdoc->last->child->string);
123520fa2881Sschwarze 		mdoc->last->child->string = mandoc_strdup(p);
123620fa2881Sschwarze 	} else {
123720fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
123820fa2881Sschwarze 		p = "AT&T UNIX ";
123920fa2881Sschwarze 		q = mdoc->last->child->string;
124020fa2881Sschwarze 		sz = strlen(p) + strlen(q) + 1;
124120fa2881Sschwarze 		buf = mandoc_malloc(sz);
124220fa2881Sschwarze 		strlcpy(buf, p, sz);
124320fa2881Sschwarze 		strlcat(buf, q, sz);
124420fa2881Sschwarze 		free(mdoc->last->child->string);
124520fa2881Sschwarze 		mdoc->last->child->string = buf;
1246f73abda9Skristaps 	}
1247f73abda9Skristaps 
124820fa2881Sschwarze 	return(1);
124920fa2881Sschwarze }
1250f73abda9Skristaps 
1251f73abda9Skristaps static int
1252f73abda9Skristaps post_an(POST_ARGS)
1253f73abda9Skristaps {
1254769ee804Sschwarze 	struct mdoc_node *np;
1255f73abda9Skristaps 
1256769ee804Sschwarze 	np = mdoc->last;
1257e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1258e7a93ef3Sschwarze 		if (0 == np->child)
1259e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1260e7a93ef3Sschwarze 	} else if (np->child)
1261bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
126220fa2881Sschwarze 
126320fa2881Sschwarze 	return(1);
1264f73abda9Skristaps }
1265f73abda9Skristaps 
1266f73abda9Skristaps 
1267f73abda9Skristaps static int
1268f73abda9Skristaps post_it(POST_ARGS)
1269f73abda9Skristaps {
127019a69263Sschwarze 	int		  i, cols;
12716093755cSschwarze 	enum mdoc_list	  lt;
1272f73abda9Skristaps 	struct mdoc_node *n, *c;
12736093755cSschwarze 	enum mandocerr	  er;
1274f73abda9Skristaps 
1275f73abda9Skristaps 	if (MDOC_BLOCK != mdoc->last->type)
1276f73abda9Skristaps 		return(1);
1277f73abda9Skristaps 
1278f73abda9Skristaps 	n = mdoc->last->parent->parent;
12798c62fbf5Sschwarze 	lt = n->norm->Bl.type;
12806093755cSschwarze 
12816093755cSschwarze 	if (LIST__NONE == lt) {
12826e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
128320fa2881Sschwarze 		return(1);
12846e03d529Sschwarze 	}
1285f73abda9Skristaps 
12866093755cSschwarze 	switch (lt) {
12876093755cSschwarze 	case (LIST_tag):
12886093755cSschwarze 		if (mdoc->last->head->child)
1289f73abda9Skristaps 			break;
12906093755cSschwarze 		/* FIXME: give this a dummy value. */
129120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1292f73abda9Skristaps 		break;
12936093755cSschwarze 	case (LIST_hang):
1294f73abda9Skristaps 		/* FALLTHROUGH */
12956093755cSschwarze 	case (LIST_ohang):
1296f73abda9Skristaps 		/* FALLTHROUGH */
12976093755cSschwarze 	case (LIST_inset):
1298f73abda9Skristaps 		/* FALLTHROUGH */
12996093755cSschwarze 	case (LIST_diag):
1300f73abda9Skristaps 		if (NULL == mdoc->last->head->child)
130120fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1302f73abda9Skristaps 		break;
13036093755cSschwarze 	case (LIST_bullet):
1304f73abda9Skristaps 		/* FALLTHROUGH */
13056093755cSschwarze 	case (LIST_dash):
1306f73abda9Skristaps 		/* FALLTHROUGH */
13076093755cSschwarze 	case (LIST_enum):
1308f73abda9Skristaps 		/* FALLTHROUGH */
13096093755cSschwarze 	case (LIST_hyphen):
13109f373962Sschwarze 		if (NULL == mdoc->last->body->child)
131120fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1312f73abda9Skristaps 		/* FALLTHROUGH */
13136093755cSschwarze 	case (LIST_item):
1314f73abda9Skristaps 		if (mdoc->last->head->child)
131520fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1316f73abda9Skristaps 		break;
13176093755cSschwarze 	case (LIST_column):
13188c62fbf5Sschwarze 		cols = (int)n->norm->Bl.ncols;
13196093755cSschwarze 
13206093755cSschwarze 		assert(NULL == mdoc->last->head->child);
13216093755cSschwarze 
13226093755cSschwarze 		if (NULL == mdoc->last->body->child)
132320fa2881Sschwarze 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
13246093755cSschwarze 
13256093755cSschwarze 		for (i = 0, c = mdoc->last->child; c; c = c->next)
13266093755cSschwarze 			if (MDOC_BODY == c->type)
1327f73abda9Skristaps 				i++;
132853292e81Sschwarze 
13296093755cSschwarze 		if (i < cols)
13306093755cSschwarze 			er = MANDOCERR_ARGCOUNT;
13316093755cSschwarze 		else if (i == cols || i == cols + 1)
1332f73abda9Skristaps 			break;
13336093755cSschwarze 		else
13346093755cSschwarze 			er = MANDOCERR_SYNTARGCOUNT;
133553292e81Sschwarze 
1336a35fc07aSschwarze 		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1337a35fc07aSschwarze 				mdoc->last->pos,
13386e03d529Sschwarze 				"columns == %d (have %d)", cols, i);
133919a69263Sschwarze 		return(MANDOCERR_ARGCOUNT == er);
1340f73abda9Skristaps 	default:
1341f73abda9Skristaps 		break;
1342f73abda9Skristaps 	}
1343f73abda9Skristaps 
1344f73abda9Skristaps 	return(1);
1345f73abda9Skristaps }
1346f73abda9Skristaps 
134720fa2881Sschwarze static int
134820fa2881Sschwarze post_bl_block(POST_ARGS)
134920fa2881Sschwarze {
1350bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
135120fa2881Sschwarze 
135220fa2881Sschwarze 	/*
135320fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
135420fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
135520fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
135620fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
135720fa2881Sschwarze 	 * rewritten into real lengths).
135820fa2881Sschwarze 	 */
135920fa2881Sschwarze 
136020fa2881Sschwarze 	n = mdoc->last;
136120fa2881Sschwarze 
13628c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
13638c62fbf5Sschwarze 			NULL == n->norm->Bl.width) {
136420fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
136520fa2881Sschwarze 			return(0);
1366bb99f0faSschwarze 		assert(n->norm->Bl.width);
13678c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
136820fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
136920fa2881Sschwarze 			return(0);
13708c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1371bb99f0faSschwarze 	}
1372bb99f0faSschwarze 
1373bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1374bb99f0faSschwarze 		if (NULL == ni->body)
1375bb99f0faSschwarze 			continue;
1376bb99f0faSschwarze 		nc = ni->body->last;
1377bb99f0faSschwarze 		while (NULL != nc) {
1378bb99f0faSschwarze 			switch (nc->tok) {
1379bb99f0faSschwarze 			case (MDOC_Pp):
1380bb99f0faSschwarze 				/* FALLTHROUGH */
1381bb99f0faSschwarze 			case (MDOC_Lp):
1382bb99f0faSschwarze 				/* FALLTHROUGH */
1383bb99f0faSschwarze 			case (MDOC_br):
1384bb99f0faSschwarze 				break;
1385bb99f0faSschwarze 			default:
1386bb99f0faSschwarze 				nc = NULL;
1387bb99f0faSschwarze 				continue;
1388bb99f0faSschwarze 			}
1389bb99f0faSschwarze 			if (NULL == ni->next) {
1390bb99f0faSschwarze 				mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1391bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1392bb99f0faSschwarze 					return(0);
1393bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1394bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
1395bb99f0faSschwarze 				mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1396bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1397bb99f0faSschwarze 			} else
1398bb99f0faSschwarze 				break;
1399bb99f0faSschwarze 			nc = ni->body->last;
1400bb99f0faSschwarze 		}
1401bb99f0faSschwarze 	}
140220fa2881Sschwarze 	return(1);
140320fa2881Sschwarze }
140420fa2881Sschwarze 
140520fa2881Sschwarze static int
140620fa2881Sschwarze post_bl_block_width(POST_ARGS)
140720fa2881Sschwarze {
140820fa2881Sschwarze 	size_t		  width;
140920fa2881Sschwarze 	int		  i;
141020fa2881Sschwarze 	enum mdoct	  tok;
141120fa2881Sschwarze 	struct mdoc_node *n;
141220fa2881Sschwarze 	char		  buf[NUMSIZ];
141320fa2881Sschwarze 
141420fa2881Sschwarze 	n = mdoc->last;
141520fa2881Sschwarze 
141620fa2881Sschwarze 	/*
141720fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
141820fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
141920fa2881Sschwarze 	 * literal string, or a scaling width.
142020fa2881Sschwarze 	 *
142120fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
142220fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
142320fa2881Sschwarze 	 */
142420fa2881Sschwarze 
14258c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
142620fa2881Sschwarze 		width = 6;
14278c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
142820fa2881Sschwarze 		return(1);
142919a69263Sschwarze 	else if (0 == (width = macro2len(tok)))  {
143020fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
143120fa2881Sschwarze 		return(1);
143220fa2881Sschwarze 	}
143320fa2881Sschwarze 
143420fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
143520fa2881Sschwarze 
143620fa2881Sschwarze 	assert(n->args);
143720fa2881Sschwarze 
143820fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
143920fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
144020fa2881Sschwarze 			break;
144120fa2881Sschwarze 
144220fa2881Sschwarze 	assert(i < (int)n->args->argc);
144320fa2881Sschwarze 
144404e980cbSschwarze 	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
144520fa2881Sschwarze 	free(n->args->argv[i].value[0]);
144620fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
144720fa2881Sschwarze 
144820fa2881Sschwarze 	/* Set our width! */
14498c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
145020fa2881Sschwarze 	return(1);
145120fa2881Sschwarze }
145220fa2881Sschwarze 
145320fa2881Sschwarze static int
145420fa2881Sschwarze post_bl_block_tag(POST_ARGS)
145520fa2881Sschwarze {
145620fa2881Sschwarze 	struct mdoc_node *n, *nn;
145720fa2881Sschwarze 	size_t		  sz, ssz;
145820fa2881Sschwarze 	int		  i;
145920fa2881Sschwarze 	char		  buf[NUMSIZ];
146020fa2881Sschwarze 
146120fa2881Sschwarze 	/*
146220fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
146320fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
146420fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
146520fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
146620fa2881Sschwarze 	 */
146720fa2881Sschwarze 
146820fa2881Sschwarze 	sz = 10;
146920fa2881Sschwarze 	n = mdoc->last;
147020fa2881Sschwarze 
147120fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
147220fa2881Sschwarze 		if (MDOC_It != nn->tok)
147320fa2881Sschwarze 			continue;
147420fa2881Sschwarze 
147520fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
147620fa2881Sschwarze 		nn = nn->head->child;
147720fa2881Sschwarze 
147820fa2881Sschwarze 		if (nn == NULL)
147920fa2881Sschwarze 			break;
148020fa2881Sschwarze 
148120fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
148220fa2881Sschwarze 			sz = strlen(nn->string) + 1;
148320fa2881Sschwarze 			break;
148420fa2881Sschwarze 		}
148520fa2881Sschwarze 
148619a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
148720fa2881Sschwarze 			sz = ssz;
148820fa2881Sschwarze 
148920fa2881Sschwarze 		break;
149020fa2881Sschwarze 	}
149120fa2881Sschwarze 
149220fa2881Sschwarze 	/* Defaults to ten ens. */
149320fa2881Sschwarze 
149404e980cbSschwarze 	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
149520fa2881Sschwarze 
149620fa2881Sschwarze 	/*
149720fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
149820fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
149920fa2881Sschwarze 	 */
150020fa2881Sschwarze 
150120fa2881Sschwarze 	assert(n->args);
150220fa2881Sschwarze 	i = (int)(n->args->argc)++;
150320fa2881Sschwarze 
150420fa2881Sschwarze 	n->args->argv = mandoc_realloc(n->args->argv,
150520fa2881Sschwarze 			n->args->argc * sizeof(struct mdoc_argv));
150620fa2881Sschwarze 
150720fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
150820fa2881Sschwarze 	n->args->argv[i].line = n->line;
150920fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
151020fa2881Sschwarze 	n->args->argv[i].sz = 1;
151120fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
151220fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
151320fa2881Sschwarze 
151420fa2881Sschwarze 	/* Set our width! */
15158c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
151620fa2881Sschwarze 	return(1);
151720fa2881Sschwarze }
151820fa2881Sschwarze 
1519f73abda9Skristaps 
1520f73abda9Skristaps static int
1521395185ccSschwarze post_bl_head(POST_ARGS)
1522395185ccSschwarze {
152320fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
152420fa2881Sschwarze 	int		  i, j;
1525395185ccSschwarze 
15268c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
152720fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
152820fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1529395185ccSschwarze 
153020fa2881Sschwarze 	/*
153120fa2881Sschwarze 	 * Convert old-style lists, where the column width specifiers
153220fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
153320fa2881Sschwarze 	 * lists where they're argument values following -column.
153420fa2881Sschwarze 	 */
153520fa2881Sschwarze 
153620fa2881Sschwarze 	/* First, disallow both types and allow normal-form. */
153720fa2881Sschwarze 
153820fa2881Sschwarze 	/*
153920fa2881Sschwarze 	 * TODO: technically, we can accept both and just merge the two
154020fa2881Sschwarze 	 * lists, but I'll leave that for another day.
154120fa2881Sschwarze 	 */
154220fa2881Sschwarze 
15438c62fbf5Sschwarze 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
154420fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
15456093755cSschwarze 		return(0);
154620fa2881Sschwarze 	} else if (NULL == mdoc->last->child)
154720fa2881Sschwarze 		return(1);
154820fa2881Sschwarze 
154920fa2881Sschwarze 	np = mdoc->last->parent;
155020fa2881Sschwarze 	assert(np->args);
155120fa2881Sschwarze 
155220fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
155320fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
155420fa2881Sschwarze 			break;
155520fa2881Sschwarze 
155620fa2881Sschwarze 	assert(j < (int)np->args->argc);
155720fa2881Sschwarze 	assert(0 == np->args->argv[j].sz);
155820fa2881Sschwarze 
155920fa2881Sschwarze 	/*
1560a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
156120fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
156220fa2881Sschwarze 	 * column field.  Then, delete the head children.
156320fa2881Sschwarze 	 */
156420fa2881Sschwarze 
156520fa2881Sschwarze 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
156620fa2881Sschwarze 	np->args->argv[j].value = mandoc_malloc
156720fa2881Sschwarze 		((size_t)mdoc->last->nchild * sizeof(char *));
156820fa2881Sschwarze 
15698c62fbf5Sschwarze 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
157004e980cbSschwarze 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
157120fa2881Sschwarze 
157220fa2881Sschwarze 	for (i = 0, nn = mdoc->last->child; nn; i++) {
157320fa2881Sschwarze 		np->args->argv[j].value[i] = nn->string;
157420fa2881Sschwarze 		nn->string = NULL;
157520fa2881Sschwarze 		nnp = nn;
157620fa2881Sschwarze 		nn = nn->next;
157720fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1578395185ccSschwarze 	}
157920fa2881Sschwarze 
158020fa2881Sschwarze 	mdoc->last->nchild = 0;
158120fa2881Sschwarze 	mdoc->last->child = NULL;
158220fa2881Sschwarze 
15836093755cSschwarze 	return(1);
1584b16e7ddfSschwarze }
1585b16e7ddfSschwarze 
1586395185ccSschwarze static int
1587f73abda9Skristaps post_bl(POST_ARGS)
1588f73abda9Skristaps {
1589*2a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
1590*2a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
1591*2a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1592f73abda9Skristaps 
1593*2a427d60Sschwarze 	nbody = mdoc->last;
1594*2a427d60Sschwarze 	switch (nbody->type) {
1595*2a427d60Sschwarze 	case (MDOC_BLOCK):
159620fa2881Sschwarze 		return(post_bl_block(mdoc));
1597*2a427d60Sschwarze 	case (MDOC_HEAD):
1598*2a427d60Sschwarze 		return(post_bl_head(mdoc));
1599*2a427d60Sschwarze 	case (MDOC_BODY):
1600f6127a73Sschwarze 		break;
1601*2a427d60Sschwarze 	default:
1602*2a427d60Sschwarze 		return(1);
1603f6127a73Sschwarze 	}
1604f6127a73Sschwarze 
1605*2a427d60Sschwarze 	nchild = nbody->child;
1606*2a427d60Sschwarze 	while (NULL != nchild) {
1607*2a427d60Sschwarze 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
1608*2a427d60Sschwarze 			nchild = nchild->next;
1609*2a427d60Sschwarze 			continue;
1610*2a427d60Sschwarze 		}
1611*2a427d60Sschwarze 
1612*2a427d60Sschwarze 		mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
1613*2a427d60Sschwarze 
1614*2a427d60Sschwarze 		/*
1615*2a427d60Sschwarze 		 * Move the node out of the Bl block.
1616*2a427d60Sschwarze 		 * First, collect all required node pointers.
1617*2a427d60Sschwarze 		 */
1618*2a427d60Sschwarze 
1619*2a427d60Sschwarze 		nblock  = nbody->parent;
1620*2a427d60Sschwarze 		nprev   = nblock->prev;
1621*2a427d60Sschwarze 		nparent = nblock->parent;
1622*2a427d60Sschwarze 		nnext   = nchild->next;
1623*2a427d60Sschwarze 
1624*2a427d60Sschwarze 		/*
1625*2a427d60Sschwarze 		 * Unlink this child.
1626*2a427d60Sschwarze 		 */
1627*2a427d60Sschwarze 
1628*2a427d60Sschwarze 		assert(NULL == nchild->prev);
1629*2a427d60Sschwarze 		if (0 == --nbody->nchild) {
1630*2a427d60Sschwarze 			nbody->child = NULL;
1631*2a427d60Sschwarze 			nbody->last  = NULL;
1632*2a427d60Sschwarze 			assert(NULL == nnext);
1633*2a427d60Sschwarze 		} else {
1634*2a427d60Sschwarze 			nbody->child = nnext;
1635*2a427d60Sschwarze 			nnext->prev = NULL;
1636*2a427d60Sschwarze 		}
1637*2a427d60Sschwarze 
1638*2a427d60Sschwarze 		/*
1639*2a427d60Sschwarze 		 * Relink this child.
1640*2a427d60Sschwarze 		 */
1641*2a427d60Sschwarze 
1642*2a427d60Sschwarze 		nchild->parent = nparent;
1643*2a427d60Sschwarze 		nchild->prev   = nprev;
1644*2a427d60Sschwarze 		nchild->next   = nblock;
1645*2a427d60Sschwarze 
1646*2a427d60Sschwarze 		nblock->prev = nchild;
1647*2a427d60Sschwarze 		nparent->nchild++;
1648*2a427d60Sschwarze 		if (NULL == nprev)
1649*2a427d60Sschwarze 			nparent->child = nchild;
1650*2a427d60Sschwarze 		else
1651*2a427d60Sschwarze 			nprev->next = nchild;
1652*2a427d60Sschwarze 
1653*2a427d60Sschwarze 		nchild = nnext;
1654f73abda9Skristaps 	}
1655f73abda9Skristaps 
1656f73abda9Skristaps 	return(1);
1657f73abda9Skristaps }
1658f73abda9Skristaps 
1659f73abda9Skristaps static int
1660f73abda9Skristaps ebool(struct mdoc *mdoc)
1661f73abda9Skristaps {
1662f73abda9Skristaps 
1663bb648afaSschwarze 	if (NULL == mdoc->last->child) {
1664bb648afaSschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1665bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1666f73abda9Skristaps 		return(1);
1667bb648afaSschwarze 	}
1668bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1669f73abda9Skristaps 
167020fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
167120fa2881Sschwarze 
167220fa2881Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "on"))
167320fa2881Sschwarze 		return(1);
167420fa2881Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "off"))
167520fa2881Sschwarze 		return(1);
167620fa2881Sschwarze 
167720fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
167820fa2881Sschwarze 	return(1);
167920fa2881Sschwarze }
1680f73abda9Skristaps 
1681f73abda9Skristaps static int
1682f73abda9Skristaps post_root(POST_ARGS)
1683f73abda9Skristaps {
168420fa2881Sschwarze 	int		  erc;
168520fa2881Sschwarze 	struct mdoc_node *n;
1686f73abda9Skristaps 
168720fa2881Sschwarze 	erc = 0;
168820fa2881Sschwarze 
168920fa2881Sschwarze 	/* Check that we have a finished prologue. */
169020fa2881Sschwarze 
169120fa2881Sschwarze 	if ( ! (MDOC_PBODY & mdoc->flags)) {
169220fa2881Sschwarze 		erc++;
16936e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1694f73abda9Skristaps 	}
1695f73abda9Skristaps 
169620fa2881Sschwarze 	n = mdoc->first;
169720fa2881Sschwarze 	assert(n);
169820fa2881Sschwarze 
169920fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
170020fa2881Sschwarze 
170120fa2881Sschwarze 	if (NULL == n->child) {
170220fa2881Sschwarze 		erc++;
170320fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
170420fa2881Sschwarze 	} else if (MDOC_BLOCK != n->child->type ||
170520fa2881Sschwarze 			MDOC_Sh != n->child->tok) {
170620fa2881Sschwarze 		erc++;
170720fa2881Sschwarze 		/* Can this be lifted?  See rxdebug.1 for example. */
170820fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
170920fa2881Sschwarze 	}
171020fa2881Sschwarze 
171120fa2881Sschwarze 	return(erc ? 0 : 1);
171220fa2881Sschwarze }
1713f73abda9Skristaps 
1714f73abda9Skristaps static int
1715f73abda9Skristaps post_st(POST_ARGS)
1716f73abda9Skristaps {
1717bb648afaSschwarze 	struct mdoc_node	 *ch;
171820fa2881Sschwarze 	const char		 *p;
1719f73abda9Skristaps 
1720bb648afaSschwarze 	if (NULL == (ch = mdoc->last->child)) {
1721bb648afaSschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1722bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1723bb648afaSschwarze 		return(1);
1724bb648afaSschwarze 	}
172520fa2881Sschwarze 
1726bb648afaSschwarze 	assert(MDOC_TEXT == ch->type);
172720fa2881Sschwarze 
1728bb648afaSschwarze 	if (NULL == (p = mdoc_a2st(ch->string))) {
172920fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
173020fa2881Sschwarze 		mdoc_node_delete(mdoc, mdoc->last);
173120fa2881Sschwarze 	} else {
1732bb648afaSschwarze 		free(ch->string);
1733bb648afaSschwarze 		ch->string = mandoc_strdup(p);
1734f73abda9Skristaps 	}
1735f73abda9Skristaps 
173620fa2881Sschwarze 	return(1);
173720fa2881Sschwarze }
1738f73abda9Skristaps 
1739f73abda9Skristaps static int
1740011fe33bSschwarze post_rs(POST_ARGS)
1741011fe33bSschwarze {
174220fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
174320fa2881Sschwarze 	int		  i, j;
1744011fe33bSschwarze 
1745bb648afaSschwarze 	switch (mdoc->last->type) {
1746bb648afaSschwarze 	case (MDOC_HEAD):
1747bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1748011fe33bSschwarze 		return(1);
1749bb648afaSschwarze 	case (MDOC_BODY):
1750bb648afaSschwarze 		if (mdoc->last->child)
1751bb648afaSschwarze 			break;
1752bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1753bb648afaSschwarze 		return(1);
1754bb648afaSschwarze 	default:
1755bb648afaSschwarze 		return(1);
1756bb648afaSschwarze 	}
1757011fe33bSschwarze 
175820fa2881Sschwarze 	/*
175920fa2881Sschwarze 	 * Make sure only certain types of nodes are allowed within the
176020fa2881Sschwarze 	 * the `Rs' body.  Delete offending nodes and raise a warning.
176120fa2881Sschwarze 	 * Do this before re-ordering for the sake of clarity.
176220fa2881Sschwarze 	 */
176320fa2881Sschwarze 
176420fa2881Sschwarze 	next = NULL;
176520fa2881Sschwarze 	for (nn = mdoc->last->child; nn; nn = next) {
176620fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
176720fa2881Sschwarze 			if (nn->tok == rsord[i])
1768011fe33bSschwarze 				break;
176920fa2881Sschwarze 
177020fa2881Sschwarze 		if (i < RSORD_MAX) {
17715d273f35Sschwarze 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
17725d273f35Sschwarze 				mdoc->last->norm->Rs.quote_T++;
177320fa2881Sschwarze 			next = nn->next;
177420fa2881Sschwarze 			continue;
177520fa2881Sschwarze 		}
177620fa2881Sschwarze 
177720fa2881Sschwarze 		next = nn->next;
177820fa2881Sschwarze 		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
177920fa2881Sschwarze 		mdoc_node_delete(mdoc, nn);
178020fa2881Sschwarze 	}
178120fa2881Sschwarze 
178220fa2881Sschwarze 	/*
1783c6176538Sschwarze 	 * Nothing to sort if only invalid nodes were found
1784c6176538Sschwarze 	 * inside the `Rs' body.
1785c6176538Sschwarze 	 */
1786c6176538Sschwarze 
1787c6176538Sschwarze 	if (NULL == mdoc->last->child)
1788c6176538Sschwarze 		return(1);
1789c6176538Sschwarze 
1790c6176538Sschwarze 	/*
179120fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
179220fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
179320fa2881Sschwarze 	 * and correctly order it.  This is a insertion sort.
179420fa2881Sschwarze 	 */
179520fa2881Sschwarze 
179620fa2881Sschwarze 	next = NULL;
179720fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
179820fa2881Sschwarze 		/* Determine order of `nn'. */
179920fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
180020fa2881Sschwarze 			if (rsord[i] == nn->tok)
180120fa2881Sschwarze 				break;
180220fa2881Sschwarze 
180320fa2881Sschwarze 		/*
180420fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
180520fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
180620fa2881Sschwarze 		 * just re-ordering, there's no need for the
180720fa2881Sschwarze 		 * full unlink process.
180820fa2881Sschwarze 		 */
180920fa2881Sschwarze 
181020fa2881Sschwarze 		if (NULL != (next = nn->next))
181120fa2881Sschwarze 			next->prev = nn->prev;
181220fa2881Sschwarze 
181320fa2881Sschwarze 		if (NULL != (prev = nn->prev))
181420fa2881Sschwarze 			prev->next = nn->next;
181520fa2881Sschwarze 
181620fa2881Sschwarze 		nn->prev = nn->next = NULL;
181720fa2881Sschwarze 
181820fa2881Sschwarze 		/*
181920fa2881Sschwarze 		 * Scan back until we reach a node that's
182020fa2881Sschwarze 		 * ordered before `nn'.
182120fa2881Sschwarze 		 */
182220fa2881Sschwarze 
182320fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
182420fa2881Sschwarze 			/* Determine order of `prev'. */
182520fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
182620fa2881Sschwarze 				if (rsord[j] == prev->tok)
182720fa2881Sschwarze 					break;
182820fa2881Sschwarze 
182920fa2881Sschwarze 			if (j <= i)
183020fa2881Sschwarze 				break;
183120fa2881Sschwarze 		}
183220fa2881Sschwarze 
183320fa2881Sschwarze 		/*
183420fa2881Sschwarze 		 * Set `nn' back into its correct place in front
183520fa2881Sschwarze 		 * of the `prev' node.
183620fa2881Sschwarze 		 */
183720fa2881Sschwarze 
183820fa2881Sschwarze 		nn->prev = prev;
183920fa2881Sschwarze 
184020fa2881Sschwarze 		if (prev) {
184120fa2881Sschwarze 			if (prev->next)
184220fa2881Sschwarze 				prev->next->prev = nn;
184320fa2881Sschwarze 			nn->next = prev->next;
184420fa2881Sschwarze 			prev->next = nn;
184520fa2881Sschwarze 		} else {
184620fa2881Sschwarze 			mdoc->last->child->prev = nn;
184720fa2881Sschwarze 			nn->next = mdoc->last->child;
184820fa2881Sschwarze 			mdoc->last->child = nn;
184920fa2881Sschwarze 		}
1850011fe33bSschwarze 	}
1851011fe33bSschwarze 
1852011fe33bSschwarze 	return(1);
1853011fe33bSschwarze }
1854011fe33bSschwarze 
1855011fe33bSschwarze static int
1856af216717Sschwarze post_ns(POST_ARGS)
1857af216717Sschwarze {
1858af216717Sschwarze 
1859af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
1860af216717Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1861af216717Sschwarze 	return(1);
1862af216717Sschwarze }
1863af216717Sschwarze 
1864af216717Sschwarze static int
1865f73abda9Skristaps post_sh(POST_ARGS)
1866f73abda9Skristaps {
1867f73abda9Skristaps 
1868f73abda9Skristaps 	if (MDOC_HEAD == mdoc->last->type)
1869f73abda9Skristaps 		return(post_sh_head(mdoc));
1870f73abda9Skristaps 	if (MDOC_BODY == mdoc->last->type)
1871f73abda9Skristaps 		return(post_sh_body(mdoc));
1872f73abda9Skristaps 
1873f73abda9Skristaps 	return(1);
1874f73abda9Skristaps }
1875f73abda9Skristaps 
1876f73abda9Skristaps static int
1877f73abda9Skristaps post_sh_body(POST_ARGS)
1878f73abda9Skristaps {
1879f73abda9Skristaps 	struct mdoc_node *n;
1880f73abda9Skristaps 
1881f8c9d6f2Sschwarze 	if (SEC_NAME != mdoc->lastsec)
1882f73abda9Skristaps 		return(1);
1883f73abda9Skristaps 
1884f73abda9Skristaps 	/*
1885f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1886f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1887f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1888f73abda9Skristaps 	 */
1889f73abda9Skristaps 
189020fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
189120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
189220fa2881Sschwarze 		return(1);
189320fa2881Sschwarze 	}
1894f73abda9Skristaps 
1895f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1896f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1897f73abda9Skristaps 			continue;
1898f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1899f73abda9Skristaps 			continue;
190020fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1901f73abda9Skristaps 	}
1902f73abda9Skristaps 
190349d529b5Sschwarze 	assert(n);
19044602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1905f73abda9Skristaps 		return(1);
1906f73abda9Skristaps 
190720fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
190820fa2881Sschwarze 	return(1);
190920fa2881Sschwarze }
1910f73abda9Skristaps 
1911f73abda9Skristaps static int
1912f73abda9Skristaps post_sh_head(POST_ARGS)
1913f73abda9Skristaps {
19143216dddfSschwarze 	char		 buf[BUFSIZ];
1915a2cff342Sschwarze 	struct mdoc_node *n;
1916f73abda9Skristaps 	enum mdoc_sec	 sec;
191704e980cbSschwarze 	int		 c;
1918f73abda9Skristaps 
1919f73abda9Skristaps 	/*
1920f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
192120fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
192220fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
192320fa2881Sschwarze 	 * manual sections.
1924f73abda9Skristaps 	 */
1925f73abda9Skristaps 
192604e980cbSschwarze 	sec = SEC_CUSTOM;
192704e980cbSschwarze 	buf[0] = '\0';
192804e980cbSschwarze 	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
192904e980cbSschwarze 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
19306e03d529Sschwarze 		return(0);
193104e980cbSschwarze 	} else if (1 == c)
193219a69263Sschwarze 		sec = a2sec(buf);
1933f73abda9Skristaps 
193420fa2881Sschwarze 	/* The NAME should be first. */
1935f73abda9Skristaps 
1936fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
193720fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
193820fa2881Sschwarze 
193920fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
194020fa2881Sschwarze 
194122881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
194222881299Sschwarze 		roff_setreg(mdoc->roff, "nS", 1);
194320fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
194422881299Sschwarze 	} else {
194522881299Sschwarze 		roff_setreg(mdoc->roff, "nS", 0);
194620fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
194722881299Sschwarze 	}
194820fa2881Sschwarze 
194920fa2881Sschwarze 	/* Mark our last section. */
195020fa2881Sschwarze 
195120fa2881Sschwarze 	mdoc->lastsec = sec;
19521eccdf28Sschwarze 
19531eccdf28Sschwarze 	/*
19541eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
19551eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
19561eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
19571eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
19581eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
19591eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
19601eccdf28Sschwarze 	 */
19611eccdf28Sschwarze 
1962a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
1963a2cff342Sschwarze 	mdoc->last->sec = sec;
1964a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
1965a2cff342Sschwarze 		n->sec = sec;
196620fa2881Sschwarze 
196720fa2881Sschwarze 	/* We don't care about custom sections after this. */
1968fccfce9dSschwarze 
1969f73abda9Skristaps 	if (SEC_CUSTOM == sec)
1970f73abda9Skristaps 		return(1);
1971fccfce9dSschwarze 
19726be99f77Sschwarze 	/*
197320fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
197420fa2881Sschwarze 	 * out of order.
19756be99f77Sschwarze 	 */
1976f73abda9Skristaps 
197720fa2881Sschwarze 	if (sec == mdoc->lastnamed)
197820fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
197920fa2881Sschwarze 
198020fa2881Sschwarze 	if (sec < mdoc->lastnamed)
198120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
198220fa2881Sschwarze 
198320fa2881Sschwarze 	/* Mark the last named section. */
198420fa2881Sschwarze 
198520fa2881Sschwarze 	mdoc->lastnamed = sec;
198620fa2881Sschwarze 
198720fa2881Sschwarze 	/* Check particular section/manual conventions. */
198820fa2881Sschwarze 
198992c0ca7fSschwarze 	assert(mdoc->meta.msec);
199020fa2881Sschwarze 
199120fa2881Sschwarze 	switch (sec) {
199220fa2881Sschwarze 	case (SEC_RETURN_VALUES):
199320fa2881Sschwarze 		/* FALLTHROUGH */
199420fa2881Sschwarze 	case (SEC_ERRORS):
199520fa2881Sschwarze 		/* FALLTHROUGH */
199620fa2881Sschwarze 	case (SEC_LIBRARY):
199792c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
1998f73abda9Skristaps 			break;
199992c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
200092c0ca7fSschwarze 			break;
200192c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
200292c0ca7fSschwarze 			break;
2003c645d318Sschwarze 		mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
2004c645d318Sschwarze 				mdoc->last->line, mdoc->last->pos, buf);
200520fa2881Sschwarze 		break;
2006f73abda9Skristaps 	default:
2007f73abda9Skristaps 		break;
2008f73abda9Skristaps 	}
2009f73abda9Skristaps 
2010f73abda9Skristaps 	return(1);
2011f73abda9Skristaps }
2012d39b9a9cSschwarze 
201320fa2881Sschwarze static int
2014f6127a73Sschwarze post_ignpar(POST_ARGS)
2015f6127a73Sschwarze {
2016f6127a73Sschwarze 	struct mdoc_node *np;
2017f6127a73Sschwarze 
2018f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
2019f6127a73Sschwarze 		return(1);
2020f6127a73Sschwarze 
2021f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2022f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2023f6127a73Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2024f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2025f6127a73Sschwarze 		}
2026f6127a73Sschwarze 
2027f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2028f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2029f6127a73Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2030f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2031f6127a73Sschwarze 		}
2032f6127a73Sschwarze 
2033f6127a73Sschwarze 	return(1);
2034f6127a73Sschwarze }
2035f6127a73Sschwarze 
2036f6127a73Sschwarze static int
203720fa2881Sschwarze pre_par(PRE_ARGS)
2038d39b9a9cSschwarze {
2039d39b9a9cSschwarze 
2040d39b9a9cSschwarze 	if (NULL == mdoc->last)
2041d39b9a9cSschwarze 		return(1);
2042f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2043f6127a73Sschwarze 		return(1);
2044d39b9a9cSschwarze 
204520fa2881Sschwarze 	/*
204620fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
204720fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
204820fa2881Sschwarze 	 */
2049d39b9a9cSschwarze 
2050e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2051e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2052e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2053d39b9a9cSschwarze 		return(1);
20548c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2055d39b9a9cSschwarze 		return(1);
20568c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2057d39b9a9cSschwarze 		return(1);
20588c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2059f6127a73Sschwarze 		return(1);
2060d39b9a9cSschwarze 
2061d39b9a9cSschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2062d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2063d39b9a9cSschwarze 	return(1);
2064d39b9a9cSschwarze }
206520fa2881Sschwarze 
206620fa2881Sschwarze static int
2067e0dd4c9cSschwarze post_par(POST_ARGS)
2068e0dd4c9cSschwarze {
2069e0dd4c9cSschwarze 
2070e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2071e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2072e0dd4c9cSschwarze 		return(1);
2073e0dd4c9cSschwarze 
2074e0dd4c9cSschwarze 	if (NULL == mdoc->last->prev) {
2075e0dd4c9cSschwarze 		if (MDOC_Sh != mdoc->last->parent->tok &&
2076e0dd4c9cSschwarze 		    MDOC_Ss != mdoc->last->parent->tok)
2077e0dd4c9cSschwarze 			return(1);
2078e0dd4c9cSschwarze 	} else {
2079e0dd4c9cSschwarze 		if (MDOC_Pp != mdoc->last->prev->tok &&
2080e0dd4c9cSschwarze 		    MDOC_Lp != mdoc->last->prev->tok &&
2081e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
2082e0dd4c9cSschwarze 		     (MDOC_sp != mdoc->last->prev->tok &&
2083e0dd4c9cSschwarze 		      MDOC_br != mdoc->last->prev->tok)))
2084e0dd4c9cSschwarze 			return(1);
2085e0dd4c9cSschwarze 	}
2086e0dd4c9cSschwarze 
2087e0dd4c9cSschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2088e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2089e0dd4c9cSschwarze 	return(1);
2090e0dd4c9cSschwarze }
2091e0dd4c9cSschwarze 
2092e0dd4c9cSschwarze static int
209320fa2881Sschwarze pre_literal(PRE_ARGS)
209420fa2881Sschwarze {
209520fa2881Sschwarze 
209620fa2881Sschwarze 	if (MDOC_BODY != n->type)
209720fa2881Sschwarze 		return(1);
209820fa2881Sschwarze 
209920fa2881Sschwarze 	/*
210020fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
210120fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
210220fa2881Sschwarze 	 */
210320fa2881Sschwarze 
210420fa2881Sschwarze 	switch (n->tok) {
210520fa2881Sschwarze 	case (MDOC_Dl):
210620fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
210720fa2881Sschwarze 		break;
210820fa2881Sschwarze 	case (MDOC_Bd):
21098c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
211020fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
21118c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
211220fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
211320fa2881Sschwarze 		break;
211420fa2881Sschwarze 	default:
211520fa2881Sschwarze 		abort();
211620fa2881Sschwarze 		/* NOTREACHED */
211720fa2881Sschwarze 	}
211820fa2881Sschwarze 
211920fa2881Sschwarze 	return(1);
212020fa2881Sschwarze }
212120fa2881Sschwarze 
212220fa2881Sschwarze static int
212320fa2881Sschwarze post_dd(POST_ARGS)
212420fa2881Sschwarze {
212520fa2881Sschwarze 	char		  buf[DATESIZE];
212620fa2881Sschwarze 	struct mdoc_node *n;
212704e980cbSschwarze 	int		  c;
212820fa2881Sschwarze 
2129b058e777Sschwarze 	if (mdoc->meta.date)
2130b058e777Sschwarze 		free(mdoc->meta.date);
213120fa2881Sschwarze 
2132b058e777Sschwarze 	n = mdoc->last;
2133b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2134a35fc07aSschwarze 		mdoc->meta.date = mandoc_normdate
2135a35fc07aSschwarze 			(mdoc->parse, NULL, n->line, n->pos);
213620fa2881Sschwarze 		return(1);
213720fa2881Sschwarze 	}
213820fa2881Sschwarze 
213904e980cbSschwarze 	buf[0] = '\0';
214004e980cbSschwarze 	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
214104e980cbSschwarze 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
214220fa2881Sschwarze 		return(0);
214304e980cbSschwarze 	}
214420fa2881Sschwarze 
214504e980cbSschwarze 	assert(c);
2146a35fc07aSschwarze 	mdoc->meta.date = mandoc_normdate
2147a35fc07aSschwarze 		(mdoc->parse, buf, n->line, n->pos);
214820fa2881Sschwarze 
214920fa2881Sschwarze 	return(1);
215020fa2881Sschwarze }
215120fa2881Sschwarze 
215220fa2881Sschwarze static int
215320fa2881Sschwarze post_dt(POST_ARGS)
215420fa2881Sschwarze {
215520fa2881Sschwarze 	struct mdoc_node *nn, *n;
215620fa2881Sschwarze 	const char	 *cp;
215720fa2881Sschwarze 	char		 *p;
215820fa2881Sschwarze 
215920fa2881Sschwarze 	n = mdoc->last;
216020fa2881Sschwarze 
216120fa2881Sschwarze 	if (mdoc->meta.title)
216220fa2881Sschwarze 		free(mdoc->meta.title);
216320fa2881Sschwarze 	if (mdoc->meta.vol)
216420fa2881Sschwarze 		free(mdoc->meta.vol);
216520fa2881Sschwarze 	if (mdoc->meta.arch)
216620fa2881Sschwarze 		free(mdoc->meta.arch);
216720fa2881Sschwarze 
216820fa2881Sschwarze 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
216920fa2881Sschwarze 
217020fa2881Sschwarze 	/* First make all characters uppercase. */
217120fa2881Sschwarze 
217220fa2881Sschwarze 	if (NULL != (nn = n->child))
217320fa2881Sschwarze 		for (p = nn->string; *p; p++) {
217404e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
217520fa2881Sschwarze 				continue;
217620fa2881Sschwarze 
217720fa2881Sschwarze 			/*
217820fa2881Sschwarze 			 * FIXME: don't be lazy: have this make all
217920fa2881Sschwarze 			 * characters be uppercase and just warn once.
218020fa2881Sschwarze 			 */
218120fa2881Sschwarze 			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
218220fa2881Sschwarze 			break;
218320fa2881Sschwarze 		}
218420fa2881Sschwarze 
218520fa2881Sschwarze 	/* Handles: `.Dt'
218620fa2881Sschwarze 	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
218720fa2881Sschwarze 	 */
218820fa2881Sschwarze 
218920fa2881Sschwarze 	if (NULL == (nn = n->child)) {
219020fa2881Sschwarze 		/* XXX: make these macro values. */
219120fa2881Sschwarze 		/* FIXME: warn about missing values. */
219220fa2881Sschwarze 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
219320fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
219420fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
219520fa2881Sschwarze 		return(1);
219620fa2881Sschwarze 	}
219720fa2881Sschwarze 
219820fa2881Sschwarze 	/* Handles: `.Dt TITLE'
219920fa2881Sschwarze 	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
220020fa2881Sschwarze 	 */
220120fa2881Sschwarze 
220220fa2881Sschwarze 	mdoc->meta.title = mandoc_strdup
220320fa2881Sschwarze 		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
220420fa2881Sschwarze 
220520fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
220620fa2881Sschwarze 		/* FIXME: warn about missing msec. */
220720fa2881Sschwarze 		/* XXX: make this a macro value. */
220820fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
220920fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
221020fa2881Sschwarze 		return(1);
221120fa2881Sschwarze 	}
221220fa2881Sschwarze 
221320fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
221420fa2881Sschwarze 	 *   --> title = TITLE, volume = SEC is msec ?
221520fa2881Sschwarze 	 *           format(msec) : SEC,
221620fa2881Sschwarze 	 *       msec = SEC is msec ? atoi(msec) : 0,
221720fa2881Sschwarze 	 *       arch = NULL
221820fa2881Sschwarze 	 */
221920fa2881Sschwarze 
222088ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
222120fa2881Sschwarze 	if (cp) {
222220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
222320fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
222420fa2881Sschwarze 	} else {
222520fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
222620fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
222720fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
222820fa2881Sschwarze 	}
222920fa2881Sschwarze 
223020fa2881Sschwarze 	if (NULL == (nn = nn->next))
223120fa2881Sschwarze 		return(1);
223220fa2881Sschwarze 
223320fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
223420fa2881Sschwarze 	 *   --> title = TITLE, volume = VOL is vol ?
223520fa2881Sschwarze 	 *       format(VOL) :
223620fa2881Sschwarze 	 *           VOL is arch ? format(arch) :
223720fa2881Sschwarze 	 *               VOL
223820fa2881Sschwarze 	 */
223920fa2881Sschwarze 
224020fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
224120fa2881Sschwarze 	if (cp) {
224220fa2881Sschwarze 		free(mdoc->meta.vol);
224320fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
224420fa2881Sschwarze 	} else {
224520fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
224620fa2881Sschwarze 		if (NULL == cp) {
224755818fdcSschwarze 			mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
224820fa2881Sschwarze 			free(mdoc->meta.vol);
224920fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
225020fa2881Sschwarze 		} else
225120fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
225220fa2881Sschwarze 	}
225320fa2881Sschwarze 
225420fa2881Sschwarze 	/* Ignore any subsequent parameters... */
225520fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
225620fa2881Sschwarze 
225720fa2881Sschwarze 	return(1);
225820fa2881Sschwarze }
225920fa2881Sschwarze 
226020fa2881Sschwarze static int
226120fa2881Sschwarze post_prol(POST_ARGS)
226220fa2881Sschwarze {
226320fa2881Sschwarze 	/*
226420fa2881Sschwarze 	 * Remove prologue macros from the document after they're
226520fa2881Sschwarze 	 * processed.  The final document uses mdoc_meta for these
226620fa2881Sschwarze 	 * values and discards the originals.
226720fa2881Sschwarze 	 */
226820fa2881Sschwarze 
226920fa2881Sschwarze 	mdoc_node_delete(mdoc, mdoc->last);
227020fa2881Sschwarze 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
227120fa2881Sschwarze 		mdoc->flags |= MDOC_PBODY;
227220fa2881Sschwarze 
227320fa2881Sschwarze 	return(1);
227420fa2881Sschwarze }
227520fa2881Sschwarze 
227620fa2881Sschwarze static int
2277992063deSschwarze post_bx(POST_ARGS)
2278992063deSschwarze {
2279992063deSschwarze 	struct mdoc_node	*n;
2280992063deSschwarze 
2281992063deSschwarze 	/*
2282992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2283992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2284992063deSschwarze 	 * uppercase blindly.
2285992063deSschwarze 	 */
2286992063deSschwarze 
2287992063deSschwarze 	n = mdoc->last->child;
2288992063deSschwarze 	if (n && NULL != (n = n->next))
228919a69263Sschwarze 		*n->string = (char)toupper
229019a69263Sschwarze 			((unsigned char)*n->string);
2291992063deSschwarze 
2292992063deSschwarze 	return(1);
2293992063deSschwarze }
2294992063deSschwarze 
2295992063deSschwarze static int
229620fa2881Sschwarze post_os(POST_ARGS)
229720fa2881Sschwarze {
229820fa2881Sschwarze 	struct mdoc_node *n;
229920fa2881Sschwarze 	char		  buf[BUFSIZ];
230004e980cbSschwarze 	int		  c;
230120fa2881Sschwarze #ifndef OSNAME
230220fa2881Sschwarze 	struct utsname	  utsname;
230320fa2881Sschwarze #endif
230420fa2881Sschwarze 
230520fa2881Sschwarze 	n = mdoc->last;
230620fa2881Sschwarze 
230720fa2881Sschwarze 	/*
2308353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2309353fa9ecSschwarze 	 * The order of precedence is:
2310353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2311353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2312353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2313353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
231420fa2881Sschwarze  	 */
231520fa2881Sschwarze 
231620fa2881Sschwarze 	free(mdoc->meta.os);
231720fa2881Sschwarze 
231804e980cbSschwarze 	buf[0] = '\0';
231904e980cbSschwarze 	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
232004e980cbSschwarze 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
232120fa2881Sschwarze 		return(0);
232204e980cbSschwarze 	}
232304e980cbSschwarze 
232404e980cbSschwarze 	assert(c);
232520fa2881Sschwarze 
232620fa2881Sschwarze 	if ('\0' == buf[0]) {
2327353fa9ecSschwarze 		if (mdoc->defos) {
2328353fa9ecSschwarze 			mdoc->meta.os = mandoc_strdup(mdoc->defos);
2329353fa9ecSschwarze 			return(1);
2330353fa9ecSschwarze 		}
233120fa2881Sschwarze #ifdef OSNAME
233220fa2881Sschwarze 		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
233320fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
233420fa2881Sschwarze 			return(0);
233520fa2881Sschwarze 		}
233620fa2881Sschwarze #else /*!OSNAME */
2337a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
233820fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
233920fa2881Sschwarze                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
234020fa2881Sschwarze                         return(post_prol(mdoc));
234120fa2881Sschwarze                 }
234220fa2881Sschwarze 
234320fa2881Sschwarze 		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
234420fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
234520fa2881Sschwarze 			return(0);
234620fa2881Sschwarze 		}
234720fa2881Sschwarze 		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
234820fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
234920fa2881Sschwarze 			return(0);
235020fa2881Sschwarze 		}
235120fa2881Sschwarze 		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
235220fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
235320fa2881Sschwarze 			return(0);
235420fa2881Sschwarze 		}
235520fa2881Sschwarze #endif /*!OSNAME*/
235620fa2881Sschwarze 	}
235720fa2881Sschwarze 
235820fa2881Sschwarze 	mdoc->meta.os = mandoc_strdup(buf);
235920fa2881Sschwarze 	return(1);
236020fa2881Sschwarze }
236120fa2881Sschwarze 
236220fa2881Sschwarze static int
236320fa2881Sschwarze post_std(POST_ARGS)
236420fa2881Sschwarze {
236520fa2881Sschwarze 	struct mdoc_node *nn, *n;
236620fa2881Sschwarze 
236720fa2881Sschwarze 	n = mdoc->last;
236820fa2881Sschwarze 
236920fa2881Sschwarze 	/*
237020fa2881Sschwarze 	 * Macros accepting `-std' as an argument have the name of the
237120fa2881Sschwarze 	 * current document (`Nm') filled in as the argument if it's not
237220fa2881Sschwarze 	 * provided.
237320fa2881Sschwarze 	 */
237420fa2881Sschwarze 
237520fa2881Sschwarze 	if (n->child)
237620fa2881Sschwarze 		return(1);
237720fa2881Sschwarze 
237820fa2881Sschwarze 	if (NULL == mdoc->meta.name)
237920fa2881Sschwarze 		return(1);
238020fa2881Sschwarze 
238120fa2881Sschwarze 	nn = n;
238220fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
238320fa2881Sschwarze 
238420fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
238520fa2881Sschwarze 		return(0);
238620fa2881Sschwarze 
238720fa2881Sschwarze 	mdoc->last = nn;
238820fa2881Sschwarze 	return(1);
238920fa2881Sschwarze }
239020fa2881Sschwarze 
239104e980cbSschwarze /*
239204e980cbSschwarze  * Concatenate a node, stopping at the first non-text.
239304e980cbSschwarze  * Concatenation is separated by a single whitespace.
239404e980cbSschwarze  * Returns -1 on fatal (string overrun) error, 0 if child nodes were
239504e980cbSschwarze  * encountered, 1 otherwise.
239604e980cbSschwarze  */
239720fa2881Sschwarze static int
239804e980cbSschwarze concat(char *p, const struct mdoc_node *n, size_t sz)
239920fa2881Sschwarze {
240020fa2881Sschwarze 
240104e980cbSschwarze 	for ( ; NULL != n; n = n->next) {
240204e980cbSschwarze 		if (MDOC_TEXT != n->type)
240320fa2881Sschwarze 			return(0);
240404e980cbSschwarze 		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
240504e980cbSschwarze 			return(-1);
240604e980cbSschwarze 		if (strlcat(p, n->string, sz) >= sz)
240704e980cbSschwarze 			return(-1);
240804e980cbSschwarze 		concat(p, n->child, sz);
240920fa2881Sschwarze 	}
241020fa2881Sschwarze 
241120fa2881Sschwarze 	return(1);
241220fa2881Sschwarze }
241320fa2881Sschwarze 
241419a69263Sschwarze static enum mdoc_sec
241519a69263Sschwarze a2sec(const char *p)
241619a69263Sschwarze {
241719a69263Sschwarze 	int		 i;
241819a69263Sschwarze 
241919a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
242019a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
242119a69263Sschwarze 			return((enum mdoc_sec)i);
242219a69263Sschwarze 
242319a69263Sschwarze 	return(SEC_CUSTOM);
242419a69263Sschwarze }
242519a69263Sschwarze 
242619a69263Sschwarze static size_t
242719a69263Sschwarze macro2len(enum mdoct macro)
242819a69263Sschwarze {
242919a69263Sschwarze 
243019a69263Sschwarze 	switch (macro) {
243119a69263Sschwarze 	case(MDOC_Ad):
243219a69263Sschwarze 		return(12);
243319a69263Sschwarze 	case(MDOC_Ao):
243419a69263Sschwarze 		return(12);
243519a69263Sschwarze 	case(MDOC_An):
243619a69263Sschwarze 		return(12);
243719a69263Sschwarze 	case(MDOC_Aq):
243819a69263Sschwarze 		return(12);
243919a69263Sschwarze 	case(MDOC_Ar):
244019a69263Sschwarze 		return(12);
244119a69263Sschwarze 	case(MDOC_Bo):
244219a69263Sschwarze 		return(12);
244319a69263Sschwarze 	case(MDOC_Bq):
244419a69263Sschwarze 		return(12);
244519a69263Sschwarze 	case(MDOC_Cd):
244619a69263Sschwarze 		return(12);
244719a69263Sschwarze 	case(MDOC_Cm):
244819a69263Sschwarze 		return(10);
244919a69263Sschwarze 	case(MDOC_Do):
245019a69263Sschwarze 		return(10);
245119a69263Sschwarze 	case(MDOC_Dq):
245219a69263Sschwarze 		return(12);
245319a69263Sschwarze 	case(MDOC_Dv):
245419a69263Sschwarze 		return(12);
245519a69263Sschwarze 	case(MDOC_Eo):
245619a69263Sschwarze 		return(12);
245719a69263Sschwarze 	case(MDOC_Em):
245819a69263Sschwarze 		return(10);
245919a69263Sschwarze 	case(MDOC_Er):
246019a69263Sschwarze 		return(17);
246119a69263Sschwarze 	case(MDOC_Ev):
246219a69263Sschwarze 		return(15);
246319a69263Sschwarze 	case(MDOC_Fa):
246419a69263Sschwarze 		return(12);
246519a69263Sschwarze 	case(MDOC_Fl):
246619a69263Sschwarze 		return(10);
246719a69263Sschwarze 	case(MDOC_Fo):
246819a69263Sschwarze 		return(16);
246919a69263Sschwarze 	case(MDOC_Fn):
247019a69263Sschwarze 		return(16);
247119a69263Sschwarze 	case(MDOC_Ic):
247219a69263Sschwarze 		return(10);
247319a69263Sschwarze 	case(MDOC_Li):
247419a69263Sschwarze 		return(16);
247519a69263Sschwarze 	case(MDOC_Ms):
247619a69263Sschwarze 		return(6);
247719a69263Sschwarze 	case(MDOC_Nm):
247819a69263Sschwarze 		return(10);
247919a69263Sschwarze 	case(MDOC_No):
248019a69263Sschwarze 		return(12);
248119a69263Sschwarze 	case(MDOC_Oo):
248219a69263Sschwarze 		return(10);
248319a69263Sschwarze 	case(MDOC_Op):
248419a69263Sschwarze 		return(14);
248519a69263Sschwarze 	case(MDOC_Pa):
248619a69263Sschwarze 		return(32);
248719a69263Sschwarze 	case(MDOC_Pf):
248819a69263Sschwarze 		return(12);
248919a69263Sschwarze 	case(MDOC_Po):
249019a69263Sschwarze 		return(12);
249119a69263Sschwarze 	case(MDOC_Pq):
249219a69263Sschwarze 		return(12);
249319a69263Sschwarze 	case(MDOC_Ql):
249419a69263Sschwarze 		return(16);
249519a69263Sschwarze 	case(MDOC_Qo):
249619a69263Sschwarze 		return(12);
249719a69263Sschwarze 	case(MDOC_So):
249819a69263Sschwarze 		return(12);
249919a69263Sschwarze 	case(MDOC_Sq):
250019a69263Sschwarze 		return(12);
250119a69263Sschwarze 	case(MDOC_Sy):
250219a69263Sschwarze 		return(6);
250319a69263Sschwarze 	case(MDOC_Sx):
250419a69263Sschwarze 		return(16);
250519a69263Sschwarze 	case(MDOC_Tn):
250619a69263Sschwarze 		return(10);
250719a69263Sschwarze 	case(MDOC_Va):
250819a69263Sschwarze 		return(12);
250919a69263Sschwarze 	case(MDOC_Vt):
251019a69263Sschwarze 		return(12);
251119a69263Sschwarze 	case(MDOC_Xr):
251219a69263Sschwarze 		return(10);
251319a69263Sschwarze 	default:
251419a69263Sschwarze 		break;
251519a69263Sschwarze 	};
251619a69263Sschwarze 	return(0);
251719a69263Sschwarze }
2518