xref: /illumos-gate/usr/src/cmd/mandoc/mdoc_validate.c (revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f)
1*260e9a87SYuri Pankov /*	$Id: mdoc_validate.c,v 1.283 2015/02/23 13:55:55 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
5*260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
695c635efSGarrett D'Amore  *
795c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
895c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
995c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1095c635efSGarrett D'Amore  *
1195c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1295c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1395c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1495c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1595c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1695c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1795c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1895c635efSGarrett D'Amore  */
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore 
21*260e9a87SYuri Pankov #include <sys/types.h>
22698f87a4SGarrett D'Amore #ifndef OSNAME
2395c635efSGarrett D'Amore #include <sys/utsname.h>
2495c635efSGarrett D'Amore #endif
2595c635efSGarrett D'Amore 
2695c635efSGarrett D'Amore #include <assert.h>
2795c635efSGarrett D'Amore #include <ctype.h>
2895c635efSGarrett D'Amore #include <limits.h>
2995c635efSGarrett D'Amore #include <stdio.h>
3095c635efSGarrett D'Amore #include <stdlib.h>
3195c635efSGarrett D'Amore #include <string.h>
3295c635efSGarrett D'Amore #include <time.h>
3395c635efSGarrett D'Amore 
3495c635efSGarrett D'Amore #include "mdoc.h"
3595c635efSGarrett D'Amore #include "mandoc.h"
36*260e9a87SYuri Pankov #include "mandoc_aux.h"
3795c635efSGarrett D'Amore #include "libmdoc.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
3995c635efSGarrett D'Amore 
4095c635efSGarrett D'Amore /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4195c635efSGarrett D'Amore 
4295c635efSGarrett D'Amore #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
4395c635efSGarrett D'Amore #define	POST_ARGS struct mdoc *mdoc
4495c635efSGarrett D'Amore 
4595c635efSGarrett D'Amore enum	check_ineq {
4695c635efSGarrett D'Amore 	CHECK_LT,
4795c635efSGarrett D'Amore 	CHECK_GT,
4895c635efSGarrett D'Amore 	CHECK_EQ
4995c635efSGarrett D'Amore };
5095c635efSGarrett D'Amore 
51*260e9a87SYuri Pankov typedef	void	(*v_pre)(PRE_ARGS);
52*260e9a87SYuri Pankov typedef	void	(*v_post)(POST_ARGS);
5395c635efSGarrett D'Amore 
5495c635efSGarrett D'Amore struct	valids {
55*260e9a87SYuri Pankov 	v_pre	 pre;
56*260e9a87SYuri Pankov 	v_post	 post;
5795c635efSGarrett D'Amore };
5895c635efSGarrett D'Amore 
5995c635efSGarrett D'Amore static	void	 check_text(struct mdoc *, int, int, char *);
60*260e9a87SYuri Pankov static	void	 check_argv(struct mdoc *,
6195c635efSGarrett D'Amore 			struct mdoc_node *, struct mdoc_argv *);
6295c635efSGarrett D'Amore static	void	 check_args(struct mdoc *, struct mdoc_node *);
63*260e9a87SYuri Pankov static	int	 child_an(const struct mdoc_node *);
6495c635efSGarrett D'Amore static	enum mdoc_sec	a2sec(const char *);
6595c635efSGarrett D'Amore static	size_t		macro2len(enum mdoct);
66*260e9a87SYuri Pankov static	void	 rewrite_macro2len(char **);
67*260e9a87SYuri Pankov 
68*260e9a87SYuri Pankov static	void	 post_an(POST_ARGS);
69*260e9a87SYuri Pankov static	void	 post_at(POST_ARGS);
70*260e9a87SYuri Pankov static	void	 post_bf(POST_ARGS);
71*260e9a87SYuri Pankov static	void	 post_bk(POST_ARGS);
72*260e9a87SYuri Pankov static	void	 post_bl(POST_ARGS);
73*260e9a87SYuri Pankov static	void	 post_bl_block(POST_ARGS);
74*260e9a87SYuri Pankov static	void	 post_bl_block_tag(POST_ARGS);
75*260e9a87SYuri Pankov static	void	 post_bl_head(POST_ARGS);
76*260e9a87SYuri Pankov static	void	 post_bx(POST_ARGS);
77*260e9a87SYuri Pankov static	void	 post_d1(POST_ARGS);
78*260e9a87SYuri Pankov static	void	 post_defaults(POST_ARGS);
79*260e9a87SYuri Pankov static	void	 post_dd(POST_ARGS);
80*260e9a87SYuri Pankov static	void	 post_dt(POST_ARGS);
81*260e9a87SYuri Pankov static	void	 post_en(POST_ARGS);
82*260e9a87SYuri Pankov static	void	 post_es(POST_ARGS);
83*260e9a87SYuri Pankov static	void	 post_eoln(POST_ARGS);
84*260e9a87SYuri Pankov static	void	 post_ex(POST_ARGS);
85*260e9a87SYuri Pankov static	void	 post_fa(POST_ARGS);
86*260e9a87SYuri Pankov static	void	 post_fn(POST_ARGS);
87*260e9a87SYuri Pankov static	void	 post_fname(POST_ARGS);
88*260e9a87SYuri Pankov static	void	 post_fo(POST_ARGS);
89*260e9a87SYuri Pankov static	void	 post_hyph(POST_ARGS);
90*260e9a87SYuri Pankov static	void	 post_ignpar(POST_ARGS);
91*260e9a87SYuri Pankov static	void	 post_it(POST_ARGS);
92*260e9a87SYuri Pankov static	void	 post_lb(POST_ARGS);
93*260e9a87SYuri Pankov static	void	 post_literal(POST_ARGS);
94*260e9a87SYuri Pankov static	void	 post_nd(POST_ARGS);
95*260e9a87SYuri Pankov static	void	 post_nm(POST_ARGS);
96*260e9a87SYuri Pankov static	void	 post_ns(POST_ARGS);
97*260e9a87SYuri Pankov static	void	 post_os(POST_ARGS);
98*260e9a87SYuri Pankov static	void	 post_par(POST_ARGS);
99*260e9a87SYuri Pankov static	void	 post_root(POST_ARGS);
100*260e9a87SYuri Pankov static	void	 post_rs(POST_ARGS);
101*260e9a87SYuri Pankov static	void	 post_sh(POST_ARGS);
102*260e9a87SYuri Pankov static	void	 post_sh_head(POST_ARGS);
103*260e9a87SYuri Pankov static	void	 post_sh_name(POST_ARGS);
104*260e9a87SYuri Pankov static	void	 post_sh_see_also(POST_ARGS);
105*260e9a87SYuri Pankov static	void	 post_sh_authors(POST_ARGS);
106*260e9a87SYuri Pankov static	void	 post_sm(POST_ARGS);
107*260e9a87SYuri Pankov static	void	 post_st(POST_ARGS);
108*260e9a87SYuri Pankov static	void	 post_vt(POST_ARGS);
109*260e9a87SYuri Pankov 
110*260e9a87SYuri Pankov static	void	 pre_an(PRE_ARGS);
111*260e9a87SYuri Pankov static	void	 pre_bd(PRE_ARGS);
112*260e9a87SYuri Pankov static	void	 pre_bl(PRE_ARGS);
113*260e9a87SYuri Pankov static	void	 pre_dd(PRE_ARGS);
114*260e9a87SYuri Pankov static	void	 pre_display(PRE_ARGS);
115*260e9a87SYuri Pankov static	void	 pre_dt(PRE_ARGS);
116*260e9a87SYuri Pankov static	void	 pre_literal(PRE_ARGS);
117*260e9a87SYuri Pankov static	void	 pre_obsolete(PRE_ARGS);
118*260e9a87SYuri Pankov static	void	 pre_os(PRE_ARGS);
119*260e9a87SYuri Pankov static	void	 pre_par(PRE_ARGS);
120*260e9a87SYuri Pankov static	void	 pre_std(PRE_ARGS);
12195c635efSGarrett D'Amore 
12295c635efSGarrett D'Amore static	const struct valids mdoc_valids[MDOC_MAX] = {
12395c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ap */
124*260e9a87SYuri Pankov 	{ pre_dd, post_dd },			/* Dd */
125*260e9a87SYuri Pankov 	{ pre_dt, post_dt },			/* Dt */
126*260e9a87SYuri Pankov 	{ pre_os, post_os },			/* Os */
127*260e9a87SYuri Pankov 	{ NULL, post_sh },			/* Sh */
128*260e9a87SYuri Pankov 	{ NULL, post_ignpar },			/* Ss */
129*260e9a87SYuri Pankov 	{ pre_par, post_par },			/* Pp */
130*260e9a87SYuri Pankov 	{ pre_display, post_d1 },		/* D1 */
131*260e9a87SYuri Pankov 	{ pre_literal, post_literal },		/* Dl */
132*260e9a87SYuri Pankov 	{ pre_bd, post_literal },		/* Bd */
13395c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ed */
134*260e9a87SYuri Pankov 	{ pre_bl, post_bl },			/* Bl */
13595c635efSGarrett D'Amore 	{ NULL, NULL },				/* El */
136*260e9a87SYuri Pankov 	{ pre_par, post_it },			/* It */
137*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ad */
138*260e9a87SYuri Pankov 	{ pre_an, post_an },			/* An */
139*260e9a87SYuri Pankov 	{ NULL, post_defaults },		/* Ar */
140*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Cd */
14195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Cm */
142*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Dv */
143*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Er */
144*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ev */
145*260e9a87SYuri Pankov 	{ pre_std, post_ex },			/* Ex */
146*260e9a87SYuri Pankov 	{ NULL, post_fa },			/* Fa */
147*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Fd */
14895c635efSGarrett D'Amore 	{ NULL, NULL },				/* Fl */
149*260e9a87SYuri Pankov 	{ NULL, post_fn },			/* Fn */
150*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ft */
151*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ic */
152*260e9a87SYuri Pankov 	{ NULL, NULL },				/* In */
153*260e9a87SYuri Pankov 	{ NULL, post_defaults },		/* Li */
154*260e9a87SYuri Pankov 	{ NULL, post_nd },			/* Nd */
155*260e9a87SYuri Pankov 	{ NULL, post_nm },			/* Nm */
15695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Op */
157*260e9a87SYuri Pankov 	{ pre_obsolete, NULL },			/* Ot */
158*260e9a87SYuri Pankov 	{ NULL, post_defaults },		/* Pa */
159*260e9a87SYuri Pankov 	{ pre_std, NULL },			/* Rv */
160*260e9a87SYuri Pankov 	{ NULL, post_st },			/* St */
16195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Va */
162*260e9a87SYuri Pankov 	{ NULL, post_vt },			/* Vt */
163*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Xr */
164*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %A */
165*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* %B */ /* FIXME: can be used outside Rs/Re. */
166*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %D */
167*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %I */
168*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %J */
169*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* %N */
170*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* %O */
171*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %P */
172*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* %R */
173*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* %T */ /* FIXME: can be used outside Rs/Re. */
174*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %V */
17595c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ac */
17695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ao */
17795c635efSGarrett D'Amore 	{ NULL, NULL },				/* Aq */
178*260e9a87SYuri Pankov 	{ NULL, post_at },			/* At */
17995c635efSGarrett D'Amore 	{ NULL, NULL },				/* Bc */
180*260e9a87SYuri Pankov 	{ NULL, post_bf },			/* Bf */
18195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Bo */
18295c635efSGarrett D'Amore 	{ NULL, NULL },				/* Bq */
18395c635efSGarrett D'Amore 	{ NULL, NULL },				/* Bsx */
184*260e9a87SYuri Pankov 	{ NULL, post_bx },			/* Bx */
185*260e9a87SYuri Pankov 	{ pre_obsolete, NULL },			/* Db */
18695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Dc */
18795c635efSGarrett D'Amore 	{ NULL, NULL },				/* Do */
18895c635efSGarrett D'Amore 	{ NULL, NULL },				/* Dq */
18995c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ec */
190*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ef */
191*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Em */
19295c635efSGarrett D'Amore 	{ NULL, NULL },				/* Eo */
19395c635efSGarrett D'Amore 	{ NULL, NULL },				/* Fx */
194*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Ms */
195*260e9a87SYuri Pankov 	{ NULL, NULL },				/* No */
196*260e9a87SYuri Pankov 	{ NULL, post_ns },			/* Ns */
19795c635efSGarrett D'Amore 	{ NULL, NULL },				/* Nx */
19895c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ox */
19995c635efSGarrett D'Amore 	{ NULL, NULL },				/* Pc */
200*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Pf */
20195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Po */
20295c635efSGarrett D'Amore 	{ NULL, NULL },				/* Pq */
20395c635efSGarrett D'Amore 	{ NULL, NULL },				/* Qc */
20495c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ql */
20595c635efSGarrett D'Amore 	{ NULL, NULL },				/* Qo */
20695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Qq */
20795c635efSGarrett D'Amore 	{ NULL, NULL },				/* Re */
208*260e9a87SYuri Pankov 	{ NULL, post_rs },			/* Rs */
20995c635efSGarrett D'Amore 	{ NULL, NULL },				/* Sc */
21095c635efSGarrett D'Amore 	{ NULL, NULL },				/* So */
21195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Sq */
212*260e9a87SYuri Pankov 	{ NULL, post_sm },			/* Sm */
213*260e9a87SYuri Pankov 	{ NULL, post_hyph },			/* Sx */
21495c635efSGarrett D'Amore 	{ NULL, NULL },				/* Sy */
21595c635efSGarrett D'Amore 	{ NULL, NULL },				/* Tn */
21695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ux */
21795c635efSGarrett D'Amore 	{ NULL, NULL },				/* Xc */
21895c635efSGarrett D'Amore 	{ NULL, NULL },				/* Xo */
219*260e9a87SYuri Pankov 	{ NULL, post_fo },			/* Fo */
220*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Fc */
22195c635efSGarrett D'Amore 	{ NULL, NULL },				/* Oo */
22295c635efSGarrett D'Amore 	{ NULL, NULL },				/* Oc */
223*260e9a87SYuri Pankov 	{ NULL, post_bk },			/* Bk */
22495c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ek */
225*260e9a87SYuri Pankov 	{ NULL, post_eoln },			/* Bt */
22695c635efSGarrett D'Amore 	{ NULL, NULL },				/* Hf */
227*260e9a87SYuri Pankov 	{ pre_obsolete, NULL },			/* Fr */
228*260e9a87SYuri Pankov 	{ NULL, post_eoln },			/* Ud */
229*260e9a87SYuri Pankov 	{ NULL, post_lb },			/* Lb */
230*260e9a87SYuri Pankov 	{ pre_par, post_par },			/* Lp */
231*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Lk */
232*260e9a87SYuri Pankov 	{ NULL, post_defaults },		/* Mt */
233*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Brq */
234*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Bro */
235*260e9a87SYuri Pankov 	{ NULL, NULL },				/* Brc */
236*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %C */
237*260e9a87SYuri Pankov 	{ pre_obsolete, post_es },		/* Es */
238*260e9a87SYuri Pankov 	{ pre_obsolete, post_en },		/* En */
23995c635efSGarrett D'Amore 	{ NULL, NULL },				/* Dx */
240*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %Q */
241*260e9a87SYuri Pankov 	{ NULL, post_par },			/* br */
242*260e9a87SYuri Pankov 	{ NULL, post_par },			/* sp */
243*260e9a87SYuri Pankov 	{ NULL, NULL },				/* %U */
24495c635efSGarrett D'Amore 	{ NULL, NULL },				/* Ta */
245*260e9a87SYuri Pankov 	{ NULL, NULL },				/* ll */
24695c635efSGarrett D'Amore };
24795c635efSGarrett D'Amore 
24895c635efSGarrett D'Amore #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
24995c635efSGarrett D'Amore 
25095c635efSGarrett D'Amore static	const enum mdoct rsord[RSORD_MAX] = {
25195c635efSGarrett D'Amore 	MDOC__A,
25295c635efSGarrett D'Amore 	MDOC__T,
25395c635efSGarrett D'Amore 	MDOC__B,
25495c635efSGarrett D'Amore 	MDOC__I,
25595c635efSGarrett D'Amore 	MDOC__J,
25695c635efSGarrett D'Amore 	MDOC__R,
25795c635efSGarrett D'Amore 	MDOC__N,
25895c635efSGarrett D'Amore 	MDOC__V,
259698f87a4SGarrett D'Amore 	MDOC__U,
26095c635efSGarrett D'Amore 	MDOC__P,
26195c635efSGarrett D'Amore 	MDOC__Q,
26295c635efSGarrett D'Amore 	MDOC__C,
263698f87a4SGarrett D'Amore 	MDOC__D,
264698f87a4SGarrett D'Amore 	MDOC__O
26595c635efSGarrett D'Amore };
26695c635efSGarrett D'Amore 
26795c635efSGarrett D'Amore static	const char * const secnames[SEC__MAX] = {
26895c635efSGarrett D'Amore 	NULL,
26995c635efSGarrett D'Amore 	"NAME",
27095c635efSGarrett D'Amore 	"LIBRARY",
27195c635efSGarrett D'Amore 	"SYNOPSIS",
27295c635efSGarrett D'Amore 	"DESCRIPTION",
273*260e9a87SYuri Pankov 	"CONTEXT",
27495c635efSGarrett D'Amore 	"IMPLEMENTATION NOTES",
27595c635efSGarrett D'Amore 	"RETURN VALUES",
27695c635efSGarrett D'Amore 	"ENVIRONMENT",
27795c635efSGarrett D'Amore 	"FILES",
27895c635efSGarrett D'Amore 	"EXIT STATUS",
27995c635efSGarrett D'Amore 	"EXAMPLES",
28095c635efSGarrett D'Amore 	"DIAGNOSTICS",
28195c635efSGarrett D'Amore 	"COMPATIBILITY",
28295c635efSGarrett D'Amore 	"ERRORS",
28395c635efSGarrett D'Amore 	"SEE ALSO",
28495c635efSGarrett D'Amore 	"STANDARDS",
28595c635efSGarrett D'Amore 	"HISTORY",
28695c635efSGarrett D'Amore 	"AUTHORS",
28795c635efSGarrett D'Amore 	"CAVEATS",
28895c635efSGarrett D'Amore 	"BUGS",
28995c635efSGarrett D'Amore 	"SECURITY CONSIDERATIONS",
29095c635efSGarrett D'Amore 	NULL
29195c635efSGarrett D'Amore };
29295c635efSGarrett D'Amore 
293*260e9a87SYuri Pankov 
294*260e9a87SYuri Pankov void
29595c635efSGarrett D'Amore mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
29695c635efSGarrett D'Amore {
297*260e9a87SYuri Pankov 	v_pre	 p;
29895c635efSGarrett D'Amore 
29995c635efSGarrett D'Amore 	switch (n->type) {
300*260e9a87SYuri Pankov 	case MDOC_TEXT:
301*260e9a87SYuri Pankov 		if (n->sec != SEC_SYNOPSIS || n->parent->tok != MDOC_Fd)
302*260e9a87SYuri Pankov 			check_text(mdoc, n->line, n->pos, n->string);
30395c635efSGarrett D'Amore 		/* FALLTHROUGH */
304*260e9a87SYuri Pankov 	case MDOC_TBL:
30595c635efSGarrett D'Amore 		/* FALLTHROUGH */
306*260e9a87SYuri Pankov 	case MDOC_EQN:
30795c635efSGarrett D'Amore 		/* FALLTHROUGH */
308*260e9a87SYuri Pankov 	case MDOC_ROOT:
309*260e9a87SYuri Pankov 		return;
31095c635efSGarrett D'Amore 	default:
31195c635efSGarrett D'Amore 		break;
31295c635efSGarrett D'Amore 	}
31395c635efSGarrett D'Amore 
31495c635efSGarrett D'Amore 	check_args(mdoc, n);
315*260e9a87SYuri Pankov 	p = mdoc_valids[n->tok].pre;
316*260e9a87SYuri Pankov 	if (*p)
317*260e9a87SYuri Pankov 		(*p)(mdoc, n);
31895c635efSGarrett D'Amore }
31995c635efSGarrett D'Amore 
320*260e9a87SYuri Pankov void
32195c635efSGarrett D'Amore mdoc_valid_post(struct mdoc *mdoc)
32295c635efSGarrett D'Amore {
323*260e9a87SYuri Pankov 	struct mdoc_node *n;
324*260e9a87SYuri Pankov 	v_post p;
32595c635efSGarrett D'Amore 
326*260e9a87SYuri Pankov 	n = mdoc->last;
327*260e9a87SYuri Pankov 	if (n->flags & MDOC_VALID)
328*260e9a87SYuri Pankov 		return;
329*260e9a87SYuri Pankov 	n->flags |= MDOC_VALID | MDOC_ENDED;
33095c635efSGarrett D'Amore 
331*260e9a87SYuri Pankov 	switch (n->type) {
332*260e9a87SYuri Pankov 	case MDOC_TEXT:
33395c635efSGarrett D'Amore 		/* FALLTHROUGH */
334*260e9a87SYuri Pankov 	case MDOC_EQN:
33595c635efSGarrett D'Amore 		/* FALLTHROUGH */
336*260e9a87SYuri Pankov 	case MDOC_TBL:
33795c635efSGarrett D'Amore 		break;
338*260e9a87SYuri Pankov 	case MDOC_ROOT:
339*260e9a87SYuri Pankov 		post_root(mdoc);
34095c635efSGarrett D'Amore 		break;
34195c635efSGarrett D'Amore 	default:
34295c635efSGarrett D'Amore 
343*260e9a87SYuri Pankov 		/*
344*260e9a87SYuri Pankov 		 * Closing delimiters are not special at the
345*260e9a87SYuri Pankov 		 * beginning of a block, opening delimiters
346*260e9a87SYuri Pankov 		 * are not special at the end.
347*260e9a87SYuri Pankov 		 */
34895c635efSGarrett D'Amore 
349*260e9a87SYuri Pankov 		if (n->child != NULL)
350*260e9a87SYuri Pankov 			n->child->flags &= ~MDOC_DELIMC;
351*260e9a87SYuri Pankov 		if (n->last != NULL)
352*260e9a87SYuri Pankov 			n->last->flags &= ~MDOC_DELIMO;
35395c635efSGarrett D'Amore 
354*260e9a87SYuri Pankov 		/* Call the macro's postprocessor. */
35595c635efSGarrett D'Amore 
356*260e9a87SYuri Pankov 		p = mdoc_valids[n->tok].post;
357*260e9a87SYuri Pankov 		if (*p)
358*260e9a87SYuri Pankov 			(*p)(mdoc);
359*260e9a87SYuri Pankov 		break;
360*260e9a87SYuri Pankov 	}
36195c635efSGarrett D'Amore }
36295c635efSGarrett D'Amore 
36395c635efSGarrett D'Amore static void
364698f87a4SGarrett D'Amore check_args(struct mdoc *mdoc, struct mdoc_node *n)
36595c635efSGarrett D'Amore {
36695c635efSGarrett D'Amore 	int		 i;
36795c635efSGarrett D'Amore 
36895c635efSGarrett D'Amore 	if (NULL == n->args)
36995c635efSGarrett D'Amore 		return;
37095c635efSGarrett D'Amore 
37195c635efSGarrett D'Amore 	assert(n->args->argc);
37295c635efSGarrett D'Amore 	for (i = 0; i < (int)n->args->argc; i++)
373698f87a4SGarrett D'Amore 		check_argv(mdoc, n, &n->args->argv[i]);
37495c635efSGarrett D'Amore }
37595c635efSGarrett D'Amore 
37695c635efSGarrett D'Amore static void
377698f87a4SGarrett D'Amore check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
37895c635efSGarrett D'Amore {
37995c635efSGarrett D'Amore 	int		 i;
38095c635efSGarrett D'Amore 
38195c635efSGarrett D'Amore 	for (i = 0; i < (int)v->sz; i++)
382698f87a4SGarrett D'Amore 		check_text(mdoc, v->line, v->pos, v->value[i]);
38395c635efSGarrett D'Amore }
38495c635efSGarrett D'Amore 
38595c635efSGarrett D'Amore static void
386698f87a4SGarrett D'Amore check_text(struct mdoc *mdoc, int ln, int pos, char *p)
38795c635efSGarrett D'Amore {
38895c635efSGarrett D'Amore 	char		*cp;
38995c635efSGarrett D'Amore 
390698f87a4SGarrett D'Amore 	if (MDOC_LITERAL & mdoc->flags)
39195c635efSGarrett D'Amore 		return;
39295c635efSGarrett D'Amore 
39395c635efSGarrett D'Amore 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
394*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
395*260e9a87SYuri Pankov 		    ln, pos + (int)(p - cp), NULL);
39695c635efSGarrett D'Amore }
39795c635efSGarrett D'Amore 
398*260e9a87SYuri Pankov static void
39995c635efSGarrett D'Amore pre_display(PRE_ARGS)
40095c635efSGarrett D'Amore {
40195c635efSGarrett D'Amore 	struct mdoc_node *node;
40295c635efSGarrett D'Amore 
40395c635efSGarrett D'Amore 	if (MDOC_BLOCK != n->type)
404*260e9a87SYuri Pankov 		return;
40595c635efSGarrett D'Amore 
406*260e9a87SYuri Pankov 	for (node = mdoc->last->parent; node; node = node->parent)
40795c635efSGarrett D'Amore 		if (MDOC_BLOCK == node->type)
40895c635efSGarrett D'Amore 			if (MDOC_Bd == node->tok)
40995c635efSGarrett D'Amore 				break;
41095c635efSGarrett D'Amore 
41195c635efSGarrett D'Amore 	if (node)
412*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_BD_NEST,
413*260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
414*260e9a87SYuri Pankov 		    "%s in Bd", mdoc_macronames[n->tok]);
41595c635efSGarrett D'Amore }
41695c635efSGarrett D'Amore 
417*260e9a87SYuri Pankov static void
41895c635efSGarrett D'Amore pre_bl(PRE_ARGS)
41995c635efSGarrett D'Amore {
420*260e9a87SYuri Pankov 	struct mdoc_argv *argv, *wa;
421*260e9a87SYuri Pankov 	int		  i;
422*260e9a87SYuri Pankov 	enum mdocargt	  mdoclt;
42395c635efSGarrett D'Amore 	enum mdoc_list	  lt;
42495c635efSGarrett D'Amore 
425*260e9a87SYuri Pankov 	if (n->type != MDOC_BLOCK)
426*260e9a87SYuri Pankov 		return;
42795c635efSGarrett D'Amore 
428*260e9a87SYuri Pankov 	/*
42995c635efSGarrett D'Amore 	 * First figure out which kind of list to use: bind ourselves to
43095c635efSGarrett D'Amore 	 * the first mentioned list type and warn about any remaining
43195c635efSGarrett D'Amore 	 * ones.  If we find no list type, we default to LIST_item.
43295c635efSGarrett D'Amore 	 */
43395c635efSGarrett D'Amore 
434*260e9a87SYuri Pankov 	wa = (n->args == NULL) ? NULL : n->args->argv;
435*260e9a87SYuri Pankov 	mdoclt = MDOC_ARG_MAX;
43695c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
437*260e9a87SYuri Pankov 		argv = n->args->argv + i;
43895c635efSGarrett D'Amore 		lt = LIST__NONE;
439*260e9a87SYuri Pankov 		switch (argv->arg) {
44095c635efSGarrett D'Amore 		/* Set list types. */
441*260e9a87SYuri Pankov 		case MDOC_Bullet:
44295c635efSGarrett D'Amore 			lt = LIST_bullet;
44395c635efSGarrett D'Amore 			break;
444*260e9a87SYuri Pankov 		case MDOC_Dash:
44595c635efSGarrett D'Amore 			lt = LIST_dash;
44695c635efSGarrett D'Amore 			break;
447*260e9a87SYuri Pankov 		case MDOC_Enum:
44895c635efSGarrett D'Amore 			lt = LIST_enum;
44995c635efSGarrett D'Amore 			break;
450*260e9a87SYuri Pankov 		case MDOC_Hyphen:
45195c635efSGarrett D'Amore 			lt = LIST_hyphen;
45295c635efSGarrett D'Amore 			break;
453*260e9a87SYuri Pankov 		case MDOC_Item:
45495c635efSGarrett D'Amore 			lt = LIST_item;
45595c635efSGarrett D'Amore 			break;
456*260e9a87SYuri Pankov 		case MDOC_Tag:
45795c635efSGarrett D'Amore 			lt = LIST_tag;
45895c635efSGarrett D'Amore 			break;
459*260e9a87SYuri Pankov 		case MDOC_Diag:
46095c635efSGarrett D'Amore 			lt = LIST_diag;
46195c635efSGarrett D'Amore 			break;
462*260e9a87SYuri Pankov 		case MDOC_Hang:
46395c635efSGarrett D'Amore 			lt = LIST_hang;
46495c635efSGarrett D'Amore 			break;
465*260e9a87SYuri Pankov 		case MDOC_Ohang:
46695c635efSGarrett D'Amore 			lt = LIST_ohang;
46795c635efSGarrett D'Amore 			break;
468*260e9a87SYuri Pankov 		case MDOC_Inset:
46995c635efSGarrett D'Amore 			lt = LIST_inset;
47095c635efSGarrett D'Amore 			break;
471*260e9a87SYuri Pankov 		case MDOC_Column:
47295c635efSGarrett D'Amore 			lt = LIST_column;
47395c635efSGarrett D'Amore 			break;
47495c635efSGarrett D'Amore 		/* Set list arguments. */
475*260e9a87SYuri Pankov 		case MDOC_Compact:
476*260e9a87SYuri Pankov 			if (n->norm->Bl.comp)
477*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
478*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
479*260e9a87SYuri Pankov 				    argv->pos, "Bl -compact");
480*260e9a87SYuri Pankov 			n->norm->Bl.comp = 1;
48195c635efSGarrett D'Amore 			break;
482*260e9a87SYuri Pankov 		case MDOC_Width:
483*260e9a87SYuri Pankov 			wa = argv;
484*260e9a87SYuri Pankov 			if (0 == argv->sz) {
485*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
486*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
487*260e9a87SYuri Pankov 				    argv->pos, "Bl -width");
488*260e9a87SYuri Pankov 				n->norm->Bl.width = "0n";
48995c635efSGarrett D'Amore 				break;
49095c635efSGarrett D'Amore 			}
491*260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.width)
492*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
493*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
494*260e9a87SYuri Pankov 				    argv->pos, "Bl -width %s",
495*260e9a87SYuri Pankov 				    argv->value[0]);
496*260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
497*260e9a87SYuri Pankov 			n->norm->Bl.width = argv->value[0];
49895c635efSGarrett D'Amore 			break;
499*260e9a87SYuri Pankov 		case MDOC_Offset:
500*260e9a87SYuri Pankov 			if (0 == argv->sz) {
501*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
502*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
503*260e9a87SYuri Pankov 				    argv->pos, "Bl -offset");
50495c635efSGarrett D'Amore 				break;
50595c635efSGarrett D'Amore 			}
506*260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.offs)
507*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
508*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
509*260e9a87SYuri Pankov 				    argv->pos, "Bl -offset %s",
510*260e9a87SYuri Pankov 				    argv->value[0]);
511*260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
512*260e9a87SYuri Pankov 			n->norm->Bl.offs = argv->value[0];
51395c635efSGarrett D'Amore 			break;
51495c635efSGarrett D'Amore 		default:
51595c635efSGarrett D'Amore 			continue;
51695c635efSGarrett D'Amore 		}
517*260e9a87SYuri Pankov 		if (LIST__NONE == lt)
518*260e9a87SYuri Pankov 			continue;
519*260e9a87SYuri Pankov 		mdoclt = argv->arg;
52095c635efSGarrett D'Amore 
52195c635efSGarrett D'Amore 		/* Check: multiple list types. */
52295c635efSGarrett D'Amore 
523*260e9a87SYuri Pankov 		if (LIST__NONE != n->norm->Bl.type) {
524*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_REP,
525*260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
526*260e9a87SYuri Pankov 			    "Bl -%s", mdoc_argnames[argv->arg]);
527*260e9a87SYuri Pankov 			continue;
52895c635efSGarrett D'Amore 		}
52995c635efSGarrett D'Amore 
53095c635efSGarrett D'Amore 		/* The list type should come first. */
53195c635efSGarrett D'Amore 
532*260e9a87SYuri Pankov 		if (n->norm->Bl.width ||
533*260e9a87SYuri Pankov 		    n->norm->Bl.offs ||
534*260e9a87SYuri Pankov 		    n->norm->Bl.comp)
535*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
536*260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos, "Bl -%s",
537*260e9a87SYuri Pankov 			    mdoc_argnames[n->args->argv[0].arg]);
538*260e9a87SYuri Pankov 
539*260e9a87SYuri Pankov 		n->norm->Bl.type = lt;
540*260e9a87SYuri Pankov 		if (LIST_column == lt) {
541*260e9a87SYuri Pankov 			n->norm->Bl.ncols = argv->sz;
542*260e9a87SYuri Pankov 			n->norm->Bl.cols = (void *)argv->value;
543*260e9a87SYuri Pankov 		}
54495c635efSGarrett D'Amore 	}
54595c635efSGarrett D'Amore 
54695c635efSGarrett D'Amore 	/* Allow lists to default to LIST_item. */
54795c635efSGarrett D'Amore 
54895c635efSGarrett D'Amore 	if (LIST__NONE == n->norm->Bl.type) {
549*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
550*260e9a87SYuri Pankov 		    n->line, n->pos, "Bl");
55195c635efSGarrett D'Amore 		n->norm->Bl.type = LIST_item;
55295c635efSGarrett D'Amore 	}
55395c635efSGarrett D'Amore 
554*260e9a87SYuri Pankov 	/*
55595c635efSGarrett D'Amore 	 * Validate the width field.  Some list types don't need width
55695c635efSGarrett D'Amore 	 * types and should be warned about them.  Others should have it
557698f87a4SGarrett D'Amore 	 * and must also be warned.  Yet others have a default and need
558698f87a4SGarrett D'Amore 	 * no warning.
55995c635efSGarrett D'Amore 	 */
56095c635efSGarrett D'Amore 
56195c635efSGarrett D'Amore 	switch (n->norm->Bl.type) {
562*260e9a87SYuri Pankov 	case LIST_tag:
563698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
564*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
565*260e9a87SYuri Pankov 			    n->line, n->pos, "Bl -tag");
56695c635efSGarrett D'Amore 		break;
567*260e9a87SYuri Pankov 	case LIST_column:
56895c635efSGarrett D'Amore 		/* FALLTHROUGH */
569*260e9a87SYuri Pankov 	case LIST_diag:
57095c635efSGarrett D'Amore 		/* FALLTHROUGH */
571*260e9a87SYuri Pankov 	case LIST_ohang:
57295c635efSGarrett D'Amore 		/* FALLTHROUGH */
573*260e9a87SYuri Pankov 	case LIST_inset:
57495c635efSGarrett D'Amore 		/* FALLTHROUGH */
575*260e9a87SYuri Pankov 	case LIST_item:
57695c635efSGarrett D'Amore 		if (n->norm->Bl.width)
577*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
578*260e9a87SYuri Pankov 			    wa->line, wa->pos, "Bl -%s",
579*260e9a87SYuri Pankov 			    mdoc_argnames[mdoclt]);
58095c635efSGarrett D'Amore 		break;
581*260e9a87SYuri Pankov 	case LIST_bullet:
582698f87a4SGarrett D'Amore 		/* FALLTHROUGH */
583*260e9a87SYuri Pankov 	case LIST_dash:
584698f87a4SGarrett D'Amore 		/* FALLTHROUGH */
585*260e9a87SYuri Pankov 	case LIST_hyphen:
586698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
587698f87a4SGarrett D'Amore 			n->norm->Bl.width = "2n";
588698f87a4SGarrett D'Amore 		break;
589*260e9a87SYuri Pankov 	case LIST_enum:
590698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
591698f87a4SGarrett D'Amore 			n->norm->Bl.width = "3n";
592698f87a4SGarrett D'Amore 		break;
59395c635efSGarrett D'Amore 	default:
59495c635efSGarrett D'Amore 		break;
59595c635efSGarrett D'Amore 	}
596*260e9a87SYuri Pankov 	pre_par(mdoc, n);
59795c635efSGarrett D'Amore }
59895c635efSGarrett D'Amore 
599*260e9a87SYuri Pankov static void
60095c635efSGarrett D'Amore pre_bd(PRE_ARGS)
60195c635efSGarrett D'Amore {
602*260e9a87SYuri Pankov 	struct mdoc_argv *argv;
603*260e9a87SYuri Pankov 	int		  i;
604*260e9a87SYuri Pankov 	enum mdoc_disp	  dt;
60595c635efSGarrett D'Amore 
606*260e9a87SYuri Pankov 	pre_literal(mdoc, n);
60795c635efSGarrett D'Amore 
608*260e9a87SYuri Pankov 	if (n->type != MDOC_BLOCK)
609*260e9a87SYuri Pankov 		return;
61095c635efSGarrett D'Amore 
61195c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
612*260e9a87SYuri Pankov 		argv = n->args->argv + i;
61395c635efSGarrett D'Amore 		dt = DISP__NONE;
61495c635efSGarrett D'Amore 
615*260e9a87SYuri Pankov 		switch (argv->arg) {
616*260e9a87SYuri Pankov 		case MDOC_Centred:
617*260e9a87SYuri Pankov 			dt = DISP_centered;
61895c635efSGarrett D'Amore 			break;
619*260e9a87SYuri Pankov 		case MDOC_Ragged:
62095c635efSGarrett D'Amore 			dt = DISP_ragged;
62195c635efSGarrett D'Amore 			break;
622*260e9a87SYuri Pankov 		case MDOC_Unfilled:
62395c635efSGarrett D'Amore 			dt = DISP_unfilled;
62495c635efSGarrett D'Amore 			break;
625*260e9a87SYuri Pankov 		case MDOC_Filled:
62695c635efSGarrett D'Amore 			dt = DISP_filled;
62795c635efSGarrett D'Amore 			break;
628*260e9a87SYuri Pankov 		case MDOC_Literal:
62995c635efSGarrett D'Amore 			dt = DISP_literal;
63095c635efSGarrett D'Amore 			break;
631*260e9a87SYuri Pankov 		case MDOC_File:
632*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
633*260e9a87SYuri Pankov 			    n->line, n->pos, NULL);
634*260e9a87SYuri Pankov 			break;
635*260e9a87SYuri Pankov 		case MDOC_Offset:
636*260e9a87SYuri Pankov 			if (0 == argv->sz) {
637*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
638*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
639*260e9a87SYuri Pankov 				    argv->pos, "Bd -offset");
64095c635efSGarrett D'Amore 				break;
64195c635efSGarrett D'Amore 			}
642*260e9a87SYuri Pankov 			if (NULL != n->norm->Bd.offs)
643*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
644*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
645*260e9a87SYuri Pankov 				    argv->pos, "Bd -offset %s",
646*260e9a87SYuri Pankov 				    argv->value[0]);
647*260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
648*260e9a87SYuri Pankov 			n->norm->Bd.offs = argv->value[0];
64995c635efSGarrett D'Amore 			break;
650*260e9a87SYuri Pankov 		case MDOC_Compact:
651*260e9a87SYuri Pankov 			if (n->norm->Bd.comp)
652*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
653*260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
654*260e9a87SYuri Pankov 				    argv->pos, "Bd -compact");
655*260e9a87SYuri Pankov 			n->norm->Bd.comp = 1;
65695c635efSGarrett D'Amore 			break;
65795c635efSGarrett D'Amore 		default:
65895c635efSGarrett D'Amore 			abort();
65995c635efSGarrett D'Amore 			/* NOTREACHED */
66095c635efSGarrett D'Amore 		}
661*260e9a87SYuri Pankov 		if (DISP__NONE == dt)
662*260e9a87SYuri Pankov 			continue;
66395c635efSGarrett D'Amore 
664*260e9a87SYuri Pankov 		if (DISP__NONE == n->norm->Bd.type)
66595c635efSGarrett D'Amore 			n->norm->Bd.type = dt;
666*260e9a87SYuri Pankov 		else
667*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BD_REP,
668*260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
669*260e9a87SYuri Pankov 			    "Bd -%s", mdoc_argnames[argv->arg]);
67095c635efSGarrett D'Amore 	}
67195c635efSGarrett D'Amore 
67295c635efSGarrett D'Amore 	if (DISP__NONE == n->norm->Bd.type) {
673*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
674*260e9a87SYuri Pankov 		    n->line, n->pos, "Bd");
67595c635efSGarrett D'Amore 		n->norm->Bd.type = DISP_ragged;
67695c635efSGarrett D'Amore 	}
677*260e9a87SYuri Pankov 	pre_par(mdoc, n);
67895c635efSGarrett D'Amore }
67995c635efSGarrett D'Amore 
680*260e9a87SYuri Pankov static void
68195c635efSGarrett D'Amore pre_an(PRE_ARGS)
68295c635efSGarrett D'Amore {
683*260e9a87SYuri Pankov 	struct mdoc_argv *argv;
684*260e9a87SYuri Pankov 	size_t	 i;
68595c635efSGarrett D'Amore 
686*260e9a87SYuri Pankov 	if (n->args == NULL)
687*260e9a87SYuri Pankov 		return;
68895c635efSGarrett D'Amore 
689*260e9a87SYuri Pankov 	for (i = 1; i < n->args->argc; i++) {
690*260e9a87SYuri Pankov 		argv = n->args->argv + i;
691*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AN_REP,
692*260e9a87SYuri Pankov 		    mdoc->parse, argv->line, argv->pos,
693*260e9a87SYuri Pankov 		    "An -%s", mdoc_argnames[argv->arg]);
694*260e9a87SYuri Pankov 	}
695*260e9a87SYuri Pankov 
696*260e9a87SYuri Pankov 	argv = n->args->argv;
697*260e9a87SYuri Pankov 	if (argv->arg == MDOC_Split)
69895c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_split;
699*260e9a87SYuri Pankov 	else if (argv->arg == MDOC_Nosplit)
70095c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_nosplit;
70195c635efSGarrett D'Amore 	else
70295c635efSGarrett D'Amore 		abort();
70395c635efSGarrett D'Amore }
70495c635efSGarrett D'Amore 
705*260e9a87SYuri Pankov static void
70695c635efSGarrett D'Amore pre_std(PRE_ARGS)
70795c635efSGarrett D'Amore {
70895c635efSGarrett D'Amore 
70995c635efSGarrett D'Amore 	if (n->args && 1 == n->args->argc)
71095c635efSGarrett D'Amore 		if (MDOC_Std == n->args->argv[0].arg)
711*260e9a87SYuri Pankov 			return;
71295c635efSGarrett D'Amore 
713*260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
714*260e9a87SYuri Pankov 	    n->line, n->pos, mdoc_macronames[n->tok]);
71595c635efSGarrett D'Amore }
71695c635efSGarrett D'Amore 
717*260e9a87SYuri Pankov static void
718*260e9a87SYuri Pankov pre_obsolete(PRE_ARGS)
71995c635efSGarrett D'Amore {
72095c635efSGarrett D'Amore 
721*260e9a87SYuri Pankov 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
722*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
723*260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
724*260e9a87SYuri Pankov }
72595c635efSGarrett D'Amore 
726*260e9a87SYuri Pankov static void
727*260e9a87SYuri Pankov pre_dt(PRE_ARGS)
728*260e9a87SYuri Pankov {
72995c635efSGarrett D'Amore 
730*260e9a87SYuri Pankov 	if (mdoc->meta.title != NULL)
731*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
732*260e9a87SYuri Pankov 		    n->line, n->pos, "Dt");
733*260e9a87SYuri Pankov 	else if (mdoc->meta.os != NULL)
734*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
735*260e9a87SYuri Pankov 		    n->line, n->pos, "Dt after Os");
73695c635efSGarrett D'Amore }
73795c635efSGarrett D'Amore 
738*260e9a87SYuri Pankov static void
73995c635efSGarrett D'Amore pre_os(PRE_ARGS)
74095c635efSGarrett D'Amore {
74195c635efSGarrett D'Amore 
742*260e9a87SYuri Pankov 	if (mdoc->meta.os != NULL)
743*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
744*260e9a87SYuri Pankov 		    n->line, n->pos, "Os");
745*260e9a87SYuri Pankov 	else if (mdoc->flags & MDOC_PBODY)
746*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
747*260e9a87SYuri Pankov 		    n->line, n->pos, "Os");
74895c635efSGarrett D'Amore }
74995c635efSGarrett D'Amore 
750*260e9a87SYuri Pankov static void
75195c635efSGarrett D'Amore pre_dd(PRE_ARGS)
75295c635efSGarrett D'Amore {
75395c635efSGarrett D'Amore 
754*260e9a87SYuri Pankov 	if (mdoc->meta.date != NULL)
755*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
756*260e9a87SYuri Pankov 		    n->line, n->pos, "Dd");
757*260e9a87SYuri Pankov 	else if (mdoc->flags & MDOC_PBODY)
758*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
759*260e9a87SYuri Pankov 		    n->line, n->pos, "Dd");
760*260e9a87SYuri Pankov 	else if (mdoc->meta.title != NULL)
761*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
762*260e9a87SYuri Pankov 		    n->line, n->pos, "Dd after Dt");
763*260e9a87SYuri Pankov 	else if (mdoc->meta.os != NULL)
764*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
765*260e9a87SYuri Pankov 		    n->line, n->pos, "Dd after Os");
76695c635efSGarrett D'Amore }
76795c635efSGarrett D'Amore 
768*260e9a87SYuri Pankov static void
76995c635efSGarrett D'Amore post_bf(POST_ARGS)
77095c635efSGarrett D'Amore {
771*260e9a87SYuri Pankov 	struct mdoc_node *np, *nch;
77295c635efSGarrett D'Amore 	enum mdocargt	  arg;
77395c635efSGarrett D'Amore 
77495c635efSGarrett D'Amore 	/*
77595c635efSGarrett D'Amore 	 * Unlike other data pointers, these are "housed" by the HEAD
77695c635efSGarrett D'Amore 	 * element, which contains the goods.
77795c635efSGarrett D'Amore 	 */
77895c635efSGarrett D'Amore 
77995c635efSGarrett D'Amore 	np = mdoc->last;
780*260e9a87SYuri Pankov 	if (MDOC_HEAD != np->type)
781*260e9a87SYuri Pankov 		return;
782*260e9a87SYuri Pankov 
78395c635efSGarrett D'Amore 	assert(MDOC_BLOCK == np->parent->type);
78495c635efSGarrett D'Amore 	assert(MDOC_Bf == np->parent->tok);
78595c635efSGarrett D'Amore 
786*260e9a87SYuri Pankov 	/* Check the number of arguments. */
78795c635efSGarrett D'Amore 
788*260e9a87SYuri Pankov 	nch = np->child;
789*260e9a87SYuri Pankov 	if (NULL == np->parent->args) {
790*260e9a87SYuri Pankov 		if (NULL == nch) {
791*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
792*260e9a87SYuri Pankov 			    np->line, np->pos, "Bf");
793*260e9a87SYuri Pankov 			return;
794*260e9a87SYuri Pankov 		}
795*260e9a87SYuri Pankov 		nch = nch->next;
79695c635efSGarrett D'Amore 	}
797*260e9a87SYuri Pankov 	if (NULL != nch)
798*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
799*260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bf ... %s", nch->string);
80095c635efSGarrett D'Amore 
80195c635efSGarrett D'Amore 	/* Extract argument into data. */
802*260e9a87SYuri Pankov 
80395c635efSGarrett D'Amore 	if (np->parent->args) {
80495c635efSGarrett D'Amore 		arg = np->parent->args->argv[0].arg;
80595c635efSGarrett D'Amore 		if (MDOC_Emphasis == arg)
80695c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Em;
80795c635efSGarrett D'Amore 		else if (MDOC_Literal == arg)
80895c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Li;
80995c635efSGarrett D'Amore 		else if (MDOC_Symbolic == arg)
81095c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Sy;
81195c635efSGarrett D'Amore 		else
81295c635efSGarrett D'Amore 			abort();
813*260e9a87SYuri Pankov 		return;
81495c635efSGarrett D'Amore 	}
81595c635efSGarrett D'Amore 
81695c635efSGarrett D'Amore 	/* Extract parameter into data. */
81795c635efSGarrett D'Amore 
81895c635efSGarrett D'Amore 	if (0 == strcmp(np->child->string, "Em"))
81995c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Em;
82095c635efSGarrett D'Amore 	else if (0 == strcmp(np->child->string, "Li"))
82195c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Li;
82295c635efSGarrett D'Amore 	else if (0 == strcmp(np->child->string, "Sy"))
82395c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Sy;
824*260e9a87SYuri Pankov 	else
825*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
826*260e9a87SYuri Pankov 		    np->child->line, np->child->pos,
827*260e9a87SYuri Pankov 		    "Bf %s", np->child->string);
82895c635efSGarrett D'Amore }
82995c635efSGarrett D'Amore 
830*260e9a87SYuri Pankov static void
83195c635efSGarrett D'Amore post_lb(POST_ARGS)
83295c635efSGarrett D'Amore {
833*260e9a87SYuri Pankov 	struct mdoc_node	*n;
834*260e9a87SYuri Pankov 	const char		*stdlibname;
835*260e9a87SYuri Pankov 	char			*libname;
83695c635efSGarrett D'Amore 
837*260e9a87SYuri Pankov 	n = mdoc->last->child;
838*260e9a87SYuri Pankov 	assert(MDOC_TEXT == n->type);
83995c635efSGarrett D'Amore 
840*260e9a87SYuri Pankov 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
841*260e9a87SYuri Pankov 		mandoc_asprintf(&libname,
842*260e9a87SYuri Pankov 		    "library \\(Lq%s\\(Rq", n->string);
843*260e9a87SYuri Pankov 	else
844*260e9a87SYuri Pankov 		libname = mandoc_strdup(stdlibname);
84595c635efSGarrett D'Amore 
846*260e9a87SYuri Pankov 	free(n->string);
847*260e9a87SYuri Pankov 	n->string = libname;
848*260e9a87SYuri Pankov }
84995c635efSGarrett D'Amore 
850*260e9a87SYuri Pankov static void
851*260e9a87SYuri Pankov post_eoln(POST_ARGS)
852*260e9a87SYuri Pankov {
853*260e9a87SYuri Pankov 	const struct mdoc_node *n;
85495c635efSGarrett D'Amore 
855*260e9a87SYuri Pankov 	n = mdoc->last;
856*260e9a87SYuri Pankov 	if (n->child)
857*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
858*260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
859*260e9a87SYuri Pankov 		    "%s %s", mdoc_macronames[n->tok],
860*260e9a87SYuri Pankov 		    n->child->string);
861*260e9a87SYuri Pankov }
86295c635efSGarrett D'Amore 
863*260e9a87SYuri Pankov static void
864*260e9a87SYuri Pankov post_fname(POST_ARGS)
865*260e9a87SYuri Pankov {
866*260e9a87SYuri Pankov 	const struct mdoc_node	*n;
867*260e9a87SYuri Pankov 	const char		*cp;
868*260e9a87SYuri Pankov 	size_t			 pos;
86995c635efSGarrett D'Amore 
870*260e9a87SYuri Pankov 	n = mdoc->last->child;
871*260e9a87SYuri Pankov 	pos = strcspn(n->string, "()");
872*260e9a87SYuri Pankov 	cp = n->string + pos;
873*260e9a87SYuri Pankov 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
874*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
875*260e9a87SYuri Pankov 		    n->line, n->pos + pos, n->string);
87695c635efSGarrett D'Amore }
87795c635efSGarrett D'Amore 
878*260e9a87SYuri Pankov static void
879*260e9a87SYuri Pankov post_fn(POST_ARGS)
88095c635efSGarrett D'Amore {
88195c635efSGarrett D'Amore 
882*260e9a87SYuri Pankov 	post_fname(mdoc);
883*260e9a87SYuri Pankov 	post_fa(mdoc);
88495c635efSGarrett D'Amore }
88595c635efSGarrett D'Amore 
886*260e9a87SYuri Pankov static void
887*260e9a87SYuri Pankov post_fo(POST_ARGS)
888*260e9a87SYuri Pankov {
889*260e9a87SYuri Pankov 	const struct mdoc_node	*n;
89095c635efSGarrett D'Amore 
891*260e9a87SYuri Pankov 	n = mdoc->last;
892*260e9a87SYuri Pankov 
893*260e9a87SYuri Pankov 	if (n->type != MDOC_HEAD)
894*260e9a87SYuri Pankov 		return;
895*260e9a87SYuri Pankov 
896*260e9a87SYuri Pankov 	if (n->child == NULL) {
897*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
898*260e9a87SYuri Pankov 		    n->line, n->pos, "Fo");
899*260e9a87SYuri Pankov 		return;
900*260e9a87SYuri Pankov 	}
901*260e9a87SYuri Pankov 	if (n->child != n->last) {
902*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
903*260e9a87SYuri Pankov 		    n->child->next->line, n->child->next->pos,
904*260e9a87SYuri Pankov 		    "Fo ... %s", n->child->next->string);
905*260e9a87SYuri Pankov 		while (n->child != n->last)
906*260e9a87SYuri Pankov 			mdoc_node_delete(mdoc, n->last);
907*260e9a87SYuri Pankov 	}
908*260e9a87SYuri Pankov 
909*260e9a87SYuri Pankov 	post_fname(mdoc);
910*260e9a87SYuri Pankov }
911*260e9a87SYuri Pankov 
912*260e9a87SYuri Pankov static void
913*260e9a87SYuri Pankov post_fa(POST_ARGS)
914*260e9a87SYuri Pankov {
915*260e9a87SYuri Pankov 	const struct mdoc_node *n;
916*260e9a87SYuri Pankov 	const char *cp;
917*260e9a87SYuri Pankov 
918*260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
919*260e9a87SYuri Pankov 		for (cp = n->string; *cp != '\0'; cp++) {
920*260e9a87SYuri Pankov 			/* Ignore callbacks and alterations. */
921*260e9a87SYuri Pankov 			if (*cp == '(' || *cp == '{')
922*260e9a87SYuri Pankov 				break;
923*260e9a87SYuri Pankov 			if (*cp != ',')
924*260e9a87SYuri Pankov 				continue;
925*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
926*260e9a87SYuri Pankov 			    n->line, n->pos + (cp - n->string),
927*260e9a87SYuri Pankov 			    n->string);
928*260e9a87SYuri Pankov 			break;
929*260e9a87SYuri Pankov 		}
930*260e9a87SYuri Pankov 	}
931*260e9a87SYuri Pankov }
932*260e9a87SYuri Pankov 
933*260e9a87SYuri Pankov static void
93495c635efSGarrett D'Amore post_vt(POST_ARGS)
93595c635efSGarrett D'Amore {
93695c635efSGarrett D'Amore 	const struct mdoc_node *n;
93795c635efSGarrett D'Amore 
93895c635efSGarrett D'Amore 	/*
93995c635efSGarrett D'Amore 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
94095c635efSGarrett D'Amore 	 * have different syntaxes (yet more context-sensitive
94195c635efSGarrett D'Amore 	 * behaviour).  ELEM types must have a child, which is already
94295c635efSGarrett D'Amore 	 * guaranteed by the in_line parsing routine; BLOCK types,
94395c635efSGarrett D'Amore 	 * specifically the BODY, should only have TEXT children.
94495c635efSGarrett D'Amore 	 */
94595c635efSGarrett D'Amore 
94695c635efSGarrett D'Amore 	if (MDOC_BODY != mdoc->last->type)
947*260e9a87SYuri Pankov 		return;
94895c635efSGarrett D'Amore 
949*260e9a87SYuri Pankov 	for (n = mdoc->last->child; n; n = n->next)
950*260e9a87SYuri Pankov 		if (MDOC_TEXT != n->type)
951*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
952*260e9a87SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
95395c635efSGarrett D'Amore }
95495c635efSGarrett D'Amore 
955*260e9a87SYuri Pankov static void
95695c635efSGarrett D'Amore post_nm(POST_ARGS)
95795c635efSGarrett D'Amore {
958*260e9a87SYuri Pankov 	struct mdoc_node	*n;
959*260e9a87SYuri Pankov 
960*260e9a87SYuri Pankov 	n = mdoc->last;
961*260e9a87SYuri Pankov 
962*260e9a87SYuri Pankov 	if (n->last != NULL &&
963*260e9a87SYuri Pankov 	    (n->last->tok == MDOC_Pp ||
964*260e9a87SYuri Pankov 	     n->last->tok == MDOC_Lp))
965*260e9a87SYuri Pankov 		mdoc_node_relink(mdoc, n->last);
96695c635efSGarrett D'Amore 
967698f87a4SGarrett D'Amore 	if (NULL != mdoc->meta.name)
968*260e9a87SYuri Pankov 		return;
96995c635efSGarrett D'Amore 
970*260e9a87SYuri Pankov 	mdoc_deroff(&mdoc->meta.name, n);
97195c635efSGarrett D'Amore 
972*260e9a87SYuri Pankov 	if (NULL == mdoc->meta.name)
973*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
974*260e9a87SYuri Pankov 		    n->line, n->pos, "Nm");
97595c635efSGarrett D'Amore }
97695c635efSGarrett D'Amore 
977*260e9a87SYuri Pankov static void
978*260e9a87SYuri Pankov post_nd(POST_ARGS)
979*260e9a87SYuri Pankov {
980*260e9a87SYuri Pankov 	struct mdoc_node	*n;
981*260e9a87SYuri Pankov 
982*260e9a87SYuri Pankov 	n = mdoc->last;
983*260e9a87SYuri Pankov 
984*260e9a87SYuri Pankov 	if (n->type != MDOC_BODY)
985*260e9a87SYuri Pankov 		return;
986*260e9a87SYuri Pankov 
987*260e9a87SYuri Pankov 	if (n->child == NULL)
988*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
989*260e9a87SYuri Pankov 		    n->line, n->pos, "Nd");
990*260e9a87SYuri Pankov 
991*260e9a87SYuri Pankov 	post_hyph(mdoc);
992*260e9a87SYuri Pankov }
993*260e9a87SYuri Pankov 
994*260e9a87SYuri Pankov static void
995*260e9a87SYuri Pankov post_d1(POST_ARGS)
996*260e9a87SYuri Pankov {
997*260e9a87SYuri Pankov 	struct mdoc_node	*n;
998*260e9a87SYuri Pankov 
999*260e9a87SYuri Pankov 	n = mdoc->last;
1000*260e9a87SYuri Pankov 
1001*260e9a87SYuri Pankov 	if (n->type != MDOC_BODY)
1002*260e9a87SYuri Pankov 		return;
1003*260e9a87SYuri Pankov 
1004*260e9a87SYuri Pankov 	if (n->child == NULL)
1005*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1006*260e9a87SYuri Pankov 		    n->line, n->pos, "D1");
1007*260e9a87SYuri Pankov 
1008*260e9a87SYuri Pankov 	post_hyph(mdoc);
1009*260e9a87SYuri Pankov }
1010*260e9a87SYuri Pankov 
1011*260e9a87SYuri Pankov static void
101295c635efSGarrett D'Amore post_literal(POST_ARGS)
101395c635efSGarrett D'Amore {
1014*260e9a87SYuri Pankov 	struct mdoc_node	*n;
101595c635efSGarrett D'Amore 
1016*260e9a87SYuri Pankov 	n = mdoc->last;
1017*260e9a87SYuri Pankov 
1018*260e9a87SYuri Pankov 	if (n->type != MDOC_BODY)
1019*260e9a87SYuri Pankov 		return;
102095c635efSGarrett D'Amore 
1021*260e9a87SYuri Pankov 	if (n->child == NULL)
1022*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1023*260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
1024*260e9a87SYuri Pankov 
1025*260e9a87SYuri Pankov 	if (n->tok == MDOC_Bd &&
1026*260e9a87SYuri Pankov 	    n->norm->Bd.type != DISP_literal &&
1027*260e9a87SYuri Pankov 	    n->norm->Bd.type != DISP_unfilled)
1028*260e9a87SYuri Pankov 		return;
1029*260e9a87SYuri Pankov 
1030*260e9a87SYuri Pankov 	mdoc->flags &= ~MDOC_LITERAL;
103195c635efSGarrett D'Amore }
103295c635efSGarrett D'Amore 
1033*260e9a87SYuri Pankov static void
103495c635efSGarrett D'Amore post_defaults(POST_ARGS)
103595c635efSGarrett D'Amore {
103695c635efSGarrett D'Amore 	struct mdoc_node *nn;
103795c635efSGarrett D'Amore 
103895c635efSGarrett D'Amore 	/*
103995c635efSGarrett D'Amore 	 * The `Ar' defaults to "file ..." if no value is provided as an
104095c635efSGarrett D'Amore 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
104195c635efSGarrett D'Amore 	 * gets an empty string.
104295c635efSGarrett D'Amore 	 */
104395c635efSGarrett D'Amore 
104495c635efSGarrett D'Amore 	if (mdoc->last->child)
1045*260e9a87SYuri Pankov 		return;
1046*260e9a87SYuri Pankov 
104795c635efSGarrett D'Amore 	nn = mdoc->last;
104895c635efSGarrett D'Amore 	mdoc->next = MDOC_NEXT_CHILD;
104995c635efSGarrett D'Amore 
105095c635efSGarrett D'Amore 	switch (nn->tok) {
1051*260e9a87SYuri Pankov 	case MDOC_Ar:
1052*260e9a87SYuri Pankov 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
1053*260e9a87SYuri Pankov 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
105495c635efSGarrett D'Amore 		break;
1055*260e9a87SYuri Pankov 	case MDOC_Pa:
105695c635efSGarrett D'Amore 		/* FALLTHROUGH */
1057*260e9a87SYuri Pankov 	case MDOC_Mt:
1058*260e9a87SYuri Pankov 		mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
105995c635efSGarrett D'Amore 		break;
106095c635efSGarrett D'Amore 	default:
106195c635efSGarrett D'Amore 		abort();
106295c635efSGarrett D'Amore 		/* NOTREACHED */
1063*260e9a87SYuri Pankov 	}
106495c635efSGarrett D'Amore 	mdoc->last = nn;
106595c635efSGarrett D'Amore }
106695c635efSGarrett D'Amore 
1067*260e9a87SYuri Pankov static void
106895c635efSGarrett D'Amore post_at(POST_ARGS)
106995c635efSGarrett D'Amore {
1070*260e9a87SYuri Pankov 	struct mdoc_node	*n;
1071*260e9a87SYuri Pankov 	const char		*std_att;
1072*260e9a87SYuri Pankov 	char			*att;
1073*260e9a87SYuri Pankov 
1074*260e9a87SYuri Pankov 	n = mdoc->last;
1075*260e9a87SYuri Pankov 	if (n->child == NULL) {
1076*260e9a87SYuri Pankov 		mdoc->next = MDOC_NEXT_CHILD;
1077*260e9a87SYuri Pankov 		mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1078*260e9a87SYuri Pankov 		mdoc->last = n;
1079*260e9a87SYuri Pankov 		return;
1080*260e9a87SYuri Pankov 	}
108195c635efSGarrett D'Amore 
108295c635efSGarrett D'Amore 	/*
108395c635efSGarrett D'Amore 	 * If we have a child, look it up in the standard keys.  If a
108495c635efSGarrett D'Amore 	 * key exist, use that instead of the child; if it doesn't,
108595c635efSGarrett D'Amore 	 * prefix "AT&T UNIX " to the existing data.
108695c635efSGarrett D'Amore 	 */
108795c635efSGarrett D'Amore 
1088*260e9a87SYuri Pankov 	n = n->child;
1089*260e9a87SYuri Pankov 	assert(MDOC_TEXT == n->type);
1090*260e9a87SYuri Pankov 	if (NULL == (std_att = mdoc_a2att(n->string))) {
1091*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1092*260e9a87SYuri Pankov 		    n->line, n->pos, "At %s", n->string);
1093*260e9a87SYuri Pankov 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
1094*260e9a87SYuri Pankov 	} else
1095*260e9a87SYuri Pankov 		att = mandoc_strdup(std_att);
109695c635efSGarrett D'Amore 
1097*260e9a87SYuri Pankov 	free(n->string);
1098*260e9a87SYuri Pankov 	n->string = att;
109995c635efSGarrett D'Amore }
110095c635efSGarrett D'Amore 
1101*260e9a87SYuri Pankov static void
110295c635efSGarrett D'Amore post_an(POST_ARGS)
110395c635efSGarrett D'Amore {
1104*260e9a87SYuri Pankov 	struct mdoc_node *np, *nch;
110595c635efSGarrett D'Amore 
110695c635efSGarrett D'Amore 	np = mdoc->last;
1107*260e9a87SYuri Pankov 	nch = np->child;
1108*260e9a87SYuri Pankov 	if (np->norm->An.auth == AUTH__NONE) {
1109*260e9a87SYuri Pankov 		if (nch == NULL)
1110*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1111*260e9a87SYuri Pankov 			    np->line, np->pos, "An");
1112*260e9a87SYuri Pankov 	} else if (nch != NULL)
1113*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1114*260e9a87SYuri Pankov 		    nch->line, nch->pos, "An ... %s", nch->string);
1115*260e9a87SYuri Pankov }
1116*260e9a87SYuri Pankov 
1117*260e9a87SYuri Pankov static void
1118*260e9a87SYuri Pankov post_en(POST_ARGS)
1119*260e9a87SYuri Pankov {
112095c635efSGarrett D'Amore 
1121*260e9a87SYuri Pankov 	if (MDOC_BLOCK == mdoc->last->type)
1122*260e9a87SYuri Pankov 		mdoc->last->norm->Es = mdoc->last_es;
112395c635efSGarrett D'Amore }
112495c635efSGarrett D'Amore 
1125*260e9a87SYuri Pankov static void
1126*260e9a87SYuri Pankov post_es(POST_ARGS)
1127*260e9a87SYuri Pankov {
112895c635efSGarrett D'Amore 
1129*260e9a87SYuri Pankov 	mdoc->last_es = mdoc->last;
1130*260e9a87SYuri Pankov }
1131*260e9a87SYuri Pankov 
1132*260e9a87SYuri Pankov static void
113395c635efSGarrett D'Amore post_it(POST_ARGS)
113495c635efSGarrett D'Amore {
113595c635efSGarrett D'Amore 	int		  i, cols;
113695c635efSGarrett D'Amore 	enum mdoc_list	  lt;
1137*260e9a87SYuri Pankov 	struct mdoc_node *nbl, *nit, *nch;
113895c635efSGarrett D'Amore 
1139*260e9a87SYuri Pankov 	nit = mdoc->last;
1140*260e9a87SYuri Pankov 	if (nit->type != MDOC_BLOCK)
1141*260e9a87SYuri Pankov 		return;
114295c635efSGarrett D'Amore 
1143*260e9a87SYuri Pankov 	nbl = nit->parent->parent;
1144*260e9a87SYuri Pankov 	lt = nbl->norm->Bl.type;
114595c635efSGarrett D'Amore 
114695c635efSGarrett D'Amore 	switch (lt) {
1147*260e9a87SYuri Pankov 	case LIST_tag:
114895c635efSGarrett D'Amore 		/* FALLTHROUGH */
1149*260e9a87SYuri Pankov 	case LIST_hang:
115095c635efSGarrett D'Amore 		/* FALLTHROUGH */
1151*260e9a87SYuri Pankov 	case LIST_ohang:
115295c635efSGarrett D'Amore 		/* FALLTHROUGH */
1153*260e9a87SYuri Pankov 	case LIST_inset:
1154*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1155*260e9a87SYuri Pankov 	case LIST_diag:
1156*260e9a87SYuri Pankov 		if (nit->head->child == NULL)
1157*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1158*260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1159*260e9a87SYuri Pankov 			    "Bl -%s It",
1160*260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
116195c635efSGarrett D'Amore 		break;
1162*260e9a87SYuri Pankov 	case LIST_bullet:
116395c635efSGarrett D'Amore 		/* FALLTHROUGH */
1164*260e9a87SYuri Pankov 	case LIST_dash:
116595c635efSGarrett D'Amore 		/* FALLTHROUGH */
1166*260e9a87SYuri Pankov 	case LIST_enum:
116795c635efSGarrett D'Amore 		/* FALLTHROUGH */
1168*260e9a87SYuri Pankov 	case LIST_hyphen:
1169*260e9a87SYuri Pankov 		if (nit->body == NULL || nit->body->child == NULL)
1170*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1171*260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1172*260e9a87SYuri Pankov 			    "Bl -%s It",
1173*260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
117495c635efSGarrett D'Amore 		/* FALLTHROUGH */
1175*260e9a87SYuri Pankov 	case LIST_item:
1176*260e9a87SYuri Pankov 		if (nit->head->child != NULL)
1177*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1178*260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1179*260e9a87SYuri Pankov 			    "It %s", nit->head->child->string);
118095c635efSGarrett D'Amore 		break;
1181*260e9a87SYuri Pankov 	case LIST_column:
1182*260e9a87SYuri Pankov 		cols = (int)nbl->norm->Bl.ncols;
118395c635efSGarrett D'Amore 
1184*260e9a87SYuri Pankov 		assert(nit->head->child == NULL);
118595c635efSGarrett D'Amore 
1186*260e9a87SYuri Pankov 		for (i = 0, nch = nit->child; nch; nch = nch->next)
1187*260e9a87SYuri Pankov 			if (nch->type == MDOC_BODY)
118895c635efSGarrett D'Amore 				i++;
118995c635efSGarrett D'Amore 
1190*260e9a87SYuri Pankov 		if (i < cols || i > cols + 1)
1191*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_COL,
1192*260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1193*260e9a87SYuri Pankov 			    "%d columns, %d cells", cols, i);
119495c635efSGarrett D'Amore 		break;
1195*260e9a87SYuri Pankov 	default:
1196*260e9a87SYuri Pankov 		abort();
119795c635efSGarrett D'Amore 	}
119895c635efSGarrett D'Amore }
119995c635efSGarrett D'Amore 
1200*260e9a87SYuri Pankov static void
1201*260e9a87SYuri Pankov post_bl_block(POST_ARGS)
120295c635efSGarrett D'Amore {
1203698f87a4SGarrett D'Amore 	struct mdoc_node *n, *ni, *nc;
120495c635efSGarrett D'Amore 
120595c635efSGarrett D'Amore 	/*
120695c635efSGarrett D'Amore 	 * These are fairly complicated, so we've broken them into two
120795c635efSGarrett D'Amore 	 * functions.  post_bl_block_tag() is called when a -tag is
120895c635efSGarrett D'Amore 	 * specified, but no -width (it must be guessed).  The second
120995c635efSGarrett D'Amore 	 * when a -width is specified (macro indicators must be
121095c635efSGarrett D'Amore 	 * rewritten into real lengths).
121195c635efSGarrett D'Amore 	 */
121295c635efSGarrett D'Amore 
121395c635efSGarrett D'Amore 	n = mdoc->last;
121495c635efSGarrett D'Amore 
1215*260e9a87SYuri Pankov 	if (LIST_tag == n->norm->Bl.type &&
1216*260e9a87SYuri Pankov 	    NULL == n->norm->Bl.width) {
1217*260e9a87SYuri Pankov 		post_bl_block_tag(mdoc);
1218698f87a4SGarrett D'Amore 		assert(n->norm->Bl.width);
1219698f87a4SGarrett D'Amore 	}
122095c635efSGarrett D'Amore 
1221698f87a4SGarrett D'Amore 	for (ni = n->body->child; ni; ni = ni->next) {
1222698f87a4SGarrett D'Amore 		if (NULL == ni->body)
1223698f87a4SGarrett D'Amore 			continue;
1224698f87a4SGarrett D'Amore 		nc = ni->body->last;
1225698f87a4SGarrett D'Amore 		while (NULL != nc) {
1226698f87a4SGarrett D'Amore 			switch (nc->tok) {
1227*260e9a87SYuri Pankov 			case MDOC_Pp:
1228698f87a4SGarrett D'Amore 				/* FALLTHROUGH */
1229*260e9a87SYuri Pankov 			case MDOC_Lp:
1230698f87a4SGarrett D'Amore 				/* FALLTHROUGH */
1231*260e9a87SYuri Pankov 			case MDOC_br:
1232698f87a4SGarrett D'Amore 				break;
1233698f87a4SGarrett D'Amore 			default:
1234698f87a4SGarrett D'Amore 				nc = NULL;
1235698f87a4SGarrett D'Amore 				continue;
1236698f87a4SGarrett D'Amore 			}
1237698f87a4SGarrett D'Amore 			if (NULL == ni->next) {
1238*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_PAR_MOVE,
1239*260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1240*260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1241*260e9a87SYuri Pankov 				mdoc_node_relink(mdoc, nc);
1242698f87a4SGarrett D'Amore 			} else if (0 == n->norm->Bl.comp &&
1243698f87a4SGarrett D'Amore 			    LIST_column != n->norm->Bl.type) {
1244*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1245*260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1246*260e9a87SYuri Pankov 				    "%s before It",
1247*260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1248698f87a4SGarrett D'Amore 				mdoc_node_delete(mdoc, nc);
1249698f87a4SGarrett D'Amore 			} else
1250698f87a4SGarrett D'Amore 				break;
1251698f87a4SGarrett D'Amore 			nc = ni->body->last;
1252698f87a4SGarrett D'Amore 		}
1253698f87a4SGarrett D'Amore 	}
125495c635efSGarrett D'Amore }
125595c635efSGarrett D'Amore 
1256*260e9a87SYuri Pankov /*
1257*260e9a87SYuri Pankov  * If the argument of -offset or -width is a macro,
1258*260e9a87SYuri Pankov  * replace it with the associated default width.
1259*260e9a87SYuri Pankov  */
1260*260e9a87SYuri Pankov void
1261*260e9a87SYuri Pankov rewrite_macro2len(char **arg)
126295c635efSGarrett D'Amore {
126395c635efSGarrett D'Amore 	size_t		  width;
126495c635efSGarrett D'Amore 	enum mdoct	  tok;
126595c635efSGarrett D'Amore 
1266*260e9a87SYuri Pankov 	if (*arg == NULL)
1267*260e9a87SYuri Pankov 		return;
1268*260e9a87SYuri Pankov 	else if ( ! strcmp(*arg, "Ds"))
126995c635efSGarrett D'Amore 		width = 6;
1270*260e9a87SYuri Pankov 	else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
1271*260e9a87SYuri Pankov 		return;
1272*260e9a87SYuri Pankov 	else
1273*260e9a87SYuri Pankov 		width = macro2len(tok);
127495c635efSGarrett D'Amore 
1275*260e9a87SYuri Pankov 	free(*arg);
1276*260e9a87SYuri Pankov 	mandoc_asprintf(arg, "%zun", width);
127795c635efSGarrett D'Amore }
127895c635efSGarrett D'Amore 
1279*260e9a87SYuri Pankov static void
128095c635efSGarrett D'Amore post_bl_block_tag(POST_ARGS)
128195c635efSGarrett D'Amore {
128295c635efSGarrett D'Amore 	struct mdoc_node *n, *nn;
128395c635efSGarrett D'Amore 	size_t		  sz, ssz;
128495c635efSGarrett D'Amore 	int		  i;
1285*260e9a87SYuri Pankov 	char		  buf[24];
128695c635efSGarrett D'Amore 
128795c635efSGarrett D'Amore 	/*
128895c635efSGarrett D'Amore 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
128995c635efSGarrett D'Amore 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
129095c635efSGarrett D'Amore 	 * ONLY if the -width argument has NOT been provided.  See
1291*260e9a87SYuri Pankov 	 * rewrite_macro2len() for converting the -width string.
129295c635efSGarrett D'Amore 	 */
129395c635efSGarrett D'Amore 
129495c635efSGarrett D'Amore 	sz = 10;
129595c635efSGarrett D'Amore 	n = mdoc->last;
129695c635efSGarrett D'Amore 
129795c635efSGarrett D'Amore 	for (nn = n->body->child; nn; nn = nn->next) {
129895c635efSGarrett D'Amore 		if (MDOC_It != nn->tok)
129995c635efSGarrett D'Amore 			continue;
130095c635efSGarrett D'Amore 
130195c635efSGarrett D'Amore 		assert(MDOC_BLOCK == nn->type);
130295c635efSGarrett D'Amore 		nn = nn->head->child;
130395c635efSGarrett D'Amore 
130495c635efSGarrett D'Amore 		if (nn == NULL)
130595c635efSGarrett D'Amore 			break;
130695c635efSGarrett D'Amore 
130795c635efSGarrett D'Amore 		if (MDOC_TEXT == nn->type) {
130895c635efSGarrett D'Amore 			sz = strlen(nn->string) + 1;
130995c635efSGarrett D'Amore 			break;
131095c635efSGarrett D'Amore 		}
131195c635efSGarrett D'Amore 
131295c635efSGarrett D'Amore 		if (0 != (ssz = macro2len(nn->tok)))
131395c635efSGarrett D'Amore 			sz = ssz;
131495c635efSGarrett D'Amore 
131595c635efSGarrett D'Amore 		break;
1316*260e9a87SYuri Pankov 	}
131795c635efSGarrett D'Amore 
131895c635efSGarrett D'Amore 	/* Defaults to ten ens. */
131995c635efSGarrett D'Amore 
1320*260e9a87SYuri Pankov 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
132195c635efSGarrett D'Amore 
132295c635efSGarrett D'Amore 	/*
132395c635efSGarrett D'Amore 	 * We have to dynamically add this to the macro's argument list.
132495c635efSGarrett D'Amore 	 * We're guaranteed that a MDOC_Width doesn't already exist.
132595c635efSGarrett D'Amore 	 */
132695c635efSGarrett D'Amore 
132795c635efSGarrett D'Amore 	assert(n->args);
132895c635efSGarrett D'Amore 	i = (int)(n->args->argc)++;
132995c635efSGarrett D'Amore 
1330*260e9a87SYuri Pankov 	n->args->argv = mandoc_reallocarray(n->args->argv,
1331*260e9a87SYuri Pankov 	    n->args->argc, sizeof(struct mdoc_argv));
133295c635efSGarrett D'Amore 
133395c635efSGarrett D'Amore 	n->args->argv[i].arg = MDOC_Width;
133495c635efSGarrett D'Amore 	n->args->argv[i].line = n->line;
133595c635efSGarrett D'Amore 	n->args->argv[i].pos = n->pos;
133695c635efSGarrett D'Amore 	n->args->argv[i].sz = 1;
133795c635efSGarrett D'Amore 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
133895c635efSGarrett D'Amore 	n->args->argv[i].value[0] = mandoc_strdup(buf);
133995c635efSGarrett D'Amore 
134095c635efSGarrett D'Amore 	/* Set our width! */
134195c635efSGarrett D'Amore 	n->norm->Bl.width = n->args->argv[i].value[0];
134295c635efSGarrett D'Amore }
134395c635efSGarrett D'Amore 
1344*260e9a87SYuri Pankov static void
1345*260e9a87SYuri Pankov post_bl_head(POST_ARGS)
134695c635efSGarrett D'Amore {
1347*260e9a87SYuri Pankov 	struct mdoc_node *nbl, *nh, *nch, *nnext;
1348*260e9a87SYuri Pankov 	struct mdoc_argv *argv;
134995c635efSGarrett D'Amore 	int		  i, j;
135095c635efSGarrett D'Amore 
1351*260e9a87SYuri Pankov 	nh = mdoc->last;
1352*260e9a87SYuri Pankov 
1353*260e9a87SYuri Pankov 	if (nh->norm->Bl.type != LIST_column) {
1354*260e9a87SYuri Pankov 		if ((nch = nh->child) == NULL)
1355*260e9a87SYuri Pankov 			return;
1356*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1357*260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1358*260e9a87SYuri Pankov 		while (nch != NULL) {
1359*260e9a87SYuri Pankov 			mdoc_node_delete(mdoc, nch);
1360*260e9a87SYuri Pankov 			nch = nh->child;
1361*260e9a87SYuri Pankov 		}
1362*260e9a87SYuri Pankov 		return;
1363*260e9a87SYuri Pankov 	}
136495c635efSGarrett D'Amore 
136595c635efSGarrett D'Amore 	/*
1366*260e9a87SYuri Pankov 	 * Append old-style lists, where the column width specifiers
136795c635efSGarrett D'Amore 	 * trail as macro parameters, to the new-style ("normal-form")
136895c635efSGarrett D'Amore 	 * lists where they're argument values following -column.
136995c635efSGarrett D'Amore 	 */
137095c635efSGarrett D'Amore 
1371*260e9a87SYuri Pankov 	if (nh->child == NULL)
1372*260e9a87SYuri Pankov 		return;
137395c635efSGarrett D'Amore 
1374*260e9a87SYuri Pankov 	nbl = nh->parent;
1375*260e9a87SYuri Pankov 	for (j = 0; j < (int)nbl->args->argc; j++)
1376*260e9a87SYuri Pankov 		if (nbl->args->argv[j].arg == MDOC_Column)
137795c635efSGarrett D'Amore 			break;
137895c635efSGarrett D'Amore 
1379*260e9a87SYuri Pankov 	assert(j < (int)nbl->args->argc);
138095c635efSGarrett D'Amore 
138195c635efSGarrett D'Amore 	/*
138295c635efSGarrett D'Amore 	 * Accommodate for new-style groff column syntax.  Shuffle the
138395c635efSGarrett D'Amore 	 * child nodes, all of which must be TEXT, as arguments for the
138495c635efSGarrett D'Amore 	 * column field.  Then, delete the head children.
138595c635efSGarrett D'Amore 	 */
138695c635efSGarrett D'Amore 
1387*260e9a87SYuri Pankov 	argv = nbl->args->argv + j;
1388*260e9a87SYuri Pankov 	i = argv->sz;
1389*260e9a87SYuri Pankov 	argv->sz += nh->nchild;
1390*260e9a87SYuri Pankov 	argv->value = mandoc_reallocarray(argv->value,
1391*260e9a87SYuri Pankov 	    argv->sz, sizeof(char *));
139295c635efSGarrett D'Amore 
1393*260e9a87SYuri Pankov 	nh->norm->Bl.ncols = argv->sz;
1394*260e9a87SYuri Pankov 	nh->norm->Bl.cols = (void *)argv->value;
139595c635efSGarrett D'Amore 
1396*260e9a87SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nnext) {
1397*260e9a87SYuri Pankov 		argv->value[i++] = nch->string;
1398*260e9a87SYuri Pankov 		nch->string = NULL;
1399*260e9a87SYuri Pankov 		nnext = nch->next;
1400*260e9a87SYuri Pankov 		mdoc_node_delete(NULL, nch);
140195c635efSGarrett D'Amore 	}
1402*260e9a87SYuri Pankov 	nh->nchild = 0;
1403*260e9a87SYuri Pankov 	nh->child = NULL;
140495c635efSGarrett D'Amore }
140595c635efSGarrett D'Amore 
1406*260e9a87SYuri Pankov static void
140795c635efSGarrett D'Amore post_bl(POST_ARGS)
140895c635efSGarrett D'Amore {
1409698f87a4SGarrett D'Amore 	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
1410698f87a4SGarrett D'Amore 	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
1411698f87a4SGarrett D'Amore 	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
141295c635efSGarrett D'Amore 
1413698f87a4SGarrett D'Amore 	nbody = mdoc->last;
1414698f87a4SGarrett D'Amore 	switch (nbody->type) {
1415*260e9a87SYuri Pankov 	case MDOC_BLOCK:
1416*260e9a87SYuri Pankov 		post_bl_block(mdoc);
1417*260e9a87SYuri Pankov 		return;
1418*260e9a87SYuri Pankov 	case MDOC_HEAD:
1419*260e9a87SYuri Pankov 		post_bl_head(mdoc);
1420*260e9a87SYuri Pankov 		return;
1421*260e9a87SYuri Pankov 	case MDOC_BODY:
1422698f87a4SGarrett D'Amore 		break;
1423698f87a4SGarrett D'Amore 	default:
1424*260e9a87SYuri Pankov 		return;
1425698f87a4SGarrett D'Amore 	}
142695c635efSGarrett D'Amore 
1427698f87a4SGarrett D'Amore 	nchild = nbody->child;
1428*260e9a87SYuri Pankov 	if (nchild == NULL) {
1429*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1430*260e9a87SYuri Pankov 		    nbody->line, nbody->pos, "Bl");
1431*260e9a87SYuri Pankov 		return;
1432*260e9a87SYuri Pankov 	}
1433*260e9a87SYuri Pankov 	while (nchild != NULL) {
1434*260e9a87SYuri Pankov 		if (nchild->tok == MDOC_It ||
1435*260e9a87SYuri Pankov 		    (nchild->tok == MDOC_Sm &&
1436*260e9a87SYuri Pankov 		     nchild->next != NULL &&
1437*260e9a87SYuri Pankov 		     nchild->next->tok == MDOC_It)) {
1438698f87a4SGarrett D'Amore 			nchild = nchild->next;
143995c635efSGarrett D'Amore 			continue;
144095c635efSGarrett D'Amore 		}
144195c635efSGarrett D'Amore 
1442*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1443*260e9a87SYuri Pankov 		    nchild->line, nchild->pos,
1444*260e9a87SYuri Pankov 		    mdoc_macronames[nchild->tok]);
1445698f87a4SGarrett D'Amore 
1446698f87a4SGarrett D'Amore 		/*
1447698f87a4SGarrett D'Amore 		 * Move the node out of the Bl block.
1448698f87a4SGarrett D'Amore 		 * First, collect all required node pointers.
1449698f87a4SGarrett D'Amore 		 */
1450698f87a4SGarrett D'Amore 
1451698f87a4SGarrett D'Amore 		nblock  = nbody->parent;
1452698f87a4SGarrett D'Amore 		nprev   = nblock->prev;
1453698f87a4SGarrett D'Amore 		nparent = nblock->parent;
1454698f87a4SGarrett D'Amore 		nnext   = nchild->next;
1455698f87a4SGarrett D'Amore 
1456698f87a4SGarrett D'Amore 		/*
1457698f87a4SGarrett D'Amore 		 * Unlink this child.
1458698f87a4SGarrett D'Amore 		 */
1459698f87a4SGarrett D'Amore 
1460698f87a4SGarrett D'Amore 		assert(NULL == nchild->prev);
1461698f87a4SGarrett D'Amore 		if (0 == --nbody->nchild) {
1462698f87a4SGarrett D'Amore 			nbody->child = NULL;
1463698f87a4SGarrett D'Amore 			nbody->last  = NULL;
1464698f87a4SGarrett D'Amore 			assert(NULL == nnext);
1465698f87a4SGarrett D'Amore 		} else {
1466698f87a4SGarrett D'Amore 			nbody->child = nnext;
1467698f87a4SGarrett D'Amore 			nnext->prev = NULL;
1468698f87a4SGarrett D'Amore 		}
1469698f87a4SGarrett D'Amore 
1470698f87a4SGarrett D'Amore 		/*
1471698f87a4SGarrett D'Amore 		 * Relink this child.
1472698f87a4SGarrett D'Amore 		 */
1473698f87a4SGarrett D'Amore 
1474698f87a4SGarrett D'Amore 		nchild->parent = nparent;
1475698f87a4SGarrett D'Amore 		nchild->prev   = nprev;
1476698f87a4SGarrett D'Amore 		nchild->next   = nblock;
1477698f87a4SGarrett D'Amore 
1478698f87a4SGarrett D'Amore 		nblock->prev = nchild;
1479698f87a4SGarrett D'Amore 		nparent->nchild++;
1480698f87a4SGarrett D'Amore 		if (NULL == nprev)
1481698f87a4SGarrett D'Amore 			nparent->child = nchild;
1482698f87a4SGarrett D'Amore 		else
1483698f87a4SGarrett D'Amore 			nprev->next = nchild;
1484698f87a4SGarrett D'Amore 
1485698f87a4SGarrett D'Amore 		nchild = nnext;
148695c635efSGarrett D'Amore 	}
1487*260e9a87SYuri Pankov }
1488*260e9a87SYuri Pankov 
1489*260e9a87SYuri Pankov static void
1490*260e9a87SYuri Pankov post_bk(POST_ARGS)
1491*260e9a87SYuri Pankov {
1492*260e9a87SYuri Pankov 	struct mdoc_node	*n;
1493*260e9a87SYuri Pankov 
1494*260e9a87SYuri Pankov 	n = mdoc->last;
149595c635efSGarrett D'Amore 
1496*260e9a87SYuri Pankov 	if (n->type == MDOC_BLOCK && n->body->child == NULL) {
1497*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1498*260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Bk");
1499*260e9a87SYuri Pankov 		mdoc_node_delete(mdoc, n);
1500*260e9a87SYuri Pankov 	}
150195c635efSGarrett D'Amore }
150295c635efSGarrett D'Amore 
1503*260e9a87SYuri Pankov static void
1504*260e9a87SYuri Pankov post_sm(struct mdoc *mdoc)
150595c635efSGarrett D'Amore {
1506*260e9a87SYuri Pankov 	struct mdoc_node	*nch;
150795c635efSGarrett D'Amore 
1508*260e9a87SYuri Pankov 	nch = mdoc->last->child;
1509*260e9a87SYuri Pankov 
1510*260e9a87SYuri Pankov 	if (nch == NULL) {
1511*260e9a87SYuri Pankov 		mdoc->flags ^= MDOC_SMOFF;
1512*260e9a87SYuri Pankov 		return;
151395c635efSGarrett D'Amore 	}
151495c635efSGarrett D'Amore 
1515*260e9a87SYuri Pankov 	assert(nch->type == MDOC_TEXT);
151695c635efSGarrett D'Amore 
1517*260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "on")) {
1518*260e9a87SYuri Pankov 		mdoc->flags &= ~MDOC_SMOFF;
1519*260e9a87SYuri Pankov 		return;
1520698f87a4SGarrett D'Amore 	}
1521*260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "off")) {
1522*260e9a87SYuri Pankov 		mdoc->flags |= MDOC_SMOFF;
1523*260e9a87SYuri Pankov 		return;
1524698f87a4SGarrett D'Amore 	}
152595c635efSGarrett D'Amore 
1526*260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_SM_BAD,
1527*260e9a87SYuri Pankov 	    mdoc->parse, nch->line, nch->pos,
1528*260e9a87SYuri Pankov 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1529*260e9a87SYuri Pankov 	mdoc_node_relink(mdoc, nch);
1530*260e9a87SYuri Pankov 	return;
153195c635efSGarrett D'Amore }
153295c635efSGarrett D'Amore 
1533*260e9a87SYuri Pankov static void
153495c635efSGarrett D'Amore post_root(POST_ARGS)
153595c635efSGarrett D'Amore {
153695c635efSGarrett D'Amore 	struct mdoc_node *n;
153795c635efSGarrett D'Amore 
1538*260e9a87SYuri Pankov 	/* Add missing prologue data. */
153995c635efSGarrett D'Amore 
1540*260e9a87SYuri Pankov 	if (mdoc->meta.date == NULL)
1541*260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ?
1542*260e9a87SYuri Pankov 		    mandoc_strdup("") :
1543*260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
154495c635efSGarrett D'Amore 
1545*260e9a87SYuri Pankov 	if (mdoc->meta.title == NULL) {
1546*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1547*260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, "EOF");
1548*260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
154995c635efSGarrett D'Amore 	}
155095c635efSGarrett D'Amore 
1551*260e9a87SYuri Pankov 	if (mdoc->meta.vol == NULL)
1552*260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup("LOCAL");
155395c635efSGarrett D'Amore 
1554*260e9a87SYuri Pankov 	if (mdoc->meta.os == NULL) {
1555*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_OS_MISSING,
1556*260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, NULL);
1557*260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup("");
155895c635efSGarrett D'Amore 	}
155995c635efSGarrett D'Amore 
1560*260e9a87SYuri Pankov 	/* Check that we begin with a proper `Sh'. */
1561*260e9a87SYuri Pankov 
1562*260e9a87SYuri Pankov 	n = mdoc->first->child;
1563*260e9a87SYuri Pankov 	while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1564*260e9a87SYuri Pankov 		n = n->next;
1565*260e9a87SYuri Pankov 
1566*260e9a87SYuri Pankov 	if (n == NULL)
1567*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1568*260e9a87SYuri Pankov 	else if (n->tok != MDOC_Sh)
1569*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1570*260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
157195c635efSGarrett D'Amore }
157295c635efSGarrett D'Amore 
1573*260e9a87SYuri Pankov static void
157495c635efSGarrett D'Amore post_st(POST_ARGS)
157595c635efSGarrett D'Amore {
1576*260e9a87SYuri Pankov 	struct mdoc_node	 *n, *nch;
157795c635efSGarrett D'Amore 	const char		 *p;
157895c635efSGarrett D'Amore 
1579*260e9a87SYuri Pankov 	n = mdoc->last;
1580*260e9a87SYuri Pankov 	nch = n->child;
158195c635efSGarrett D'Amore 
1582*260e9a87SYuri Pankov 	assert(MDOC_TEXT == nch->type);
158395c635efSGarrett D'Amore 
1584*260e9a87SYuri Pankov 	if (NULL == (p = mdoc_a2st(nch->string))) {
1585*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1586*260e9a87SYuri Pankov 		    nch->line, nch->pos, "St %s", nch->string);
1587*260e9a87SYuri Pankov 		mdoc_node_delete(mdoc, n);
158895c635efSGarrett D'Amore 	} else {
1589*260e9a87SYuri Pankov 		free(nch->string);
1590*260e9a87SYuri Pankov 		nch->string = mandoc_strdup(p);
159195c635efSGarrett D'Amore 	}
159295c635efSGarrett D'Amore }
159395c635efSGarrett D'Amore 
1594*260e9a87SYuri Pankov static void
159595c635efSGarrett D'Amore post_rs(POST_ARGS)
159695c635efSGarrett D'Amore {
1597*260e9a87SYuri Pankov 	struct mdoc_node *np, *nch, *next, *prev;
159895c635efSGarrett D'Amore 	int		  i, j;
159995c635efSGarrett D'Amore 
1600*260e9a87SYuri Pankov 	np = mdoc->last;
160195c635efSGarrett D'Amore 
1602*260e9a87SYuri Pankov 	if (np->type != MDOC_BODY)
1603*260e9a87SYuri Pankov 		return;
160495c635efSGarrett D'Amore 
1605*260e9a87SYuri Pankov 	if (np->child == NULL) {
1606*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1607*260e9a87SYuri Pankov 		    np->line, np->pos, "Rs");
1608*260e9a87SYuri Pankov 		return;
160995c635efSGarrett D'Amore 	}
161095c635efSGarrett D'Amore 
161195c635efSGarrett D'Amore 	/*
161295c635efSGarrett D'Amore 	 * The full `Rs' block needs special handling to order the
161395c635efSGarrett D'Amore 	 * sub-elements according to `rsord'.  Pick through each element
1614*260e9a87SYuri Pankov 	 * and correctly order it.  This is an insertion sort.
161595c635efSGarrett D'Amore 	 */
161695c635efSGarrett D'Amore 
161795c635efSGarrett D'Amore 	next = NULL;
1618*260e9a87SYuri Pankov 	for (nch = np->child->next; nch != NULL; nch = next) {
1619*260e9a87SYuri Pankov 		/* Determine order number of this child. */
162095c635efSGarrett D'Amore 		for (i = 0; i < RSORD_MAX; i++)
1621*260e9a87SYuri Pankov 			if (rsord[i] == nch->tok)
162295c635efSGarrett D'Amore 				break;
162395c635efSGarrett D'Amore 
1624*260e9a87SYuri Pankov 		if (i == RSORD_MAX) {
1625*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_RS_BAD,
1626*260e9a87SYuri Pankov 			    mdoc->parse, nch->line, nch->pos,
1627*260e9a87SYuri Pankov 			    mdoc_macronames[nch->tok]);
1628*260e9a87SYuri Pankov 			i = -1;
1629*260e9a87SYuri Pankov 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1630*260e9a87SYuri Pankov 			np->norm->Rs.quote_T++;
1631*260e9a87SYuri Pankov 
1632*260e9a87SYuri Pankov 		/*
1633*260e9a87SYuri Pankov 		 * Remove this child from the chain.  This somewhat
163495c635efSGarrett D'Amore 		 * repeats mdoc_node_unlink(), but since we're
163595c635efSGarrett D'Amore 		 * just re-ordering, there's no need for the
163695c635efSGarrett D'Amore 		 * full unlink process.
163795c635efSGarrett D'Amore 		 */
163895c635efSGarrett D'Amore 
1639*260e9a87SYuri Pankov 		if ((next = nch->next) != NULL)
1640*260e9a87SYuri Pankov 			next->prev = nch->prev;
164195c635efSGarrett D'Amore 
1642*260e9a87SYuri Pankov 		if ((prev = nch->prev) != NULL)
1643*260e9a87SYuri Pankov 			prev->next = nch->next;
164495c635efSGarrett D'Amore 
1645*260e9a87SYuri Pankov 		nch->prev = nch->next = NULL;
1646*260e9a87SYuri Pankov 
1647*260e9a87SYuri Pankov 		/*
164895c635efSGarrett D'Amore 		 * Scan back until we reach a node that's
1649*260e9a87SYuri Pankov 		 * to be ordered before this child.
165095c635efSGarrett D'Amore 		 */
165195c635efSGarrett D'Amore 
165295c635efSGarrett D'Amore 		for ( ; prev ; prev = prev->prev) {
165395c635efSGarrett D'Amore 			/* Determine order of `prev'. */
165495c635efSGarrett D'Amore 			for (j = 0; j < RSORD_MAX; j++)
165595c635efSGarrett D'Amore 				if (rsord[j] == prev->tok)
165695c635efSGarrett D'Amore 					break;
1657*260e9a87SYuri Pankov 			if (j == RSORD_MAX)
1658*260e9a87SYuri Pankov 				j = -1;
165995c635efSGarrett D'Amore 
166095c635efSGarrett D'Amore 			if (j <= i)
166195c635efSGarrett D'Amore 				break;
166295c635efSGarrett D'Amore 		}
166395c635efSGarrett D'Amore 
166495c635efSGarrett D'Amore 		/*
1665*260e9a87SYuri Pankov 		 * Set this child back into its correct place
1666*260e9a87SYuri Pankov 		 * in front of the `prev' node.
166795c635efSGarrett D'Amore 		 */
166895c635efSGarrett D'Amore 
1669*260e9a87SYuri Pankov 		nch->prev = prev;
167095c635efSGarrett D'Amore 
1671*260e9a87SYuri Pankov 		if (prev == NULL) {
1672*260e9a87SYuri Pankov 			np->child->prev = nch;
1673*260e9a87SYuri Pankov 			nch->next = np->child;
1674*260e9a87SYuri Pankov 			np->child = nch;
167595c635efSGarrett D'Amore 		} else {
1676*260e9a87SYuri Pankov 			if (prev->next)
1677*260e9a87SYuri Pankov 				prev->next->prev = nch;
1678*260e9a87SYuri Pankov 			nch->next = prev->next;
1679*260e9a87SYuri Pankov 			prev->next = nch;
168095c635efSGarrett D'Amore 		}
168195c635efSGarrett D'Amore 	}
168295c635efSGarrett D'Amore }
168395c635efSGarrett D'Amore 
1684698f87a4SGarrett D'Amore /*
1685698f87a4SGarrett D'Amore  * For some arguments of some macros,
1686698f87a4SGarrett D'Amore  * convert all breakable hyphens into ASCII_HYPH.
1687698f87a4SGarrett D'Amore  */
1688*260e9a87SYuri Pankov static void
1689698f87a4SGarrett D'Amore post_hyph(POST_ARGS)
1690698f87a4SGarrett D'Amore {
1691*260e9a87SYuri Pankov 	struct mdoc_node	*nch;
1692698f87a4SGarrett D'Amore 	char			*cp;
1693698f87a4SGarrett D'Amore 
1694*260e9a87SYuri Pankov 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1695*260e9a87SYuri Pankov 		if (nch->type != MDOC_TEXT)
1696698f87a4SGarrett D'Amore 			continue;
1697698f87a4SGarrett D'Amore 		cp = nch->string;
1698*260e9a87SYuri Pankov 		if (*cp == '\0')
1699698f87a4SGarrett D'Amore 			continue;
1700*260e9a87SYuri Pankov 		while (*(++cp) != '\0')
1701*260e9a87SYuri Pankov 			if (*cp == '-' &&
1702698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[-1]) &&
1703698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[1]))
1704698f87a4SGarrett D'Amore 				*cp = ASCII_HYPH;
1705698f87a4SGarrett D'Amore 	}
1706698f87a4SGarrett D'Amore }
1707698f87a4SGarrett D'Amore 
1708*260e9a87SYuri Pankov static void
170995c635efSGarrett D'Amore post_ns(POST_ARGS)
171095c635efSGarrett D'Amore {
171195c635efSGarrett D'Amore 
171295c635efSGarrett D'Amore 	if (MDOC_LINE & mdoc->last->flags)
1713*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1714*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
171595c635efSGarrett D'Amore }
171695c635efSGarrett D'Amore 
1717*260e9a87SYuri Pankov static void
171895c635efSGarrett D'Amore post_sh(POST_ARGS)
171995c635efSGarrett D'Amore {
172095c635efSGarrett D'Amore 
1721*260e9a87SYuri Pankov 	post_ignpar(mdoc);
172295c635efSGarrett D'Amore 
1723*260e9a87SYuri Pankov 	switch (mdoc->last->type) {
1724*260e9a87SYuri Pankov 	case MDOC_HEAD:
1725*260e9a87SYuri Pankov 		post_sh_head(mdoc);
1726*260e9a87SYuri Pankov 		break;
1727*260e9a87SYuri Pankov 	case MDOC_BODY:
1728*260e9a87SYuri Pankov 		switch (mdoc->lastsec)  {
1729*260e9a87SYuri Pankov 		case SEC_NAME:
1730*260e9a87SYuri Pankov 			post_sh_name(mdoc);
1731*260e9a87SYuri Pankov 			break;
1732*260e9a87SYuri Pankov 		case SEC_SEE_ALSO:
1733*260e9a87SYuri Pankov 			post_sh_see_also(mdoc);
1734*260e9a87SYuri Pankov 			break;
1735*260e9a87SYuri Pankov 		case SEC_AUTHORS:
1736*260e9a87SYuri Pankov 			post_sh_authors(mdoc);
1737*260e9a87SYuri Pankov 			break;
1738*260e9a87SYuri Pankov 		default:
1739*260e9a87SYuri Pankov 			break;
1740*260e9a87SYuri Pankov 		}
1741*260e9a87SYuri Pankov 		break;
1742*260e9a87SYuri Pankov 	default:
1743*260e9a87SYuri Pankov 		break;
1744*260e9a87SYuri Pankov 	}
174595c635efSGarrett D'Amore }
174695c635efSGarrett D'Amore 
1747*260e9a87SYuri Pankov static void
1748*260e9a87SYuri Pankov post_sh_name(POST_ARGS)
174995c635efSGarrett D'Amore {
175095c635efSGarrett D'Amore 	struct mdoc_node *n;
1751*260e9a87SYuri Pankov 	int hasnm, hasnd;
175295c635efSGarrett D'Amore 
1753*260e9a87SYuri Pankov 	hasnm = hasnd = 0;
175495c635efSGarrett D'Amore 
1755*260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1756*260e9a87SYuri Pankov 		switch (n->tok) {
1757*260e9a87SYuri Pankov 		case MDOC_Nm:
1758*260e9a87SYuri Pankov 			hasnm = 1;
1759*260e9a87SYuri Pankov 			break;
1760*260e9a87SYuri Pankov 		case MDOC_Nd:
1761*260e9a87SYuri Pankov 			hasnd = 1;
1762*260e9a87SYuri Pankov 			if (n->next != NULL)
1763*260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1764*260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos, NULL);
1765*260e9a87SYuri Pankov 			break;
1766*260e9a87SYuri Pankov 		case MDOC_MAX:
1767*260e9a87SYuri Pankov 			if (hasnm)
1768*260e9a87SYuri Pankov 				break;
1769*260e9a87SYuri Pankov 			/* FALLTHROUGH */
1770*260e9a87SYuri Pankov 		default:
1771*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1772*260e9a87SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
1773*260e9a87SYuri Pankov 			break;
1774*260e9a87SYuri Pankov 		}
177595c635efSGarrett D'Amore 	}
177695c635efSGarrett D'Amore 
1777*260e9a87SYuri Pankov 	if ( ! hasnm)
1778*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1779*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1780*260e9a87SYuri Pankov 	if ( ! hasnd)
1781*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1782*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1783*260e9a87SYuri Pankov }
1784*260e9a87SYuri Pankov 
1785*260e9a87SYuri Pankov static void
1786*260e9a87SYuri Pankov post_sh_see_also(POST_ARGS)
1787*260e9a87SYuri Pankov {
1788*260e9a87SYuri Pankov 	const struct mdoc_node	*n;
1789*260e9a87SYuri Pankov 	const char		*name, *sec;
1790*260e9a87SYuri Pankov 	const char		*lastname, *lastsec, *lastpunct;
1791*260e9a87SYuri Pankov 	int			 cmp;
1792*260e9a87SYuri Pankov 
1793*260e9a87SYuri Pankov 	n = mdoc->last->child;
1794*260e9a87SYuri Pankov 	lastname = lastsec = lastpunct = NULL;
1795*260e9a87SYuri Pankov 	while (n != NULL) {
1796*260e9a87SYuri Pankov 		if (n->tok != MDOC_Xr || n->nchild < 2)
1797*260e9a87SYuri Pankov 			break;
1798*260e9a87SYuri Pankov 
1799*260e9a87SYuri Pankov 		/* Process one .Xr node. */
1800*260e9a87SYuri Pankov 
1801*260e9a87SYuri Pankov 		name = n->child->string;
1802*260e9a87SYuri Pankov 		sec = n->child->next->string;
1803*260e9a87SYuri Pankov 		if (lastsec != NULL) {
1804*260e9a87SYuri Pankov 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1805*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1806*260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1807*260e9a87SYuri Pankov 				    "%s before %s(%s)", lastpunct,
1808*260e9a87SYuri Pankov 				    name, sec);
1809*260e9a87SYuri Pankov 			cmp = strcmp(lastsec, sec);
1810*260e9a87SYuri Pankov 			if (cmp > 0)
1811*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1812*260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1813*260e9a87SYuri Pankov 				    "%s(%s) after %s(%s)", name,
1814*260e9a87SYuri Pankov 				    sec, lastname, lastsec);
1815*260e9a87SYuri Pankov 			else if (cmp == 0 &&
1816*260e9a87SYuri Pankov 			    strcasecmp(lastname, name) > 0)
1817*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1818*260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1819*260e9a87SYuri Pankov 				    "%s after %s", name, lastname);
1820*260e9a87SYuri Pankov 		}
1821*260e9a87SYuri Pankov 		lastname = name;
1822*260e9a87SYuri Pankov 		lastsec = sec;
1823*260e9a87SYuri Pankov 
1824*260e9a87SYuri Pankov 		/* Process the following node. */
1825*260e9a87SYuri Pankov 
1826*260e9a87SYuri Pankov 		n = n->next;
1827*260e9a87SYuri Pankov 		if (n == NULL)
1828*260e9a87SYuri Pankov 			break;
1829*260e9a87SYuri Pankov 		if (n->tok == MDOC_Xr) {
1830*260e9a87SYuri Pankov 			lastpunct = "none";
183195c635efSGarrett D'Amore 			continue;
1832*260e9a87SYuri Pankov 		}
1833*260e9a87SYuri Pankov 		if (n->type != MDOC_TEXT)
1834*260e9a87SYuri Pankov 			break;
1835*260e9a87SYuri Pankov 		for (name = n->string; *name != '\0'; name++)
1836*260e9a87SYuri Pankov 			if (isalpha((const unsigned char)*name))
1837*260e9a87SYuri Pankov 				return;
1838*260e9a87SYuri Pankov 		lastpunct = n->string;
1839*260e9a87SYuri Pankov 		if (n->next == NULL)
1840*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1841*260e9a87SYuri Pankov 			    n->line, n->pos, "%s after %s(%s)",
1842*260e9a87SYuri Pankov 			    lastpunct, lastname, lastsec);
1843*260e9a87SYuri Pankov 		n = n->next;
184495c635efSGarrett D'Amore 	}
1845*260e9a87SYuri Pankov }
184695c635efSGarrett D'Amore 
1847*260e9a87SYuri Pankov static int
1848*260e9a87SYuri Pankov child_an(const struct mdoc_node *n)
1849*260e9a87SYuri Pankov {
185095c635efSGarrett D'Amore 
1851*260e9a87SYuri Pankov 	for (n = n->child; n != NULL; n = n->next)
1852*260e9a87SYuri Pankov 		if ((n->tok == MDOC_An && n->nchild) || child_an(n))
1853*260e9a87SYuri Pankov 			return(1);
1854*260e9a87SYuri Pankov 	return(0);
185595c635efSGarrett D'Amore }
185695c635efSGarrett D'Amore 
1857*260e9a87SYuri Pankov static void
1858*260e9a87SYuri Pankov post_sh_authors(POST_ARGS)
1859*260e9a87SYuri Pankov {
1860*260e9a87SYuri Pankov 
1861*260e9a87SYuri Pankov 	if ( ! child_an(mdoc->last))
1862*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1863*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1864*260e9a87SYuri Pankov }
1865*260e9a87SYuri Pankov 
1866*260e9a87SYuri Pankov static void
186795c635efSGarrett D'Amore post_sh_head(POST_ARGS)
186895c635efSGarrett D'Amore {
186995c635efSGarrett D'Amore 	struct mdoc_node *n;
1870*260e9a87SYuri Pankov 	const char	*goodsec;
1871*260e9a87SYuri Pankov 	char		*secname;
187295c635efSGarrett D'Amore 	enum mdoc_sec	 sec;
187395c635efSGarrett D'Amore 
187495c635efSGarrett D'Amore 	/*
187595c635efSGarrett D'Amore 	 * Process a new section.  Sections are either "named" or
187695c635efSGarrett D'Amore 	 * "custom".  Custom sections are user-defined, while named ones
187795c635efSGarrett D'Amore 	 * follow a conventional order and may only appear in certain
187895c635efSGarrett D'Amore 	 * manual sections.
187995c635efSGarrett D'Amore 	 */
188095c635efSGarrett D'Amore 
1881*260e9a87SYuri Pankov 	secname = NULL;
188295c635efSGarrett D'Amore 	sec = SEC_CUSTOM;
1883*260e9a87SYuri Pankov 	mdoc_deroff(&secname, mdoc->last);
1884*260e9a87SYuri Pankov 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
188595c635efSGarrett D'Amore 
188695c635efSGarrett D'Amore 	/* The NAME should be first. */
188795c635efSGarrett D'Amore 
188895c635efSGarrett D'Amore 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1889*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1890*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1891*260e9a87SYuri Pankov 		    "Sh %s", secname);
189295c635efSGarrett D'Amore 
189395c635efSGarrett D'Amore 	/* The SYNOPSIS gets special attention in other areas. */
189495c635efSGarrett D'Amore 
1895698f87a4SGarrett D'Amore 	if (SEC_SYNOPSIS == sec) {
1896698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 1, '=');
189795c635efSGarrett D'Amore 		mdoc->flags |= MDOC_SYNOPSIS;
1898698f87a4SGarrett D'Amore 	} else {
1899698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 0, '=');
190095c635efSGarrett D'Amore 		mdoc->flags &= ~MDOC_SYNOPSIS;
1901698f87a4SGarrett D'Amore 	}
190295c635efSGarrett D'Amore 
190395c635efSGarrett D'Amore 	/* Mark our last section. */
190495c635efSGarrett D'Amore 
190595c635efSGarrett D'Amore 	mdoc->lastsec = sec;
190695c635efSGarrett D'Amore 
190795c635efSGarrett D'Amore 	/*
190895c635efSGarrett D'Amore 	 * Set the section attribute for the current HEAD, for its
190995c635efSGarrett D'Amore 	 * parent BLOCK, and for the HEAD children; the latter can
191095c635efSGarrett D'Amore 	 * only be TEXT nodes, so no recursion is needed.
191195c635efSGarrett D'Amore 	 * For other blocks and elements, including .Sh BODY, this is
191295c635efSGarrett D'Amore 	 * done when allocating the node data structures, but for .Sh
191395c635efSGarrett D'Amore 	 * BLOCK and HEAD, the section is still unknown at that time.
191495c635efSGarrett D'Amore 	 */
191595c635efSGarrett D'Amore 
191695c635efSGarrett D'Amore 	mdoc->last->parent->sec = sec;
191795c635efSGarrett D'Amore 	mdoc->last->sec = sec;
191895c635efSGarrett D'Amore 	for (n = mdoc->last->child; n; n = n->next)
191995c635efSGarrett D'Amore 		n->sec = sec;
192095c635efSGarrett D'Amore 
192195c635efSGarrett D'Amore 	/* We don't care about custom sections after this. */
192295c635efSGarrett D'Amore 
1923*260e9a87SYuri Pankov 	if (SEC_CUSTOM == sec) {
1924*260e9a87SYuri Pankov 		free(secname);
1925*260e9a87SYuri Pankov 		return;
1926*260e9a87SYuri Pankov 	}
192795c635efSGarrett D'Amore 
192895c635efSGarrett D'Amore 	/*
192995c635efSGarrett D'Amore 	 * Check whether our non-custom section is being repeated or is
193095c635efSGarrett D'Amore 	 * out of order.
193195c635efSGarrett D'Amore 	 */
193295c635efSGarrett D'Amore 
193395c635efSGarrett D'Amore 	if (sec == mdoc->lastnamed)
1934*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1935*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1936*260e9a87SYuri Pankov 		    "Sh %s", secname);
193795c635efSGarrett D'Amore 
193895c635efSGarrett D'Amore 	if (sec < mdoc->lastnamed)
1939*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1940*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1941*260e9a87SYuri Pankov 		    "Sh %s", secname);
194295c635efSGarrett D'Amore 
194395c635efSGarrett D'Amore 	/* Mark the last named section. */
194495c635efSGarrett D'Amore 
194595c635efSGarrett D'Amore 	mdoc->lastnamed = sec;
194695c635efSGarrett D'Amore 
194795c635efSGarrett D'Amore 	/* Check particular section/manual conventions. */
194895c635efSGarrett D'Amore 
1949*260e9a87SYuri Pankov 	if (mdoc->meta.msec == NULL) {
1950*260e9a87SYuri Pankov 		free(secname);
1951*260e9a87SYuri Pankov 		return;
1952*260e9a87SYuri Pankov 	}
195395c635efSGarrett D'Amore 
1954*260e9a87SYuri Pankov 	goodsec = NULL;
195595c635efSGarrett D'Amore 	switch (sec) {
1956*260e9a87SYuri Pankov 	case SEC_ERRORS:
1957*260e9a87SYuri Pankov 		if (*mdoc->meta.msec == '4')
1958*260e9a87SYuri Pankov 			break;
1959*260e9a87SYuri Pankov 		goodsec = "2, 3, 4, 9";
196095c635efSGarrett D'Amore 		/* FALLTHROUGH */
1961*260e9a87SYuri Pankov 	case SEC_RETURN_VALUES:
196295c635efSGarrett D'Amore 		/* FALLTHROUGH */
1963*260e9a87SYuri Pankov 	case SEC_LIBRARY:
196495c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '2')
196595c635efSGarrett D'Amore 			break;
196695c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '3')
196795c635efSGarrett D'Amore 			break;
1968*260e9a87SYuri Pankov 		if (NULL == goodsec)
1969*260e9a87SYuri Pankov 			goodsec = "2, 3, 9";
1970*260e9a87SYuri Pankov 		/* FALLTHROUGH */
1971*260e9a87SYuri Pankov 	case SEC_CONTEXT:
197295c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '9')
197395c635efSGarrett D'Amore 			break;
1974*260e9a87SYuri Pankov 		if (NULL == goodsec)
1975*260e9a87SYuri Pankov 			goodsec = "9";
1976*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1977*260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1978*260e9a87SYuri Pankov 		    "Sh %s for %s only", secname, goodsec);
197995c635efSGarrett D'Amore 		break;
198095c635efSGarrett D'Amore 	default:
198195c635efSGarrett D'Amore 		break;
198295c635efSGarrett D'Amore 	}
1983*260e9a87SYuri Pankov 	free(secname);
198495c635efSGarrett D'Amore }
198595c635efSGarrett D'Amore 
1986*260e9a87SYuri Pankov static void
198795c635efSGarrett D'Amore post_ignpar(POST_ARGS)
198895c635efSGarrett D'Amore {
198995c635efSGarrett D'Amore 	struct mdoc_node *np;
199095c635efSGarrett D'Amore 
1991*260e9a87SYuri Pankov 	switch (mdoc->last->type) {
1992*260e9a87SYuri Pankov 	case MDOC_HEAD:
1993*260e9a87SYuri Pankov 		post_hyph(mdoc);
1994*260e9a87SYuri Pankov 		return;
1995*260e9a87SYuri Pankov 	case MDOC_BODY:
1996*260e9a87SYuri Pankov 		break;
1997*260e9a87SYuri Pankov 	default:
1998*260e9a87SYuri Pankov 		return;
1999*260e9a87SYuri Pankov 	}
200095c635efSGarrett D'Amore 
200195c635efSGarrett D'Amore 	if (NULL != (np = mdoc->last->child))
200295c635efSGarrett D'Amore 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2003*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
2004*260e9a87SYuri Pankov 			    mdoc->parse, np->line, np->pos,
2005*260e9a87SYuri Pankov 			    "%s after %s", mdoc_macronames[np->tok],
2006*260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
200795c635efSGarrett D'Amore 			mdoc_node_delete(mdoc, np);
200895c635efSGarrett D'Amore 		}
200995c635efSGarrett D'Amore 
201095c635efSGarrett D'Amore 	if (NULL != (np = mdoc->last->last))
201195c635efSGarrett D'Amore 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
2012*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2013*260e9a87SYuri Pankov 			    np->line, np->pos, "%s at the end of %s",
2014*260e9a87SYuri Pankov 			    mdoc_macronames[np->tok],
2015*260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
201695c635efSGarrett D'Amore 			mdoc_node_delete(mdoc, np);
201795c635efSGarrett D'Amore 		}
201895c635efSGarrett D'Amore }
201995c635efSGarrett D'Amore 
2020*260e9a87SYuri Pankov static void
202195c635efSGarrett D'Amore pre_par(PRE_ARGS)
202295c635efSGarrett D'Amore {
202395c635efSGarrett D'Amore 
202495c635efSGarrett D'Amore 	if (NULL == mdoc->last)
2025*260e9a87SYuri Pankov 		return;
202695c635efSGarrett D'Amore 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2027*260e9a87SYuri Pankov 		return;
202895c635efSGarrett D'Amore 
2029*260e9a87SYuri Pankov 	/*
203095c635efSGarrett D'Amore 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
203195c635efSGarrett D'Amore 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
203295c635efSGarrett D'Amore 	 */
203395c635efSGarrett D'Amore 
2034698f87a4SGarrett D'Amore 	if (MDOC_Pp != mdoc->last->tok &&
2035698f87a4SGarrett D'Amore 	    MDOC_Lp != mdoc->last->tok &&
2036698f87a4SGarrett D'Amore 	    MDOC_br != mdoc->last->tok)
2037*260e9a87SYuri Pankov 		return;
203895c635efSGarrett D'Amore 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2039*260e9a87SYuri Pankov 		return;
204095c635efSGarrett D'Amore 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2041*260e9a87SYuri Pankov 		return;
204295c635efSGarrett D'Amore 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2043*260e9a87SYuri Pankov 		return;
204495c635efSGarrett D'Amore 
2045*260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2046*260e9a87SYuri Pankov 	    mdoc->last->line, mdoc->last->pos,
2047*260e9a87SYuri Pankov 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
2048*260e9a87SYuri Pankov 	    mdoc_macronames[n->tok]);
204995c635efSGarrett D'Amore 	mdoc_node_delete(mdoc, mdoc->last);
205095c635efSGarrett D'Amore }
205195c635efSGarrett D'Amore 
2052*260e9a87SYuri Pankov static void
2053698f87a4SGarrett D'Amore post_par(POST_ARGS)
2054698f87a4SGarrett D'Amore {
2055*260e9a87SYuri Pankov 	struct mdoc_node *np;
2056698f87a4SGarrett D'Amore 
2057*260e9a87SYuri Pankov 	np = mdoc->last;
2058698f87a4SGarrett D'Amore 
2059*260e9a87SYuri Pankov 	if (np->tok == MDOC_sp) {
2060*260e9a87SYuri Pankov 		if (np->nchild > 1)
2061*260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2062*260e9a87SYuri Pankov 			    np->child->next->line, np->child->next->pos,
2063*260e9a87SYuri Pankov 			    "sp ... %s", np->child->next->string);
2064*260e9a87SYuri Pankov 	} else if (np->child != NULL)
2065*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2066*260e9a87SYuri Pankov 		    mdoc->parse, np->line, np->pos, "%s %s",
2067*260e9a87SYuri Pankov 		    mdoc_macronames[np->tok], np->child->string);
2068*260e9a87SYuri Pankov 
2069*260e9a87SYuri Pankov 	if (NULL == (np = mdoc->last->prev)) {
2070*260e9a87SYuri Pankov 		np = mdoc->last->parent;
2071*260e9a87SYuri Pankov 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
2072*260e9a87SYuri Pankov 			return;
2073*260e9a87SYuri Pankov 	} else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
2074*260e9a87SYuri Pankov 	    (MDOC_br != mdoc->last->tok ||
2075*260e9a87SYuri Pankov 	     (MDOC_sp != np->tok && MDOC_br != np->tok)))
2076*260e9a87SYuri Pankov 		return;
2077698f87a4SGarrett D'Amore 
2078*260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2079*260e9a87SYuri Pankov 	    mdoc->last->line, mdoc->last->pos,
2080*260e9a87SYuri Pankov 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
2081*260e9a87SYuri Pankov 	    mdoc_macronames[np->tok]);
2082698f87a4SGarrett D'Amore 	mdoc_node_delete(mdoc, mdoc->last);
2083698f87a4SGarrett D'Amore }
2084698f87a4SGarrett D'Amore 
2085*260e9a87SYuri Pankov static void
208695c635efSGarrett D'Amore pre_literal(PRE_ARGS)
208795c635efSGarrett D'Amore {
208895c635efSGarrett D'Amore 
2089*260e9a87SYuri Pankov 	pre_display(mdoc, n);
2090*260e9a87SYuri Pankov 
209195c635efSGarrett D'Amore 	if (MDOC_BODY != n->type)
2092*260e9a87SYuri Pankov 		return;
209395c635efSGarrett D'Amore 
209495c635efSGarrett D'Amore 	/*
209595c635efSGarrett D'Amore 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
209695c635efSGarrett D'Amore 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
209795c635efSGarrett D'Amore 	 */
209895c635efSGarrett D'Amore 
209995c635efSGarrett D'Amore 	switch (n->tok) {
2100*260e9a87SYuri Pankov 	case MDOC_Dl:
210195c635efSGarrett D'Amore 		mdoc->flags |= MDOC_LITERAL;
210295c635efSGarrett D'Amore 		break;
2103*260e9a87SYuri Pankov 	case MDOC_Bd:
210495c635efSGarrett D'Amore 		if (DISP_literal == n->norm->Bd.type)
210595c635efSGarrett D'Amore 			mdoc->flags |= MDOC_LITERAL;
210695c635efSGarrett D'Amore 		if (DISP_unfilled == n->norm->Bd.type)
210795c635efSGarrett D'Amore 			mdoc->flags |= MDOC_LITERAL;
210895c635efSGarrett D'Amore 		break;
210995c635efSGarrett D'Amore 	default:
211095c635efSGarrett D'Amore 		abort();
211195c635efSGarrett D'Amore 		/* NOTREACHED */
211295c635efSGarrett D'Amore 	}
211395c635efSGarrett D'Amore }
211495c635efSGarrett D'Amore 
2115*260e9a87SYuri Pankov static void
211695c635efSGarrett D'Amore post_dd(POST_ARGS)
211795c635efSGarrett D'Amore {
211895c635efSGarrett D'Amore 	struct mdoc_node *n;
2119*260e9a87SYuri Pankov 	char		 *datestr;
212095c635efSGarrett D'Amore 
212195c635efSGarrett D'Amore 	if (mdoc->meta.date)
212295c635efSGarrett D'Amore 		free(mdoc->meta.date);
212395c635efSGarrett D'Amore 
212495c635efSGarrett D'Amore 	n = mdoc->last;
212595c635efSGarrett D'Amore 	if (NULL == n->child || '\0' == n->child->string[0]) {
2126*260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2127*260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2128*260e9a87SYuri Pankov 		goto out;
212995c635efSGarrett D'Amore 	}
213095c635efSGarrett D'Amore 
2131*260e9a87SYuri Pankov 	datestr = NULL;
2132*260e9a87SYuri Pankov 	mdoc_deroff(&datestr, n);
2133*260e9a87SYuri Pankov 	if (mdoc->quick)
2134*260e9a87SYuri Pankov 		mdoc->meta.date = datestr;
2135*260e9a87SYuri Pankov 	else {
2136*260e9a87SYuri Pankov 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
2137*260e9a87SYuri Pankov 		    datestr, n->line, n->pos);
2138*260e9a87SYuri Pankov 		free(datestr);
213995c635efSGarrett D'Amore 	}
2140*260e9a87SYuri Pankov out:
2141*260e9a87SYuri Pankov 	mdoc_node_delete(mdoc, n);
214295c635efSGarrett D'Amore }
214395c635efSGarrett D'Amore 
2144*260e9a87SYuri Pankov static void
214595c635efSGarrett D'Amore post_dt(POST_ARGS)
214695c635efSGarrett D'Amore {
214795c635efSGarrett D'Amore 	struct mdoc_node *nn, *n;
214895c635efSGarrett D'Amore 	const char	 *cp;
214995c635efSGarrett D'Amore 	char		 *p;
215095c635efSGarrett D'Amore 
215195c635efSGarrett D'Amore 	n = mdoc->last;
215295c635efSGarrett D'Amore 
2153*260e9a87SYuri Pankov 	free(mdoc->meta.title);
2154*260e9a87SYuri Pankov 	free(mdoc->meta.msec);
2155*260e9a87SYuri Pankov 	free(mdoc->meta.vol);
2156*260e9a87SYuri Pankov 	free(mdoc->meta.arch);
215795c635efSGarrett D'Amore 
2158*260e9a87SYuri Pankov 	mdoc->meta.title = NULL;
2159*260e9a87SYuri Pankov 	mdoc->meta.msec = NULL;
2160*260e9a87SYuri Pankov 	mdoc->meta.vol = NULL;
2161*260e9a87SYuri Pankov 	mdoc->meta.arch = NULL;
216295c635efSGarrett D'Amore 
2163*260e9a87SYuri Pankov 	/* Mandatory first argument: title. */
216495c635efSGarrett D'Amore 
2165*260e9a87SYuri Pankov 	nn = n->child;
2166*260e9a87SYuri Pankov 	if (nn == NULL || *nn->string == '\0') {
2167*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2168*260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Dt");
2169*260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2170*260e9a87SYuri Pankov 	} else {
2171*260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup(nn->string);
217295c635efSGarrett D'Amore 
2173*260e9a87SYuri Pankov 		/* Check that all characters are uppercase. */
217495c635efSGarrett D'Amore 
2175*260e9a87SYuri Pankov 		for (p = nn->string; *p != '\0'; p++)
2176*260e9a87SYuri Pankov 			if (islower((unsigned char)*p)) {
2177*260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2178*260e9a87SYuri Pankov 				    mdoc->parse, nn->line,
2179*260e9a87SYuri Pankov 				    nn->pos + (p - nn->string),
2180*260e9a87SYuri Pankov 				    "Dt %s", nn->string);
2181*260e9a87SYuri Pankov 				break;
2182*260e9a87SYuri Pankov 			}
218395c635efSGarrett D'Amore 	}
218495c635efSGarrett D'Amore 
2185*260e9a87SYuri Pankov 	/* Mandatory second argument: section.�*/
218695c635efSGarrett D'Amore 
2187*260e9a87SYuri Pankov 	if (nn != NULL)
2188*260e9a87SYuri Pankov 		nn = nn->next;
218995c635efSGarrett D'Amore 
2190*260e9a87SYuri Pankov 	if (nn == NULL) {
2191*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2192*260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
2193*260e9a87SYuri Pankov 		    "Dt %s", mdoc->meta.title);
219495c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2195*260e9a87SYuri Pankov 		goto out;  /* msec and arch remain NULL. */
219695c635efSGarrett D'Amore 	}
219795c635efSGarrett D'Amore 
2198*260e9a87SYuri Pankov 	mdoc->meta.msec = mandoc_strdup(nn->string);
2199*260e9a87SYuri Pankov 
2200*260e9a87SYuri Pankov 	/* Infer volume title from section number. */
220195c635efSGarrett D'Amore 
220295c635efSGarrett D'Amore 	cp = mandoc_a2msec(nn->string);
2203*260e9a87SYuri Pankov 	if (cp == NULL) {
2204*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2205*260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
220695c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup(nn->string);
2207*260e9a87SYuri Pankov 	} else
2208*260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup(cp);
220995c635efSGarrett D'Amore 
2210*260e9a87SYuri Pankov 	/* Optional third argument: architecture. */
221195c635efSGarrett D'Amore 
2212*260e9a87SYuri Pankov 	if ((nn = nn->next) == NULL)
2213*260e9a87SYuri Pankov 		goto out;
221495c635efSGarrett D'Amore 
2215*260e9a87SYuri Pankov 	for (p = nn->string; *p != '\0'; p++)
2216*260e9a87SYuri Pankov 		*p = tolower((unsigned char)*p);
2217*260e9a87SYuri Pankov 	mdoc->meta.arch = mandoc_strdup(nn->string);
221895c635efSGarrett D'Amore 
2219*260e9a87SYuri Pankov 	/* Ignore fourth and later arguments. */
222095c635efSGarrett D'Amore 
2221*260e9a87SYuri Pankov 	if ((nn = nn->next) != NULL)
2222*260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2223*260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
222495c635efSGarrett D'Amore 
2225*260e9a87SYuri Pankov out:
2226*260e9a87SYuri Pankov 	mdoc_node_delete(mdoc, n);
222795c635efSGarrett D'Amore }
222895c635efSGarrett D'Amore 
2229*260e9a87SYuri Pankov static void
223095c635efSGarrett D'Amore post_bx(POST_ARGS)
223195c635efSGarrett D'Amore {
223295c635efSGarrett D'Amore 	struct mdoc_node	*n;
223395c635efSGarrett D'Amore 
2234*260e9a87SYuri Pankov 	/*
223595c635efSGarrett D'Amore 	 * Make `Bx's second argument always start with an uppercase
223695c635efSGarrett D'Amore 	 * letter.  Groff checks if it's an "accepted" term, but we just
223795c635efSGarrett D'Amore 	 * uppercase blindly.
223895c635efSGarrett D'Amore 	 */
223995c635efSGarrett D'Amore 
224095c635efSGarrett D'Amore 	n = mdoc->last->child;
224195c635efSGarrett D'Amore 	if (n && NULL != (n = n->next))
2242*260e9a87SYuri Pankov 		*n->string = (char)toupper((unsigned char)*n->string);
224395c635efSGarrett D'Amore }
224495c635efSGarrett D'Amore 
2245*260e9a87SYuri Pankov static void
224695c635efSGarrett D'Amore post_os(POST_ARGS)
224795c635efSGarrett D'Amore {
224895c635efSGarrett D'Amore #ifndef OSNAME
224995c635efSGarrett D'Amore 	struct utsname	  utsname;
2250*260e9a87SYuri Pankov 	static char	 *defbuf;
225195c635efSGarrett D'Amore #endif
2252*260e9a87SYuri Pankov 	struct mdoc_node *n;
225395c635efSGarrett D'Amore 
225495c635efSGarrett D'Amore 	n = mdoc->last;
225595c635efSGarrett D'Amore 
225695c635efSGarrett D'Amore 	/*
2257698f87a4SGarrett D'Amore 	 * Set the operating system by way of the `Os' macro.
2258698f87a4SGarrett D'Amore 	 * The order of precedence is:
2259698f87a4SGarrett D'Amore 	 * 1. the argument of the `Os' macro, unless empty
2260698f87a4SGarrett D'Amore 	 * 2. the -Ios=foo command line argument, if provided
2261698f87a4SGarrett D'Amore 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2262698f87a4SGarrett D'Amore 	 * 4. "sysname release" from uname(3)
2263*260e9a87SYuri Pankov 	 */
226495c635efSGarrett D'Amore 
2265698f87a4SGarrett D'Amore 	free(mdoc->meta.os);
2266*260e9a87SYuri Pankov 	mdoc->meta.os = NULL;
2267*260e9a87SYuri Pankov 	mdoc_deroff(&mdoc->meta.os, n);
2268*260e9a87SYuri Pankov 	if (mdoc->meta.os)
2269*260e9a87SYuri Pankov 		goto out;
227095c635efSGarrett D'Amore 
2271*260e9a87SYuri Pankov 	if (mdoc->defos) {
2272*260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2273*260e9a87SYuri Pankov 		goto out;
227495c635efSGarrett D'Amore 	}
227595c635efSGarrett D'Amore 
227695c635efSGarrett D'Amore #ifdef OSNAME
2277*260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(OSNAME);
227895c635efSGarrett D'Amore #else /*!OSNAME */
2279*260e9a87SYuri Pankov 	if (NULL == defbuf) {
228095c635efSGarrett D'Amore 		if (-1 == uname(&utsname)) {
2281*260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2282*260e9a87SYuri Pankov 			    n->line, n->pos, "Os");
2283*260e9a87SYuri Pankov 			defbuf = mandoc_strdup("UNKNOWN");
2284*260e9a87SYuri Pankov 		} else
2285*260e9a87SYuri Pankov 			mandoc_asprintf(&defbuf, "%s %s",
2286*260e9a87SYuri Pankov 			    utsname.sysname, utsname.release);
228795c635efSGarrett D'Amore 	}
2288*260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(defbuf);
2289*260e9a87SYuri Pankov #endif /*!OSNAME*/
229095c635efSGarrett D'Amore 
2291*260e9a87SYuri Pankov out:
2292*260e9a87SYuri Pankov 	mdoc_node_delete(mdoc, n);
229395c635efSGarrett D'Amore }
229495c635efSGarrett D'Amore 
2295*260e9a87SYuri Pankov /*
2296*260e9a87SYuri Pankov  * If no argument is provided,
2297*260e9a87SYuri Pankov  * fill in the name of the current manual page.
2298*260e9a87SYuri Pankov  */
2299*260e9a87SYuri Pankov static void
2300*260e9a87SYuri Pankov post_ex(POST_ARGS)
230195c635efSGarrett D'Amore {
2302*260e9a87SYuri Pankov 	struct mdoc_node *n;
230395c635efSGarrett D'Amore 
230495c635efSGarrett D'Amore 	n = mdoc->last;
230595c635efSGarrett D'Amore 
230695c635efSGarrett D'Amore 	if (n->child)
2307*260e9a87SYuri Pankov 		return;
230895c635efSGarrett D'Amore 
2309*260e9a87SYuri Pankov 	if (mdoc->meta.name == NULL) {
2310*260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
2311*260e9a87SYuri Pankov 		    n->line, n->pos, "Ex");
2312*260e9a87SYuri Pankov 		return;
231395c635efSGarrett D'Amore 	}
231495c635efSGarrett D'Amore 
2315*260e9a87SYuri Pankov 	mdoc->next = MDOC_NEXT_CHILD;
2316*260e9a87SYuri Pankov 	mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
2317*260e9a87SYuri Pankov 	mdoc->last = n;
231895c635efSGarrett D'Amore }
231995c635efSGarrett D'Amore 
2320*260e9a87SYuri Pankov static enum mdoc_sec
232195c635efSGarrett D'Amore a2sec(const char *p)
232295c635efSGarrett D'Amore {
232395c635efSGarrett D'Amore 	int		 i;
232495c635efSGarrett D'Amore 
2325*260e9a87SYuri Pankov 	for (i = 0; i < (int)SEC__MAX; i++)
232695c635efSGarrett D'Amore 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
232795c635efSGarrett D'Amore 			return((enum mdoc_sec)i);
232895c635efSGarrett D'Amore 
232995c635efSGarrett D'Amore 	return(SEC_CUSTOM);
233095c635efSGarrett D'Amore }
233195c635efSGarrett D'Amore 
233295c635efSGarrett D'Amore static size_t
233395c635efSGarrett D'Amore macro2len(enum mdoct macro)
233495c635efSGarrett D'Amore {
233595c635efSGarrett D'Amore 
233695c635efSGarrett D'Amore 	switch (macro) {
2337*260e9a87SYuri Pankov 	case MDOC_Ad:
233895c635efSGarrett D'Amore 		return(12);
2339*260e9a87SYuri Pankov 	case MDOC_Ao:
234095c635efSGarrett D'Amore 		return(12);
2341*260e9a87SYuri Pankov 	case MDOC_An:
234295c635efSGarrett D'Amore 		return(12);
2343*260e9a87SYuri Pankov 	case MDOC_Aq:
234495c635efSGarrett D'Amore 		return(12);
2345*260e9a87SYuri Pankov 	case MDOC_Ar:
234695c635efSGarrett D'Amore 		return(12);
2347*260e9a87SYuri Pankov 	case MDOC_Bo:
234895c635efSGarrett D'Amore 		return(12);
2349*260e9a87SYuri Pankov 	case MDOC_Bq:
235095c635efSGarrett D'Amore 		return(12);
2351*260e9a87SYuri Pankov 	case MDOC_Cd:
235295c635efSGarrett D'Amore 		return(12);
2353*260e9a87SYuri Pankov 	case MDOC_Cm:
235495c635efSGarrett D'Amore 		return(10);
2355*260e9a87SYuri Pankov 	case MDOC_Do:
235695c635efSGarrett D'Amore 		return(10);
2357*260e9a87SYuri Pankov 	case MDOC_Dq:
235895c635efSGarrett D'Amore 		return(12);
2359*260e9a87SYuri Pankov 	case MDOC_Dv:
236095c635efSGarrett D'Amore 		return(12);
2361*260e9a87SYuri Pankov 	case MDOC_Eo:
236295c635efSGarrett D'Amore 		return(12);
2363*260e9a87SYuri Pankov 	case MDOC_Em:
236495c635efSGarrett D'Amore 		return(10);
2365*260e9a87SYuri Pankov 	case MDOC_Er:
236695c635efSGarrett D'Amore 		return(17);
2367*260e9a87SYuri Pankov 	case MDOC_Ev:
236895c635efSGarrett D'Amore 		return(15);
2369*260e9a87SYuri Pankov 	case MDOC_Fa:
237095c635efSGarrett D'Amore 		return(12);
2371*260e9a87SYuri Pankov 	case MDOC_Fl:
237295c635efSGarrett D'Amore 		return(10);
2373*260e9a87SYuri Pankov 	case MDOC_Fo:
237495c635efSGarrett D'Amore 		return(16);
2375*260e9a87SYuri Pankov 	case MDOC_Fn:
237695c635efSGarrett D'Amore 		return(16);
2377*260e9a87SYuri Pankov 	case MDOC_Ic:
237895c635efSGarrett D'Amore 		return(10);
2379*260e9a87SYuri Pankov 	case MDOC_Li:
238095c635efSGarrett D'Amore 		return(16);
2381*260e9a87SYuri Pankov 	case MDOC_Ms:
238295c635efSGarrett D'Amore 		return(6);
2383*260e9a87SYuri Pankov 	case MDOC_Nm:
238495c635efSGarrett D'Amore 		return(10);
2385*260e9a87SYuri Pankov 	case MDOC_No:
238695c635efSGarrett D'Amore 		return(12);
2387*260e9a87SYuri Pankov 	case MDOC_Oo:
238895c635efSGarrett D'Amore 		return(10);
2389*260e9a87SYuri Pankov 	case MDOC_Op:
239095c635efSGarrett D'Amore 		return(14);
2391*260e9a87SYuri Pankov 	case MDOC_Pa:
239295c635efSGarrett D'Amore 		return(32);
2393*260e9a87SYuri Pankov 	case MDOC_Pf:
239495c635efSGarrett D'Amore 		return(12);
2395*260e9a87SYuri Pankov 	case MDOC_Po:
239695c635efSGarrett D'Amore 		return(12);
2397*260e9a87SYuri Pankov 	case MDOC_Pq:
239895c635efSGarrett D'Amore 		return(12);
2399*260e9a87SYuri Pankov 	case MDOC_Ql:
240095c635efSGarrett D'Amore 		return(16);
2401*260e9a87SYuri Pankov 	case MDOC_Qo:
240295c635efSGarrett D'Amore 		return(12);
2403*260e9a87SYuri Pankov 	case MDOC_So:
240495c635efSGarrett D'Amore 		return(12);
2405*260e9a87SYuri Pankov 	case MDOC_Sq:
240695c635efSGarrett D'Amore 		return(12);
2407*260e9a87SYuri Pankov 	case MDOC_Sy:
240895c635efSGarrett D'Amore 		return(6);
2409*260e9a87SYuri Pankov 	case MDOC_Sx:
241095c635efSGarrett D'Amore 		return(16);
2411*260e9a87SYuri Pankov 	case MDOC_Tn:
241295c635efSGarrett D'Amore 		return(10);
2413*260e9a87SYuri Pankov 	case MDOC_Va:
241495c635efSGarrett D'Amore 		return(12);
2415*260e9a87SYuri Pankov 	case MDOC_Vt:
241695c635efSGarrett D'Amore 		return(12);
2417*260e9a87SYuri Pankov 	case MDOC_Xr:
241895c635efSGarrett D'Amore 		return(10);
241995c635efSGarrett D'Amore 	default:
242095c635efSGarrett D'Amore 		break;
242195c635efSGarrett D'Amore 	};
242295c635efSGarrett D'Amore 	return(0);
242395c635efSGarrett D'Amore }
2424