xref: /openbsd/usr.bin/mandoc/mdoc_validate.c (revision 0ff14c71)
1*0ff14c71Sschwarze /*	$OpenBSD: mdoc_validate.c,v 1.169 2014/10/13 14:01:03 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  */
19dadc3a61Sschwarze 
20dadc3a61Sschwarze #include <sys/types.h>
2120fa2881Sschwarze #ifndef OSNAME
2220fa2881Sschwarze #include <sys/utsname.h>
2320fa2881Sschwarze #endif
2420fa2881Sschwarze 
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 {
59d52d1586Sschwarze 	v_pre	 pre;
60753701eeSschwarze 	v_post	 post;
61f73abda9Skristaps };
62f73abda9Skristaps 
637c2be9f8Sschwarze static	int	 check_count(struct mdoc *, enum mdoc_type,
647c2be9f8Sschwarze 			enum check_lvl, enum check_ineq, int);
6520fa2881Sschwarze static	void	 check_text(struct mdoc *, int, int, char *);
6620fa2881Sschwarze static	void	 check_argv(struct mdoc *,
6731e23753Sschwarze 			struct mdoc_node *, struct mdoc_argv *);
6820fa2881Sschwarze static	void	 check_args(struct mdoc *, struct mdoc_node *);
69cd6c268fSschwarze static	int	 child_an(const 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);
87753701eeSschwarze static	int	 post_bk(POST_ARGS);
8867c719adSschwarze static	int	 post_bl(POST_ARGS);
8920fa2881Sschwarze static	int	 post_bl_block(POST_ARGS);
9020fa2881Sschwarze static	int	 post_bl_block_width(POST_ARGS);
9120fa2881Sschwarze static	int	 post_bl_block_tag(POST_ARGS);
9267c719adSschwarze static	int	 post_bl_head(POST_ARGS);
93992063deSschwarze static	int	 post_bx(POST_ARGS);
94753701eeSschwarze static	int	 post_d1(POST_ARGS);
954039b21cSschwarze static	int	 post_defaults(POST_ARGS);
9620fa2881Sschwarze static	int	 post_dd(POST_ARGS);
976093755cSschwarze static	int	 post_dt(POST_ARGS);
98551cd4a8Sschwarze static	int	 post_en(POST_ARGS);
99551cd4a8Sschwarze static	int	 post_es(POST_ARGS);
10020fa2881Sschwarze static	int	 post_eoln(POST_ARGS);
101e214f641Sschwarze static	int	 post_ex(POST_ARGS);
1027e92c062Sschwarze static	int	 post_fa(POST_ARGS);
1030c5064e3Sschwarze static	int	 post_fn(POST_ARGS);
1040c5064e3Sschwarze static	int	 post_fname(POST_ARGS);
105753701eeSschwarze static	int	 post_fo(POST_ARGS);
1064039b21cSschwarze static	int	 post_hyph(POST_ARGS);
107753701eeSschwarze static	int	 post_hyphtext(POST_ARGS);
1084039b21cSschwarze static	int	 post_ignpar(POST_ARGS);
10967c719adSschwarze static	int	 post_it(POST_ARGS);
11067c719adSschwarze static	int	 post_lb(POST_ARGS);
1114039b21cSschwarze static	int	 post_literal(POST_ARGS);
112753701eeSschwarze static	int	 post_nd(POST_ARGS);
11367c719adSschwarze static	int	 post_nm(POST_ARGS);
114af216717Sschwarze static	int	 post_ns(POST_ARGS);
11520fa2881Sschwarze static	int	 post_os(POST_ARGS);
116e0dd4c9cSschwarze static	int	 post_par(POST_ARGS);
11767c719adSschwarze static	int	 post_root(POST_ARGS);
118011fe33bSschwarze static	int	 post_rs(POST_ARGS);
11967c719adSschwarze static	int	 post_sh(POST_ARGS);
12067c719adSschwarze static	int	 post_sh_head(POST_ARGS);
121cd6c268fSschwarze static	int	 post_sh_name(POST_ARGS);
1227c384856Sschwarze static	int	 post_sh_see_also(POST_ARGS);
123cd6c268fSschwarze static	int	 post_sh_authors(POST_ARGS);
12467c719adSschwarze static	int	 post_st(POST_ARGS);
1258521b0bcSschwarze static	int	 post_vt(POST_ARGS);
126f73abda9Skristaps static	int	 pre_an(PRE_ARGS);
127f73abda9Skristaps static	int	 pre_bd(PRE_ARGS);
128f73abda9Skristaps static	int	 pre_bl(PRE_ARGS);
129f73abda9Skristaps static	int	 pre_dd(PRE_ARGS);
130f73abda9Skristaps static	int	 pre_display(PRE_ARGS);
131f73abda9Skristaps static	int	 pre_dt(PRE_ARGS);
13220fa2881Sschwarze static	int	 pre_literal(PRE_ARGS);
133551cd4a8Sschwarze static	int	 pre_obsolete(PRE_ARGS);
134f73abda9Skristaps static	int	 pre_os(PRE_ARGS);
13520fa2881Sschwarze static	int	 pre_par(PRE_ARGS);
13620fa2881Sschwarze static	int	 pre_std(PRE_ARGS);
137f73abda9Skristaps 
13819a69263Sschwarze static	const struct valids mdoc_valids[MDOC_MAX] = {
139099cfa7eSschwarze 	{ NULL, NULL },				/* Ap */
140753701eeSschwarze 	{ pre_dd, post_dd },			/* Dd */
141753701eeSschwarze 	{ pre_dt, post_dt },			/* Dt */
142753701eeSschwarze 	{ pre_os, post_os },			/* Os */
143753701eeSschwarze 	{ NULL, post_sh },			/* Sh */
144753701eeSschwarze 	{ NULL, post_ignpar },			/* Ss */
145753701eeSschwarze 	{ pre_par, post_par },			/* Pp */
146753701eeSschwarze 	{ pre_display, post_d1 },		/* D1 */
147753701eeSschwarze 	{ pre_literal, post_literal },		/* Dl */
148753701eeSschwarze 	{ pre_bd, post_literal },		/* Bd */
149f73abda9Skristaps 	{ NULL, NULL },				/* Ed */
150753701eeSschwarze 	{ pre_bl, post_bl },			/* Bl */
151f73abda9Skristaps 	{ NULL, NULL },				/* El */
152753701eeSschwarze 	{ pre_par, post_it },			/* It */
153e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ad */
154753701eeSschwarze 	{ pre_an, post_an },			/* An */
155753701eeSschwarze 	{ NULL, post_defaults },		/* Ar */
156e7a93ef3Sschwarze 	{ NULL, NULL },				/* Cd */
157f73abda9Skristaps 	{ NULL, NULL },				/* Cm */
158f73abda9Skristaps 	{ NULL, NULL },				/* Dv */
1594039b21cSschwarze 	{ NULL, NULL },				/* Er */
160f73abda9Skristaps 	{ NULL, NULL },				/* Ev */
161753701eeSschwarze 	{ pre_std, post_ex },			/* Ex */
1627e92c062Sschwarze 	{ NULL, post_fa },			/* Fa */
163753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* Fd */
164f73abda9Skristaps 	{ NULL, NULL },				/* Fl */
1650c5064e3Sschwarze 	{ NULL, post_fn },			/* Fn */
166e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ft */
167e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ic */
168753701eeSschwarze 	{ NULL, ewarn_eq1 },			/* In */
169753701eeSschwarze 	{ NULL, post_defaults },		/* Li */
170753701eeSschwarze 	{ NULL, post_nd },			/* Nd */
171753701eeSschwarze 	{ NULL, post_nm },			/* Nm */
172bca76d61Sschwarze 	{ NULL, NULL },				/* Op */
173d52d1586Sschwarze 	{ pre_obsolete, NULL },			/* Ot */
174753701eeSschwarze 	{ NULL, post_defaults },		/* Pa */
175d52d1586Sschwarze 	{ pre_std, NULL },			/* Rv */
176753701eeSschwarze 	{ NULL, post_st },			/* St */
177f73abda9Skristaps 	{ NULL, NULL },				/* Va */
178753701eeSschwarze 	{ NULL, post_vt },			/* Vt */
179753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* Xr */
180753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %A */
181753701eeSschwarze 	{ NULL, post_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
182753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %D */
183753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %I */
184753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %J */
185753701eeSschwarze 	{ NULL, post_hyphtext },		/* %N */
186753701eeSschwarze 	{ NULL, post_hyphtext },		/* %O */
187753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %P */
188753701eeSschwarze 	{ NULL, post_hyphtext },		/* %R */
189753701eeSschwarze 	{ NULL, post_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
190753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %V */
191f73abda9Skristaps 	{ NULL, NULL },				/* Ac */
192f73abda9Skristaps 	{ NULL, NULL },				/* Ao */
193bca76d61Sschwarze 	{ NULL, NULL },				/* Aq */
194753701eeSschwarze 	{ NULL, post_at },			/* At */
195f73abda9Skristaps 	{ NULL, NULL },				/* Bc */
196753701eeSschwarze 	{ NULL, post_bf },			/* Bf */
197f73abda9Skristaps 	{ NULL, NULL },				/* Bo */
198bca76d61Sschwarze 	{ NULL, NULL },				/* Bq */
199f73abda9Skristaps 	{ NULL, NULL },				/* Bsx */
200753701eeSschwarze 	{ NULL, post_bx },			/* Bx */
201753701eeSschwarze 	{ NULL, ebool },			/* Db */
202f73abda9Skristaps 	{ NULL, NULL },				/* Dc */
203f73abda9Skristaps 	{ NULL, NULL },				/* Do */
204bca76d61Sschwarze 	{ NULL, NULL },				/* Dq */
205f73abda9Skristaps 	{ NULL, NULL },				/* Ec */
206f73abda9Skristaps 	{ NULL, NULL },				/* Ef */
207f73abda9Skristaps 	{ NULL, NULL },				/* Em */
208f73abda9Skristaps 	{ NULL, NULL },				/* Eo */
209f73abda9Skristaps 	{ NULL, NULL },				/* Fx */
210e7a93ef3Sschwarze 	{ NULL, NULL },				/* Ms */
211753701eeSschwarze 	{ NULL, ewarn_eq0 },			/* No */
212753701eeSschwarze 	{ NULL, post_ns },			/* Ns */
213f73abda9Skristaps 	{ NULL, NULL },				/* Nx */
214f73abda9Skristaps 	{ NULL, NULL },				/* Ox */
215f73abda9Skristaps 	{ NULL, NULL },				/* Pc */
216753701eeSschwarze 	{ NULL, ewarn_eq1 },			/* Pf */
217f73abda9Skristaps 	{ NULL, NULL },				/* Po */
218bca76d61Sschwarze 	{ NULL, NULL },				/* Pq */
219f73abda9Skristaps 	{ NULL, NULL },				/* Qc */
220bca76d61Sschwarze 	{ NULL, NULL },				/* Ql */
221f73abda9Skristaps 	{ NULL, NULL },				/* Qo */
222bca76d61Sschwarze 	{ NULL, NULL },				/* Qq */
223f73abda9Skristaps 	{ NULL, NULL },				/* Re */
224753701eeSschwarze 	{ NULL, post_rs },			/* Rs */
225f73abda9Skristaps 	{ NULL, NULL },				/* Sc */
226f73abda9Skristaps 	{ NULL, NULL },				/* So */
227bca76d61Sschwarze 	{ NULL, NULL },				/* Sq */
228753701eeSschwarze 	{ NULL, ebool },			/* Sm */
229753701eeSschwarze 	{ NULL, post_hyph },			/* Sx */
230e7a93ef3Sschwarze 	{ NULL, NULL },				/* Sy */
231e7a93ef3Sschwarze 	{ NULL, NULL },				/* Tn */
232f73abda9Skristaps 	{ NULL, NULL },				/* Ux */
233f73abda9Skristaps 	{ NULL, NULL },				/* Xc */
234f73abda9Skristaps 	{ NULL, NULL },				/* Xo */
235753701eeSschwarze 	{ NULL, post_fo },			/* Fo */
236f73abda9Skristaps 	{ NULL, NULL },				/* Fc */
237f73abda9Skristaps 	{ NULL, NULL },				/* Oo */
238f73abda9Skristaps 	{ NULL, NULL },				/* Oc */
239753701eeSschwarze 	{ NULL, post_bk },			/* Bk */
240f73abda9Skristaps 	{ NULL, NULL },				/* Ek */
241753701eeSschwarze 	{ NULL, post_eoln },			/* Bt */
242f73abda9Skristaps 	{ NULL, NULL },				/* Hf */
243d52d1586Sschwarze 	{ pre_obsolete, NULL },			/* Fr */
244753701eeSschwarze 	{ NULL, post_eoln },			/* Ud */
245753701eeSschwarze 	{ NULL, post_lb },			/* Lb */
246753701eeSschwarze 	{ pre_par, post_par },			/* Lp */
247e7a93ef3Sschwarze 	{ NULL, NULL },				/* Lk */
248753701eeSschwarze 	{ NULL, post_defaults },		/* Mt */
249bca76d61Sschwarze 	{ NULL, NULL },				/* Brq */
250f73abda9Skristaps 	{ NULL, NULL },				/* Bro */
251f73abda9Skristaps 	{ NULL, NULL },				/* Brc */
252753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %C */
253753701eeSschwarze 	{ pre_obsolete, post_es },		/* Es */
254753701eeSschwarze 	{ pre_obsolete, post_en },		/* En */
255f73abda9Skristaps 	{ NULL, NULL },				/* Dx */
256753701eeSschwarze 	{ NULL, ewarn_ge1 },			/* %Q */
257753701eeSschwarze 	{ NULL, post_par },			/* br */
258753701eeSschwarze 	{ NULL, post_par },			/* sp */
259753701eeSschwarze 	{ NULL, ewarn_eq1 },			/* %U */
2606093755cSschwarze 	{ NULL, NULL },				/* Ta */
2615281506aSschwarze 	{ NULL, NULL },				/* ll */
262f73abda9Skristaps };
263f73abda9Skristaps 
26420fa2881Sschwarze #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
26520fa2881Sschwarze 
26620fa2881Sschwarze static	const enum mdoct rsord[RSORD_MAX] = {
26720fa2881Sschwarze 	MDOC__A,
26820fa2881Sschwarze 	MDOC__T,
26920fa2881Sschwarze 	MDOC__B,
27020fa2881Sschwarze 	MDOC__I,
27120fa2881Sschwarze 	MDOC__J,
27220fa2881Sschwarze 	MDOC__R,
27320fa2881Sschwarze 	MDOC__N,
27420fa2881Sschwarze 	MDOC__V,
2750397c682Sschwarze 	MDOC__U,
27620fa2881Sschwarze 	MDOC__P,
27720fa2881Sschwarze 	MDOC__Q,
2784e32ec8fSschwarze 	MDOC__C,
27920fa2881Sschwarze 	MDOC__D,
2804e32ec8fSschwarze 	MDOC__O
28120fa2881Sschwarze };
28220fa2881Sschwarze 
28319a69263Sschwarze static	const char * const secnames[SEC__MAX] = {
28419a69263Sschwarze 	NULL,
28519a69263Sschwarze 	"NAME",
28619a69263Sschwarze 	"LIBRARY",
28719a69263Sschwarze 	"SYNOPSIS",
28819a69263Sschwarze 	"DESCRIPTION",
28903ab2f23Sdlg 	"CONTEXT",
29019a69263Sschwarze 	"IMPLEMENTATION NOTES",
29119a69263Sschwarze 	"RETURN VALUES",
29219a69263Sschwarze 	"ENVIRONMENT",
29319a69263Sschwarze 	"FILES",
29419a69263Sschwarze 	"EXIT STATUS",
29519a69263Sschwarze 	"EXAMPLES",
29619a69263Sschwarze 	"DIAGNOSTICS",
29719a69263Sschwarze 	"COMPATIBILITY",
29819a69263Sschwarze 	"ERRORS",
29919a69263Sschwarze 	"SEE ALSO",
30019a69263Sschwarze 	"STANDARDS",
30119a69263Sschwarze 	"HISTORY",
30219a69263Sschwarze 	"AUTHORS",
30319a69263Sschwarze 	"CAVEATS",
30419a69263Sschwarze 	"BUGS",
30519a69263Sschwarze 	"SECURITY CONSIDERATIONS",
30619a69263Sschwarze 	NULL
30719a69263Sschwarze };
308f73abda9Skristaps 
30949aff9f8Sschwarze 
310f73abda9Skristaps int
3116093755cSschwarze mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
312f73abda9Skristaps {
313d52d1586Sschwarze 	v_pre	 p;
314f73abda9Skristaps 
3152791bd1cSschwarze 	switch (n->type) {
31649aff9f8Sschwarze 	case MDOC_TEXT:
317d52d1586Sschwarze 		check_text(mdoc, n->line, n->pos, n->string);
3182791bd1cSschwarze 		/* FALLTHROUGH */
31949aff9f8Sschwarze 	case MDOC_TBL:
3202791bd1cSschwarze 		/* FALLTHROUGH */
32149aff9f8Sschwarze 	case MDOC_EQN:
3228d973ab1Sschwarze 		/* FALLTHROUGH */
32349aff9f8Sschwarze 	case MDOC_ROOT:
32420fa2881Sschwarze 		return(1);
3252791bd1cSschwarze 	default:
3262791bd1cSschwarze 		break;
327f73abda9Skristaps 	}
328f73abda9Skristaps 
32920fa2881Sschwarze 	check_args(mdoc, n);
330d52d1586Sschwarze 	p = mdoc_valids[n->tok].pre;
331d52d1586Sschwarze 	return(*p ? (*p)(mdoc, n) : 1);
332f73abda9Skristaps }
333f73abda9Skristaps 
334f73abda9Skristaps int
335f73abda9Skristaps mdoc_valid_post(struct mdoc *mdoc)
336f73abda9Skristaps {
337753701eeSschwarze 	struct mdoc_node *n;
338753701eeSschwarze 	v_post p;
339f73abda9Skristaps 
340753701eeSschwarze 	n = mdoc->last;
341753701eeSschwarze 	if (n->flags & MDOC_VALID)
342f73abda9Skristaps 		return(1);
343753701eeSschwarze 	n->flags |= MDOC_VALID;
344f73abda9Skristaps 
345753701eeSschwarze 	switch (n->type) {
34649aff9f8Sschwarze 	case MDOC_TEXT:
3472791bd1cSschwarze 		/* FALLTHROUGH */
34849aff9f8Sschwarze 	case MDOC_EQN:
3498d973ab1Sschwarze 		/* FALLTHROUGH */
35049aff9f8Sschwarze 	case MDOC_TBL:
351f73abda9Skristaps 		return(1);
35249aff9f8Sschwarze 	case MDOC_ROOT:
353f73abda9Skristaps 		return(post_root(mdoc));
3542791bd1cSschwarze 	default:
355753701eeSschwarze 		p = mdoc_valids[n->tok].post;
356753701eeSschwarze 		return(*p ? (*p)(mdoc) : 1);
3572791bd1cSschwarze 	}
358f73abda9Skristaps }
359f73abda9Skristaps 
3607c2be9f8Sschwarze static int
3617ead8a4eSschwarze check_count(struct mdoc *mdoc, enum mdoc_type type,
3627c2be9f8Sschwarze 		enum check_lvl lvl, enum check_ineq ineq, int val)
363f73abda9Skristaps {
3647c2be9f8Sschwarze 	const char	*p;
365bb648afaSschwarze 	enum mandocerr	 t;
3667c2be9f8Sschwarze 
3677ead8a4eSschwarze 	if (mdoc->last->type != type)
3687c2be9f8Sschwarze 		return(1);
3697c2be9f8Sschwarze 
3707c2be9f8Sschwarze 	switch (ineq) {
37149aff9f8Sschwarze 	case CHECK_LT:
3727c2be9f8Sschwarze 		p = "less than ";
3737ead8a4eSschwarze 		if (mdoc->last->nchild < val)
3747c2be9f8Sschwarze 			return(1);
3757c2be9f8Sschwarze 		break;
37649aff9f8Sschwarze 	case CHECK_GT:
377bb648afaSschwarze 		p = "more than ";
3787ead8a4eSschwarze 		if (mdoc->last->nchild > val)
3797c2be9f8Sschwarze 			return(1);
3807c2be9f8Sschwarze 		break;
38149aff9f8Sschwarze 	case CHECK_EQ:
3827c2be9f8Sschwarze 		p = "";
3837ead8a4eSschwarze 		if (val == mdoc->last->nchild)
3847c2be9f8Sschwarze 			return(1);
3857c2be9f8Sschwarze 		break;
38620fa2881Sschwarze 	default:
38720fa2881Sschwarze 		abort();
38820fa2881Sschwarze 		/* NOTREACHED */
3897c2be9f8Sschwarze 	}
3907c2be9f8Sschwarze 
391bb648afaSschwarze 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
39249aff9f8Sschwarze 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
39349aff9f8Sschwarze 	    mdoc->last->pos, "want %s%d children (have %d)",
3947ead8a4eSschwarze 	    p, val, mdoc->last->nchild);
39519a69263Sschwarze 	return(1);
3967c2be9f8Sschwarze }
397f73abda9Skristaps 
3987c2be9f8Sschwarze static int
3997c2be9f8Sschwarze berr_ge1(POST_ARGS)
400f73abda9Skristaps {
401f73abda9Skristaps 
402bb648afaSschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
403f73abda9Skristaps }
404f73abda9Skristaps 
4057c2be9f8Sschwarze static int
4067c2be9f8Sschwarze bwarn_ge1(POST_ARGS)
4077c2be9f8Sschwarze {
4087c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
409f73abda9Skristaps }
410f73abda9Skristaps 
4117c2be9f8Sschwarze static int
4127c2be9f8Sschwarze ewarn_eq0(POST_ARGS)
4137c2be9f8Sschwarze {
4147c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
4157c2be9f8Sschwarze }
4167c2be9f8Sschwarze 
4177c2be9f8Sschwarze static int
418bb648afaSschwarze ewarn_eq1(POST_ARGS)
419bb648afaSschwarze {
420bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
421bb648afaSschwarze }
422bb648afaSschwarze 
423bb648afaSschwarze static int
4247c2be9f8Sschwarze ewarn_ge1(POST_ARGS)
4257c2be9f8Sschwarze {
4267c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
4277c2be9f8Sschwarze }
4287c2be9f8Sschwarze 
4297c2be9f8Sschwarze static int
430bb648afaSschwarze ewarn_le1(POST_ARGS)
4317c2be9f8Sschwarze {
432bb648afaSschwarze 	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
4337c2be9f8Sschwarze }
4347c2be9f8Sschwarze 
4357c2be9f8Sschwarze static int
4367c2be9f8Sschwarze hwarn_eq0(POST_ARGS)
4377c2be9f8Sschwarze {
4387c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
4397c2be9f8Sschwarze }
4407c2be9f8Sschwarze 
4417c2be9f8Sschwarze static int
4427c2be9f8Sschwarze hwarn_eq1(POST_ARGS)
4437c2be9f8Sschwarze {
4447c2be9f8Sschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
4457c2be9f8Sschwarze }
4467c2be9f8Sschwarze 
4477c2be9f8Sschwarze static int
448bb648afaSschwarze hwarn_ge1(POST_ARGS)
449bb648afaSschwarze {
450bb648afaSschwarze 	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
451bb648afaSschwarze }
452bb648afaSschwarze 
45320fa2881Sschwarze static void
4547ead8a4eSschwarze check_args(struct mdoc *mdoc, struct mdoc_node *n)
455f73abda9Skristaps {
456f73abda9Skristaps 	int		 i;
457f73abda9Skristaps 
458f73abda9Skristaps 	if (NULL == n->args)
45920fa2881Sschwarze 		return;
460f73abda9Skristaps 
461f73abda9Skristaps 	assert(n->args->argc);
462f73abda9Skristaps 	for (i = 0; i < (int)n->args->argc; i++)
4637ead8a4eSschwarze 		check_argv(mdoc, n, &n->args->argv[i]);
464f73abda9Skristaps }
465f73abda9Skristaps 
46620fa2881Sschwarze static void
4677ead8a4eSschwarze check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
468f73abda9Skristaps {
469f73abda9Skristaps 	int		 i;
470f73abda9Skristaps 
471f73abda9Skristaps 	for (i = 0; i < (int)v->sz; i++)
4727ead8a4eSschwarze 		check_text(mdoc, v->line, v->pos, v->value[i]);
473f73abda9Skristaps }
474f73abda9Skristaps 
47520fa2881Sschwarze static void
4767ead8a4eSschwarze check_text(struct mdoc *mdoc, int ln, int pos, char *p)
477f73abda9Skristaps {
47804e980cbSschwarze 	char		*cp;
479769ee804Sschwarze 
4807ead8a4eSschwarze 	if (MDOC_LITERAL & mdoc->flags)
4811cdbf331Sschwarze 		return;
4821cdbf331Sschwarze 
4831cdbf331Sschwarze 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
484dd5b31c3Sschwarze 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
485dd5b31c3Sschwarze 		    ln, pos + (int)(p - cp), NULL);
486f73abda9Skristaps }
487f73abda9Skristaps 
488f73abda9Skristaps static int
489f73abda9Skristaps pre_display(PRE_ARGS)
490f73abda9Skristaps {
491f73abda9Skristaps 	struct mdoc_node *node;
492f73abda9Skristaps 
493f73abda9Skristaps 	if (MDOC_BLOCK != n->type)
494f73abda9Skristaps 		return(1);
495f73abda9Skristaps 
496f73abda9Skristaps 	for (node = mdoc->last->parent; node; node = node->parent)
497f73abda9Skristaps 		if (MDOC_BLOCK == node->type)
498f73abda9Skristaps 			if (MDOC_Bd == node->tok)
499f73abda9Skristaps 				break;
50020fa2881Sschwarze 
50105c39368Sschwarze 	if (node)
502b723eac2Sschwarze 		mandoc_vmsg(MANDOCERR_BD_NEST,
503b723eac2Sschwarze 		    mdoc->parse, n->line, n->pos,
504b723eac2Sschwarze 		    "%s in Bd", mdoc_macronames[n->tok]);
50505c39368Sschwarze 
50605c39368Sschwarze 	return(1);
507f73abda9Skristaps }
508f73abda9Skristaps 
509f73abda9Skristaps static int
510f73abda9Skristaps pre_bl(PRE_ARGS)
511f73abda9Skristaps {
512769ee804Sschwarze 	struct mdoc_node *np;
513aa99c14fSschwarze 	struct mdoc_argv *argv, *wa;
5144a9f685fSschwarze 	int		  i;
515aa99c14fSschwarze 	enum mdocargt	  mdoclt;
5164a9f685fSschwarze 	enum mdoc_list	  lt;
517f73abda9Skristaps 
5186093755cSschwarze 	if (MDOC_BLOCK != n->type) {
519769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
520769ee804Sschwarze 			assert(n->pending);
521769ee804Sschwarze 			np = n->pending->parent;
522769ee804Sschwarze 		} else
523769ee804Sschwarze 			np = n->parent;
524769ee804Sschwarze 
525769ee804Sschwarze 		assert(np);
526769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
527769ee804Sschwarze 		assert(MDOC_Bl == np->tok);
528f73abda9Skristaps 		return(1);
5296e03d529Sschwarze 	}
530f73abda9Skristaps 
5316093755cSschwarze 	/*
5326093755cSschwarze 	 * First figure out which kind of list to use: bind ourselves to
5336093755cSschwarze 	 * the first mentioned list type and warn about any remaining
5346093755cSschwarze 	 * ones.  If we find no list type, we default to LIST_item.
5356093755cSschwarze 	 */
536f73abda9Skristaps 
537dadc3a61Sschwarze 	wa = (n->args == NULL) ? NULL : n->args->argv;
538aa99c14fSschwarze 	mdoclt = MDOC_ARG_MAX;
5396093755cSschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
5404a9f685fSschwarze 		argv = n->args->argv + i;
5416093755cSschwarze 		lt = LIST__NONE;
5424a9f685fSschwarze 		switch (argv->arg) {
5436093755cSschwarze 		/* Set list types. */
54449aff9f8Sschwarze 		case MDOC_Bullet:
5456093755cSschwarze 			lt = LIST_bullet;
5466093755cSschwarze 			break;
54749aff9f8Sschwarze 		case MDOC_Dash:
5486093755cSschwarze 			lt = LIST_dash;
5496093755cSschwarze 			break;
55049aff9f8Sschwarze 		case MDOC_Enum:
5516093755cSschwarze 			lt = LIST_enum;
5526093755cSschwarze 			break;
55349aff9f8Sschwarze 		case MDOC_Hyphen:
5546093755cSschwarze 			lt = LIST_hyphen;
5556093755cSschwarze 			break;
55649aff9f8Sschwarze 		case MDOC_Item:
5576093755cSschwarze 			lt = LIST_item;
5586093755cSschwarze 			break;
55949aff9f8Sschwarze 		case MDOC_Tag:
5606093755cSschwarze 			lt = LIST_tag;
5616093755cSschwarze 			break;
56249aff9f8Sschwarze 		case MDOC_Diag:
5636093755cSschwarze 			lt = LIST_diag;
5646093755cSschwarze 			break;
56549aff9f8Sschwarze 		case MDOC_Hang:
5666093755cSschwarze 			lt = LIST_hang;
5676093755cSschwarze 			break;
56849aff9f8Sschwarze 		case MDOC_Ohang:
5696093755cSschwarze 			lt = LIST_ohang;
5706093755cSschwarze 			break;
57149aff9f8Sschwarze 		case MDOC_Inset:
5726093755cSschwarze 			lt = LIST_inset;
5736093755cSschwarze 			break;
57449aff9f8Sschwarze 		case MDOC_Column:
5756093755cSschwarze 			lt = LIST_column;
57664d728e4Sschwarze 			break;
5776093755cSschwarze 		/* Set list arguments. */
57849aff9f8Sschwarze 		case MDOC_Compact:
5794a9f685fSschwarze 			if (n->norm->Bl.comp)
5804a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
5814a9f685fSschwarze 				    mdoc->parse, argv->line,
5824a9f685fSschwarze 				    argv->pos, "Bl -compact");
5834a9f685fSschwarze 			n->norm->Bl.comp = 1;
58450e63e03Sschwarze 			break;
58549aff9f8Sschwarze 		case MDOC_Width:
586aa99c14fSschwarze 			wa = argv;
5874a9f685fSschwarze 			if (0 == argv->sz) {
5884a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
5894a9f685fSschwarze 				    mdoc->parse, argv->line,
5904a9f685fSschwarze 				    argv->pos, "Bl -width");
5914a9f685fSschwarze 				n->norm->Bl.width = "0n";
59222972b14Sschwarze 				break;
59322972b14Sschwarze 			}
5944a9f685fSschwarze 			if (NULL != n->norm->Bl.width)
5954a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
5964a9f685fSschwarze 				    mdoc->parse, argv->line,
5974a9f685fSschwarze 				    argv->pos, "Bl -width %s",
5984a9f685fSschwarze 				    argv->value[0]);
5994a9f685fSschwarze 			n->norm->Bl.width = argv->value[0];
60064d728e4Sschwarze 			break;
60149aff9f8Sschwarze 		case MDOC_Offset:
6024a9f685fSschwarze 			if (0 == argv->sz) {
6034a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
6044a9f685fSschwarze 				    mdoc->parse, argv->line,
6054a9f685fSschwarze 				    argv->pos, "Bl -offset");
60631e23753Sschwarze 				break;
60731e23753Sschwarze 			}
6084a9f685fSschwarze 			if (NULL != n->norm->Bl.offs)
6094a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
6104a9f685fSschwarze 				    mdoc->parse, argv->line,
6114a9f685fSschwarze 				    argv->pos, "Bl -offset %s",
6124a9f685fSschwarze 				    argv->value[0]);
6134a9f685fSschwarze 			n->norm->Bl.offs = argv->value[0];
614f73abda9Skristaps 			break;
615ddce0b0cSschwarze 		default:
616ddce0b0cSschwarze 			continue;
617f73abda9Skristaps 		}
618dc0d8bb2Sschwarze 		if (LIST__NONE == lt)
619dc0d8bb2Sschwarze 			continue;
620aa99c14fSschwarze 		mdoclt = argv->arg;
621f73abda9Skristaps 
6226093755cSschwarze 		/* Check: multiple list types. */
6236093755cSschwarze 
624dc0d8bb2Sschwarze 		if (LIST__NONE != n->norm->Bl.type) {
625bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_REP,
626dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
627bd594191Sschwarze 			    "Bl -%s", mdoc_argnames[argv->arg]);
628dc0d8bb2Sschwarze 			continue;
629769ee804Sschwarze 		}
6306093755cSschwarze 
6316093755cSschwarze 		/* The list type should come first. */
6326093755cSschwarze 
6338c62fbf5Sschwarze 		if (n->norm->Bl.width ||
6348c62fbf5Sschwarze 		    n->norm->Bl.offs ||
6358c62fbf5Sschwarze 		    n->norm->Bl.comp)
636bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
637bd594191Sschwarze 			    mdoc->parse, n->line, n->pos, "Bl -%s",
63866788495Sschwarze 			    mdoc_argnames[n->args->argv[0].arg]);
639dc0d8bb2Sschwarze 
640dc0d8bb2Sschwarze 		n->norm->Bl.type = lt;
641dc0d8bb2Sschwarze 		if (LIST_column == lt) {
642dc0d8bb2Sschwarze 			n->norm->Bl.ncols = argv->sz;
643dc0d8bb2Sschwarze 			n->norm->Bl.cols = (void *)argv->value;
644dc0d8bb2Sschwarze 		}
6456093755cSschwarze 	}
6466093755cSschwarze 
6476093755cSschwarze 	/* Allow lists to default to LIST_item. */
6486093755cSschwarze 
6498c62fbf5Sschwarze 	if (LIST__NONE == n->norm->Bl.type) {
650bd594191Sschwarze 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
651bd594191Sschwarze 		    n->line, n->pos, "Bl");
6528c62fbf5Sschwarze 		n->norm->Bl.type = LIST_item;
6536e03d529Sschwarze 	}
654f73abda9Skristaps 
65564d728e4Sschwarze 	/*
65664d728e4Sschwarze 	 * Validate the width field.  Some list types don't need width
65764d728e4Sschwarze 	 * types and should be warned about them.  Others should have it
6585eced068Sschwarze 	 * and must also be warned.  Yet others have a default and need
6595eced068Sschwarze 	 * no warning.
66064d728e4Sschwarze 	 */
66164d728e4Sschwarze 
6628c62fbf5Sschwarze 	switch (n->norm->Bl.type) {
66349aff9f8Sschwarze 	case LIST_tag:
6645eced068Sschwarze 		if (NULL == n->norm->Bl.width)
665bd594191Sschwarze 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
666bd594191Sschwarze 			    n->line, n->pos, "Bl -tag");
667f73abda9Skristaps 		break;
66849aff9f8Sschwarze 	case LIST_column:
6696093755cSschwarze 		/* FALLTHROUGH */
67049aff9f8Sschwarze 	case LIST_diag:
6716093755cSschwarze 		/* FALLTHROUGH */
67249aff9f8Sschwarze 	case LIST_ohang:
6736093755cSschwarze 		/* FALLTHROUGH */
67449aff9f8Sschwarze 	case LIST_inset:
6756093755cSschwarze 		/* FALLTHROUGH */
67649aff9f8Sschwarze 	case LIST_item:
6778c62fbf5Sschwarze 		if (n->norm->Bl.width)
678aa99c14fSschwarze 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
679aa99c14fSschwarze 			    wa->line, wa->pos, "Bl -%s",
680aa99c14fSschwarze 			    mdoc_argnames[mdoclt]);
6816093755cSschwarze 		break;
68249aff9f8Sschwarze 	case LIST_bullet:
6835eced068Sschwarze 		/* FALLTHROUGH */
68449aff9f8Sschwarze 	case LIST_dash:
6855eced068Sschwarze 		/* FALLTHROUGH */
68649aff9f8Sschwarze 	case LIST_hyphen:
6875eced068Sschwarze 		if (NULL == n->norm->Bl.width)
6885eced068Sschwarze 			n->norm->Bl.width = "2n";
6895eced068Sschwarze 		break;
69049aff9f8Sschwarze 	case LIST_enum:
6915eced068Sschwarze 		if (NULL == n->norm->Bl.width)
6925eced068Sschwarze 			n->norm->Bl.width = "3n";
6935eced068Sschwarze 		break;
69464d728e4Sschwarze 	default:
695f73abda9Skristaps 		break;
69664d728e4Sschwarze 	}
69764d728e4Sschwarze 
698d52d1586Sschwarze 	return(pre_par(mdoc, n));
699f73abda9Skristaps }
700f73abda9Skristaps 
701f73abda9Skristaps static int
702f73abda9Skristaps pre_bd(PRE_ARGS)
703f73abda9Skristaps {
704769ee804Sschwarze 	struct mdoc_node *np;
7054a9f685fSschwarze 	struct mdoc_argv *argv;
7064a9f685fSschwarze 	int		  i;
7074a9f685fSschwarze 	enum mdoc_disp	  dt;
708f73abda9Skristaps 
709d52d1586Sschwarze 	pre_literal(mdoc, n);
710d52d1586Sschwarze 
71131e23753Sschwarze 	if (MDOC_BLOCK != n->type) {
712769ee804Sschwarze 		if (ENDBODY_NOT != n->end) {
713769ee804Sschwarze 			assert(n->pending);
714769ee804Sschwarze 			np = n->pending->parent;
715769ee804Sschwarze 		} else
716769ee804Sschwarze 			np = n->parent;
717769ee804Sschwarze 
718769ee804Sschwarze 		assert(np);
719769ee804Sschwarze 		assert(MDOC_BLOCK == np->type);
720769ee804Sschwarze 		assert(MDOC_Bd == np->tok);
721f73abda9Skristaps 		return(1);
7226e03d529Sschwarze 	}
723f73abda9Skristaps 
72431e23753Sschwarze 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
7254a9f685fSschwarze 		argv = n->args->argv + i;
72631e23753Sschwarze 		dt = DISP__NONE;
72731e23753Sschwarze 
7284a9f685fSschwarze 		switch (argv->arg) {
72949aff9f8Sschwarze 		case MDOC_Centred:
7302065e47aSschwarze 			dt = DISP_centered;
73131e23753Sschwarze 			break;
73249aff9f8Sschwarze 		case MDOC_Ragged:
73331e23753Sschwarze 			dt = DISP_ragged;
73431e23753Sschwarze 			break;
73549aff9f8Sschwarze 		case MDOC_Unfilled:
73631e23753Sschwarze 			dt = DISP_unfilled;
73731e23753Sschwarze 			break;
73849aff9f8Sschwarze 		case MDOC_Filled:
73931e23753Sschwarze 			dt = DISP_filled;
74031e23753Sschwarze 			break;
74149aff9f8Sschwarze 		case MDOC_Literal:
74231e23753Sschwarze 			dt = DISP_literal;
743f73abda9Skristaps 			break;
74449aff9f8Sschwarze 		case MDOC_File:
745bd594191Sschwarze 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
746bd594191Sschwarze 			    n->line, n->pos, NULL);
7476e03d529Sschwarze 			return(0);
74849aff9f8Sschwarze 		case MDOC_Offset:
7494a9f685fSschwarze 			if (0 == argv->sz) {
7504a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7514a9f685fSschwarze 				    mdoc->parse, argv->line,
7524a9f685fSschwarze 				    argv->pos, "Bd -offset");
753f73abda9Skristaps 				break;
754f73abda9Skristaps 			}
7554a9f685fSschwarze 			if (NULL != n->norm->Bd.offs)
7564a9f685fSschwarze 				mandoc_vmsg(MANDOCERR_ARG_REP,
7574a9f685fSschwarze 				    mdoc->parse, argv->line,
7584a9f685fSschwarze 				    argv->pos, "Bd -offset %s",
7594a9f685fSschwarze 				    argv->value[0]);
7604a9f685fSschwarze 			n->norm->Bd.offs = argv->value[0];
76131e23753Sschwarze 			break;
76249aff9f8Sschwarze 		case MDOC_Compact:
7634a9f685fSschwarze 			if (n->norm->Bd.comp)
7644a9f685fSschwarze 				mandoc_msg(MANDOCERR_ARG_REP,
7654a9f685fSschwarze 				    mdoc->parse, argv->line,
7664a9f685fSschwarze 				    argv->pos, "Bd -compact");
7674a9f685fSschwarze 			n->norm->Bd.comp = 1;
76831e23753Sschwarze 			break;
76931e23753Sschwarze 		default:
77031e23753Sschwarze 			abort();
77131e23753Sschwarze 			/* NOTREACHED */
77231e23753Sschwarze 		}
773dc0d8bb2Sschwarze 		if (DISP__NONE == dt)
774dc0d8bb2Sschwarze 			continue;
77531e23753Sschwarze 
776dc0d8bb2Sschwarze 		if (DISP__NONE == n->norm->Bd.type)
7778c62fbf5Sschwarze 			n->norm->Bd.type = dt;
778dc0d8bb2Sschwarze 		else
779bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_BD_REP,
780dc0d8bb2Sschwarze 			    mdoc->parse, n->line, n->pos,
781bd594191Sschwarze 			    "Bd -%s", mdoc_argnames[argv->arg]);
78231e23753Sschwarze 	}
78331e23753Sschwarze 
7848c62fbf5Sschwarze 	if (DISP__NONE == n->norm->Bd.type) {
785bd594191Sschwarze 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
786bd594191Sschwarze 		    n->line, n->pos, "Bd");
7878c62fbf5Sschwarze 		n->norm->Bd.type = DISP_ragged;
78831e23753Sschwarze 	}
78931e23753Sschwarze 
790d52d1586Sschwarze 	return(pre_par(mdoc, n));
791f73abda9Skristaps }
792f73abda9Skristaps 
793f73abda9Skristaps static int
794f73abda9Skristaps pre_an(PRE_ARGS)
795f73abda9Skristaps {
796aa99c14fSschwarze 	struct mdoc_argv *argv;
797aa99c14fSschwarze 	size_t	 i;
798f73abda9Skristaps 
799aa99c14fSschwarze 	if (n->args == NULL)
800f73abda9Skristaps 		return(1);
801769ee804Sschwarze 
802aa99c14fSschwarze 	for (i = 1; i < n->args->argc; i++) {
803aa99c14fSschwarze 		argv = n->args->argv + i;
804aa99c14fSschwarze 		mandoc_vmsg(MANDOCERR_AN_REP,
805aa99c14fSschwarze 		    mdoc->parse, argv->line, argv->pos,
806aa99c14fSschwarze 		    "An -%s", mdoc_argnames[argv->arg]);
807aa99c14fSschwarze 	}
8087c2be9f8Sschwarze 
809aa99c14fSschwarze 	argv = n->args->argv;
810aa99c14fSschwarze 	if (argv->arg == MDOC_Split)
8118c62fbf5Sschwarze 		n->norm->An.auth = AUTH_split;
812aa99c14fSschwarze 	else if (argv->arg == MDOC_Nosplit)
8138c62fbf5Sschwarze 		n->norm->An.auth = AUTH_nosplit;
814769ee804Sschwarze 	else
815769ee804Sschwarze 		abort();
816769ee804Sschwarze 
817769ee804Sschwarze 	return(1);
818f73abda9Skristaps }
819f73abda9Skristaps 
820f73abda9Skristaps static int
82120fa2881Sschwarze pre_std(PRE_ARGS)
822f73abda9Skristaps {
823f73abda9Skristaps 
82420fa2881Sschwarze 	if (n->args && 1 == n->args->argc)
82520fa2881Sschwarze 		if (MDOC_Std == n->args->argv[0].arg)
82620fa2881Sschwarze 			return(1);
827f73abda9Skristaps 
82866788495Sschwarze 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
82966788495Sschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
8306093755cSschwarze 	return(1);
8316093755cSschwarze }
8326093755cSschwarze 
8336093755cSschwarze static int
834551cd4a8Sschwarze pre_obsolete(PRE_ARGS)
835551cd4a8Sschwarze {
836551cd4a8Sschwarze 
837551cd4a8Sschwarze 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
838551cd4a8Sschwarze 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
839551cd4a8Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
840551cd4a8Sschwarze 	return(1);
841551cd4a8Sschwarze }
842551cd4a8Sschwarze 
843551cd4a8Sschwarze static int
844f73abda9Skristaps pre_dt(PRE_ARGS)
845f73abda9Skristaps {
846f73abda9Skristaps 
8473fdead0cSschwarze 	if (mdoc->meta.title != NULL)
84851fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
84951fcab2fSschwarze 		    n->line, n->pos, "Dt");
8503fdead0cSschwarze 	else if (mdoc->meta.os != NULL)
8513fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
8523fdead0cSschwarze 		    n->line, n->pos, "Dt after Os");
853f73abda9Skristaps 	return(1);
854f73abda9Skristaps }
855f73abda9Skristaps 
856f73abda9Skristaps static int
857f73abda9Skristaps pre_os(PRE_ARGS)
858f73abda9Skristaps {
859f73abda9Skristaps 
8603fdead0cSschwarze 	if (mdoc->meta.os != NULL)
86151fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
86251fcab2fSschwarze 		    n->line, n->pos, "Os");
8633fdead0cSschwarze 	else if (mdoc->flags & MDOC_PBODY)
8643fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
8653fdead0cSschwarze 		    n->line, n->pos, "Os");
866f73abda9Skristaps 	return(1);
867f73abda9Skristaps }
868f73abda9Skristaps 
869f73abda9Skristaps static int
870f73abda9Skristaps pre_dd(PRE_ARGS)
871f73abda9Skristaps {
872f73abda9Skristaps 
8733fdead0cSschwarze 	if (mdoc->meta.date != NULL)
87451fcab2fSschwarze 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
87551fcab2fSschwarze 		    n->line, n->pos, "Dd");
8763fdead0cSschwarze 	else if (mdoc->flags & MDOC_PBODY)
8773fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
8783fdead0cSschwarze 		    n->line, n->pos, "Dd");
8793fdead0cSschwarze 	else if (mdoc->meta.title != NULL)
8803fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
8813fdead0cSschwarze 		    n->line, n->pos, "Dd after Dt");
8823fdead0cSschwarze 	else if (mdoc->meta.os != NULL)
8833fdead0cSschwarze 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
8843fdead0cSschwarze 		    n->line, n->pos, "Dd after Os");
885f73abda9Skristaps 	return(1);
886f73abda9Skristaps }
887f73abda9Skristaps 
888f73abda9Skristaps static int
889f73abda9Skristaps post_bf(POST_ARGS)
890f73abda9Skristaps {
891ecb10c32Sschwarze 	struct mdoc_node *np, *nch;
892ddce0b0cSschwarze 	enum mdocargt	  arg;
893f73abda9Skristaps 
894769ee804Sschwarze 	/*
895769ee804Sschwarze 	 * Unlike other data pointers, these are "housed" by the HEAD
896769ee804Sschwarze 	 * element, which contains the goods.
897769ee804Sschwarze 	 */
898769ee804Sschwarze 
899769ee804Sschwarze 	if (MDOC_HEAD != mdoc->last->type) {
900769ee804Sschwarze 		if (ENDBODY_NOT != mdoc->last->end) {
901769ee804Sschwarze 			assert(mdoc->last->pending);
902769ee804Sschwarze 			np = mdoc->last->pending->parent->head;
903769ee804Sschwarze 		} else if (MDOC_BLOCK != mdoc->last->type) {
904769ee804Sschwarze 			np = mdoc->last->parent->head;
905769ee804Sschwarze 		} else
906769ee804Sschwarze 			np = mdoc->last->head;
907769ee804Sschwarze 
908769ee804Sschwarze 		assert(np);
909769ee804Sschwarze 		assert(MDOC_HEAD == np->type);
910769ee804Sschwarze 		assert(MDOC_Bf == np->tok);
911f73abda9Skristaps 		return(1);
9126e03d529Sschwarze 	}
913f73abda9Skristaps 
914769ee804Sschwarze 	np = mdoc->last;
915769ee804Sschwarze 	assert(MDOC_BLOCK == np->parent->type);
916769ee804Sschwarze 	assert(MDOC_Bf == np->parent->tok);
91750d41253Sschwarze 
918ecb10c32Sschwarze 	/* Check the number of arguments. */
919f73abda9Skristaps 
920ecb10c32Sschwarze 	nch = np->child;
921ecb10c32Sschwarze 	if (NULL == np->parent->args) {
922ecb10c32Sschwarze 		if (NULL == nch) {
923bd594191Sschwarze 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
924bd594191Sschwarze 			    np->line, np->pos, "Bf");
92520fa2881Sschwarze 			return(1);
92620fa2881Sschwarze 		}
927ecb10c32Sschwarze 		nch = nch->next;
928ecb10c32Sschwarze 	}
929ecb10c32Sschwarze 	if (NULL != nch)
930ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
931ecb10c32Sschwarze 		    nch->line, nch->pos, "Bf ... %s", nch->string);
932769ee804Sschwarze 
933769ee804Sschwarze 	/* Extract argument into data. */
934769ee804Sschwarze 
935769ee804Sschwarze 	if (np->parent->args) {
936769ee804Sschwarze 		arg = np->parent->args->argv[0].arg;
937769ee804Sschwarze 		if (MDOC_Emphasis == arg)
9388c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Em;
939769ee804Sschwarze 		else if (MDOC_Literal == arg)
9408c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Li;
941769ee804Sschwarze 		else if (MDOC_Symbolic == arg)
9428c62fbf5Sschwarze 			np->norm->Bf.font = FONT_Sy;
943769ee804Sschwarze 		else
944769ee804Sschwarze 			abort();
945769ee804Sschwarze 		return(1);
946769ee804Sschwarze 	}
947769ee804Sschwarze 
948769ee804Sschwarze 	/* Extract parameter into data. */
949769ee804Sschwarze 
950769ee804Sschwarze 	if (0 == strcmp(np->child->string, "Em"))
9518c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Em;
952769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Li"))
9538c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Li;
954769ee804Sschwarze 	else if (0 == strcmp(np->child->string, "Sy"))
9558c62fbf5Sschwarze 		np->norm->Bf.font = FONT_Sy;
95620fa2881Sschwarze 	else
957ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
958ecb10c32Sschwarze 		    np->child->line, np->child->pos,
959ecb10c32Sschwarze 		    "Bf %s", np->child->string);
960769ee804Sschwarze 
961769ee804Sschwarze 	return(1);
962f73abda9Skristaps }
963f73abda9Skristaps 
964f73abda9Skristaps static int
96571719887Sschwarze post_lb(POST_ARGS)
96671719887Sschwarze {
96777e000ffSschwarze 	struct mdoc_node	*n;
96877e000ffSschwarze 	const char		*stdlibname;
96977e000ffSschwarze 	char			*libname;
97071719887Sschwarze 
971bb648afaSschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
972bb648afaSschwarze 
97377e000ffSschwarze 	n = mdoc->last->child;
97420fa2881Sschwarze 
97577e000ffSschwarze 	assert(n);
97677e000ffSschwarze 	assert(MDOC_TEXT == n->type);
97720fa2881Sschwarze 
97877e000ffSschwarze 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
97977e000ffSschwarze 		mandoc_asprintf(&libname,
98077e000ffSschwarze 		    "library \\(lq%s\\(rq", n->string);
98177e000ffSschwarze 	else
98277e000ffSschwarze 		libname = mandoc_strdup(stdlibname);
98320fa2881Sschwarze 
98477e000ffSschwarze 	free(n->string);
98577e000ffSschwarze 	n->string = libname;
98620fa2881Sschwarze 	return(1);
98720fa2881Sschwarze }
98871719887Sschwarze 
98971719887Sschwarze static int
990b31af00dSschwarze post_eoln(POST_ARGS)
991b31af00dSschwarze {
992ecb10c32Sschwarze 	const struct mdoc_node *n;
993b31af00dSschwarze 
994ecb10c32Sschwarze 	n = mdoc->last;
995ecb10c32Sschwarze 	if (n->child)
996ecb10c32Sschwarze 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
997ecb10c32Sschwarze 		    mdoc->parse, n->line, n->pos,
998ecb10c32Sschwarze 		    "%s %s", mdoc_macronames[n->tok],
999ecb10c32Sschwarze 		    n->child->string);
1000b31af00dSschwarze 	return(1);
1001b31af00dSschwarze }
1002b31af00dSschwarze 
1003b31af00dSschwarze static int
10040c5064e3Sschwarze post_fname(POST_ARGS)
10050c5064e3Sschwarze {
10060c5064e3Sschwarze 	const struct mdoc_node	*n;
1007*0ff14c71Sschwarze 	const char		*cp;
10080c5064e3Sschwarze 	size_t			 pos;
10090c5064e3Sschwarze 
10100c5064e3Sschwarze 	n = mdoc->last->child;
10110c5064e3Sschwarze 	pos = strcspn(n->string, "()");
1012*0ff14c71Sschwarze 	cp = n->string + pos;
1013*0ff14c71Sschwarze 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
10140c5064e3Sschwarze 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
10150c5064e3Sschwarze 		    n->line, n->pos + pos, n->string);
10160c5064e3Sschwarze 	return(1);
10170c5064e3Sschwarze }
10180c5064e3Sschwarze 
10190c5064e3Sschwarze static int
10200c5064e3Sschwarze post_fn(POST_ARGS)
10210c5064e3Sschwarze {
10220c5064e3Sschwarze 
10230c5064e3Sschwarze 	post_fname(mdoc);
10240c5064e3Sschwarze 	post_fa(mdoc);
10250c5064e3Sschwarze 	return(1);
10260c5064e3Sschwarze }
10270c5064e3Sschwarze 
10280c5064e3Sschwarze static int
1029753701eeSschwarze post_fo(POST_ARGS)
1030753701eeSschwarze {
1031753701eeSschwarze 
1032753701eeSschwarze 	hwarn_eq1(mdoc);
1033753701eeSschwarze 	bwarn_ge1(mdoc);
1034b8e7c8e3Sschwarze 	if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild)
10350c5064e3Sschwarze 		post_fname(mdoc);
1036753701eeSschwarze 	return(1);
1037753701eeSschwarze }
1038753701eeSschwarze 
1039753701eeSschwarze static int
10407e92c062Sschwarze post_fa(POST_ARGS)
10417e92c062Sschwarze {
10427e92c062Sschwarze 	const struct mdoc_node *n;
10437e92c062Sschwarze 	const char *cp;
10447e92c062Sschwarze 
10457e92c062Sschwarze 	for (n = mdoc->last->child; n != NULL; n = n->next) {
10467e92c062Sschwarze 		for (cp = n->string; *cp != '\0'; cp++) {
10477e92c062Sschwarze 			/* Ignore callbacks and alterations. */
10487e92c062Sschwarze 			if (*cp == '(' || *cp == '{')
10497e92c062Sschwarze 				break;
10507e92c062Sschwarze 			if (*cp != ',')
10517e92c062Sschwarze 				continue;
10527e92c062Sschwarze 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
10537e92c062Sschwarze 			    n->line, n->pos + (cp - n->string),
10547e92c062Sschwarze 			    n->string);
10557e92c062Sschwarze 			break;
10567e92c062Sschwarze 		}
10577e92c062Sschwarze 	}
10587e92c062Sschwarze 	return(1);
10597e92c062Sschwarze }
10607e92c062Sschwarze 
10617e92c062Sschwarze static int
10628521b0bcSschwarze post_vt(POST_ARGS)
10638521b0bcSschwarze {
10648521b0bcSschwarze 	const struct mdoc_node *n;
10658521b0bcSschwarze 
10668521b0bcSschwarze 	/*
10678521b0bcSschwarze 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
10688521b0bcSschwarze 	 * have different syntaxes (yet more context-sensitive
1069e7a93ef3Sschwarze 	 * behaviour).  ELEM types must have a child, which is already
1070e7a93ef3Sschwarze 	 * guaranteed by the in_line parsing routine; BLOCK types,
10718521b0bcSschwarze 	 * specifically the BODY, should only have TEXT children.
10728521b0bcSschwarze 	 */
10738521b0bcSschwarze 
10748521b0bcSschwarze 	if (MDOC_BODY != mdoc->last->type)
10758521b0bcSschwarze 		return(1);
10768521b0bcSschwarze 
10778521b0bcSschwarze 	for (n = mdoc->last->child; n; n = n->next)
1078bc49dbe1Sschwarze 		if (MDOC_TEXT != n->type)
1079dd25b57cSschwarze 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
1080dd25b57cSschwarze 			    n->line, n->pos, mdoc_macronames[n->tok]);
10818521b0bcSschwarze 
10828521b0bcSschwarze 	return(1);
10838521b0bcSschwarze }
10848521b0bcSschwarze 
10858521b0bcSschwarze static int
1086f73abda9Skristaps post_nm(POST_ARGS)
1087f73abda9Skristaps {
108820fa2881Sschwarze 
1089160ac481Sschwarze 	if (NULL != mdoc->meta.name)
109020fa2881Sschwarze 		return(1);
109120fa2881Sschwarze 
109283af2bccSschwarze 	mdoc_deroff(&mdoc->meta.name, mdoc->last);
109320fa2881Sschwarze 
1094e214f641Sschwarze 	if (NULL == mdoc->meta.name)
1095bd594191Sschwarze 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1096bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos, "Nm");
109720fa2881Sschwarze 	return(1);
109820fa2881Sschwarze }
109920fa2881Sschwarze 
110020fa2881Sschwarze static int
1101753701eeSschwarze post_nd(POST_ARGS)
1102753701eeSschwarze {
1103753701eeSschwarze 
1104753701eeSschwarze 	berr_ge1(mdoc);
1105753701eeSschwarze 	return(post_hyph(mdoc));
1106753701eeSschwarze }
1107753701eeSschwarze 
1108753701eeSschwarze static int
1109753701eeSschwarze post_d1(POST_ARGS)
1110753701eeSschwarze {
1111753701eeSschwarze 
1112753701eeSschwarze 	bwarn_ge1(mdoc);
1113753701eeSschwarze 	return(post_hyph(mdoc));
1114753701eeSschwarze }
1115753701eeSschwarze 
1116753701eeSschwarze static int
111720fa2881Sschwarze post_literal(POST_ARGS)
111820fa2881Sschwarze {
111920fa2881Sschwarze 
1120753701eeSschwarze 	if (mdoc->last->tok == MDOC_Bd)
1121753701eeSschwarze 		hwarn_eq0(mdoc);
1122753701eeSschwarze 	bwarn_ge1(mdoc);
1123753701eeSschwarze 
112420fa2881Sschwarze 	/*
112520fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
112620fa2881Sschwarze 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
112720fa2881Sschwarze 	 * this in literal mode, but it doesn't hurt to just switch it
112820fa2881Sschwarze 	 * off in general since displays can't be nested.
112920fa2881Sschwarze 	 */
113020fa2881Sschwarze 
113120fa2881Sschwarze 	if (MDOC_BODY == mdoc->last->type)
113220fa2881Sschwarze 		mdoc->flags &= ~MDOC_LITERAL;
113320fa2881Sschwarze 
113420fa2881Sschwarze 	return(1);
113520fa2881Sschwarze }
113620fa2881Sschwarze 
113720fa2881Sschwarze static int
113820fa2881Sschwarze post_defaults(POST_ARGS)
113920fa2881Sschwarze {
114020fa2881Sschwarze 	struct mdoc_node *nn;
114120fa2881Sschwarze 
114220fa2881Sschwarze 	/*
114320fa2881Sschwarze 	 * The `Ar' defaults to "file ..." if no value is provided as an
114420fa2881Sschwarze 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
114520fa2881Sschwarze 	 * gets an empty string.
114620fa2881Sschwarze 	 */
1147f73abda9Skristaps 
1148f73abda9Skristaps 	if (mdoc->last->child)
1149f73abda9Skristaps 		return(1);
115020fa2881Sschwarze 
115120fa2881Sschwarze 	nn = mdoc->last;
115220fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
115320fa2881Sschwarze 
115420fa2881Sschwarze 	switch (nn->tok) {
115549aff9f8Sschwarze 	case MDOC_Ar:
115620fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
115720fa2881Sschwarze 			return(0);
115820fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
115920fa2881Sschwarze 			return(0);
116020fa2881Sschwarze 		break;
116149aff9f8Sschwarze 	case MDOC_Li:
116220fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
116320fa2881Sschwarze 			return(0);
116420fa2881Sschwarze 		break;
116549aff9f8Sschwarze 	case MDOC_Pa:
116620fa2881Sschwarze 		/* FALLTHROUGH */
116749aff9f8Sschwarze 	case MDOC_Mt:
116820fa2881Sschwarze 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
116920fa2881Sschwarze 			return(0);
117020fa2881Sschwarze 		break;
117120fa2881Sschwarze 	default:
117220fa2881Sschwarze 		abort();
117320fa2881Sschwarze 		/* NOTREACHED */
1174f73abda9Skristaps 	}
1175f73abda9Skristaps 
117620fa2881Sschwarze 	mdoc->last = nn;
117720fa2881Sschwarze 	return(1);
117820fa2881Sschwarze }
1179f73abda9Skristaps 
1180f73abda9Skristaps static int
1181f73abda9Skristaps post_at(POST_ARGS)
1182f73abda9Skristaps {
11830b2f1307Sschwarze 	struct mdoc_node	*n;
11840b2f1307Sschwarze 	const char		*std_att;
11850b2f1307Sschwarze 	char			*att;
118620fa2881Sschwarze 
1187753701eeSschwarze 	n = mdoc->last;
1188753701eeSschwarze 	if (n->child == NULL) {
1189753701eeSschwarze 		mdoc->next = MDOC_NEXT_CHILD;
1190753701eeSschwarze 		if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"))
1191753701eeSschwarze 			return(0);
1192753701eeSschwarze 		mdoc->last = n;
1193753701eeSschwarze 		return(1);
1194753701eeSschwarze 	}
1195753701eeSschwarze 
119620fa2881Sschwarze 	/*
119720fa2881Sschwarze 	 * If we have a child, look it up in the standard keys.  If a
119820fa2881Sschwarze 	 * key exist, use that instead of the child; if it doesn't,
119920fa2881Sschwarze 	 * prefix "AT&T UNIX " to the existing data.
120020fa2881Sschwarze 	 */
1201f73abda9Skristaps 
1202753701eeSschwarze 	n = n->child;
12030b2f1307Sschwarze 	assert(MDOC_TEXT == n->type);
12040b2f1307Sschwarze 	if (NULL == (std_att = mdoc_a2att(n->string))) {
1205bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1206bd594191Sschwarze 		    n->line, n->pos, "At %s", n->string);
12070b2f1307Sschwarze 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
12080b2f1307Sschwarze 	} else
12090b2f1307Sschwarze 		att = mandoc_strdup(std_att);
1210f73abda9Skristaps 
12110b2f1307Sschwarze 	free(n->string);
12120b2f1307Sschwarze 	n->string = att;
121320fa2881Sschwarze 	return(1);
121420fa2881Sschwarze }
1215f73abda9Skristaps 
1216f73abda9Skristaps static int
1217f73abda9Skristaps post_an(POST_ARGS)
1218f73abda9Skristaps {
1219769ee804Sschwarze 	struct mdoc_node *np;
1220f73abda9Skristaps 
1221769ee804Sschwarze 	np = mdoc->last;
1222e7a93ef3Sschwarze 	if (AUTH__NONE == np->norm->An.auth) {
1223e7a93ef3Sschwarze 		if (0 == np->child)
1224e7a93ef3Sschwarze 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1225e7a93ef3Sschwarze 	} else if (np->child)
1226bb648afaSschwarze 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
122720fa2881Sschwarze 
122820fa2881Sschwarze 	return(1);
1229f73abda9Skristaps }
1230f73abda9Skristaps 
1231f73abda9Skristaps static int
1232551cd4a8Sschwarze post_en(POST_ARGS)
1233551cd4a8Sschwarze {
1234551cd4a8Sschwarze 
1235551cd4a8Sschwarze 	if (MDOC_BLOCK == mdoc->last->type)
1236551cd4a8Sschwarze 		mdoc->last->norm->Es = mdoc->last_es;
1237551cd4a8Sschwarze 	return(1);
1238551cd4a8Sschwarze }
1239551cd4a8Sschwarze 
1240551cd4a8Sschwarze static int
1241551cd4a8Sschwarze post_es(POST_ARGS)
1242551cd4a8Sschwarze {
1243551cd4a8Sschwarze 
1244551cd4a8Sschwarze 	mdoc->last_es = mdoc->last;
1245551cd4a8Sschwarze 	return(1);
1246551cd4a8Sschwarze }
1247551cd4a8Sschwarze 
1248551cd4a8Sschwarze static int
1249f73abda9Skristaps post_it(POST_ARGS)
1250f73abda9Skristaps {
125119a69263Sschwarze 	int		  i, cols;
12526093755cSschwarze 	enum mdoc_list	  lt;
12539530682eSschwarze 	struct mdoc_node *nbl, *nit, *nch;
1254f73abda9Skristaps 
12559530682eSschwarze 	nit = mdoc->last;
12569530682eSschwarze 	if (MDOC_BLOCK != nit->type)
1257f73abda9Skristaps 		return(1);
1258f73abda9Skristaps 
12599530682eSschwarze 	nbl = nit->parent->parent;
12609530682eSschwarze 	lt = nbl->norm->Bl.type;
12616093755cSschwarze 
12626093755cSschwarze 	switch (lt) {
126349aff9f8Sschwarze 	case LIST_tag:
12649530682eSschwarze 		/* FALLTHROUGH */
126549aff9f8Sschwarze 	case LIST_hang:
1266f73abda9Skristaps 		/* FALLTHROUGH */
126749aff9f8Sschwarze 	case LIST_ohang:
1268f73abda9Skristaps 		/* FALLTHROUGH */
126949aff9f8Sschwarze 	case LIST_inset:
1270f73abda9Skristaps 		/* FALLTHROUGH */
127149aff9f8Sschwarze 	case LIST_diag:
12729530682eSschwarze 		if (NULL == nit->head->child)
1273bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
12749530682eSschwarze 			    mdoc->parse, nit->line, nit->pos,
1275bd594191Sschwarze 			    "Bl -%s It",
12769530682eSschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1277f73abda9Skristaps 		break;
127849aff9f8Sschwarze 	case LIST_bullet:
1279f73abda9Skristaps 		/* FALLTHROUGH */
128049aff9f8Sschwarze 	case LIST_dash:
1281f73abda9Skristaps 		/* FALLTHROUGH */
128249aff9f8Sschwarze 	case LIST_enum:
1283f73abda9Skristaps 		/* FALLTHROUGH */
128449aff9f8Sschwarze 	case LIST_hyphen:
12859530682eSschwarze 		if (NULL == nit->body->child)
1286bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
128766788495Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1288bd594191Sschwarze 			    "Bl -%s It",
128966788495Sschwarze 			    mdoc_argnames[nbl->args->argv[0].arg]);
1290f73abda9Skristaps 		/* FALLTHROUGH */
129149aff9f8Sschwarze 	case LIST_item:
12929530682eSschwarze 		if (NULL != nit->head->child)
1293ecb10c32Sschwarze 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1294ecb10c32Sschwarze 			    mdoc->parse, nit->line, nit->pos,
1295ecb10c32Sschwarze 			    "It %s", nit->head->child->string);
1296f73abda9Skristaps 		break;
129749aff9f8Sschwarze 	case LIST_column:
12989530682eSschwarze 		cols = (int)nbl->norm->Bl.ncols;
12996093755cSschwarze 
13009530682eSschwarze 		assert(NULL == nit->head->child);
13016093755cSschwarze 
13029530682eSschwarze 		for (i = 0, nch = nit->child; nch; nch = nch->next)
13039530682eSschwarze 			if (MDOC_BODY == nch->type)
1304f73abda9Skristaps 				i++;
130553292e81Sschwarze 
1306e14c4c11Sschwarze 		if (i < cols || i > cols + 1)
1307e14c4c11Sschwarze 			mandoc_vmsg(MANDOCERR_ARGCOUNT,
1308e14c4c11Sschwarze 			    mdoc->parse, nit->line, nit->pos,
13096e03d529Sschwarze 			    "columns == %d (have %d)", cols, i);
1310e14c4c11Sschwarze 		break;
1311f73abda9Skristaps 	default:
131266788495Sschwarze 		abort();
1313f73abda9Skristaps 	}
1314f73abda9Skristaps 
1315f73abda9Skristaps 	return(1);
1316f73abda9Skristaps }
1317f73abda9Skristaps 
131820fa2881Sschwarze static int
131920fa2881Sschwarze post_bl_block(POST_ARGS)
132020fa2881Sschwarze {
1321bb99f0faSschwarze 	struct mdoc_node *n, *ni, *nc;
132220fa2881Sschwarze 
132320fa2881Sschwarze 	/*
132420fa2881Sschwarze 	 * These are fairly complicated, so we've broken them into two
132520fa2881Sschwarze 	 * functions.  post_bl_block_tag() is called when a -tag is
132620fa2881Sschwarze 	 * specified, but no -width (it must be guessed).  The second
132720fa2881Sschwarze 	 * when a -width is specified (macro indicators must be
132820fa2881Sschwarze 	 * rewritten into real lengths).
132920fa2881Sschwarze 	 */
133020fa2881Sschwarze 
133120fa2881Sschwarze 	n = mdoc->last;
133220fa2881Sschwarze 
13338c62fbf5Sschwarze 	if (LIST_tag == n->norm->Bl.type &&
13348c62fbf5Sschwarze 	    NULL == n->norm->Bl.width) {
133520fa2881Sschwarze 		if ( ! post_bl_block_tag(mdoc))
133620fa2881Sschwarze 			return(0);
1337bb99f0faSschwarze 		assert(n->norm->Bl.width);
13388c62fbf5Sschwarze 	} else if (NULL != n->norm->Bl.width) {
133920fa2881Sschwarze 		if ( ! post_bl_block_width(mdoc))
134020fa2881Sschwarze 			return(0);
13418c62fbf5Sschwarze 		assert(n->norm->Bl.width);
1342bb99f0faSschwarze 	}
1343bb99f0faSschwarze 
1344bb99f0faSschwarze 	for (ni = n->body->child; ni; ni = ni->next) {
1345bb99f0faSschwarze 		if (NULL == ni->body)
1346bb99f0faSschwarze 			continue;
1347bb99f0faSschwarze 		nc = ni->body->last;
1348bb99f0faSschwarze 		while (NULL != nc) {
1349bb99f0faSschwarze 			switch (nc->tok) {
135049aff9f8Sschwarze 			case MDOC_Pp:
1351bb99f0faSschwarze 				/* FALLTHROUGH */
135249aff9f8Sschwarze 			case MDOC_Lp:
1353bb99f0faSschwarze 				/* FALLTHROUGH */
135449aff9f8Sschwarze 			case MDOC_br:
1355bb99f0faSschwarze 				break;
1356bb99f0faSschwarze 			default:
1357bb99f0faSschwarze 				nc = NULL;
1358bb99f0faSschwarze 				continue;
1359bb99f0faSschwarze 			}
1360bb99f0faSschwarze 			if (NULL == ni->next) {
136120369664Sschwarze 				mandoc_msg(MANDOCERR_PAR_MOVE,
136220369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
136320369664Sschwarze 				    mdoc_macronames[nc->tok]);
1364bb99f0faSschwarze 				if ( ! mdoc_node_relink(mdoc, nc))
1365bb99f0faSschwarze 					return(0);
1366bb99f0faSschwarze 			} else if (0 == n->norm->Bl.comp &&
1367bb99f0faSschwarze 			    LIST_column != n->norm->Bl.type) {
136820369664Sschwarze 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
136920369664Sschwarze 				    mdoc->parse, nc->line, nc->pos,
137020369664Sschwarze 				    "%s before It",
137120369664Sschwarze 				    mdoc_macronames[nc->tok]);
1372bb99f0faSschwarze 				mdoc_node_delete(mdoc, nc);
1373bb99f0faSschwarze 			} else
1374bb99f0faSschwarze 				break;
1375bb99f0faSschwarze 			nc = ni->body->last;
1376bb99f0faSschwarze 		}
1377bb99f0faSschwarze 	}
137820fa2881Sschwarze 	return(1);
137920fa2881Sschwarze }
138020fa2881Sschwarze 
138120fa2881Sschwarze static int
138220fa2881Sschwarze post_bl_block_width(POST_ARGS)
138320fa2881Sschwarze {
138420fa2881Sschwarze 	size_t		  width;
138520fa2881Sschwarze 	int		  i;
138620fa2881Sschwarze 	enum mdoct	  tok;
138720fa2881Sschwarze 	struct mdoc_node *n;
138847813146Sschwarze 	char		  buf[24];
138920fa2881Sschwarze 
139020fa2881Sschwarze 	n = mdoc->last;
139120fa2881Sschwarze 
139220fa2881Sschwarze 	/*
139320fa2881Sschwarze 	 * Calculate the real width of a list from the -width string,
139420fa2881Sschwarze 	 * which may contain a macro (with a known default width), a
139520fa2881Sschwarze 	 * literal string, or a scaling width.
139620fa2881Sschwarze 	 *
139720fa2881Sschwarze 	 * If the value to -width is a macro, then we re-write it to be
139820fa2881Sschwarze 	 * the macro's width as set in share/tmac/mdoc/doc-common.
139920fa2881Sschwarze 	 */
140020fa2881Sschwarze 
14018c62fbf5Sschwarze 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
140220fa2881Sschwarze 		width = 6;
14038c62fbf5Sschwarze 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
140420fa2881Sschwarze 		return(1);
1405dc0d8bb2Sschwarze 	else
1406dc0d8bb2Sschwarze 		width = macro2len(tok);
140720fa2881Sschwarze 
140820fa2881Sschwarze 	/* The value already exists: free and reallocate it. */
140920fa2881Sschwarze 
141020fa2881Sschwarze 	assert(n->args);
141120fa2881Sschwarze 
141220fa2881Sschwarze 	for (i = 0; i < (int)n->args->argc; i++)
141320fa2881Sschwarze 		if (MDOC_Width == n->args->argv[i].arg)
141420fa2881Sschwarze 			break;
141520fa2881Sschwarze 
141620fa2881Sschwarze 	assert(i < (int)n->args->argc);
141720fa2881Sschwarze 
141847813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
141920fa2881Sschwarze 	free(n->args->argv[i].value[0]);
142020fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
142120fa2881Sschwarze 
142220fa2881Sschwarze 	/* Set our width! */
14238c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
142420fa2881Sschwarze 	return(1);
142520fa2881Sschwarze }
142620fa2881Sschwarze 
142720fa2881Sschwarze static int
142820fa2881Sschwarze post_bl_block_tag(POST_ARGS)
142920fa2881Sschwarze {
143020fa2881Sschwarze 	struct mdoc_node *n, *nn;
143120fa2881Sschwarze 	size_t		  sz, ssz;
143220fa2881Sschwarze 	int		  i;
143347813146Sschwarze 	char		  buf[24];
143420fa2881Sschwarze 
143520fa2881Sschwarze 	/*
143620fa2881Sschwarze 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
143720fa2881Sschwarze 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
143820fa2881Sschwarze 	 * ONLY if the -width argument has NOT been provided.  See
143920fa2881Sschwarze 	 * post_bl_block_width() for converting the -width string.
144020fa2881Sschwarze 	 */
144120fa2881Sschwarze 
144220fa2881Sschwarze 	sz = 10;
144320fa2881Sschwarze 	n = mdoc->last;
144420fa2881Sschwarze 
144520fa2881Sschwarze 	for (nn = n->body->child; nn; nn = nn->next) {
144620fa2881Sschwarze 		if (MDOC_It != nn->tok)
144720fa2881Sschwarze 			continue;
144820fa2881Sschwarze 
144920fa2881Sschwarze 		assert(MDOC_BLOCK == nn->type);
145020fa2881Sschwarze 		nn = nn->head->child;
145120fa2881Sschwarze 
145220fa2881Sschwarze 		if (nn == NULL)
145320fa2881Sschwarze 			break;
145420fa2881Sschwarze 
145520fa2881Sschwarze 		if (MDOC_TEXT == nn->type) {
145620fa2881Sschwarze 			sz = strlen(nn->string) + 1;
145720fa2881Sschwarze 			break;
145820fa2881Sschwarze 		}
145920fa2881Sschwarze 
146019a69263Sschwarze 		if (0 != (ssz = macro2len(nn->tok)))
146120fa2881Sschwarze 			sz = ssz;
146220fa2881Sschwarze 
146320fa2881Sschwarze 		break;
146420fa2881Sschwarze 	}
146520fa2881Sschwarze 
146620fa2881Sschwarze 	/* Defaults to ten ens. */
146720fa2881Sschwarze 
146847813146Sschwarze 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
146920fa2881Sschwarze 
147020fa2881Sschwarze 	/*
147120fa2881Sschwarze 	 * We have to dynamically add this to the macro's argument list.
147220fa2881Sschwarze 	 * We're guaranteed that a MDOC_Width doesn't already exist.
147320fa2881Sschwarze 	 */
147420fa2881Sschwarze 
147520fa2881Sschwarze 	assert(n->args);
147620fa2881Sschwarze 	i = (int)(n->args->argc)++;
147720fa2881Sschwarze 
14788286bf36Sschwarze 	n->args->argv = mandoc_reallocarray(n->args->argv,
14798286bf36Sschwarze 	    n->args->argc, sizeof(struct mdoc_argv));
148020fa2881Sschwarze 
148120fa2881Sschwarze 	n->args->argv[i].arg = MDOC_Width;
148220fa2881Sschwarze 	n->args->argv[i].line = n->line;
148320fa2881Sschwarze 	n->args->argv[i].pos = n->pos;
148420fa2881Sschwarze 	n->args->argv[i].sz = 1;
148520fa2881Sschwarze 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
148620fa2881Sschwarze 	n->args->argv[i].value[0] = mandoc_strdup(buf);
148720fa2881Sschwarze 
148820fa2881Sschwarze 	/* Set our width! */
14898c62fbf5Sschwarze 	n->norm->Bl.width = n->args->argv[i].value[0];
149020fa2881Sschwarze 	return(1);
149120fa2881Sschwarze }
149220fa2881Sschwarze 
1493f73abda9Skristaps static int
1494395185ccSschwarze post_bl_head(POST_ARGS)
1495395185ccSschwarze {
149620fa2881Sschwarze 	struct mdoc_node *np, *nn, *nnp;
1497f5174743Sschwarze 	struct mdoc_argv *argv;
149820fa2881Sschwarze 	int		  i, j;
1499395185ccSschwarze 
15008c62fbf5Sschwarze 	if (LIST_column != mdoc->last->norm->Bl.type)
150120fa2881Sschwarze 		/* FIXME: this should be ERROR class... */
150220fa2881Sschwarze 		return(hwarn_eq0(mdoc));
1503395185ccSschwarze 
150420fa2881Sschwarze 	/*
1505f5174743Sschwarze 	 * Append old-style lists, where the column width specifiers
150620fa2881Sschwarze 	 * trail as macro parameters, to the new-style ("normal-form")
150720fa2881Sschwarze 	 * lists where they're argument values following -column.
150820fa2881Sschwarze 	 */
150920fa2881Sschwarze 
1510f5174743Sschwarze 	if (mdoc->last->child == NULL)
151120fa2881Sschwarze 		return(1);
151220fa2881Sschwarze 
151320fa2881Sschwarze 	np = mdoc->last->parent;
151420fa2881Sschwarze 	assert(np->args);
151520fa2881Sschwarze 
151620fa2881Sschwarze 	for (j = 0; j < (int)np->args->argc; j++)
151720fa2881Sschwarze 		if (MDOC_Column == np->args->argv[j].arg)
151820fa2881Sschwarze 			break;
151920fa2881Sschwarze 
152020fa2881Sschwarze 	assert(j < (int)np->args->argc);
152120fa2881Sschwarze 
152220fa2881Sschwarze 	/*
1523a5e11edeSschwarze 	 * Accommodate for new-style groff column syntax.  Shuffle the
152420fa2881Sschwarze 	 * child nodes, all of which must be TEXT, as arguments for the
152520fa2881Sschwarze 	 * column field.  Then, delete the head children.
152620fa2881Sschwarze 	 */
152720fa2881Sschwarze 
1528f5174743Sschwarze 	argv = np->args->argv + j;
1529f5174743Sschwarze 	i = argv->sz;
1530f5174743Sschwarze 	argv->sz += mdoc->last->nchild;
1531f5174743Sschwarze 	argv->value = mandoc_reallocarray(argv->value,
1532f5174743Sschwarze 	    argv->sz, sizeof(char *));
153320fa2881Sschwarze 
1534f5174743Sschwarze 	mdoc->last->norm->Bl.ncols = argv->sz;
1535f5174743Sschwarze 	mdoc->last->norm->Bl.cols = (void *)argv->value;
153620fa2881Sschwarze 
1537f5174743Sschwarze 	for (nn = mdoc->last->child; nn; i++) {
1538f5174743Sschwarze 		argv->value[i] = nn->string;
153920fa2881Sschwarze 		nn->string = NULL;
154020fa2881Sschwarze 		nnp = nn;
154120fa2881Sschwarze 		nn = nn->next;
154220fa2881Sschwarze 		mdoc_node_delete(NULL, nnp);
1543395185ccSschwarze 	}
154420fa2881Sschwarze 
154520fa2881Sschwarze 	mdoc->last->nchild = 0;
154620fa2881Sschwarze 	mdoc->last->child = NULL;
154720fa2881Sschwarze 
15486093755cSschwarze 	return(1);
1549b16e7ddfSschwarze }
1550b16e7ddfSschwarze 
1551395185ccSschwarze static int
1552f73abda9Skristaps post_bl(POST_ARGS)
1553f73abda9Skristaps {
15542a427d60Sschwarze 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
15552a427d60Sschwarze 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
15562a427d60Sschwarze 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
1557f73abda9Skristaps 
15582a427d60Sschwarze 	nbody = mdoc->last;
15592a427d60Sschwarze 	switch (nbody->type) {
156049aff9f8Sschwarze 	case MDOC_BLOCK:
156120fa2881Sschwarze 		return(post_bl_block(mdoc));
156249aff9f8Sschwarze 	case MDOC_HEAD:
15632a427d60Sschwarze 		return(post_bl_head(mdoc));
156449aff9f8Sschwarze 	case MDOC_BODY:
1565f6127a73Sschwarze 		break;
15662a427d60Sschwarze 	default:
15672a427d60Sschwarze 		return(1);
1568f6127a73Sschwarze 	}
1569f6127a73Sschwarze 
1570753701eeSschwarze 	bwarn_ge1(mdoc);
1571753701eeSschwarze 
15722a427d60Sschwarze 	nchild = nbody->child;
15732a427d60Sschwarze 	while (NULL != nchild) {
15742a427d60Sschwarze 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
15752a427d60Sschwarze 			nchild = nchild->next;
15762a427d60Sschwarze 			continue;
15772a427d60Sschwarze 		}
15782a427d60Sschwarze 
1579dd25b57cSschwarze 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1580dd25b57cSschwarze 		    nchild->line, nchild->pos,
1581dd25b57cSschwarze 		    mdoc_macronames[nchild->tok]);
15822a427d60Sschwarze 
15832a427d60Sschwarze 		/*
15842a427d60Sschwarze 		 * Move the node out of the Bl block.
15852a427d60Sschwarze 		 * First, collect all required node pointers.
15862a427d60Sschwarze 		 */
15872a427d60Sschwarze 
15882a427d60Sschwarze 		nblock  = nbody->parent;
15892a427d60Sschwarze 		nprev   = nblock->prev;
15902a427d60Sschwarze 		nparent = nblock->parent;
15912a427d60Sschwarze 		nnext   = nchild->next;
15922a427d60Sschwarze 
15932a427d60Sschwarze 		/*
15942a427d60Sschwarze 		 * Unlink this child.
15952a427d60Sschwarze 		 */
15962a427d60Sschwarze 
15972a427d60Sschwarze 		assert(NULL == nchild->prev);
15982a427d60Sschwarze 		if (0 == --nbody->nchild) {
15992a427d60Sschwarze 			nbody->child = NULL;
16002a427d60Sschwarze 			nbody->last  = NULL;
16012a427d60Sschwarze 			assert(NULL == nnext);
16022a427d60Sschwarze 		} else {
16032a427d60Sschwarze 			nbody->child = nnext;
16042a427d60Sschwarze 			nnext->prev = NULL;
16052a427d60Sschwarze 		}
16062a427d60Sschwarze 
16072a427d60Sschwarze 		/*
16082a427d60Sschwarze 		 * Relink this child.
16092a427d60Sschwarze 		 */
16102a427d60Sschwarze 
16112a427d60Sschwarze 		nchild->parent = nparent;
16122a427d60Sschwarze 		nchild->prev   = nprev;
16132a427d60Sschwarze 		nchild->next   = nblock;
16142a427d60Sschwarze 
16152a427d60Sschwarze 		nblock->prev = nchild;
16162a427d60Sschwarze 		nparent->nchild++;
16172a427d60Sschwarze 		if (NULL == nprev)
16182a427d60Sschwarze 			nparent->child = nchild;
16192a427d60Sschwarze 		else
16202a427d60Sschwarze 			nprev->next = nchild;
16212a427d60Sschwarze 
16222a427d60Sschwarze 		nchild = nnext;
1623f73abda9Skristaps 	}
1624f73abda9Skristaps 
1625f73abda9Skristaps 	return(1);
1626f73abda9Skristaps }
1627f73abda9Skristaps 
1628f73abda9Skristaps static int
1629753701eeSschwarze post_bk(POST_ARGS)
1630753701eeSschwarze {
1631753701eeSschwarze 
1632753701eeSschwarze 	hwarn_eq0(mdoc);
1633753701eeSschwarze 	bwarn_ge1(mdoc);
1634753701eeSschwarze 	return(1);
1635753701eeSschwarze }
1636753701eeSschwarze 
1637753701eeSschwarze static int
1638f73abda9Skristaps ebool(struct mdoc *mdoc)
1639f73abda9Skristaps {
1640dc0d8bb2Sschwarze 	struct mdoc_node	*nch;
1641dc0d8bb2Sschwarze 	enum mdoct		 tok;
1642f73abda9Skristaps 
1643dc0d8bb2Sschwarze 	tok = mdoc->last->tok;
1644dc0d8bb2Sschwarze 	nch = mdoc->last->child;
1645dc0d8bb2Sschwarze 
1646dc0d8bb2Sschwarze 	if (NULL == nch) {
1647dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1648f9e7bf99Sschwarze 			mdoc->flags ^= MDOC_SMOFF;
1649f73abda9Skristaps 		return(1);
1650bb648afaSschwarze 	}
1651f9e7bf99Sschwarze 
1652f9e7bf99Sschwarze 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
1653f73abda9Skristaps 
1654dc0d8bb2Sschwarze 	assert(MDOC_TEXT == nch->type);
165520fa2881Sschwarze 
1656dc0d8bb2Sschwarze 	if (0 == strcmp(nch->string, "on")) {
1657dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1658ec2beb53Sschwarze 			mdoc->flags &= ~MDOC_SMOFF;
165920fa2881Sschwarze 		return(1);
1660ec2beb53Sschwarze 	}
1661dc0d8bb2Sschwarze 	if (0 == strcmp(nch->string, "off")) {
1662dc0d8bb2Sschwarze 		if (MDOC_Sm == tok)
1663ec2beb53Sschwarze 			mdoc->flags |= MDOC_SMOFF;
166420fa2881Sschwarze 		return(1);
1665ec2beb53Sschwarze 	}
166620fa2881Sschwarze 
1667dc0d8bb2Sschwarze 	mandoc_vmsg(MANDOCERR_SM_BAD,
1668dc0d8bb2Sschwarze 	    mdoc->parse, nch->line, nch->pos,
1669dc0d8bb2Sschwarze 	    "%s %s", mdoc_macronames[tok], nch->string);
1670dc0d8bb2Sschwarze 	return(mdoc_node_relink(mdoc, nch));
167120fa2881Sschwarze }
1672f73abda9Skristaps 
1673f73abda9Skristaps static int
1674f73abda9Skristaps post_root(POST_ARGS)
1675f73abda9Skristaps {
167620fa2881Sschwarze 	struct mdoc_node *n;
1677f73abda9Skristaps 
1678ac1f49d0Sschwarze 	/* Add missing prologue data. */
167920fa2881Sschwarze 
1680ac1f49d0Sschwarze 	if (mdoc->meta.date == NULL)
1681ac1f49d0Sschwarze 		mdoc->meta.date = mdoc->quick ?
1682ac1f49d0Sschwarze 		    mandoc_strdup("") :
1683ac1f49d0Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
16843fdead0cSschwarze 
16853fdead0cSschwarze 	if (mdoc->meta.title == NULL) {
16863fdead0cSschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
16873fdead0cSschwarze 		    mdoc->parse, 0, 0, "EOF");
16883fdead0cSschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
16893fdead0cSschwarze 	}
16903fdead0cSschwarze 
1691ac1f49d0Sschwarze 	if (mdoc->meta.vol == NULL)
1692ac1f49d0Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
16933fdead0cSschwarze 
16943fdead0cSschwarze 	if (mdoc->meta.os == NULL) {
16953fdead0cSschwarze 		mandoc_msg(MANDOCERR_OS_MISSING,
16963fdead0cSschwarze 		    mdoc->parse, 0, 0, NULL);
16973fdead0cSschwarze 		mdoc->meta.os = mandoc_strdup("");
1698f73abda9Skristaps 	}
1699f73abda9Skristaps 
170020fa2881Sschwarze 	/* Check that we begin with a proper `Sh'. */
170120fa2881Sschwarze 
1702e20417bdSschwarze 	n = mdoc->first->child;
1703e20417bdSschwarze 	while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1704e20417bdSschwarze 		n = n->next;
1705e20417bdSschwarze 
1706e20417bdSschwarze 	if (n == NULL)
1707e20417bdSschwarze 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1708e20417bdSschwarze 	else if (n->tok != MDOC_Sh)
170951fcab2fSschwarze 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1710e20417bdSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
171120fa2881Sschwarze 
1712ac1f49d0Sschwarze 	return(1);
171320fa2881Sschwarze }
1714f73abda9Skristaps 
1715f73abda9Skristaps static int
1716f73abda9Skristaps post_st(POST_ARGS)
1717f73abda9Skristaps {
1718dc0d8bb2Sschwarze 	struct mdoc_node	 *n, *nch;
171920fa2881Sschwarze 	const char		 *p;
1720f73abda9Skristaps 
1721dc0d8bb2Sschwarze 	n = mdoc->last;
1722dc0d8bb2Sschwarze 	nch = n->child;
1723dc0d8bb2Sschwarze 
1724dc0d8bb2Sschwarze 	if (NULL == nch) {
1725307e5a07Sschwarze 		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1726dc0d8bb2Sschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1727dc0d8bb2Sschwarze 		mdoc_node_delete(mdoc, n);
1728bb648afaSschwarze 		return(1);
1729bb648afaSschwarze 	}
173020fa2881Sschwarze 
1731dc0d8bb2Sschwarze 	assert(MDOC_TEXT == nch->type);
173220fa2881Sschwarze 
1733dc0d8bb2Sschwarze 	if (NULL == (p = mdoc_a2st(nch->string))) {
1734bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1735bd594191Sschwarze 		    nch->line, nch->pos, "St %s", nch->string);
1736dc0d8bb2Sschwarze 		mdoc_node_delete(mdoc, n);
173720fa2881Sschwarze 	} else {
1738dc0d8bb2Sschwarze 		free(nch->string);
1739dc0d8bb2Sschwarze 		nch->string = mandoc_strdup(p);
1740f73abda9Skristaps 	}
1741f73abda9Skristaps 
174220fa2881Sschwarze 	return(1);
174320fa2881Sschwarze }
1744f73abda9Skristaps 
1745f73abda9Skristaps static int
1746011fe33bSschwarze post_rs(POST_ARGS)
1747011fe33bSschwarze {
174820fa2881Sschwarze 	struct mdoc_node *nn, *next, *prev;
174920fa2881Sschwarze 	int		  i, j;
1750011fe33bSschwarze 
1751bb648afaSschwarze 	switch (mdoc->last->type) {
175249aff9f8Sschwarze 	case MDOC_HEAD:
1753bb648afaSschwarze 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1754011fe33bSschwarze 		return(1);
175549aff9f8Sschwarze 	case MDOC_BODY:
1756bb648afaSschwarze 		if (mdoc->last->child)
1757bb648afaSschwarze 			break;
1758bb648afaSschwarze 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1759bb648afaSschwarze 		return(1);
1760bb648afaSschwarze 	default:
1761bb648afaSschwarze 		return(1);
1762bb648afaSschwarze 	}
1763011fe33bSschwarze 
176420fa2881Sschwarze 	/*
176520fa2881Sschwarze 	 * The full `Rs' block needs special handling to order the
176620fa2881Sschwarze 	 * sub-elements according to `rsord'.  Pick through each element
1767b538baa5Sschwarze 	 * and correctly order it.  This is an insertion sort.
176820fa2881Sschwarze 	 */
176920fa2881Sschwarze 
177020fa2881Sschwarze 	next = NULL;
177120fa2881Sschwarze 	for (nn = mdoc->last->child->next; nn; nn = next) {
177220fa2881Sschwarze 		/* Determine order of `nn'. */
177320fa2881Sschwarze 		for (i = 0; i < RSORD_MAX; i++)
177420fa2881Sschwarze 			if (rsord[i] == nn->tok)
177520fa2881Sschwarze 				break;
177620fa2881Sschwarze 
1777b538baa5Sschwarze 		if (i == RSORD_MAX) {
1778b538baa5Sschwarze 			mandoc_msg(MANDOCERR_RS_BAD,
1779b538baa5Sschwarze 			    mdoc->parse, nn->line, nn->pos,
1780b538baa5Sschwarze 			    mdoc_macronames[nn->tok]);
1781b538baa5Sschwarze 			i = -1;
1782b538baa5Sschwarze 		} else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
1783b538baa5Sschwarze 			mdoc->last->norm->Rs.quote_T++;
1784b538baa5Sschwarze 
178520fa2881Sschwarze 		/*
178620fa2881Sschwarze 		 * Remove `nn' from the chain.  This somewhat
178720fa2881Sschwarze 		 * repeats mdoc_node_unlink(), but since we're
178820fa2881Sschwarze 		 * just re-ordering, there's no need for the
178920fa2881Sschwarze 		 * full unlink process.
179020fa2881Sschwarze 		 */
179120fa2881Sschwarze 
179220fa2881Sschwarze 		if (NULL != (next = nn->next))
179320fa2881Sschwarze 			next->prev = nn->prev;
179420fa2881Sschwarze 
179520fa2881Sschwarze 		if (NULL != (prev = nn->prev))
179620fa2881Sschwarze 			prev->next = nn->next;
179720fa2881Sschwarze 
179820fa2881Sschwarze 		nn->prev = nn->next = NULL;
179920fa2881Sschwarze 
180020fa2881Sschwarze 		/*
180120fa2881Sschwarze 		 * Scan back until we reach a node that's
180220fa2881Sschwarze 		 * ordered before `nn'.
180320fa2881Sschwarze 		 */
180420fa2881Sschwarze 
180520fa2881Sschwarze 		for ( ; prev ; prev = prev->prev) {
180620fa2881Sschwarze 			/* Determine order of `prev'. */
180720fa2881Sschwarze 			for (j = 0; j < RSORD_MAX; j++)
180820fa2881Sschwarze 				if (rsord[j] == prev->tok)
180920fa2881Sschwarze 					break;
1810b538baa5Sschwarze 			if (j == RSORD_MAX)
1811b538baa5Sschwarze 				j = -1;
181220fa2881Sschwarze 
181320fa2881Sschwarze 			if (j <= i)
181420fa2881Sschwarze 				break;
181520fa2881Sschwarze 		}
181620fa2881Sschwarze 
181720fa2881Sschwarze 		/*
181820fa2881Sschwarze 		 * Set `nn' back into its correct place in front
181920fa2881Sschwarze 		 * of the `prev' node.
182020fa2881Sschwarze 		 */
182120fa2881Sschwarze 
182220fa2881Sschwarze 		nn->prev = prev;
182320fa2881Sschwarze 
182420fa2881Sschwarze 		if (prev) {
182520fa2881Sschwarze 			if (prev->next)
182620fa2881Sschwarze 				prev->next->prev = nn;
182720fa2881Sschwarze 			nn->next = prev->next;
182820fa2881Sschwarze 			prev->next = nn;
182920fa2881Sschwarze 		} else {
183020fa2881Sschwarze 			mdoc->last->child->prev = nn;
183120fa2881Sschwarze 			nn->next = mdoc->last->child;
183220fa2881Sschwarze 			mdoc->last->child = nn;
183320fa2881Sschwarze 		}
1834011fe33bSschwarze 	}
1835011fe33bSschwarze 
1836011fe33bSschwarze 	return(1);
1837011fe33bSschwarze }
1838011fe33bSschwarze 
18394039b21cSschwarze /*
18404039b21cSschwarze  * For some arguments of some macros,
18414039b21cSschwarze  * convert all breakable hyphens into ASCII_HYPH.
18424039b21cSschwarze  */
18434039b21cSschwarze static int
18444039b21cSschwarze post_hyph(POST_ARGS)
18454039b21cSschwarze {
18464039b21cSschwarze 	struct mdoc_node	*n, *nch;
18474039b21cSschwarze 	char			*cp;
18484039b21cSschwarze 
18494039b21cSschwarze 	n = mdoc->last;
18504039b21cSschwarze 	switch (n->type) {
185149aff9f8Sschwarze 	case MDOC_HEAD:
18524039b21cSschwarze 		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
18534039b21cSschwarze 			break;
18544039b21cSschwarze 		return(1);
185549aff9f8Sschwarze 	case MDOC_BODY:
18564039b21cSschwarze 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
18574039b21cSschwarze 			break;
18584039b21cSschwarze 		return(1);
185949aff9f8Sschwarze 	case MDOC_ELEM:
18604039b21cSschwarze 		break;
18614039b21cSschwarze 	default:
18624039b21cSschwarze 		return(1);
18634039b21cSschwarze 	}
18644039b21cSschwarze 
18654039b21cSschwarze 	for (nch = n->child; nch; nch = nch->next) {
18664039b21cSschwarze 		if (MDOC_TEXT != nch->type)
18674039b21cSschwarze 			continue;
18684039b21cSschwarze 		cp = nch->string;
1869b7e2b14eSschwarze 		if ('\0' == *cp)
18704039b21cSschwarze 			continue;
18714039b21cSschwarze 		while ('\0' != *(++cp))
18724039b21cSschwarze 			if ('-' == *cp &&
18734039b21cSschwarze 			    isalpha((unsigned char)cp[-1]) &&
18744039b21cSschwarze 			    isalpha((unsigned char)cp[1]))
18754039b21cSschwarze 				*cp = ASCII_HYPH;
18764039b21cSschwarze 	}
18774039b21cSschwarze 	return(1);
18784039b21cSschwarze }
18794039b21cSschwarze 
1880011fe33bSschwarze static int
1881753701eeSschwarze post_hyphtext(POST_ARGS)
1882753701eeSschwarze {
1883753701eeSschwarze 
1884753701eeSschwarze 	ewarn_ge1(mdoc);
1885753701eeSschwarze 	return(post_hyph(mdoc));
1886753701eeSschwarze }
1887753701eeSschwarze 
1888753701eeSschwarze static int
1889af216717Sschwarze post_ns(POST_ARGS)
1890af216717Sschwarze {
1891af216717Sschwarze 
1892af216717Sschwarze 	if (MDOC_LINE & mdoc->last->flags)
189328153913Sschwarze 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
189428153913Sschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
1895af216717Sschwarze 	return(1);
1896af216717Sschwarze }
1897af216717Sschwarze 
1898af216717Sschwarze static int
1899f73abda9Skristaps post_sh(POST_ARGS)
1900f73abda9Skristaps {
1901f73abda9Skristaps 
1902753701eeSschwarze 	post_ignpar(mdoc);
1903753701eeSschwarze 
1904cd6c268fSschwarze 	switch (mdoc->last->type) {
1905cd6c268fSschwarze 	case MDOC_HEAD:
1906f73abda9Skristaps 		return(post_sh_head(mdoc));
1907cd6c268fSschwarze 	case MDOC_BODY:
1908cd6c268fSschwarze 		switch (mdoc->lastsec)  {
1909cd6c268fSschwarze 		case SEC_NAME:
1910cd6c268fSschwarze 			return(post_sh_name(mdoc));
19117c384856Sschwarze 		case SEC_SEE_ALSO:
19127c384856Sschwarze 			return(post_sh_see_also(mdoc));
1913cd6c268fSschwarze 		case SEC_AUTHORS:
1914cd6c268fSschwarze 			return(post_sh_authors(mdoc));
1915cd6c268fSschwarze 		default:
1916cd6c268fSschwarze 			break;
1917cd6c268fSschwarze 		}
1918cd6c268fSschwarze 		break;
1919cd6c268fSschwarze 	default:
1920cd6c268fSschwarze 		break;
1921cd6c268fSschwarze 	}
1922f73abda9Skristaps 
1923f73abda9Skristaps 	return(1);
1924f73abda9Skristaps }
1925f73abda9Skristaps 
1926f73abda9Skristaps static int
1927cd6c268fSschwarze post_sh_name(POST_ARGS)
1928f73abda9Skristaps {
1929f73abda9Skristaps 	struct mdoc_node *n;
1930f73abda9Skristaps 
1931f73abda9Skristaps 	/*
1932f73abda9Skristaps 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1933f73abda9Skristaps 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1934f73abda9Skristaps 	 * children of the BODY declaration can also be "text".
1935f73abda9Skristaps 	 */
1936f73abda9Skristaps 
193720fa2881Sschwarze 	if (NULL == (n = mdoc->last->child)) {
193851fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
193951fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos, "empty");
194020fa2881Sschwarze 		return(1);
194120fa2881Sschwarze 	}
1942f73abda9Skristaps 
1943f73abda9Skristaps 	for ( ; n && n->next; n = n->next) {
1944f73abda9Skristaps 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1945f73abda9Skristaps 			continue;
1946f73abda9Skristaps 		if (MDOC_TEXT == n->type)
1947f73abda9Skristaps 			continue;
194851fcab2fSschwarze 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
194951fcab2fSschwarze 		    n->line, n->pos, mdoc_macronames[n->tok]);
1950f73abda9Skristaps 	}
1951f73abda9Skristaps 
195249d529b5Sschwarze 	assert(n);
19534602e85cSschwarze 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1954f73abda9Skristaps 		return(1);
1955f73abda9Skristaps 
195651fcab2fSschwarze 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
195751fcab2fSschwarze 	    n->line, n->pos, mdoc_macronames[n->tok]);
195820fa2881Sschwarze 	return(1);
195920fa2881Sschwarze }
1960f73abda9Skristaps 
1961f73abda9Skristaps static int
19627c384856Sschwarze post_sh_see_also(POST_ARGS)
19637c384856Sschwarze {
19647c384856Sschwarze 	const struct mdoc_node	*n;
19657c384856Sschwarze 	const char 		*name, *sec;
19667c384856Sschwarze 	const char		*lastname, *lastsec, *lastpunct;
19677c384856Sschwarze 	int			 cmp;
19687c384856Sschwarze 
19697c384856Sschwarze 	n = mdoc->last->child;
19707c384856Sschwarze 	lastname = lastsec = lastpunct = NULL;
19717c384856Sschwarze 	while (n != NULL) {
19727c384856Sschwarze 		if (n->tok != MDOC_Xr || n->nchild < 2)
19737c384856Sschwarze 			break;
19747c384856Sschwarze 
19757c384856Sschwarze 		/* Process one .Xr node. */
19767c384856Sschwarze 
19777c384856Sschwarze 		name = n->child->string;
19787c384856Sschwarze 		sec = n->child->next->string;
19797c384856Sschwarze 		if (lastsec != NULL) {
19807c384856Sschwarze 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
19817c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
19827c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
19837c384856Sschwarze 				    "%s before %s(%s)", lastpunct,
19847c384856Sschwarze 				    name, sec);
19857c384856Sschwarze 			cmp = strcmp(lastsec, sec);
19867c384856Sschwarze 			if (cmp > 0)
19877c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
19887c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
19897c384856Sschwarze 				    "%s(%s) after %s(%s)", name,
19907c384856Sschwarze 				    sec, lastname, lastsec);
19917c384856Sschwarze 			else if (cmp == 0 &&
19927c384856Sschwarze 			    strcasecmp(lastname, name) > 0)
19937c384856Sschwarze 				mandoc_vmsg(MANDOCERR_XR_ORDER,
19947c384856Sschwarze 				    mdoc->parse, n->line, n->pos,
19957c384856Sschwarze 				    "%s after %s", name, lastname);
19967c384856Sschwarze 		}
19977c384856Sschwarze 		lastname = name;
19987c384856Sschwarze 		lastsec = sec;
19997c384856Sschwarze 
20007c384856Sschwarze 		/* Process the following node. */
20017c384856Sschwarze 
20027c384856Sschwarze 		n = n->next;
20037c384856Sschwarze 		if (n == NULL)
20047c384856Sschwarze 			break;
20057c384856Sschwarze 		if (n->tok == MDOC_Xr) {
20067c384856Sschwarze 			lastpunct = "none";
20077c384856Sschwarze 			continue;
20087c384856Sschwarze 		}
20097c384856Sschwarze 		if (n->type != MDOC_TEXT)
20107c384856Sschwarze 			break;
20117c384856Sschwarze 		for (name = n->string; *name != '\0'; name++)
20127c384856Sschwarze 			if (isalpha((const unsigned char)*name))
20137c384856Sschwarze 				return(1);
20147c384856Sschwarze 		lastpunct = n->string;
20157c384856Sschwarze 		if (n->next == NULL)
20167c384856Sschwarze 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
20177c384856Sschwarze 			    n->line, n->pos, "%s after %s(%s)",
20187c384856Sschwarze 			    lastpunct, lastname, lastsec);
20197c384856Sschwarze 		n = n->next;
20207c384856Sschwarze 	}
20217c384856Sschwarze 	return(1);
20227c384856Sschwarze }
20237c384856Sschwarze 
20247c384856Sschwarze static int
2025cd6c268fSschwarze child_an(const struct mdoc_node *n)
2026cd6c268fSschwarze {
2027cd6c268fSschwarze 
2028cd6c268fSschwarze 	for (n = n->child; n != NULL; n = n->next)
2029cd6c268fSschwarze 		if ((n->tok == MDOC_An && n->nchild) || child_an(n))
2030cd6c268fSschwarze 			return(1);
2031cd6c268fSschwarze 	return(0);
2032cd6c268fSschwarze }
2033cd6c268fSschwarze 
2034cd6c268fSschwarze static int
2035cd6c268fSschwarze post_sh_authors(POST_ARGS)
2036cd6c268fSschwarze {
2037cd6c268fSschwarze 
2038cd6c268fSschwarze 	if ( ! child_an(mdoc->last))
2039cd6c268fSschwarze 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2040cd6c268fSschwarze 		    mdoc->last->line, mdoc->last->pos, NULL);
2041cd6c268fSschwarze 	return(1);
2042cd6c268fSschwarze }
2043cd6c268fSschwarze 
2044cd6c268fSschwarze static int
2045f73abda9Skristaps post_sh_head(POST_ARGS)
2046f73abda9Skristaps {
2047a2cff342Sschwarze 	struct mdoc_node *n;
204851fcab2fSschwarze 	const char	*goodsec;
204946133849Sschwarze 	char		*secname;
2050f73abda9Skristaps 	enum mdoc_sec	 sec;
2051f73abda9Skristaps 
2052f73abda9Skristaps 	/*
2053f73abda9Skristaps 	 * Process a new section.  Sections are either "named" or
205420fa2881Sschwarze 	 * "custom".  Custom sections are user-defined, while named ones
205520fa2881Sschwarze 	 * follow a conventional order and may only appear in certain
205620fa2881Sschwarze 	 * manual sections.
2057f73abda9Skristaps 	 */
2058f73abda9Skristaps 
205983af2bccSschwarze 	secname = NULL;
206004e980cbSschwarze 	sec = SEC_CUSTOM;
206146133849Sschwarze 	mdoc_deroff(&secname, mdoc->last);
206246133849Sschwarze 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
2063f73abda9Skristaps 
206420fa2881Sschwarze 	/* The NAME should be first. */
2065f73abda9Skristaps 
2066fccfce9dSschwarze 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
2067bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
2068bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
2069bd594191Sschwarze 		    "Sh %s", secname);
207020fa2881Sschwarze 
207120fa2881Sschwarze 	/* The SYNOPSIS gets special attention in other areas. */
207220fa2881Sschwarze 
207322881299Sschwarze 	if (SEC_SYNOPSIS == sec) {
207475088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 1, '=');
207520fa2881Sschwarze 		mdoc->flags |= MDOC_SYNOPSIS;
207622881299Sschwarze 	} else {
207775088a49Sschwarze 		roff_setreg(mdoc->roff, "nS", 0, '=');
207820fa2881Sschwarze 		mdoc->flags &= ~MDOC_SYNOPSIS;
207922881299Sschwarze 	}
208020fa2881Sschwarze 
208120fa2881Sschwarze 	/* Mark our last section. */
208220fa2881Sschwarze 
208320fa2881Sschwarze 	mdoc->lastsec = sec;
20841eccdf28Sschwarze 
20851eccdf28Sschwarze 	/*
20861eccdf28Sschwarze 	 * Set the section attribute for the current HEAD, for its
20871eccdf28Sschwarze 	 * parent BLOCK, and for the HEAD children; the latter can
20881eccdf28Sschwarze 	 * only be TEXT nodes, so no recursion is needed.
20891eccdf28Sschwarze 	 * For other blocks and elements, including .Sh BODY, this is
20901eccdf28Sschwarze 	 * done when allocating the node data structures, but for .Sh
20911eccdf28Sschwarze 	 * BLOCK and HEAD, the section is still unknown at that time.
20921eccdf28Sschwarze 	 */
20931eccdf28Sschwarze 
2094a2cff342Sschwarze 	mdoc->last->parent->sec = sec;
2095a2cff342Sschwarze 	mdoc->last->sec = sec;
2096a2cff342Sschwarze 	for (n = mdoc->last->child; n; n = n->next)
2097a2cff342Sschwarze 		n->sec = sec;
209820fa2881Sschwarze 
209920fa2881Sschwarze 	/* We don't care about custom sections after this. */
2100fccfce9dSschwarze 
210146133849Sschwarze 	if (SEC_CUSTOM == sec) {
210246133849Sschwarze 		free(secname);
2103f73abda9Skristaps 		return(1);
210446133849Sschwarze 	}
2105fccfce9dSschwarze 
21066be99f77Sschwarze 	/*
210720fa2881Sschwarze 	 * Check whether our non-custom section is being repeated or is
210820fa2881Sschwarze 	 * out of order.
21096be99f77Sschwarze 	 */
2110f73abda9Skristaps 
211120fa2881Sschwarze 	if (sec == mdoc->lastnamed)
2112bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2113bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
2114bd594191Sschwarze 		    "Sh %s", secname);
211520fa2881Sschwarze 
211620fa2881Sschwarze 	if (sec < mdoc->lastnamed)
2117bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2118bd594191Sschwarze 		    mdoc->last->line, mdoc->last->pos,
2119bd594191Sschwarze 		    "Sh %s", secname);
212020fa2881Sschwarze 
212120fa2881Sschwarze 	/* Mark the last named section. */
212220fa2881Sschwarze 
212320fa2881Sschwarze 	mdoc->lastnamed = sec;
212420fa2881Sschwarze 
212520fa2881Sschwarze 	/* Check particular section/manual conventions. */
212620fa2881Sschwarze 
21273fdead0cSschwarze 	if (mdoc->meta.msec == NULL) {
21283fdead0cSschwarze 		free(secname);
21293fdead0cSschwarze 		return(1);
21303fdead0cSschwarze 	}
213120fa2881Sschwarze 
213251fcab2fSschwarze 	goodsec = NULL;
213320fa2881Sschwarze 	switch (sec) {
213449aff9f8Sschwarze 	case SEC_ERRORS:
2135be89e780Sschwarze 		if (*mdoc->meta.msec == '4')
2136be89e780Sschwarze 			break;
213751fcab2fSschwarze 		goodsec = "2, 3, 4, 9";
2138be89e780Sschwarze 		/* FALLTHROUGH */
213949aff9f8Sschwarze 	case SEC_RETURN_VALUES:
214020fa2881Sschwarze 		/* FALLTHROUGH */
214149aff9f8Sschwarze 	case SEC_LIBRARY:
214292c0ca7fSschwarze 		if (*mdoc->meta.msec == '2')
2143f73abda9Skristaps 			break;
214492c0ca7fSschwarze 		if (*mdoc->meta.msec == '3')
214592c0ca7fSschwarze 			break;
214651fcab2fSschwarze 		if (NULL == goodsec)
214751fcab2fSschwarze 			goodsec = "2, 3, 9";
214803ab2f23Sdlg 		/* FALLTHROUGH */
214949aff9f8Sschwarze 	case SEC_CONTEXT:
215092c0ca7fSschwarze 		if (*mdoc->meta.msec == '9')
215192c0ca7fSschwarze 			break;
215251fcab2fSschwarze 		if (NULL == goodsec)
215351fcab2fSschwarze 			goodsec = "9";
215451fcab2fSschwarze 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
215551fcab2fSschwarze 		    mdoc->last->line, mdoc->last->pos,
2156bd594191Sschwarze 		    "Sh %s for %s only", secname, goodsec);
215720fa2881Sschwarze 		break;
2158f73abda9Skristaps 	default:
2159f73abda9Skristaps 		break;
2160f73abda9Skristaps 	}
2161f73abda9Skristaps 
216246133849Sschwarze 	free(secname);
2163f73abda9Skristaps 	return(1);
2164f73abda9Skristaps }
2165d39b9a9cSschwarze 
216620fa2881Sschwarze static int
2167f6127a73Sschwarze post_ignpar(POST_ARGS)
2168f6127a73Sschwarze {
2169f6127a73Sschwarze 	struct mdoc_node *np;
2170f6127a73Sschwarze 
2171753701eeSschwarze 	hwarn_ge1(mdoc);
2172753701eeSschwarze 	post_hyph(mdoc);
2173753701eeSschwarze 
2174f6127a73Sschwarze 	if (MDOC_BODY != mdoc->last->type)
2175f6127a73Sschwarze 		return(1);
2176f6127a73Sschwarze 
2177f6127a73Sschwarze 	if (NULL != (np = mdoc->last->child))
2178f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
217920369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
218020369664Sschwarze 			    mdoc->parse, np->line, np->pos,
218120369664Sschwarze 			    "%s after %s", mdoc_macronames[np->tok],
218220369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2183f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2184f6127a73Sschwarze 		}
2185f6127a73Sschwarze 
2186f6127a73Sschwarze 	if (NULL != (np = mdoc->last->last))
2187f6127a73Sschwarze 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
218820369664Sschwarze 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
218920369664Sschwarze 			    np->line, np->pos, "%s at the end of %s",
219020369664Sschwarze 			    mdoc_macronames[np->tok],
219120369664Sschwarze 			    mdoc_macronames[mdoc->last->tok]);
2192f6127a73Sschwarze 			mdoc_node_delete(mdoc, np);
2193f6127a73Sschwarze 		}
2194f6127a73Sschwarze 
2195f6127a73Sschwarze 	return(1);
2196f6127a73Sschwarze }
2197f6127a73Sschwarze 
2198f6127a73Sschwarze static int
219920fa2881Sschwarze pre_par(PRE_ARGS)
2200d39b9a9cSschwarze {
2201d39b9a9cSschwarze 
2202d39b9a9cSschwarze 	if (NULL == mdoc->last)
2203d39b9a9cSschwarze 		return(1);
2204f6127a73Sschwarze 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2205f6127a73Sschwarze 		return(1);
2206d39b9a9cSschwarze 
220720fa2881Sschwarze 	/*
220820fa2881Sschwarze 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
220920fa2881Sschwarze 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
221020fa2881Sschwarze 	 */
2211d39b9a9cSschwarze 
2212e0dd4c9cSschwarze 	if (MDOC_Pp != mdoc->last->tok &&
2213e0dd4c9cSschwarze 	    MDOC_Lp != mdoc->last->tok &&
2214e0dd4c9cSschwarze 	    MDOC_br != mdoc->last->tok)
2215d39b9a9cSschwarze 		return(1);
22168c62fbf5Sschwarze 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2217d39b9a9cSschwarze 		return(1);
22188c62fbf5Sschwarze 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2219d39b9a9cSschwarze 		return(1);
22208c62fbf5Sschwarze 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2221f6127a73Sschwarze 		return(1);
2222d39b9a9cSschwarze 
222320369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
222420369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
222520369664Sschwarze 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
222620369664Sschwarze 	    mdoc_macronames[n->tok]);
2227d39b9a9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2228d39b9a9cSschwarze 	return(1);
2229d39b9a9cSschwarze }
223020fa2881Sschwarze 
223120fa2881Sschwarze static int
2232e0dd4c9cSschwarze post_par(POST_ARGS)
2233e0dd4c9cSschwarze {
223420369664Sschwarze 	struct mdoc_node *np;
2235e0dd4c9cSschwarze 
2236753701eeSschwarze 	if (mdoc->last->tok == MDOC_sp)
2237753701eeSschwarze 		ewarn_le1(mdoc);
2238753701eeSschwarze 	else
2239753701eeSschwarze 		ewarn_eq0(mdoc);
2240753701eeSschwarze 
2241e0dd4c9cSschwarze 	if (MDOC_ELEM != mdoc->last->type &&
2242e0dd4c9cSschwarze 	    MDOC_BLOCK != mdoc->last->type)
2243e0dd4c9cSschwarze 		return(1);
2244e0dd4c9cSschwarze 
224520369664Sschwarze 	if (NULL == (np = mdoc->last->prev)) {
224620369664Sschwarze 		np = mdoc->last->parent;
224720369664Sschwarze 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2248e0dd4c9cSschwarze 			return(1);
2249e0dd4c9cSschwarze 	} else {
225020369664Sschwarze 		if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2251e0dd4c9cSschwarze 		    (MDOC_br != mdoc->last->tok ||
225220369664Sschwarze 		     (MDOC_sp != np->tok && MDOC_br != np->tok)))
2253e0dd4c9cSschwarze 			return(1);
2254e0dd4c9cSschwarze 	}
2255e0dd4c9cSschwarze 
225620369664Sschwarze 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
225720369664Sschwarze 	    mdoc->last->line, mdoc->last->pos,
225820369664Sschwarze 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
225920369664Sschwarze 	    mdoc_macronames[np->tok]);
2260e0dd4c9cSschwarze 	mdoc_node_delete(mdoc, mdoc->last);
2261e0dd4c9cSschwarze 	return(1);
2262e0dd4c9cSschwarze }
2263e0dd4c9cSschwarze 
2264e0dd4c9cSschwarze static int
226520fa2881Sschwarze pre_literal(PRE_ARGS)
226620fa2881Sschwarze {
226720fa2881Sschwarze 
2268d52d1586Sschwarze 	pre_display(mdoc, n);
2269d52d1586Sschwarze 
227020fa2881Sschwarze 	if (MDOC_BODY != n->type)
227120fa2881Sschwarze 		return(1);
227220fa2881Sschwarze 
227320fa2881Sschwarze 	/*
227420fa2881Sschwarze 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
227520fa2881Sschwarze 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
227620fa2881Sschwarze 	 */
227720fa2881Sschwarze 
227820fa2881Sschwarze 	switch (n->tok) {
227949aff9f8Sschwarze 	case MDOC_Dl:
228020fa2881Sschwarze 		mdoc->flags |= MDOC_LITERAL;
228120fa2881Sschwarze 		break;
228249aff9f8Sschwarze 	case MDOC_Bd:
22838c62fbf5Sschwarze 		if (DISP_literal == n->norm->Bd.type)
228420fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
22858c62fbf5Sschwarze 		if (DISP_unfilled == n->norm->Bd.type)
228620fa2881Sschwarze 			mdoc->flags |= MDOC_LITERAL;
228720fa2881Sschwarze 		break;
228820fa2881Sschwarze 	default:
228920fa2881Sschwarze 		abort();
229020fa2881Sschwarze 		/* NOTREACHED */
229120fa2881Sschwarze 	}
229220fa2881Sschwarze 
229320fa2881Sschwarze 	return(1);
229420fa2881Sschwarze }
229520fa2881Sschwarze 
229620fa2881Sschwarze static int
229720fa2881Sschwarze post_dd(POST_ARGS)
229820fa2881Sschwarze {
229920fa2881Sschwarze 	struct mdoc_node *n;
230083af2bccSschwarze 	char		 *datestr;
230120fa2881Sschwarze 
2302b058e777Sschwarze 	if (mdoc->meta.date)
2303b058e777Sschwarze 		free(mdoc->meta.date);
230420fa2881Sschwarze 
2305b058e777Sschwarze 	n = mdoc->last;
2306b058e777Sschwarze 	if (NULL == n->child || '\0' == n->child->string[0]) {
2307231c7061Sschwarze 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2308231c7061Sschwarze 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
23093fdead0cSschwarze 		goto out;
231020fa2881Sschwarze 	}
231120fa2881Sschwarze 
231283af2bccSschwarze 	datestr = NULL;
231383af2bccSschwarze 	mdoc_deroff(&datestr, n);
231483af2bccSschwarze 	if (mdoc->quick)
231583af2bccSschwarze 		mdoc->meta.date = datestr;
231683af2bccSschwarze 	else {
231783af2bccSschwarze 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
231883af2bccSschwarze 		    datestr, n->line, n->pos);
231983af2bccSschwarze 		free(datestr);
232004e980cbSschwarze 	}
23213fdead0cSschwarze out:
23223fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
23233fdead0cSschwarze 	return(1);
232420fa2881Sschwarze }
232520fa2881Sschwarze 
232620fa2881Sschwarze static int
232720fa2881Sschwarze post_dt(POST_ARGS)
232820fa2881Sschwarze {
232920fa2881Sschwarze 	struct mdoc_node *nn, *n;
233020fa2881Sschwarze 	const char	 *cp;
233120fa2881Sschwarze 	char		 *p;
233220fa2881Sschwarze 
233320fa2881Sschwarze 	n = mdoc->last;
233420fa2881Sschwarze 
233520fa2881Sschwarze 	free(mdoc->meta.title);
23363fdead0cSschwarze 	free(mdoc->meta.msec);
233720fa2881Sschwarze 	free(mdoc->meta.vol);
233820fa2881Sschwarze 	free(mdoc->meta.arch);
233920fa2881Sschwarze 
23403fdead0cSschwarze 	mdoc->meta.title = NULL;
23413fdead0cSschwarze 	mdoc->meta.msec = NULL;
23423fdead0cSschwarze 	mdoc->meta.vol = NULL;
23433fdead0cSschwarze 	mdoc->meta.arch = NULL;
234420fa2881Sschwarze 
234551fcab2fSschwarze 	/* First check that all characters are uppercase. */
234620fa2881Sschwarze 
234720fa2881Sschwarze 	if (NULL != (nn = n->child))
234820fa2881Sschwarze 		for (p = nn->string; *p; p++) {
234904e980cbSschwarze 			if (toupper((unsigned char)*p) == *p)
235020fa2881Sschwarze 				continue;
2351bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_TITLE_CASE,
235251fcab2fSschwarze 			    mdoc->parse, nn->line,
235351fcab2fSschwarze 			    nn->pos + (p - nn->string),
2354bd594191Sschwarze 			    "Dt %s", nn->string);
235520fa2881Sschwarze 			break;
235620fa2881Sschwarze 		}
235720fa2881Sschwarze 
23583fdead0cSschwarze 	/* No argument: msec and arch remain NULL. */
235920fa2881Sschwarze 
236020fa2881Sschwarze 	if (NULL == (nn = n->child)) {
23613fdead0cSschwarze 		mandoc_msg(MANDOCERR_DT_NOTITLE,
23623fdead0cSschwarze 		    mdoc->parse, n->line, n->pos, "Dt");
23633fdead0cSschwarze 		mdoc->meta.title = mandoc_strdup("UNTITLED");
236420fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
23653fdead0cSschwarze 		goto out;
236620fa2881Sschwarze 	}
236720fa2881Sschwarze 
23683fdead0cSschwarze 	/* One argument: msec and arch remain NULL. */
236920fa2881Sschwarze 
237049aff9f8Sschwarze 	mdoc->meta.title = mandoc_strdup(
23713fdead0cSschwarze 	    '\0' == nn->string[0] ? "UNTITLED" : nn->string);
237220fa2881Sschwarze 
237320fa2881Sschwarze 	if (NULL == (nn = nn->next)) {
23743fdead0cSschwarze 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
23753fdead0cSschwarze 		    mdoc->parse, n->line, n->pos,
23763fdead0cSschwarze 		    "Dt %s", mdoc->meta.title);
237720fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup("LOCAL");
23783fdead0cSschwarze 		goto out;
237920fa2881Sschwarze 	}
238020fa2881Sschwarze 
238120fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC'
238249aff9f8Sschwarze 	 * title = TITLE,
238349aff9f8Sschwarze 	 * volume = SEC is msec ? format(msec) : SEC,
238420fa2881Sschwarze 	 * msec = SEC is msec ? atoi(msec) : 0,
238520fa2881Sschwarze 	 * arch = NULL
238620fa2881Sschwarze 	 */
238720fa2881Sschwarze 
238888ec69e3Sschwarze 	cp = mandoc_a2msec(nn->string);
238920fa2881Sschwarze 	if (cp) {
239020fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
239120fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
239220fa2881Sschwarze 	} else {
2393bd594191Sschwarze 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2394bd594191Sschwarze 		    nn->line, nn->pos, "Dt ... %s", nn->string);
239520fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(nn->string);
239620fa2881Sschwarze 		mdoc->meta.msec = mandoc_strdup(nn->string);
239720fa2881Sschwarze 	}
239820fa2881Sschwarze 
239920fa2881Sschwarze 	if (NULL == (nn = nn->next))
24003fdead0cSschwarze 		goto out;
240120fa2881Sschwarze 
240220fa2881Sschwarze 	/* Handles: `.Dt TITLE SEC VOL'
240349aff9f8Sschwarze 	 * title = TITLE,
240449aff9f8Sschwarze 	 * volume = VOL is vol ? format(VOL) :
240520fa2881Sschwarze 	 *	    VOL is arch ? format(arch) :
240620fa2881Sschwarze 	 *	    VOL
240720fa2881Sschwarze 	 */
240820fa2881Sschwarze 
240920fa2881Sschwarze 	cp = mdoc_a2vol(nn->string);
241020fa2881Sschwarze 	if (cp) {
241120fa2881Sschwarze 		free(mdoc->meta.vol);
241220fa2881Sschwarze 		mdoc->meta.vol = mandoc_strdup(cp);
241320fa2881Sschwarze 	} else {
241420fa2881Sschwarze 		cp = mdoc_a2arch(nn->string);
241520fa2881Sschwarze 		if (NULL == cp) {
2416bd594191Sschwarze 			mandoc_vmsg(MANDOCERR_ARCH_BAD, mdoc->parse,
2417bd594191Sschwarze 			    nn->line, nn->pos, "Dt ... %s", nn->string);
241820fa2881Sschwarze 			free(mdoc->meta.vol);
241920fa2881Sschwarze 			mdoc->meta.vol = mandoc_strdup(nn->string);
242020fa2881Sschwarze 		} else
242120fa2881Sschwarze 			mdoc->meta.arch = mandoc_strdup(cp);
242220fa2881Sschwarze 	}
242320fa2881Sschwarze 
242420fa2881Sschwarze 	/* Ignore any subsequent parameters... */
242520fa2881Sschwarze 	/* FIXME: warn about subsequent parameters. */
24263fdead0cSschwarze out:
24273fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
242820fa2881Sschwarze 	return(1);
242920fa2881Sschwarze }
243020fa2881Sschwarze 
243120fa2881Sschwarze static int
2432992063deSschwarze post_bx(POST_ARGS)
2433992063deSschwarze {
2434992063deSschwarze 	struct mdoc_node	*n;
2435992063deSschwarze 
2436992063deSschwarze 	/*
2437992063deSschwarze 	 * Make `Bx's second argument always start with an uppercase
2438992063deSschwarze 	 * letter.  Groff checks if it's an "accepted" term, but we just
2439992063deSschwarze 	 * uppercase blindly.
2440992063deSschwarze 	 */
2441992063deSschwarze 
2442992063deSschwarze 	n = mdoc->last->child;
2443992063deSschwarze 	if (n && NULL != (n = n->next))
244449aff9f8Sschwarze 		*n->string = (char)toupper((unsigned char)*n->string);
2445992063deSschwarze 
2446992063deSschwarze 	return(1);
2447992063deSschwarze }
2448992063deSschwarze 
2449992063deSschwarze static int
245020fa2881Sschwarze post_os(POST_ARGS)
245120fa2881Sschwarze {
245220fa2881Sschwarze #ifndef OSNAME
245320fa2881Sschwarze 	struct utsname	  utsname;
24544c468128Sschwarze 	static char	 *defbuf;
245520fa2881Sschwarze #endif
24564c468128Sschwarze 	struct mdoc_node *n;
245720fa2881Sschwarze 
245820fa2881Sschwarze 	n = mdoc->last;
245920fa2881Sschwarze 
246020fa2881Sschwarze 	/*
2461353fa9ecSschwarze 	 * Set the operating system by way of the `Os' macro.
2462353fa9ecSschwarze 	 * The order of precedence is:
2463353fa9ecSschwarze 	 * 1. the argument of the `Os' macro, unless empty
2464353fa9ecSschwarze 	 * 2. the -Ios=foo command line argument, if provided
2465353fa9ecSschwarze 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2466353fa9ecSschwarze 	 * 4. "sysname release" from uname(3)
246720fa2881Sschwarze 	 */
246820fa2881Sschwarze 
246920fa2881Sschwarze 	free(mdoc->meta.os);
247083af2bccSschwarze 	mdoc->meta.os = NULL;
247183af2bccSschwarze 	mdoc_deroff(&mdoc->meta.os, n);
247283af2bccSschwarze 	if (mdoc->meta.os)
24733fdead0cSschwarze 		goto out;
24744c468128Sschwarze 
2475353fa9ecSschwarze 	if (mdoc->defos) {
2476353fa9ecSschwarze 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
24773fdead0cSschwarze 		goto out;
2478353fa9ecSschwarze 	}
24794c468128Sschwarze 
248020fa2881Sschwarze #ifdef OSNAME
24814c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(OSNAME);
248220fa2881Sschwarze #else /*!OSNAME */
24834c468128Sschwarze 	if (NULL == defbuf) {
2484a35fc07aSschwarze 		if (-1 == uname(&utsname)) {
2485f79e7afeSschwarze 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2486f79e7afeSschwarze 			    n->line, n->pos, "Os");
24874c468128Sschwarze 			defbuf = mandoc_strdup("UNKNOWN");
2488a450f7c4Sschwarze 		} else
2489a450f7c4Sschwarze 			mandoc_asprintf(&defbuf, "%s %s",
2490a450f7c4Sschwarze 			    utsname.sysname, utsname.release);
249120fa2881Sschwarze 	}
24924c468128Sschwarze 	mdoc->meta.os = mandoc_strdup(defbuf);
249320fa2881Sschwarze #endif /*!OSNAME*/
24943fdead0cSschwarze 
24953fdead0cSschwarze out:
24963fdead0cSschwarze 	mdoc_node_delete(mdoc, n);
24973fdead0cSschwarze 	return(1);
249820fa2881Sschwarze }
249920fa2881Sschwarze 
2500e214f641Sschwarze /*
2501e214f641Sschwarze  * If no argument is provided,
2502e214f641Sschwarze  * fill in the name of the current manual page.
2503e214f641Sschwarze  */
250420fa2881Sschwarze static int
2505e214f641Sschwarze post_ex(POST_ARGS)
250620fa2881Sschwarze {
2507e214f641Sschwarze 	struct mdoc_node *n;
250820fa2881Sschwarze 
250920fa2881Sschwarze 	n = mdoc->last;
251020fa2881Sschwarze 
251120fa2881Sschwarze 	if (n->child)
251220fa2881Sschwarze 		return(1);
251320fa2881Sschwarze 
2514e214f641Sschwarze 	if (mdoc->meta.name == NULL) {
2515bd594191Sschwarze 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2516bd594191Sschwarze 		    n->line, n->pos, "Ex");
251720fa2881Sschwarze 		return(1);
2518e214f641Sschwarze 	}
251920fa2881Sschwarze 
252020fa2881Sschwarze 	mdoc->next = MDOC_NEXT_CHILD;
252120fa2881Sschwarze 
252220fa2881Sschwarze 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
252320fa2881Sschwarze 		return(0);
252420fa2881Sschwarze 
2525e214f641Sschwarze 	mdoc->last = n;
252620fa2881Sschwarze 	return(1);
252720fa2881Sschwarze }
252820fa2881Sschwarze 
252919a69263Sschwarze static enum mdoc_sec
253019a69263Sschwarze a2sec(const char *p)
253119a69263Sschwarze {
253219a69263Sschwarze 	int		 i;
253319a69263Sschwarze 
253419a69263Sschwarze 	for (i = 0; i < (int)SEC__MAX; i++)
253519a69263Sschwarze 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
253619a69263Sschwarze 			return((enum mdoc_sec)i);
253719a69263Sschwarze 
253819a69263Sschwarze 	return(SEC_CUSTOM);
253919a69263Sschwarze }
254019a69263Sschwarze 
254119a69263Sschwarze static size_t
254219a69263Sschwarze macro2len(enum mdoct macro)
254319a69263Sschwarze {
254419a69263Sschwarze 
254519a69263Sschwarze 	switch (macro) {
254649aff9f8Sschwarze 	case MDOC_Ad:
254719a69263Sschwarze 		return(12);
254849aff9f8Sschwarze 	case MDOC_Ao:
254919a69263Sschwarze 		return(12);
255049aff9f8Sschwarze 	case MDOC_An:
255119a69263Sschwarze 		return(12);
255249aff9f8Sschwarze 	case MDOC_Aq:
255319a69263Sschwarze 		return(12);
255449aff9f8Sschwarze 	case MDOC_Ar:
255519a69263Sschwarze 		return(12);
255649aff9f8Sschwarze 	case MDOC_Bo:
255719a69263Sschwarze 		return(12);
255849aff9f8Sschwarze 	case MDOC_Bq:
255919a69263Sschwarze 		return(12);
256049aff9f8Sschwarze 	case MDOC_Cd:
256119a69263Sschwarze 		return(12);
256249aff9f8Sschwarze 	case MDOC_Cm:
256319a69263Sschwarze 		return(10);
256449aff9f8Sschwarze 	case MDOC_Do:
256519a69263Sschwarze 		return(10);
256649aff9f8Sschwarze 	case MDOC_Dq:
256719a69263Sschwarze 		return(12);
256849aff9f8Sschwarze 	case MDOC_Dv:
256919a69263Sschwarze 		return(12);
257049aff9f8Sschwarze 	case MDOC_Eo:
257119a69263Sschwarze 		return(12);
257249aff9f8Sschwarze 	case MDOC_Em:
257319a69263Sschwarze 		return(10);
257449aff9f8Sschwarze 	case MDOC_Er:
257519a69263Sschwarze 		return(17);
257649aff9f8Sschwarze 	case MDOC_Ev:
257719a69263Sschwarze 		return(15);
257849aff9f8Sschwarze 	case MDOC_Fa:
257919a69263Sschwarze 		return(12);
258049aff9f8Sschwarze 	case MDOC_Fl:
258119a69263Sschwarze 		return(10);
258249aff9f8Sschwarze 	case MDOC_Fo:
258319a69263Sschwarze 		return(16);
258449aff9f8Sschwarze 	case MDOC_Fn:
258519a69263Sschwarze 		return(16);
258649aff9f8Sschwarze 	case MDOC_Ic:
258719a69263Sschwarze 		return(10);
258849aff9f8Sschwarze 	case MDOC_Li:
258919a69263Sschwarze 		return(16);
259049aff9f8Sschwarze 	case MDOC_Ms:
259119a69263Sschwarze 		return(6);
259249aff9f8Sschwarze 	case MDOC_Nm:
259319a69263Sschwarze 		return(10);
259449aff9f8Sschwarze 	case MDOC_No:
259519a69263Sschwarze 		return(12);
259649aff9f8Sschwarze 	case MDOC_Oo:
259719a69263Sschwarze 		return(10);
259849aff9f8Sschwarze 	case MDOC_Op:
259919a69263Sschwarze 		return(14);
260049aff9f8Sschwarze 	case MDOC_Pa:
260119a69263Sschwarze 		return(32);
260249aff9f8Sschwarze 	case MDOC_Pf:
260319a69263Sschwarze 		return(12);
260449aff9f8Sschwarze 	case MDOC_Po:
260519a69263Sschwarze 		return(12);
260649aff9f8Sschwarze 	case MDOC_Pq:
260719a69263Sschwarze 		return(12);
260849aff9f8Sschwarze 	case MDOC_Ql:
260919a69263Sschwarze 		return(16);
261049aff9f8Sschwarze 	case MDOC_Qo:
261119a69263Sschwarze 		return(12);
261249aff9f8Sschwarze 	case MDOC_So:
261319a69263Sschwarze 		return(12);
261449aff9f8Sschwarze 	case MDOC_Sq:
261519a69263Sschwarze 		return(12);
261649aff9f8Sschwarze 	case MDOC_Sy:
261719a69263Sschwarze 		return(6);
261849aff9f8Sschwarze 	case MDOC_Sx:
261919a69263Sschwarze 		return(16);
262049aff9f8Sschwarze 	case MDOC_Tn:
262119a69263Sschwarze 		return(10);
262249aff9f8Sschwarze 	case MDOC_Va:
262319a69263Sschwarze 		return(12);
262449aff9f8Sschwarze 	case MDOC_Vt:
262519a69263Sschwarze 		return(12);
262649aff9f8Sschwarze 	case MDOC_Xr:
262719a69263Sschwarze 		return(10);
262819a69263Sschwarze 	default:
262919a69263Sschwarze 		break;
263019a69263Sschwarze 	};
263119a69263Sschwarze 	return(0);
263219a69263Sschwarze }
2633