xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 4a9f685f)
1*4a9f685fSschwarze /*	$Id: mdoc_validate.c,v 1.145 2014/07/05 01:11:33 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 
8467c719adSschwarze static	int	 post_an(POST_ARGS);
8567c719adSschwarze static	int	 post_at(POST_ARGS);
8667c719adSschwarze static	int	 post_bf(POST_ARGS);
8767c719adSschwarze static	int	 post_bl(POST_ARGS);
8820fa2881Sschwarze static	int	 post_bl_block(POST_ARGS);
8920fa2881Sschwarze static	int	 post_bl_block_width(POST_ARGS);
9020fa2881Sschwarze static	int	 post_bl_block_tag(POST_ARGS);
9167c719adSschwarze static	int	 post_bl_head(POST_ARGS);
92992063deSschwarze static	int	 post_bx(POST_ARGS);
934039b21cSschwarze static	int	 post_defaults(POST_ARGS);
9420fa2881Sschwarze static	int	 post_dd(POST_ARGS);
956093755cSschwarze static	int	 post_dt(POST_ARGS);
96551cd4a8Sschwarze static	int	 post_en(POST_ARGS);
97551cd4a8Sschwarze static	int	 post_es(POST_ARGS);
9820fa2881Sschwarze static	int	 post_eoln(POST_ARGS);
994039b21cSschwarze static	int	 post_hyph(POST_ARGS);
1004039b21cSschwarze static	int	 post_ignpar(POST_ARGS);
10167c719adSschwarze static	int	 post_it(POST_ARGS);
10267c719adSschwarze static	int	 post_lb(POST_ARGS);
1034039b21cSschwarze static	int	 post_literal(POST_ARGS);
10467c719adSschwarze static	int	 post_nm(POST_ARGS);
105af216717Sschwarze static	int	 post_ns(POST_ARGS);
10620fa2881Sschwarze static	int	 post_os(POST_ARGS);
107e0dd4c9cSschwarze static	int	 post_par(POST_ARGS);
10820fa2881Sschwarze static	int	 post_prol(POST_ARGS);
10967c719adSschwarze static	int	 post_root(POST_ARGS);
110011fe33bSschwarze static	int	 post_rs(POST_ARGS);
11167c719adSschwarze static	int	 post_sh(POST_ARGS);
11267c719adSschwarze static	int	 post_sh_body(POST_ARGS);
11367c719adSschwarze static	int	 post_sh_head(POST_ARGS);
11467c719adSschwarze static	int	 post_st(POST_ARGS);
11520fa2881Sschwarze static	int	 post_std(POST_ARGS);
1168521b0bcSschwarze static	int	 post_vt(POST_ARGS);
117f73abda9Skristaps static	int	 pre_an(PRE_ARGS);
118f73abda9Skristaps static	int	 pre_bd(PRE_ARGS);
119f73abda9Skristaps static	int	 pre_bl(PRE_ARGS);
120f73abda9Skristaps static	int	 pre_dd(PRE_ARGS);
121f73abda9Skristaps static	int	 pre_display(PRE_ARGS);
122f73abda9Skristaps static	int	 pre_dt(PRE_ARGS);
123f73abda9Skristaps static	int	 pre_it(PRE_ARGS);
12420fa2881Sschwarze static	int	 pre_literal(PRE_ARGS);
125551cd4a8Sschwarze static	int	 pre_obsolete(PRE_ARGS);
126f73abda9Skristaps static	int	 pre_os(PRE_ARGS);
12720fa2881Sschwarze static	int	 pre_par(PRE_ARGS);
128f73abda9Skristaps static	int	 pre_sh(PRE_ARGS);
129f73abda9Skristaps static	int	 pre_ss(PRE_ARGS);
13020fa2881Sschwarze static	int	 pre_std(PRE_ARGS);
131f73abda9Skristaps 
13267c719adSschwarze static	v_post	 posts_an[] = { post_an, NULL };
13320fa2881Sschwarze static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
13420fa2881Sschwarze static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
135ecb10c32Sschwarze static	v_post	 posts_bf[] = { post_bf, NULL };
13620fa2881Sschwarze static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
13767c719adSschwarze static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
138992063deSschwarze static	v_post	 posts_bx[] = { post_bx, NULL };
139bb648afaSschwarze static	v_post	 posts_bool[] = { ebool, NULL };
140b31af00dSschwarze static	v_post	 posts_eoln[] = { post_eoln, NULL };
14120fa2881Sschwarze static	v_post	 posts_defaults[] = { post_defaults, NULL };
1424039b21cSschwarze static	v_post	 posts_d1[] = { bwarn_ge1, post_hyph, NULL };
143b058e777Sschwarze static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
144bb648afaSschwarze static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
14520fa2881Sschwarze static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
146551cd4a8Sschwarze static	v_post	 posts_en[] = { post_en, NULL };
147551cd4a8Sschwarze static	v_post	 posts_es[] = { post_es, NULL };
14867c719adSschwarze static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
1494039b21cSschwarze static	v_post	 posts_hyph[] = { post_hyph, NULL };
1504039b21cSschwarze static	v_post	 posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
15167c719adSschwarze static	v_post	 posts_it[] = { post_it, NULL };
152bb648afaSschwarze static	v_post	 posts_lb[] = { post_lb, NULL };
1534039b21cSschwarze static	v_post	 posts_nd[] = { berr_ge1, post_hyph, NULL };
15467c719adSschwarze static	v_post	 posts_nm[] = { post_nm, NULL };
155da272f5eSschwarze static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
156af216717Sschwarze static	v_post	 posts_ns[] = { post_ns, NULL };
15720fa2881Sschwarze static	v_post	 posts_os[] = { post_os, post_prol, NULL };
158e0dd4c9cSschwarze static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
159bb648afaSschwarze static	v_post	 posts_rs[] = { post_rs, NULL };
1604039b21cSschwarze static	v_post	 posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
161e0dd4c9cSschwarze static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
1624039b21cSschwarze static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
163bb648afaSschwarze static	v_post	 posts_st[] = { post_st, NULL };
16420fa2881Sschwarze static	v_post	 posts_std[] = { post_std, NULL };
165e7a93ef3Sschwarze static	v_post	 posts_text[] = { ewarn_ge1, NULL };
166bb648afaSschwarze static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
1678521b0bcSschwarze static	v_post	 posts_vt[] = { post_vt, NULL };
168f73abda9Skristaps static	v_pre	 pres_an[] = { pre_an, NULL };
16920fa2881Sschwarze static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
17020fa2881Sschwarze static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
171f73abda9Skristaps static	v_pre	 pres_d1[] = { pre_display, NULL };
17220fa2881Sschwarze static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
17367c719adSschwarze static	v_pre	 pres_dd[] = { pre_dd, NULL };
1746be99f77Sschwarze static	v_pre	 pres_dt[] = { pre_dt, NULL };
175f6127a73Sschwarze static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
176551cd4a8Sschwarze static	v_pre	 pres_obsolete[] = { pre_obsolete, NULL };
1776be99f77Sschwarze static	v_pre	 pres_os[] = { pre_os, NULL };
17820fa2881Sschwarze static	v_pre	 pres_pp[] = { pre_par, NULL };
179f73abda9Skristaps static	v_pre	 pres_sh[] = { pre_sh, NULL };
180f73abda9Skristaps static	v_pre	 pres_ss[] = { pre_ss, NULL };
18120fa2881Sschwarze static	v_pre	 pres_std[] = { pre_std, NULL };
182f73abda9Skristaps 
18319a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
184099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
18520fa2881Sschwarze 	{ pres_dd, posts_dd },			/* Dd */
1866093755cSschwarze 	{ pres_dt, posts_dt },			/* Dt */
18720fa2881Sschwarze 	{ pres_os, posts_os },			/* Os */
188f73abda9Skristaps 	{ pres_sh, posts_sh },			/* Sh */
189f73abda9Skristaps 	{ pres_ss, posts_ss },			/* Ss */
190e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Pp */
1914039b21cSschwarze 	{ pres_d1, posts_d1 },			/* D1 */
19220fa2881Sschwarze 	{ pres_dl, posts_dl },			/* Dl */
19320fa2881Sschwarze 	{ pres_bd, posts_bd },			/* Bd */
194f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
195f73abda9Skristaps 	{ pres_bl, posts_bl },			/* Bl */
196f73abda9Skristaps 	{ NULL, NULL },				/* El */
197f73abda9Skristaps 	{ pres_it, posts_it },			/* It */
198e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
199f73abda9Skristaps 	{ pres_an, posts_an },			/* An */
20020fa2881Sschwarze 	{ NULL, posts_defaults },		/* Ar */
201e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
202f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
203f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
2044039b21cSschwarze 	{ NULL, NULL },				/* Er */
205f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
20620fa2881Sschwarze 	{ pres_std, posts_std },		/* Ex */
207f73abda9Skristaps 	{ NULL, NULL },				/* Fa */
2084039b21cSschwarze 	{ NULL, posts_text },			/* Fd */
209f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
210e7a93ef3Sschwarze 	{ NULL, NULL },				/* Fn */
211e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
212e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
213b822ca0dSschwarze 	{ NULL, posts_text1 },			/* In */
21420fa2881Sschwarze 	{ NULL, posts_defaults },		/* Li */
2154602e85cSschwarze 	{ NULL, posts_nd },			/* Nd */
216f73abda9Skristaps 	{ NULL, posts_nm },			/* Nm */
217bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
218551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Ot */
21920fa2881Sschwarze 	{ NULL, posts_defaults },		/* Pa */
22020fa2881Sschwarze 	{ pres_std, posts_std },		/* Rv */
221f73abda9Skristaps 	{ NULL, posts_st },			/* St */
222f73abda9Skristaps 	{ NULL, NULL },				/* Va */
2238521b0bcSschwarze 	{ NULL, posts_vt },			/* Vt */
224e7a93ef3Sschwarze 	{ NULL, posts_text },			/* Xr */
225f73abda9Skristaps 	{ NULL, posts_text },			/* %A */
2264039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
227b058e777Sschwarze 	{ NULL, posts_text },			/* %D */
228f73abda9Skristaps 	{ NULL, posts_text },			/* %I */
229f73abda9Skristaps 	{ NULL, posts_text },			/* %J */
2304039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %N */
2314039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %O */
232f73abda9Skristaps 	{ NULL, posts_text },			/* %P */
2334039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %R */
2344039b21cSschwarze 	{ NULL, posts_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
235f73abda9Skristaps 	{ NULL, posts_text },			/* %V */
236f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
237f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
238bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
239f73abda9Skristaps 	{ NULL, posts_at },			/* At */
240f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
241f73abda9Skristaps 	{ NULL, posts_bf },			/* Bf */
242f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
243bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
244f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
245992063deSschwarze 	{ NULL, posts_bx },			/* Bx */
246f73abda9Skristaps 	{ NULL, posts_bool },			/* Db */
247f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
248f73abda9Skristaps 	{ NULL, NULL },				/* Do */
249bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
250f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
251f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
252f73abda9Skristaps 	{ NULL, NULL },				/* Em */
253f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
254f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
255e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
256f73abda9Skristaps 	{ NULL, posts_notext },			/* No */
257af216717Sschwarze 	{ NULL, posts_ns },			/* Ns */
258f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
259f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
260f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
261b822ca0dSschwarze 	{ NULL, posts_text1 },			/* Pf */
262f73abda9Skristaps 	{ NULL, NULL },				/* Po */
263bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
264f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
265bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
266f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
267bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
268f73abda9Skristaps 	{ NULL, NULL },				/* Re */
2698c62fbf5Sschwarze 	{ NULL, posts_rs },			/* Rs */
270f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
271f73abda9Skristaps 	{ NULL, NULL },				/* So */
272bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
273f73abda9Skristaps 	{ NULL, posts_bool },			/* Sm */
2744039b21cSschwarze 	{ NULL, posts_hyph },			/* Sx */
275e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
276e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
277f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
278f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
279f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
280f73abda9Skristaps 	{ NULL, posts_fo },			/* Fo */
281f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
282f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
283f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
28420fa2881Sschwarze 	{ NULL, posts_bk },			/* Bk */
285f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
286b31af00dSschwarze 	{ NULL, posts_eoln },			/* Bt */
287f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
288551cd4a8Sschwarze 	{ pres_obsolete, NULL },		/* Fr */
289b31af00dSschwarze 	{ NULL, posts_eoln },			/* Ud */
290b31af00dSschwarze 	{ NULL, posts_lb },			/* Lb */
291e0dd4c9cSschwarze 	{ pres_pp, posts_pp },			/* Lp */
292e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
29320fa2881Sschwarze 	{ NULL, posts_defaults },		/* Mt */
294bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
295f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
296f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
297f73abda9Skristaps 	{ NULL, posts_text },			/* %C */
298551cd4a8Sschwarze 	{ pres_obsolete, posts_es },		/* Es */
299551cd4a8Sschwarze 	{ pres_obsolete, posts_en },		/* En */
300f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
301f73abda9Skristaps 	{ NULL, posts_text },			/* %Q */
302e0dd4c9cSschwarze 	{ NULL, posts_pp },			/* br */
303e0dd4c9cSschwarze 	{ NULL, posts_sp },			/* sp */
304b822ca0dSschwarze 	{ NULL, posts_text1 },			/* %U */
3056093755cSschwarze 	{ NULL, NULL },				/* Ta */
3065281506aSschwarze 	{ NULL, NULL },				/* ll */
307f73abda9Skristaps };
308f73abda9Skristaps 
30920fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
31020fa2881Sschwarze 
31120fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
31220fa2881Sschwarze 	MDOC__A,
31320fa2881Sschwarze 	MDOC__T,
31420fa2881Sschwarze 	MDOC__B,
31520fa2881Sschwarze 	MDOC__I,
31620fa2881Sschwarze 	MDOC__J,
31720fa2881Sschwarze 	MDOC__R,
31820fa2881Sschwarze 	MDOC__N,
31920fa2881Sschwarze 	MDOC__V,
3200397c682Sschwarze 	MDOC__U,
32120fa2881Sschwarze 	MDOC__P,
32220fa2881Sschwarze 	MDOC__Q,
3234e32ec8fSschwarze 	MDOC__C,
32420fa2881Sschwarze 	MDOC__D,
3254e32ec8fSschwarze 	MDOC__O
32620fa2881Sschwarze };
32720fa2881Sschwarze 
32819a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
32919a69263Sschwarze 	NULL,
33019a69263Sschwarze 	"NAME",
33119a69263Sschwarze 	"LIBRARY",
33219a69263Sschwarze 	"SYNOPSIS",
33319a69263Sschwarze 	"DESCRIPTION",
33403ab2f23Sdlg 	"CONTEXT",
33519a69263Sschwarze 	"IMPLEMENTATION NOTES",
33619a69263Sschwarze 	"RETURN VALUES",
33719a69263Sschwarze 	"ENVIRONMENT",
33819a69263Sschwarze 	"FILES",
33919a69263Sschwarze 	"EXIT STATUS",
34019a69263Sschwarze 	"EXAMPLES",
34119a69263Sschwarze 	"DIAGNOSTICS",
34219a69263Sschwarze 	"COMPATIBILITY",
34319a69263Sschwarze 	"ERRORS",
34419a69263Sschwarze 	"SEE ALSO",
34519a69263Sschwarze 	"STANDARDS",
34619a69263Sschwarze 	"HISTORY",
34719a69263Sschwarze 	"AUTHORS",
34819a69263Sschwarze 	"CAVEATS",
34919a69263Sschwarze 	"BUGS",
35019a69263Sschwarze 	"SECURITY CONSIDERATIONS",
35119a69263Sschwarze 	NULL
35219a69263Sschwarze };
353f73abda9Skristaps 
35449aff9f8Sschwarze 
355f73abda9Skristaps int
3566093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
357f73abda9Skristaps {
358f73abda9Skristaps 	v_pre		*p;
359f73abda9Skristaps 	int		 line, pos;
36031e23753Sschwarze 	char		*tp;
361f73abda9Skristaps 
3622791bd1cSschwarze 	switch (n->type) {
36349aff9f8Sschwarze 	case MDOC_TEXT:
364f73abda9Skristaps 		tp = n->string;
365f73abda9Skristaps 		line = n->line;
366f73abda9Skristaps 		pos = n->pos;
36720fa2881Sschwarze 		check_text(mdoc, line, pos, tp);
3682791bd1cSschwarze 		/* FALLTHROUGH */
36949aff9f8Sschwarze 	case MDOC_TBL:
3702791bd1cSschwarze 		/* FALLTHROUGH */
37149aff9f8Sschwarze 	case MDOC_EQN:
3728d973ab1Sschwarze 		/* FALLTHROUGH */
37349aff9f8Sschwarze 	case MDOC_ROOT:
37420fa2881Sschwarze 		return(1);
3752791bd1cSschwarze 	default:
3762791bd1cSschwarze 		break;
377f73abda9Skristaps 	}
378f73abda9Skristaps 
37920fa2881Sschwarze 	check_args(mdoc, n);
38020fa2881Sschwarze 
381f73abda9Skristaps 	if (NULL == mdoc_valids[n->tok].pre)
382f73abda9Skristaps 		return(1);
383f73abda9Skristaps 	for (p = mdoc_valids[n->tok].pre; *p; p++)
384f73abda9Skristaps 		if ( ! (*p)(mdoc, n))
385f73abda9Skristaps 			return(0);
386f73abda9Skristaps 	return(1);
387f73abda9Skristaps }
388f73abda9Skristaps 
389f73abda9Skristaps int
390f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc)
391f73abda9Skristaps {
392f73abda9Skristaps 	v_post		*p;
393f73abda9Skristaps 
394f73abda9Skristaps 	if (MDOC_VALID & mdoc->last->flags)
395f73abda9Skristaps 		return(1);
396f73abda9Skristaps 	mdoc->last->flags |= MDOC_VALID;
397f73abda9Skristaps 
3982791bd1cSschwarze 	switch (mdoc->last->type) {
39949aff9f8Sschwarze 	case MDOC_TEXT:
4002791bd1cSschwarze 		/* FALLTHROUGH */
40149aff9f8Sschwarze 	case MDOC_EQN:
4028d973ab1Sschwarze 		/* FALLTHROUGH */
40349aff9f8Sschwarze 	case MDOC_TBL:
404f73abda9Skristaps 		return(1);
40549aff9f8Sschwarze 	case MDOC_ROOT:
406f73abda9Skristaps 		return(post_root(mdoc));
4072791bd1cSschwarze 	default:
4082791bd1cSschwarze 		break;
4092791bd1cSschwarze 	}
410f73abda9Skristaps 
411f73abda9Skristaps 	if (NULL == mdoc_valids[mdoc->last->tok].post)
412f73abda9Skristaps 		return(1);
413f73abda9Skristaps 	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
414f73abda9Skristaps 		if ( ! (*p)(mdoc))
415f73abda9Skristaps 			return(0);
416f73abda9Skristaps 
417f73abda9Skristaps 	return(1);
418f73abda9Skristaps }
419f73abda9Skristaps 
4207c2be9f8Sschwarze static int
4217ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type,
4227c2be9f8Sschwarze 		enum check_lvl lvl, enum check_ineq ineq, int val)
423f73abda9Skristaps {
4247c2be9f8Sschwarze 	const char	*p;
425bb648afaSschwarze 	enum mandocerr	 t;
4267c2be9f8Sschwarze 
4277ead8a4eSschwarze 	if (mdoc->last->type != type)
4287c2be9f8Sschwarze 		return(1);
4297c2be9f8Sschwarze 
4307c2be9f8Sschwarze 	switch (ineq) {
43149aff9f8Sschwarze 	case CHECK_LT:
4327c2be9f8Sschwarze 		p = "less than ";
4337ead8a4eSschwarze 		if (mdoc->last->nchild < val)
4347c2be9f8Sschwarze 			return(1);
4357c2be9f8Sschwarze 		break;
43649aff9f8Sschwarze 	case CHECK_GT:
437bb648afaSschwarze 		p = "more than ";
4387ead8a4eSschwarze 		if (mdoc->last->nchild > val)
4397c2be9f8Sschwarze 			return(1);
4407c2be9f8Sschwarze 		break;
44149aff9f8Sschwarze 	case CHECK_EQ:
4427c2be9f8Sschwarze 		p = "";
4437ead8a4eSschwarze 		if (val == mdoc->last->nchild)
4447c2be9f8Sschwarze 			return(1);
4457c2be9f8Sschwarze 		break;
44620fa2881Sschwarze 	default:
44720fa2881Sschwarze 		abort();
44820fa2881Sschwarze 		/* NOTREACHED */
4497c2be9f8Sschwarze 	}
4507c2be9f8Sschwarze 
451bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
45249aff9f8Sschwarze 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
45349aff9f8Sschwarze 	    mdoc->last->pos, "want %s%d children (have %d)",
4547ead8a4eSschwarze 	    p, val, mdoc->last->nchild);
45519a69263Sschwarze 	return(1);
4567c2be9f8Sschwarze }
457f73abda9Skristaps 
4587c2be9f8Sschwarze static int
4597c2be9f8Sschwarze berr_ge1(POST_ARGS)
460f73abda9Skristaps {
461f73abda9Skristaps 
462bb648afaSschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
463f73abda9Skristaps }
464f73abda9Skristaps 
4657c2be9f8Sschwarze static int
4667c2be9f8Sschwarze bwarn_ge1(POST_ARGS)
4677c2be9f8Sschwarze {
4687c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
469f73abda9Skristaps }
470f73abda9Skristaps 
4717c2be9f8Sschwarze static int
4727c2be9f8Sschwarze ewarn_eq0(POST_ARGS)
4737c2be9f8Sschwarze {
4747c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
4757c2be9f8Sschwarze }
4767c2be9f8Sschwarze 
4777c2be9f8Sschwarze static int
478bb648afaSschwarze ewarn_eq1(POST_ARGS)
479bb648afaSschwarze {
480bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
481bb648afaSschwarze }
482bb648afaSschwarze 
483bb648afaSschwarze static int
4847c2be9f8Sschwarze ewarn_ge1(POST_ARGS)
4857c2be9f8Sschwarze {
4867c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
4877c2be9f8Sschwarze }
4887c2be9f8Sschwarze 
4897c2be9f8Sschwarze static int
490bb648afaSschwarze ewarn_le1(POST_ARGS)
4917c2be9f8Sschwarze {
492bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
4937c2be9f8Sschwarze }
4947c2be9f8Sschwarze 
4957c2be9f8Sschwarze static int
4967c2be9f8Sschwarze hwarn_eq0(POST_ARGS)
4977c2be9f8Sschwarze {
4987c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
4997c2be9f8Sschwarze }
5007c2be9f8Sschwarze 
5017c2be9f8Sschwarze static int
5027c2be9f8Sschwarze hwarn_eq1(POST_ARGS)
5037c2be9f8Sschwarze {
5047c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
5057c2be9f8Sschwarze }
5067c2be9f8Sschwarze 
5077c2be9f8Sschwarze static int
508bb648afaSschwarze hwarn_ge1(POST_ARGS)
509bb648afaSschwarze {
510bb648afaSschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
511bb648afaSschwarze }
512bb648afaSschwarze 
51320fa2881Sschwarze static void
5147ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n)
515f73abda9Skristaps {
516f73abda9Skristaps 	int		 i;
517f73abda9Skristaps 
518f73abda9Skristaps 	if (NULL == n->args)
51920fa2881Sschwarze 		return;
520f73abda9Skristaps 
521f73abda9Skristaps 	assert(n->args->argc);
522f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
5237ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
524f73abda9Skristaps }
525f73abda9Skristaps 
52620fa2881Sschwarze static void
5277ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
528f73abda9Skristaps {
529f73abda9Skristaps 	int		 i;
530f73abda9Skristaps 
531f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
5327ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
533f73abda9Skristaps 
53420fa2881Sschwarze 	/* FIXME: move to post_std(). */
53520fa2881Sschwarze 
53620fa2881Sschwarze 	if (MDOC_Std == v->arg)
5377ead8a4eSschwarze 		if ( ! (v->sz || mdoc->meta.name))
5387ead8a4eSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
539f73abda9Skristaps }
540f73abda9Skristaps 
54120fa2881Sschwarze static void
5427ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
543f73abda9Skristaps {
54404e980cbSschwarze 	char		*cp;
545769ee804Sschwarze 
5467ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
5471cdbf331Sschwarze 		return;
5481cdbf331Sschwarze 
5491cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
5507ead8a4eSschwarze 		mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
551f73abda9Skristaps }
552f73abda9Skristaps 
553f73abda9Skristaps static int
554dd94fa3aSschwarze check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
555f73abda9Skristaps {
556f73abda9Skristaps 
557f73abda9Skristaps 	assert(n->parent);
558f73abda9Skristaps 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
559f73abda9Skristaps 			(t == n->parent->type))
560f73abda9Skristaps 		return(1);
561f73abda9Skristaps 
56249aff9f8Sschwarze 	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse,
56349aff9f8Sschwarze 	    n->line, n->pos, "want parent %s",
56449aff9f8Sschwarze 	    MDOC_ROOT == t ? "<root>" : mdoc_macronames[tok]);
5656e03d529Sschwarze 	return(0);
566f73abda9Skristaps }
567f73abda9Skristaps 
568f73abda9Skristaps 
569f73abda9Skristaps static int
570f73abda9Skristaps pre_display(PRE_ARGS)
571f73abda9Skristaps {
572f73abda9Skristaps 	struct mdoc_node *node;
573f73abda9Skristaps 
574f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
575f73abda9Skristaps 		return(1);
576f73abda9Skristaps 
577f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
578f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
579f73abda9Skristaps 			if (MDOC_Bd == node->tok)
580f73abda9Skristaps 				break;
58120fa2881Sschwarze 
58205c39368Sschwarze 	if (node)
583b723eac2Sschwarze 		mandoc_vmsg(MANDOCERR_BD_NEST,
584b723eac2Sschwarze 		    mdoc->parse, n->line, n->pos,
585b723eac2Sschwarze 		    "%s in Bd", mdoc_macronames[n->tok]);
58605c39368Sschwarze 
58705c39368Sschwarze 	return(1);
588f73abda9Skristaps }
589f73abda9Skristaps 
590f73abda9Skristaps static int
591f73abda9Skristaps pre_bl(PRE_ARGS)
592f73abda9Skristaps {
593769ee804Sschwarze 	struct mdoc_node *np;
594*4a9f685fSschwarze 	struct mdoc_argv *argv;
595*4a9f685fSschwarze 	int		  i;
596*4a9f685fSschwarze 	enum mdoc_list	  lt;
597f73abda9Skristaps 
5986093755cSschwarze 	if (MDOC_BLOCK != n->type) {
599769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
600769ee804Sschwarze 			assert(n->pending);
601769ee804Sschwarze 			np = n->pending->parent;
602769ee804Sschwarze 		} else
603769ee804Sschwarze 			np = n->parent;
604769ee804Sschwarze 
605769ee804Sschwarze 		assert(np);
606769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
607769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
608f73abda9Skristaps 		return(1);
6096e03d529Sschwarze 	}
610f73abda9Skristaps 
6116093755cSschwarze 	/*
6126093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
6136093755cSschwarze 	 * the first mentioned list type and warn about any remaining
6146093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
6156093755cSschwarze 	 */
616f73abda9Skristaps 
6176093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
618*4a9f685fSschwarze 		argv = n->args->argv + i;
6196093755cSschwarze 		lt = LIST__NONE;
620*4a9f685fSschwarze 		switch (argv->arg) {
6216093755cSschwarze 		/* Set list types. */
62249aff9f8Sschwarze 		case MDOC_Bullet:
6236093755cSschwarze 			lt = LIST_bullet;
6246093755cSschwarze 			break;
62549aff9f8Sschwarze 		case MDOC_Dash:
6266093755cSschwarze 			lt = LIST_dash;
6276093755cSschwarze 			break;
62849aff9f8Sschwarze 		case MDOC_Enum:
6296093755cSschwarze 			lt = LIST_enum;
6306093755cSschwarze 			break;
63149aff9f8Sschwarze 		case MDOC_Hyphen:
6326093755cSschwarze 			lt = LIST_hyphen;
6336093755cSschwarze 			break;
63449aff9f8Sschwarze 		case MDOC_Item:
6356093755cSschwarze 			lt = LIST_item;
6366093755cSschwarze 			break;
63749aff9f8Sschwarze 		case MDOC_Tag:
6386093755cSschwarze 			lt = LIST_tag;
6396093755cSschwarze 			break;
64049aff9f8Sschwarze 		case MDOC_Diag:
6416093755cSschwarze 			lt = LIST_diag;
6426093755cSschwarze 			break;
64349aff9f8Sschwarze 		case MDOC_Hang:
6446093755cSschwarze 			lt = LIST_hang;
6456093755cSschwarze 			break;
64649aff9f8Sschwarze 		case MDOC_Ohang:
6476093755cSschwarze 			lt = LIST_ohang;
6486093755cSschwarze 			break;
64949aff9f8Sschwarze 		case MDOC_Inset:
6506093755cSschwarze 			lt = LIST_inset;
6516093755cSschwarze 			break;
65249aff9f8Sschwarze 		case MDOC_Column:
6536093755cSschwarze 			lt = LIST_column;
65464d728e4Sschwarze 			break;
6556093755cSschwarze 		/* Set list arguments. */
65649aff9f8Sschwarze 		case MDOC_Compact:
657*4a9f685fSschwarze 			if (n->norm->Bl.comp)
658*4a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
659*4a9f685fSschwarze 				    mdoc->parse, argv->line,
660*4a9f685fSschwarze 				    argv->pos, "Bl -compact");
661*4a9f685fSschwarze 			n->norm->Bl.comp = 1;
66250e63e03Sschwarze 			break;
66349aff9f8Sschwarze 		case MDOC_Width:
664*4a9f685fSschwarze 			if (0 == argv->sz) {
665*4a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
666*4a9f685fSschwarze 				    mdoc->parse, argv->line,
667*4a9f685fSschwarze 				    argv->pos, "Bl -width");
668*4a9f685fSschwarze 				n->norm->Bl.width = "0n";
66922972b14Sschwarze 				break;
67022972b14Sschwarze 			}
671*4a9f685fSschwarze 			if (NULL != n->norm->Bl.width)
672*4a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
673*4a9f685fSschwarze 				    mdoc->parse, argv->line,
674*4a9f685fSschwarze 				    argv->pos, "Bl -width %s",
675*4a9f685fSschwarze 				    argv->value[0]);
676*4a9f685fSschwarze 			n->norm->Bl.width = argv->value[0];
67764d728e4Sschwarze 			break;
67849aff9f8Sschwarze 		case MDOC_Offset:
679*4a9f685fSschwarze 			if (0 == argv->sz) {
680*4a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
681*4a9f685fSschwarze 				    mdoc->parse, argv->line,
682*4a9f685fSschwarze 				    argv->pos, "Bl -offset");
68331e23753Sschwarze 				break;
68431e23753Sschwarze 			}
685*4a9f685fSschwarze 			if (NULL != n->norm->Bl.offs)
686*4a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
687*4a9f685fSschwarze 				    mdoc->parse, argv->line,
688*4a9f685fSschwarze 				    argv->pos, "Bl -offset %s",
689*4a9f685fSschwarze 				    argv->value[0]);
690*4a9f685fSschwarze 			n->norm->Bl.offs = argv->value[0];
691f73abda9Skristaps 			break;
692ddce0b0cSschwarze 		default:
693ddce0b0cSschwarze 			continue;
694f73abda9Skristaps 		}
695f73abda9Skristaps 
6966093755cSschwarze 		/* Check: multiple list types. */
6976093755cSschwarze 
6988c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
69920fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
7006093755cSschwarze 
7016093755cSschwarze 		/* Assign list type. */
7026093755cSschwarze 
7038c62fbf5Sschwarze 		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
7048c62fbf5Sschwarze 			n->norm->Bl.type = lt;
705769ee804Sschwarze 			/* Set column information, too. */
706769ee804Sschwarze 			if (LIST_column == lt) {
7078c62fbf5Sschwarze 				n->norm->Bl.ncols =
708769ee804Sschwarze 				    n->args->argv[i].sz;
70904e980cbSschwarze 				n->norm->Bl.cols = (void *)
710769ee804Sschwarze 				    n->args->argv[i].value;
711769ee804Sschwarze 			}
712769ee804Sschwarze 		}
7136093755cSschwarze 
7146093755cSschwarze 		/* The list type should come first. */
7156093755cSschwarze 
7168c62fbf5Sschwarze 		if (n->norm->Bl.type == LIST__NONE)
7178c62fbf5Sschwarze 			if (n->norm->Bl.width ||
7188c62fbf5Sschwarze 			    n->norm->Bl.offs ||
7198c62fbf5Sschwarze 			    n->norm->Bl.comp)
72066788495Sschwarze 				mandoc_msg(MANDOCERR_BL_LATETYPE,
72166788495Sschwarze 				    mdoc->parse, n->line, n->pos,
72266788495Sschwarze 				    mdoc_argnames[n->args->argv[0].arg]);
7236093755cSschwarze 		continue;
7246093755cSschwarze 	}
7256093755cSschwarze 
7266093755cSschwarze 	/* Allow lists to default to LIST_item. */
7276093755cSschwarze 
7288c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
72966788495Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BL_NOTYPE);
7308c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
7316e03d529Sschwarze 	}
732f73abda9Skristaps 
73364d728e4Sschwarze 	/*
73464d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
73564d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
7365eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
7375eced068Sschwarze 	 * no warning.
73864d728e4Sschwarze 	 */
73964d728e4Sschwarze 
7408c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
74149aff9f8Sschwarze 	case LIST_tag:
7425eced068Sschwarze 		if (NULL == n->norm->Bl.width)
74366788495Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BL_WIDTH);
744f73abda9Skristaps 		break;
74549aff9f8Sschwarze 	case LIST_column:
7466093755cSschwarze 		/* FALLTHROUGH */
74749aff9f8Sschwarze 	case LIST_diag:
7486093755cSschwarze 		/* FALLTHROUGH */
74949aff9f8Sschwarze 	case LIST_ohang:
7506093755cSschwarze 		/* FALLTHROUGH */
75149aff9f8Sschwarze 	case LIST_inset:
7526093755cSschwarze 		/* FALLTHROUGH */
75349aff9f8Sschwarze 	case LIST_item:
7548c62fbf5Sschwarze 		if (n->norm->Bl.width)
755817ac90bSschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
7566093755cSschwarze 		break;
75749aff9f8Sschwarze 	case LIST_bullet:
7585eced068Sschwarze 		/* FALLTHROUGH */
75949aff9f8Sschwarze 	case LIST_dash:
7605eced068Sschwarze 		/* FALLTHROUGH */
76149aff9f8Sschwarze 	case LIST_hyphen:
7625eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7635eced068Sschwarze 			n->norm->Bl.width = "2n";
7645eced068Sschwarze 		break;
76549aff9f8Sschwarze 	case LIST_enum:
7665eced068Sschwarze 		if (NULL == n->norm->Bl.width)
7675eced068Sschwarze 			n->norm->Bl.width = "3n";
7685eced068Sschwarze 		break;
76964d728e4Sschwarze 	default:
770f73abda9Skristaps 		break;
77164d728e4Sschwarze 	}
77264d728e4Sschwarze 
773f73abda9Skristaps 	return(1);
774f73abda9Skristaps }
775f73abda9Skristaps 
776f73abda9Skristaps static int
777f73abda9Skristaps pre_bd(PRE_ARGS)
778f73abda9Skristaps {
779769ee804Sschwarze 	struct mdoc_node *np;
780*4a9f685fSschwarze 	struct mdoc_argv *argv;
781*4a9f685fSschwarze 	int		  i;
782*4a9f685fSschwarze 	enum mdoc_disp	  dt;
783f73abda9Skristaps 
78431e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
785769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
786769ee804Sschwarze 			assert(n->pending);
787769ee804Sschwarze 			np = n->pending->parent;
788769ee804Sschwarze 		} else
789769ee804Sschwarze 			np = n->parent;
790769ee804Sschwarze 
791769ee804Sschwarze 		assert(np);
792769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
793769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
794f73abda9Skristaps 		return(1);
7956e03d529Sschwarze 	}
796f73abda9Skristaps 
79731e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
798*4a9f685fSschwarze 		argv = n->args->argv + i;
79931e23753Sschwarze 		dt = DISP__NONE;
80031e23753Sschwarze 
801*4a9f685fSschwarze 		switch (argv->arg) {
80249aff9f8Sschwarze 		case MDOC_Centred:
80331e23753Sschwarze 			dt = DISP_centred;
80431e23753Sschwarze 			break;
80549aff9f8Sschwarze 		case MDOC_Ragged:
80631e23753Sschwarze 			dt = DISP_ragged;
80731e23753Sschwarze 			break;
80849aff9f8Sschwarze 		case MDOC_Unfilled:
80931e23753Sschwarze 			dt = DISP_unfilled;
81031e23753Sschwarze 			break;
81149aff9f8Sschwarze 		case MDOC_Filled:
81231e23753Sschwarze 			dt = DISP_filled;
81331e23753Sschwarze 			break;
81449aff9f8Sschwarze 		case MDOC_Literal:
81531e23753Sschwarze 			dt = DISP_literal;
816f73abda9Skristaps 			break;
81749aff9f8Sschwarze 		case MDOC_File:
81831e23753Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
8196e03d529Sschwarze 			return(0);
82049aff9f8Sschwarze 		case MDOC_Offset:
821*4a9f685fSschwarze 			if (0 == argv->sz) {
822*4a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
823*4a9f685fSschwarze 				    mdoc->parse, argv->line,
824*4a9f685fSschwarze 				    argv->pos, "Bd -offset");
825f73abda9Skristaps 				break;
826f73abda9Skristaps 			}
827*4a9f685fSschwarze 			if (NULL != n->norm->Bd.offs)
828*4a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
829*4a9f685fSschwarze 				    mdoc->parse, argv->line,
830*4a9f685fSschwarze 				    argv->pos, "Bd -offset %s",
831*4a9f685fSschwarze 				    argv->value[0]);
832*4a9f685fSschwarze 			n->norm->Bd.offs = argv->value[0];
83331e23753Sschwarze 			break;
83449aff9f8Sschwarze 		case MDOC_Compact:
835*4a9f685fSschwarze 			if (n->norm->Bd.comp)
836*4a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
837*4a9f685fSschwarze 				    mdoc->parse, argv->line,
838*4a9f685fSschwarze 				    argv->pos, "Bd -compact");
839*4a9f685fSschwarze 			n->norm->Bd.comp = 1;
84031e23753Sschwarze 			break;
84131e23753Sschwarze 		default:
84231e23753Sschwarze 			abort();
84331e23753Sschwarze 			/* NOTREACHED */
84431e23753Sschwarze 		}
84531e23753Sschwarze 
84631e23753Sschwarze 		/* Check whether a type has already been assigned. */
84731e23753Sschwarze 
8488c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
84920fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
85031e23753Sschwarze 
85131e23753Sschwarze 		/* Make our type assignment. */
85231e23753Sschwarze 
8538c62fbf5Sschwarze 		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
8548c62fbf5Sschwarze 			n->norm->Bd.type = dt;
85531e23753Sschwarze 	}
85631e23753Sschwarze 
8578c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
85866788495Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BD_NOTYPE);
8598c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
86031e23753Sschwarze 	}
86131e23753Sschwarze 
86231e23753Sschwarze 	return(1);
863f73abda9Skristaps }
864f73abda9Skristaps 
865f73abda9Skristaps static int
866f73abda9Skristaps pre_ss(PRE_ARGS)
867f73abda9Skristaps {
868f73abda9Skristaps 
869f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
870f73abda9Skristaps 		return(1);
871f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
872f73abda9Skristaps }
873f73abda9Skristaps 
874f73abda9Skristaps static int
875f73abda9Skristaps pre_sh(PRE_ARGS)
876f73abda9Skristaps {
877f73abda9Skristaps 
878f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
879f73abda9Skristaps 		return(1);
880a4c002ecSschwarze 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
881f73abda9Skristaps }
882f73abda9Skristaps 
883f73abda9Skristaps static int
884f73abda9Skristaps pre_it(PRE_ARGS)
885f73abda9Skristaps {
886f73abda9Skristaps 
887f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
888f73abda9Skristaps 		return(1);
88920fa2881Sschwarze 
890f73abda9Skristaps 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
891f73abda9Skristaps }
892f73abda9Skristaps 
893f73abda9Skristaps static int
894f73abda9Skristaps pre_an(PRE_ARGS)
895f73abda9Skristaps {
8966475d5b0Sschwarze 	int		 i;
897f73abda9Skristaps 
898769ee804Sschwarze 	if (NULL == n->args)
899f73abda9Skristaps 		return(1);
900769ee804Sschwarze 
9016475d5b0Sschwarze 	for (i = 1; i < (int)n->args->argc; i++)
90220fa2881Sschwarze 		mdoc_pmsg(mdoc, n->args->argv[i].line,
90320fa2881Sschwarze 		    n->args->argv[i].pos, MANDOCERR_IGNARGV);
9047c2be9f8Sschwarze 
905769ee804Sschwarze 	if (MDOC_Split == n->args->argv[0].arg)
9068c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
907769ee804Sschwarze 	else if (MDOC_Nosplit == n->args->argv[0].arg)
9088c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
909769ee804Sschwarze 	else
910769ee804Sschwarze 		abort();
911769ee804Sschwarze 
912769ee804Sschwarze 	return(1);
913f73abda9Skristaps }
914f73abda9Skristaps 
915f73abda9Skristaps static int
91620fa2881Sschwarze pre_std(PRE_ARGS)
917f73abda9Skristaps {
918f73abda9Skristaps 
91920fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
92020fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
92120fa2881Sschwarze 			return(1);
922f73abda9Skristaps 
92366788495Sschwarze 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
92466788495Sschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
9256093755cSschwarze 	return(1);
9266093755cSschwarze }
9276093755cSschwarze 
9286093755cSschwarze static int
929551cd4a8Sschwarze pre_obsolete(PRE_ARGS)
930551cd4a8Sschwarze {
931551cd4a8Sschwarze 
932551cd4a8Sschwarze 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
933551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
934551cd4a8Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
935551cd4a8Sschwarze 	return(1);
936551cd4a8Sschwarze }
937551cd4a8Sschwarze 
938551cd4a8Sschwarze static int
939f73abda9Skristaps pre_dt(PRE_ARGS)
940f73abda9Skristaps {
941f73abda9Skristaps 
942b058e777Sschwarze 	if (NULL == mdoc->meta.date || mdoc->meta.os)
94351fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
94451fcab2fSschwarze 		    n->line, n->pos, "Dt");
94520fa2881Sschwarze 
946f73abda9Skristaps 	if (mdoc->meta.title)
94751fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
94851fcab2fSschwarze 		    n->line, n->pos, "Dt");
94920fa2881Sschwarze 
950f73abda9Skristaps 	return(1);
951f73abda9Skristaps }
952f73abda9Skristaps 
953f73abda9Skristaps static int
954f73abda9Skristaps pre_os(PRE_ARGS)
955f73abda9Skristaps {
956f73abda9Skristaps 
957b058e777Sschwarze 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
95851fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
95951fcab2fSschwarze 		    n->line, n->pos, "Os");
96020fa2881Sschwarze 
961f73abda9Skristaps 	if (mdoc->meta.os)
96251fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
96351fcab2fSschwarze 		    n->line, n->pos, "Os");
96420fa2881Sschwarze 
965f73abda9Skristaps 	return(1);
966f73abda9Skristaps }
967f73abda9Skristaps 
968f73abda9Skristaps static int
969f73abda9Skristaps pre_dd(PRE_ARGS)
970f73abda9Skristaps {
971f73abda9Skristaps 
972f73abda9Skristaps 	if (mdoc->meta.title || mdoc->meta.os)
97351fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
97451fcab2fSschwarze 		    n->line, n->pos, "Dd");
97520fa2881Sschwarze 
976f73abda9Skristaps 	if (mdoc->meta.date)
97751fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
97851fcab2fSschwarze 		    n->line, n->pos, "Dd");
97920fa2881Sschwarze 
980f73abda9Skristaps 	return(1);
981f73abda9Skristaps }
982f73abda9Skristaps 
983f73abda9Skristaps static int
984f73abda9Skristaps post_bf(POST_ARGS)
985f73abda9Skristaps {
986ecb10c32Sschwarze 	struct mdoc_node *np, *nch;
987ddce0b0cSschwarze 	enum mdocargt	  arg;
988f73abda9Skristaps 
989769ee804Sschwarze 	/*
990769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
991769ee804Sschwarze 	 * element, which contains the goods.
992769ee804Sschwarze 	 */
993769ee804Sschwarze 
994769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
995769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
996769ee804Sschwarze 			assert(mdoc->last->pending);
997769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
998769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
999769ee804Sschwarze 			np = mdoc->last->parent->head;
1000769ee804Sschwarze 		} else
1001769ee804Sschwarze 			np = mdoc->last->head;
1002769ee804Sschwarze 
1003769ee804Sschwarze 		assert(np);
1004769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
1005769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
1006f73abda9Skristaps 		return(1);
10076e03d529Sschwarze 	}
1008f73abda9Skristaps 
1009769ee804Sschwarze 	np = mdoc->last;
1010769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
1011769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
101250d41253Sschwarze 
1013ecb10c32Sschwarze 	/* Check the number of arguments. */
1014f73abda9Skristaps 
1015ecb10c32Sschwarze 	nch = np->child;
1016ecb10c32Sschwarze 	if (NULL == np->parent->args) {
1017ecb10c32Sschwarze 		if (NULL == nch) {
1018ecb10c32Sschwarze 			mdoc_nmsg(mdoc, np, MANDOCERR_BF_NOFONT);
101920fa2881Sschwarze 			return(1);
102020fa2881Sschwarze 		}
1021ecb10c32Sschwarze 		nch = nch->next;
1022ecb10c32Sschwarze 	}
1023ecb10c32Sschwarze 	if (NULL != nch)
1024ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1025ecb10c32Sschwarze 		    nch->line, nch->pos, "Bf ... %s", nch->string);
1026769ee804Sschwarze 
1027769ee804Sschwarze 	/* Extract argument into data. */
1028769ee804Sschwarze 
1029769ee804Sschwarze 	if (np->parent->args) {
1030769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
1031769ee804Sschwarze 		if (MDOC_Emphasis == arg)
10328c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
1033769ee804Sschwarze 		else if (MDOC_Literal == arg)
10348c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
1035769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
10368c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
1037769ee804Sschwarze 		else
1038769ee804Sschwarze 			abort();
1039769ee804Sschwarze 		return(1);
1040769ee804Sschwarze 	}
1041769ee804Sschwarze 
1042769ee804Sschwarze 	/* Extract parameter into data. */
1043769ee804Sschwarze 
1044769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
10458c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
1046769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
10478c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
1048769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
10498c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
105020fa2881Sschwarze 	else
1051ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1052ecb10c32Sschwarze 		    np->child->line, np->child->pos,
1053ecb10c32Sschwarze 		    "Bf %s", np->child->string);
1054769ee804Sschwarze 
1055769ee804Sschwarze 	return(1);
1056f73abda9Skristaps }
1057f73abda9Skristaps 
1058f73abda9Skristaps static int
105971719887Sschwarze post_lb(POST_ARGS)
106071719887Sschwarze {
106177e000ffSschwarze 	struct mdoc_node	*n;
106277e000ffSschwarze 	const char		*stdlibname;
106377e000ffSschwarze 	char			*libname;
106471719887Sschwarze 
1065bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1066bb648afaSschwarze 
106777e000ffSschwarze 	n = mdoc->last->child;
106820fa2881Sschwarze 
106977e000ffSschwarze 	assert(n);
107077e000ffSschwarze 	assert(MDOC_TEXT == n->type);
107120fa2881Sschwarze 
107277e000ffSschwarze 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
107377e000ffSschwarze 		mandoc_asprintf(&libname,
107477e000ffSschwarze 		    "library \\(lq%s\\(rq", n->string);
107577e000ffSschwarze 	else
107677e000ffSschwarze 		libname = mandoc_strdup(stdlibname);
107720fa2881Sschwarze 
107877e000ffSschwarze 	free(n->string);
107977e000ffSschwarze 	n->string = libname;
108020fa2881Sschwarze 	return(1);
108120fa2881Sschwarze }
108271719887Sschwarze 
108371719887Sschwarze static int
1084b31af00dSschwarze post_eoln(POST_ARGS)
1085b31af00dSschwarze {
1086ecb10c32Sschwarze 	const struct mdoc_node *n;
1087b31af00dSschwarze 
1088ecb10c32Sschwarze 	n = mdoc->last;
1089ecb10c32Sschwarze 	if (n->child)
1090ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
1091ecb10c32Sschwarze 		    mdoc->parse, n->line, n->pos,
1092ecb10c32Sschwarze 		    "%s %s", mdoc_macronames[n->tok],
1093ecb10c32Sschwarze 		    n->child->string);
1094b31af00dSschwarze 	return(1);
1095b31af00dSschwarze }
1096b31af00dSschwarze 
1097b31af00dSschwarze static int
10988521b0bcSschwarze post_vt(POST_ARGS)
10998521b0bcSschwarze {
11008521b0bcSschwarze 	const struct mdoc_node *n;
11018521b0bcSschwarze 
11028521b0bcSschwarze 	/*
11038521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
11048521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1105e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1106e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
11078521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
11088521b0bcSschwarze 	 */
11098521b0bcSschwarze 
11108521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
11118521b0bcSschwarze 		return(1);
11128521b0bcSschwarze 
11138521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1114bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
1115dd25b57cSschwarze 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1116dd25b57cSschwarze 			    n->line, n->pos, mdoc_macronames[n->tok]);
11178521b0bcSschwarze 
11188521b0bcSschwarze 	return(1);
11198521b0bcSschwarze }
11208521b0bcSschwarze 
11218521b0bcSschwarze static int
1122f73abda9Skristaps post_nm(POST_ARGS)
1123f73abda9Skristaps {
112420fa2881Sschwarze 
1125160ac481Sschwarze 	if (NULL != mdoc->meta.name)
112620fa2881Sschwarze 		return(1);
112720fa2881Sschwarze 
112883af2bccSschwarze 	mdoc_deroff(&mdoc->meta.name, mdoc->last);
112920fa2881Sschwarze 
113083af2bccSschwarze 	if (NULL == mdoc->meta.name) {
1131160ac481Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1132160ac481Sschwarze 		mdoc->meta.name = mandoc_strdup("UNKNOWN");
1133160ac481Sschwarze 	}
113420fa2881Sschwarze 	return(1);
113520fa2881Sschwarze }
113620fa2881Sschwarze 
113720fa2881Sschwarze static int
113820fa2881Sschwarze post_literal(POST_ARGS)
113920fa2881Sschwarze {
114020fa2881Sschwarze 
114120fa2881Sschwarze 	/*
114220fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
114320fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
114420fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
114520fa2881Sschwarze 	 * off in general since displays can't be nested.
114620fa2881Sschwarze 	 */
114720fa2881Sschwarze 
114820fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
114920fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
115020fa2881Sschwarze 
115120fa2881Sschwarze 	return(1);
115220fa2881Sschwarze }
115320fa2881Sschwarze 
115420fa2881Sschwarze static int
115520fa2881Sschwarze post_defaults(POST_ARGS)
115620fa2881Sschwarze {
115720fa2881Sschwarze 	struct mdoc_node *nn;
115820fa2881Sschwarze 
115920fa2881Sschwarze 	/*
116020fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
116120fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
116220fa2881Sschwarze 	 * gets an empty string.
116320fa2881Sschwarze 	 */
1164f73abda9Skristaps 
1165f73abda9Skristaps 	if (mdoc->last->child)
1166f73abda9Skristaps 		return(1);
116720fa2881Sschwarze 
116820fa2881Sschwarze 	nn = mdoc->last;
116920fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
117020fa2881Sschwarze 
117120fa2881Sschwarze 	switch (nn->tok) {
117249aff9f8Sschwarze 	case MDOC_Ar:
117320fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
117420fa2881Sschwarze 			return(0);
117520fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
117620fa2881Sschwarze 			return(0);
117720fa2881Sschwarze 		break;
117849aff9f8Sschwarze 	case MDOC_At:
117920fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
118020fa2881Sschwarze 			return(0);
118120fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
118220fa2881Sschwarze 			return(0);
118320fa2881Sschwarze 		break;
118449aff9f8Sschwarze 	case MDOC_Li:
118520fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
118620fa2881Sschwarze 			return(0);
118720fa2881Sschwarze 		break;
118849aff9f8Sschwarze 	case MDOC_Pa:
118920fa2881Sschwarze 		/* FALLTHROUGH */
119049aff9f8Sschwarze 	case MDOC_Mt:
119120fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
119220fa2881Sschwarze 			return(0);
119320fa2881Sschwarze 		break;
119420fa2881Sschwarze 	default:
119520fa2881Sschwarze 		abort();
119620fa2881Sschwarze 		/* NOTREACHED */
1197f73abda9Skristaps 	}
1198f73abda9Skristaps 
119920fa2881Sschwarze 	mdoc->last = nn;
120020fa2881Sschwarze 	return(1);
120120fa2881Sschwarze }
1202f73abda9Skristaps 
1203f73abda9Skristaps static int
1204f73abda9Skristaps post_at(POST_ARGS)
1205f73abda9Skristaps {
12060b2f1307Sschwarze 	struct mdoc_node	*n;
12070b2f1307Sschwarze 	const char		*std_att;
12080b2f1307Sschwarze 	char			*att;
120920fa2881Sschwarze 
121020fa2881Sschwarze 	/*
121120fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
121220fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
121320fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
121420fa2881Sschwarze 	 */
1215f73abda9Skristaps 
12160b2f1307Sschwarze 	if (NULL == (n = mdoc->last->child))
1217f73abda9Skristaps 		return(1);
121820fa2881Sschwarze 
12190b2f1307Sschwarze 	assert(MDOC_TEXT == n->type);
12200b2f1307Sschwarze 	if (NULL == (std_att = mdoc_a2att(n->string))) {
122120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
12220b2f1307Sschwarze 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
12230b2f1307Sschwarze 	} else
12240b2f1307Sschwarze 		att = mandoc_strdup(std_att);
1225f73abda9Skristaps 
12260b2f1307Sschwarze 	free(n->string);
12270b2f1307Sschwarze 	n->string = att;
122820fa2881Sschwarze 	return(1);
122920fa2881Sschwarze }
1230f73abda9Skristaps 
1231f73abda9Skristaps static int
1232f73abda9Skristaps post_an(POST_ARGS)
1233f73abda9Skristaps {
1234769ee804Sschwarze 	struct mdoc_node *np;
1235f73abda9Skristaps 
1236769ee804Sschwarze 	np = mdoc->last;
1237e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1238e7a93ef3Sschwarze 		if (0 == np->child)
1239e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1240e7a93ef3Sschwarze 	} else if (np->child)
1241bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
124220fa2881Sschwarze 
124320fa2881Sschwarze 	return(1);
1244f73abda9Skristaps }
1245f73abda9Skristaps 
1246f73abda9Skristaps static int
1247551cd4a8Sschwarze post_en(POST_ARGS)
1248551cd4a8Sschwarze {
1249551cd4a8Sschwarze 
1250551cd4a8Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
1251551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1252551cd4a8Sschwarze 	return(1);
1253551cd4a8Sschwarze }
1254551cd4a8Sschwarze 
1255551cd4a8Sschwarze static int
1256551cd4a8Sschwarze post_es(POST_ARGS)
1257551cd4a8Sschwarze {
1258551cd4a8Sschwarze 
1259551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1260551cd4a8Sschwarze 	return(1);
1261551cd4a8Sschwarze }
1262551cd4a8Sschwarze 
1263551cd4a8Sschwarze static int
1264f73abda9Skristaps post_it(POST_ARGS)
1265f73abda9Skristaps {
126619a69263Sschwarze 	int		  i, cols;
12676093755cSschwarze 	enum mdoc_list	  lt;
12689530682eSschwarze 	struct mdoc_node *nbl, *nit, *nch;
12696093755cSschwarze 	enum mandocerr	  er;
1270f73abda9Skristaps 
12719530682eSschwarze 	nit = mdoc->last;
12729530682eSschwarze 	if (MDOC_BLOCK != nit->type)
1273f73abda9Skristaps 		return(1);
1274f73abda9Skristaps 
12759530682eSschwarze 	nbl = nit->parent->parent;
12769530682eSschwarze 	lt = nbl->norm->Bl.type;
12776093755cSschwarze 
12786093755cSschwarze 	switch (lt) {
127949aff9f8Sschwarze 	case LIST_tag:
12809530682eSschwarze 		/* FALLTHROUGH */
128149aff9f8Sschwarze 	case LIST_hang:
1282f73abda9Skristaps 		/* FALLTHROUGH */
128349aff9f8Sschwarze 	case LIST_ohang:
1284f73abda9Skristaps 		/* FALLTHROUGH */
128549aff9f8Sschwarze 	case LIST_inset:
1286f73abda9Skristaps 		/* FALLTHROUGH */
128749aff9f8Sschwarze 	case LIST_diag:
12889530682eSschwarze 		if (NULL == nit->head->child)
12899530682eSschwarze 			mandoc_msg(MANDOCERR_IT_NOHEAD,
12909530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
12919530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1292f73abda9Skristaps 		break;
129349aff9f8Sschwarze 	case LIST_bullet:
1294f73abda9Skristaps 		/* FALLTHROUGH */
129549aff9f8Sschwarze 	case LIST_dash:
1296f73abda9Skristaps 		/* FALLTHROUGH */
129749aff9f8Sschwarze 	case LIST_enum:
1298f73abda9Skristaps 		/* FALLTHROUGH */
129949aff9f8Sschwarze 	case LIST_hyphen:
13009530682eSschwarze 		if (NULL == nit->body->child)
130166788495Sschwarze 			mandoc_msg(MANDOCERR_IT_NOBODY,
130266788495Sschwarze 			    mdoc->parse, nit->line, nit->pos,
130366788495Sschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1304f73abda9Skristaps 		/* FALLTHROUGH */
130549aff9f8Sschwarze 	case LIST_item:
13069530682eSschwarze 		if (NULL != nit->head->child)
1307ecb10c32Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1308ecb10c32Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1309ecb10c32Sschwarze 			    "It %s", nit->head->child->string);
1310f73abda9Skristaps 		break;
131149aff9f8Sschwarze 	case LIST_column:
13129530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
13136093755cSschwarze 
13149530682eSschwarze 		assert(NULL == nit->head->child);
13156093755cSschwarze 
13169530682eSschwarze 		for (i = 0, nch = nit->child; nch; nch = nch->next)
13179530682eSschwarze 			if (MDOC_BODY == nch->type)
1318f73abda9Skristaps 				i++;
131953292e81Sschwarze 
13206093755cSschwarze 		if (i < cols)
13216093755cSschwarze 			er = MANDOCERR_ARGCOUNT;
13226093755cSschwarze 		else if (i == cols || i == cols + 1)
1323f73abda9Skristaps 			break;
13246093755cSschwarze 		else
13256093755cSschwarze 			er = MANDOCERR_SYNTARGCOUNT;
132653292e81Sschwarze 
13279530682eSschwarze 		mandoc_vmsg(er, mdoc->parse, nit->line, nit->pos,
13286e03d529Sschwarze 		    "columns == %d (have %d)", cols, i);
132919a69263Sschwarze 		return(MANDOCERR_ARGCOUNT == er);
1330f73abda9Skristaps 	default:
133166788495Sschwarze 		abort();
1332f73abda9Skristaps 	}
1333f73abda9Skristaps 
1334f73abda9Skristaps 	return(1);
1335f73abda9Skristaps }
1336f73abda9Skristaps 
133720fa2881Sschwarze static int
133820fa2881Sschwarze post_bl_block(POST_ARGS)
133920fa2881Sschwarze {
1340bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
134120fa2881Sschwarze 
134220fa2881Sschwarze 	/*
134320fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
134420fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
134520fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
134620fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
134720fa2881Sschwarze 	 * rewritten into real lengths).
134820fa2881Sschwarze 	 */
134920fa2881Sschwarze 
135020fa2881Sschwarze 	n = mdoc->last;
135120fa2881Sschwarze 
13528c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
13538c62fbf5Sschwarze 	    NULL == n->norm->Bl.width) {
135420fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
135520fa2881Sschwarze 			return(0);
1356bb99f0faSschwarze 		assert(n->norm->Bl.width);
13578c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
135820fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
135920fa2881Sschwarze 			return(0);
13608c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1361bb99f0faSschwarze 	}
1362bb99f0faSschwarze 
1363bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1364bb99f0faSschwarze 		if (NULL == ni->body)
1365bb99f0faSschwarze 			continue;
1366bb99f0faSschwarze 		nc = ni->body->last;
1367bb99f0faSschwarze 		while (NULL != nc) {
1368bb99f0faSschwarze 			switch (nc->tok) {
136949aff9f8Sschwarze 			case MDOC_Pp:
1370bb99f0faSschwarze 				/* FALLTHROUGH */
137149aff9f8Sschwarze 			case MDOC_Lp:
1372bb99f0faSschwarze 				/* FALLTHROUGH */
137349aff9f8Sschwarze 			case MDOC_br:
1374bb99f0faSschwarze 				break;
1375bb99f0faSschwarze 			default:
1376bb99f0faSschwarze 				nc = NULL;
1377bb99f0faSschwarze 				continue;
1378bb99f0faSschwarze 			}
1379bb99f0faSschwarze 			if (NULL == ni->next) {
138020369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
138120369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
138220369664Sschwarze 				    mdoc_macronames[nc->tok]);
1383bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1384bb99f0faSschwarze 					return(0);
1385bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1386bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
138720369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
138820369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
138920369664Sschwarze 				    "%s before It",
139020369664Sschwarze 				    mdoc_macronames[nc->tok]);
1391bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1392bb99f0faSschwarze 			} else
1393bb99f0faSschwarze 				break;
1394bb99f0faSschwarze 			nc = ni->body->last;
1395bb99f0faSschwarze 		}
1396bb99f0faSschwarze 	}
139720fa2881Sschwarze 	return(1);
139820fa2881Sschwarze }
139920fa2881Sschwarze 
140020fa2881Sschwarze static int
140120fa2881Sschwarze post_bl_block_width(POST_ARGS)
140220fa2881Sschwarze {
140320fa2881Sschwarze 	size_t		  width;
140420fa2881Sschwarze 	int		  i;
140520fa2881Sschwarze 	enum mdoct	  tok;
140620fa2881Sschwarze 	struct mdoc_node *n;
140747813146Sschwarze 	char		  buf[24];
140820fa2881Sschwarze 
140920fa2881Sschwarze 	n = mdoc->last;
141020fa2881Sschwarze 
141120fa2881Sschwarze 	/*
141220fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
141320fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
141420fa2881Sschwarze 	 * literal string, or a scaling width.
141520fa2881Sschwarze 	 *
141620fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
141720fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
141820fa2881Sschwarze 	 */
141920fa2881Sschwarze 
14208c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
142120fa2881Sschwarze 		width = 6;
14228c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
142320fa2881Sschwarze 		return(1);
142419a69263Sschwarze 	else if (0 == (width = macro2len(tok)))  {
142520fa2881Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
142620fa2881Sschwarze 		return(1);
142720fa2881Sschwarze 	}
142820fa2881Sschwarze 
142920fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
143020fa2881Sschwarze 
143120fa2881Sschwarze 	assert(n->args);
143220fa2881Sschwarze 
143320fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
143420fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
143520fa2881Sschwarze 			break;
143620fa2881Sschwarze 
143720fa2881Sschwarze 	assert(i < (int)n->args->argc);
143820fa2881Sschwarze 
143947813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
144020fa2881Sschwarze 	free(n->args->argv[i].value[0]);
144120fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
144220fa2881Sschwarze 
144320fa2881Sschwarze 	/* Set our width! */
14448c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
144520fa2881Sschwarze 	return(1);
144620fa2881Sschwarze }
144720fa2881Sschwarze 
144820fa2881Sschwarze static int
144920fa2881Sschwarze post_bl_block_tag(POST_ARGS)
145020fa2881Sschwarze {
145120fa2881Sschwarze 	struct mdoc_node *n, *nn;
145220fa2881Sschwarze 	size_t		  sz, ssz;
145320fa2881Sschwarze 	int		  i;
145447813146Sschwarze 	char		  buf[24];
145520fa2881Sschwarze 
145620fa2881Sschwarze 	/*
145720fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
145820fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
145920fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
146020fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
146120fa2881Sschwarze 	 */
146220fa2881Sschwarze 
146320fa2881Sschwarze 	sz = 10;
146420fa2881Sschwarze 	n = mdoc->last;
146520fa2881Sschwarze 
146620fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
146720fa2881Sschwarze 		if (MDOC_It != nn->tok)
146820fa2881Sschwarze 			continue;
146920fa2881Sschwarze 
147020fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
147120fa2881Sschwarze 		nn = nn->head->child;
147220fa2881Sschwarze 
147320fa2881Sschwarze 		if (nn == NULL)
147420fa2881Sschwarze 			break;
147520fa2881Sschwarze 
147620fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
147720fa2881Sschwarze 			sz = strlen(nn->string) + 1;
147820fa2881Sschwarze 			break;
147920fa2881Sschwarze 		}
148020fa2881Sschwarze 
148119a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
148220fa2881Sschwarze 			sz = ssz;
148320fa2881Sschwarze 
148420fa2881Sschwarze 		break;
148520fa2881Sschwarze 	}
148620fa2881Sschwarze 
148720fa2881Sschwarze 	/* Defaults to ten ens. */
148820fa2881Sschwarze 
148947813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
149020fa2881Sschwarze 
149120fa2881Sschwarze 	/*
149220fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
149320fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
149420fa2881Sschwarze 	 */
149520fa2881Sschwarze 
149620fa2881Sschwarze 	assert(n->args);
149720fa2881Sschwarze 	i = (int)(n->args->argc)++;
149820fa2881Sschwarze 
14998286bf36Sschwarze 	n->args->argv = mandoc_reallocarray(n->args->argv,
15008286bf36Sschwarze 	    n->args->argc, sizeof(struct mdoc_argv));
150120fa2881Sschwarze 
150220fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
150320fa2881Sschwarze 	n->args->argv[i].line = n->line;
150420fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
150520fa2881Sschwarze 	n->args->argv[i].sz = 1;
150620fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
150720fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
150820fa2881Sschwarze 
150920fa2881Sschwarze 	/* Set our width! */
15108c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
151120fa2881Sschwarze 	return(1);
151220fa2881Sschwarze }
151320fa2881Sschwarze 
1514f73abda9Skristaps static int
1515395185ccSschwarze post_bl_head(POST_ARGS)
1516395185ccSschwarze {
151720fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
151820fa2881Sschwarze 	int		  i, j;
1519395185ccSschwarze 
15208c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
152120fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
152220fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1523395185ccSschwarze 
152420fa2881Sschwarze 	/*
152520fa2881Sschwarze 	 * Convert old-style lists, where the column width specifiers
152620fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
152720fa2881Sschwarze 	 * lists where they're argument values following -column.
152820fa2881Sschwarze 	 */
152920fa2881Sschwarze 
153020fa2881Sschwarze 	/* First, disallow both types and allow normal-form. */
153120fa2881Sschwarze 
153220fa2881Sschwarze 	/*
153320fa2881Sschwarze 	 * TODO: technically, we can accept both and just merge the two
153420fa2881Sschwarze 	 * lists, but I'll leave that for another day.
153520fa2881Sschwarze 	 */
153620fa2881Sschwarze 
15378c62fbf5Sschwarze 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
153820fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
15396093755cSschwarze 		return(0);
154020fa2881Sschwarze 	} else if (NULL == mdoc->last->child)
154120fa2881Sschwarze 		return(1);
154220fa2881Sschwarze 
154320fa2881Sschwarze 	np = mdoc->last->parent;
154420fa2881Sschwarze 	assert(np->args);
154520fa2881Sschwarze 
154620fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
154720fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
154820fa2881Sschwarze 			break;
154920fa2881Sschwarze 
155020fa2881Sschwarze 	assert(j < (int)np->args->argc);
155120fa2881Sschwarze 	assert(0 == np->args->argv[j].sz);
155220fa2881Sschwarze 
155320fa2881Sschwarze 	/*
1554a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
155520fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
155620fa2881Sschwarze 	 * column field.  Then, delete the head children.
155720fa2881Sschwarze 	 */
155820fa2881Sschwarze 
155920fa2881Sschwarze 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
15608286bf36Sschwarze 	np->args->argv[j].value = mandoc_reallocarray(NULL,
15618286bf36Sschwarze 	    (size_t)mdoc->last->nchild, sizeof(char *));
156220fa2881Sschwarze 
15638c62fbf5Sschwarze 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
156404e980cbSschwarze 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
156520fa2881Sschwarze 
156620fa2881Sschwarze 	for (i = 0, nn = mdoc->last->child; nn; i++) {
156720fa2881Sschwarze 		np->args->argv[j].value[i] = nn->string;
156820fa2881Sschwarze 		nn->string = NULL;
156920fa2881Sschwarze 		nnp = nn;
157020fa2881Sschwarze 		nn = nn->next;
157120fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1572395185ccSschwarze 	}
157320fa2881Sschwarze 
157420fa2881Sschwarze 	mdoc->last->nchild = 0;
157520fa2881Sschwarze 	mdoc->last->child = NULL;
157620fa2881Sschwarze 
15776093755cSschwarze 	return(1);
1578b16e7ddfSschwarze }
1579b16e7ddfSschwarze 
1580395185ccSschwarze static int
1581f73abda9Skristaps post_bl(POST_ARGS)
1582f73abda9Skristaps {
15832a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
15842a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
15852a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1586f73abda9Skristaps 
15872a427d60Sschwarze 	nbody = mdoc->last;
15882a427d60Sschwarze 	switch (nbody->type) {
158949aff9f8Sschwarze 	case MDOC_BLOCK:
159020fa2881Sschwarze 		return(post_bl_block(mdoc));
159149aff9f8Sschwarze 	case MDOC_HEAD:
15922a427d60Sschwarze 		return(post_bl_head(mdoc));
159349aff9f8Sschwarze 	case MDOC_BODY:
1594f6127a73Sschwarze 		break;
15952a427d60Sschwarze 	default:
15962a427d60Sschwarze 		return(1);
1597f6127a73Sschwarze 	}
1598f6127a73Sschwarze 
15992a427d60Sschwarze 	nchild = nbody->child;
16002a427d60Sschwarze 	while (NULL != nchild) {
16012a427d60Sschwarze 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
16022a427d60Sschwarze 			nchild = nchild->next;
16032a427d60Sschwarze 			continue;
16042a427d60Sschwarze 		}
16052a427d60Sschwarze 
1606dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1607dd25b57cSschwarze 		    nchild->line, nchild->pos,
1608dd25b57cSschwarze 		    mdoc_macronames[nchild->tok]);
16092a427d60Sschwarze 
16102a427d60Sschwarze 		/*
16112a427d60Sschwarze 		 * Move the node out of the Bl block.
16122a427d60Sschwarze 		 * First, collect all required node pointers.
16132a427d60Sschwarze 		 */
16142a427d60Sschwarze 
16152a427d60Sschwarze 		nblock  = nbody->parent;
16162a427d60Sschwarze 		nprev   = nblock->prev;
16172a427d60Sschwarze 		nparent = nblock->parent;
16182a427d60Sschwarze 		nnext   = nchild->next;
16192a427d60Sschwarze 
16202a427d60Sschwarze 		/*
16212a427d60Sschwarze 		 * Unlink this child.
16222a427d60Sschwarze 		 */
16232a427d60Sschwarze 
16242a427d60Sschwarze 		assert(NULL == nchild->prev);
16252a427d60Sschwarze 		if (0 == --nbody->nchild) {
16262a427d60Sschwarze 			nbody->child = NULL;
16272a427d60Sschwarze 			nbody->last  = NULL;
16282a427d60Sschwarze 			assert(NULL == nnext);
16292a427d60Sschwarze 		} else {
16302a427d60Sschwarze 			nbody->child = nnext;
16312a427d60Sschwarze 			nnext->prev = NULL;
16322a427d60Sschwarze 		}
16332a427d60Sschwarze 
16342a427d60Sschwarze 		/*
16352a427d60Sschwarze 		 * Relink this child.
16362a427d60Sschwarze 		 */
16372a427d60Sschwarze 
16382a427d60Sschwarze 		nchild->parent = nparent;
16392a427d60Sschwarze 		nchild->prev   = nprev;
16402a427d60Sschwarze 		nchild->next   = nblock;
16412a427d60Sschwarze 
16422a427d60Sschwarze 		nblock->prev = nchild;
16432a427d60Sschwarze 		nparent->nchild++;
16442a427d60Sschwarze 		if (NULL == nprev)
16452a427d60Sschwarze 			nparent->child = nchild;
16462a427d60Sschwarze 		else
16472a427d60Sschwarze 			nprev->next = nchild;
16482a427d60Sschwarze 
16492a427d60Sschwarze 		nchild = nnext;
1650f73abda9Skristaps 	}
1651f73abda9Skristaps 
1652f73abda9Skristaps 	return(1);
1653f73abda9Skristaps }
1654f73abda9Skristaps 
1655f73abda9Skristaps static int
1656f73abda9Skristaps ebool(struct mdoc *mdoc)
1657f73abda9Skristaps {
1658f73abda9Skristaps 
1659bb648afaSschwarze 	if (NULL == mdoc->last->child) {
1660f9e7bf99Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1661f9e7bf99Sschwarze 			mdoc->flags ^= MDOC_SMOFF;
1662f73abda9Skristaps 		return(1);
1663bb648afaSschwarze 	}
1664f9e7bf99Sschwarze 
1665f9e7bf99Sschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1666f73abda9Skristaps 
166720fa2881Sschwarze 	assert(MDOC_TEXT == mdoc->last->child->type);
166820fa2881Sschwarze 
1669ec2beb53Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "on")) {
1670ec2beb53Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1671ec2beb53Sschwarze 			mdoc->flags &= ~MDOC_SMOFF;
167220fa2881Sschwarze 		return(1);
1673ec2beb53Sschwarze 	}
1674ec2beb53Sschwarze 	if (0 == strcmp(mdoc->last->child->string, "off")) {
1675ec2beb53Sschwarze 		if (MDOC_Sm == mdoc->last->tok)
1676ec2beb53Sschwarze 			mdoc->flags |= MDOC_SMOFF;
167720fa2881Sschwarze 		return(1);
1678ec2beb53Sschwarze 	}
167920fa2881Sschwarze 
168020fa2881Sschwarze 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
168120fa2881Sschwarze 	return(1);
168220fa2881Sschwarze }
1683f73abda9Skristaps 
1684f73abda9Skristaps static int
1685f73abda9Skristaps post_root(POST_ARGS)
1686f73abda9Skristaps {
168743edbcc8Sschwarze 	int		  ret;
168820fa2881Sschwarze 	struct mdoc_node *n;
1689f73abda9Skristaps 
169043edbcc8Sschwarze 	ret = 1;
169120fa2881Sschwarze 
169220fa2881Sschwarze 	/* Check that we have a finished prologue. */
169320fa2881Sschwarze 
169420fa2881Sschwarze 	if ( ! (MDOC_PBODY & mdoc->flags)) {
169543edbcc8Sschwarze 		ret = 0;
16966e03d529Sschwarze 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1697f73abda9Skristaps 	}
1698f73abda9Skristaps 
169920fa2881Sschwarze 	n = mdoc->first;
170020fa2881Sschwarze 	assert(n);
170120fa2881Sschwarze 
170220fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
170320fa2881Sschwarze 
170443edbcc8Sschwarze 	if (NULL == n->child)
170543edbcc8Sschwarze 		mdoc_nmsg(mdoc, n, MANDOCERR_DOC_EMPTY);
170651fcab2fSschwarze 	else if (MDOC_Sh != n->child->tok)
170751fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
170851fcab2fSschwarze 		    n->child->line, n->child->pos,
170951fcab2fSschwarze 		    mdoc_macronames[n->child->tok]);
171020fa2881Sschwarze 
171143edbcc8Sschwarze 	return(ret);
171220fa2881Sschwarze }
1713f73abda9Skristaps 
1714f73abda9Skristaps static int
1715f73abda9Skristaps post_st(POST_ARGS)
1716f73abda9Skristaps {
1717bb648afaSschwarze 	struct mdoc_node	 *ch;
171820fa2881Sschwarze 	const char		 *p;
1719f73abda9Skristaps 
1720bb648afaSschwarze 	if (NULL == (ch = mdoc->last->child)) {
1721307e5a07Sschwarze 		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1722307e5a07Sschwarze 		    mdoc->last->line, mdoc->last->pos,
1723307e5a07Sschwarze 		    mdoc_macronames[mdoc->last->tok]);
1724bb648afaSschwarze 		mdoc_node_delete(mdoc, mdoc->last);
1725bb648afaSschwarze 		return(1);
1726bb648afaSschwarze 	}
172720fa2881Sschwarze 
1728bb648afaSschwarze 	assert(MDOC_TEXT == ch->type);
172920fa2881Sschwarze 
1730bb648afaSschwarze 	if (NULL == (p = mdoc_a2st(ch->string))) {
173120fa2881Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
173220fa2881Sschwarze 		mdoc_node_delete(mdoc, mdoc->last);
173320fa2881Sschwarze 	} else {
1734bb648afaSschwarze 		free(ch->string);
1735bb648afaSschwarze 		ch->string = mandoc_strdup(p);
1736f73abda9Skristaps 	}
1737f73abda9Skristaps 
173820fa2881Sschwarze 	return(1);
173920fa2881Sschwarze }
1740f73abda9Skristaps 
1741f73abda9Skristaps static int
1742011fe33bSschwarze post_rs(POST_ARGS)
1743011fe33bSschwarze {
174420fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
174520fa2881Sschwarze 	int		  i, j;
1746011fe33bSschwarze 
1747bb648afaSschwarze 	switch (mdoc->last->type) {
174849aff9f8Sschwarze 	case MDOC_HEAD:
1749bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1750011fe33bSschwarze 		return(1);
175149aff9f8Sschwarze 	case MDOC_BODY:
1752bb648afaSschwarze 		if (mdoc->last->child)
1753bb648afaSschwarze 			break;
1754bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1755bb648afaSschwarze 		return(1);
1756bb648afaSschwarze 	default:
1757bb648afaSschwarze 		return(1);
1758bb648afaSschwarze 	}
1759011fe33bSschwarze 
176020fa2881Sschwarze 	/*
176120fa2881Sschwarze 	 * Make sure only certain types of nodes are allowed within the
176220fa2881Sschwarze 	 * the `Rs' body.  Delete offending nodes and raise a warning.
176320fa2881Sschwarze 	 * Do this before re-ordering for the sake of clarity.
176420fa2881Sschwarze 	 */
176520fa2881Sschwarze 
176620fa2881Sschwarze 	next = NULL;
176720fa2881Sschwarze 	for (nn = mdoc->last->child; nn; nn = next) {
176820fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
176920fa2881Sschwarze 			if (nn->tok == rsord[i])
1770011fe33bSschwarze 				break;
177120fa2881Sschwarze 
177220fa2881Sschwarze 		if (i < RSORD_MAX) {
17735d273f35Sschwarze 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
17745d273f35Sschwarze 				mdoc->last->norm->Rs.quote_T++;
177520fa2881Sschwarze 			next = nn->next;
177620fa2881Sschwarze 			continue;
177720fa2881Sschwarze 		}
177820fa2881Sschwarze 
177920fa2881Sschwarze 		next = nn->next;
1780dd25b57cSschwarze 		mandoc_msg(MANDOCERR_RS_SKIP, mdoc->parse,
1781dd25b57cSschwarze 		    nn->line, nn->pos, mdoc_macronames[nn->tok]);
178220fa2881Sschwarze 		mdoc_node_delete(mdoc, nn);
178320fa2881Sschwarze 	}
178420fa2881Sschwarze 
178520fa2881Sschwarze 	/*
1786c6176538Sschwarze 	 * Nothing to sort if only invalid nodes were found
1787c6176538Sschwarze 	 * inside the `Rs' body.
1788c6176538Sschwarze 	 */
1789c6176538Sschwarze 
1790c6176538Sschwarze 	if (NULL == mdoc->last->child)
1791c6176538Sschwarze 		return(1);
1792c6176538Sschwarze 
1793c6176538Sschwarze 	/*
179420fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
179520fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
179620fa2881Sschwarze 	 * and correctly order it.  This is a insertion sort.
179720fa2881Sschwarze 	 */
179820fa2881Sschwarze 
179920fa2881Sschwarze 	next = NULL;
180020fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
180120fa2881Sschwarze 		/* Determine order of `nn'. */
180220fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
180320fa2881Sschwarze 			if (rsord[i] == nn->tok)
180420fa2881Sschwarze 				break;
180520fa2881Sschwarze 
180620fa2881Sschwarze 		/*
180720fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
180820fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
180920fa2881Sschwarze 		 * just re-ordering, there's no need for the
181020fa2881Sschwarze 		 * full unlink process.
181120fa2881Sschwarze 		 */
181220fa2881Sschwarze 
181320fa2881Sschwarze 		if (NULL != (next = nn->next))
181420fa2881Sschwarze 			next->prev = nn->prev;
181520fa2881Sschwarze 
181620fa2881Sschwarze 		if (NULL != (prev = nn->prev))
181720fa2881Sschwarze 			prev->next = nn->next;
181820fa2881Sschwarze 
181920fa2881Sschwarze 		nn->prev = nn->next = NULL;
182020fa2881Sschwarze 
182120fa2881Sschwarze 		/*
182220fa2881Sschwarze 		 * Scan back until we reach a node that's
182320fa2881Sschwarze 		 * ordered before `nn'.
182420fa2881Sschwarze 		 */
182520fa2881Sschwarze 
182620fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
182720fa2881Sschwarze 			/* Determine order of `prev'. */
182820fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
182920fa2881Sschwarze 				if (rsord[j] == prev->tok)
183020fa2881Sschwarze 					break;
183120fa2881Sschwarze 
183220fa2881Sschwarze 			if (j <= i)
183320fa2881Sschwarze 				break;
183420fa2881Sschwarze 		}
183520fa2881Sschwarze 
183620fa2881Sschwarze 		/*
183720fa2881Sschwarze 		 * Set `nn' back into its correct place in front
183820fa2881Sschwarze 		 * of the `prev' node.
183920fa2881Sschwarze 		 */
184020fa2881Sschwarze 
184120fa2881Sschwarze 		nn->prev = prev;
184220fa2881Sschwarze 
184320fa2881Sschwarze 		if (prev) {
184420fa2881Sschwarze 			if (prev->next)
184520fa2881Sschwarze 				prev->next->prev = nn;
184620fa2881Sschwarze 			nn->next = prev->next;
184720fa2881Sschwarze 			prev->next = nn;
184820fa2881Sschwarze 		} else {
184920fa2881Sschwarze 			mdoc->last->child->prev = nn;
185020fa2881Sschwarze 			nn->next = mdoc->last->child;
185120fa2881Sschwarze 			mdoc->last->child = nn;
185220fa2881Sschwarze 		}
1853011fe33bSschwarze 	}
1854011fe33bSschwarze 
1855011fe33bSschwarze 	return(1);
1856011fe33bSschwarze }
1857011fe33bSschwarze 
18584039b21cSschwarze /*
18594039b21cSschwarze  * For some arguments of some macros,
18604039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
18614039b21cSschwarze  */
18624039b21cSschwarze static int
18634039b21cSschwarze post_hyph(POST_ARGS)
18644039b21cSschwarze {
18654039b21cSschwarze 	struct mdoc_node	*n, *nch;
18664039b21cSschwarze 	char			*cp;
18674039b21cSschwarze 
18684039b21cSschwarze 	n = mdoc->last;
18694039b21cSschwarze 	switch (n->type) {
187049aff9f8Sschwarze 	case MDOC_HEAD:
18714039b21cSschwarze 		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
18724039b21cSschwarze 			break;
18734039b21cSschwarze 		return(1);
187449aff9f8Sschwarze 	case MDOC_BODY:
18754039b21cSschwarze 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
18764039b21cSschwarze 			break;
18774039b21cSschwarze 		return(1);
187849aff9f8Sschwarze 	case MDOC_ELEM:
18794039b21cSschwarze 		break;
18804039b21cSschwarze 	default:
18814039b21cSschwarze 		return(1);
18824039b21cSschwarze 	}
18834039b21cSschwarze 
18844039b21cSschwarze 	for (nch = n->child; nch; nch = nch->next) {
18854039b21cSschwarze 		if (MDOC_TEXT != nch->type)
18864039b21cSschwarze 			continue;
18874039b21cSschwarze 		cp = nch->string;
1888b7e2b14eSschwarze 		if ('\0' == *cp)
18894039b21cSschwarze 			continue;
18904039b21cSschwarze 		while ('\0' != *(++cp))
18914039b21cSschwarze 			if ('-' == *cp &&
18924039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
18934039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
18944039b21cSschwarze 				*cp = ASCII_HYPH;
18954039b21cSschwarze 	}
18964039b21cSschwarze 	return(1);
18974039b21cSschwarze }
18984039b21cSschwarze 
1899011fe33bSschwarze static int
1900af216717Sschwarze post_ns(POST_ARGS)
1901af216717Sschwarze {
1902af216717Sschwarze 
1903af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
1904b723eac2Sschwarze 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NS_SKIP);
1905af216717Sschwarze 	return(1);
1906af216717Sschwarze }
1907af216717Sschwarze 
1908af216717Sschwarze static int
1909f73abda9Skristaps post_sh(POST_ARGS)
1910f73abda9Skristaps {
1911f73abda9Skristaps 
1912f73abda9Skristaps 	if (MDOC_HEAD == mdoc->last->type)
1913f73abda9Skristaps 		return(post_sh_head(mdoc));
1914f73abda9Skristaps 	if (MDOC_BODY == mdoc->last->type)
1915f73abda9Skristaps 		return(post_sh_body(mdoc));
1916f73abda9Skristaps 
1917f73abda9Skristaps 	return(1);
1918f73abda9Skristaps }
1919f73abda9Skristaps 
1920f73abda9Skristaps static int
1921f73abda9Skristaps post_sh_body(POST_ARGS)
1922f73abda9Skristaps {
1923f73abda9Skristaps 	struct mdoc_node *n;
1924f73abda9Skristaps 
1925f8c9d6f2Sschwarze 	if (SEC_NAME != mdoc->lastsec)
1926f73abda9Skristaps 		return(1);
1927f73abda9Skristaps 
1928f73abda9Skristaps 	/*
1929f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1930f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1931f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1932f73abda9Skristaps 	 */
1933f73abda9Skristaps 
193420fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
193551fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
193651fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, "empty");
193720fa2881Sschwarze 		return(1);
193820fa2881Sschwarze 	}
1939f73abda9Skristaps 
1940f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1941f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1942f73abda9Skristaps 			continue;
1943f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1944f73abda9Skristaps 			continue;
194551fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
194651fcab2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1947f73abda9Skristaps 	}
1948f73abda9Skristaps 
194949d529b5Sschwarze 	assert(n);
19504602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1951f73abda9Skristaps 		return(1);
1952f73abda9Skristaps 
195351fcab2fSschwarze 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
195451fcab2fSschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
195520fa2881Sschwarze 	return(1);
195620fa2881Sschwarze }
1957f73abda9Skristaps 
1958f73abda9Skristaps static int
1959f73abda9Skristaps post_sh_head(POST_ARGS)
1960f73abda9Skristaps {
1961a2cff342Sschwarze 	struct mdoc_node *n;
196251fcab2fSschwarze 	const char	*goodsec;
196346133849Sschwarze 	char		*secname;
1964f73abda9Skristaps 	enum mdoc_sec	 sec;
1965f73abda9Skristaps 
1966f73abda9Skristaps 	/*
1967f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
196820fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
196920fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
197020fa2881Sschwarze 	 * manual sections.
1971f73abda9Skristaps 	 */
1972f73abda9Skristaps 
197383af2bccSschwarze 	secname = NULL;
197404e980cbSschwarze 	sec = SEC_CUSTOM;
197546133849Sschwarze 	mdoc_deroff(&secname, mdoc->last);
197646133849Sschwarze 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
1977f73abda9Skristaps 
197820fa2881Sschwarze 	/* The NAME should be first. */
1979f73abda9Skristaps 
1980fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
198151fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
198251fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
198320fa2881Sschwarze 
198420fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
198520fa2881Sschwarze 
198622881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
198775088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
198820fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
198922881299Sschwarze 	} else {
199075088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
199120fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
199222881299Sschwarze 	}
199320fa2881Sschwarze 
199420fa2881Sschwarze 	/* Mark our last section. */
199520fa2881Sschwarze 
199620fa2881Sschwarze 	mdoc->lastsec = sec;
19971eccdf28Sschwarze 
19981eccdf28Sschwarze 	/*
19991eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
20001eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
20011eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
20021eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
20031eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
20041eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
20051eccdf28Sschwarze 	 */
20061eccdf28Sschwarze 
2007a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
2008a2cff342Sschwarze 	mdoc->last->sec = sec;
2009a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
2010a2cff342Sschwarze 		n->sec = sec;
201120fa2881Sschwarze 
201220fa2881Sschwarze 	/* We don't care about custom sections after this. */
2013fccfce9dSschwarze 
201446133849Sschwarze 	if (SEC_CUSTOM == sec) {
201546133849Sschwarze 		free(secname);
2016f73abda9Skristaps 		return(1);
201746133849Sschwarze 	}
2018fccfce9dSschwarze 
20196be99f77Sschwarze 	/*
202020fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
202120fa2881Sschwarze 	 * out of order.
20226be99f77Sschwarze 	 */
2023f73abda9Skristaps 
202420fa2881Sschwarze 	if (sec == mdoc->lastnamed)
202551fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->parse,
202651fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
202720fa2881Sschwarze 
202820fa2881Sschwarze 	if (sec < mdoc->lastnamed)
202951fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->parse,
203051fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, secname);
203120fa2881Sschwarze 
203220fa2881Sschwarze 	/* Mark the last named section. */
203320fa2881Sschwarze 
203420fa2881Sschwarze 	mdoc->lastnamed = sec;
203520fa2881Sschwarze 
203620fa2881Sschwarze 	/* Check particular section/manual conventions. */
203720fa2881Sschwarze 
203892c0ca7fSschwarze 	assert(mdoc->meta.msec);
203920fa2881Sschwarze 
204051fcab2fSschwarze 	goodsec = NULL;
204120fa2881Sschwarze 	switch (sec) {
204249aff9f8Sschwarze 	case SEC_ERRORS:
2043be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
2044be89e780Sschwarze 			break;
204551fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
2046be89e780Sschwarze 		/* FALLTHROUGH */
204749aff9f8Sschwarze 	case SEC_RETURN_VALUES:
204820fa2881Sschwarze 		/* FALLTHROUGH */
204949aff9f8Sschwarze 	case SEC_LIBRARY:
205092c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
2051f73abda9Skristaps 			break;
205292c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
205392c0ca7fSschwarze 			break;
205451fcab2fSschwarze 		if (NULL == goodsec)
205551fcab2fSschwarze 			goodsec = "2, 3, 9";
205603ab2f23Sdlg 		/* FALLTHROUGH */
205749aff9f8Sschwarze 	case SEC_CONTEXT:
205892c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
205992c0ca7fSschwarze 			break;
206051fcab2fSschwarze 		if (NULL == goodsec)
206151fcab2fSschwarze 			goodsec = "9";
206251fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
206351fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
206451fcab2fSschwarze 		    "%s for %s only", secname, goodsec);
206520fa2881Sschwarze 		break;
2066f73abda9Skristaps 	default:
2067f73abda9Skristaps 		break;
2068f73abda9Skristaps 	}
2069f73abda9Skristaps 
207046133849Sschwarze 	free(secname);
2071f73abda9Skristaps 	return(1);
2072f73abda9Skristaps }
2073d39b9a9cSschwarze 
207420fa2881Sschwarze static int
2075f6127a73Sschwarze post_ignpar(POST_ARGS)
2076f6127a73Sschwarze {
2077f6127a73Sschwarze 	struct mdoc_node *np;
2078f6127a73Sschwarze 
2079f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
2080f6127a73Sschwarze 		return(1);
2081f6127a73Sschwarze 
2082f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2083f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
208420369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
208520369664Sschwarze 			    mdoc->parse, np->line, np->pos,
208620369664Sschwarze 			    "%s after %s", mdoc_macronames[np->tok],
208720369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2088f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2089f6127a73Sschwarze 		}
2090f6127a73Sschwarze 
2091f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2092f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
209320369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
209420369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
209520369664Sschwarze 			    mdoc_macronames[np->tok],
209620369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2097f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2098f6127a73Sschwarze 		}
2099f6127a73Sschwarze 
2100f6127a73Sschwarze 	return(1);
2101f6127a73Sschwarze }
2102f6127a73Sschwarze 
2103f6127a73Sschwarze static int
210420fa2881Sschwarze pre_par(PRE_ARGS)
2105d39b9a9cSschwarze {
2106d39b9a9cSschwarze 
2107d39b9a9cSschwarze 	if (NULL == mdoc->last)
2108d39b9a9cSschwarze 		return(1);
2109f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2110f6127a73Sschwarze 		return(1);
2111d39b9a9cSschwarze 
211220fa2881Sschwarze 	/*
211320fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
211420fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
211520fa2881Sschwarze 	 */
2116d39b9a9cSschwarze 
2117e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2118e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2119e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2120d39b9a9cSschwarze 		return(1);
21218c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2122d39b9a9cSschwarze 		return(1);
21238c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2124d39b9a9cSschwarze 		return(1);
21258c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2126f6127a73Sschwarze 		return(1);
2127d39b9a9cSschwarze 
212820369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
212920369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
213020369664Sschwarze 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
213120369664Sschwarze 	    mdoc_macronames[n->tok]);
2132d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2133d39b9a9cSschwarze 	return(1);
2134d39b9a9cSschwarze }
213520fa2881Sschwarze 
213620fa2881Sschwarze static int
2137e0dd4c9cSschwarze post_par(POST_ARGS)
2138e0dd4c9cSschwarze {
213920369664Sschwarze 	struct mdoc_node *np;
2140e0dd4c9cSschwarze 
2141e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2142e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2143e0dd4c9cSschwarze 		return(1);
2144e0dd4c9cSschwarze 
214520369664Sschwarze 	if (NULL == (np = mdoc->last->prev)) {
214620369664Sschwarze 		np = mdoc->last->parent;
214720369664Sschwarze 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2148e0dd4c9cSschwarze 			return(1);
2149e0dd4c9cSschwarze 	} else {
215020369664Sschwarze 		if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2151e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
215220369664Sschwarze 		     (MDOC_sp != np->tok && MDOC_br != np->tok)))
2153e0dd4c9cSschwarze 			return(1);
2154e0dd4c9cSschwarze 	}
2155e0dd4c9cSschwarze 
215620369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
215720369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
215820369664Sschwarze 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
215920369664Sschwarze 	    mdoc_macronames[np->tok]);
2160e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2161e0dd4c9cSschwarze 	return(1);
2162e0dd4c9cSschwarze }
2163e0dd4c9cSschwarze 
2164e0dd4c9cSschwarze static int
216520fa2881Sschwarze pre_literal(PRE_ARGS)
216620fa2881Sschwarze {
216720fa2881Sschwarze 
216820fa2881Sschwarze 	if (MDOC_BODY != n->type)
216920fa2881Sschwarze 		return(1);
217020fa2881Sschwarze 
217120fa2881Sschwarze 	/*
217220fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
217320fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
217420fa2881Sschwarze 	 */
217520fa2881Sschwarze 
217620fa2881Sschwarze 	switch (n->tok) {
217749aff9f8Sschwarze 	case MDOC_Dl:
217820fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
217920fa2881Sschwarze 		break;
218049aff9f8Sschwarze 	case MDOC_Bd:
21818c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
218220fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
21838c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
218420fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
218520fa2881Sschwarze 		break;
218620fa2881Sschwarze 	default:
218720fa2881Sschwarze 		abort();
218820fa2881Sschwarze 		/* NOTREACHED */
218920fa2881Sschwarze 	}
219020fa2881Sschwarze 
219120fa2881Sschwarze 	return(1);
219220fa2881Sschwarze }
219320fa2881Sschwarze 
219420fa2881Sschwarze static int
219520fa2881Sschwarze post_dd(POST_ARGS)
219620fa2881Sschwarze {
219720fa2881Sschwarze 	struct mdoc_node *n;
219883af2bccSschwarze 	char		 *datestr;
219920fa2881Sschwarze 
2200b058e777Sschwarze 	if (mdoc->meta.date)
2201b058e777Sschwarze 		free(mdoc->meta.date);
220220fa2881Sschwarze 
2203b058e777Sschwarze 	n = mdoc->last;
2204b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2205231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2206231c7061Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
220720fa2881Sschwarze 		return(1);
220820fa2881Sschwarze 	}
220920fa2881Sschwarze 
221083af2bccSschwarze 	datestr = NULL;
221183af2bccSschwarze 	mdoc_deroff(&datestr, n);
221283af2bccSschwarze 	if (mdoc->quick)
221383af2bccSschwarze 		mdoc->meta.date = datestr;
221483af2bccSschwarze 	else {
221583af2bccSschwarze 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
221683af2bccSschwarze 		    datestr, n->line, n->pos);
221783af2bccSschwarze 		free(datestr);
221804e980cbSschwarze 	}
221920fa2881Sschwarze 	return(1);
222020fa2881Sschwarze }
222120fa2881Sschwarze 
222220fa2881Sschwarze static int
222320fa2881Sschwarze post_dt(POST_ARGS)
222420fa2881Sschwarze {
222520fa2881Sschwarze 	struct mdoc_node *nn, *n;
222620fa2881Sschwarze 	const char	 *cp;
222720fa2881Sschwarze 	char		 *p;
222820fa2881Sschwarze 
222920fa2881Sschwarze 	n = mdoc->last;
223020fa2881Sschwarze 
223120fa2881Sschwarze 	if (mdoc->meta.title)
223220fa2881Sschwarze 		free(mdoc->meta.title);
223320fa2881Sschwarze 	if (mdoc->meta.vol)
223420fa2881Sschwarze 		free(mdoc->meta.vol);
223520fa2881Sschwarze 	if (mdoc->meta.arch)
223620fa2881Sschwarze 		free(mdoc->meta.arch);
223720fa2881Sschwarze 
223820fa2881Sschwarze 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
223920fa2881Sschwarze 
224051fcab2fSschwarze 	/* First check that all characters are uppercase. */
224120fa2881Sschwarze 
224220fa2881Sschwarze 	if (NULL != (nn = n->child))
224320fa2881Sschwarze 		for (p = nn->string; *p; p++) {
224404e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
224520fa2881Sschwarze 				continue;
224651fcab2fSschwarze 			mandoc_msg(MANDOCERR_TITLE_CASE,
224751fcab2fSschwarze 			    mdoc->parse, nn->line,
224851fcab2fSschwarze 			    nn->pos + (p - nn->string),
224951fcab2fSschwarze 			    nn->string);
225020fa2881Sschwarze 			break;
225120fa2881Sschwarze 		}
225220fa2881Sschwarze 
225320fa2881Sschwarze 	/* Handles: `.Dt'
225449aff9f8Sschwarze 	 * title = unknown, volume = local, msec = 0, arch = NULL
225520fa2881Sschwarze 	 */
225620fa2881Sschwarze 
225720fa2881Sschwarze 	if (NULL == (nn = n->child)) {
225820fa2881Sschwarze 		/* XXX: make these macro values. */
225920fa2881Sschwarze 		/* FIXME: warn about missing values. */
226020fa2881Sschwarze 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
226120fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
226220fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
226320fa2881Sschwarze 		return(1);
226420fa2881Sschwarze 	}
226520fa2881Sschwarze 
226620fa2881Sschwarze 	/* Handles: `.Dt TITLE'
226749aff9f8Sschwarze 	 * title = TITLE, volume = local, msec = 0, arch = NULL
226820fa2881Sschwarze 	 */
226920fa2881Sschwarze 
227049aff9f8Sschwarze 	mdoc->meta.title = mandoc_strdup(
227149aff9f8Sschwarze 	    '\0' == nn->string[0] ? "UNKNOWN" : nn->string);
227220fa2881Sschwarze 
227320fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
227420fa2881Sschwarze 		/* FIXME: warn about missing msec. */
227520fa2881Sschwarze 		/* XXX: make this a macro value. */
227620fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
227720fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup("1");
227820fa2881Sschwarze 		return(1);
227920fa2881Sschwarze 	}
228020fa2881Sschwarze 
228120fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
228249aff9f8Sschwarze 	 * title = TITLE,
228349aff9f8Sschwarze 	 * volume = SEC is msec ? format(msec) : SEC,
228420fa2881Sschwarze 	 * msec = SEC is msec ? atoi(msec) : 0,
228520fa2881Sschwarze 	 * arch = NULL
228620fa2881Sschwarze 	 */
228720fa2881Sschwarze 
228888ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
228920fa2881Sschwarze 	if (cp) {
229020fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
229120fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
229220fa2881Sschwarze 	} else {
229351fcab2fSschwarze 		mandoc_msg(MANDOCERR_MSEC_BAD, mdoc->parse,
229451fcab2fSschwarze 		    nn->line, nn->pos, nn->string);
229520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
229620fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
229720fa2881Sschwarze 	}
229820fa2881Sschwarze 
229920fa2881Sschwarze 	if (NULL == (nn = nn->next))
230020fa2881Sschwarze 		return(1);
230120fa2881Sschwarze 
230220fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
230349aff9f8Sschwarze 	 * title = TITLE,
230449aff9f8Sschwarze 	 * volume = VOL is vol ? format(VOL) :
230520fa2881Sschwarze 	 *	    VOL is arch ? format(arch) :
230620fa2881Sschwarze 	 *	    VOL
230720fa2881Sschwarze 	 */
230820fa2881Sschwarze 
230920fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
231020fa2881Sschwarze 	if (cp) {
231120fa2881Sschwarze 		free(mdoc->meta.vol);
231220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
231320fa2881Sschwarze 	} else {
231420fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
231520fa2881Sschwarze 		if (NULL == cp) {
231651fcab2fSschwarze 			mandoc_msg(MANDOCERR_ARCH_BAD, mdoc->parse,
231751fcab2fSschwarze 			    nn->line, nn->pos, nn->string);
231820fa2881Sschwarze 			free(mdoc->meta.vol);
231920fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
232020fa2881Sschwarze 		} else
232120fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
232220fa2881Sschwarze 	}
232320fa2881Sschwarze 
232420fa2881Sschwarze 	/* Ignore any subsequent parameters... */
232520fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
232620fa2881Sschwarze 
232720fa2881Sschwarze 	return(1);
232820fa2881Sschwarze }
232920fa2881Sschwarze 
233020fa2881Sschwarze static int
233120fa2881Sschwarze post_prol(POST_ARGS)
233220fa2881Sschwarze {
233320fa2881Sschwarze 	/*
233420fa2881Sschwarze 	 * Remove prologue macros from the document after they're
233520fa2881Sschwarze 	 * processed.  The final document uses mdoc_meta for these
233620fa2881Sschwarze 	 * values and discards the originals.
233720fa2881Sschwarze 	 */
233820fa2881Sschwarze 
233920fa2881Sschwarze 	mdoc_node_delete(mdoc, mdoc->last);
234020fa2881Sschwarze 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
234120fa2881Sschwarze 		mdoc->flags |= MDOC_PBODY;
234220fa2881Sschwarze 
234320fa2881Sschwarze 	return(1);
234420fa2881Sschwarze }
234520fa2881Sschwarze 
234620fa2881Sschwarze static int
2347992063deSschwarze post_bx(POST_ARGS)
2348992063deSschwarze {
2349992063deSschwarze 	struct mdoc_node	*n;
2350992063deSschwarze 
2351992063deSschwarze 	/*
2352992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2353992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2354992063deSschwarze 	 * uppercase blindly.
2355992063deSschwarze 	 */
2356992063deSschwarze 
2357992063deSschwarze 	n = mdoc->last->child;
2358992063deSschwarze 	if (n && NULL != (n = n->next))
235949aff9f8Sschwarze 		*n->string = (char)toupper((unsigned char)*n->string);
2360992063deSschwarze 
2361992063deSschwarze 	return(1);
2362992063deSschwarze }
2363992063deSschwarze 
2364992063deSschwarze static int
236520fa2881Sschwarze post_os(POST_ARGS)
236620fa2881Sschwarze {
236720fa2881Sschwarze #ifndef OSNAME
236820fa2881Sschwarze 	struct utsname	  utsname;
23694c468128Sschwarze 	static char	 *defbuf;
237020fa2881Sschwarze #endif
23714c468128Sschwarze 	struct mdoc_node *n;
237220fa2881Sschwarze 
237320fa2881Sschwarze 	n = mdoc->last;
237420fa2881Sschwarze 
237520fa2881Sschwarze 	/*
2376353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2377353fa9ecSschwarze 	 * The order of precedence is:
2378353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2379353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2380353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2381353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
238220fa2881Sschwarze 	 */
238320fa2881Sschwarze 
238420fa2881Sschwarze 	free(mdoc->meta.os);
238583af2bccSschwarze 	mdoc->meta.os = NULL;
238683af2bccSschwarze 	mdoc_deroff(&mdoc->meta.os, n);
238783af2bccSschwarze 	if (mdoc->meta.os)
23884c468128Sschwarze 		return(1);
23894c468128Sschwarze 
2390353fa9ecSschwarze 	if (mdoc->defos) {
2391353fa9ecSschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2392353fa9ecSschwarze 		return(1);
2393353fa9ecSschwarze 	}
23944c468128Sschwarze 
239520fa2881Sschwarze #ifdef OSNAME
23964c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
239720fa2881Sschwarze #else /*!OSNAME */
23984c468128Sschwarze 	if (NULL == defbuf) {
2399a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
240020fa2881Sschwarze 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
24014c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2402a450f7c4Sschwarze 		} else
2403a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2404a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
240520fa2881Sschwarze 	}
24064c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
240720fa2881Sschwarze #endif /*!OSNAME*/
240820fa2881Sschwarze 	return(1);
240920fa2881Sschwarze }
241020fa2881Sschwarze 
241120fa2881Sschwarze static int
241220fa2881Sschwarze post_std(POST_ARGS)
241320fa2881Sschwarze {
241420fa2881Sschwarze 	struct mdoc_node *nn, *n;
241520fa2881Sschwarze 
241620fa2881Sschwarze 	n = mdoc->last;
241720fa2881Sschwarze 
241820fa2881Sschwarze 	/*
241920fa2881Sschwarze 	 * Macros accepting `-std' as an argument have the name of the
242020fa2881Sschwarze 	 * current document (`Nm') filled in as the argument if it's not
242120fa2881Sschwarze 	 * provided.
242220fa2881Sschwarze 	 */
242320fa2881Sschwarze 
242420fa2881Sschwarze 	if (n->child)
242520fa2881Sschwarze 		return(1);
242620fa2881Sschwarze 
242720fa2881Sschwarze 	if (NULL == mdoc->meta.name)
242820fa2881Sschwarze 		return(1);
242920fa2881Sschwarze 
243020fa2881Sschwarze 	nn = n;
243120fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
243220fa2881Sschwarze 
243320fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
243420fa2881Sschwarze 		return(0);
243520fa2881Sschwarze 
243620fa2881Sschwarze 	mdoc->last = nn;
243720fa2881Sschwarze 	return(1);
243820fa2881Sschwarze }
243920fa2881Sschwarze 
244019a69263Sschwarze static enum mdoc_sec
244119a69263Sschwarze a2sec(const char *p)
244219a69263Sschwarze {
244319a69263Sschwarze 	int		 i;
244419a69263Sschwarze 
244519a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
244619a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
244719a69263Sschwarze 			return((enum mdoc_sec)i);
244819a69263Sschwarze 
244919a69263Sschwarze 	return(SEC_CUSTOM);
245019a69263Sschwarze }
245119a69263Sschwarze 
245219a69263Sschwarze static size_t
245319a69263Sschwarze macro2len(enum mdoct macro)
245419a69263Sschwarze {
245519a69263Sschwarze 
245619a69263Sschwarze 	switch (macro) {
245749aff9f8Sschwarze 	case MDOC_Ad:
245819a69263Sschwarze 		return(12);
245949aff9f8Sschwarze 	case MDOC_Ao:
246019a69263Sschwarze 		return(12);
246149aff9f8Sschwarze 	case MDOC_An:
246219a69263Sschwarze 		return(12);
246349aff9f8Sschwarze 	case MDOC_Aq:
246419a69263Sschwarze 		return(12);
246549aff9f8Sschwarze 	case MDOC_Ar:
246619a69263Sschwarze 		return(12);
246749aff9f8Sschwarze 	case MDOC_Bo:
246819a69263Sschwarze 		return(12);
246949aff9f8Sschwarze 	case MDOC_Bq:
247019a69263Sschwarze 		return(12);
247149aff9f8Sschwarze 	case MDOC_Cd:
247219a69263Sschwarze 		return(12);
247349aff9f8Sschwarze 	case MDOC_Cm:
247419a69263Sschwarze 		return(10);
247549aff9f8Sschwarze 	case MDOC_Do:
247619a69263Sschwarze 		return(10);
247749aff9f8Sschwarze 	case MDOC_Dq:
247819a69263Sschwarze 		return(12);
247949aff9f8Sschwarze 	case MDOC_Dv:
248019a69263Sschwarze 		return(12);
248149aff9f8Sschwarze 	case MDOC_Eo:
248219a69263Sschwarze 		return(12);
248349aff9f8Sschwarze 	case MDOC_Em:
248419a69263Sschwarze 		return(10);
248549aff9f8Sschwarze 	case MDOC_Er:
248619a69263Sschwarze 		return(17);
248749aff9f8Sschwarze 	case MDOC_Ev:
248819a69263Sschwarze 		return(15);
248949aff9f8Sschwarze 	case MDOC_Fa:
249019a69263Sschwarze 		return(12);
249149aff9f8Sschwarze 	case MDOC_Fl:
249219a69263Sschwarze 		return(10);
249349aff9f8Sschwarze 	case MDOC_Fo:
249419a69263Sschwarze 		return(16);
249549aff9f8Sschwarze 	case MDOC_Fn:
249619a69263Sschwarze 		return(16);
249749aff9f8Sschwarze 	case MDOC_Ic:
249819a69263Sschwarze 		return(10);
249949aff9f8Sschwarze 	case MDOC_Li:
250019a69263Sschwarze 		return(16);
250149aff9f8Sschwarze 	case MDOC_Ms:
250219a69263Sschwarze 		return(6);
250349aff9f8Sschwarze 	case MDOC_Nm:
250419a69263Sschwarze 		return(10);
250549aff9f8Sschwarze 	case MDOC_No:
250619a69263Sschwarze 		return(12);
250749aff9f8Sschwarze 	case MDOC_Oo:
250819a69263Sschwarze 		return(10);
250949aff9f8Sschwarze 	case MDOC_Op:
251019a69263Sschwarze 		return(14);
251149aff9f8Sschwarze 	case MDOC_Pa:
251219a69263Sschwarze 		return(32);
251349aff9f8Sschwarze 	case MDOC_Pf:
251419a69263Sschwarze 		return(12);
251549aff9f8Sschwarze 	case MDOC_Po:
251619a69263Sschwarze 		return(12);
251749aff9f8Sschwarze 	case MDOC_Pq:
251819a69263Sschwarze 		return(12);
251949aff9f8Sschwarze 	case MDOC_Ql:
252019a69263Sschwarze 		return(16);
252149aff9f8Sschwarze 	case MDOC_Qo:
252219a69263Sschwarze 		return(12);
252349aff9f8Sschwarze 	case MDOC_So:
252419a69263Sschwarze 		return(12);
252549aff9f8Sschwarze 	case MDOC_Sq:
252619a69263Sschwarze 		return(12);
252749aff9f8Sschwarze 	case MDOC_Sy:
252819a69263Sschwarze 		return(6);
252949aff9f8Sschwarze 	case MDOC_Sx:
253019a69263Sschwarze 		return(16);
253149aff9f8Sschwarze 	case MDOC_Tn:
253219a69263Sschwarze 		return(10);
253349aff9f8Sschwarze 	case MDOC_Va:
253419a69263Sschwarze 		return(12);
253549aff9f8Sschwarze 	case MDOC_Vt:
253619a69263Sschwarze 		return(12);
253749aff9f8Sschwarze 	case MDOC_Xr:
253819a69263Sschwarze 		return(10);
253919a69263Sschwarze 	default:
254019a69263Sschwarze 		break;
254119a69263Sschwarze 	};
254219a69263Sschwarze 	return(0);
254319a69263Sschwarze }
2544