xref: /minix3/external/bsd/mdocml/dist/mdoc_validate.c (revision e1cdaee10649323af446eb1a74571984b2ab3181)
10a6a1f1dSLionel Sambuc /*	Id: mdoc_validate.c,v 1.198 2013/12/15 21:23:52 schwarze Exp  */
2d65f6f70SBen Gras /*
30a6a1f1dSLionel Sambuc  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
40a6a1f1dSLionel Sambuc  * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras  *
6d65f6f70SBen Gras  * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras  * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras  * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras  *
10d65f6f70SBen Gras  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras  */
18d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
19d65f6f70SBen Gras #include "config.h"
20d65f6f70SBen Gras #endif
21d65f6f70SBen Gras 
22d65f6f70SBen Gras #ifndef OSNAME
23d65f6f70SBen Gras #include <sys/utsname.h>
24d65f6f70SBen Gras #endif
25d65f6f70SBen Gras 
26d65f6f70SBen Gras #include <sys/types.h>
27d65f6f70SBen Gras 
28d65f6f70SBen Gras #include <assert.h>
29d65f6f70SBen Gras #include <ctype.h>
30d65f6f70SBen Gras #include <limits.h>
31d65f6f70SBen Gras #include <stdio.h>
32d65f6f70SBen Gras #include <stdlib.h>
33d65f6f70SBen Gras #include <string.h>
34d65f6f70SBen Gras #include <time.h>
35d65f6f70SBen Gras 
3692395e9cSLionel Sambuc #include "mdoc.h"
37d65f6f70SBen Gras #include "mandoc.h"
38d65f6f70SBen Gras #include "libmdoc.h"
39d65f6f70SBen Gras #include "libmandoc.h"
40d65f6f70SBen Gras 
41d65f6f70SBen Gras /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42d65f6f70SBen Gras 
43d65f6f70SBen Gras #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
44d65f6f70SBen Gras #define	POST_ARGS struct mdoc *mdoc
45d65f6f70SBen Gras 
46d65f6f70SBen Gras #define	NUMSIZ	  32
47d65f6f70SBen Gras #define	DATESIZE  32
48d65f6f70SBen Gras 
49d65f6f70SBen Gras enum	check_ineq {
50d65f6f70SBen Gras 	CHECK_LT,
51d65f6f70SBen Gras 	CHECK_GT,
52d65f6f70SBen Gras 	CHECK_EQ
53d65f6f70SBen Gras };
54d65f6f70SBen Gras 
55d65f6f70SBen Gras enum	check_lvl {
56d65f6f70SBen Gras 	CHECK_WARN,
57d65f6f70SBen Gras 	CHECK_ERROR,
58d65f6f70SBen Gras };
59d65f6f70SBen Gras 
60d65f6f70SBen Gras typedef	int	(*v_pre)(PRE_ARGS);
61d65f6f70SBen Gras typedef	int	(*v_post)(POST_ARGS);
62d65f6f70SBen Gras 
63d65f6f70SBen Gras struct	valids {
64d65f6f70SBen Gras 	v_pre	*pre;
65d65f6f70SBen Gras 	v_post	*post;
66d65f6f70SBen Gras };
67d65f6f70SBen Gras 
68d65f6f70SBen Gras static	int	 check_count(struct mdoc *, enum mdoc_type,
69d65f6f70SBen Gras 			enum check_lvl, enum check_ineq, int);
70d65f6f70SBen Gras static	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
71d65f6f70SBen Gras static	void	 check_text(struct mdoc *, int, int, char *);
72d65f6f70SBen Gras static	void	 check_argv(struct mdoc *,
73d65f6f70SBen Gras 			struct mdoc_node *, struct mdoc_argv *);
74d65f6f70SBen Gras static	void	 check_args(struct mdoc *, struct mdoc_node *);
7592395e9cSLionel Sambuc static	int	 concat(char *, const struct mdoc_node *, size_t);
7692395e9cSLionel Sambuc static	enum mdoc_sec	a2sec(const char *);
7792395e9cSLionel Sambuc static	size_t		macro2len(enum mdoct);
78d65f6f70SBen Gras 
79d65f6f70SBen Gras static	int	 ebool(POST_ARGS);
80d65f6f70SBen Gras static	int	 berr_ge1(POST_ARGS);
81d65f6f70SBen Gras static	int	 bwarn_ge1(POST_ARGS);
82d65f6f70SBen Gras static	int	 ewarn_eq0(POST_ARGS);
83d65f6f70SBen Gras static	int	 ewarn_eq1(POST_ARGS);
84d65f6f70SBen Gras static	int	 ewarn_ge1(POST_ARGS);
85d65f6f70SBen Gras static	int	 ewarn_le1(POST_ARGS);
86d65f6f70SBen Gras static	int	 hwarn_eq0(POST_ARGS);
87d65f6f70SBen Gras static	int	 hwarn_eq1(POST_ARGS);
88d65f6f70SBen Gras static	int	 hwarn_ge1(POST_ARGS);
89d65f6f70SBen Gras static	int	 hwarn_le1(POST_ARGS);
90d65f6f70SBen Gras 
91d65f6f70SBen Gras static	int	 post_an(POST_ARGS);
92d65f6f70SBen Gras static	int	 post_at(POST_ARGS);
93d65f6f70SBen Gras static	int	 post_bf(POST_ARGS);
94d65f6f70SBen Gras static	int	 post_bl(POST_ARGS);
95d65f6f70SBen Gras static	int	 post_bl_block(POST_ARGS);
96d65f6f70SBen Gras static	int	 post_bl_block_width(POST_ARGS);
97d65f6f70SBen Gras static	int	 post_bl_block_tag(POST_ARGS);
98d65f6f70SBen Gras static	int	 post_bl_head(POST_ARGS);
9992395e9cSLionel Sambuc static	int	 post_bx(POST_ARGS);
1000a6a1f1dSLionel Sambuc static	int	 post_defaults(POST_ARGS);
101d65f6f70SBen Gras static	int	 post_dd(POST_ARGS);
102d65f6f70SBen Gras static	int	 post_dt(POST_ARGS);
103d65f6f70SBen Gras static	int	 post_eoln(POST_ARGS);
1040a6a1f1dSLionel Sambuc static	int	 post_hyph(POST_ARGS);
1050a6a1f1dSLionel Sambuc static	int	 post_ignpar(POST_ARGS);
106d65f6f70SBen Gras static	int	 post_it(POST_ARGS);
107d65f6f70SBen Gras static	int	 post_lb(POST_ARGS);
1080a6a1f1dSLionel Sambuc static	int	 post_literal(POST_ARGS);
109d65f6f70SBen Gras static	int	 post_nm(POST_ARGS);
11092395e9cSLionel Sambuc static	int	 post_ns(POST_ARGS);
111d65f6f70SBen Gras static	int	 post_os(POST_ARGS);
1120a6a1f1dSLionel Sambuc static	int	 post_par(POST_ARGS);
113d65f6f70SBen Gras static	int	 post_prol(POST_ARGS);
114d65f6f70SBen Gras static	int	 post_root(POST_ARGS);
115d65f6f70SBen Gras static	int	 post_rs(POST_ARGS);
116d65f6f70SBen Gras static	int	 post_sh(POST_ARGS);
117d65f6f70SBen Gras static	int	 post_sh_body(POST_ARGS);
118d65f6f70SBen Gras static	int	 post_sh_head(POST_ARGS);
119d65f6f70SBen Gras static	int	 post_st(POST_ARGS);
120d65f6f70SBen Gras static	int	 post_std(POST_ARGS);
121d65f6f70SBen Gras static	int	 post_vt(POST_ARGS);
122d65f6f70SBen Gras static	int	 pre_an(PRE_ARGS);
123d65f6f70SBen Gras static	int	 pre_bd(PRE_ARGS);
124d65f6f70SBen Gras static	int	 pre_bl(PRE_ARGS);
125d65f6f70SBen Gras static	int	 pre_dd(PRE_ARGS);
126d65f6f70SBen Gras static	int	 pre_display(PRE_ARGS);
127d65f6f70SBen Gras static	int	 pre_dt(PRE_ARGS);
128d65f6f70SBen Gras static	int	 pre_it(PRE_ARGS);
129d65f6f70SBen Gras static	int	 pre_literal(PRE_ARGS);
130d65f6f70SBen Gras static	int	 pre_os(PRE_ARGS);
131d65f6f70SBen Gras static	int	 pre_par(PRE_ARGS);
132d65f6f70SBen Gras static	int	 pre_sh(PRE_ARGS);
133d65f6f70SBen Gras static	int	 pre_ss(PRE_ARGS);
134d65f6f70SBen Gras static	int	 pre_std(PRE_ARGS);
135d65f6f70SBen Gras 
136d65f6f70SBen Gras static	v_post	 posts_an[] = { post_an, NULL };
137d65f6f70SBen Gras static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
138d65f6f70SBen Gras static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
139d65f6f70SBen Gras static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
140d65f6f70SBen Gras static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
141d65f6f70SBen Gras static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
14292395e9cSLionel Sambuc static	v_post	 posts_bx[] = { post_bx, NULL };
143d65f6f70SBen Gras static	v_post	 posts_bool[] = { ebool, NULL };
144d65f6f70SBen Gras static	v_post	 posts_eoln[] = { post_eoln, NULL };
145d65f6f70SBen Gras static	v_post	 posts_defaults[] = { post_defaults, NULL };
1460a6a1f1dSLionel Sambuc static	v_post	 posts_d1[] = { bwarn_ge1, post_hyph, NULL };
14792395e9cSLionel Sambuc static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
148d65f6f70SBen Gras static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
149d65f6f70SBen Gras static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
150d65f6f70SBen Gras static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
1510a6a1f1dSLionel Sambuc static	v_post	 posts_hyph[] = { post_hyph, NULL };
1520a6a1f1dSLionel Sambuc static	v_post	 posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
153d65f6f70SBen Gras static	v_post	 posts_it[] = { post_it, NULL };
154d65f6f70SBen Gras static	v_post	 posts_lb[] = { post_lb, NULL };
1550a6a1f1dSLionel Sambuc static	v_post	 posts_nd[] = { berr_ge1, post_hyph, NULL };
156d65f6f70SBen Gras static	v_post	 posts_nm[] = { post_nm, NULL };
157d65f6f70SBen Gras static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
15892395e9cSLionel Sambuc static	v_post	 posts_ns[] = { post_ns, NULL };
159d65f6f70SBen Gras static	v_post	 posts_os[] = { post_os, post_prol, NULL };
1600a6a1f1dSLionel Sambuc static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
161d65f6f70SBen Gras static	v_post	 posts_rs[] = { post_rs, NULL };
1620a6a1f1dSLionel Sambuc static	v_post	 posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
1630a6a1f1dSLionel Sambuc static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
1640a6a1f1dSLionel Sambuc static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
165d65f6f70SBen Gras static	v_post	 posts_st[] = { post_st, NULL };
166d65f6f70SBen Gras static	v_post	 posts_std[] = { post_std, NULL };
16792395e9cSLionel Sambuc static	v_post	 posts_text[] = { ewarn_ge1, NULL };
168d65f6f70SBen Gras static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
169d65f6f70SBen Gras static	v_post	 posts_vt[] = { post_vt, NULL };
170d65f6f70SBen Gras static	v_pre	 pres_an[] = { pre_an, NULL };
171d65f6f70SBen Gras static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
172d65f6f70SBen Gras static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
173d65f6f70SBen Gras static	v_pre	 pres_d1[] = { pre_display, NULL };
174d65f6f70SBen Gras static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
175d65f6f70SBen Gras static	v_pre	 pres_dd[] = { pre_dd, NULL };
176d65f6f70SBen Gras static	v_pre	 pres_dt[] = { pre_dt, NULL };
177d65f6f70SBen Gras static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
178d65f6f70SBen Gras static	v_pre	 pres_os[] = { pre_os, NULL };
179d65f6f70SBen Gras static	v_pre	 pres_pp[] = { pre_par, NULL };
180d65f6f70SBen Gras static	v_pre	 pres_sh[] = { pre_sh, NULL };
181d65f6f70SBen Gras static	v_pre	 pres_ss[] = { pre_ss, NULL };
182d65f6f70SBen Gras static	v_pre	 pres_std[] = { pre_std, NULL };
183d65f6f70SBen Gras 
18492395e9cSLionel Sambuc static	const struct valids mdoc_valids[MDOC_MAX] = {
185d65f6f70SBen Gras 	{ NULL, NULL },				/* Ap */
186d65f6f70SBen Gras 	{ pres_dd, posts_dd },			/* Dd */
187d65f6f70SBen Gras 	{ pres_dt, posts_dt },			/* Dt */
188d65f6f70SBen Gras 	{ pres_os, posts_os },			/* Os */
189d65f6f70SBen Gras 	{ pres_sh, posts_sh },			/* Sh */
190d65f6f70SBen Gras 	{ pres_ss, posts_ss },			/* Ss */
1910a6a1f1dSLionel Sambuc 	{ pres_pp, posts_pp },			/* Pp */
1920a6a1f1dSLionel Sambuc 	{ pres_d1, posts_d1 },			/* D1 */
193d65f6f70SBen Gras 	{ pres_dl, posts_dl },			/* Dl */
194d65f6f70SBen Gras 	{ pres_bd, posts_bd },			/* Bd */
195d65f6f70SBen Gras 	{ NULL, NULL },				/* Ed */
196d65f6f70SBen Gras 	{ pres_bl, posts_bl },			/* Bl */
197d65f6f70SBen Gras 	{ NULL, NULL },				/* El */
198d65f6f70SBen Gras 	{ pres_it, posts_it },			/* It */
19992395e9cSLionel Sambuc 	{ NULL, NULL },				/* Ad */
200d65f6f70SBen Gras 	{ pres_an, posts_an },			/* An */
201d65f6f70SBen Gras 	{ NULL, posts_defaults },		/* Ar */
20292395e9cSLionel Sambuc 	{ NULL, NULL },				/* Cd */
203d65f6f70SBen Gras 	{ NULL, NULL },				/* Cm */
204d65f6f70SBen Gras 	{ NULL, NULL },				/* Dv */
2050a6a1f1dSLionel Sambuc 	{ NULL, NULL },				/* Er */
206d65f6f70SBen Gras 	{ NULL, NULL },				/* Ev */
207d65f6f70SBen Gras 	{ pres_std, posts_std },		/* Ex */
208d65f6f70SBen Gras 	{ NULL, NULL },				/* Fa */
2090a6a1f1dSLionel Sambuc 	{ NULL, posts_text },			/* Fd */
210d65f6f70SBen Gras 	{ NULL, NULL },				/* Fl */
21192395e9cSLionel Sambuc 	{ NULL, NULL },				/* Fn */
21292395e9cSLionel Sambuc 	{ NULL, NULL },				/* Ft */
21392395e9cSLionel Sambuc 	{ NULL, NULL },				/* Ic */
214d65f6f70SBen Gras 	{ NULL, posts_text1 },			/* In */
215d65f6f70SBen Gras 	{ NULL, posts_defaults },		/* Li */
216d65f6f70SBen Gras 	{ NULL, posts_nd },			/* Nd */
217d65f6f70SBen Gras 	{ NULL, posts_nm },			/* Nm */
218d65f6f70SBen Gras 	{ NULL, NULL },				/* Op */
219d65f6f70SBen Gras 	{ NULL, NULL },				/* Ot */
220d65f6f70SBen Gras 	{ NULL, posts_defaults },		/* Pa */
221d65f6f70SBen Gras 	{ pres_std, posts_std },		/* Rv */
222d65f6f70SBen Gras 	{ NULL, posts_st },			/* St */
223d65f6f70SBen Gras 	{ NULL, NULL },				/* Va */
224d65f6f70SBen Gras 	{ NULL, posts_vt },			/* Vt */
22592395e9cSLionel Sambuc 	{ NULL, posts_text },			/* Xr */
226d65f6f70SBen Gras 	{ NULL, posts_text },			/* %A */
2270a6a1f1dSLionel Sambuc 	{ NULL, posts_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
22892395e9cSLionel Sambuc 	{ NULL, posts_text },			/* %D */
229d65f6f70SBen Gras 	{ NULL, posts_text },			/* %I */
230d65f6f70SBen Gras 	{ NULL, posts_text },			/* %J */
2310a6a1f1dSLionel Sambuc 	{ NULL, posts_hyphtext },		/* %N */
2320a6a1f1dSLionel Sambuc 	{ NULL, posts_hyphtext },		/* %O */
233d65f6f70SBen Gras 	{ NULL, posts_text },			/* %P */
2340a6a1f1dSLionel Sambuc 	{ NULL, posts_hyphtext },		/* %R */
2350a6a1f1dSLionel Sambuc 	{ NULL, posts_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
236d65f6f70SBen Gras 	{ NULL, posts_text },			/* %V */
237d65f6f70SBen Gras 	{ NULL, NULL },				/* Ac */
238d65f6f70SBen Gras 	{ NULL, NULL },				/* Ao */
239d65f6f70SBen Gras 	{ NULL, NULL },				/* Aq */
240d65f6f70SBen Gras 	{ NULL, posts_at },			/* At */
241d65f6f70SBen Gras 	{ NULL, NULL },				/* Bc */
242d65f6f70SBen Gras 	{ NULL, posts_bf },			/* Bf */
243d65f6f70SBen Gras 	{ NULL, NULL },				/* Bo */
244d65f6f70SBen Gras 	{ NULL, NULL },				/* Bq */
245d65f6f70SBen Gras 	{ NULL, NULL },				/* Bsx */
24692395e9cSLionel Sambuc 	{ NULL, posts_bx },			/* Bx */
247d65f6f70SBen Gras 	{ NULL, posts_bool },			/* Db */
248d65f6f70SBen Gras 	{ NULL, NULL },				/* Dc */
249d65f6f70SBen Gras 	{ NULL, NULL },				/* Do */
250d65f6f70SBen Gras 	{ NULL, NULL },				/* Dq */
251d65f6f70SBen Gras 	{ NULL, NULL },				/* Ec */
252d65f6f70SBen Gras 	{ NULL, NULL },				/* Ef */
253d65f6f70SBen Gras 	{ NULL, NULL },				/* Em */
254d65f6f70SBen Gras 	{ NULL, NULL },				/* Eo */
255d65f6f70SBen Gras 	{ NULL, NULL },				/* Fx */
25692395e9cSLionel Sambuc 	{ NULL, NULL },				/* Ms */
257d65f6f70SBen Gras 	{ NULL, posts_notext },			/* No */
25892395e9cSLionel Sambuc 	{ NULL, posts_ns },			/* Ns */
259d65f6f70SBen Gras 	{ NULL, NULL },				/* Nx */
260d65f6f70SBen Gras 	{ NULL, NULL },				/* Ox */
261d65f6f70SBen Gras 	{ NULL, NULL },				/* Pc */
262d65f6f70SBen Gras 	{ NULL, posts_text1 },			/* Pf */
263d65f6f70SBen Gras 	{ NULL, NULL },				/* Po */
264d65f6f70SBen Gras 	{ NULL, NULL },				/* Pq */
265d65f6f70SBen Gras 	{ NULL, NULL },				/* Qc */
266d65f6f70SBen Gras 	{ NULL, NULL },				/* Ql */
267d65f6f70SBen Gras 	{ NULL, NULL },				/* Qo */
268d65f6f70SBen Gras 	{ NULL, NULL },				/* Qq */
269d65f6f70SBen Gras 	{ NULL, NULL },				/* Re */
270d65f6f70SBen Gras 	{ NULL, posts_rs },			/* Rs */
271d65f6f70SBen Gras 	{ NULL, NULL },				/* Sc */
272d65f6f70SBen Gras 	{ NULL, NULL },				/* So */
273d65f6f70SBen Gras 	{ NULL, NULL },				/* Sq */
274d65f6f70SBen Gras 	{ NULL, posts_bool },			/* Sm */
2750a6a1f1dSLionel Sambuc 	{ NULL, posts_hyph },			/* Sx */
27692395e9cSLionel Sambuc 	{ NULL, NULL },				/* Sy */
27792395e9cSLionel Sambuc 	{ NULL, NULL },				/* Tn */
278d65f6f70SBen Gras 	{ NULL, NULL },				/* Ux */
279d65f6f70SBen Gras 	{ NULL, NULL },				/* Xc */
280d65f6f70SBen Gras 	{ NULL, NULL },				/* Xo */
281d65f6f70SBen Gras 	{ NULL, posts_fo },			/* Fo */
282d65f6f70SBen Gras 	{ NULL, NULL },				/* Fc */
283d65f6f70SBen Gras 	{ NULL, NULL },				/* Oo */
284d65f6f70SBen Gras 	{ NULL, NULL },				/* Oc */
285d65f6f70SBen Gras 	{ NULL, posts_bk },			/* Bk */
286d65f6f70SBen Gras 	{ NULL, NULL },				/* Ek */
287d65f6f70SBen Gras 	{ NULL, posts_eoln },			/* Bt */
288d65f6f70SBen Gras 	{ NULL, NULL },				/* Hf */
289d65f6f70SBen Gras 	{ NULL, NULL },				/* Fr */
290d65f6f70SBen Gras 	{ NULL, posts_eoln },			/* Ud */
291d65f6f70SBen Gras 	{ NULL, posts_lb },			/* Lb */
2920a6a1f1dSLionel Sambuc 	{ pres_pp, posts_pp },			/* Lp */
29392395e9cSLionel Sambuc 	{ NULL, NULL },				/* Lk */
294d65f6f70SBen Gras 	{ NULL, posts_defaults },		/* Mt */
295d65f6f70SBen Gras 	{ NULL, NULL },				/* Brq */
296d65f6f70SBen Gras 	{ NULL, NULL },				/* Bro */
297d65f6f70SBen Gras 	{ NULL, NULL },				/* Brc */
298d65f6f70SBen Gras 	{ NULL, posts_text },			/* %C */
299d65f6f70SBen Gras 	{ NULL, NULL },				/* Es */
300d65f6f70SBen Gras 	{ NULL, NULL },				/* En */
301d65f6f70SBen Gras 	{ NULL, NULL },				/* Dx */
302d65f6f70SBen Gras 	{ NULL, posts_text },			/* %Q */
3030a6a1f1dSLionel Sambuc 	{ NULL, posts_pp },			/* br */
3040a6a1f1dSLionel Sambuc 	{ NULL, posts_sp },			/* sp */
305d65f6f70SBen Gras 	{ NULL, posts_text1 },			/* %U */
306d65f6f70SBen Gras 	{ NULL, NULL },				/* Ta */
307d65f6f70SBen Gras };
308d65f6f70SBen Gras 
309d65f6f70SBen Gras #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
310d65f6f70SBen Gras 
311d65f6f70SBen Gras static	const enum mdoct rsord[RSORD_MAX] = {
312d65f6f70SBen Gras 	MDOC__A,
313d65f6f70SBen Gras 	MDOC__T,
314d65f6f70SBen Gras 	MDOC__B,
315d65f6f70SBen Gras 	MDOC__I,
316d65f6f70SBen Gras 	MDOC__J,
317d65f6f70SBen Gras 	MDOC__R,
318d65f6f70SBen Gras 	MDOC__N,
319d65f6f70SBen Gras 	MDOC__V,
3200a6a1f1dSLionel Sambuc 	MDOC__U,
321d65f6f70SBen Gras 	MDOC__P,
322d65f6f70SBen Gras 	MDOC__Q,
323d65f6f70SBen Gras 	MDOC__C,
3240a6a1f1dSLionel Sambuc 	MDOC__D,
3250a6a1f1dSLionel Sambuc 	MDOC__O
326d65f6f70SBen Gras };
327d65f6f70SBen Gras 
32892395e9cSLionel Sambuc static	const char * const secnames[SEC__MAX] = {
32992395e9cSLionel Sambuc 	NULL,
33092395e9cSLionel Sambuc 	"NAME",
33192395e9cSLionel Sambuc 	"LIBRARY",
33292395e9cSLionel Sambuc 	"SYNOPSIS",
33392395e9cSLionel Sambuc 	"DESCRIPTION",
33492395e9cSLionel Sambuc 	"IMPLEMENTATION NOTES",
33592395e9cSLionel Sambuc 	"RETURN VALUES",
33692395e9cSLionel Sambuc 	"ENVIRONMENT",
33792395e9cSLionel Sambuc 	"FILES",
33892395e9cSLionel Sambuc 	"EXIT STATUS",
33992395e9cSLionel Sambuc 	"EXAMPLES",
34092395e9cSLionel Sambuc 	"DIAGNOSTICS",
34192395e9cSLionel Sambuc 	"COMPATIBILITY",
34292395e9cSLionel Sambuc 	"ERRORS",
34392395e9cSLionel Sambuc 	"SEE ALSO",
34492395e9cSLionel Sambuc 	"STANDARDS",
34592395e9cSLionel Sambuc 	"HISTORY",
34692395e9cSLionel Sambuc 	"AUTHORS",
34792395e9cSLionel Sambuc 	"CAVEATS",
34892395e9cSLionel Sambuc 	"BUGS",
34992395e9cSLionel Sambuc 	"SECURITY CONSIDERATIONS",
35092395e9cSLionel Sambuc 	NULL
35192395e9cSLionel Sambuc };
352d65f6f70SBen Gras 
353d65f6f70SBen Gras int
mdoc_valid_pre(struct mdoc * mdoc,struct mdoc_node * n)354d65f6f70SBen Gras mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
355d65f6f70SBen Gras {
356d65f6f70SBen Gras 	v_pre		*p;
357d65f6f70SBen Gras 	int		 line, pos;
358d65f6f70SBen Gras 	char		*tp;
359d65f6f70SBen Gras 
360d65f6f70SBen Gras 	switch (n->type) {
361d65f6f70SBen Gras 	case (MDOC_TEXT):
362d65f6f70SBen Gras 		tp = n->string;
363d65f6f70SBen Gras 		line = n->line;
364d65f6f70SBen Gras 		pos = n->pos;
365d65f6f70SBen Gras 		check_text(mdoc, line, pos, tp);
366d65f6f70SBen Gras 		/* FALLTHROUGH */
367d65f6f70SBen Gras 	case (MDOC_TBL):
368d65f6f70SBen Gras 		/* FALLTHROUGH */
36992395e9cSLionel Sambuc 	case (MDOC_EQN):
37092395e9cSLionel Sambuc 		/* FALLTHROUGH */
371d65f6f70SBen Gras 	case (MDOC_ROOT):
372d65f6f70SBen Gras 		return(1);
373d65f6f70SBen Gras 	default:
374d65f6f70SBen Gras 		break;
375d65f6f70SBen Gras 	}
376d65f6f70SBen Gras 
377d65f6f70SBen Gras 	check_args(mdoc, n);
378d65f6f70SBen Gras 
379d65f6f70SBen Gras 	if (NULL == mdoc_valids[n->tok].pre)
380d65f6f70SBen Gras 		return(1);
381d65f6f70SBen Gras 	for (p = mdoc_valids[n->tok].pre; *p; p++)
382d65f6f70SBen Gras 		if ( ! (*p)(mdoc, n))
383d65f6f70SBen Gras 			return(0);
384d65f6f70SBen Gras 	return(1);
385d65f6f70SBen Gras }
386d65f6f70SBen Gras 
387d65f6f70SBen Gras 
388d65f6f70SBen Gras int
mdoc_valid_post(struct mdoc * mdoc)389d65f6f70SBen Gras mdoc_valid_post(struct mdoc *mdoc)
390d65f6f70SBen Gras {
391d65f6f70SBen Gras 	v_post		*p;
392d65f6f70SBen Gras 
393d65f6f70SBen Gras 	if (MDOC_VALID & mdoc->last->flags)
394d65f6f70SBen Gras 		return(1);
395d65f6f70SBen Gras 	mdoc->last->flags |= MDOC_VALID;
396d65f6f70SBen Gras 
397d65f6f70SBen Gras 	switch (mdoc->last->type) {
398d65f6f70SBen Gras 	case (MDOC_TEXT):
399d65f6f70SBen Gras 		/* FALLTHROUGH */
40092395e9cSLionel Sambuc 	case (MDOC_EQN):
40192395e9cSLionel Sambuc 		/* FALLTHROUGH */
402d65f6f70SBen Gras 	case (MDOC_TBL):
403d65f6f70SBen Gras 		return(1);
404d65f6f70SBen Gras 	case (MDOC_ROOT):
405d65f6f70SBen Gras 		return(post_root(mdoc));
406d65f6f70SBen Gras 	default:
407d65f6f70SBen Gras 		break;
408d65f6f70SBen Gras 	}
409d65f6f70SBen Gras 
410d65f6f70SBen Gras 	if (NULL == mdoc_valids[mdoc->last->tok].post)
411d65f6f70SBen Gras 		return(1);
412d65f6f70SBen Gras 	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
413d65f6f70SBen Gras 		if ( ! (*p)(mdoc))
414d65f6f70SBen Gras 			return(0);
415d65f6f70SBen Gras 
416d65f6f70SBen Gras 	return(1);
417d65f6f70SBen Gras }
418d65f6f70SBen Gras 
419d65f6f70SBen Gras static int
check_count(struct mdoc * mdoc,enum mdoc_type type,enum check_lvl lvl,enum check_ineq ineq,int val)4200a6a1f1dSLionel Sambuc check_count(struct mdoc *mdoc, enum mdoc_type type,
421d65f6f70SBen Gras 		enum check_lvl lvl, enum check_ineq ineq, int val)
422d65f6f70SBen Gras {
423d65f6f70SBen Gras 	const char	*p;
424d65f6f70SBen Gras 	enum mandocerr	 t;
425d65f6f70SBen Gras 
4260a6a1f1dSLionel Sambuc 	if (mdoc->last->type != type)
427d65f6f70SBen Gras 		return(1);
428d65f6f70SBen Gras 
429d65f6f70SBen Gras 	switch (ineq) {
430d65f6f70SBen Gras 	case (CHECK_LT):
431d65f6f70SBen Gras 		p = "less than ";
4320a6a1f1dSLionel Sambuc 		if (mdoc->last->nchild < val)
433d65f6f70SBen Gras 			return(1);
434d65f6f70SBen Gras 		break;
435d65f6f70SBen Gras 	case (CHECK_GT):
436d65f6f70SBen Gras 		p = "more than ";
4370a6a1f1dSLionel Sambuc 		if (mdoc->last->nchild > val)
438d65f6f70SBen Gras 			return(1);
439d65f6f70SBen Gras 		break;
440d65f6f70SBen Gras 	case (CHECK_EQ):
441d65f6f70SBen Gras 		p = "";
4420a6a1f1dSLionel Sambuc 		if (val == mdoc->last->nchild)
443d65f6f70SBen Gras 			return(1);
444d65f6f70SBen Gras 		break;
445d65f6f70SBen Gras 	default:
446d65f6f70SBen Gras 		abort();
447d65f6f70SBen Gras 		/* NOTREACHED */
448d65f6f70SBen Gras 	}
449d65f6f70SBen Gras 
450d65f6f70SBen Gras 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
4510a6a1f1dSLionel Sambuc 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line, mdoc->last->pos,
452d65f6f70SBen Gras 			"want %s%d children (have %d)",
4530a6a1f1dSLionel Sambuc 			p, val, mdoc->last->nchild);
45492395e9cSLionel Sambuc 	return(1);
455d65f6f70SBen Gras }
456d65f6f70SBen Gras 
457d65f6f70SBen Gras static int
berr_ge1(POST_ARGS)458d65f6f70SBen Gras berr_ge1(POST_ARGS)
459d65f6f70SBen Gras {
460d65f6f70SBen Gras 
461d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
462d65f6f70SBen Gras }
463d65f6f70SBen Gras 
464d65f6f70SBen Gras static int
bwarn_ge1(POST_ARGS)465d65f6f70SBen Gras bwarn_ge1(POST_ARGS)
466d65f6f70SBen Gras {
467d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
468d65f6f70SBen Gras }
469d65f6f70SBen Gras 
470d65f6f70SBen Gras static int
ewarn_eq0(POST_ARGS)471d65f6f70SBen Gras ewarn_eq0(POST_ARGS)
472d65f6f70SBen Gras {
473d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
474d65f6f70SBen Gras }
475d65f6f70SBen Gras 
476d65f6f70SBen Gras static int
ewarn_eq1(POST_ARGS)477d65f6f70SBen Gras ewarn_eq1(POST_ARGS)
478d65f6f70SBen Gras {
479d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
480d65f6f70SBen Gras }
481d65f6f70SBen Gras 
482d65f6f70SBen Gras static int
ewarn_ge1(POST_ARGS)483d65f6f70SBen Gras ewarn_ge1(POST_ARGS)
484d65f6f70SBen Gras {
485d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
486d65f6f70SBen Gras }
487d65f6f70SBen Gras 
488d65f6f70SBen Gras static int
ewarn_le1(POST_ARGS)489d65f6f70SBen Gras ewarn_le1(POST_ARGS)
490d65f6f70SBen Gras {
491d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
492d65f6f70SBen Gras }
493d65f6f70SBen Gras 
494d65f6f70SBen Gras static int
hwarn_eq0(POST_ARGS)495d65f6f70SBen Gras hwarn_eq0(POST_ARGS)
496d65f6f70SBen Gras {
497d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
498d65f6f70SBen Gras }
499d65f6f70SBen Gras 
500d65f6f70SBen Gras static int
hwarn_eq1(POST_ARGS)501d65f6f70SBen Gras hwarn_eq1(POST_ARGS)
502d65f6f70SBen Gras {
503d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
504d65f6f70SBen Gras }
505d65f6f70SBen Gras 
506d65f6f70SBen Gras static int
hwarn_ge1(POST_ARGS)507d65f6f70SBen Gras hwarn_ge1(POST_ARGS)
508d65f6f70SBen Gras {
509d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
510d65f6f70SBen Gras }
511d65f6f70SBen Gras 
512d65f6f70SBen Gras static int
hwarn_le1(POST_ARGS)513d65f6f70SBen Gras hwarn_le1(POST_ARGS)
514d65f6f70SBen Gras {
515d65f6f70SBen Gras 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
516d65f6f70SBen Gras }
517d65f6f70SBen Gras 
518d65f6f70SBen Gras static void
check_args(struct mdoc * mdoc,struct mdoc_node * n)5190a6a1f1dSLionel Sambuc check_args(struct mdoc *mdoc, struct mdoc_node *n)
520d65f6f70SBen Gras {
521d65f6f70SBen Gras 	int		 i;
522d65f6f70SBen Gras 
523d65f6f70SBen Gras 	if (NULL == n->args)
524d65f6f70SBen Gras 		return;
525d65f6f70SBen Gras 
526d65f6f70SBen Gras 	assert(n->args->argc);
527d65f6f70SBen Gras 	for (i = 0; i < (int)n->args->argc; i++)
5280a6a1f1dSLionel Sambuc 		check_argv(mdoc, n, &n->args->argv[i]);
529d65f6f70SBen Gras }
530d65f6f70SBen Gras 
531d65f6f70SBen Gras static void
check_argv(struct mdoc * mdoc,struct mdoc_node * n,struct mdoc_argv * v)5320a6a1f1dSLionel Sambuc check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
533d65f6f70SBen Gras {
534d65f6f70SBen Gras 	int		 i;
535d65f6f70SBen Gras 
536d65f6f70SBen Gras 	for (i = 0; i < (int)v->sz; i++)
5370a6a1f1dSLionel Sambuc 		check_text(mdoc, v->line, v->pos, v->value[i]);
538d65f6f70SBen Gras 
539d65f6f70SBen Gras 	/* FIXME: move to post_std(). */
540d65f6f70SBen Gras 
541d65f6f70SBen Gras 	if (MDOC_Std == v->arg)
5420a6a1f1dSLionel Sambuc 		if ( ! (v->sz || mdoc->meta.name))
5430a6a1f1dSLionel Sambuc 			mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
544d65f6f70SBen Gras }
545d65f6f70SBen Gras 
546d65f6f70SBen Gras static void
check_text(struct mdoc * mdoc,int ln,int pos,char * p)5470a6a1f1dSLionel Sambuc check_text(struct mdoc *mdoc, int ln, int pos, char *p)
548d65f6f70SBen Gras {
54992395e9cSLionel Sambuc 	char		*cp;
550d65f6f70SBen Gras 
5510a6a1f1dSLionel Sambuc 	if (MDOC_LITERAL & mdoc->flags)
55292395e9cSLionel Sambuc 		return;
553d65f6f70SBen Gras 
55492395e9cSLionel Sambuc 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
5550a6a1f1dSLionel Sambuc 		mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
556d65f6f70SBen Gras }
557d65f6f70SBen Gras 
558d65f6f70SBen Gras static int
check_parent(PRE_ARGS,enum mdoct tok,enum mdoc_type t)559d65f6f70SBen Gras check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
560d65f6f70SBen Gras {
561d65f6f70SBen Gras 
562d65f6f70SBen Gras 	assert(n->parent);
563d65f6f70SBen Gras 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
564d65f6f70SBen Gras 			(t == n->parent->type))
565d65f6f70SBen Gras 		return(1);
566d65f6f70SBen Gras 
56792395e9cSLionel Sambuc 	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
56892395e9cSLionel Sambuc 			n->pos, "want parent %s", MDOC_ROOT == t ?
56992395e9cSLionel Sambuc 			"<root>" : mdoc_macronames[tok]);
570d65f6f70SBen Gras 	return(0);
571d65f6f70SBen Gras }
572d65f6f70SBen Gras 
573d65f6f70SBen Gras 
574d65f6f70SBen Gras static int
pre_display(PRE_ARGS)575d65f6f70SBen Gras pre_display(PRE_ARGS)
576d65f6f70SBen Gras {
577d65f6f70SBen Gras 	struct mdoc_node *node;
578d65f6f70SBen Gras 
579d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type)
580d65f6f70SBen Gras 		return(1);
581d65f6f70SBen Gras 
582d65f6f70SBen Gras 	for (node = mdoc->last->parent; node; node = node->parent)
583d65f6f70SBen Gras 		if (MDOC_BLOCK == node->type)
584d65f6f70SBen Gras 			if (MDOC_Bd == node->tok)
585d65f6f70SBen Gras 				break;
586d65f6f70SBen Gras 
587d65f6f70SBen Gras 	if (node)
588d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
589d65f6f70SBen Gras 
590d65f6f70SBen Gras 	return(1);
591d65f6f70SBen Gras }
592d65f6f70SBen Gras 
593d65f6f70SBen Gras 
594d65f6f70SBen Gras static int
pre_bl(PRE_ARGS)595d65f6f70SBen Gras pre_bl(PRE_ARGS)
596d65f6f70SBen Gras {
597d65f6f70SBen Gras 	int		  i, comp, dup;
598d65f6f70SBen Gras 	const char	 *offs, *width;
599d65f6f70SBen Gras 	enum mdoc_list	  lt;
600*e1cdaee1SLionel Sambuc #if !defined(NDEBUG) && defined(__minix)
601d65f6f70SBen Gras 	struct mdoc_node *np;
602*e1cdaee1SLionel Sambuc #endif /* !defined(NDEBUG) && defined(__minix) */
603d65f6f70SBen Gras 
604d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type) {
605*e1cdaee1SLionel Sambuc #if !defined(NDEBUG) && defined(__minix)
606d65f6f70SBen Gras 		if (ENDBODY_NOT != n->end) {
607d65f6f70SBen Gras 			assert(n->pending);
608d65f6f70SBen Gras 			np = n->pending->parent;
609d65f6f70SBen Gras 		} else
610d65f6f70SBen Gras 			np = n->parent;
611d65f6f70SBen Gras 
612d65f6f70SBen Gras 		assert(np);
613d65f6f70SBen Gras 		assert(MDOC_BLOCK == np->type);
614d65f6f70SBen Gras 		assert(MDOC_Bl == np->tok);
615*e1cdaee1SLionel Sambuc #endif /* !defined(NDEBUG) && defined(__minix) */
616d65f6f70SBen Gras 		return(1);
617d65f6f70SBen Gras 	}
618d65f6f70SBen Gras 
619d65f6f70SBen Gras 	/*
620d65f6f70SBen Gras 	 * First figure out which kind of list to use: bind ourselves to
621d65f6f70SBen Gras 	 * the first mentioned list type and warn about any remaining
622d65f6f70SBen Gras 	 * ones.  If we find no list type, we default to LIST_item.
623d65f6f70SBen Gras 	 */
624d65f6f70SBen Gras 
625d65f6f70SBen Gras 	/* LINTED */
626d65f6f70SBen Gras 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
627d65f6f70SBen Gras 		lt = LIST__NONE;
628d65f6f70SBen Gras 		dup = comp = 0;
629d65f6f70SBen Gras 		width = offs = NULL;
630d65f6f70SBen Gras 		switch (n->args->argv[i].arg) {
631d65f6f70SBen Gras 		/* Set list types. */
632d65f6f70SBen Gras 		case (MDOC_Bullet):
633d65f6f70SBen Gras 			lt = LIST_bullet;
634d65f6f70SBen Gras 			break;
635d65f6f70SBen Gras 		case (MDOC_Dash):
636d65f6f70SBen Gras 			lt = LIST_dash;
637d65f6f70SBen Gras 			break;
638d65f6f70SBen Gras 		case (MDOC_Enum):
639d65f6f70SBen Gras 			lt = LIST_enum;
640d65f6f70SBen Gras 			break;
641d65f6f70SBen Gras 		case (MDOC_Hyphen):
642d65f6f70SBen Gras 			lt = LIST_hyphen;
643d65f6f70SBen Gras 			break;
644d65f6f70SBen Gras 		case (MDOC_Item):
645d65f6f70SBen Gras 			lt = LIST_item;
646d65f6f70SBen Gras 			break;
647d65f6f70SBen Gras 		case (MDOC_Tag):
648d65f6f70SBen Gras 			lt = LIST_tag;
649d65f6f70SBen Gras 			break;
650d65f6f70SBen Gras 		case (MDOC_Diag):
651d65f6f70SBen Gras 			lt = LIST_diag;
652d65f6f70SBen Gras 			break;
653d65f6f70SBen Gras 		case (MDOC_Hang):
654d65f6f70SBen Gras 			lt = LIST_hang;
655d65f6f70SBen Gras 			break;
656d65f6f70SBen Gras 		case (MDOC_Ohang):
657d65f6f70SBen Gras 			lt = LIST_ohang;
658d65f6f70SBen Gras 			break;
659d65f6f70SBen Gras 		case (MDOC_Inset):
660d65f6f70SBen Gras 			lt = LIST_inset;
661d65f6f70SBen Gras 			break;
662d65f6f70SBen Gras 		case (MDOC_Column):
663d65f6f70SBen Gras 			lt = LIST_column;
664d65f6f70SBen Gras 			break;
665d65f6f70SBen Gras 		/* Set list arguments. */
666d65f6f70SBen Gras 		case (MDOC_Compact):
667d65f6f70SBen Gras 			dup = n->norm->Bl.comp;
668d65f6f70SBen Gras 			comp = 1;
669d65f6f70SBen Gras 			break;
670d65f6f70SBen Gras 		case (MDOC_Width):
67184d9c625SLionel Sambuc 			/* NB: this can be empty! */
67284d9c625SLionel Sambuc 			if (n->args->argv[i].sz) {
673d65f6f70SBen Gras 				width = n->args->argv[i].value[0];
67484d9c625SLionel Sambuc 				dup = (NULL != n->norm->Bl.width);
67584d9c625SLionel Sambuc 				break;
67684d9c625SLionel Sambuc 			}
67784d9c625SLionel Sambuc 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
678d65f6f70SBen Gras 			break;
679d65f6f70SBen Gras 		case (MDOC_Offset):
680d65f6f70SBen Gras 			/* NB: this can be empty! */
681d65f6f70SBen Gras 			if (n->args->argv[i].sz) {
682d65f6f70SBen Gras 				offs = n->args->argv[i].value[0];
683d65f6f70SBen Gras 				dup = (NULL != n->norm->Bl.offs);
684d65f6f70SBen Gras 				break;
685d65f6f70SBen Gras 			}
686d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
687d65f6f70SBen Gras 			break;
688d65f6f70SBen Gras 		default:
689d65f6f70SBen Gras 			continue;
690d65f6f70SBen Gras 		}
691d65f6f70SBen Gras 
692d65f6f70SBen Gras 		/* Check: duplicate auxiliary arguments. */
693d65f6f70SBen Gras 
694d65f6f70SBen Gras 		if (dup)
695d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
696d65f6f70SBen Gras 
697d65f6f70SBen Gras 		if (comp && ! dup)
698d65f6f70SBen Gras 			n->norm->Bl.comp = comp;
699d65f6f70SBen Gras 		if (offs && ! dup)
700d65f6f70SBen Gras 			n->norm->Bl.offs = offs;
701d65f6f70SBen Gras 		if (width && ! dup)
702d65f6f70SBen Gras 			n->norm->Bl.width = width;
703d65f6f70SBen Gras 
704d65f6f70SBen Gras 		/* Check: multiple list types. */
705d65f6f70SBen Gras 
706d65f6f70SBen Gras 		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
707d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
708d65f6f70SBen Gras 
709d65f6f70SBen Gras 		/* Assign list type. */
710d65f6f70SBen Gras 
711d65f6f70SBen Gras 		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
712d65f6f70SBen Gras 			n->norm->Bl.type = lt;
713d65f6f70SBen Gras 			/* Set column information, too. */
714d65f6f70SBen Gras 			if (LIST_column == lt) {
715d65f6f70SBen Gras 				n->norm->Bl.ncols =
716d65f6f70SBen Gras 					n->args->argv[i].sz;
71792395e9cSLionel Sambuc 				n->norm->Bl.cols = (void *)
718d65f6f70SBen Gras 					n->args->argv[i].value;
719d65f6f70SBen Gras 			}
720d65f6f70SBen Gras 		}
721d65f6f70SBen Gras 
722d65f6f70SBen Gras 		/* The list type should come first. */
723d65f6f70SBen Gras 
724d65f6f70SBen Gras 		if (n->norm->Bl.type == LIST__NONE)
725d65f6f70SBen Gras 			if (n->norm->Bl.width ||
726d65f6f70SBen Gras 					n->norm->Bl.offs ||
727d65f6f70SBen Gras 					n->norm->Bl.comp)
728d65f6f70SBen Gras 				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
729d65f6f70SBen Gras 
730d65f6f70SBen Gras 		continue;
731d65f6f70SBen Gras 	}
732d65f6f70SBen Gras 
733d65f6f70SBen Gras 	/* Allow lists to default to LIST_item. */
734d65f6f70SBen Gras 
735d65f6f70SBen Gras 	if (LIST__NONE == n->norm->Bl.type) {
736d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
737d65f6f70SBen Gras 		n->norm->Bl.type = LIST_item;
738d65f6f70SBen Gras 	}
739d65f6f70SBen Gras 
740d65f6f70SBen Gras 	/*
741d65f6f70SBen Gras 	 * Validate the width field.  Some list types don't need width
742d65f6f70SBen Gras 	 * types and should be warned about them.  Others should have it
7430a6a1f1dSLionel Sambuc 	 * and must also be warned.  Yet others have a default and need
7440a6a1f1dSLionel Sambuc 	 * no warning.
745d65f6f70SBen Gras 	 */
746d65f6f70SBen Gras 
747d65f6f70SBen Gras 	switch (n->norm->Bl.type) {
748d65f6f70SBen Gras 	case (LIST_tag):
7490a6a1f1dSLionel Sambuc 		if (NULL == n->norm->Bl.width)
750d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
751d65f6f70SBen Gras 		break;
752d65f6f70SBen Gras 	case (LIST_column):
753d65f6f70SBen Gras 		/* FALLTHROUGH */
754d65f6f70SBen Gras 	case (LIST_diag):
755d65f6f70SBen Gras 		/* FALLTHROUGH */
756d65f6f70SBen Gras 	case (LIST_ohang):
757d65f6f70SBen Gras 		/* FALLTHROUGH */
758d65f6f70SBen Gras 	case (LIST_inset):
759d65f6f70SBen Gras 		/* FALLTHROUGH */
760d65f6f70SBen Gras 	case (LIST_item):
761d65f6f70SBen Gras 		if (n->norm->Bl.width)
762d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
763d65f6f70SBen Gras 		break;
7640a6a1f1dSLionel Sambuc 	case (LIST_bullet):
7650a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
7660a6a1f1dSLionel Sambuc 	case (LIST_dash):
7670a6a1f1dSLionel Sambuc 		/* FALLTHROUGH */
7680a6a1f1dSLionel Sambuc 	case (LIST_hyphen):
7690a6a1f1dSLionel Sambuc 		if (NULL == n->norm->Bl.width)
7700a6a1f1dSLionel Sambuc 			n->norm->Bl.width = "2n";
7710a6a1f1dSLionel Sambuc 		break;
7720a6a1f1dSLionel Sambuc 	case (LIST_enum):
7730a6a1f1dSLionel Sambuc 		if (NULL == n->norm->Bl.width)
7740a6a1f1dSLionel Sambuc 			n->norm->Bl.width = "3n";
7750a6a1f1dSLionel Sambuc 		break;
776d65f6f70SBen Gras 	default:
777d65f6f70SBen Gras 		break;
778d65f6f70SBen Gras 	}
779d65f6f70SBen Gras 
780d65f6f70SBen Gras 	return(1);
781d65f6f70SBen Gras }
782d65f6f70SBen Gras 
783d65f6f70SBen Gras 
784d65f6f70SBen Gras static int
pre_bd(PRE_ARGS)785d65f6f70SBen Gras pre_bd(PRE_ARGS)
786d65f6f70SBen Gras {
787d65f6f70SBen Gras 	int		  i, dup, comp;
788d65f6f70SBen Gras 	enum mdoc_disp 	  dt;
789d65f6f70SBen Gras 	const char	 *offs;
790*e1cdaee1SLionel Sambuc #if !defined(NDEBUG) && defined(__minix)
791d65f6f70SBen Gras 	struct mdoc_node *np;
792*e1cdaee1SLionel Sambuc #endif /* !defined(NDEBUG) && defined(__minix) */
793d65f6f70SBen Gras 
794d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type) {
795*e1cdaee1SLionel Sambuc #if !defined(NDEBUG) && defined(__minix)
796d65f6f70SBen Gras 		if (ENDBODY_NOT != n->end) {
797d65f6f70SBen Gras 			assert(n->pending);
798d65f6f70SBen Gras 			np = n->pending->parent;
799d65f6f70SBen Gras 		} else
800d65f6f70SBen Gras 			np = n->parent;
801d65f6f70SBen Gras 
802d65f6f70SBen Gras 		assert(np);
803d65f6f70SBen Gras 		assert(MDOC_BLOCK == np->type);
804d65f6f70SBen Gras 		assert(MDOC_Bd == np->tok);
805*e1cdaee1SLionel Sambuc #endif /* !defined(NDEBUG) && defined(__minix) */
806d65f6f70SBen Gras 		return(1);
807d65f6f70SBen Gras 	}
808d65f6f70SBen Gras 
809d65f6f70SBen Gras 	/* LINTED */
810d65f6f70SBen Gras 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
811d65f6f70SBen Gras 		dt = DISP__NONE;
812d65f6f70SBen Gras 		dup = comp = 0;
813d65f6f70SBen Gras 		offs = NULL;
814d65f6f70SBen Gras 
815d65f6f70SBen Gras 		switch (n->args->argv[i].arg) {
816d65f6f70SBen Gras 		case (MDOC_Centred):
817d65f6f70SBen Gras 			dt = DISP_centred;
818d65f6f70SBen Gras 			break;
819d65f6f70SBen Gras 		case (MDOC_Ragged):
820d65f6f70SBen Gras 			dt = DISP_ragged;
821d65f6f70SBen Gras 			break;
822d65f6f70SBen Gras 		case (MDOC_Unfilled):
823d65f6f70SBen Gras 			dt = DISP_unfilled;
824d65f6f70SBen Gras 			break;
825d65f6f70SBen Gras 		case (MDOC_Filled):
826d65f6f70SBen Gras 			dt = DISP_filled;
827d65f6f70SBen Gras 			break;
828d65f6f70SBen Gras 		case (MDOC_Literal):
829d65f6f70SBen Gras 			dt = DISP_literal;
830d65f6f70SBen Gras 			break;
831d65f6f70SBen Gras 		case (MDOC_File):
832d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
833d65f6f70SBen Gras 			return(0);
834d65f6f70SBen Gras 		case (MDOC_Offset):
835d65f6f70SBen Gras 			/* NB: this can be empty! */
836d65f6f70SBen Gras 			if (n->args->argv[i].sz) {
837d65f6f70SBen Gras 				offs = n->args->argv[i].value[0];
838d65f6f70SBen Gras 				dup = (NULL != n->norm->Bd.offs);
839d65f6f70SBen Gras 				break;
840d65f6f70SBen Gras 			}
841d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
842d65f6f70SBen Gras 			break;
843d65f6f70SBen Gras 		case (MDOC_Compact):
844d65f6f70SBen Gras 			comp = 1;
845d65f6f70SBen Gras 			dup = n->norm->Bd.comp;
846d65f6f70SBen Gras 			break;
847d65f6f70SBen Gras 		default:
848d65f6f70SBen Gras 			abort();
849d65f6f70SBen Gras 			/* NOTREACHED */
850d65f6f70SBen Gras 		}
851d65f6f70SBen Gras 
852d65f6f70SBen Gras 		/* Check whether we have duplicates. */
853d65f6f70SBen Gras 
854d65f6f70SBen Gras 		if (dup)
855d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
856d65f6f70SBen Gras 
857d65f6f70SBen Gras 		/* Make our auxiliary assignments. */
858d65f6f70SBen Gras 
859d65f6f70SBen Gras 		if (offs && ! dup)
860d65f6f70SBen Gras 			n->norm->Bd.offs = offs;
861d65f6f70SBen Gras 		if (comp && ! dup)
862d65f6f70SBen Gras 			n->norm->Bd.comp = comp;
863d65f6f70SBen Gras 
864d65f6f70SBen Gras 		/* Check whether a type has already been assigned. */
865d65f6f70SBen Gras 
866d65f6f70SBen Gras 		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
867d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
868d65f6f70SBen Gras 
869d65f6f70SBen Gras 		/* Make our type assignment. */
870d65f6f70SBen Gras 
871d65f6f70SBen Gras 		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
872d65f6f70SBen Gras 			n->norm->Bd.type = dt;
873d65f6f70SBen Gras 	}
874d65f6f70SBen Gras 
875d65f6f70SBen Gras 	if (DISP__NONE == n->norm->Bd.type) {
876d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
877d65f6f70SBen Gras 		n->norm->Bd.type = DISP_ragged;
878d65f6f70SBen Gras 	}
879d65f6f70SBen Gras 
880d65f6f70SBen Gras 	return(1);
881d65f6f70SBen Gras }
882d65f6f70SBen Gras 
883d65f6f70SBen Gras 
884d65f6f70SBen Gras static int
pre_ss(PRE_ARGS)885d65f6f70SBen Gras pre_ss(PRE_ARGS)
886d65f6f70SBen Gras {
887d65f6f70SBen Gras 
888d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type)
889d65f6f70SBen Gras 		return(1);
890d65f6f70SBen Gras 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
891d65f6f70SBen Gras }
892d65f6f70SBen Gras 
893d65f6f70SBen Gras 
894d65f6f70SBen Gras static int
pre_sh(PRE_ARGS)895d65f6f70SBen Gras pre_sh(PRE_ARGS)
896d65f6f70SBen Gras {
897d65f6f70SBen Gras 
898d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type)
899d65f6f70SBen Gras 		return(1);
900d65f6f70SBen Gras 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
901d65f6f70SBen Gras }
902d65f6f70SBen Gras 
903d65f6f70SBen Gras 
904d65f6f70SBen Gras static int
pre_it(PRE_ARGS)905d65f6f70SBen Gras pre_it(PRE_ARGS)
906d65f6f70SBen Gras {
907d65f6f70SBen Gras 
908d65f6f70SBen Gras 	if (MDOC_BLOCK != n->type)
909d65f6f70SBen Gras 		return(1);
910d65f6f70SBen Gras 
911d65f6f70SBen Gras 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
912d65f6f70SBen Gras }
913d65f6f70SBen Gras 
914d65f6f70SBen Gras 
915d65f6f70SBen Gras static int
pre_an(PRE_ARGS)916d65f6f70SBen Gras pre_an(PRE_ARGS)
917d65f6f70SBen Gras {
918d65f6f70SBen Gras 	int		 i;
919d65f6f70SBen Gras 
920d65f6f70SBen Gras 	if (NULL == n->args)
921d65f6f70SBen Gras 		return(1);
922d65f6f70SBen Gras 
923d65f6f70SBen Gras 	for (i = 1; i < (int)n->args->argc; i++)
924d65f6f70SBen Gras 		mdoc_pmsg(mdoc, n->args->argv[i].line,
925d65f6f70SBen Gras 			n->args->argv[i].pos, MANDOCERR_IGNARGV);
926d65f6f70SBen Gras 
927d65f6f70SBen Gras 	if (MDOC_Split == n->args->argv[0].arg)
928d65f6f70SBen Gras 		n->norm->An.auth = AUTH_split;
929d65f6f70SBen Gras 	else if (MDOC_Nosplit == n->args->argv[0].arg)
930d65f6f70SBen Gras 		n->norm->An.auth = AUTH_nosplit;
931d65f6f70SBen Gras 	else
932d65f6f70SBen Gras 		abort();
933d65f6f70SBen Gras 
934d65f6f70SBen Gras 	return(1);
935d65f6f70SBen Gras }
936d65f6f70SBen Gras 
937d65f6f70SBen Gras static int
pre_std(PRE_ARGS)938d65f6f70SBen Gras pre_std(PRE_ARGS)
939d65f6f70SBen Gras {
940d65f6f70SBen Gras 
941d65f6f70SBen Gras 	if (n->args && 1 == n->args->argc)
942d65f6f70SBen Gras 		if (MDOC_Std == n->args->argv[0].arg)
943d65f6f70SBen Gras 			return(1);
944d65f6f70SBen Gras 
945d65f6f70SBen Gras 	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
946d65f6f70SBen Gras 	return(1);
947d65f6f70SBen Gras }
948d65f6f70SBen Gras 
949d65f6f70SBen Gras static int
pre_dt(PRE_ARGS)950d65f6f70SBen Gras pre_dt(PRE_ARGS)
951d65f6f70SBen Gras {
952d65f6f70SBen Gras 
95392395e9cSLionel Sambuc 	if (NULL == mdoc->meta.date || mdoc->meta.os)
954d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
955d65f6f70SBen Gras 
956d65f6f70SBen Gras 	if (mdoc->meta.title)
957d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
958d65f6f70SBen Gras 
959d65f6f70SBen Gras 	return(1);
960d65f6f70SBen Gras }
961d65f6f70SBen Gras 
962d65f6f70SBen Gras static int
pre_os(PRE_ARGS)963d65f6f70SBen Gras pre_os(PRE_ARGS)
964d65f6f70SBen Gras {
965d65f6f70SBen Gras 
96692395e9cSLionel Sambuc 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
967d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
968d65f6f70SBen Gras 
969d65f6f70SBen Gras 	if (mdoc->meta.os)
970d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
971d65f6f70SBen Gras 
972d65f6f70SBen Gras 	return(1);
973d65f6f70SBen Gras }
974d65f6f70SBen Gras 
975d65f6f70SBen Gras static int
pre_dd(PRE_ARGS)976d65f6f70SBen Gras pre_dd(PRE_ARGS)
977d65f6f70SBen Gras {
978d65f6f70SBen Gras 
979d65f6f70SBen Gras 	if (mdoc->meta.title || mdoc->meta.os)
980d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
981d65f6f70SBen Gras 
982d65f6f70SBen Gras 	if (mdoc->meta.date)
983d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
984d65f6f70SBen Gras 
985d65f6f70SBen Gras 	return(1);
986d65f6f70SBen Gras }
987d65f6f70SBen Gras 
988d65f6f70SBen Gras 
989d65f6f70SBen Gras static int
post_bf(POST_ARGS)990d65f6f70SBen Gras post_bf(POST_ARGS)
991d65f6f70SBen Gras {
992d65f6f70SBen Gras 	struct mdoc_node *np;
993d65f6f70SBen Gras 	enum mdocargt	  arg;
994d65f6f70SBen Gras 
995d65f6f70SBen Gras 	/*
996d65f6f70SBen Gras 	 * Unlike other data pointers, these are "housed" by the HEAD
997d65f6f70SBen Gras 	 * element, which contains the goods.
998d65f6f70SBen Gras 	 */
999d65f6f70SBen Gras 
1000d65f6f70SBen Gras 	if (MDOC_HEAD != mdoc->last->type) {
1001d65f6f70SBen Gras 		if (ENDBODY_NOT != mdoc->last->end) {
1002d65f6f70SBen Gras 			assert(mdoc->last->pending);
1003d65f6f70SBen Gras 			np = mdoc->last->pending->parent->head;
1004d65f6f70SBen Gras 		} else if (MDOC_BLOCK != mdoc->last->type) {
1005d65f6f70SBen Gras 			np = mdoc->last->parent->head;
1006d65f6f70SBen Gras 		} else
1007d65f6f70SBen Gras 			np = mdoc->last->head;
1008d65f6f70SBen Gras 
1009d65f6f70SBen Gras 		assert(np);
1010d65f6f70SBen Gras 		assert(MDOC_HEAD == np->type);
1011d65f6f70SBen Gras 		assert(MDOC_Bf == np->tok);
1012d65f6f70SBen Gras 		return(1);
1013d65f6f70SBen Gras 	}
1014d65f6f70SBen Gras 
1015d65f6f70SBen Gras 	np = mdoc->last;
1016d65f6f70SBen Gras 	assert(MDOC_BLOCK == np->parent->type);
1017d65f6f70SBen Gras 	assert(MDOC_Bf == np->parent->tok);
1018d65f6f70SBen Gras 
1019d65f6f70SBen Gras 	/*
1020d65f6f70SBen Gras 	 * Cannot have both argument and parameter.
1021d65f6f70SBen Gras 	 * If neither is specified, let it through with a warning.
1022d65f6f70SBen Gras 	 */
1023d65f6f70SBen Gras 
1024d65f6f70SBen Gras 	if (np->parent->args && np->child) {
1025d65f6f70SBen Gras 		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1026d65f6f70SBen Gras 		return(0);
1027d65f6f70SBen Gras 	} else if (NULL == np->parent->args && NULL == np->child) {
1028d65f6f70SBen Gras 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1029d65f6f70SBen Gras 		return(1);
1030d65f6f70SBen Gras 	}
1031d65f6f70SBen Gras 
1032d65f6f70SBen Gras 	/* Extract argument into data. */
1033d65f6f70SBen Gras 
1034d65f6f70SBen Gras 	if (np->parent->args) {
1035d65f6f70SBen Gras 		arg = np->parent->args->argv[0].arg;
1036d65f6f70SBen Gras 		if (MDOC_Emphasis == arg)
1037d65f6f70SBen Gras 			np->norm->Bf.font = FONT_Em;
1038d65f6f70SBen Gras 		else if (MDOC_Literal == arg)
1039d65f6f70SBen Gras 			np->norm->Bf.font = FONT_Li;
1040d65f6f70SBen Gras 		else if (MDOC_Symbolic == arg)
1041d65f6f70SBen Gras 			np->norm->Bf.font = FONT_Sy;
1042d65f6f70SBen Gras 		else
1043d65f6f70SBen Gras 			abort();
1044d65f6f70SBen Gras 		return(1);
1045d65f6f70SBen Gras 	}
1046d65f6f70SBen Gras 
1047d65f6f70SBen Gras 	/* Extract parameter into data. */
1048d65f6f70SBen Gras 
1049d65f6f70SBen Gras 	if (0 == strcmp(np->child->string, "Em"))
1050d65f6f70SBen Gras 		np->norm->Bf.font = FONT_Em;
1051d65f6f70SBen Gras 	else if (0 == strcmp(np->child->string, "Li"))
1052d65f6f70SBen Gras 		np->norm->Bf.font = FONT_Li;
1053d65f6f70SBen Gras 	else if (0 == strcmp(np->child->string, "Sy"))
1054d65f6f70SBen Gras 		np->norm->Bf.font = FONT_Sy;
1055d65f6f70SBen Gras 	else
1056d65f6f70SBen Gras 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1057d65f6f70SBen Gras 
1058d65f6f70SBen Gras 	return(1);
1059d65f6f70SBen Gras }
1060d65f6f70SBen Gras 
1061d65f6f70SBen Gras static int
post_lb(POST_ARGS)1062d65f6f70SBen Gras post_lb(POST_ARGS)
1063d65f6f70SBen Gras {
1064d65f6f70SBen Gras 	const char	*p;
1065d65f6f70SBen Gras 	char		*buf;
1066d65f6f70SBen Gras 	size_t		 sz;
1067d65f6f70SBen Gras 
1068d65f6f70SBen Gras 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1069d65f6f70SBen Gras 
1070d65f6f70SBen Gras 	assert(mdoc->last->child);
1071d65f6f70SBen Gras 	assert(MDOC_TEXT == mdoc->last->child->type);
1072d65f6f70SBen Gras 
1073d65f6f70SBen Gras 	p = mdoc_a2lib(mdoc->last->child->string);
1074d65f6f70SBen Gras 
1075d65f6f70SBen Gras 	/* If lookup ok, replace with table value. */
1076d65f6f70SBen Gras 
1077d65f6f70SBen Gras 	if (p) {
1078d65f6f70SBen Gras 		free(mdoc->last->child->string);
1079d65f6f70SBen Gras 		mdoc->last->child->string = mandoc_strdup(p);
1080d65f6f70SBen Gras 		return(1);
1081d65f6f70SBen Gras 	}
1082d65f6f70SBen Gras 
1083d65f6f70SBen Gras 	/* If not, use "library ``xxxx''. */
1084d65f6f70SBen Gras 
1085d65f6f70SBen Gras 	sz = strlen(mdoc->last->child->string) +
1086d65f6f70SBen Gras 		2 + strlen("\\(lqlibrary\\(rq");
1087d65f6f70SBen Gras 	buf = mandoc_malloc(sz);
1088d65f6f70SBen Gras 	snprintf(buf, sz, "library \\(lq%s\\(rq",
1089d65f6f70SBen Gras 			mdoc->last->child->string);
1090d65f6f70SBen Gras 	free(mdoc->last->child->string);
1091d65f6f70SBen Gras 	mdoc->last->child->string = buf;
1092d65f6f70SBen Gras 	return(1);
1093d65f6f70SBen Gras }
1094d65f6f70SBen Gras 
1095d65f6f70SBen Gras static int
post_eoln(POST_ARGS)1096d65f6f70SBen Gras post_eoln(POST_ARGS)
1097d65f6f70SBen Gras {
1098d65f6f70SBen Gras 
1099d65f6f70SBen Gras 	if (mdoc->last->child)
1100d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1101d65f6f70SBen Gras 	return(1);
1102d65f6f70SBen Gras }
1103d65f6f70SBen Gras 
1104d65f6f70SBen Gras 
1105d65f6f70SBen Gras static int
post_vt(POST_ARGS)1106d65f6f70SBen Gras post_vt(POST_ARGS)
1107d65f6f70SBen Gras {
1108d65f6f70SBen Gras 	const struct mdoc_node *n;
1109d65f6f70SBen Gras 
1110d65f6f70SBen Gras 	/*
1111d65f6f70SBen Gras 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
1112d65f6f70SBen Gras 	 * have different syntaxes (yet more context-sensitive
111392395e9cSLionel Sambuc 	 * behaviour).  ELEM types must have a child, which is already
111492395e9cSLionel Sambuc 	 * guaranteed by the in_line parsing routine; BLOCK types,
1115d65f6f70SBen Gras 	 * specifically the BODY, should only have TEXT children.
1116d65f6f70SBen Gras 	 */
1117d65f6f70SBen Gras 
1118d65f6f70SBen Gras 	if (MDOC_BODY != mdoc->last->type)
1119d65f6f70SBen Gras 		return(1);
1120d65f6f70SBen Gras 
1121d65f6f70SBen Gras 	for (n = mdoc->last->child; n; n = n->next)
1122d65f6f70SBen Gras 		if (MDOC_TEXT != n->type)
1123d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1124d65f6f70SBen Gras 
1125d65f6f70SBen Gras 	return(1);
1126d65f6f70SBen Gras }
1127d65f6f70SBen Gras 
1128d65f6f70SBen Gras 
1129d65f6f70SBen Gras static int
post_nm(POST_ARGS)1130d65f6f70SBen Gras post_nm(POST_ARGS)
1131d65f6f70SBen Gras {
1132d65f6f70SBen Gras 	char		 buf[BUFSIZ];
113392395e9cSLionel Sambuc 	int		 c;
1134d65f6f70SBen Gras 
11350a6a1f1dSLionel Sambuc 	if (NULL != mdoc->meta.name)
1136d65f6f70SBen Gras 		return(1);
1137d65f6f70SBen Gras 
11380a6a1f1dSLionel Sambuc 	/* Try to use our children for setting the meta name. */
1139d65f6f70SBen Gras 
11400a6a1f1dSLionel Sambuc 	if (NULL != mdoc->last->child) {
114192395e9cSLionel Sambuc 		buf[0] = '\0';
11420a6a1f1dSLionel Sambuc 		c = concat(buf, mdoc->last->child, BUFSIZ);
11430a6a1f1dSLionel Sambuc 	} else
11440a6a1f1dSLionel Sambuc 		c = 0;
11450a6a1f1dSLionel Sambuc 
11460a6a1f1dSLionel Sambuc 	switch (c) {
11470a6a1f1dSLionel Sambuc 	case (-1):
114892395e9cSLionel Sambuc 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1149d65f6f70SBen Gras 		return(0);
11500a6a1f1dSLionel Sambuc 	case (0):
11510a6a1f1dSLionel Sambuc 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
11520a6a1f1dSLionel Sambuc 		mdoc->meta.name = mandoc_strdup("UNKNOWN");
11530a6a1f1dSLionel Sambuc 		break;
11540a6a1f1dSLionel Sambuc 	default:
1155d65f6f70SBen Gras 		mdoc->meta.name = mandoc_strdup(buf);
11560a6a1f1dSLionel Sambuc 		break;
11570a6a1f1dSLionel Sambuc 	}
1158d65f6f70SBen Gras 	return(1);
1159d65f6f70SBen Gras }
1160d65f6f70SBen Gras 
1161d65f6f70SBen Gras static int
post_literal(POST_ARGS)1162d65f6f70SBen Gras post_literal(POST_ARGS)
1163d65f6f70SBen Gras {
1164d65f6f70SBen Gras 
1165d65f6f70SBen Gras 	/*
1166d65f6f70SBen Gras 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1167d65f6f70SBen Gras 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1168d65f6f70SBen Gras 	 * this in literal mode, but it doesn't hurt to just switch it
1169d65f6f70SBen Gras 	 * off in general since displays can't be nested.
1170d65f6f70SBen Gras 	 */
1171d65f6f70SBen Gras 
1172d65f6f70SBen Gras 	if (MDOC_BODY == mdoc->last->type)
1173d65f6f70SBen Gras 		mdoc->flags &= ~MDOC_LITERAL;
1174d65f6f70SBen Gras 
1175d65f6f70SBen Gras 	return(1);
1176d65f6f70SBen Gras }
1177d65f6f70SBen Gras 
1178d65f6f70SBen Gras static int
post_defaults(POST_ARGS)1179d65f6f70SBen Gras post_defaults(POST_ARGS)
1180d65f6f70SBen Gras {
1181d65f6f70SBen Gras 	struct mdoc_node *nn;
1182d65f6f70SBen Gras 
1183d65f6f70SBen Gras 	/*
1184d65f6f70SBen Gras 	 * The `Ar' defaults to "file ..." if no value is provided as an
1185d65f6f70SBen Gras 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1186d65f6f70SBen Gras 	 * gets an empty string.
1187d65f6f70SBen Gras 	 */
1188d65f6f70SBen Gras 
1189d65f6f70SBen Gras 	if (mdoc->last->child)
1190d65f6f70SBen Gras 		return(1);
1191d65f6f70SBen Gras 
1192d65f6f70SBen Gras 	nn = mdoc->last;
1193d65f6f70SBen Gras 	mdoc->next = MDOC_NEXT_CHILD;
1194d65f6f70SBen Gras 
1195d65f6f70SBen Gras 	switch (nn->tok) {
1196d65f6f70SBen Gras 	case (MDOC_Ar):
1197d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1198d65f6f70SBen Gras 			return(0);
1199d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1200d65f6f70SBen Gras 			return(0);
1201d65f6f70SBen Gras 		break;
1202d65f6f70SBen Gras 	case (MDOC_At):
1203d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1204d65f6f70SBen Gras 			return(0);
1205d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1206d65f6f70SBen Gras 			return(0);
1207d65f6f70SBen Gras 		break;
1208d65f6f70SBen Gras 	case (MDOC_Li):
1209d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1210d65f6f70SBen Gras 			return(0);
1211d65f6f70SBen Gras 		break;
1212d65f6f70SBen Gras 	case (MDOC_Pa):
1213d65f6f70SBen Gras 		/* FALLTHROUGH */
1214d65f6f70SBen Gras 	case (MDOC_Mt):
1215d65f6f70SBen Gras 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1216d65f6f70SBen Gras 			return(0);
1217d65f6f70SBen Gras 		break;
1218d65f6f70SBen Gras 	default:
1219d65f6f70SBen Gras 		abort();
1220d65f6f70SBen Gras 		/* NOTREACHED */
1221d65f6f70SBen Gras 	}
1222d65f6f70SBen Gras 
1223d65f6f70SBen Gras 	mdoc->last = nn;
1224d65f6f70SBen Gras 	return(1);
1225d65f6f70SBen Gras }
1226d65f6f70SBen Gras 
1227d65f6f70SBen Gras static int
post_at(POST_ARGS)1228d65f6f70SBen Gras post_at(POST_ARGS)
1229d65f6f70SBen Gras {
1230d65f6f70SBen Gras 	const char	 *p, *q;
1231d65f6f70SBen Gras 	char		 *buf;
1232d65f6f70SBen Gras 	size_t		  sz;
1233d65f6f70SBen Gras 
1234d65f6f70SBen Gras 	/*
1235d65f6f70SBen Gras 	 * If we have a child, look it up in the standard keys.  If a
1236d65f6f70SBen Gras 	 * key exist, use that instead of the child; if it doesn't,
1237d65f6f70SBen Gras 	 * prefix "AT&T UNIX " to the existing data.
1238d65f6f70SBen Gras 	 */
1239d65f6f70SBen Gras 
1240d65f6f70SBen Gras 	if (NULL == mdoc->last->child)
1241d65f6f70SBen Gras 		return(1);
1242d65f6f70SBen Gras 
1243d65f6f70SBen Gras 	assert(MDOC_TEXT == mdoc->last->child->type);
1244d65f6f70SBen Gras 	p = mdoc_a2att(mdoc->last->child->string);
1245d65f6f70SBen Gras 
1246d65f6f70SBen Gras 	if (p) {
1247d65f6f70SBen Gras 		free(mdoc->last->child->string);
1248d65f6f70SBen Gras 		mdoc->last->child->string = mandoc_strdup(p);
1249d65f6f70SBen Gras 	} else {
1250d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1251d65f6f70SBen Gras 		p = "AT&T UNIX ";
1252d65f6f70SBen Gras 		q = mdoc->last->child->string;
1253d65f6f70SBen Gras 		sz = strlen(p) + strlen(q) + 1;
1254d65f6f70SBen Gras 		buf = mandoc_malloc(sz);
1255d65f6f70SBen Gras 		strlcpy(buf, p, sz);
1256d65f6f70SBen Gras 		strlcat(buf, q, sz);
1257d65f6f70SBen Gras 		free(mdoc->last->child->string);
1258d65f6f70SBen Gras 		mdoc->last->child->string = buf;
1259d65f6f70SBen Gras 	}
1260d65f6f70SBen Gras 
1261d65f6f70SBen Gras 	return(1);
1262d65f6f70SBen Gras }
1263d65f6f70SBen Gras 
1264d65f6f70SBen Gras static int
post_an(POST_ARGS)1265d65f6f70SBen Gras post_an(POST_ARGS)
1266d65f6f70SBen Gras {
1267d65f6f70SBen Gras 	struct mdoc_node *np;
1268d65f6f70SBen Gras 
1269d65f6f70SBen Gras 	np = mdoc->last;
127092395e9cSLionel Sambuc 	if (AUTH__NONE == np->norm->An.auth) {
127192395e9cSLionel Sambuc 		if (0 == np->child)
127292395e9cSLionel Sambuc 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
127392395e9cSLionel Sambuc 	} else if (np->child)
1274d65f6f70SBen Gras 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1275d65f6f70SBen Gras 
1276d65f6f70SBen Gras 	return(1);
1277d65f6f70SBen Gras }
1278d65f6f70SBen Gras 
1279d65f6f70SBen Gras 
1280d65f6f70SBen Gras static int
post_it(POST_ARGS)1281d65f6f70SBen Gras post_it(POST_ARGS)
1282d65f6f70SBen Gras {
128392395e9cSLionel Sambuc 	int		  i, cols;
1284d65f6f70SBen Gras 	enum mdoc_list	  lt;
1285d65f6f70SBen Gras 	struct mdoc_node *n, *c;
1286d65f6f70SBen Gras 	enum mandocerr	  er;
1287d65f6f70SBen Gras 
1288d65f6f70SBen Gras 	if (MDOC_BLOCK != mdoc->last->type)
1289d65f6f70SBen Gras 		return(1);
1290d65f6f70SBen Gras 
1291d65f6f70SBen Gras 	n = mdoc->last->parent->parent;
1292d65f6f70SBen Gras 	lt = n->norm->Bl.type;
1293d65f6f70SBen Gras 
1294d65f6f70SBen Gras 	if (LIST__NONE == lt) {
1295d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1296d65f6f70SBen Gras 		return(1);
1297d65f6f70SBen Gras 	}
1298d65f6f70SBen Gras 
1299d65f6f70SBen Gras 	switch (lt) {
1300d65f6f70SBen Gras 	case (LIST_tag):
1301d65f6f70SBen Gras 		if (mdoc->last->head->child)
1302d65f6f70SBen Gras 			break;
1303d65f6f70SBen Gras 		/* FIXME: give this a dummy value. */
1304d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1305d65f6f70SBen Gras 		break;
1306d65f6f70SBen Gras 	case (LIST_hang):
1307d65f6f70SBen Gras 		/* FALLTHROUGH */
1308d65f6f70SBen Gras 	case (LIST_ohang):
1309d65f6f70SBen Gras 		/* FALLTHROUGH */
1310d65f6f70SBen Gras 	case (LIST_inset):
1311d65f6f70SBen Gras 		/* FALLTHROUGH */
1312d65f6f70SBen Gras 	case (LIST_diag):
1313d65f6f70SBen Gras 		if (NULL == mdoc->last->head->child)
1314d65f6f70SBen Gras 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1315d65f6f70SBen Gras 		break;
1316d65f6f70SBen Gras 	case (LIST_bullet):
1317d65f6f70SBen Gras 		/* FALLTHROUGH */
1318d65f6f70SBen Gras 	case (LIST_dash):
1319d65f6f70SBen Gras 		/* FALLTHROUGH */
1320d65f6f70SBen Gras 	case (LIST_enum):
1321d65f6f70SBen Gras 		/* FALLTHROUGH */
1322d65f6f70SBen Gras 	case (LIST_hyphen):
1323d65f6f70SBen Gras 		if (NULL == mdoc->last->body->child)
1324d65f6f70SBen Gras 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1325d65f6f70SBen Gras 		/* FALLTHROUGH */
1326d65f6f70SBen Gras 	case (LIST_item):
1327d65f6f70SBen Gras 		if (mdoc->last->head->child)
1328d65f6f70SBen Gras 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1329d65f6f70SBen Gras 		break;
1330d65f6f70SBen Gras 	case (LIST_column):
1331d65f6f70SBen Gras 		cols = (int)n->norm->Bl.ncols;
1332d65f6f70SBen Gras 
1333d65f6f70SBen Gras 		assert(NULL == mdoc->last->head->child);
1334d65f6f70SBen Gras 
1335d65f6f70SBen Gras 		if (NULL == mdoc->last->body->child)
1336d65f6f70SBen Gras 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1337d65f6f70SBen Gras 
1338d65f6f70SBen Gras 		for (i = 0, c = mdoc->last->child; c; c = c->next)
1339d65f6f70SBen Gras 			if (MDOC_BODY == c->type)
1340d65f6f70SBen Gras 				i++;
1341d65f6f70SBen Gras 
1342d65f6f70SBen Gras 		if (i < cols)
1343d65f6f70SBen Gras 			er = MANDOCERR_ARGCOUNT;
1344d65f6f70SBen Gras 		else if (i == cols || i == cols + 1)
1345d65f6f70SBen Gras 			break;
1346d65f6f70SBen Gras 		else
1347d65f6f70SBen Gras 			er = MANDOCERR_SYNTARGCOUNT;
1348d65f6f70SBen Gras 
134992395e9cSLionel Sambuc 		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
135092395e9cSLionel Sambuc 				mdoc->last->pos,
1351d65f6f70SBen Gras 				"columns == %d (have %d)", cols, i);
135292395e9cSLionel Sambuc 		return(MANDOCERR_ARGCOUNT == er);
1353d65f6f70SBen Gras 	default:
1354d65f6f70SBen Gras 		break;
1355d65f6f70SBen Gras 	}
1356d65f6f70SBen Gras 
1357d65f6f70SBen Gras 	return(1);
1358d65f6f70SBen Gras }
1359d65f6f70SBen Gras 
1360d65f6f70SBen Gras static int
post_bl_block(POST_ARGS)1361d65f6f70SBen Gras post_bl_block(POST_ARGS)
1362d65f6f70SBen Gras {
13630a6a1f1dSLionel Sambuc 	struct mdoc_node *n, *ni, *nc;
1364d65f6f70SBen Gras 
1365d65f6f70SBen Gras 	/*
1366d65f6f70SBen Gras 	 * These are fairly complicated, so we've broken them into two
1367d65f6f70SBen Gras 	 * functions.  post_bl_block_tag() is called when a -tag is
1368d65f6f70SBen Gras 	 * specified, but no -width (it must be guessed).  The second
1369d65f6f70SBen Gras 	 * when a -width is specified (macro indicators must be
1370d65f6f70SBen Gras 	 * rewritten into real lengths).
1371d65f6f70SBen Gras 	 */
1372d65f6f70SBen Gras 
1373d65f6f70SBen Gras 	n = mdoc->last;
1374d65f6f70SBen Gras 
1375d65f6f70SBen Gras 	if (LIST_tag == n->norm->Bl.type &&
1376d65f6f70SBen Gras 			NULL == n->norm->Bl.width) {
1377d65f6f70SBen Gras 		if ( ! post_bl_block_tag(mdoc))
1378d65f6f70SBen Gras 			return(0);
13790a6a1f1dSLionel Sambuc 		assert(n->norm->Bl.width);
1380d65f6f70SBen Gras 	} else if (NULL != n->norm->Bl.width) {
1381d65f6f70SBen Gras 		if ( ! post_bl_block_width(mdoc))
1382d65f6f70SBen Gras 			return(0);
1383d65f6f70SBen Gras 		assert(n->norm->Bl.width);
13840a6a1f1dSLionel Sambuc 	}
13850a6a1f1dSLionel Sambuc 
13860a6a1f1dSLionel Sambuc 	for (ni = n->body->child; ni; ni = ni->next) {
13870a6a1f1dSLionel Sambuc 		if (NULL == ni->body)
13880a6a1f1dSLionel Sambuc 			continue;
13890a6a1f1dSLionel Sambuc 		nc = ni->body->last;
13900a6a1f1dSLionel Sambuc 		while (NULL != nc) {
13910a6a1f1dSLionel Sambuc 			switch (nc->tok) {
13920a6a1f1dSLionel Sambuc 			case (MDOC_Pp):
13930a6a1f1dSLionel Sambuc 				/* FALLTHROUGH */
13940a6a1f1dSLionel Sambuc 			case (MDOC_Lp):
13950a6a1f1dSLionel Sambuc 				/* FALLTHROUGH */
13960a6a1f1dSLionel Sambuc 			case (MDOC_br):
13970a6a1f1dSLionel Sambuc 				break;
13980a6a1f1dSLionel Sambuc 			default:
13990a6a1f1dSLionel Sambuc 				nc = NULL;
14000a6a1f1dSLionel Sambuc 				continue;
14010a6a1f1dSLionel Sambuc 			}
14020a6a1f1dSLionel Sambuc 			if (NULL == ni->next) {
14030a6a1f1dSLionel Sambuc 				mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
14040a6a1f1dSLionel Sambuc 				if ( ! mdoc_node_relink(mdoc, nc))
14050a6a1f1dSLionel Sambuc 					return(0);
14060a6a1f1dSLionel Sambuc 			} else if (0 == n->norm->Bl.comp &&
14070a6a1f1dSLionel Sambuc 			    LIST_column != n->norm->Bl.type) {
14080a6a1f1dSLionel Sambuc 				mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
14090a6a1f1dSLionel Sambuc 				mdoc_node_delete(mdoc, nc);
14100a6a1f1dSLionel Sambuc 			} else
14110a6a1f1dSLionel Sambuc 				break;
14120a6a1f1dSLionel Sambuc 			nc = ni->body->last;
14130a6a1f1dSLionel Sambuc 		}
14140a6a1f1dSLionel Sambuc 	}
1415d65f6f70SBen Gras 	return(1);
1416d65f6f70SBen Gras }
1417d65f6f70SBen Gras 
1418d65f6f70SBen Gras static int
post_bl_block_width(POST_ARGS)1419d65f6f70SBen Gras post_bl_block_width(POST_ARGS)
1420d65f6f70SBen Gras {
1421d65f6f70SBen Gras 	size_t		  width;
1422d65f6f70SBen Gras 	int		  i;
1423d65f6f70SBen Gras 	enum mdoct	  tok;
1424d65f6f70SBen Gras 	struct mdoc_node *n;
1425d65f6f70SBen Gras 	char		  buf[NUMSIZ];
1426d65f6f70SBen Gras 
1427d65f6f70SBen Gras 	n = mdoc->last;
1428d65f6f70SBen Gras 
1429d65f6f70SBen Gras 	/*
1430d65f6f70SBen Gras 	 * Calculate the real width of a list from the -width string,
1431d65f6f70SBen Gras 	 * which may contain a macro (with a known default width), a
1432d65f6f70SBen Gras 	 * literal string, or a scaling width.
1433d65f6f70SBen Gras 	 *
1434d65f6f70SBen Gras 	 * If the value to -width is a macro, then we re-write it to be
1435d65f6f70SBen Gras 	 * the macro's width as set in share/tmac/mdoc/doc-common.
1436d65f6f70SBen Gras 	 */
1437d65f6f70SBen Gras 
1438d65f6f70SBen Gras 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
1439d65f6f70SBen Gras 		width = 6;
1440d65f6f70SBen Gras 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1441d65f6f70SBen Gras 		return(1);
144292395e9cSLionel Sambuc 	else if (0 == (width = macro2len(tok)))  {
1443d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1444d65f6f70SBen Gras 		return(1);
1445d65f6f70SBen Gras 	}
1446d65f6f70SBen Gras 
1447d65f6f70SBen Gras 	/* The value already exists: free and reallocate it. */
1448d65f6f70SBen Gras 
1449d65f6f70SBen Gras 	assert(n->args);
1450d65f6f70SBen Gras 
1451d65f6f70SBen Gras 	for (i = 0; i < (int)n->args->argc; i++)
1452d65f6f70SBen Gras 		if (MDOC_Width == n->args->argv[i].arg)
1453d65f6f70SBen Gras 			break;
1454d65f6f70SBen Gras 
1455d65f6f70SBen Gras 	assert(i < (int)n->args->argc);
1456d65f6f70SBen Gras 
145792395e9cSLionel Sambuc 	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1458d65f6f70SBen Gras 	free(n->args->argv[i].value[0]);
1459d65f6f70SBen Gras 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1460d65f6f70SBen Gras 
1461d65f6f70SBen Gras 	/* Set our width! */
1462d65f6f70SBen Gras 	n->norm->Bl.width = n->args->argv[i].value[0];
1463d65f6f70SBen Gras 	return(1);
1464d65f6f70SBen Gras }
1465d65f6f70SBen Gras 
1466d65f6f70SBen Gras static int
post_bl_block_tag(POST_ARGS)1467d65f6f70SBen Gras post_bl_block_tag(POST_ARGS)
1468d65f6f70SBen Gras {
1469d65f6f70SBen Gras 	struct mdoc_node *n, *nn;
1470d65f6f70SBen Gras 	size_t		  sz, ssz;
1471d65f6f70SBen Gras 	int		  i;
1472d65f6f70SBen Gras 	char		  buf[NUMSIZ];
1473d65f6f70SBen Gras 
1474d65f6f70SBen Gras 	/*
1475d65f6f70SBen Gras 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1476d65f6f70SBen Gras 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1477d65f6f70SBen Gras 	 * ONLY if the -width argument has NOT been provided.  See
1478d65f6f70SBen Gras 	 * post_bl_block_width() for converting the -width string.
1479d65f6f70SBen Gras 	 */
1480d65f6f70SBen Gras 
1481d65f6f70SBen Gras 	sz = 10;
1482d65f6f70SBen Gras 	n = mdoc->last;
1483d65f6f70SBen Gras 
1484d65f6f70SBen Gras 	for (nn = n->body->child; nn; nn = nn->next) {
1485d65f6f70SBen Gras 		if (MDOC_It != nn->tok)
1486d65f6f70SBen Gras 			continue;
1487d65f6f70SBen Gras 
1488d65f6f70SBen Gras 		assert(MDOC_BLOCK == nn->type);
1489d65f6f70SBen Gras 		nn = nn->head->child;
1490d65f6f70SBen Gras 
1491d65f6f70SBen Gras 		if (nn == NULL)
1492d65f6f70SBen Gras 			break;
1493d65f6f70SBen Gras 
1494d65f6f70SBen Gras 		if (MDOC_TEXT == nn->type) {
1495d65f6f70SBen Gras 			sz = strlen(nn->string) + 1;
1496d65f6f70SBen Gras 			break;
1497d65f6f70SBen Gras 		}
1498d65f6f70SBen Gras 
149992395e9cSLionel Sambuc 		if (0 != (ssz = macro2len(nn->tok)))
1500d65f6f70SBen Gras 			sz = ssz;
1501d65f6f70SBen Gras 
1502d65f6f70SBen Gras 		break;
1503d65f6f70SBen Gras 	}
1504d65f6f70SBen Gras 
1505d65f6f70SBen Gras 	/* Defaults to ten ens. */
1506d65f6f70SBen Gras 
150792395e9cSLionel Sambuc 	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1508d65f6f70SBen Gras 
1509d65f6f70SBen Gras 	/*
1510d65f6f70SBen Gras 	 * We have to dynamically add this to the macro's argument list.
1511d65f6f70SBen Gras 	 * We're guaranteed that a MDOC_Width doesn't already exist.
1512d65f6f70SBen Gras 	 */
1513d65f6f70SBen Gras 
1514d65f6f70SBen Gras 	assert(n->args);
1515d65f6f70SBen Gras 	i = (int)(n->args->argc)++;
1516d65f6f70SBen Gras 
1517d65f6f70SBen Gras 	n->args->argv = mandoc_realloc(n->args->argv,
1518d65f6f70SBen Gras 			n->args->argc * sizeof(struct mdoc_argv));
1519d65f6f70SBen Gras 
1520d65f6f70SBen Gras 	n->args->argv[i].arg = MDOC_Width;
1521d65f6f70SBen Gras 	n->args->argv[i].line = n->line;
1522d65f6f70SBen Gras 	n->args->argv[i].pos = n->pos;
1523d65f6f70SBen Gras 	n->args->argv[i].sz = 1;
1524d65f6f70SBen Gras 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1525d65f6f70SBen Gras 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1526d65f6f70SBen Gras 
1527d65f6f70SBen Gras 	/* Set our width! */
1528d65f6f70SBen Gras 	n->norm->Bl.width = n->args->argv[i].value[0];
1529d65f6f70SBen Gras 	return(1);
1530d65f6f70SBen Gras }
1531d65f6f70SBen Gras 
1532d65f6f70SBen Gras 
1533d65f6f70SBen Gras static int
post_bl_head(POST_ARGS)1534d65f6f70SBen Gras post_bl_head(POST_ARGS)
1535d65f6f70SBen Gras {
1536d65f6f70SBen Gras 	struct mdoc_node *np, *nn, *nnp;
1537d65f6f70SBen Gras 	int		  i, j;
1538d65f6f70SBen Gras 
1539d65f6f70SBen Gras 	if (LIST_column != mdoc->last->norm->Bl.type)
1540d65f6f70SBen Gras 		/* FIXME: this should be ERROR class... */
1541d65f6f70SBen Gras 		return(hwarn_eq0(mdoc));
1542d65f6f70SBen Gras 
1543d65f6f70SBen Gras 	/*
1544d65f6f70SBen Gras 	 * Convert old-style lists, where the column width specifiers
1545d65f6f70SBen Gras 	 * trail as macro parameters, to the new-style ("normal-form")
1546d65f6f70SBen Gras 	 * lists where they're argument values following -column.
1547d65f6f70SBen Gras 	 */
1548d65f6f70SBen Gras 
1549d65f6f70SBen Gras 	/* First, disallow both types and allow normal-form. */
1550d65f6f70SBen Gras 
1551d65f6f70SBen Gras 	/*
1552d65f6f70SBen Gras 	 * TODO: technically, we can accept both and just merge the two
1553d65f6f70SBen Gras 	 * lists, but I'll leave that for another day.
1554d65f6f70SBen Gras 	 */
1555d65f6f70SBen Gras 
1556d65f6f70SBen Gras 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1557d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1558d65f6f70SBen Gras 		return(0);
1559d65f6f70SBen Gras 	} else if (NULL == mdoc->last->child)
1560d65f6f70SBen Gras 		return(1);
1561d65f6f70SBen Gras 
1562d65f6f70SBen Gras 	np = mdoc->last->parent;
1563d65f6f70SBen Gras 	assert(np->args);
1564d65f6f70SBen Gras 
1565d65f6f70SBen Gras 	for (j = 0; j < (int)np->args->argc; j++)
1566d65f6f70SBen Gras 		if (MDOC_Column == np->args->argv[j].arg)
1567d65f6f70SBen Gras 			break;
1568d65f6f70SBen Gras 
1569d65f6f70SBen Gras 	assert(j < (int)np->args->argc);
1570d65f6f70SBen Gras 	assert(0 == np->args->argv[j].sz);
1571d65f6f70SBen Gras 
1572d65f6f70SBen Gras 	/*
157392395e9cSLionel Sambuc 	 * Accommodate for new-style groff column syntax.  Shuffle the
1574d65f6f70SBen Gras 	 * child nodes, all of which must be TEXT, as arguments for the
1575d65f6f70SBen Gras 	 * column field.  Then, delete the head children.
1576d65f6f70SBen Gras 	 */
1577d65f6f70SBen Gras 
1578d65f6f70SBen Gras 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1579d65f6f70SBen Gras 	np->args->argv[j].value = mandoc_malloc
1580d65f6f70SBen Gras 		((size_t)mdoc->last->nchild * sizeof(char *));
1581d65f6f70SBen Gras 
1582d65f6f70SBen Gras 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
158392395e9cSLionel Sambuc 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1584d65f6f70SBen Gras 
1585d65f6f70SBen Gras 	for (i = 0, nn = mdoc->last->child; nn; i++) {
1586d65f6f70SBen Gras 		np->args->argv[j].value[i] = nn->string;
1587d65f6f70SBen Gras 		nn->string = NULL;
1588d65f6f70SBen Gras 		nnp = nn;
1589d65f6f70SBen Gras 		nn = nn->next;
1590d65f6f70SBen Gras 		mdoc_node_delete(NULL, nnp);
1591d65f6f70SBen Gras 	}
1592d65f6f70SBen Gras 
1593d65f6f70SBen Gras 	mdoc->last->nchild = 0;
1594d65f6f70SBen Gras 	mdoc->last->child = NULL;
1595d65f6f70SBen Gras 
1596d65f6f70SBen Gras 	return(1);
1597d65f6f70SBen Gras }
1598d65f6f70SBen Gras 
1599d65f6f70SBen Gras static int
post_bl(POST_ARGS)1600d65f6f70SBen Gras post_bl(POST_ARGS)
1601d65f6f70SBen Gras {
16020a6a1f1dSLionel Sambuc 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
16030a6a1f1dSLionel Sambuc 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
16040a6a1f1dSLionel Sambuc 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1605d65f6f70SBen Gras 
16060a6a1f1dSLionel Sambuc 	nbody = mdoc->last;
16070a6a1f1dSLionel Sambuc 	switch (nbody->type) {
16080a6a1f1dSLionel Sambuc 	case (MDOC_BLOCK):
1609d65f6f70SBen Gras 		return(post_bl_block(mdoc));
16100a6a1f1dSLionel Sambuc 	case (MDOC_HEAD):
16110a6a1f1dSLionel Sambuc 		return(post_bl_head(mdoc));
16120a6a1f1dSLionel Sambuc 	case (MDOC_BODY):
1613d65f6f70SBen Gras 		break;
16140a6a1f1dSLionel Sambuc 	default:
16150a6a1f1dSLionel Sambuc 		return(1);
1616d65f6f70SBen Gras 	}
1617d65f6f70SBen Gras 
16180a6a1f1dSLionel Sambuc 	nchild = nbody->child;
16190a6a1f1dSLionel Sambuc 	while (NULL != nchild) {
16200a6a1f1dSLionel Sambuc 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
16210a6a1f1dSLionel Sambuc 			nchild = nchild->next;
16220a6a1f1dSLionel Sambuc 			continue;
16230a6a1f1dSLionel Sambuc 		}
16240a6a1f1dSLionel Sambuc 
16250a6a1f1dSLionel Sambuc 		mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
16260a6a1f1dSLionel Sambuc 
16270a6a1f1dSLionel Sambuc 		/*
16280a6a1f1dSLionel Sambuc 		 * Move the node out of the Bl block.
16290a6a1f1dSLionel Sambuc 		 * First, collect all required node pointers.
16300a6a1f1dSLionel Sambuc 		 */
16310a6a1f1dSLionel Sambuc 
16320a6a1f1dSLionel Sambuc 		nblock  = nbody->parent;
16330a6a1f1dSLionel Sambuc 		nprev   = nblock->prev;
16340a6a1f1dSLionel Sambuc 		nparent = nblock->parent;
16350a6a1f1dSLionel Sambuc 		nnext   = nchild->next;
16360a6a1f1dSLionel Sambuc 
16370a6a1f1dSLionel Sambuc 		/*
16380a6a1f1dSLionel Sambuc 		 * Unlink this child.
16390a6a1f1dSLionel Sambuc 		 */
16400a6a1f1dSLionel Sambuc 
16410a6a1f1dSLionel Sambuc 		assert(NULL == nchild->prev);
16420a6a1f1dSLionel Sambuc 		if (0 == --nbody->nchild) {
16430a6a1f1dSLionel Sambuc 			nbody->child = NULL;
16440a6a1f1dSLionel Sambuc 			nbody->last  = NULL;
16450a6a1f1dSLionel Sambuc 			assert(NULL == nnext);
16460a6a1f1dSLionel Sambuc 		} else {
16470a6a1f1dSLionel Sambuc 			nbody->child = nnext;
16480a6a1f1dSLionel Sambuc 			nnext->prev = NULL;
16490a6a1f1dSLionel Sambuc 		}
16500a6a1f1dSLionel Sambuc 
16510a6a1f1dSLionel Sambuc 		/*
16520a6a1f1dSLionel Sambuc 		 * Relink this child.
16530a6a1f1dSLionel Sambuc 		 */
16540a6a1f1dSLionel Sambuc 
16550a6a1f1dSLionel Sambuc 		nchild->parent = nparent;
16560a6a1f1dSLionel Sambuc 		nchild->prev   = nprev;
16570a6a1f1dSLionel Sambuc 		nchild->next   = nblock;
16580a6a1f1dSLionel Sambuc 
16590a6a1f1dSLionel Sambuc 		nblock->prev = nchild;
16600a6a1f1dSLionel Sambuc 		nparent->nchild++;
16610a6a1f1dSLionel Sambuc 		if (NULL == nprev)
16620a6a1f1dSLionel Sambuc 			nparent->child = nchild;
16630a6a1f1dSLionel Sambuc 		else
16640a6a1f1dSLionel Sambuc 			nprev->next = nchild;
16650a6a1f1dSLionel Sambuc 
16660a6a1f1dSLionel Sambuc 		nchild = nnext;
1667d65f6f70SBen Gras 	}
1668d65f6f70SBen Gras 
1669d65f6f70SBen Gras 	return(1);
1670d65f6f70SBen Gras }
1671d65f6f70SBen Gras 
1672d65f6f70SBen Gras static int
ebool(struct mdoc * mdoc)1673d65f6f70SBen Gras ebool(struct mdoc *mdoc)
1674d65f6f70SBen Gras {
1675d65f6f70SBen Gras 
1676d65f6f70SBen Gras 	if (NULL == mdoc->last->child) {
1677d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1678d65f6f70SBen Gras 		mdoc_node_delete(mdoc, mdoc->last);
1679d65f6f70SBen Gras 		return(1);
1680d65f6f70SBen Gras 	}
1681d65f6f70SBen Gras 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1682d65f6f70SBen Gras 
1683d65f6f70SBen Gras 	assert(MDOC_TEXT == mdoc->last->child->type);
1684d65f6f70SBen Gras 
16850a6a1f1dSLionel Sambuc 	if (0 == strcmp(mdoc->last->child->string, "on")) {
16860a6a1f1dSLionel Sambuc 		if (MDOC_Sm == mdoc->last->tok)
16870a6a1f1dSLionel Sambuc 			mdoc->flags &= ~MDOC_SMOFF;
1688d65f6f70SBen Gras 		return(1);
16890a6a1f1dSLionel Sambuc 	}
16900a6a1f1dSLionel Sambuc 	if (0 == strcmp(mdoc->last->child->string, "off")) {
16910a6a1f1dSLionel Sambuc 		if (MDOC_Sm == mdoc->last->tok)
16920a6a1f1dSLionel Sambuc 			mdoc->flags |= MDOC_SMOFF;
1693d65f6f70SBen Gras 		return(1);
16940a6a1f1dSLionel Sambuc 	}
1695d65f6f70SBen Gras 
1696d65f6f70SBen Gras 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1697d65f6f70SBen Gras 	return(1);
1698d65f6f70SBen Gras }
1699d65f6f70SBen Gras 
1700d65f6f70SBen Gras static int
post_root(POST_ARGS)1701d65f6f70SBen Gras post_root(POST_ARGS)
1702d65f6f70SBen Gras {
1703d65f6f70SBen Gras 	int		  erc;
1704d65f6f70SBen Gras 	struct mdoc_node *n;
1705d65f6f70SBen Gras 
1706d65f6f70SBen Gras 	erc = 0;
1707d65f6f70SBen Gras 
1708d65f6f70SBen Gras 	/* Check that we have a finished prologue. */
1709d65f6f70SBen Gras 
1710d65f6f70SBen Gras 	if ( ! (MDOC_PBODY & mdoc->flags)) {
1711d65f6f70SBen Gras 		erc++;
1712d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1713d65f6f70SBen Gras 	}
1714d65f6f70SBen Gras 
1715d65f6f70SBen Gras 	n = mdoc->first;
1716d65f6f70SBen Gras 	assert(n);
1717d65f6f70SBen Gras 
1718d65f6f70SBen Gras 	/* Check that we begin with a proper `Sh'. */
1719d65f6f70SBen Gras 
1720d65f6f70SBen Gras 	if (NULL == n->child) {
1721d65f6f70SBen Gras 		erc++;
1722d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1723d65f6f70SBen Gras 	} else if (MDOC_BLOCK != n->child->type ||
1724d65f6f70SBen Gras 			MDOC_Sh != n->child->tok) {
1725d65f6f70SBen Gras 		erc++;
1726d65f6f70SBen Gras 		/* Can this be lifted?  See rxdebug.1 for example. */
1727d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1728d65f6f70SBen Gras 	}
1729d65f6f70SBen Gras 
1730d65f6f70SBen Gras 	return(erc ? 0 : 1);
1731d65f6f70SBen Gras }
1732d65f6f70SBen Gras 
1733d65f6f70SBen Gras static int
post_st(POST_ARGS)1734d65f6f70SBen Gras post_st(POST_ARGS)
1735d65f6f70SBen Gras {
1736d65f6f70SBen Gras 	struct mdoc_node	 *ch;
1737d65f6f70SBen Gras 	const char		 *p;
1738d65f6f70SBen Gras 
1739d65f6f70SBen Gras 	if (NULL == (ch = mdoc->last->child)) {
1740d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1741d65f6f70SBen Gras 		mdoc_node_delete(mdoc, mdoc->last);
1742d65f6f70SBen Gras 		return(1);
1743d65f6f70SBen Gras 	}
1744d65f6f70SBen Gras 
1745d65f6f70SBen Gras 	assert(MDOC_TEXT == ch->type);
1746d65f6f70SBen Gras 
1747d65f6f70SBen Gras 	if (NULL == (p = mdoc_a2st(ch->string))) {
1748d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1749d65f6f70SBen Gras 		mdoc_node_delete(mdoc, mdoc->last);
1750d65f6f70SBen Gras 	} else {
1751d65f6f70SBen Gras 		free(ch->string);
1752d65f6f70SBen Gras 		ch->string = mandoc_strdup(p);
1753d65f6f70SBen Gras 	}
1754d65f6f70SBen Gras 
1755d65f6f70SBen Gras 	return(1);
1756d65f6f70SBen Gras }
1757d65f6f70SBen Gras 
1758d65f6f70SBen Gras static int
post_rs(POST_ARGS)1759d65f6f70SBen Gras post_rs(POST_ARGS)
1760d65f6f70SBen Gras {
1761d65f6f70SBen Gras 	struct mdoc_node *nn, *next, *prev;
1762d65f6f70SBen Gras 	int		  i, j;
1763d65f6f70SBen Gras 
1764d65f6f70SBen Gras 	switch (mdoc->last->type) {
1765d65f6f70SBen Gras 	case (MDOC_HEAD):
1766d65f6f70SBen Gras 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1767d65f6f70SBen Gras 		return(1);
1768d65f6f70SBen Gras 	case (MDOC_BODY):
1769d65f6f70SBen Gras 		if (mdoc->last->child)
1770d65f6f70SBen Gras 			break;
1771d65f6f70SBen Gras 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1772d65f6f70SBen Gras 		return(1);
1773d65f6f70SBen Gras 	default:
1774d65f6f70SBen Gras 		return(1);
1775d65f6f70SBen Gras 	}
1776d65f6f70SBen Gras 
1777d65f6f70SBen Gras 	/*
1778d65f6f70SBen Gras 	 * Make sure only certain types of nodes are allowed within the
1779d65f6f70SBen Gras 	 * the `Rs' body.  Delete offending nodes and raise a warning.
1780d65f6f70SBen Gras 	 * Do this before re-ordering for the sake of clarity.
1781d65f6f70SBen Gras 	 */
1782d65f6f70SBen Gras 
1783d65f6f70SBen Gras 	next = NULL;
1784d65f6f70SBen Gras 	for (nn = mdoc->last->child; nn; nn = next) {
1785d65f6f70SBen Gras 		for (i = 0; i < RSORD_MAX; i++)
1786d65f6f70SBen Gras 			if (nn->tok == rsord[i])
1787d65f6f70SBen Gras 				break;
1788d65f6f70SBen Gras 
1789d65f6f70SBen Gras 		if (i < RSORD_MAX) {
179092395e9cSLionel Sambuc 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
179192395e9cSLionel Sambuc 				mdoc->last->norm->Rs.quote_T++;
1792d65f6f70SBen Gras 			next = nn->next;
1793d65f6f70SBen Gras 			continue;
1794d65f6f70SBen Gras 		}
1795d65f6f70SBen Gras 
1796d65f6f70SBen Gras 		next = nn->next;
1797d65f6f70SBen Gras 		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1798d65f6f70SBen Gras 		mdoc_node_delete(mdoc, nn);
1799d65f6f70SBen Gras 	}
1800d65f6f70SBen Gras 
1801d65f6f70SBen Gras 	/*
180292395e9cSLionel Sambuc 	 * Nothing to sort if only invalid nodes were found
180392395e9cSLionel Sambuc 	 * inside the `Rs' body.
180492395e9cSLionel Sambuc 	 */
180592395e9cSLionel Sambuc 
180692395e9cSLionel Sambuc 	if (NULL == mdoc->last->child)
180792395e9cSLionel Sambuc 		return(1);
180892395e9cSLionel Sambuc 
180992395e9cSLionel Sambuc 	/*
1810d65f6f70SBen Gras 	 * The full `Rs' block needs special handling to order the
1811d65f6f70SBen Gras 	 * sub-elements according to `rsord'.  Pick through each element
1812d65f6f70SBen Gras 	 * and correctly order it.  This is a insertion sort.
1813d65f6f70SBen Gras 	 */
1814d65f6f70SBen Gras 
1815d65f6f70SBen Gras 	next = NULL;
1816d65f6f70SBen Gras 	for (nn = mdoc->last->child->next; nn; nn = next) {
1817d65f6f70SBen Gras 		/* Determine order of `nn'. */
1818d65f6f70SBen Gras 		for (i = 0; i < RSORD_MAX; i++)
1819d65f6f70SBen Gras 			if (rsord[i] == nn->tok)
1820d65f6f70SBen Gras 				break;
1821d65f6f70SBen Gras 
1822d65f6f70SBen Gras 		/*
1823d65f6f70SBen Gras 		 * Remove `nn' from the chain.  This somewhat
1824d65f6f70SBen Gras 		 * repeats mdoc_node_unlink(), but since we're
1825d65f6f70SBen Gras 		 * just re-ordering, there's no need for the
1826d65f6f70SBen Gras 		 * full unlink process.
1827d65f6f70SBen Gras 		 */
1828d65f6f70SBen Gras 
1829d65f6f70SBen Gras 		if (NULL != (next = nn->next))
1830d65f6f70SBen Gras 			next->prev = nn->prev;
1831d65f6f70SBen Gras 
1832d65f6f70SBen Gras 		if (NULL != (prev = nn->prev))
1833d65f6f70SBen Gras 			prev->next = nn->next;
1834d65f6f70SBen Gras 
1835d65f6f70SBen Gras 		nn->prev = nn->next = NULL;
1836d65f6f70SBen Gras 
1837d65f6f70SBen Gras 		/*
1838d65f6f70SBen Gras 		 * Scan back until we reach a node that's
1839d65f6f70SBen Gras 		 * ordered before `nn'.
1840d65f6f70SBen Gras 		 */
1841d65f6f70SBen Gras 
1842d65f6f70SBen Gras 		for ( ; prev ; prev = prev->prev) {
1843d65f6f70SBen Gras 			/* Determine order of `prev'. */
1844d65f6f70SBen Gras 			for (j = 0; j < RSORD_MAX; j++)
1845d65f6f70SBen Gras 				if (rsord[j] == prev->tok)
1846d65f6f70SBen Gras 					break;
1847d65f6f70SBen Gras 
1848d65f6f70SBen Gras 			if (j <= i)
1849d65f6f70SBen Gras 				break;
1850d65f6f70SBen Gras 		}
1851d65f6f70SBen Gras 
1852d65f6f70SBen Gras 		/*
1853d65f6f70SBen Gras 		 * Set `nn' back into its correct place in front
1854d65f6f70SBen Gras 		 * of the `prev' node.
1855d65f6f70SBen Gras 		 */
1856d65f6f70SBen Gras 
1857d65f6f70SBen Gras 		nn->prev = prev;
1858d65f6f70SBen Gras 
1859d65f6f70SBen Gras 		if (prev) {
1860d65f6f70SBen Gras 			if (prev->next)
1861d65f6f70SBen Gras 				prev->next->prev = nn;
1862d65f6f70SBen Gras 			nn->next = prev->next;
1863d65f6f70SBen Gras 			prev->next = nn;
1864d65f6f70SBen Gras 		} else {
1865d65f6f70SBen Gras 			mdoc->last->child->prev = nn;
1866d65f6f70SBen Gras 			nn->next = mdoc->last->child;
1867d65f6f70SBen Gras 			mdoc->last->child = nn;
1868d65f6f70SBen Gras 		}
1869d65f6f70SBen Gras 	}
1870d65f6f70SBen Gras 
1871d65f6f70SBen Gras 	return(1);
1872d65f6f70SBen Gras }
1873d65f6f70SBen Gras 
18740a6a1f1dSLionel Sambuc /*
18750a6a1f1dSLionel Sambuc  * For some arguments of some macros,
18760a6a1f1dSLionel Sambuc  * convert all breakable hyphens into ASCII_HYPH.
18770a6a1f1dSLionel Sambuc  */
18780a6a1f1dSLionel Sambuc static int
post_hyph(POST_ARGS)18790a6a1f1dSLionel Sambuc post_hyph(POST_ARGS)
18800a6a1f1dSLionel Sambuc {
18810a6a1f1dSLionel Sambuc 	struct mdoc_node	*n, *nch;
18820a6a1f1dSLionel Sambuc 	char			*cp;
18830a6a1f1dSLionel Sambuc 
18840a6a1f1dSLionel Sambuc 	n = mdoc->last;
18850a6a1f1dSLionel Sambuc 	switch (n->type) {
18860a6a1f1dSLionel Sambuc 	case (MDOC_HEAD):
18870a6a1f1dSLionel Sambuc 		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
18880a6a1f1dSLionel Sambuc 			break;
18890a6a1f1dSLionel Sambuc 		return(1);
18900a6a1f1dSLionel Sambuc 	case (MDOC_BODY):
18910a6a1f1dSLionel Sambuc 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
18920a6a1f1dSLionel Sambuc 			break;
18930a6a1f1dSLionel Sambuc 		return(1);
18940a6a1f1dSLionel Sambuc 	case (MDOC_ELEM):
18950a6a1f1dSLionel Sambuc 		break;
18960a6a1f1dSLionel Sambuc 	default:
18970a6a1f1dSLionel Sambuc 		return(1);
18980a6a1f1dSLionel Sambuc 	}
18990a6a1f1dSLionel Sambuc 
19000a6a1f1dSLionel Sambuc 	for (nch = n->child; nch; nch = nch->next) {
19010a6a1f1dSLionel Sambuc 		if (MDOC_TEXT != nch->type)
19020a6a1f1dSLionel Sambuc 			continue;
19030a6a1f1dSLionel Sambuc 		cp = nch->string;
19040a6a1f1dSLionel Sambuc 		if (3 > strnlen(cp, 3))
19050a6a1f1dSLionel Sambuc 			continue;
19060a6a1f1dSLionel Sambuc 		while ('\0' != *(++cp))
19070a6a1f1dSLionel Sambuc 			if ('-' == *cp &&
19080a6a1f1dSLionel Sambuc 			    isalpha((unsigned char)cp[-1]) &&
19090a6a1f1dSLionel Sambuc 			    isalpha((unsigned char)cp[1]))
19100a6a1f1dSLionel Sambuc 				*cp = ASCII_HYPH;
19110a6a1f1dSLionel Sambuc 	}
19120a6a1f1dSLionel Sambuc 	return(1);
19130a6a1f1dSLionel Sambuc }
19140a6a1f1dSLionel Sambuc 
1915d65f6f70SBen Gras static int
post_ns(POST_ARGS)191692395e9cSLionel Sambuc post_ns(POST_ARGS)
191792395e9cSLionel Sambuc {
191892395e9cSLionel Sambuc 
191992395e9cSLionel Sambuc 	if (MDOC_LINE & mdoc->last->flags)
192092395e9cSLionel Sambuc 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
192192395e9cSLionel Sambuc 	return(1);
192292395e9cSLionel Sambuc }
192392395e9cSLionel Sambuc 
192492395e9cSLionel Sambuc static int
post_sh(POST_ARGS)1925d65f6f70SBen Gras post_sh(POST_ARGS)
1926d65f6f70SBen Gras {
1927d65f6f70SBen Gras 
1928d65f6f70SBen Gras 	if (MDOC_HEAD == mdoc->last->type)
1929d65f6f70SBen Gras 		return(post_sh_head(mdoc));
1930d65f6f70SBen Gras 	if (MDOC_BODY == mdoc->last->type)
1931d65f6f70SBen Gras 		return(post_sh_body(mdoc));
1932d65f6f70SBen Gras 
1933d65f6f70SBen Gras 	return(1);
1934d65f6f70SBen Gras }
1935d65f6f70SBen Gras 
1936d65f6f70SBen Gras static int
post_sh_body(POST_ARGS)1937d65f6f70SBen Gras post_sh_body(POST_ARGS)
1938d65f6f70SBen Gras {
1939d65f6f70SBen Gras 	struct mdoc_node *n;
1940d65f6f70SBen Gras 
1941d65f6f70SBen Gras 	if (SEC_NAME != mdoc->lastsec)
1942d65f6f70SBen Gras 		return(1);
1943d65f6f70SBen Gras 
1944d65f6f70SBen Gras 	/*
1945d65f6f70SBen Gras 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1946d65f6f70SBen Gras 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1947d65f6f70SBen Gras 	 * children of the BODY declaration can also be "text".
1948d65f6f70SBen Gras 	 */
1949d65f6f70SBen Gras 
1950d65f6f70SBen Gras 	if (NULL == (n = mdoc->last->child)) {
1951d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1952d65f6f70SBen Gras 		return(1);
1953d65f6f70SBen Gras 	}
1954d65f6f70SBen Gras 
1955d65f6f70SBen Gras 	for ( ; n && n->next; n = n->next) {
1956d65f6f70SBen Gras 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1957d65f6f70SBen Gras 			continue;
1958d65f6f70SBen Gras 		if (MDOC_TEXT == n->type)
1959d65f6f70SBen Gras 			continue;
1960d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1961d65f6f70SBen Gras 	}
1962d65f6f70SBen Gras 
1963d65f6f70SBen Gras 	assert(n);
1964d65f6f70SBen Gras 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1965d65f6f70SBen Gras 		return(1);
1966d65f6f70SBen Gras 
1967d65f6f70SBen Gras 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1968d65f6f70SBen Gras 	return(1);
1969d65f6f70SBen Gras }
1970d65f6f70SBen Gras 
1971d65f6f70SBen Gras static int
post_sh_head(POST_ARGS)1972d65f6f70SBen Gras post_sh_head(POST_ARGS)
1973d65f6f70SBen Gras {
1974d65f6f70SBen Gras 	char		 buf[BUFSIZ];
197592395e9cSLionel Sambuc 	struct mdoc_node *n;
1976d65f6f70SBen Gras 	enum mdoc_sec	 sec;
197792395e9cSLionel Sambuc 	int		 c;
1978d65f6f70SBen Gras 
1979d65f6f70SBen Gras 	/*
1980d65f6f70SBen Gras 	 * Process a new section.  Sections are either "named" or
1981d65f6f70SBen Gras 	 * "custom".  Custom sections are user-defined, while named ones
1982d65f6f70SBen Gras 	 * follow a conventional order and may only appear in certain
1983d65f6f70SBen Gras 	 * manual sections.
1984d65f6f70SBen Gras 	 */
1985d65f6f70SBen Gras 
198692395e9cSLionel Sambuc 	sec = SEC_CUSTOM;
198792395e9cSLionel Sambuc 	buf[0] = '\0';
198892395e9cSLionel Sambuc 	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
198992395e9cSLionel Sambuc 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1990d65f6f70SBen Gras 		return(0);
199192395e9cSLionel Sambuc 	} else if (1 == c)
199292395e9cSLionel Sambuc 		sec = a2sec(buf);
1993d65f6f70SBen Gras 
1994d65f6f70SBen Gras 	/* The NAME should be first. */
1995d65f6f70SBen Gras 
1996d65f6f70SBen Gras 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1997d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1998d65f6f70SBen Gras 
1999d65f6f70SBen Gras 	/* The SYNOPSIS gets special attention in other areas. */
2000d65f6f70SBen Gras 
20010a6a1f1dSLionel Sambuc 	if (SEC_SYNOPSIS == sec) {
20020a6a1f1dSLionel Sambuc 		roff_setreg(mdoc->roff, "nS", 1, '=');
2003d65f6f70SBen Gras 		mdoc->flags |= MDOC_SYNOPSIS;
20040a6a1f1dSLionel Sambuc 	} else {
20050a6a1f1dSLionel Sambuc 		roff_setreg(mdoc->roff, "nS", 0, '=');
2006d65f6f70SBen Gras 		mdoc->flags &= ~MDOC_SYNOPSIS;
20070a6a1f1dSLionel Sambuc 	}
2008d65f6f70SBen Gras 
2009d65f6f70SBen Gras 	/* Mark our last section. */
2010d65f6f70SBen Gras 
2011d65f6f70SBen Gras 	mdoc->lastsec = sec;
2012d65f6f70SBen Gras 
201392395e9cSLionel Sambuc 	/*
201492395e9cSLionel Sambuc 	 * Set the section attribute for the current HEAD, for its
201592395e9cSLionel Sambuc 	 * parent BLOCK, and for the HEAD children; the latter can
201692395e9cSLionel Sambuc 	 * only be TEXT nodes, so no recursion is needed.
201792395e9cSLionel Sambuc 	 * For other blocks and elements, including .Sh BODY, this is
201892395e9cSLionel Sambuc 	 * done when allocating the node data structures, but for .Sh
201992395e9cSLionel Sambuc 	 * BLOCK and HEAD, the section is still unknown at that time.
202092395e9cSLionel Sambuc 	 */
202192395e9cSLionel Sambuc 
202292395e9cSLionel Sambuc 	mdoc->last->parent->sec = sec;
202392395e9cSLionel Sambuc 	mdoc->last->sec = sec;
202492395e9cSLionel Sambuc 	for (n = mdoc->last->child; n; n = n->next)
202592395e9cSLionel Sambuc 		n->sec = sec;
202692395e9cSLionel Sambuc 
2027d65f6f70SBen Gras 	/* We don't care about custom sections after this. */
2028d65f6f70SBen Gras 
2029d65f6f70SBen Gras 	if (SEC_CUSTOM == sec)
2030d65f6f70SBen Gras 		return(1);
2031d65f6f70SBen Gras 
2032d65f6f70SBen Gras 	/*
2033d65f6f70SBen Gras 	 * Check whether our non-custom section is being repeated or is
2034d65f6f70SBen Gras 	 * out of order.
2035d65f6f70SBen Gras 	 */
2036d65f6f70SBen Gras 
2037d65f6f70SBen Gras 	if (sec == mdoc->lastnamed)
2038d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
2039d65f6f70SBen Gras 
2040d65f6f70SBen Gras 	if (sec < mdoc->lastnamed)
2041d65f6f70SBen Gras 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
2042d65f6f70SBen Gras 
2043d65f6f70SBen Gras 	/* Mark the last named section. */
2044d65f6f70SBen Gras 
2045d65f6f70SBen Gras 	mdoc->lastnamed = sec;
2046d65f6f70SBen Gras 
2047d65f6f70SBen Gras 	/* Check particular section/manual conventions. */
2048d65f6f70SBen Gras 
2049d65f6f70SBen Gras 	assert(mdoc->meta.msec);
2050d65f6f70SBen Gras 
2051d65f6f70SBen Gras 	switch (sec) {
2052d65f6f70SBen Gras 	case (SEC_RETURN_VALUES):
2053d65f6f70SBen Gras 		/* FALLTHROUGH */
2054d65f6f70SBen Gras 	case (SEC_ERRORS):
2055d65f6f70SBen Gras 		/* FALLTHROUGH */
2056d65f6f70SBen Gras 	case (SEC_LIBRARY):
2057d65f6f70SBen Gras 		if (*mdoc->meta.msec == '2')
2058d65f6f70SBen Gras 			break;
2059d65f6f70SBen Gras 		if (*mdoc->meta.msec == '3')
2060d65f6f70SBen Gras 			break;
2061d65f6f70SBen Gras 		if (*mdoc->meta.msec == '9')
2062d65f6f70SBen Gras 			break;
20630a6a1f1dSLionel Sambuc 		mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
20640a6a1f1dSLionel Sambuc 				mdoc->last->line, mdoc->last->pos, buf);
2065d65f6f70SBen Gras 		break;
2066d65f6f70SBen Gras 	default:
2067d65f6f70SBen Gras 		break;
2068d65f6f70SBen Gras 	}
2069d65f6f70SBen Gras 
2070d65f6f70SBen Gras 	return(1);
2071d65f6f70SBen Gras }
2072d65f6f70SBen Gras 
2073d65f6f70SBen Gras static int
post_ignpar(POST_ARGS)2074d65f6f70SBen Gras post_ignpar(POST_ARGS)
2075d65f6f70SBen Gras {
2076d65f6f70SBen Gras 	struct mdoc_node *np;
2077d65f6f70SBen Gras 
2078d65f6f70SBen Gras 	if (MDOC_BODY != mdoc->last->type)
2079d65f6f70SBen Gras 		return(1);
2080d65f6f70SBen Gras 
2081d65f6f70SBen Gras 	if (NULL != (np = mdoc->last->child))
2082d65f6f70SBen Gras 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2083d65f6f70SBen Gras 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2084d65f6f70SBen Gras 			mdoc_node_delete(mdoc, np);
2085d65f6f70SBen Gras 		}
2086d65f6f70SBen Gras 
2087d65f6f70SBen Gras 	if (NULL != (np = mdoc->last->last))
2088d65f6f70SBen Gras 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2089d65f6f70SBen Gras 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
2090d65f6f70SBen Gras 			mdoc_node_delete(mdoc, np);
2091d65f6f70SBen Gras 		}
2092d65f6f70SBen Gras 
2093d65f6f70SBen Gras 	return(1);
2094d65f6f70SBen Gras }
2095d65f6f70SBen Gras 
2096d65f6f70SBen Gras static int
pre_par(PRE_ARGS)2097d65f6f70SBen Gras pre_par(PRE_ARGS)
2098d65f6f70SBen Gras {
2099d65f6f70SBen Gras 
2100d65f6f70SBen Gras 	if (NULL == mdoc->last)
2101d65f6f70SBen Gras 		return(1);
2102d65f6f70SBen Gras 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2103d65f6f70SBen Gras 		return(1);
2104d65f6f70SBen Gras 
2105d65f6f70SBen Gras 	/*
2106d65f6f70SBen Gras 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2107d65f6f70SBen Gras 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2108d65f6f70SBen Gras 	 */
2109d65f6f70SBen Gras 
21100a6a1f1dSLionel Sambuc 	if (MDOC_Pp != mdoc->last->tok &&
21110a6a1f1dSLionel Sambuc 	    MDOC_Lp != mdoc->last->tok &&
21120a6a1f1dSLionel Sambuc 	    MDOC_br != mdoc->last->tok)
2113d65f6f70SBen Gras 		return(1);
2114d65f6f70SBen Gras 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2115d65f6f70SBen Gras 		return(1);
2116d65f6f70SBen Gras 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2117d65f6f70SBen Gras 		return(1);
2118d65f6f70SBen Gras 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2119d65f6f70SBen Gras 		return(1);
2120d65f6f70SBen Gras 
2121d65f6f70SBen Gras 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2122d65f6f70SBen Gras 	mdoc_node_delete(mdoc, mdoc->last);
2123d65f6f70SBen Gras 	return(1);
2124d65f6f70SBen Gras }
2125d65f6f70SBen Gras 
2126d65f6f70SBen Gras static int
post_par(POST_ARGS)21270a6a1f1dSLionel Sambuc post_par(POST_ARGS)
21280a6a1f1dSLionel Sambuc {
21290a6a1f1dSLionel Sambuc 
21300a6a1f1dSLionel Sambuc 	if (MDOC_ELEM != mdoc->last->type &&
21310a6a1f1dSLionel Sambuc 	    MDOC_BLOCK != mdoc->last->type)
21320a6a1f1dSLionel Sambuc 		return(1);
21330a6a1f1dSLionel Sambuc 
21340a6a1f1dSLionel Sambuc 	if (NULL == mdoc->last->prev) {
21350a6a1f1dSLionel Sambuc 		if (MDOC_Sh != mdoc->last->parent->tok &&
21360a6a1f1dSLionel Sambuc 		    MDOC_Ss != mdoc->last->parent->tok)
21370a6a1f1dSLionel Sambuc 			return(1);
21380a6a1f1dSLionel Sambuc 	} else {
21390a6a1f1dSLionel Sambuc 		if (MDOC_Pp != mdoc->last->prev->tok &&
21400a6a1f1dSLionel Sambuc 		    MDOC_Lp != mdoc->last->prev->tok &&
21410a6a1f1dSLionel Sambuc 		    (MDOC_br != mdoc->last->tok ||
21420a6a1f1dSLionel Sambuc 		     (MDOC_sp != mdoc->last->prev->tok &&
21430a6a1f1dSLionel Sambuc 		      MDOC_br != mdoc->last->prev->tok)))
21440a6a1f1dSLionel Sambuc 			return(1);
21450a6a1f1dSLionel Sambuc 	}
21460a6a1f1dSLionel Sambuc 
21470a6a1f1dSLionel Sambuc 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
21480a6a1f1dSLionel Sambuc 	mdoc_node_delete(mdoc, mdoc->last);
21490a6a1f1dSLionel Sambuc 	return(1);
21500a6a1f1dSLionel Sambuc }
21510a6a1f1dSLionel Sambuc 
21520a6a1f1dSLionel Sambuc static int
pre_literal(PRE_ARGS)2153d65f6f70SBen Gras pre_literal(PRE_ARGS)
2154d65f6f70SBen Gras {
2155d65f6f70SBen Gras 
2156d65f6f70SBen Gras 	if (MDOC_BODY != n->type)
2157d65f6f70SBen Gras 		return(1);
2158d65f6f70SBen Gras 
2159d65f6f70SBen Gras 	/*
2160d65f6f70SBen Gras 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2161d65f6f70SBen Gras 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2162d65f6f70SBen Gras 	 */
2163d65f6f70SBen Gras 
2164d65f6f70SBen Gras 	switch (n->tok) {
2165d65f6f70SBen Gras 	case (MDOC_Dl):
2166d65f6f70SBen Gras 		mdoc->flags |= MDOC_LITERAL;
2167d65f6f70SBen Gras 		break;
2168d65f6f70SBen Gras 	case (MDOC_Bd):
2169d65f6f70SBen Gras 		if (DISP_literal == n->norm->Bd.type)
2170d65f6f70SBen Gras 			mdoc->flags |= MDOC_LITERAL;
2171d65f6f70SBen Gras 		if (DISP_unfilled == n->norm->Bd.type)
2172d65f6f70SBen Gras 			mdoc->flags |= MDOC_LITERAL;
2173d65f6f70SBen Gras 		break;
2174d65f6f70SBen Gras 	default:
2175d65f6f70SBen Gras 		abort();
2176d65f6f70SBen Gras 		/* NOTREACHED */
2177d65f6f70SBen Gras 	}
2178d65f6f70SBen Gras 
2179d65f6f70SBen Gras 	return(1);
2180d65f6f70SBen Gras }
2181d65f6f70SBen Gras 
2182d65f6f70SBen Gras static int
post_dd(POST_ARGS)2183d65f6f70SBen Gras post_dd(POST_ARGS)
2184d65f6f70SBen Gras {
2185d65f6f70SBen Gras 	char		  buf[DATESIZE];
2186d65f6f70SBen Gras 	struct mdoc_node *n;
218792395e9cSLionel Sambuc 	int		  c;
218892395e9cSLionel Sambuc 
218992395e9cSLionel Sambuc 	if (mdoc->meta.date)
219092395e9cSLionel Sambuc 		free(mdoc->meta.date);
2191d65f6f70SBen Gras 
2192d65f6f70SBen Gras 	n = mdoc->last;
219392395e9cSLionel Sambuc 	if (NULL == n->child || '\0' == n->child->string[0]) {
219492395e9cSLionel Sambuc 		mdoc->meta.date = mandoc_normdate
219592395e9cSLionel Sambuc 			(mdoc->parse, NULL, n->line, n->pos);
2196d65f6f70SBen Gras 		return(1);
2197d65f6f70SBen Gras 	}
2198d65f6f70SBen Gras 
219992395e9cSLionel Sambuc 	buf[0] = '\0';
220092395e9cSLionel Sambuc 	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
220192395e9cSLionel Sambuc 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2202d65f6f70SBen Gras 		return(0);
2203d65f6f70SBen Gras 	}
2204d65f6f70SBen Gras 
220592395e9cSLionel Sambuc 	assert(c);
220692395e9cSLionel Sambuc 	mdoc->meta.date = mandoc_normdate
220792395e9cSLionel Sambuc 		(mdoc->parse, buf, n->line, n->pos);
220892395e9cSLionel Sambuc 
2209d65f6f70SBen Gras 	return(1);
2210d65f6f70SBen Gras }
2211d65f6f70SBen Gras 
2212d65f6f70SBen Gras static int
post_dt(POST_ARGS)2213d65f6f70SBen Gras post_dt(POST_ARGS)
2214d65f6f70SBen Gras {
2215d65f6f70SBen Gras 	struct mdoc_node *nn, *n;
2216d65f6f70SBen Gras 	const char	 *cp;
2217d65f6f70SBen Gras 	char		 *p;
2218d65f6f70SBen Gras 
2219d65f6f70SBen Gras 	n = mdoc->last;
2220d65f6f70SBen Gras 
2221d65f6f70SBen Gras 	if (mdoc->meta.title)
2222d65f6f70SBen Gras 		free(mdoc->meta.title);
2223d65f6f70SBen Gras 	if (mdoc->meta.vol)
2224d65f6f70SBen Gras 		free(mdoc->meta.vol);
2225d65f6f70SBen Gras 	if (mdoc->meta.arch)
2226d65f6f70SBen Gras 		free(mdoc->meta.arch);
2227d65f6f70SBen Gras 
2228d65f6f70SBen Gras 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2229d65f6f70SBen Gras 
2230d65f6f70SBen Gras 	/* First make all characters uppercase. */
2231d65f6f70SBen Gras 
2232d65f6f70SBen Gras 	if (NULL != (nn = n->child))
2233d65f6f70SBen Gras 		for (p = nn->string; *p; p++) {
223492395e9cSLionel Sambuc 			if (toupper((unsigned char)*p) == *p)
2235d65f6f70SBen Gras 				continue;
2236d65f6f70SBen Gras 
2237d65f6f70SBen Gras 			/*
2238d65f6f70SBen Gras 			 * FIXME: don't be lazy: have this make all
2239d65f6f70SBen Gras 			 * characters be uppercase and just warn once.
2240d65f6f70SBen Gras 			 */
2241d65f6f70SBen Gras 			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2242d65f6f70SBen Gras 			break;
2243d65f6f70SBen Gras 		}
2244d65f6f70SBen Gras 
2245d65f6f70SBen Gras 	/* Handles: `.Dt'
2246d65f6f70SBen Gras 	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
2247d65f6f70SBen Gras 	 */
2248d65f6f70SBen Gras 
2249d65f6f70SBen Gras 	if (NULL == (nn = n->child)) {
2250d65f6f70SBen Gras 		/* XXX: make these macro values. */
2251d65f6f70SBen Gras 		/* FIXME: warn about missing values. */
2252d65f6f70SBen Gras 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
2253d65f6f70SBen Gras 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2254d65f6f70SBen Gras 		mdoc->meta.msec = mandoc_strdup("1");
2255d65f6f70SBen Gras 		return(1);
2256d65f6f70SBen Gras 	}
2257d65f6f70SBen Gras 
2258d65f6f70SBen Gras 	/* Handles: `.Dt TITLE'
2259d65f6f70SBen Gras 	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2260d65f6f70SBen Gras 	 */
2261d65f6f70SBen Gras 
2262d65f6f70SBen Gras 	mdoc->meta.title = mandoc_strdup
2263d65f6f70SBen Gras 		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2264d65f6f70SBen Gras 
2265d65f6f70SBen Gras 	if (NULL == (nn = nn->next)) {
2266d65f6f70SBen Gras 		/* FIXME: warn about missing msec. */
2267d65f6f70SBen Gras 		/* XXX: make this a macro value. */
2268d65f6f70SBen Gras 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2269d65f6f70SBen Gras 		mdoc->meta.msec = mandoc_strdup("1");
2270d65f6f70SBen Gras 		return(1);
2271d65f6f70SBen Gras 	}
2272d65f6f70SBen Gras 
2273d65f6f70SBen Gras 	/* Handles: `.Dt TITLE SEC'
2274d65f6f70SBen Gras 	 *   --> title = TITLE, volume = SEC is msec ?
2275d65f6f70SBen Gras 	 *           format(msec) : SEC,
2276d65f6f70SBen Gras 	 *       msec = SEC is msec ? atoi(msec) : 0,
2277d65f6f70SBen Gras 	 *       arch = NULL
2278d65f6f70SBen Gras 	 */
2279d65f6f70SBen Gras 
228092395e9cSLionel Sambuc 	cp = mandoc_a2msec(nn->string);
2281d65f6f70SBen Gras 	if (cp) {
2282d65f6f70SBen Gras 		mdoc->meta.vol = mandoc_strdup(cp);
2283d65f6f70SBen Gras 		mdoc->meta.msec = mandoc_strdup(nn->string);
2284d65f6f70SBen Gras 	} else {
2285d65f6f70SBen Gras 		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2286d65f6f70SBen Gras 		mdoc->meta.vol = mandoc_strdup(nn->string);
2287d65f6f70SBen Gras 		mdoc->meta.msec = mandoc_strdup(nn->string);
2288d65f6f70SBen Gras 	}
2289d65f6f70SBen Gras 
2290d65f6f70SBen Gras 	if (NULL == (nn = nn->next))
2291d65f6f70SBen Gras 		return(1);
2292d65f6f70SBen Gras 
2293d65f6f70SBen Gras 	/* Handles: `.Dt TITLE SEC VOL'
2294d65f6f70SBen Gras 	 *   --> title = TITLE, volume = VOL is vol ?
2295d65f6f70SBen Gras 	 *       format(VOL) :
2296d65f6f70SBen Gras 	 *           VOL is arch ? format(arch) :
2297d65f6f70SBen Gras 	 *               VOL
2298d65f6f70SBen Gras 	 */
2299d65f6f70SBen Gras 
2300d65f6f70SBen Gras 	cp = mdoc_a2vol(nn->string);
2301d65f6f70SBen Gras 	if (cp) {
2302d65f6f70SBen Gras 		free(mdoc->meta.vol);
2303d65f6f70SBen Gras 		mdoc->meta.vol = mandoc_strdup(cp);
2304d65f6f70SBen Gras 	} else {
2305d65f6f70SBen Gras 		cp = mdoc_a2arch(nn->string);
2306d65f6f70SBen Gras 		if (NULL == cp) {
23070a6a1f1dSLionel Sambuc 			mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
2308d65f6f70SBen Gras 			free(mdoc->meta.vol);
2309d65f6f70SBen Gras 			mdoc->meta.vol = mandoc_strdup(nn->string);
2310d65f6f70SBen Gras 		} else
2311d65f6f70SBen Gras 			mdoc->meta.arch = mandoc_strdup(cp);
2312d65f6f70SBen Gras 	}
2313d65f6f70SBen Gras 
2314d65f6f70SBen Gras 	/* Ignore any subsequent parameters... */
2315d65f6f70SBen Gras 	/* FIXME: warn about subsequent parameters. */
2316d65f6f70SBen Gras 
2317d65f6f70SBen Gras 	return(1);
2318d65f6f70SBen Gras }
2319d65f6f70SBen Gras 
2320d65f6f70SBen Gras static int
post_prol(POST_ARGS)2321d65f6f70SBen Gras post_prol(POST_ARGS)
2322d65f6f70SBen Gras {
2323d65f6f70SBen Gras 	/*
2324d65f6f70SBen Gras 	 * Remove prologue macros from the document after they're
2325d65f6f70SBen Gras 	 * processed.  The final document uses mdoc_meta for these
2326d65f6f70SBen Gras 	 * values and discards the originals.
2327d65f6f70SBen Gras 	 */
2328d65f6f70SBen Gras 
2329d65f6f70SBen Gras 	mdoc_node_delete(mdoc, mdoc->last);
2330d65f6f70SBen Gras 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2331d65f6f70SBen Gras 		mdoc->flags |= MDOC_PBODY;
2332d65f6f70SBen Gras 
2333d65f6f70SBen Gras 	return(1);
2334d65f6f70SBen Gras }
2335d65f6f70SBen Gras 
2336d65f6f70SBen Gras static int
post_bx(POST_ARGS)233792395e9cSLionel Sambuc post_bx(POST_ARGS)
233892395e9cSLionel Sambuc {
233992395e9cSLionel Sambuc 	struct mdoc_node	*n;
234092395e9cSLionel Sambuc 
234192395e9cSLionel Sambuc 	/*
234292395e9cSLionel Sambuc 	 * Make `Bx's second argument always start with an uppercase
234392395e9cSLionel Sambuc 	 * letter.  Groff checks if it's an "accepted" term, but we just
234492395e9cSLionel Sambuc 	 * uppercase blindly.
234592395e9cSLionel Sambuc 	 */
234692395e9cSLionel Sambuc 
234792395e9cSLionel Sambuc 	n = mdoc->last->child;
234892395e9cSLionel Sambuc 	if (n && NULL != (n = n->next))
234992395e9cSLionel Sambuc 		*n->string = (char)toupper
235092395e9cSLionel Sambuc 			((unsigned char)*n->string);
235192395e9cSLionel Sambuc 
235292395e9cSLionel Sambuc 	return(1);
235392395e9cSLionel Sambuc }
235492395e9cSLionel Sambuc 
235592395e9cSLionel Sambuc static int
post_os(POST_ARGS)2356d65f6f70SBen Gras post_os(POST_ARGS)
2357d65f6f70SBen Gras {
2358d65f6f70SBen Gras 	struct mdoc_node *n;
2359d65f6f70SBen Gras 	char		  buf[BUFSIZ];
236092395e9cSLionel Sambuc 	int		  c;
2361d65f6f70SBen Gras #ifndef OSNAME
2362d65f6f70SBen Gras 	struct utsname	  utsname;
2363d65f6f70SBen Gras #endif
2364d65f6f70SBen Gras 
2365d65f6f70SBen Gras 	n = mdoc->last;
2366d65f6f70SBen Gras 
2367d65f6f70SBen Gras 	/*
23680a6a1f1dSLionel Sambuc 	 * Set the operating system by way of the `Os' macro.
23690a6a1f1dSLionel Sambuc 	 * The order of precedence is:
23700a6a1f1dSLionel Sambuc 	 * 1. the argument of the `Os' macro, unless empty
23710a6a1f1dSLionel Sambuc 	 * 2. the -Ios=foo command line argument, if provided
23720a6a1f1dSLionel Sambuc 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
23730a6a1f1dSLionel Sambuc 	 * 4. "sysname release" from uname(3)
2374d65f6f70SBen Gras  	 */
2375d65f6f70SBen Gras 
2376d65f6f70SBen Gras 	free(mdoc->meta.os);
2377d65f6f70SBen Gras 
237892395e9cSLionel Sambuc 	buf[0] = '\0';
237992395e9cSLionel Sambuc 	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
238092395e9cSLionel Sambuc 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2381d65f6f70SBen Gras 		return(0);
238292395e9cSLionel Sambuc 	}
238392395e9cSLionel Sambuc 
238492395e9cSLionel Sambuc 	assert(c);
2385d65f6f70SBen Gras 
2386d65f6f70SBen Gras 	if ('\0' == buf[0]) {
23870a6a1f1dSLionel Sambuc 		if (mdoc->defos) {
23880a6a1f1dSLionel Sambuc 			mdoc->meta.os = mandoc_strdup(mdoc->defos);
23890a6a1f1dSLionel Sambuc 			return(1);
23900a6a1f1dSLionel Sambuc 		}
2391d65f6f70SBen Gras #ifdef OSNAME
2392d65f6f70SBen Gras 		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2393d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2394d65f6f70SBen Gras 			return(0);
2395d65f6f70SBen Gras 		}
2396d65f6f70SBen Gras #else /*!OSNAME */
239792395e9cSLionel Sambuc 		if (-1 == uname(&utsname)) {
2398d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2399d65f6f70SBen Gras                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
2400d65f6f70SBen Gras                         return(post_prol(mdoc));
2401d65f6f70SBen Gras                 }
2402d65f6f70SBen Gras 
2403d65f6f70SBen Gras 		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2404d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2405d65f6f70SBen Gras 			return(0);
2406d65f6f70SBen Gras 		}
2407d65f6f70SBen Gras 		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2408d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2409d65f6f70SBen Gras 			return(0);
2410d65f6f70SBen Gras 		}
2411d65f6f70SBen Gras 		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2412d65f6f70SBen Gras 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2413d65f6f70SBen Gras 			return(0);
2414d65f6f70SBen Gras 		}
2415d65f6f70SBen Gras #endif /*!OSNAME*/
2416d65f6f70SBen Gras 	}
2417d65f6f70SBen Gras 
2418d65f6f70SBen Gras 	mdoc->meta.os = mandoc_strdup(buf);
2419d65f6f70SBen Gras 	return(1);
2420d65f6f70SBen Gras }
2421d65f6f70SBen Gras 
2422d65f6f70SBen Gras static int
post_std(POST_ARGS)2423d65f6f70SBen Gras post_std(POST_ARGS)
2424d65f6f70SBen Gras {
2425d65f6f70SBen Gras 	struct mdoc_node *nn, *n;
2426d65f6f70SBen Gras 
2427d65f6f70SBen Gras 	n = mdoc->last;
2428d65f6f70SBen Gras 
2429d65f6f70SBen Gras 	/*
2430d65f6f70SBen Gras 	 * Macros accepting `-std' as an argument have the name of the
2431d65f6f70SBen Gras 	 * current document (`Nm') filled in as the argument if it's not
2432d65f6f70SBen Gras 	 * provided.
2433d65f6f70SBen Gras 	 */
2434d65f6f70SBen Gras 
2435d65f6f70SBen Gras 	if (n->child)
2436d65f6f70SBen Gras 		return(1);
2437d65f6f70SBen Gras 
2438d65f6f70SBen Gras 	if (NULL == mdoc->meta.name)
2439d65f6f70SBen Gras 		return(1);
2440d65f6f70SBen Gras 
2441d65f6f70SBen Gras 	nn = n;
2442d65f6f70SBen Gras 	mdoc->next = MDOC_NEXT_CHILD;
2443d65f6f70SBen Gras 
2444d65f6f70SBen Gras 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2445d65f6f70SBen Gras 		return(0);
2446d65f6f70SBen Gras 
2447d65f6f70SBen Gras 	mdoc->last = nn;
2448d65f6f70SBen Gras 	return(1);
2449d65f6f70SBen Gras }
2450d65f6f70SBen Gras 
245192395e9cSLionel Sambuc /*
245292395e9cSLionel Sambuc  * Concatenate a node, stopping at the first non-text.
245392395e9cSLionel Sambuc  * Concatenation is separated by a single whitespace.
245492395e9cSLionel Sambuc  * Returns -1 on fatal (string overrun) error, 0 if child nodes were
245592395e9cSLionel Sambuc  * encountered, 1 otherwise.
245692395e9cSLionel Sambuc  */
2457d65f6f70SBen Gras static int
concat(char * p,const struct mdoc_node * n,size_t sz)245892395e9cSLionel Sambuc concat(char *p, const struct mdoc_node *n, size_t sz)
2459d65f6f70SBen Gras {
2460d65f6f70SBen Gras 
246192395e9cSLionel Sambuc 	for ( ; NULL != n; n = n->next) {
246292395e9cSLionel Sambuc 		if (MDOC_TEXT != n->type)
2463d65f6f70SBen Gras 			return(0);
246492395e9cSLionel Sambuc 		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
246592395e9cSLionel Sambuc 			return(-1);
246692395e9cSLionel Sambuc 		if (strlcat(p, n->string, sz) >= sz)
246792395e9cSLionel Sambuc 			return(-1);
246892395e9cSLionel Sambuc 		concat(p, n->child, sz);
2469d65f6f70SBen Gras 	}
2470d65f6f70SBen Gras 
2471d65f6f70SBen Gras 	return(1);
2472d65f6f70SBen Gras }
2473d65f6f70SBen Gras 
247492395e9cSLionel Sambuc static enum mdoc_sec
a2sec(const char * p)247592395e9cSLionel Sambuc a2sec(const char *p)
247692395e9cSLionel Sambuc {
247792395e9cSLionel Sambuc 	int		 i;
247892395e9cSLionel Sambuc 
247992395e9cSLionel Sambuc 	for (i = 0; i < (int)SEC__MAX; i++)
248092395e9cSLionel Sambuc 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
248192395e9cSLionel Sambuc 			return((enum mdoc_sec)i);
248292395e9cSLionel Sambuc 
248392395e9cSLionel Sambuc 	return(SEC_CUSTOM);
248492395e9cSLionel Sambuc }
248592395e9cSLionel Sambuc 
248692395e9cSLionel Sambuc static size_t
macro2len(enum mdoct macro)248792395e9cSLionel Sambuc macro2len(enum mdoct macro)
248892395e9cSLionel Sambuc {
248992395e9cSLionel Sambuc 
249092395e9cSLionel Sambuc 	switch (macro) {
249192395e9cSLionel Sambuc 	case(MDOC_Ad):
249292395e9cSLionel Sambuc 		return(12);
249392395e9cSLionel Sambuc 	case(MDOC_Ao):
249492395e9cSLionel Sambuc 		return(12);
249592395e9cSLionel Sambuc 	case(MDOC_An):
249692395e9cSLionel Sambuc 		return(12);
249792395e9cSLionel Sambuc 	case(MDOC_Aq):
249892395e9cSLionel Sambuc 		return(12);
249992395e9cSLionel Sambuc 	case(MDOC_Ar):
250092395e9cSLionel Sambuc 		return(12);
250192395e9cSLionel Sambuc 	case(MDOC_Bo):
250292395e9cSLionel Sambuc 		return(12);
250392395e9cSLionel Sambuc 	case(MDOC_Bq):
250492395e9cSLionel Sambuc 		return(12);
250592395e9cSLionel Sambuc 	case(MDOC_Cd):
250692395e9cSLionel Sambuc 		return(12);
250792395e9cSLionel Sambuc 	case(MDOC_Cm):
250892395e9cSLionel Sambuc 		return(10);
250992395e9cSLionel Sambuc 	case(MDOC_Do):
251092395e9cSLionel Sambuc 		return(10);
251192395e9cSLionel Sambuc 	case(MDOC_Dq):
251292395e9cSLionel Sambuc 		return(12);
251392395e9cSLionel Sambuc 	case(MDOC_Dv):
251492395e9cSLionel Sambuc 		return(12);
251592395e9cSLionel Sambuc 	case(MDOC_Eo):
251692395e9cSLionel Sambuc 		return(12);
251792395e9cSLionel Sambuc 	case(MDOC_Em):
251892395e9cSLionel Sambuc 		return(10);
251992395e9cSLionel Sambuc 	case(MDOC_Er):
252092395e9cSLionel Sambuc 		return(17);
252192395e9cSLionel Sambuc 	case(MDOC_Ev):
252292395e9cSLionel Sambuc 		return(15);
252392395e9cSLionel Sambuc 	case(MDOC_Fa):
252492395e9cSLionel Sambuc 		return(12);
252592395e9cSLionel Sambuc 	case(MDOC_Fl):
252692395e9cSLionel Sambuc 		return(10);
252792395e9cSLionel Sambuc 	case(MDOC_Fo):
252892395e9cSLionel Sambuc 		return(16);
252992395e9cSLionel Sambuc 	case(MDOC_Fn):
253092395e9cSLionel Sambuc 		return(16);
253192395e9cSLionel Sambuc 	case(MDOC_Ic):
253292395e9cSLionel Sambuc 		return(10);
253392395e9cSLionel Sambuc 	case(MDOC_Li):
253492395e9cSLionel Sambuc 		return(16);
253592395e9cSLionel Sambuc 	case(MDOC_Ms):
253692395e9cSLionel Sambuc 		return(6);
253792395e9cSLionel Sambuc 	case(MDOC_Nm):
253892395e9cSLionel Sambuc 		return(10);
253992395e9cSLionel Sambuc 	case(MDOC_No):
254092395e9cSLionel Sambuc 		return(12);
254192395e9cSLionel Sambuc 	case(MDOC_Oo):
254292395e9cSLionel Sambuc 		return(10);
254392395e9cSLionel Sambuc 	case(MDOC_Op):
254492395e9cSLionel Sambuc 		return(14);
254592395e9cSLionel Sambuc 	case(MDOC_Pa):
254692395e9cSLionel Sambuc 		return(32);
254792395e9cSLionel Sambuc 	case(MDOC_Pf):
254892395e9cSLionel Sambuc 		return(12);
254992395e9cSLionel Sambuc 	case(MDOC_Po):
255092395e9cSLionel Sambuc 		return(12);
255192395e9cSLionel Sambuc 	case(MDOC_Pq):
255292395e9cSLionel Sambuc 		return(12);
255392395e9cSLionel Sambuc 	case(MDOC_Ql):
255492395e9cSLionel Sambuc 		return(16);
255592395e9cSLionel Sambuc 	case(MDOC_Qo):
255692395e9cSLionel Sambuc 		return(12);
255792395e9cSLionel Sambuc 	case(MDOC_So):
255892395e9cSLionel Sambuc 		return(12);
255992395e9cSLionel Sambuc 	case(MDOC_Sq):
256092395e9cSLionel Sambuc 		return(12);
256192395e9cSLionel Sambuc 	case(MDOC_Sy):
256292395e9cSLionel Sambuc 		return(6);
256392395e9cSLionel Sambuc 	case(MDOC_Sx):
256492395e9cSLionel Sambuc 		return(16);
256592395e9cSLionel Sambuc 	case(MDOC_Tn):
256692395e9cSLionel Sambuc 		return(10);
256792395e9cSLionel Sambuc 	case(MDOC_Va):
256892395e9cSLionel Sambuc 		return(12);
256992395e9cSLionel Sambuc 	case(MDOC_Vt):
257092395e9cSLionel Sambuc 		return(12);
257192395e9cSLionel Sambuc 	case(MDOC_Xr):
257292395e9cSLionel Sambuc 		return(10);
257392395e9cSLionel Sambuc 	default:
257492395e9cSLionel Sambuc 		break;
257592395e9cSLionel Sambuc 	};
257692395e9cSLionel Sambuc 	return(0);
257792395e9cSLionel Sambuc }
2578