xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 9530682e)
1*9530682eSschwarze /*	$Id: mdoc_validate.c,v 1.142 2014/07/03 23:23:45 schwarze Exp $ */
2f73abda9Skristaps /*
322972b14Sschwarze  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4231c7061Sschwarze  * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
539c2a57eSschwarze  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6f73abda9Skristaps  *
7f73abda9Skristaps  * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze  * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze  * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps  *
11a6464425Sschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12a6464425Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13a6464425Sschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14a6464425Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18f73abda9Skristaps  */
1920fa2881Sschwarze #ifndef OSNAME
2020fa2881Sschwarze #include <sys/utsname.h>
2120fa2881Sschwarze #endif
2220fa2881Sschwarze 
23f73abda9Skristaps #include <sys/types.h>
24f73abda9Skristaps 
25f73abda9Skristaps #include <assert.h>
26f73abda9Skristaps #include <ctype.h>
27d92dc4efSschwarze #include <limits.h>
283216dddfSschwarze #include <stdio.h>
29f73abda9Skristaps #include <stdlib.h>
30f73abda9Skristaps #include <string.h>
3120fa2881Sschwarze #include <time.h>
32f73abda9Skristaps 
33a35fc07aSschwarze #include "mdoc.h"
346e03d529Sschwarze #include "mandoc.h"
354f4f7972Sschwarze #include "mandoc_aux.h"
36f73abda9Skristaps #include "libmdoc.h"
37f6854d5cSschwarze #include "libmandoc.h"
38f73abda9Skristaps 
39f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
40f73abda9Skristaps 
416093755cSschwarze #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
42f73abda9Skristaps #define	POST_ARGS struct mdoc *mdoc
43f73abda9Skristaps 
447c2be9f8Sschwarze enum	check_ineq {
457c2be9f8Sschwarze 	CHECK_LT,
467c2be9f8Sschwarze 	CHECK_GT,
477c2be9f8Sschwarze 	CHECK_EQ
487c2be9f8Sschwarze };
497c2be9f8Sschwarze 
507c2be9f8Sschwarze enum	check_lvl {
517c2be9f8Sschwarze 	CHECK_WARN,
527c2be9f8Sschwarze 	CHECK_ERROR,
537c2be9f8Sschwarze };
547c2be9f8Sschwarze 
55f73abda9Skristaps typedef	int	(*v_pre)(PRE_ARGS);
56f73abda9Skristaps typedef	int	(*v_post)(POST_ARGS);
57f73abda9Skristaps 
58f73abda9Skristaps struct	valids {
59f73abda9Skristaps 	v_pre	*pre;
60f73abda9Skristaps 	v_post	*post;
61f73abda9Skristaps };
62f73abda9Skristaps 
637c2be9f8Sschwarze static	int	 check_count(struct mdoc *, enum mdoc_type,
647c2be9f8Sschwarze 			enum check_lvl, enum check_ineq, int);
65dd94fa3aSschwarze static	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
6620fa2881Sschwarze static	void	 check_text(struct mdoc *, int, int, char *);
6720fa2881Sschwarze static	void	 check_argv(struct mdoc *,
6831e23753Sschwarze 			struct mdoc_node *, struct mdoc_argv *);
6920fa2881Sschwarze static	void	 check_args(struct mdoc *, struct mdoc_node *);
7019a69263Sschwarze static	enum mdoc_sec	a2sec(const char *);
7119a69263Sschwarze static	size_t		macro2len(enum mdoct);
7267c719adSschwarze 
737c2be9f8Sschwarze static	int	 ebool(POST_ARGS);
7467c719adSschwarze static	int	 berr_ge1(POST_ARGS);
7567c719adSschwarze static	int	 bwarn_ge1(POST_ARGS);
76da272f5eSschwarze static	int	 ewarn_eq0(POST_ARGS);
77bb648afaSschwarze static	int	 ewarn_eq1(POST_ARGS);
7867c719adSschwarze static	int	 ewarn_ge1(POST_ARGS);
79bb648afaSschwarze static	int	 ewarn_le1(POST_ARGS);
80b16e7ddfSschwarze static	int	 hwarn_eq0(POST_ARGS);
817c2be9f8Sschwarze static	int	 hwarn_eq1(POST_ARGS);
82bb648afaSschwarze static	int	 hwarn_ge1(POST_ARGS);
8367c719adSschwarze static	int	 hwarn_le1(POST_ARGS);
8467c719adSschwarze 
8567c719adSschwarze static	int	 post_an(POST_ARGS);
8667c719adSschwarze static	int	 post_at(POST_ARGS);
8767c719adSschwarze static	int	 post_bf(POST_ARGS);
8867c719adSschwarze static	int	 post_bl(POST_ARGS);
8920fa2881Sschwarze static	int	 post_bl_block(POST_ARGS);
9020fa2881Sschwarze static	int	 post_bl_block_width(POST_ARGS);
9120fa2881Sschwarze static	int	 post_bl_block_tag(POST_ARGS);
9267c719adSschwarze static	int	 post_bl_head(POST_ARGS);
93992063deSschwarze static	int	 post_bx(POST_ARGS);
944039b21cSschwarze static	int	 post_defaults(POST_ARGS);
9520fa2881Sschwarze static	int	 post_dd(POST_ARGS);
966093755cSschwarze static	int	 post_dt(POST_ARGS);
97551cd4a8Sschwarze static	int	 post_en(POST_ARGS);
98551cd4a8Sschwarze static	int	 post_es(POST_ARGS);
9920fa2881Sschwarze static	int	 post_eoln(POST_ARGS);
1004039b21cSschwarze static	int	 post_hyph(POST_ARGS);
1014039b21cSschwarze static	int	 post_ignpar(POST_ARGS);
10267c719adSschwarze static	int	 post_it(POST_ARGS);
10367c719adSschwarze static	int	 post_lb(POST_ARGS);
1044039b21cSschwarze static	int	 post_literal(POST_ARGS);
10567c719adSschwarze static	int	 post_nm(POST_ARGS);
106af216717Sschwarze static	int	 post_ns(POST_ARGS);
10720fa2881Sschwarze static	int	 post_os(POST_ARGS);
108e0dd4c9cSschwarze static	int	 post_par(POST_ARGS);
10920fa2881Sschwarze static	int	 post_prol(POST_ARGS);
11067c719adSschwarze static	int	 post_root(POST_ARGS);
111011fe33bSschwarze static	int	 post_rs(POST_ARGS);
11267c719adSschwarze static	int	 post_sh(POST_ARGS);
11367c719adSschwarze static	int	 post_sh_body(POST_ARGS);
11467c719adSschwarze static	int	 post_sh_head(POST_ARGS);
11567c719adSschwarze static	int	 post_st(POST_ARGS);
11620fa2881Sschwarze static	int	 post_std(POST_ARGS);
1178521b0bcSschwarze static	int	 post_vt(POST_ARGS);
118f73abda9Skristaps static	int	 pre_an(PRE_ARGS);
119f73abda9Skristaps static	int	 pre_bd(PRE_ARGS);
120f73abda9Skristaps static	int	 pre_bl(PRE_ARGS);
121f73abda9Skristaps static	int	 pre_dd(PRE_ARGS);
122f73abda9Skristaps static	int	 pre_display(PRE_ARGS);
123f73abda9Skristaps static	int	 pre_dt(PRE_ARGS);
124f73abda9Skristaps static	int	 pre_it(PRE_ARGS);
12520fa2881Sschwarze static	int	 pre_literal(PRE_ARGS);
126551cd4a8Sschwarze static	int	 pre_obsolete(PRE_ARGS);
127f73abda9Skristaps static	int	 pre_os(PRE_ARGS);
12820fa2881Sschwarze static	int	 pre_par(PRE_ARGS);
129f73abda9Skristaps static	int	 pre_sh(PRE_ARGS);
130f73abda9Skristaps static	int	 pre_ss(PRE_ARGS);
13120fa2881Sschwarze static	int	 pre_std(PRE_ARGS);
132f73abda9Skristaps 
13367c719adSschwarze static	v_post	 posts_an[] = { post_an, NULL };
13420fa2881Sschwarze static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
13520fa2881Sschwarze static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
13667c719adSschwarze static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
13720fa2881Sschwarze static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
13867c719adSschwarze static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
139992063deSschwarze static	v_post	 posts_bx[] = { post_bx, NULL };
140bb648afaSschwarze static	v_post	 posts_bool[] = { ebool, NULL };
141b31af00dSschwarze static	v_post	 posts_eoln[] = { post_eoln, NULL };
14220fa2881Sschwarze static	v_post	 posts_defaults[] = { post_defaults, NULL };
1434039b21cSschwarze static	v_post	 posts_d1[] = { bwarn_ge1, post_hyph, NULL };
144b058e777Sschwarze static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
145bb648afaSschwarze static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
14620fa2881Sschwarze static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
147551cd4a8Sschwarze static	v_post	 posts_en[] = { post_en, NULL };
148551cd4a8Sschwarze static	v_post	 posts_es[] = { post_es, NULL };
14967c719adSschwarze static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
1504039b21cSschwarze static	v_post	 posts_hyph[] = { post_hyph, NULL };
1514039b21cSschwarze static	v_post	 posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
15267c719adSschwarze static	v_post	 posts_it[] = { post_it, NULL };
153bb648afaSschwarze static	v_post	 posts_lb[] = { post_lb, NULL };
1544039b21cSschwarze static	v_post	 posts_nd[] = { berr_ge1, post_hyph, NULL };
15567c719adSschwarze static	v_post	 posts_nm[] = { post_nm, NULL };
156da272f5eSschwarze static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
157af216717Sschwarze static	v_post	 posts_ns[] = { post_ns, NULL };
15820fa2881Sschwarze static	v_post	 posts_os[] = { post_os, post_prol, NULL };
159e0dd4c9cSschwarze static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
160bb648afaSschwarze static	v_post	 posts_rs[] = { post_rs, NULL };
1614039b21cSschwarze static	v_post	 posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
162e0dd4c9cSschwarze static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
1634039b21cSschwarze static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
164bb648afaSschwarze static	v_post	 posts_st[] = { post_st, NULL };
16520fa2881Sschwarze static	v_post	 posts_std[] = { post_std, NULL };
166e7a93ef3Sschwarze static	v_post	 posts_text[] = { ewarn_ge1, NULL };
167bb648afaSschwarze static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
1688521b0bcSschwarze static	v_post	 posts_vt[] = { post_vt, NULL };
169f73abda9Skristaps static	v_pre	 pres_an[] = { pre_an, NULL };
17020fa2881Sschwarze static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
17120fa2881Sschwarze static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
172f73abda9Skristaps static	v_pre	 pres_d1[] = { pre_display, NULL };
17320fa2881Sschwarze static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
17467c719adSschwarze static	v_pre	 pres_dd[] = { pre_dd, NULL };
1756be99f77Sschwarze static	v_pre	 pres_dt[] = { pre_dt, NULL };
176f6127a73Sschwarze static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
177551cd4a8Sschwarze static	v_pre	 pres_obsolete[] = { pre_obsolete, NULL };
1786be99f77Sschwarze static	v_pre	 pres_os[] = { pre_os, NULL };
17920fa2881Sschwarze static	v_pre	 pres_pp[] = { pre_par, NULL };
180f73abda9Skristaps static	v_pre	 pres_sh[] = { pre_sh, NULL };
181f73abda9Skristaps static	v_pre	 pres_ss[] = { pre_ss, NULL };
18220fa2881Sschwarze static	v_pre	 pres_std[] = { pre_std, NULL };
183f73abda9Skristaps 
18419a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
185099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
18620fa2881Sschwarze 	{ pres_dd, posts_dd },			/* Dd */
1876093755cSschwarze 	{ pres_dt, posts_dt },			/* Dt */
18820fa2881Sschwarze 	{ pres_os, posts_os },			/* Os */
189f73abda9Skristaps 	{ pres_sh, posts_sh },			/* Sh */
190f73abda9Skristaps 	{ pres_ss, posts_ss },			/* Ss */
191e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Pp */
1924039b21cSschwarze 	{ pres_d1, posts_d1 },			/* D1 */
19320fa2881Sschwarze 	{ pres_dl, posts_dl },			/* Dl */
19420fa2881Sschwarze 	{ pres_bd, posts_bd },			/* Bd */
195f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
196f73abda9Skristaps 	{ pres_bl, posts_bl },			/* Bl */
197f73abda9Skristaps 	{ NULL, NULL },				/* El */
198f73abda9Skristaps 	{ pres_it, posts_it },			/* It */
199e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
200f73abda9Skristaps 	{ pres_an, posts_an },			/* An */
20120fa2881Sschwarze 	{ NULL, posts_defaults },		/* Ar */
202e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
203f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
204f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
2054039b21cSschwarze 	{ NULL, NULL },				/* Er */
206f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
20720fa2881Sschwarze 	{ pres_std, posts_std },		/* Ex */
208f73abda9Skristaps 	{ NULL, NULL },				/* Fa */
2094039b21cSschwarze 	{ NULL, posts_text },			/* Fd */
210f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
211e7a93ef3Sschwarze 	{ NULL, NULL },				/* Fn */
212e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
213e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
214b822ca0dSschwarze 	{ NULL, posts_text1 },			/* In */
21520fa2881Sschwarze 	{ NULL, posts_defaults },		/* Li */
2164602e85cSschwarze 	{ NULL, posts_nd },			/* Nd */
217f73abda9Skristaps 	{ NULL, posts_nm },			/* Nm */
218bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
219551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Ot */
22020fa2881Sschwarze 	{ NULL, posts_defaults },		/* Pa */
22120fa2881Sschwarze 	{ pres_std, posts_std },		/* Rv */
222f73abda9Skristaps 	{ NULL, posts_st },			/* St */
223f73abda9Skristaps 	{ NULL, NULL },				/* Va */
2248521b0bcSschwarze 	{ NULL, posts_vt },			/* Vt */
225e7a93ef3Sschwarze 	{ NULL, posts_text },			/* Xr */
226f73abda9Skristaps 	{ NULL, posts_text },			/* %A */
2274039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
228b058e777Sschwarze 	{ NULL, posts_text },			/* %D */
229f73abda9Skristaps 	{ NULL, posts_text },			/* %I */
230f73abda9Skristaps 	{ NULL, posts_text },			/* %J */
2314039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %N */
2324039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %O */
233f73abda9Skristaps 	{ NULL, posts_text },			/* %P */
2344039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %R */
2354039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
236f73abda9Skristaps 	{ NULL, posts_text },			/* %V */
237f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
238f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
239bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
240f73abda9Skristaps 	{ NULL, posts_at },			/* At */
241f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
242f73abda9Skristaps 	{ NULL, posts_bf },			/* Bf */
243f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
244bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
245f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
246992063deSschwarze 	{ NULL, posts_bx },			/* Bx */
247f73abda9Skristaps 	{ NULL, posts_bool },			/* Db */
248f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
249f73abda9Skristaps 	{ NULL, NULL },				/* Do */
250bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
251f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
252f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
253f73abda9Skristaps 	{ NULL, NULL },				/* Em */
254f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
255f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
256e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
257f73abda9Skristaps 	{ NULL, posts_notext },			/* No */
258af216717Sschwarze 	{ NULL, posts_ns },			/* Ns */
259f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
260f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
261f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
262b822ca0dSschwarze 	{ NULL, posts_text1 },			/* Pf */
263f73abda9Skristaps 	{ NULL, NULL },				/* Po */
264bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
265f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
266bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
267f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
268bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
269f73abda9Skristaps 	{ NULL, NULL },				/* Re */
2708c62fbf5Sschwarze 	{ NULL, posts_rs },			/* Rs */
271f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
272f73abda9Skristaps 	{ NULL, NULL },				/* So */
273bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
274f73abda9Skristaps 	{ NULL, posts_bool },			/* Sm */
2754039b21cSschwarze 	{ NULL, posts_hyph },			/* Sx */
276e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
277e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
278f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
279f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
280f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
281f73abda9Skristaps 	{ NULL, posts_fo },			/* Fo */
282f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
283f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
284f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
28520fa2881Sschwarze 	{ NULL, posts_bk },			/* Bk */
286f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
287b31af00dSschwarze 	{ NULL, posts_eoln },			/* Bt */
288f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
289551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Fr */
290b31af00dSschwarze 	{ NULL, posts_eoln },			/* Ud */
291b31af00dSschwarze 	{ NULL, posts_lb },			/* Lb */
292e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Lp */
293e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
29420fa2881Sschwarze 	{ NULL, posts_defaults },		/* Mt */
295bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
296f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
297f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
298f73abda9Skristaps 	{ NULL, posts_text },			/* %C */
299551cd4a8Sschwarze 	{ pres_obsolete, posts_es },		/* Es */
300551cd4a8Sschwarze 	{ pres_obsolete, posts_en },		/* En */
301f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
302f73abda9Skristaps 	{ NULL, posts_text },			/* %Q */
303e0dd4c9cSschwarze 	{ NULL, posts_pp },			/* br */
304e0dd4c9cSschwarze 	{ NULL, posts_sp },			/* sp */
305b822ca0dSschwarze 	{ NULL, posts_text1 },			/* %U */
3066093755cSschwarze 	{ NULL, NULL },				/* Ta */
3075281506aSschwarze 	{ NULL, NULL },				/* ll */
308f73abda9Skristaps };
309f73abda9Skristaps 
31020fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
31120fa2881Sschwarze 
31220fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
31320fa2881Sschwarze 	MDOC__A,
31420fa2881Sschwarze 	MDOC__T,
31520fa2881Sschwarze 	MDOC__B,
31620fa2881Sschwarze 	MDOC__I,
31720fa2881Sschwarze 	MDOC__J,
31820fa2881Sschwarze 	MDOC__R,
31920fa2881Sschwarze 	MDOC__N,
32020fa2881Sschwarze 	MDOC__V,
3210397c682Sschwarze 	MDOC__U,
32220fa2881Sschwarze 	MDOC__P,
32320fa2881Sschwarze 	MDOC__Q,
3244e32ec8fSschwarze 	MDOC__C,
32520fa2881Sschwarze 	MDOC__D,
3264e32ec8fSschwarze 	MDOC__O
32720fa2881Sschwarze };
32820fa2881Sschwarze 
32919a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
33019a69263Sschwarze 	NULL,
33119a69263Sschwarze 	"NAME",
33219a69263Sschwarze 	"LIBRARY",
33319a69263Sschwarze 	"SYNOPSIS",
33419a69263Sschwarze 	"DESCRIPTION",
33503ab2f23Sdlg 	"CONTEXT",
33619a69263Sschwarze 	"IMPLEMENTATION NOTES",
33719a69263Sschwarze 	"RETURN VALUES",
33819a69263Sschwarze 	"ENVIRONMENT",
33919a69263Sschwarze 	"FILES",
34019a69263Sschwarze 	"EXIT STATUS",
34119a69263Sschwarze 	"EXAMPLES",
34219a69263Sschwarze 	"DIAGNOSTICS",
34319a69263Sschwarze 	"COMPATIBILITY",
34419a69263Sschwarze 	"ERRORS",
34519a69263Sschwarze 	"SEE ALSO",
34619a69263Sschwarze 	"STANDARDS",
34719a69263Sschwarze 	"HISTORY",
34819a69263Sschwarze 	"AUTHORS",
34919a69263Sschwarze 	"CAVEATS",
35019a69263Sschwarze 	"BUGS",
35119a69263Sschwarze 	"SECURITY CONSIDERATIONS",
35219a69263Sschwarze 	NULL
35319a69263Sschwarze };
354f73abda9Skristaps 
35549aff9f8Sschwarze 
356f73abda9Skristaps int
3576093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
358f73abda9Skristaps {
359f73abda9Skristaps 	v_pre		*p;
360f73abda9Skristaps 	int		 line, pos;
36131e23753Sschwarze 	char		*tp;
362f73abda9Skristaps 
3632791bd1cSschwarze 	switch (n->type) {
36449aff9f8Sschwarze 	case MDOC_TEXT:
365f73abda9Skristaps 		tp = n->string;
366f73abda9Skristaps 		line = n->line;
367f73abda9Skristaps 		pos = n->pos;
36820fa2881Sschwarze 		check_text(mdoc, line, pos, tp);
3692791bd1cSschwarze 		/* FALLTHROUGH */
37049aff9f8Sschwarze 	case MDOC_TBL:
3712791bd1cSschwarze 		/* FALLTHROUGH */
37249aff9f8Sschwarze 	case MDOC_EQN:
3738d973ab1Sschwarze 		/* FALLTHROUGH */
37449aff9f8Sschwarze 	case MDOC_ROOT:
37520fa2881Sschwarze 		return(1);
3762791bd1cSschwarze 	default:
3772791bd1cSschwarze 		break;
378f73abda9Skristaps 	}
379f73abda9Skristaps 
38020fa2881Sschwarze 	check_args(mdoc, n);
38120fa2881Sschwarze 
382f73abda9Skristaps 	if (NULL == mdoc_valids[n->tok].pre)
383f73abda9Skristaps 		return(1);
384f73abda9Skristaps 	for (p = mdoc_valids[n->tok].pre; *p; p++)
385f73abda9Skristaps 		if ( ! (*p)(mdoc, n))
386f73abda9Skristaps 			return(0);
387f73abda9Skristaps 	return(1);
388f73abda9Skristaps }
389f73abda9Skristaps 
390f73abda9Skristaps int
391f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc)
392f73abda9Skristaps {
393f73abda9Skristaps 	v_post		*p;
394f73abda9Skristaps 
395f73abda9Skristaps 	if (MDOC_VALID & mdoc->last->flags)
396f73abda9Skristaps 		return(1);
397f73abda9Skristaps 	mdoc->last->flags |= MDOC_VALID;
398f73abda9Skristaps 
3992791bd1cSschwarze 	switch (mdoc->last->type) {
40049aff9f8Sschwarze 	case MDOC_TEXT:
4012791bd1cSschwarze 		/* FALLTHROUGH */
40249aff9f8Sschwarze 	case MDOC_EQN:
4038d973ab1Sschwarze 		/* FALLTHROUGH */
40449aff9f8Sschwarze 	case MDOC_TBL:
405f73abda9Skristaps 		return(1);
40649aff9f8Sschwarze 	case MDOC_ROOT:
407f73abda9Skristaps 		return(post_root(mdoc));
4082791bd1cSschwarze 	default:
4092791bd1cSschwarze 		break;
4102791bd1cSschwarze 	}
411f73abda9Skristaps 
412f73abda9Skristaps 	if (NULL == mdoc_valids[mdoc->last->tok].post)
413f73abda9Skristaps 		return(1);
414f73abda9Skristaps 	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
415f73abda9Skristaps 		if ( ! (*p)(mdoc))
416f73abda9Skristaps 			return(0);
417f73abda9Skristaps 
418f73abda9Skristaps 	return(1);
419f73abda9Skristaps }
420f73abda9Skristaps 
4217c2be9f8Sschwarze static int
4227ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type,
4237c2be9f8Sschwarze 		enum check_lvl lvl, enum check_ineq ineq, int val)
424f73abda9Skristaps {
4257c2be9f8Sschwarze 	const char	*p;
426bb648afaSschwarze 	enum mandocerr	 t;
4277c2be9f8Sschwarze 
4287ead8a4eSschwarze 	if (mdoc->last->type != type)
4297c2be9f8Sschwarze 		return(1);
4307c2be9f8Sschwarze 
4317c2be9f8Sschwarze 	switch (ineq) {
43249aff9f8Sschwarze 	case CHECK_LT:
4337c2be9f8Sschwarze 		p = "less than ";
4347ead8a4eSschwarze 		if (mdoc->last->nchild < val)
4357c2be9f8Sschwarze 			return(1);
4367c2be9f8Sschwarze 		break;
43749aff9f8Sschwarze 	case CHECK_GT:
438bb648afaSschwarze 		p = "more than ";
4397ead8a4eSschwarze 		if (mdoc->last->nchild > val)
4407c2be9f8Sschwarze 			return(1);
4417c2be9f8Sschwarze 		break;
44249aff9f8Sschwarze 	case CHECK_EQ:
4437c2be9f8Sschwarze 		p = "";
4447ead8a4eSschwarze 		if (val == mdoc->last->nchild)
4457c2be9f8Sschwarze 			return(1);
4467c2be9f8Sschwarze 		break;
44720fa2881Sschwarze 	default:
44820fa2881Sschwarze 		abort();
44920fa2881Sschwarze 		/* NOTREACHED */
4507c2be9f8Sschwarze 	}
4517c2be9f8Sschwarze 
452bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
45349aff9f8Sschwarze 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
45449aff9f8Sschwarze 	    mdoc->last->pos, "want %s%d children (have %d)",
4557ead8a4eSschwarze 	    p, val, mdoc->last->nchild);
45619a69263Sschwarze 	return(1);
4577c2be9f8Sschwarze }
458f73abda9Skristaps 
4597c2be9f8Sschwarze static int
4607c2be9f8Sschwarze berr_ge1(POST_ARGS)
461f73abda9Skristaps {
462f73abda9Skristaps 
463bb648afaSschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
464f73abda9Skristaps }
465f73abda9Skristaps 
4667c2be9f8Sschwarze static int
4677c2be9f8Sschwarze bwarn_ge1(POST_ARGS)
4687c2be9f8Sschwarze {
4697c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
470f73abda9Skristaps }
471f73abda9Skristaps 
4727c2be9f8Sschwarze static int
4737c2be9f8Sschwarze ewarn_eq0(POST_ARGS)
4747c2be9f8Sschwarze {
4757c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
4767c2be9f8Sschwarze }
4777c2be9f8Sschwarze 
4787c2be9f8Sschwarze static int
479bb648afaSschwarze ewarn_eq1(POST_ARGS)
480bb648afaSschwarze {
481bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
482bb648afaSschwarze }
483bb648afaSschwarze 
484bb648afaSschwarze static int
4857c2be9f8Sschwarze ewarn_ge1(POST_ARGS)
4867c2be9f8Sschwarze {
4877c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
4887c2be9f8Sschwarze }
4897c2be9f8Sschwarze 
4907c2be9f8Sschwarze static int
491bb648afaSschwarze ewarn_le1(POST_ARGS)
4927c2be9f8Sschwarze {
493bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
4947c2be9f8Sschwarze }
4957c2be9f8Sschwarze 
4967c2be9f8Sschwarze static int
4977c2be9f8Sschwarze hwarn_eq0(POST_ARGS)
4987c2be9f8Sschwarze {
4997c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
5007c2be9f8Sschwarze }
5017c2be9f8Sschwarze 
5027c2be9f8Sschwarze static int
5037c2be9f8Sschwarze hwarn_eq1(POST_ARGS)
5047c2be9f8Sschwarze {
5057c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
5067c2be9f8Sschwarze }
5077c2be9f8Sschwarze 
5087c2be9f8Sschwarze static int
509bb648afaSschwarze hwarn_ge1(POST_ARGS)
510bb648afaSschwarze {
511bb648afaSschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
512bb648afaSschwarze }
513bb648afaSschwarze 
514bb648afaSschwarze static int
5157c2be9f8Sschwarze hwarn_le1(POST_ARGS)
5167c2be9f8Sschwarze {
5177c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
5187c2be9f8Sschwarze }
519f73abda9Skristaps 
52020fa2881Sschwarze static void
5217ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n)
522f73abda9Skristaps {
523f73abda9Skristaps 	int		 i;
524f73abda9Skristaps 
525f73abda9Skristaps 	if (NULL == n->args)
52620fa2881Sschwarze 		return;
527f73abda9Skristaps 
528f73abda9Skristaps 	assert(n->args->argc);
529f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
5307ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
531f73abda9Skristaps }
532f73abda9Skristaps 
53320fa2881Sschwarze static void
5347ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
535f73abda9Skristaps {
536f73abda9Skristaps 	int		 i;
537f73abda9Skristaps 
538f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
5397ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
540f73abda9Skristaps 
54120fa2881Sschwarze 	/* FIXME: move to post_std(). */
54220fa2881Sschwarze 
54320fa2881Sschwarze 	if (MDOC_Std == v->arg)
5447ead8a4eSschwarze 		if ( ! (v->sz || mdoc->meta.name))
5457ead8a4eSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
546f73abda9Skristaps }
547f73abda9Skristaps 
54820fa2881Sschwarze static void
5497ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
550f73abda9Skristaps {
55104e980cbSschwarze 	char		*cp;
552769ee804Sschwarze 
5537ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
5541cdbf331Sschwarze 		return;
5551cdbf331Sschwarze 
5561cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
5577ead8a4eSschwarze 		mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
558f73abda9Skristaps }
559f73abda9Skristaps 
560f73abda9Skristaps static int
561dd94fa3aSschwarze check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
562f73abda9Skristaps {
563f73abda9Skristaps 
564f73abda9Skristaps 	assert(n->parent);
565f73abda9Skristaps 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
566f73abda9Skristaps 			(t == n->parent->type))
567f73abda9Skristaps 		return(1);
568f73abda9Skristaps 
56949aff9f8Sschwarze 	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse,
57049aff9f8Sschwarze 	    n->line, n->pos, "want parent %s",
57149aff9f8Sschwarze 	    MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]);
5726e03d529Sschwarze 	return(0);
573f73abda9Skristaps }
574f73abda9Skristaps 
575f73abda9Skristaps 
576f73abda9Skristaps static int
577f73abda9Skristaps pre_display(PRE_ARGS)
578f73abda9Skristaps {
579f73abda9Skristaps 	struct mdoc_node *node;
580f73abda9Skristaps 
581f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
582f73abda9Skristaps 		return(1);
583f73abda9Skristaps 
584f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
585f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
586f73abda9Skristaps 			if (MDOC_Bd == node->tok)
587f73abda9Skristaps 				break;
58820fa2881Sschwarze 
58905c39368Sschwarze 	if (node)
590b723eac2Sschwarze 		mandoc_vmsg(MANDOCERR_BD_NEST,
591b723eac2Sschwarze 		    mdoc->parse, n->line, n->pos,
592b723eac2Sschwarze 		    "%s in Bd", mdoc_macronames[n->tok]);
59305c39368Sschwarze 
59405c39368Sschwarze 	return(1);
595f73abda9Skristaps }
596f73abda9Skristaps 
597f73abda9Skristaps static int
598f73abda9Skristaps pre_bl(PRE_ARGS)
599f73abda9Skristaps {
60031e23753Sschwarze 	int		  i, comp, dup;
60131e23753Sschwarze 	const char	 *offs, *width;
6026093755cSschwarze 	enum mdoc_list	  lt;
603769ee804Sschwarze 	struct mdoc_node *np;
604f73abda9Skristaps 
6056093755cSschwarze 	if (MDOC_BLOCK != n->type) {
606769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
607769ee804Sschwarze 			assert(n->pending);
608769ee804Sschwarze 			np = n->pending->parent;
609769ee804Sschwarze 		} else
610769ee804Sschwarze 			np = n->parent;
611769ee804Sschwarze 
612769ee804Sschwarze 		assert(np);
613769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
614769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
615f73abda9Skristaps 		return(1);
6166e03d529Sschwarze 	}
617f73abda9Skristaps 
6186093755cSschwarze 	/*
6196093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
6206093755cSschwarze 	 * the first mentioned list type and warn about any remaining
6216093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
6226093755cSschwarze 	 */
623f73abda9Skristaps 
6246093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
6256093755cSschwarze 		lt = LIST__NONE;
62631e23753Sschwarze 		dup = comp = 0;
62731e23753Sschwarze 		width = offs = NULL;
6286093755cSschwarze 		switch (n->args->argv[i].arg) {
6296093755cSschwarze 		/* Set list types. */
63049aff9f8Sschwarze 		case MDOC_Bullet:
6316093755cSschwarze 			lt = LIST_bullet;
6326093755cSschwarze 			break;
63349aff9f8Sschwarze 		case MDOC_Dash:
6346093755cSschwarze 			lt = LIST_dash;
6356093755cSschwarze 			break;
63649aff9f8Sschwarze 		case MDOC_Enum:
6376093755cSschwarze 			lt = LIST_enum;
6386093755cSschwarze 			break;
63949aff9f8Sschwarze 		case MDOC_Hyphen:
6406093755cSschwarze 			lt = LIST_hyphen;
6416093755cSschwarze 			break;
64249aff9f8Sschwarze 		case MDOC_Item:
6436093755cSschwarze 			lt = LIST_item;
6446093755cSschwarze 			break;
64549aff9f8Sschwarze 		case MDOC_Tag:
6466093755cSschwarze 			lt = LIST_tag;
6476093755cSschwarze 			break;
64849aff9f8Sschwarze 		case MDOC_Diag:
6496093755cSschwarze 			lt = LIST_diag;
6506093755cSschwarze 			break;
65149aff9f8Sschwarze 		case MDOC_Hang:
6526093755cSschwarze 			lt = LIST_hang;
6536093755cSschwarze 			break;
65449aff9f8Sschwarze 		case MDOC_Ohang:
6556093755cSschwarze 			lt = LIST_ohang;
6566093755cSschwarze 			break;
65749aff9f8Sschwarze 		case MDOC_Inset:
6586093755cSschwarze 			lt = LIST_inset;
6596093755cSschwarze 			break;
66049aff9f8Sschwarze 		case MDOC_Column:
6616093755cSschwarze 			lt = LIST_column;
66264d728e4Sschwarze 			break;
6636093755cSschwarze 		/* Set list arguments. */
66449aff9f8Sschwarze 		case MDOC_Compact:
6658c62fbf5Sschwarze 			dup = n->norm->Bl.comp;
66631e23753Sschwarze 			comp = 1;
66750e63e03Sschwarze 			break;
66849aff9f8Sschwarze 		case MDOC_Width:
66922972b14Sschwarze 			/* NB: this can be empty! */
67022972b14Sschwarze 			if (n->args->argv[i].sz) {
67131e23753Sschwarze 				width = n->args->argv[i].value[0];
67222972b14Sschwarze 				dup = (NULL != n->norm->Bl.width);
67322972b14Sschwarze 				break;
67422972b14Sschwarze 			}
67522972b14Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
67664d728e4Sschwarze 			break;
67749aff9f8Sschwarze 		case MDOC_Offset:
67831e23753Sschwarze 			/* NB: this can be empty! */
67931e23753Sschwarze 			if (n->args->argv[i].sz) {
68031e23753Sschwarze 				offs = n->args->argv[i].value[0];
6818c62fbf5Sschwarze 				dup = (NULL != n->norm->Bl.offs);
68231e23753Sschwarze 				break;
68331e23753Sschwarze 			}
68420fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
685f73abda9Skristaps 			break;
686ddce0b0cSschwarze 		default:
687ddce0b0cSschwarze 			continue;
688f73abda9Skristaps 		}
689f73abda9Skristaps 
6906093755cSschwarze 		/* Check: duplicate auxiliary arguments. */
6916093755cSschwarze 
69220fa2881Sschwarze 		if (dup)
69320fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
6946093755cSschwarze 
69531e23753Sschwarze 		if (comp && ! dup)
6968c62fbf5Sschwarze 			n->norm->Bl.comp = comp;
69731e23753Sschwarze 		if (offs && ! dup)
6988c62fbf5Sschwarze 			n->norm->Bl.offs = offs;
69931e23753Sschwarze 		if (width && ! dup)
7008c62fbf5Sschwarze 			n->norm->Bl.width = width;
70131e23753Sschwarze 
7026093755cSschwarze 		/* Check: multiple list types. */
7036093755cSschwarze 
7048c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
70520fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
7066093755cSschwarze 
7076093755cSschwarze 		/* Assign list type. */
7086093755cSschwarze 
7098c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
7108c62fbf5Sschwarze 			n->norm->Bl.type = lt;
711769ee804Sschwarze 			/* Set column information, too. */
712769ee804Sschwarze 			if (LIST_column == lt) {
7138c62fbf5Sschwarze 				n->norm->Bl.ncols =
714769ee804Sschwarze 				    n->args->argv[i].sz;
71504e980cbSschwarze 				n->norm->Bl.cols = (void *)
716769ee804Sschwarze 				    n->args->argv[i].value;
717769ee804Sschwarze 			}
718769ee804Sschwarze 		}
7196093755cSschwarze 
7206093755cSschwarze 		/* The list type should come first. */
7216093755cSschwarze 
7228c62fbf5Sschwarze 		if (n->norm->Bl.type == LIST__NONE)
7238c62fbf5Sschwarze 			if (n->norm->Bl.width ||
7248c62fbf5Sschwarze 			    n->norm->Bl.offs ||
7258c62fbf5Sschwarze 			    n->norm->Bl.comp)
72620fa2881Sschwarze 				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
7276093755cSschwarze 
7286093755cSschwarze 		continue;
7296093755cSschwarze 	}
7306093755cSschwarze 
7316093755cSschwarze 	/* Allow lists to default to LIST_item. */
7326093755cSschwarze 
7338c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
73420fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
7358c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
7366e03d529Sschwarze 	}
737f73abda9Skristaps 
73864d728e4Sschwarze 	/*
73964d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
74064d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
7415eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
7425eced068Sschwarze 	 * no warning.
74364d728e4Sschwarze 	 */
74464d728e4Sschwarze 
7458c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
74649aff9f8Sschwarze 	case LIST_tag:
7475eced068Sschwarze 		if (NULL == n->norm->Bl.width)
74820fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
749f73abda9Skristaps 		break;
75049aff9f8Sschwarze 	case LIST_column:
7516093755cSschwarze 		/* FALLTHROUGH */
75249aff9f8Sschwarze 	case LIST_diag:
7536093755cSschwarze 		/* FALLTHROUGH */
75449aff9f8Sschwarze 	case LIST_ohang:
7556093755cSschwarze 		/* FALLTHROUGH */
75649aff9f8Sschwarze 	case LIST_inset:
7576093755cSschwarze 		/* FALLTHROUGH */
75849aff9f8Sschwarze 	case LIST_item:
7598c62fbf5Sschwarze 		if (n->norm->Bl.width)
760817ac90bSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
7616093755cSschwarze 		break;
76249aff9f8Sschwarze 	case LIST_bullet:
7635eced068Sschwarze 		/* FALLTHROUGH */
76449aff9f8Sschwarze 	case LIST_dash:
7655eced068Sschwarze 		/* FALLTHROUGH */
76649aff9f8Sschwarze 	case LIST_hyphen:
7675eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7685eced068Sschwarze 			n->norm->Bl.width = "2n";
7695eced068Sschwarze 		break;
77049aff9f8Sschwarze 	case LIST_enum:
7715eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7725eced068Sschwarze 			n->norm->Bl.width = "3n";
7735eced068Sschwarze 		break;
77464d728e4Sschwarze 	default:
775f73abda9Skristaps 		break;
77664d728e4Sschwarze 	}
77764d728e4Sschwarze 
778f73abda9Skristaps 	return(1);
779f73abda9Skristaps }
780f73abda9Skristaps 
781f73abda9Skristaps static int
782f73abda9Skristaps pre_bd(PRE_ARGS)
783f73abda9Skristaps {
78431e23753Sschwarze 	int		  i, dup, comp;
78531e23753Sschwarze 	enum mdoc_disp	  dt;
78631e23753Sschwarze 	const char	 *offs;
787769ee804Sschwarze 	struct mdoc_node *np;
788f73abda9Skristaps 
78931e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
790769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
791769ee804Sschwarze 			assert(n->pending);
792769ee804Sschwarze 			np = n->pending->parent;
793769ee804Sschwarze 		} else
794769ee804Sschwarze 			np = n->parent;
795769ee804Sschwarze 
796769ee804Sschwarze 		assert(np);
797769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
798769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
799f73abda9Skristaps 		return(1);
8006e03d529Sschwarze 	}
801f73abda9Skristaps 
80231e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
80331e23753Sschwarze 		dt = DISP__NONE;
80431e23753Sschwarze 		dup = comp = 0;
80531e23753Sschwarze 		offs = NULL;
80631e23753Sschwarze 
807f73abda9Skristaps 		switch (n->args->argv[i].arg) {
80849aff9f8Sschwarze 		case MDOC_Centred:
80931e23753Sschwarze 			dt = DISP_centred;
81031e23753Sschwarze 			break;
81149aff9f8Sschwarze 		case MDOC_Ragged:
81231e23753Sschwarze 			dt = DISP_ragged;
81331e23753Sschwarze 			break;
81449aff9f8Sschwarze 		case MDOC_Unfilled:
81531e23753Sschwarze 			dt = DISP_unfilled;
81631e23753Sschwarze 			break;
81749aff9f8Sschwarze 		case MDOC_Filled:
81831e23753Sschwarze 			dt = DISP_filled;
81931e23753Sschwarze 			break;
82049aff9f8Sschwarze 		case MDOC_Literal:
82131e23753Sschwarze 			dt = DISP_literal;
822f73abda9Skristaps 			break;
82349aff9f8Sschwarze 		case MDOC_File:
82431e23753Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
8256e03d529Sschwarze 			return(0);
82649aff9f8Sschwarze 		case MDOC_Offset:
82731e23753Sschwarze 			/* NB: this can be empty! */
82831e23753Sschwarze 			if (n->args->argv[i].sz) {
82931e23753Sschwarze 				offs = n->args->argv[i].value[0];
8308c62fbf5Sschwarze 				dup = (NULL != n->norm->Bd.offs);
831f73abda9Skristaps 				break;
832f73abda9Skristaps 			}
83320fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
83431e23753Sschwarze 			break;
83549aff9f8Sschwarze 		case MDOC_Compact:
83631e23753Sschwarze 			comp = 1;
8378c62fbf5Sschwarze 			dup = n->norm->Bd.comp;
83831e23753Sschwarze 			break;
83931e23753Sschwarze 		default:
84031e23753Sschwarze 			abort();
84131e23753Sschwarze 			/* NOTREACHED */
84231e23753Sschwarze 		}
84331e23753Sschwarze 
84431e23753Sschwarze 		/* Check whether we have duplicates. */
84531e23753Sschwarze 
84620fa2881Sschwarze 		if (dup)
84720fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
84831e23753Sschwarze 
84931e23753Sschwarze 		/* Make our auxiliary assignments. */
85031e23753Sschwarze 
85131e23753Sschwarze 		if (offs && ! dup)
8528c62fbf5Sschwarze 			n->norm->Bd.offs = offs;
85331e23753Sschwarze 		if (comp && ! dup)
8548c62fbf5Sschwarze 			n->norm->Bd.comp = comp;
85531e23753Sschwarze 
85631e23753Sschwarze 		/* Check whether a type has already been assigned. */
85731e23753Sschwarze 
8588c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
85920fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
86031e23753Sschwarze 
86131e23753Sschwarze 		/* Make our type assignment. */
86231e23753Sschwarze 
8638c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
8648c62fbf5Sschwarze 			n->norm->Bd.type = dt;
86531e23753Sschwarze 	}
86631e23753Sschwarze 
8678c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
86820fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
8698c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
87031e23753Sschwarze 	}
87131e23753Sschwarze 
87231e23753Sschwarze 	return(1);
873f73abda9Skristaps }
874f73abda9Skristaps 
875f73abda9Skristaps static int
876f73abda9Skristaps pre_ss(PRE_ARGS)
877f73abda9Skristaps {
878f73abda9Skristaps 
879f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
880f73abda9Skristaps 		return(1);
881f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
882f73abda9Skristaps }
883f73abda9Skristaps 
884f73abda9Skristaps static int
885f73abda9Skristaps pre_sh(PRE_ARGS)
886f73abda9Skristaps {
887f73abda9Skristaps 
888f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
889f73abda9Skristaps 		return(1);
890a4c002ecSschwarze 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
891f73abda9Skristaps }
892f73abda9Skristaps 
893f73abda9Skristaps static int
894f73abda9Skristaps pre_it(PRE_ARGS)
895f73abda9Skristaps {
896f73abda9Skristaps 
897f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
898f73abda9Skristaps 		return(1);
89920fa2881Sschwarze 
900f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
901f73abda9Skristaps }
902f73abda9Skristaps 
903f73abda9Skristaps static int
904f73abda9Skristaps pre_an(PRE_ARGS)
905f73abda9Skristaps {
9066475d5b0Sschwarze 	int		 i;
907f73abda9Skristaps 
908769ee804Sschwarze 	if (NULL == n->args)
909f73abda9Skristaps 		return(1);
910769ee804Sschwarze 
9116475d5b0Sschwarze 	for (i = 1; i < (int)n->args->argc; i++)
91220fa2881Sschwarze 		mdoc_pmsg(mdoc, n->args->argv[i].line,
91320fa2881Sschwarze 		    n->args->argv[i].pos, MANDOCERR_IGNARGV);
9147c2be9f8Sschwarze 
915769ee804Sschwarze 	if (MDOC_Split == n->args->argv[0].arg)
9168c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
917769ee804Sschwarze 	else if (MDOC_Nosplit == n->args->argv[0].arg)
9188c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
919769ee804Sschwarze 	else
920769ee804Sschwarze 		abort();
921769ee804Sschwarze 
922769ee804Sschwarze 	return(1);
923f73abda9Skristaps }
924f73abda9Skristaps 
925f73abda9Skristaps static int
92620fa2881Sschwarze pre_std(PRE_ARGS)
927f73abda9Skristaps {
928f73abda9Skristaps 
92920fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
93020fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
93120fa2881Sschwarze 			return(1);
932f73abda9Skristaps 
93320fa2881Sschwarze 	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
9346093755cSschwarze 	return(1);
9356093755cSschwarze }
9366093755cSschwarze 
9376093755cSschwarze static int
938551cd4a8Sschwarze pre_obsolete(PRE_ARGS)
939551cd4a8Sschwarze {
940551cd4a8Sschwarze 
941551cd4a8Sschwarze 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
942551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
943551cd4a8Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
944551cd4a8Sschwarze 	return(1);
945551cd4a8Sschwarze }
946551cd4a8Sschwarze 
947551cd4a8Sschwarze static int
948f73abda9Skristaps pre_dt(PRE_ARGS)
949f73abda9Skristaps {
950f73abda9Skristaps 
951b058e777Sschwarze 	if (NULL == mdoc->meta.date || mdoc->meta.os)
95251fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
95351fcab2fSschwarze 		    n->line, n->pos, "Dt");
95420fa2881Sschwarze 
955f73abda9Skristaps 	if (mdoc->meta.title)
95651fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
95751fcab2fSschwarze 		    n->line, n->pos, "Dt");
95820fa2881Sschwarze 
959f73abda9Skristaps 	return(1);
960f73abda9Skristaps }
961f73abda9Skristaps 
962f73abda9Skristaps static int
963f73abda9Skristaps pre_os(PRE_ARGS)
964f73abda9Skristaps {
965f73abda9Skristaps 
966b058e777Sschwarze 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
96751fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
96851fcab2fSschwarze 		    n->line, n->pos, "Os");
96920fa2881Sschwarze 
970f73abda9Skristaps 	if (mdoc->meta.os)
97151fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
97251fcab2fSschwarze 		    n->line, n->pos, "Os");
97320fa2881Sschwarze 
974f73abda9Skristaps 	return(1);
975f73abda9Skristaps }
976f73abda9Skristaps 
977f73abda9Skristaps static int
978f73abda9Skristaps pre_dd(PRE_ARGS)
979f73abda9Skristaps {
980f73abda9Skristaps 
981f73abda9Skristaps 	if (mdoc->meta.title || mdoc->meta.os)
98251fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
98351fcab2fSschwarze 		    n->line, n->pos, "Dd");
98420fa2881Sschwarze 
985f73abda9Skristaps 	if (mdoc->meta.date)
98651fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
98751fcab2fSschwarze 		    n->line, n->pos, "Dd");
98820fa2881Sschwarze 
989f73abda9Skristaps 	return(1);
990f73abda9Skristaps }
991f73abda9Skristaps 
992f73abda9Skristaps static int
993f73abda9Skristaps post_bf(POST_ARGS)
994f73abda9Skristaps {
995769ee804Sschwarze 	struct mdoc_node *np;
996ddce0b0cSschwarze 	enum mdocargt	  arg;
997f73abda9Skristaps 
998769ee804Sschwarze 	/*
999769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
1000769ee804Sschwarze 	 * element, which contains the goods.
1001769ee804Sschwarze 	 */
1002769ee804Sschwarze 
1003769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
1004769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
1005769ee804Sschwarze 			assert(mdoc->last->pending);
1006769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
1007769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
1008769ee804Sschwarze 			np = mdoc->last->parent->head;
1009769ee804Sschwarze 		} else
1010769ee804Sschwarze 			np = mdoc->last->head;
1011769ee804Sschwarze 
1012769ee804Sschwarze 		assert(np);
1013769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
1014769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
1015f73abda9Skristaps 		return(1);
10166e03d529Sschwarze 	}
1017f73abda9Skristaps 
1018769ee804Sschwarze 	np = mdoc->last;
1019769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
1020769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
102150d41253Sschwarze 
1022769ee804Sschwarze 	/*
1023769ee804Sschwarze 	 * Cannot have both argument and parameter.
1024769ee804Sschwarze 	 * If neither is specified, let it through with a warning.
1025769ee804Sschwarze 	 */
1026f73abda9Skristaps 
1027769ee804Sschwarze 	if (np->parent->args && np->child) {
1028769ee804Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
10296e03d529Sschwarze 		return(0);
103020fa2881Sschwarze 	} else if (NULL == np->parent->args && NULL == np->child) {
103120fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
103220fa2881Sschwarze 		return(1);
103320fa2881Sschwarze 	}
1034769ee804Sschwarze 
1035769ee804Sschwarze 	/* Extract argument into data. */
1036769ee804Sschwarze 
1037769ee804Sschwarze 	if (np->parent->args) {
1038769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
1039769ee804Sschwarze 		if (MDOC_Emphasis == arg)
10408c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
1041769ee804Sschwarze 		else if (MDOC_Literal == arg)
10428c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
1043769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
10448c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
1045769ee804Sschwarze 		else
1046769ee804Sschwarze 			abort();
1047769ee804Sschwarze 		return(1);
1048769ee804Sschwarze 	}
1049769ee804Sschwarze 
1050769ee804Sschwarze 	/* Extract parameter into data. */
1051769ee804Sschwarze 
1052769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
10538c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
1054769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
10558c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
1056769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
10578c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
105820fa2881Sschwarze 	else
105920fa2881Sschwarze 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1060769ee804Sschwarze 
1061769ee804Sschwarze 	return(1);
1062f73abda9Skristaps }
1063f73abda9Skristaps 
1064f73abda9Skristaps static int
106571719887Sschwarze post_lb(POST_ARGS)
106671719887Sschwarze {
106777e000ffSschwarze 	struct mdoc_node	*n;
106877e000ffSschwarze 	const char		*stdlibname;
106977e000ffSschwarze 	char			*libname;
107071719887Sschwarze 
1071bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1072bb648afaSschwarze 
107377e000ffSschwarze 	n = mdoc->last->child;
107420fa2881Sschwarze 
107577e000ffSschwarze 	assert(n);
107677e000ffSschwarze 	assert(MDOC_TEXT == n->type);
107720fa2881Sschwarze 
107877e000ffSschwarze 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
107977e000ffSschwarze 		mandoc_asprintf(&libname,
108077e000ffSschwarze 		    "library \\(lq%s\\(rq", n->string);
108177e000ffSschwarze 	else
108277e000ffSschwarze 		libname = mandoc_strdup(stdlibname);
108320fa2881Sschwarze 
108477e000ffSschwarze 	free(n->string);
108577e000ffSschwarze 	n->string = libname;
108620fa2881Sschwarze 	return(1);
108720fa2881Sschwarze }
108871719887Sschwarze 
108971719887Sschwarze static int
1090b31af00dSschwarze post_eoln(POST_ARGS)
1091b31af00dSschwarze {
1092b31af00dSschwarze 
109320fa2881Sschwarze 	if (mdoc->last->child)
109420fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1095b31af00dSschwarze 	return(1);
1096b31af00dSschwarze }
1097b31af00dSschwarze 
1098b31af00dSschwarze static int
10998521b0bcSschwarze post_vt(POST_ARGS)
11008521b0bcSschwarze {
11018521b0bcSschwarze 	const struct mdoc_node *n;
11028521b0bcSschwarze 
11038521b0bcSschwarze 	/*
11048521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
11058521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1106e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1107e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
11088521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
11098521b0bcSschwarze 	 */
11108521b0bcSschwarze 
11118521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
11128521b0bcSschwarze 		return(1);
11138521b0bcSschwarze 
11148521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1115bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
1116dd25b57cSschwarze 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1117dd25b57cSschwarze 			    n->line, n->pos, mdoc_macronames[n->tok]);
11188521b0bcSschwarze 
11198521b0bcSschwarze 	return(1);
11208521b0bcSschwarze }
11218521b0bcSschwarze 
11228521b0bcSschwarze static int
1123f73abda9Skristaps post_nm(POST_ARGS)
1124f73abda9Skristaps {
112520fa2881Sschwarze 
1126160ac481Sschwarze 	if (NULL != mdoc->meta.name)
112720fa2881Sschwarze 		return(1);
112820fa2881Sschwarze 
112983af2bccSschwarze 	mdoc_deroff(&mdoc->meta.name, mdoc->last);
113020fa2881Sschwarze 
113183af2bccSschwarze 	if (NULL == mdoc->meta.name) {
1132160ac481Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1133160ac481Sschwarze 		mdoc->meta.name = mandoc_strdup("UNKNOWN");
1134160ac481Sschwarze 	}
113520fa2881Sschwarze 	return(1);
113620fa2881Sschwarze }
113720fa2881Sschwarze 
113820fa2881Sschwarze static int
113920fa2881Sschwarze post_literal(POST_ARGS)
114020fa2881Sschwarze {
114120fa2881Sschwarze 
114220fa2881Sschwarze 	/*
114320fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
114420fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
114520fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
114620fa2881Sschwarze 	 * off in general since displays can't be nested.
114720fa2881Sschwarze 	 */
114820fa2881Sschwarze 
114920fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
115020fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
115120fa2881Sschwarze 
115220fa2881Sschwarze 	return(1);
115320fa2881Sschwarze }
115420fa2881Sschwarze 
115520fa2881Sschwarze static int
115620fa2881Sschwarze post_defaults(POST_ARGS)
115720fa2881Sschwarze {
115820fa2881Sschwarze 	struct mdoc_node *nn;
115920fa2881Sschwarze 
116020fa2881Sschwarze 	/*
116120fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
116220fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
116320fa2881Sschwarze 	 * gets an empty string.
116420fa2881Sschwarze 	 */
1165f73abda9Skristaps 
1166f73abda9Skristaps 	if (mdoc->last->child)
1167f73abda9Skristaps 		return(1);
116820fa2881Sschwarze 
116920fa2881Sschwarze 	nn = mdoc->last;
117020fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
117120fa2881Sschwarze 
117220fa2881Sschwarze 	switch (nn->tok) {
117349aff9f8Sschwarze 	case MDOC_Ar:
117420fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
117520fa2881Sschwarze 			return(0);
117620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
117720fa2881Sschwarze 			return(0);
117820fa2881Sschwarze 		break;
117949aff9f8Sschwarze 	case MDOC_At:
118020fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
118120fa2881Sschwarze 			return(0);
118220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
118320fa2881Sschwarze 			return(0);
118420fa2881Sschwarze 		break;
118549aff9f8Sschwarze 	case MDOC_Li:
118620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
118720fa2881Sschwarze 			return(0);
118820fa2881Sschwarze 		break;
118949aff9f8Sschwarze 	case MDOC_Pa:
119020fa2881Sschwarze 		/* FALLTHROUGH */
119149aff9f8Sschwarze 	case MDOC_Mt:
119220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
119320fa2881Sschwarze 			return(0);
119420fa2881Sschwarze 		break;
119520fa2881Sschwarze 	default:
119620fa2881Sschwarze 		abort();
119720fa2881Sschwarze 		/* NOTREACHED */
1198f73abda9Skristaps 	}
1199f73abda9Skristaps 
120020fa2881Sschwarze 	mdoc->last = nn;
120120fa2881Sschwarze 	return(1);
120220fa2881Sschwarze }
1203f73abda9Skristaps 
1204f73abda9Skristaps static int
1205f73abda9Skristaps post_at(POST_ARGS)
1206f73abda9Skristaps {
12070b2f1307Sschwarze 	struct mdoc_node	*n;
12080b2f1307Sschwarze 	const char		*std_att;
12090b2f1307Sschwarze 	char			*att;
121020fa2881Sschwarze 
121120fa2881Sschwarze 	/*
121220fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
121320fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
121420fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
121520fa2881Sschwarze 	 */
1216f73abda9Skristaps 
12170b2f1307Sschwarze 	if (NULL == (n = mdoc->last->child))
1218f73abda9Skristaps 		return(1);
121920fa2881Sschwarze 
12200b2f1307Sschwarze 	assert(MDOC_TEXT == n->type);
12210b2f1307Sschwarze 	if (NULL == (std_att = mdoc_a2att(n->string))) {
122220fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
12230b2f1307Sschwarze 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
12240b2f1307Sschwarze 	} else
12250b2f1307Sschwarze 		att = mandoc_strdup(std_att);
1226f73abda9Skristaps 
12270b2f1307Sschwarze 	free(n->string);
12280b2f1307Sschwarze 	n->string = att;
122920fa2881Sschwarze 	return(1);
123020fa2881Sschwarze }
1231f73abda9Skristaps 
1232f73abda9Skristaps static int
1233f73abda9Skristaps post_an(POST_ARGS)
1234f73abda9Skristaps {
1235769ee804Sschwarze 	struct mdoc_node *np;
1236f73abda9Skristaps 
1237769ee804Sschwarze 	np = mdoc->last;
1238e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1239e7a93ef3Sschwarze 		if (0 == np->child)
1240e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1241e7a93ef3Sschwarze 	} else if (np->child)
1242bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
124320fa2881Sschwarze 
124420fa2881Sschwarze 	return(1);
1245f73abda9Skristaps }
1246f73abda9Skristaps 
1247f73abda9Skristaps static int
1248551cd4a8Sschwarze post_en(POST_ARGS)
1249551cd4a8Sschwarze {
1250551cd4a8Sschwarze 
1251551cd4a8Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
1252551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1253551cd4a8Sschwarze 	return(1);
1254551cd4a8Sschwarze }
1255551cd4a8Sschwarze 
1256551cd4a8Sschwarze static int
1257551cd4a8Sschwarze post_es(POST_ARGS)
1258551cd4a8Sschwarze {
1259551cd4a8Sschwarze 
1260551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1261551cd4a8Sschwarze 	return(1);
1262551cd4a8Sschwarze }
1263551cd4a8Sschwarze 
1264551cd4a8Sschwarze static int
1265f73abda9Skristaps post_it(POST_ARGS)
1266f73abda9Skristaps {
126719a69263Sschwarze 	int		  i, cols;
12686093755cSschwarze 	enum mdoc_list	  lt;
1269*9530682eSschwarze 	struct mdoc_node *nbl, *nit, *nch;
12706093755cSschwarze 	enum mandocerr	  er;
1271f73abda9Skristaps 
1272*9530682eSschwarze 	nit = mdoc->last;
1273*9530682eSschwarze 	if (MDOC_BLOCK != nit->type)
1274f73abda9Skristaps 		return(1);
1275f73abda9Skristaps 
1276*9530682eSschwarze 	nbl = nit->parent->parent;
1277*9530682eSschwarze 	lt = nbl->norm->Bl.type;
12786093755cSschwarze 
12796093755cSschwarze 	if (LIST__NONE == lt) {
1280*9530682eSschwarze 		mdoc_nmsg(mdoc, nit, MANDOCERR_LISTTYPE);
128120fa2881Sschwarze 		return(1);
12826e03d529Sschwarze 	}
1283f73abda9Skristaps 
12846093755cSschwarze 	switch (lt) {
128549aff9f8Sschwarze 	case LIST_tag:
1286*9530682eSschwarze 		/* FALLTHROUGH */
128749aff9f8Sschwarze 	case LIST_hang:
1288f73abda9Skristaps 		/* FALLTHROUGH */
128949aff9f8Sschwarze 	case LIST_ohang:
1290f73abda9Skristaps 		/* FALLTHROUGH */
129149aff9f8Sschwarze 	case LIST_inset:
1292f73abda9Skristaps 		/* FALLTHROUGH */
129349aff9f8Sschwarze 	case LIST_diag:
1294*9530682eSschwarze 		if (NULL == nit->head->child)
1295*9530682eSschwarze 			mandoc_msg(MANDOCERR_IT_NOHEAD,
1296*9530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
1297*9530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1298f73abda9Skristaps 		break;
129949aff9f8Sschwarze 	case LIST_bullet:
1300f73abda9Skristaps 		/* FALLTHROUGH */
130149aff9f8Sschwarze 	case LIST_dash:
1302f73abda9Skristaps 		/* FALLTHROUGH */
130349aff9f8Sschwarze 	case LIST_enum:
1304f73abda9Skristaps 		/* FALLTHROUGH */
130549aff9f8Sschwarze 	case LIST_hyphen:
1306*9530682eSschwarze 		if (NULL == nit->body->child)
1307*9530682eSschwarze 			mdoc_nmsg(mdoc, nit, MANDOCERR_NOBODY);
1308f73abda9Skristaps 		/* FALLTHROUGH */
130949aff9f8Sschwarze 	case LIST_item:
1310*9530682eSschwarze 		if (NULL != nit->head->child)
1311*9530682eSschwarze 			mdoc_nmsg(mdoc, nit, MANDOCERR_ARGSLOST);
1312f73abda9Skristaps 		break;
131349aff9f8Sschwarze 	case LIST_column:
1314*9530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
13156093755cSschwarze 
1316*9530682eSschwarze 		assert(NULL == nit->head->child);
13176093755cSschwarze 
1318*9530682eSschwarze 		if (NULL == nit->body->child)
1319*9530682eSschwarze 			mdoc_nmsg(mdoc, nit, MANDOCERR_NOBODY);
13206093755cSschwarze 
1321*9530682eSschwarze 		for (i = 0, nch = nit->child; nch; nch = nch->next)
1322*9530682eSschwarze 			if (MDOC_BODY == nch->type)
1323f73abda9Skristaps 				i++;
132453292e81Sschwarze 
13256093755cSschwarze 		if (i < cols)
13266093755cSschwarze 			er = MANDOCERR_ARGCOUNT;
13276093755cSschwarze 		else if (i == cols || i == cols + 1)
1328f73abda9Skristaps 			break;
13296093755cSschwarze 		else
13306093755cSschwarze 			er = MANDOCERR_SYNTARGCOUNT;
133153292e81Sschwarze 
1332*9530682eSschwarze 		mandoc_vmsg(er, mdoc->parse, nit->line, nit->pos,
13336e03d529Sschwarze 		    "columns == %d (have %d)", cols, i);
133419a69263Sschwarze 		return(MANDOCERR_ARGCOUNT == er);
1335f73abda9Skristaps 	default:
1336f73abda9Skristaps 		break;
1337f73abda9Skristaps 	}
1338f73abda9Skristaps 
1339f73abda9Skristaps 	return(1);
1340f73abda9Skristaps }
1341f73abda9Skristaps 
134220fa2881Sschwarze static int
134320fa2881Sschwarze post_bl_block(POST_ARGS)
134420fa2881Sschwarze {
1345bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
134620fa2881Sschwarze 
134720fa2881Sschwarze 	/*
134820fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
134920fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
135020fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
135120fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
135220fa2881Sschwarze 	 * rewritten into real lengths).
135320fa2881Sschwarze 	 */
135420fa2881Sschwarze 
135520fa2881Sschwarze 	n = mdoc->last;
135620fa2881Sschwarze 
13578c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
13588c62fbf5Sschwarze 	    NULL == n->norm->Bl.width) {
135920fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
136020fa2881Sschwarze 			return(0);
1361bb99f0faSschwarze 		assert(n->norm->Bl.width);
13628c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
136320fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
136420fa2881Sschwarze 			return(0);
13658c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1366bb99f0faSschwarze 	}
1367bb99f0faSschwarze 
1368bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1369bb99f0faSschwarze 		if (NULL == ni->body)
1370bb99f0faSschwarze 			continue;
1371bb99f0faSschwarze 		nc = ni->body->last;
1372bb99f0faSschwarze 		while (NULL != nc) {
1373bb99f0faSschwarze 			switch (nc->tok) {
137449aff9f8Sschwarze 			case MDOC_Pp:
1375bb99f0faSschwarze 				/* FALLTHROUGH */
137649aff9f8Sschwarze 			case MDOC_Lp:
1377bb99f0faSschwarze 				/* FALLTHROUGH */
137849aff9f8Sschwarze 			case MDOC_br:
1379bb99f0faSschwarze 				break;
1380bb99f0faSschwarze 			default:
1381bb99f0faSschwarze 				nc = NULL;
1382bb99f0faSschwarze 				continue;
1383bb99f0faSschwarze 			}
1384bb99f0faSschwarze 			if (NULL == ni->next) {
138520369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
138620369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
138720369664Sschwarze 				    mdoc_macronames[nc->tok]);
1388bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1389bb99f0faSschwarze 					return(0);
1390bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1391bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
139220369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
139320369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
139420369664Sschwarze 				    "%s before It",
139520369664Sschwarze 				    mdoc_macronames[nc->tok]);
1396bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1397bb99f0faSschwarze 			} else
1398bb99f0faSschwarze 				break;
1399bb99f0faSschwarze 			nc = ni->body->last;
1400bb99f0faSschwarze 		}
1401bb99f0faSschwarze 	}
140220fa2881Sschwarze 	return(1);
140320fa2881Sschwarze }
140420fa2881Sschwarze 
140520fa2881Sschwarze static int
140620fa2881Sschwarze post_bl_block_width(POST_ARGS)
140720fa2881Sschwarze {
140820fa2881Sschwarze 	size_t		  width;
140920fa2881Sschwarze 	int		  i;
141020fa2881Sschwarze 	enum mdoct	  tok;
141120fa2881Sschwarze 	struct mdoc_node *n;
141247813146Sschwarze 	char		  buf[24];
141320fa2881Sschwarze 
141420fa2881Sschwarze 	n = mdoc->last;
141520fa2881Sschwarze 
141620fa2881Sschwarze 	/*
141720fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
141820fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
141920fa2881Sschwarze 	 * literal string, or a scaling width.
142020fa2881Sschwarze 	 *
142120fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
142220fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
142320fa2881Sschwarze 	 */
142420fa2881Sschwarze 
14258c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
142620fa2881Sschwarze 		width = 6;
14278c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
142820fa2881Sschwarze 		return(1);
142919a69263Sschwarze 	else if (0 == (width = macro2len(tok)))  {
143020fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
143120fa2881Sschwarze 		return(1);
143220fa2881Sschwarze 	}
143320fa2881Sschwarze 
143420fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
143520fa2881Sschwarze 
143620fa2881Sschwarze 	assert(n->args);
143720fa2881Sschwarze 
143820fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
143920fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
144020fa2881Sschwarze 			break;
144120fa2881Sschwarze 
144220fa2881Sschwarze 	assert(i < (int)n->args->argc);
144320fa2881Sschwarze 
144447813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
144520fa2881Sschwarze 	free(n->args->argv[i].value[0]);
144620fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
144720fa2881Sschwarze 
144820fa2881Sschwarze 	/* Set our width! */
14498c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
145020fa2881Sschwarze 	return(1);
145120fa2881Sschwarze }
145220fa2881Sschwarze 
145320fa2881Sschwarze static int
145420fa2881Sschwarze post_bl_block_tag(POST_ARGS)
145520fa2881Sschwarze {
145620fa2881Sschwarze 	struct mdoc_node *n, *nn;
145720fa2881Sschwarze 	size_t		  sz, ssz;
145820fa2881Sschwarze 	int		  i;
145947813146Sschwarze 	char		  buf[24];
146020fa2881Sschwarze 
146120fa2881Sschwarze 	/*
146220fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
146320fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
146420fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
146520fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
146620fa2881Sschwarze 	 */
146720fa2881Sschwarze 
146820fa2881Sschwarze 	sz = 10;
146920fa2881Sschwarze 	n = mdoc->last;
147020fa2881Sschwarze 
147120fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
147220fa2881Sschwarze 		if (MDOC_It != nn->tok)
147320fa2881Sschwarze 			continue;
147420fa2881Sschwarze 
147520fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
147620fa2881Sschwarze 		nn = nn->head->child;
147720fa2881Sschwarze 
147820fa2881Sschwarze 		if (nn == NULL)
147920fa2881Sschwarze 			break;
148020fa2881Sschwarze 
148120fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
148220fa2881Sschwarze 			sz = strlen(nn->string) + 1;
148320fa2881Sschwarze 			break;
148420fa2881Sschwarze 		}
148520fa2881Sschwarze 
148619a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
148720fa2881Sschwarze 			sz = ssz;
148820fa2881Sschwarze 
148920fa2881Sschwarze 		break;
149020fa2881Sschwarze 	}
149120fa2881Sschwarze 
149220fa2881Sschwarze 	/* Defaults to ten ens. */
149320fa2881Sschwarze 
149447813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
149520fa2881Sschwarze 
149620fa2881Sschwarze 	/*
149720fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
149820fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
149920fa2881Sschwarze 	 */
150020fa2881Sschwarze 
150120fa2881Sschwarze 	assert(n->args);
150220fa2881Sschwarze 	i = (int)(n->args->argc)++;
150320fa2881Sschwarze 
15048286bf36Sschwarze 	n->args->argv = mandoc_reallocarray(n->args->argv,
15058286bf36Sschwarze 	    n->args->argc, sizeof(struct mdoc_argv));
150620fa2881Sschwarze 
150720fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
150820fa2881Sschwarze 	n->args->argv[i].line = n->line;
150920fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
151020fa2881Sschwarze 	n->args->argv[i].sz = 1;
151120fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
151220fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
151320fa2881Sschwarze 
151420fa2881Sschwarze 	/* Set our width! */
15158c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
151620fa2881Sschwarze 	return(1);
151720fa2881Sschwarze }
151820fa2881Sschwarze 
1519f73abda9Skristaps static int
1520395185ccSschwarze post_bl_head(POST_ARGS)
1521395185ccSschwarze {
152220fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
152320fa2881Sschwarze 	int		  i, j;
1524395185ccSschwarze 
15258c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
152620fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
152720fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1528395185ccSschwarze 
152920fa2881Sschwarze 	/*
153020fa2881Sschwarze 	 * Convert old-style lists, where the column width specifiers
153120fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
153220fa2881Sschwarze 	 * lists where they're argument values following -column.
153320fa2881Sschwarze 	 */
153420fa2881Sschwarze 
153520fa2881Sschwarze 	/* First, disallow both types and allow normal-form. */
153620fa2881Sschwarze 
153720fa2881Sschwarze 	/*
153820fa2881Sschwarze 	 * TODO: technically, we can accept both and just merge the two
153920fa2881Sschwarze 	 * lists, but I'll leave that for another day.
154020fa2881Sschwarze 	 */
154120fa2881Sschwarze 
15428c62fbf5Sschwarze 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
154320fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
15446093755cSschwarze 		return(0);
154520fa2881Sschwarze 	} else if (NULL == mdoc->last->child)
154620fa2881Sschwarze 		return(1);
154720fa2881Sschwarze 
154820fa2881Sschwarze 	np = mdoc->last->parent;
154920fa2881Sschwarze 	assert(np->args);
155020fa2881Sschwarze 
155120fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
155220fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
155320fa2881Sschwarze 			break;
155420fa2881Sschwarze 
155520fa2881Sschwarze 	assert(j < (int)np->args->argc);
155620fa2881Sschwarze 	assert(0 == np->args->argv[j].sz);
155720fa2881Sschwarze 
155820fa2881Sschwarze 	/*
1559a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
156020fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
156120fa2881Sschwarze 	 * column field.  Then, delete the head children.
156220fa2881Sschwarze 	 */
156320fa2881Sschwarze 
156420fa2881Sschwarze 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
15658286bf36Sschwarze 	np->args->argv[j].value = mandoc_reallocarray(NULL,
15668286bf36Sschwarze 	    (size_t)mdoc->last->nchild, sizeof(char *));
156720fa2881Sschwarze 
15688c62fbf5Sschwarze 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
156904e980cbSschwarze 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
157020fa2881Sschwarze 
157120fa2881Sschwarze 	for (i = 0, nn = mdoc->last->child; nn; i++) {
157220fa2881Sschwarze 		np->args->argv[j].value[i] = nn->string;
157320fa2881Sschwarze 		nn->string = NULL;
157420fa2881Sschwarze 		nnp = nn;
157520fa2881Sschwarze 		nn = nn->next;
157620fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1577395185ccSschwarze 	}
157820fa2881Sschwarze 
157920fa2881Sschwarze 	mdoc->last->nchild = 0;
158020fa2881Sschwarze 	mdoc->last->child = NULL;
158120fa2881Sschwarze 
15826093755cSschwarze 	return(1);
1583b16e7ddfSschwarze }
1584b16e7ddfSschwarze 
1585395185ccSschwarze static int
1586f73abda9Skristaps post_bl(POST_ARGS)
1587f73abda9Skristaps {
15882a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
15892a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
15902a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1591f73abda9Skristaps 
15922a427d60Sschwarze 	nbody = mdoc->last;
15932a427d60Sschwarze 	switch (nbody->type) {
159449aff9f8Sschwarze 	case MDOC_BLOCK:
159520fa2881Sschwarze 		return(post_bl_block(mdoc));
159649aff9f8Sschwarze 	case MDOC_HEAD:
15972a427d60Sschwarze 		return(post_bl_head(mdoc));
159849aff9f8Sschwarze 	case MDOC_BODY:
1599f6127a73Sschwarze 		break;
16002a427d60Sschwarze 	default:
16012a427d60Sschwarze 		return(1);
1602f6127a73Sschwarze 	}
1603f6127a73Sschwarze 
16042a427d60Sschwarze 	nchild = nbody->child;
16052a427d60Sschwarze 	while (NULL != nchild) {
16062a427d60Sschwarze 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
16072a427d60Sschwarze 			nchild = nchild->next;
16082a427d60Sschwarze 			continue;
16092a427d60Sschwarze 		}
16102a427d60Sschwarze 
1611dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1612dd25b57cSschwarze 		    nchild->line, nchild->pos,
1613dd25b57cSschwarze 		    mdoc_macronames[nchild->tok]);
16142a427d60Sschwarze 
16152a427d60Sschwarze 		/*
16162a427d60Sschwarze 		 * Move the node out of the Bl block.
16172a427d60Sschwarze 		 * First, collect all required node pointers.
16182a427d60Sschwarze 		 */
16192a427d60Sschwarze 
16202a427d60Sschwarze 		nblock  = nbody->parent;
16212a427d60Sschwarze 		nprev   = nblock->prev;
16222a427d60Sschwarze 		nparent = nblock->parent;
16232a427d60Sschwarze 		nnext   = nchild->next;
16242a427d60Sschwarze 
16252a427d60Sschwarze 		/*
16262a427d60Sschwarze 		 * Unlink this child.
16272a427d60Sschwarze 		 */
16282a427d60Sschwarze 
16292a427d60Sschwarze 		assert(NULL == nchild->prev);
16302a427d60Sschwarze 		if (0 == --nbody->nchild) {
16312a427d60Sschwarze 			nbody->child = NULL;
16322a427d60Sschwarze 			nbody->last  = NULL;
16332a427d60Sschwarze 			assert(NULL == nnext);
16342a427d60Sschwarze 		} else {
16352a427d60Sschwarze 			nbody->child = nnext;
16362a427d60Sschwarze 			nnext->prev = NULL;
16372a427d60Sschwarze 		}
16382a427d60Sschwarze 
16392a427d60Sschwarze 		/*
16402a427d60Sschwarze 		 * Relink this child.
16412a427d60Sschwarze 		 */
16422a427d60Sschwarze 
16432a427d60Sschwarze 		nchild->parent = nparent;
16442a427d60Sschwarze 		nchild->prev   = nprev;
16452a427d60Sschwarze 		nchild->next   = nblock;
16462a427d60Sschwarze 
16472a427d60Sschwarze 		nblock->prev = nchild;
16482a427d60Sschwarze 		nparent->nchild++;
16492a427d60Sschwarze 		if (NULL == nprev)
16502a427d60Sschwarze 			nparent->child = nchild;
16512a427d60Sschwarze 		else
16522a427d60Sschwarze 			nprev->next = nchild;
16532a427d60Sschwarze 
16542a427d60Sschwarze 		nchild = nnext;
1655f73abda9Skristaps 	}
1656f73abda9Skristaps 
1657f73abda9Skristaps 	return(1);
1658f73abda9Skristaps }
1659f73abda9Skristaps 
1660f73abda9Skristaps static int
1661f73abda9Skristaps ebool(struct mdoc *mdoc)
1662f73abda9Skristaps {
1663f73abda9Skristaps 
1664bb648afaSschwarze 	if (NULL == mdoc->last->child) {
1665f9e7bf99Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1666f9e7bf99Sschwarze 			mdoc->flags ^= MDOC_SMOFF;
1667f73abda9Skristaps 		return(1);
1668bb648afaSschwarze 	}
1669f9e7bf99Sschwarze 
1670f9e7bf99Sschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1671f73abda9Skristaps 
167220fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
167320fa2881Sschwarze 
1674ec2beb53Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "on")) {
1675ec2beb53Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1676ec2beb53Sschwarze 			mdoc->flags &= ~MDOC_SMOFF;
167720fa2881Sschwarze 		return(1);
1678ec2beb53Sschwarze 	}
1679ec2beb53Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "off")) {
1680ec2beb53Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1681ec2beb53Sschwarze 			mdoc->flags |= MDOC_SMOFF;
168220fa2881Sschwarze 		return(1);
1683ec2beb53Sschwarze 	}
168420fa2881Sschwarze 
168520fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
168620fa2881Sschwarze 	return(1);
168720fa2881Sschwarze }
1688f73abda9Skristaps 
1689f73abda9Skristaps static int
1690f73abda9Skristaps post_root(POST_ARGS)
1691f73abda9Skristaps {
169243edbcc8Sschwarze 	int		  ret;
169320fa2881Sschwarze 	struct mdoc_node *n;
1694f73abda9Skristaps 
169543edbcc8Sschwarze 	ret = 1;
169620fa2881Sschwarze 
169720fa2881Sschwarze 	/* Check that we have a finished prologue. */
169820fa2881Sschwarze 
169920fa2881Sschwarze 	if ( ! (MDOC_PBODY & mdoc->flags)) {
170043edbcc8Sschwarze 		ret = 0;
17016e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1702f73abda9Skristaps 	}
1703f73abda9Skristaps 
170420fa2881Sschwarze 	n = mdoc->first;
170520fa2881Sschwarze 	assert(n);
170620fa2881Sschwarze 
170720fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
170820fa2881Sschwarze 
170943edbcc8Sschwarze 	if (NULL == n->child)
171043edbcc8Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY);
171151fcab2fSschwarze 	else if (MDOC_Sh != n->child->tok)
171251fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
171351fcab2fSschwarze 		    n->child->line, n->child->pos,
171451fcab2fSschwarze 		    mdoc_macronames[n->child->tok]);
171520fa2881Sschwarze 
171643edbcc8Sschwarze 	return(ret);
171720fa2881Sschwarze }
1718f73abda9Skristaps 
1719f73abda9Skristaps static int
1720f73abda9Skristaps post_st(POST_ARGS)
1721f73abda9Skristaps {
1722bb648afaSschwarze 	struct mdoc_node	 *ch;
172320fa2881Sschwarze 	const char		 *p;
1724f73abda9Skristaps 
1725bb648afaSschwarze 	if (NULL == (ch = mdoc->last->child)) {
1726307e5a07Sschwarze 		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1727307e5a07Sschwarze 		    mdoc->last->line, mdoc->last->pos,
1728307e5a07Sschwarze 		    mdoc_macronames[mdoc->last->tok]);
1729bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1730bb648afaSschwarze 		return(1);
1731bb648afaSschwarze 	}
173220fa2881Sschwarze 
1733bb648afaSschwarze 	assert(MDOC_TEXT == ch->type);
173420fa2881Sschwarze 
1735bb648afaSschwarze 	if (NULL == (p = mdoc_a2st(ch->string))) {
173620fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
173720fa2881Sschwarze 		mdoc_node_delete(mdoc, mdoc->last);
173820fa2881Sschwarze 	} else {
1739bb648afaSschwarze 		free(ch->string);
1740bb648afaSschwarze 		ch->string = mandoc_strdup(p);
1741f73abda9Skristaps 	}
1742f73abda9Skristaps 
174320fa2881Sschwarze 	return(1);
174420fa2881Sschwarze }
1745f73abda9Skristaps 
1746f73abda9Skristaps static int
1747011fe33bSschwarze post_rs(POST_ARGS)
1748011fe33bSschwarze {
174920fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
175020fa2881Sschwarze 	int		  i, j;
1751011fe33bSschwarze 
1752bb648afaSschwarze 	switch (mdoc->last->type) {
175349aff9f8Sschwarze 	case MDOC_HEAD:
1754bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1755011fe33bSschwarze 		return(1);
175649aff9f8Sschwarze 	case MDOC_BODY:
1757bb648afaSschwarze 		if (mdoc->last->child)
1758bb648afaSschwarze 			break;
1759bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1760bb648afaSschwarze 		return(1);
1761bb648afaSschwarze 	default:
1762bb648afaSschwarze 		return(1);
1763bb648afaSschwarze 	}
1764011fe33bSschwarze 
176520fa2881Sschwarze 	/*
176620fa2881Sschwarze 	 * Make sure only certain types of nodes are allowed within the
176720fa2881Sschwarze 	 * the `Rs' body.  Delete offending nodes and raise a warning.
176820fa2881Sschwarze 	 * Do this before re-ordering for the sake of clarity.
176920fa2881Sschwarze 	 */
177020fa2881Sschwarze 
177120fa2881Sschwarze 	next = NULL;
177220fa2881Sschwarze 	for (nn = mdoc->last->child; nn; nn = next) {
177320fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
177420fa2881Sschwarze 			if (nn->tok == rsord[i])
1775011fe33bSschwarze 				break;
177620fa2881Sschwarze 
177720fa2881Sschwarze 		if (i < RSORD_MAX) {
17785d273f35Sschwarze 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
17795d273f35Sschwarze 				mdoc->last->norm->Rs.quote_T++;
178020fa2881Sschwarze 			next = nn->next;
178120fa2881Sschwarze 			continue;
178220fa2881Sschwarze 		}
178320fa2881Sschwarze 
178420fa2881Sschwarze 		next = nn->next;
1785dd25b57cSschwarze 		mandoc_msg(MANDOCERR_RS_SKIP, mdoc->parse,
1786dd25b57cSschwarze 		    nn->line, nn->pos, mdoc_macronames[nn->tok]);
178720fa2881Sschwarze 		mdoc_node_delete(mdoc, nn);
178820fa2881Sschwarze 	}
178920fa2881Sschwarze 
179020fa2881Sschwarze 	/*
1791c6176538Sschwarze 	 * Nothing to sort if only invalid nodes were found
1792c6176538Sschwarze 	 * inside the `Rs' body.
1793c6176538Sschwarze 	 */
1794c6176538Sschwarze 
1795c6176538Sschwarze 	if (NULL == mdoc->last->child)
1796c6176538Sschwarze 		return(1);
1797c6176538Sschwarze 
1798c6176538Sschwarze 	/*
179920fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
180020fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
180120fa2881Sschwarze 	 * and correctly order it.  This is a insertion sort.
180220fa2881Sschwarze 	 */
180320fa2881Sschwarze 
180420fa2881Sschwarze 	next = NULL;
180520fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
180620fa2881Sschwarze 		/* Determine order of `nn'. */
180720fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
180820fa2881Sschwarze 			if (rsord[i] == nn->tok)
180920fa2881Sschwarze 				break;
181020fa2881Sschwarze 
181120fa2881Sschwarze 		/*
181220fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
181320fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
181420fa2881Sschwarze 		 * just re-ordering, there's no need for the
181520fa2881Sschwarze 		 * full unlink process.
181620fa2881Sschwarze 		 */
181720fa2881Sschwarze 
181820fa2881Sschwarze 		if (NULL != (next = nn->next))
181920fa2881Sschwarze 			next->prev = nn->prev;
182020fa2881Sschwarze 
182120fa2881Sschwarze 		if (NULL != (prev = nn->prev))
182220fa2881Sschwarze 			prev->next = nn->next;
182320fa2881Sschwarze 
182420fa2881Sschwarze 		nn->prev = nn->next = NULL;
182520fa2881Sschwarze 
182620fa2881Sschwarze 		/*
182720fa2881Sschwarze 		 * Scan back until we reach a node that's
182820fa2881Sschwarze 		 * ordered before `nn'.
182920fa2881Sschwarze 		 */
183020fa2881Sschwarze 
183120fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
183220fa2881Sschwarze 			/* Determine order of `prev'. */
183320fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
183420fa2881Sschwarze 				if (rsord[j] == prev->tok)
183520fa2881Sschwarze 					break;
183620fa2881Sschwarze 
183720fa2881Sschwarze 			if (j <= i)
183820fa2881Sschwarze 				break;
183920fa2881Sschwarze 		}
184020fa2881Sschwarze 
184120fa2881Sschwarze 		/*
184220fa2881Sschwarze 		 * Set `nn' back into its correct place in front
184320fa2881Sschwarze 		 * of the `prev' node.
184420fa2881Sschwarze 		 */
184520fa2881Sschwarze 
184620fa2881Sschwarze 		nn->prev = prev;
184720fa2881Sschwarze 
184820fa2881Sschwarze 		if (prev) {
184920fa2881Sschwarze 			if (prev->next)
185020fa2881Sschwarze 				prev->next->prev = nn;
185120fa2881Sschwarze 			nn->next = prev->next;
185220fa2881Sschwarze 			prev->next = nn;
185320fa2881Sschwarze 		} else {
185420fa2881Sschwarze 			mdoc->last->child->prev = nn;
185520fa2881Sschwarze 			nn->next = mdoc->last->child;
185620fa2881Sschwarze 			mdoc->last->child = nn;
185720fa2881Sschwarze 		}
1858011fe33bSschwarze 	}
1859011fe33bSschwarze 
1860011fe33bSschwarze 	return(1);
1861011fe33bSschwarze }
1862011fe33bSschwarze 
18634039b21cSschwarze /*
18644039b21cSschwarze  * For some arguments of some macros,
18654039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
18664039b21cSschwarze  */
18674039b21cSschwarze static int
18684039b21cSschwarze post_hyph(POST_ARGS)
18694039b21cSschwarze {
18704039b21cSschwarze 	struct mdoc_node	*n, *nch;
18714039b21cSschwarze 	char			*cp;
18724039b21cSschwarze 
18734039b21cSschwarze 	n = mdoc->last;
18744039b21cSschwarze 	switch (n->type) {
187549aff9f8Sschwarze 	case MDOC_HEAD:
18764039b21cSschwarze 		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
18774039b21cSschwarze 			break;
18784039b21cSschwarze 		return(1);
187949aff9f8Sschwarze 	case MDOC_BODY:
18804039b21cSschwarze 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
18814039b21cSschwarze 			break;
18824039b21cSschwarze 		return(1);
188349aff9f8Sschwarze 	case MDOC_ELEM:
18844039b21cSschwarze 		break;
18854039b21cSschwarze 	default:
18864039b21cSschwarze 		return(1);
18874039b21cSschwarze 	}
18884039b21cSschwarze 
18894039b21cSschwarze 	for (nch = n->child; nch; nch = nch->next) {
18904039b21cSschwarze 		if (MDOC_TEXT != nch->type)
18914039b21cSschwarze 			continue;
18924039b21cSschwarze 		cp = nch->string;
1893b7e2b14eSschwarze 		if ('\0' == *cp)
18944039b21cSschwarze 			continue;
18954039b21cSschwarze 		while ('\0' != *(++cp))
18964039b21cSschwarze 			if ('-' == *cp &&
18974039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
18984039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
18994039b21cSschwarze 				*cp = ASCII_HYPH;
19004039b21cSschwarze 	}
19014039b21cSschwarze 	return(1);
19024039b21cSschwarze }
19034039b21cSschwarze 
1904011fe33bSschwarze static int
1905af216717Sschwarze post_ns(POST_ARGS)
1906af216717Sschwarze {
1907af216717Sschwarze 
1908af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
1909b723eac2Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP);
1910af216717Sschwarze 	return(1);
1911af216717Sschwarze }
1912af216717Sschwarze 
1913af216717Sschwarze static int
1914f73abda9Skristaps post_sh(POST_ARGS)
1915f73abda9Skristaps {
1916f73abda9Skristaps 
1917f73abda9Skristaps 	if (MDOC_HEAD == mdoc->last->type)
1918f73abda9Skristaps 		return(post_sh_head(mdoc));
1919f73abda9Skristaps 	if (MDOC_BODY == mdoc->last->type)
1920f73abda9Skristaps 		return(post_sh_body(mdoc));
1921f73abda9Skristaps 
1922f73abda9Skristaps 	return(1);
1923f73abda9Skristaps }
1924f73abda9Skristaps 
1925f73abda9Skristaps static int
1926f73abda9Skristaps post_sh_body(POST_ARGS)
1927f73abda9Skristaps {
1928f73abda9Skristaps 	struct mdoc_node *n;
1929f73abda9Skristaps 
1930f8c9d6f2Sschwarze 	if (SEC_NAME != mdoc->lastsec)
1931f73abda9Skristaps 		return(1);
1932f73abda9Skristaps 
1933f73abda9Skristaps 	/*
1934f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1935f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1936f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1937f73abda9Skristaps 	 */
1938f73abda9Skristaps 
193920fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
194051fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
194151fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, "empty");
194220fa2881Sschwarze 		return(1);
194320fa2881Sschwarze 	}
1944f73abda9Skristaps 
1945f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1946f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1947f73abda9Skristaps 			continue;
1948f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1949f73abda9Skristaps 			continue;
195051fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
195151fcab2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1952f73abda9Skristaps 	}
1953f73abda9Skristaps 
195449d529b5Sschwarze 	assert(n);
19554602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1956f73abda9Skristaps 		return(1);
1957f73abda9Skristaps 
195851fcab2fSschwarze 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
195951fcab2fSschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
196020fa2881Sschwarze 	return(1);
196120fa2881Sschwarze }
1962f73abda9Skristaps 
1963f73abda9Skristaps static int
1964f73abda9Skristaps post_sh_head(POST_ARGS)
1965f73abda9Skristaps {
1966a2cff342Sschwarze 	struct mdoc_node *n;
196751fcab2fSschwarze 	const char	*goodsec;
196846133849Sschwarze 	char		*secname;
1969f73abda9Skristaps 	enum mdoc_sec	 sec;
1970f73abda9Skristaps 
1971f73abda9Skristaps 	/*
1972f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
197320fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
197420fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
197520fa2881Sschwarze 	 * manual sections.
1976f73abda9Skristaps 	 */
1977f73abda9Skristaps 
197883af2bccSschwarze 	secname = NULL;
197904e980cbSschwarze 	sec = SEC_CUSTOM;
198046133849Sschwarze 	mdoc_deroff(&secname, mdoc->last);
198146133849Sschwarze 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1982f73abda9Skristaps 
198320fa2881Sschwarze 	/* The NAME should be first. */
1984f73abda9Skristaps 
1985fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
198651fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
198751fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
198820fa2881Sschwarze 
198920fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
199020fa2881Sschwarze 
199122881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
199275088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
199320fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
199422881299Sschwarze 	} else {
199575088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
199620fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
199722881299Sschwarze 	}
199820fa2881Sschwarze 
199920fa2881Sschwarze 	/* Mark our last section. */
200020fa2881Sschwarze 
200120fa2881Sschwarze 	mdoc->lastsec = sec;
20021eccdf28Sschwarze 
20031eccdf28Sschwarze 	/*
20041eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
20051eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
20061eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
20071eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
20081eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
20091eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
20101eccdf28Sschwarze 	 */
20111eccdf28Sschwarze 
2012a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
2013a2cff342Sschwarze 	mdoc->last->sec = sec;
2014a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
2015a2cff342Sschwarze 		n->sec = sec;
201620fa2881Sschwarze 
201720fa2881Sschwarze 	/* We don't care about custom sections after this. */
2018fccfce9dSschwarze 
201946133849Sschwarze 	if (SEC_CUSTOM == sec) {
202046133849Sschwarze 		free(secname);
2021f73abda9Skristaps 		return(1);
202246133849Sschwarze 	}
2023fccfce9dSschwarze 
20246be99f77Sschwarze 	/*
202520fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
202620fa2881Sschwarze 	 * out of order.
20276be99f77Sschwarze 	 */
2028f73abda9Skristaps 
202920fa2881Sschwarze 	if (sec == mdoc->lastnamed)
203051fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse,
203151fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
203220fa2881Sschwarze 
203320fa2881Sschwarze 	if (sec < mdoc->lastnamed)
203451fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse,
203551fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
203620fa2881Sschwarze 
203720fa2881Sschwarze 	/* Mark the last named section. */
203820fa2881Sschwarze 
203920fa2881Sschwarze 	mdoc->lastnamed = sec;
204020fa2881Sschwarze 
204120fa2881Sschwarze 	/* Check particular section/manual conventions. */
204220fa2881Sschwarze 
204392c0ca7fSschwarze 	assert(mdoc->meta.msec);
204420fa2881Sschwarze 
204551fcab2fSschwarze 	goodsec = NULL;
204620fa2881Sschwarze 	switch (sec) {
204749aff9f8Sschwarze 	case SEC_ERRORS:
2048be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
2049be89e780Sschwarze 			break;
205051fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
2051be89e780Sschwarze 		/* FALLTHROUGH */
205249aff9f8Sschwarze 	case SEC_RETURN_VALUES:
205320fa2881Sschwarze 		/* FALLTHROUGH */
205449aff9f8Sschwarze 	case SEC_LIBRARY:
205592c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
2056f73abda9Skristaps 			break;
205792c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
205892c0ca7fSschwarze 			break;
205951fcab2fSschwarze 		if (NULL == goodsec)
206051fcab2fSschwarze 			goodsec = "2, 3, 9";
206103ab2f23Sdlg 		/* FALLTHROUGH */
206249aff9f8Sschwarze 	case SEC_CONTEXT:
206392c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
206492c0ca7fSschwarze 			break;
206551fcab2fSschwarze 		if (NULL == goodsec)
206651fcab2fSschwarze 			goodsec = "9";
206751fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
206851fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
206951fcab2fSschwarze 		    "%s for %s only", secname, goodsec);
207020fa2881Sschwarze 		break;
2071f73abda9Skristaps 	default:
2072f73abda9Skristaps 		break;
2073f73abda9Skristaps 	}
2074f73abda9Skristaps 
207546133849Sschwarze 	free(secname);
2076f73abda9Skristaps 	return(1);
2077f73abda9Skristaps }
2078d39b9a9cSschwarze 
207920fa2881Sschwarze static int
2080f6127a73Sschwarze post_ignpar(POST_ARGS)
2081f6127a73Sschwarze {
2082f6127a73Sschwarze 	struct mdoc_node *np;
2083f6127a73Sschwarze 
2084f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
2085f6127a73Sschwarze 		return(1);
2086f6127a73Sschwarze 
2087f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2088f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
208920369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
209020369664Sschwarze 			    mdoc->parse, np->line, np->pos,
209120369664Sschwarze 			    "%s after %s", mdoc_macronames[np->tok],
209220369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2093f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2094f6127a73Sschwarze 		}
2095f6127a73Sschwarze 
2096f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2097f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
209820369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
209920369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
210020369664Sschwarze 			    mdoc_macronames[np->tok],
210120369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2102f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2103f6127a73Sschwarze 		}
2104f6127a73Sschwarze 
2105f6127a73Sschwarze 	return(1);
2106f6127a73Sschwarze }
2107f6127a73Sschwarze 
2108f6127a73Sschwarze static int
210920fa2881Sschwarze pre_par(PRE_ARGS)
2110d39b9a9cSschwarze {
2111d39b9a9cSschwarze 
2112d39b9a9cSschwarze 	if (NULL == mdoc->last)
2113d39b9a9cSschwarze 		return(1);
2114f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2115f6127a73Sschwarze 		return(1);
2116d39b9a9cSschwarze 
211720fa2881Sschwarze 	/*
211820fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
211920fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
212020fa2881Sschwarze 	 */
2121d39b9a9cSschwarze 
2122e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2123e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2124e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2125d39b9a9cSschwarze 		return(1);
21268c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2127d39b9a9cSschwarze 		return(1);
21288c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2129d39b9a9cSschwarze 		return(1);
21308c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2131f6127a73Sschwarze 		return(1);
2132d39b9a9cSschwarze 
213320369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
213420369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
213520369664Sschwarze 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
213620369664Sschwarze 	    mdoc_macronames[n->tok]);
2137d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2138d39b9a9cSschwarze 	return(1);
2139d39b9a9cSschwarze }
214020fa2881Sschwarze 
214120fa2881Sschwarze static int
2142e0dd4c9cSschwarze post_par(POST_ARGS)
2143e0dd4c9cSschwarze {
214420369664Sschwarze 	struct mdoc_node *np;
2145e0dd4c9cSschwarze 
2146e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2147e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2148e0dd4c9cSschwarze 		return(1);
2149e0dd4c9cSschwarze 
215020369664Sschwarze 	if (NULL == (np = mdoc->last->prev)) {
215120369664Sschwarze 		np = mdoc->last->parent;
215220369664Sschwarze 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2153e0dd4c9cSschwarze 			return(1);
2154e0dd4c9cSschwarze 	} else {
215520369664Sschwarze 		if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2156e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
215720369664Sschwarze 		     (MDOC_sp != np->tok && MDOC_br != np->tok)))
2158e0dd4c9cSschwarze 			return(1);
2159e0dd4c9cSschwarze 	}
2160e0dd4c9cSschwarze 
216120369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
216220369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
216320369664Sschwarze 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
216420369664Sschwarze 	    mdoc_macronames[np->tok]);
2165e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2166e0dd4c9cSschwarze 	return(1);
2167e0dd4c9cSschwarze }
2168e0dd4c9cSschwarze 
2169e0dd4c9cSschwarze static int
217020fa2881Sschwarze pre_literal(PRE_ARGS)
217120fa2881Sschwarze {
217220fa2881Sschwarze 
217320fa2881Sschwarze 	if (MDOC_BODY != n->type)
217420fa2881Sschwarze 		return(1);
217520fa2881Sschwarze 
217620fa2881Sschwarze 	/*
217720fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
217820fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
217920fa2881Sschwarze 	 */
218020fa2881Sschwarze 
218120fa2881Sschwarze 	switch (n->tok) {
218249aff9f8Sschwarze 	case MDOC_Dl:
218320fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
218420fa2881Sschwarze 		break;
218549aff9f8Sschwarze 	case MDOC_Bd:
21868c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
218720fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
21888c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
218920fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
219020fa2881Sschwarze 		break;
219120fa2881Sschwarze 	default:
219220fa2881Sschwarze 		abort();
219320fa2881Sschwarze 		/* NOTREACHED */
219420fa2881Sschwarze 	}
219520fa2881Sschwarze 
219620fa2881Sschwarze 	return(1);
219720fa2881Sschwarze }
219820fa2881Sschwarze 
219920fa2881Sschwarze static int
220020fa2881Sschwarze post_dd(POST_ARGS)
220120fa2881Sschwarze {
220220fa2881Sschwarze 	struct mdoc_node *n;
220383af2bccSschwarze 	char		 *datestr;
220420fa2881Sschwarze 
2205b058e777Sschwarze 	if (mdoc->meta.date)
2206b058e777Sschwarze 		free(mdoc->meta.date);
220720fa2881Sschwarze 
2208b058e777Sschwarze 	n = mdoc->last;
2209b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2210231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2211231c7061Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
221220fa2881Sschwarze 		return(1);
221320fa2881Sschwarze 	}
221420fa2881Sschwarze 
221583af2bccSschwarze 	datestr = NULL;
221683af2bccSschwarze 	mdoc_deroff(&datestr, n);
221783af2bccSschwarze 	if (mdoc->quick)
221883af2bccSschwarze 		mdoc->meta.date = datestr;
221983af2bccSschwarze 	else {
222083af2bccSschwarze 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
222183af2bccSschwarze 		    datestr, n->line, n->pos);
222283af2bccSschwarze 		free(datestr);
222304e980cbSschwarze 	}
222420fa2881Sschwarze 	return(1);
222520fa2881Sschwarze }
222620fa2881Sschwarze 
222720fa2881Sschwarze static int
222820fa2881Sschwarze post_dt(POST_ARGS)
222920fa2881Sschwarze {
223020fa2881Sschwarze 	struct mdoc_node *nn, *n;
223120fa2881Sschwarze 	const char	 *cp;
223220fa2881Sschwarze 	char		 *p;
223320fa2881Sschwarze 
223420fa2881Sschwarze 	n = mdoc->last;
223520fa2881Sschwarze 
223620fa2881Sschwarze 	if (mdoc->meta.title)
223720fa2881Sschwarze 		free(mdoc->meta.title);
223820fa2881Sschwarze 	if (mdoc->meta.vol)
223920fa2881Sschwarze 		free(mdoc->meta.vol);
224020fa2881Sschwarze 	if (mdoc->meta.arch)
224120fa2881Sschwarze 		free(mdoc->meta.arch);
224220fa2881Sschwarze 
224320fa2881Sschwarze 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
224420fa2881Sschwarze 
224551fcab2fSschwarze 	/* First check that all characters are uppercase. */
224620fa2881Sschwarze 
224720fa2881Sschwarze 	if (NULL != (nn = n->child))
224820fa2881Sschwarze 		for (p = nn->string; *p; p++) {
224904e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
225020fa2881Sschwarze 				continue;
225151fcab2fSschwarze 			mandoc_msg(MANDOCERR_TITLE_CASE,
225251fcab2fSschwarze 			    mdoc->parse, nn->line,
225351fcab2fSschwarze 			    nn->pos + (p - nn->string),
225451fcab2fSschwarze 			    nn->string);
225520fa2881Sschwarze 			break;
225620fa2881Sschwarze 		}
225720fa2881Sschwarze 
225820fa2881Sschwarze 	/* Handles: `.Dt'
225949aff9f8Sschwarze 	 * title = unknown, volume = local, msec = 0, arch = NULL
226020fa2881Sschwarze 	 */
226120fa2881Sschwarze 
226220fa2881Sschwarze 	if (NULL == (nn = n->child)) {
226320fa2881Sschwarze 		/* XXX: make these macro values. */
226420fa2881Sschwarze 		/* FIXME: warn about missing values. */
226520fa2881Sschwarze 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
226620fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
226720fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
226820fa2881Sschwarze 		return(1);
226920fa2881Sschwarze 	}
227020fa2881Sschwarze 
227120fa2881Sschwarze 	/* Handles: `.Dt TITLE'
227249aff9f8Sschwarze 	 * title = TITLE, volume = local, msec = 0, arch = NULL
227320fa2881Sschwarze 	 */
227420fa2881Sschwarze 
227549aff9f8Sschwarze 	mdoc->meta.title = mandoc_strdup(
227649aff9f8Sschwarze 	    '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
227720fa2881Sschwarze 
227820fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
227920fa2881Sschwarze 		/* FIXME: warn about missing msec. */
228020fa2881Sschwarze 		/* XXX: make this a macro value. */
228120fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
228220fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
228320fa2881Sschwarze 		return(1);
228420fa2881Sschwarze 	}
228520fa2881Sschwarze 
228620fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
228749aff9f8Sschwarze 	 * title = TITLE,
228849aff9f8Sschwarze 	 * volume = SEC is msec ? format(msec) : SEC,
228920fa2881Sschwarze 	 * msec = SEC is msec ? atoi(msec) : 0,
229020fa2881Sschwarze 	 * arch = NULL
229120fa2881Sschwarze 	 */
229220fa2881Sschwarze 
229388ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
229420fa2881Sschwarze 	if (cp) {
229520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
229620fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
229720fa2881Sschwarze 	} else {
229851fcab2fSschwarze 		mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
229951fcab2fSschwarze 		    nn->line, nn->pos, nn->string);
230020fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
230120fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
230220fa2881Sschwarze 	}
230320fa2881Sschwarze 
230420fa2881Sschwarze 	if (NULL == (nn = nn->next))
230520fa2881Sschwarze 		return(1);
230620fa2881Sschwarze 
230720fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
230849aff9f8Sschwarze 	 * title = TITLE,
230949aff9f8Sschwarze 	 * volume = VOL is vol ? format(VOL) :
231020fa2881Sschwarze 	 *	    VOL is arch ? format(arch) :
231120fa2881Sschwarze 	 *	    VOL
231220fa2881Sschwarze 	 */
231320fa2881Sschwarze 
231420fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
231520fa2881Sschwarze 	if (cp) {
231620fa2881Sschwarze 		free(mdoc->meta.vol);
231720fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
231820fa2881Sschwarze 	} else {
231920fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
232020fa2881Sschwarze 		if (NULL == cp) {
232151fcab2fSschwarze 			mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
232251fcab2fSschwarze 			    nn->line, nn->pos, nn->string);
232320fa2881Sschwarze 			free(mdoc->meta.vol);
232420fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
232520fa2881Sschwarze 		} else
232620fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
232720fa2881Sschwarze 	}
232820fa2881Sschwarze 
232920fa2881Sschwarze 	/* Ignore any subsequent parameters... */
233020fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
233120fa2881Sschwarze 
233220fa2881Sschwarze 	return(1);
233320fa2881Sschwarze }
233420fa2881Sschwarze 
233520fa2881Sschwarze static int
233620fa2881Sschwarze post_prol(POST_ARGS)
233720fa2881Sschwarze {
233820fa2881Sschwarze 	/*
233920fa2881Sschwarze 	 * Remove prologue macros from the document after they're
234020fa2881Sschwarze 	 * processed.  The final document uses mdoc_meta for these
234120fa2881Sschwarze 	 * values and discards the originals.
234220fa2881Sschwarze 	 */
234320fa2881Sschwarze 
234420fa2881Sschwarze 	mdoc_node_delete(mdoc, mdoc->last);
234520fa2881Sschwarze 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
234620fa2881Sschwarze 		mdoc->flags |= MDOC_PBODY;
234720fa2881Sschwarze 
234820fa2881Sschwarze 	return(1);
234920fa2881Sschwarze }
235020fa2881Sschwarze 
235120fa2881Sschwarze static int
2352992063deSschwarze post_bx(POST_ARGS)
2353992063deSschwarze {
2354992063deSschwarze 	struct mdoc_node	*n;
2355992063deSschwarze 
2356992063deSschwarze 	/*
2357992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2358992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2359992063deSschwarze 	 * uppercase blindly.
2360992063deSschwarze 	 */
2361992063deSschwarze 
2362992063deSschwarze 	n = mdoc->last->child;
2363992063deSschwarze 	if (n && NULL != (n = n->next))
236449aff9f8Sschwarze 		*n->string = (char)toupper((unsigned char)*n->string);
2365992063deSschwarze 
2366992063deSschwarze 	return(1);
2367992063deSschwarze }
2368992063deSschwarze 
2369992063deSschwarze static int
237020fa2881Sschwarze post_os(POST_ARGS)
237120fa2881Sschwarze {
237220fa2881Sschwarze #ifndef OSNAME
237320fa2881Sschwarze 	struct utsname	  utsname;
23744c468128Sschwarze 	static char	 *defbuf;
237520fa2881Sschwarze #endif
23764c468128Sschwarze 	struct mdoc_node *n;
237720fa2881Sschwarze 
237820fa2881Sschwarze 	n = mdoc->last;
237920fa2881Sschwarze 
238020fa2881Sschwarze 	/*
2381353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2382353fa9ecSschwarze 	 * The order of precedence is:
2383353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2384353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2385353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2386353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
238720fa2881Sschwarze 	 */
238820fa2881Sschwarze 
238920fa2881Sschwarze 	free(mdoc->meta.os);
239083af2bccSschwarze 	mdoc->meta.os = NULL;
239183af2bccSschwarze 	mdoc_deroff(&mdoc->meta.os, n);
239283af2bccSschwarze 	if (mdoc->meta.os)
23934c468128Sschwarze 		return(1);
23944c468128Sschwarze 
2395353fa9ecSschwarze 	if (mdoc->defos) {
2396353fa9ecSschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2397353fa9ecSschwarze 		return(1);
2398353fa9ecSschwarze 	}
23994c468128Sschwarze 
240020fa2881Sschwarze #ifdef OSNAME
24014c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
240220fa2881Sschwarze #else /*!OSNAME */
24034c468128Sschwarze 	if (NULL == defbuf) {
2404a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
240520fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
24064c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2407a450f7c4Sschwarze 		} else
2408a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2409a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
241020fa2881Sschwarze 	}
24114c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
241220fa2881Sschwarze #endif /*!OSNAME*/
241320fa2881Sschwarze 	return(1);
241420fa2881Sschwarze }
241520fa2881Sschwarze 
241620fa2881Sschwarze static int
241720fa2881Sschwarze post_std(POST_ARGS)
241820fa2881Sschwarze {
241920fa2881Sschwarze 	struct mdoc_node *nn, *n;
242020fa2881Sschwarze 
242120fa2881Sschwarze 	n = mdoc->last;
242220fa2881Sschwarze 
242320fa2881Sschwarze 	/*
242420fa2881Sschwarze 	 * Macros accepting `-std' as an argument have the name of the
242520fa2881Sschwarze 	 * current document (`Nm') filled in as the argument if it's not
242620fa2881Sschwarze 	 * provided.
242720fa2881Sschwarze 	 */
242820fa2881Sschwarze 
242920fa2881Sschwarze 	if (n->child)
243020fa2881Sschwarze 		return(1);
243120fa2881Sschwarze 
243220fa2881Sschwarze 	if (NULL == mdoc->meta.name)
243320fa2881Sschwarze 		return(1);
243420fa2881Sschwarze 
243520fa2881Sschwarze 	nn = n;
243620fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
243720fa2881Sschwarze 
243820fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
243920fa2881Sschwarze 		return(0);
244020fa2881Sschwarze 
244120fa2881Sschwarze 	mdoc->last = nn;
244220fa2881Sschwarze 	return(1);
244320fa2881Sschwarze }
244420fa2881Sschwarze 
244519a69263Sschwarze static enum mdoc_sec
244619a69263Sschwarze a2sec(const char *p)
244719a69263Sschwarze {
244819a69263Sschwarze 	int		 i;
244919a69263Sschwarze 
245019a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
245119a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
245219a69263Sschwarze 			return((enum mdoc_sec)i);
245319a69263Sschwarze 
245419a69263Sschwarze 	return(SEC_CUSTOM);
245519a69263Sschwarze }
245619a69263Sschwarze 
245719a69263Sschwarze static size_t
245819a69263Sschwarze macro2len(enum mdoct macro)
245919a69263Sschwarze {
246019a69263Sschwarze 
246119a69263Sschwarze 	switch (macro) {
246249aff9f8Sschwarze 	case MDOC_Ad:
246319a69263Sschwarze 		return(12);
246449aff9f8Sschwarze 	case MDOC_Ao:
246519a69263Sschwarze 		return(12);
246649aff9f8Sschwarze 	case MDOC_An:
246719a69263Sschwarze 		return(12);
246849aff9f8Sschwarze 	case MDOC_Aq:
246919a69263Sschwarze 		return(12);
247049aff9f8Sschwarze 	case MDOC_Ar:
247119a69263Sschwarze 		return(12);
247249aff9f8Sschwarze 	case MDOC_Bo:
247319a69263Sschwarze 		return(12);
247449aff9f8Sschwarze 	case MDOC_Bq:
247519a69263Sschwarze 		return(12);
247649aff9f8Sschwarze 	case MDOC_Cd:
247719a69263Sschwarze 		return(12);
247849aff9f8Sschwarze 	case MDOC_Cm:
247919a69263Sschwarze 		return(10);
248049aff9f8Sschwarze 	case MDOC_Do:
248119a69263Sschwarze 		return(10);
248249aff9f8Sschwarze 	case MDOC_Dq:
248319a69263Sschwarze 		return(12);
248449aff9f8Sschwarze 	case MDOC_Dv:
248519a69263Sschwarze 		return(12);
248649aff9f8Sschwarze 	case MDOC_Eo:
248719a69263Sschwarze 		return(12);
248849aff9f8Sschwarze 	case MDOC_Em:
248919a69263Sschwarze 		return(10);
249049aff9f8Sschwarze 	case MDOC_Er:
249119a69263Sschwarze 		return(17);
249249aff9f8Sschwarze 	case MDOC_Ev:
249319a69263Sschwarze 		return(15);
249449aff9f8Sschwarze 	case MDOC_Fa:
249519a69263Sschwarze 		return(12);
249649aff9f8Sschwarze 	case MDOC_Fl:
249719a69263Sschwarze 		return(10);
249849aff9f8Sschwarze 	case MDOC_Fo:
249919a69263Sschwarze 		return(16);
250049aff9f8Sschwarze 	case MDOC_Fn:
250119a69263Sschwarze 		return(16);
250249aff9f8Sschwarze 	case MDOC_Ic:
250319a69263Sschwarze 		return(10);
250449aff9f8Sschwarze 	case MDOC_Li:
250519a69263Sschwarze 		return(16);
250649aff9f8Sschwarze 	case MDOC_Ms:
250719a69263Sschwarze 		return(6);
250849aff9f8Sschwarze 	case MDOC_Nm:
250919a69263Sschwarze 		return(10);
251049aff9f8Sschwarze 	case MDOC_No:
251119a69263Sschwarze 		return(12);
251249aff9f8Sschwarze 	case MDOC_Oo:
251319a69263Sschwarze 		return(10);
251449aff9f8Sschwarze 	case MDOC_Op:
251519a69263Sschwarze 		return(14);
251649aff9f8Sschwarze 	case MDOC_Pa:
251719a69263Sschwarze 		return(32);
251849aff9f8Sschwarze 	case MDOC_Pf:
251919a69263Sschwarze 		return(12);
252049aff9f8Sschwarze 	case MDOC_Po:
252119a69263Sschwarze 		return(12);
252249aff9f8Sschwarze 	case MDOC_Pq:
252319a69263Sschwarze 		return(12);
252449aff9f8Sschwarze 	case MDOC_Ql:
252519a69263Sschwarze 		return(16);
252649aff9f8Sschwarze 	case MDOC_Qo:
252719a69263Sschwarze 		return(12);
252849aff9f8Sschwarze 	case MDOC_So:
252919a69263Sschwarze 		return(12);
253049aff9f8Sschwarze 	case MDOC_Sq:
253119a69263Sschwarze 		return(12);
253249aff9f8Sschwarze 	case MDOC_Sy:
253319a69263Sschwarze 		return(6);
253449aff9f8Sschwarze 	case MDOC_Sx:
253519a69263Sschwarze 		return(16);
253649aff9f8Sschwarze 	case MDOC_Tn:
253719a69263Sschwarze 		return(10);
253849aff9f8Sschwarze 	case MDOC_Va:
253919a69263Sschwarze 		return(12);
254049aff9f8Sschwarze 	case MDOC_Vt:
254119a69263Sschwarze 		return(12);
254249aff9f8Sschwarze 	case MDOC_Xr:
254319a69263Sschwarze 		return(10);
254419a69263Sschwarze 	default:
254519a69263Sschwarze 		break;
254619a69263Sschwarze 	};
254719a69263Sschwarze 	return(0);
254819a69263Sschwarze }
2549