1cec8643bSMichal Nowak /*	$Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4cec8643bSMichal Nowak  * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
5260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
63c308cd1SRob Johnston  * Copyright 2019 Joyent, Inc.
795c635efSGarrett D'Amore  *
895c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
995c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
1095c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1195c635efSGarrett D'Amore  *
12371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1395c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1595c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1695c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1795c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1895c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1995c635efSGarrett D'Amore  */
2095c635efSGarrett D'Amore #include "config.h"
2195c635efSGarrett D'Amore 
22260e9a87SYuri Pankov #include <sys/types.h>
23698f87a4SGarrett D'Amore #ifndef OSNAME
2495c635efSGarrett D'Amore #include <sys/utsname.h>
2595c635efSGarrett D'Amore #endif
2695c635efSGarrett D'Amore 
2795c635efSGarrett D'Amore #include <assert.h>
2895c635efSGarrett D'Amore #include <ctype.h>
2995c635efSGarrett D'Amore #include <limits.h>
3095c635efSGarrett D'Amore #include <stdio.h>
3195c635efSGarrett D'Amore #include <stdlib.h>
3295c635efSGarrett D'Amore #include <string.h>
3395c635efSGarrett D'Amore #include <time.h>
3495c635efSGarrett D'Amore 
35260e9a87SYuri Pankov #include "mandoc_aux.h"
36371584c2SYuri Pankov #include "mandoc.h"
37c66b8046SYuri Pankov #include "mandoc_xr.h"
38371584c2SYuri Pankov #include "roff.h"
39371584c2SYuri Pankov #include "mdoc.h"
4095c635efSGarrett D'Amore #include "libmandoc.h"
41371584c2SYuri Pankov #include "roff_int.h"
42371584c2SYuri Pankov #include "libmdoc.h"
4395c635efSGarrett D'Amore 
4495c635efSGarrett D'Amore /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4595c635efSGarrett D'Amore 
46371584c2SYuri Pankov #define	POST_ARGS struct roff_man *mdoc
4795c635efSGarrett D'Amore 
4895c635efSGarrett D'Amore enum	check_ineq {
4995c635efSGarrett D'Amore 	CHECK_LT,
5095c635efSGarrett D'Amore 	CHECK_GT,
5195c635efSGarrett D'Amore 	CHECK_EQ
5295c635efSGarrett D'Amore };
5395c635efSGarrett D'Amore 
54260e9a87SYuri Pankov typedef	void	(*v_post)(POST_ARGS);
5595c635efSGarrett D'Amore 
56a40ea1a7SYuri Pankov static	int	 build_list(struct roff_man *, int);
57371584c2SYuri Pankov static	void	 check_argv(struct roff_man *,
58371584c2SYuri Pankov 			struct roff_node *, struct mdoc_argv *);
59371584c2SYuri Pankov static	void	 check_args(struct roff_man *, struct roff_node *);
606640c13bSYuri Pankov static	void	 check_text(struct roff_man *, int, int, char *);
616640c13bSYuri Pankov static	void	 check_text_em(struct roff_man *, int, int, char *);
62c66b8046SYuri Pankov static	void	 check_toptext(struct roff_man *, int, int, const char *);
63371584c2SYuri Pankov static	int	 child_an(const struct roff_node *);
64c66b8046SYuri Pankov static	size_t		macro2len(enum roff_tok);
65c66b8046SYuri Pankov static	void	 rewrite_macro2len(struct roff_man *, char **);
66c66b8046SYuri Pankov static	int	 similar(const char *, const char *);
67cec8643bSMichal Nowak static	void	 post_abort(POST_ARGS);
68260e9a87SYuri Pankov 
69260e9a87SYuri Pankov static	void	 post_an(POST_ARGS);
70371584c2SYuri Pankov static	void	 post_an_norm(POST_ARGS);
71260e9a87SYuri Pankov static	void	 post_at(POST_ARGS);
72371584c2SYuri Pankov static	void	 post_bd(POST_ARGS);
73260e9a87SYuri Pankov static	void	 post_bf(POST_ARGS);
74260e9a87SYuri Pankov static	void	 post_bk(POST_ARGS);
75260e9a87SYuri Pankov static	void	 post_bl(POST_ARGS);
76260e9a87SYuri Pankov static	void	 post_bl_block(POST_ARGS);
77260e9a87SYuri Pankov static	void	 post_bl_head(POST_ARGS);
78371584c2SYuri Pankov static	void	 post_bl_norm(POST_ARGS);
79260e9a87SYuri Pankov static	void	 post_bx(POST_ARGS);
80260e9a87SYuri Pankov static	void	 post_defaults(POST_ARGS);
81371584c2SYuri Pankov static	void	 post_display(POST_ARGS);
82260e9a87SYuri Pankov static	void	 post_dd(POST_ARGS);
83c66b8046SYuri Pankov static	void	 post_delim(POST_ARGS);
84c66b8046SYuri Pankov static	void	 post_delim_nb(POST_ARGS);
85260e9a87SYuri Pankov static	void	 post_dt(POST_ARGS);
86260e9a87SYuri Pankov static	void	 post_en(POST_ARGS);
87260e9a87SYuri Pankov static	void	 post_es(POST_ARGS);
88260e9a87SYuri Pankov static	void	 post_eoln(POST_ARGS);
89260e9a87SYuri Pankov static	void	 post_ex(POST_ARGS);
90260e9a87SYuri Pankov static	void	 post_fa(POST_ARGS);
91260e9a87SYuri Pankov static	void	 post_fn(POST_ARGS);
92260e9a87SYuri Pankov static	void	 post_fname(POST_ARGS);
93260e9a87SYuri Pankov static	void	 post_fo(POST_ARGS);
94260e9a87SYuri Pankov static	void	 post_hyph(POST_ARGS);
95260e9a87SYuri Pankov static	void	 post_ignpar(POST_ARGS);
96260e9a87SYuri Pankov static	void	 post_it(POST_ARGS);
97260e9a87SYuri Pankov static	void	 post_lb(POST_ARGS);
98260e9a87SYuri Pankov static	void	 post_nd(POST_ARGS);
99260e9a87SYuri Pankov static	void	 post_nm(POST_ARGS);
100260e9a87SYuri Pankov static	void	 post_ns(POST_ARGS);
101371584c2SYuri Pankov static	void	 post_obsolete(POST_ARGS);
102260e9a87SYuri Pankov static	void	 post_os(POST_ARGS);
103260e9a87SYuri Pankov static	void	 post_par(POST_ARGS);
104371584c2SYuri Pankov static	void	 post_prevpar(POST_ARGS);
105260e9a87SYuri Pankov static	void	 post_root(POST_ARGS);
106260e9a87SYuri Pankov static	void	 post_rs(POST_ARGS);
107a40ea1a7SYuri Pankov static	void	 post_rv(POST_ARGS);
108260e9a87SYuri Pankov static	void	 post_sh(POST_ARGS);
109260e9a87SYuri Pankov static	void	 post_sh_head(POST_ARGS);
110260e9a87SYuri Pankov static	void	 post_sh_name(POST_ARGS);
111260e9a87SYuri Pankov static	void	 post_sh_see_also(POST_ARGS);
112260e9a87SYuri Pankov static	void	 post_sh_authors(POST_ARGS);
113260e9a87SYuri Pankov static	void	 post_sm(POST_ARGS);
114260e9a87SYuri Pankov static	void	 post_st(POST_ARGS);
115371584c2SYuri Pankov static	void	 post_std(POST_ARGS);
116c66b8046SYuri Pankov static	void	 post_sx(POST_ARGS);
117c66b8046SYuri Pankov static	void	 post_useless(POST_ARGS);
118a40ea1a7SYuri Pankov static	void	 post_xr(POST_ARGS);
119a40ea1a7SYuri Pankov static	void	 post_xx(POST_ARGS);
120371584c2SYuri Pankov 
121cec8643bSMichal Nowak static	const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
122371584c2SYuri Pankov 	post_dd,	/* Dd */
123371584c2SYuri Pankov 	post_dt,	/* Dt */
124371584c2SYuri Pankov 	post_os,	/* Os */
125371584c2SYuri Pankov 	post_sh,	/* Sh */
126371584c2SYuri Pankov 	post_ignpar,	/* Ss */
127371584c2SYuri Pankov 	post_par,	/* Pp */
128371584c2SYuri Pankov 	post_display,	/* D1 */
129371584c2SYuri Pankov 	post_display,	/* Dl */
130371584c2SYuri Pankov 	post_display,	/* Bd */
131371584c2SYuri Pankov 	NULL,		/* Ed */
132371584c2SYuri Pankov 	post_bl,	/* Bl */
133371584c2SYuri Pankov 	NULL,		/* El */
134371584c2SYuri Pankov 	post_it,	/* It */
135c66b8046SYuri Pankov 	post_delim_nb,	/* Ad */
136371584c2SYuri Pankov 	post_an,	/* An */
137c66b8046SYuri Pankov 	NULL,		/* Ap */
138371584c2SYuri Pankov 	post_defaults,	/* Ar */
139371584c2SYuri Pankov 	NULL,		/* Cd */
140c66b8046SYuri Pankov 	post_delim_nb,	/* Cm */
141c66b8046SYuri Pankov 	post_delim_nb,	/* Dv */
142c66b8046SYuri Pankov 	post_delim_nb,	/* Er */
143c66b8046SYuri Pankov 	post_delim_nb,	/* Ev */
144371584c2SYuri Pankov 	post_ex,	/* Ex */
145371584c2SYuri Pankov 	post_fa,	/* Fa */
146371584c2SYuri Pankov 	NULL,		/* Fd */
147c66b8046SYuri Pankov 	post_delim_nb,	/* Fl */
148371584c2SYuri Pankov 	post_fn,	/* Fn */
149c66b8046SYuri Pankov 	post_delim_nb,	/* Ft */
150c66b8046SYuri Pankov 	post_delim_nb,	/* Ic */
151c66b8046SYuri Pankov 	post_delim_nb,	/* In */
152371584c2SYuri Pankov 	post_defaults,	/* Li */
153371584c2SYuri Pankov 	post_nd,	/* Nd */
154371584c2SYuri Pankov 	post_nm,	/* Nm */
155c66b8046SYuri Pankov 	post_delim_nb,	/* Op */
156cec8643bSMichal Nowak 	post_abort,	/* Ot */
157371584c2SYuri Pankov 	post_defaults,	/* Pa */
158a40ea1a7SYuri Pankov 	post_rv,	/* Rv */
159371584c2SYuri Pankov 	post_st,	/* St */
160c66b8046SYuri Pankov 	post_delim_nb,	/* Va */
161c66b8046SYuri Pankov 	post_delim_nb,	/* Vt */
162a40ea1a7SYuri Pankov 	post_xr,	/* Xr */
163371584c2SYuri Pankov 	NULL,		/* %A */
164371584c2SYuri Pankov 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
165371584c2SYuri Pankov 	NULL,		/* %D */
166371584c2SYuri Pankov 	NULL,		/* %I */
167371584c2SYuri Pankov 	NULL,		/* %J */
168371584c2SYuri Pankov 	post_hyph,	/* %N */
169371584c2SYuri Pankov 	post_hyph,	/* %O */
170371584c2SYuri Pankov 	NULL,		/* %P */
171371584c2SYuri Pankov 	post_hyph,	/* %R */
172371584c2SYuri Pankov 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
173371584c2SYuri Pankov 	NULL,		/* %V */
174371584c2SYuri Pankov 	NULL,		/* Ac */
1756640c13bSYuri Pankov 	NULL,		/* Ao */
176c66b8046SYuri Pankov 	post_delim_nb,	/* Aq */
177371584c2SYuri Pankov 	post_at,	/* At */
178371584c2SYuri Pankov 	NULL,		/* Bc */
179371584c2SYuri Pankov 	post_bf,	/* Bf */
1806640c13bSYuri Pankov 	NULL,		/* Bo */
181371584c2SYuri Pankov 	NULL,		/* Bq */
182a40ea1a7SYuri Pankov 	post_xx,	/* Bsx */
183371584c2SYuri Pankov 	post_bx,	/* Bx */
184371584c2SYuri Pankov 	post_obsolete,	/* Db */
185371584c2SYuri Pankov 	NULL,		/* Dc */
186371584c2SYuri Pankov 	NULL,		/* Do */
187371584c2SYuri Pankov 	NULL,		/* Dq */
188371584c2SYuri Pankov 	NULL,		/* Ec */
189371584c2SYuri Pankov 	NULL,		/* Ef */
190c66b8046SYuri Pankov 	post_delim_nb,	/* Em */
191371584c2SYuri Pankov 	NULL,		/* Eo */
192a40ea1a7SYuri Pankov 	post_xx,	/* Fx */
193c66b8046SYuri Pankov 	post_delim_nb,	/* Ms */
194371584c2SYuri Pankov 	NULL,		/* No */
195371584c2SYuri Pankov 	post_ns,	/* Ns */
196a40ea1a7SYuri Pankov 	post_xx,	/* Nx */
197a40ea1a7SYuri Pankov 	post_xx,	/* Ox */
198371584c2SYuri Pankov 	NULL,		/* Pc */
199371584c2SYuri Pankov 	NULL,		/* Pf */
2006640c13bSYuri Pankov 	NULL,		/* Po */
201c66b8046SYuri Pankov 	post_delim_nb,	/* Pq */
202371584c2SYuri Pankov 	NULL,		/* Qc */
203c66b8046SYuri Pankov 	post_delim_nb,	/* Ql */
2046640c13bSYuri Pankov 	NULL,		/* Qo */
205c66b8046SYuri Pankov 	post_delim_nb,	/* Qq */
206371584c2SYuri Pankov 	NULL,		/* Re */
207371584c2SYuri Pankov 	post_rs,	/* Rs */
208371584c2SYuri Pankov 	NULL,		/* Sc */
2096640c13bSYuri Pankov 	NULL,		/* So */
210c66b8046SYuri Pankov 	post_delim_nb,	/* Sq */
211371584c2SYuri Pankov 	post_sm,	/* Sm */
212c66b8046SYuri Pankov 	post_sx,	/* Sx */
213c66b8046SYuri Pankov 	post_delim_nb,	/* Sy */
214c66b8046SYuri Pankov 	post_useless,	/* Tn */
215a40ea1a7SYuri Pankov 	post_xx,	/* Ux */
216371584c2SYuri Pankov 	NULL,		/* Xc */
217371584c2SYuri Pankov 	NULL,		/* Xo */
218371584c2SYuri Pankov 	post_fo,	/* Fo */
219371584c2SYuri Pankov 	NULL,		/* Fc */
2206640c13bSYuri Pankov 	NULL,		/* Oo */
221371584c2SYuri Pankov 	NULL,		/* Oc */
222371584c2SYuri Pankov 	post_bk,	/* Bk */
223371584c2SYuri Pankov 	NULL,		/* Ek */
224371584c2SYuri Pankov 	post_eoln,	/* Bt */
225c66b8046SYuri Pankov 	post_obsolete,	/* Hf */
226371584c2SYuri Pankov 	post_obsolete,	/* Fr */
227371584c2SYuri Pankov 	post_eoln,	/* Ud */
228371584c2SYuri Pankov 	post_lb,	/* Lb */
229cec8643bSMichal Nowak 	post_abort,	/* Lp */
230c66b8046SYuri Pankov 	post_delim_nb,	/* Lk */
231371584c2SYuri Pankov 	post_defaults,	/* Mt */
232c66b8046SYuri Pankov 	post_delim_nb,	/* Brq */
2336640c13bSYuri Pankov 	NULL,		/* Bro */
234371584c2SYuri Pankov 	NULL,		/* Brc */
235371584c2SYuri Pankov 	NULL,		/* %C */
236371584c2SYuri Pankov 	post_es,	/* Es */
237371584c2SYuri Pankov 	post_en,	/* En */
238a40ea1a7SYuri Pankov 	post_xx,	/* Dx */
239371584c2SYuri Pankov 	NULL,		/* %Q */
240371584c2SYuri Pankov 	NULL,		/* %U */
241371584c2SYuri Pankov 	NULL,		/* Ta */
24295c635efSGarrett D'Amore };
24395c635efSGarrett D'Amore 
24495c635efSGarrett D'Amore #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
24595c635efSGarrett D'Amore 
246c66b8046SYuri Pankov static	const enum roff_tok rsord[RSORD_MAX] = {
24795c635efSGarrett D'Amore 	MDOC__A,
24895c635efSGarrett D'Amore 	MDOC__T,
24995c635efSGarrett D'Amore 	MDOC__B,
25095c635efSGarrett D'Amore 	MDOC__I,
25195c635efSGarrett D'Amore 	MDOC__J,
25295c635efSGarrett D'Amore 	MDOC__R,
25395c635efSGarrett D'Amore 	MDOC__N,
25495c635efSGarrett D'Amore 	MDOC__V,
255698f87a4SGarrett D'Amore 	MDOC__U,
25695c635efSGarrett D'Amore 	MDOC__P,
25795c635efSGarrett D'Amore 	MDOC__Q,
25895c635efSGarrett D'Amore 	MDOC__C,
259698f87a4SGarrett D'Amore 	MDOC__D,
260698f87a4SGarrett D'Amore 	MDOC__O
26195c635efSGarrett D'Amore };
26295c635efSGarrett D'Amore 
26395c635efSGarrett D'Amore static	const char * const secnames[SEC__MAX] = {
26495c635efSGarrett D'Amore 	NULL,
26595c635efSGarrett D'Amore 	"NAME",
26695c635efSGarrett D'Amore 	"LIBRARY",
26795c635efSGarrett D'Amore 	"SYNOPSIS",
26895c635efSGarrett D'Amore 	"DESCRIPTION",
269260e9a87SYuri Pankov 	"CONTEXT",
27095c635efSGarrett D'Amore 	"IMPLEMENTATION NOTES",
27195c635efSGarrett D'Amore 	"RETURN VALUES",
27295c635efSGarrett D'Amore 	"ENVIRONMENT",
27395c635efSGarrett D'Amore 	"FILES",
27495c635efSGarrett D'Amore 	"EXIT STATUS",
27595c635efSGarrett D'Amore 	"EXAMPLES",
27695c635efSGarrett D'Amore 	"DIAGNOSTICS",
27795c635efSGarrett D'Amore 	"COMPATIBILITY",
27895c635efSGarrett D'Amore 	"ERRORS",
27995c635efSGarrett D'Amore 	"SEE ALSO",
28095c635efSGarrett D'Amore 	"STANDARDS",
28195c635efSGarrett D'Amore 	"HISTORY",
28295c635efSGarrett D'Amore 	"AUTHORS",
28395c635efSGarrett D'Amore 	"CAVEATS",
28495c635efSGarrett D'Amore 	"BUGS",
28595c635efSGarrett D'Amore 	"SECURITY CONSIDERATIONS",
28695c635efSGarrett D'Amore 	NULL
28795c635efSGarrett D'Amore };
28895c635efSGarrett D'Amore 
289260e9a87SYuri Pankov 
290cec8643bSMichal Nowak /* Validate the subtree rooted at mdoc->last. */
291260e9a87SYuri Pankov void
mdoc_validate(struct roff_man * mdoc)292cec8643bSMichal Nowak mdoc_validate(struct roff_man *mdoc)
29395c635efSGarrett D'Amore {
2946640c13bSYuri Pankov 	struct roff_node *n, *np;
295c66b8046SYuri Pankov 	const v_post *p;
29695c635efSGarrett D'Amore 
297cec8643bSMichal Nowak 	/*
298cec8643bSMichal Nowak 	 * Translate obsolete macros to modern macros first
299cec8643bSMichal Nowak 	 * such that later code does not need to look
300cec8643bSMichal Nowak 	 * for the obsolete versions.
301cec8643bSMichal Nowak 	 */
302cec8643bSMichal Nowak 
303371584c2SYuri Pankov 	n = mdoc->last;
304cec8643bSMichal Nowak 	switch (n->tok) {
305cec8643bSMichal Nowak 	case MDOC_Lp:
306cec8643bSMichal Nowak 		n->tok = MDOC_Pp;
307cec8643bSMichal Nowak 		break;
308cec8643bSMichal Nowak 	case MDOC_Ot:
309cec8643bSMichal Nowak 		post_obsolete(mdoc);
310cec8643bSMichal Nowak 		n->tok = MDOC_Ft;
311cec8643bSMichal Nowak 		break;
312cec8643bSMichal Nowak 	default:
313cec8643bSMichal Nowak 		break;
314cec8643bSMichal Nowak 	}
315cec8643bSMichal Nowak 
316cec8643bSMichal Nowak 	/*
317cec8643bSMichal Nowak 	 * Iterate over all children, recursing into each one
318cec8643bSMichal Nowak 	 * in turn, depth-first.
319cec8643bSMichal Nowak 	 */
320cec8643bSMichal Nowak 
321371584c2SYuri Pankov 	mdoc->last = mdoc->last->child;
322371584c2SYuri Pankov 	while (mdoc->last != NULL) {
323cec8643bSMichal Nowak 		mdoc_validate(mdoc);
324371584c2SYuri Pankov 		if (mdoc->last == n)
325371584c2SYuri Pankov 			mdoc->last = mdoc->last->child;
326371584c2SYuri Pankov 		else
327371584c2SYuri Pankov 			mdoc->last = mdoc->last->next;
328371584c2SYuri Pankov 	}
329371584c2SYuri Pankov 
330cec8643bSMichal Nowak 	/* Finally validate the macro itself. */
331cec8643bSMichal Nowak 
332371584c2SYuri Pankov 	mdoc->last = n;
333371584c2SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
33495c635efSGarrett D'Amore 	switch (n->type) {
335371584c2SYuri Pankov 	case ROFFT_TEXT:
3366640c13bSYuri Pankov 		np = n->parent;
337a40ea1a7SYuri Pankov 		if (n->sec != SEC_SYNOPSIS ||
3386640c13bSYuri Pankov 		    (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
339260e9a87SYuri Pankov 			check_text(mdoc, n->line, n->pos, n->string);
340cec8643bSMichal Nowak 		if ((n->flags & NODE_NOFILL) == 0 &&
3416640c13bSYuri Pankov 		    (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
3426640c13bSYuri Pankov 		     np->parent->parent->norm->Bl.type != LIST_diag))
3436640c13bSYuri Pankov 			check_text_em(mdoc, n->line, n->pos, n->string);
3446640c13bSYuri Pankov 		if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
3456640c13bSYuri Pankov 		    (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
346c66b8046SYuri Pankov 			check_toptext(mdoc, n->line, n->pos, n->string);
34795c635efSGarrett D'Amore 		break;
3486640c13bSYuri Pankov 	case ROFFT_COMMENT:
349371584c2SYuri Pankov 	case ROFFT_EQN:
350371584c2SYuri Pankov 	case ROFFT_TBL:
35195c635efSGarrett D'Amore 		break;
352371584c2SYuri Pankov 	case ROFFT_ROOT:
353260e9a87SYuri Pankov 		post_root(mdoc);
35495c635efSGarrett D'Amore 		break;
35595c635efSGarrett D'Amore 	default:
356371584c2SYuri Pankov 		check_args(mdoc, mdoc->last);
35795c635efSGarrett D'Amore 
358260e9a87SYuri Pankov 		/*
359260e9a87SYuri Pankov 		 * Closing delimiters are not special at the
360260e9a87SYuri Pankov 		 * beginning of a block, opening delimiters
361260e9a87SYuri Pankov 		 * are not special at the end.
362260e9a87SYuri Pankov 		 */
36395c635efSGarrett D'Amore 
364260e9a87SYuri Pankov 		if (n->child != NULL)
365a40ea1a7SYuri Pankov 			n->child->flags &= ~NODE_DELIMC;
366260e9a87SYuri Pankov 		if (n->last != NULL)
367a40ea1a7SYuri Pankov 			n->last->flags &= ~NODE_DELIMO;
36895c635efSGarrett D'Amore 
369260e9a87SYuri Pankov 		/* Call the macro's postprocessor. */
37095c635efSGarrett D'Amore 
371c66b8046SYuri Pankov 		if (n->tok < ROFF_MAX) {
372cec8643bSMichal Nowak 			roff_validate(mdoc);
373c66b8046SYuri Pankov 			break;
374c66b8046SYuri Pankov 		}
375c66b8046SYuri Pankov 
376c66b8046SYuri Pankov 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
377cec8643bSMichal Nowak 		p = mdoc_valids + (n->tok - MDOC_Dd);
378260e9a87SYuri Pankov 		if (*p)
379260e9a87SYuri Pankov 			(*p)(mdoc);
380371584c2SYuri Pankov 		if (mdoc->last == n)
381371584c2SYuri Pankov 			mdoc_state(mdoc, n);
382260e9a87SYuri Pankov 		break;
383260e9a87SYuri Pankov 	}
38495c635efSGarrett D'Amore }
38595c635efSGarrett D'Amore 
38695c635efSGarrett D'Amore static void
check_args(struct roff_man * mdoc,struct roff_node * n)387371584c2SYuri Pankov check_args(struct roff_man *mdoc, struct roff_node *n)
38895c635efSGarrett D'Amore {
38995c635efSGarrett D'Amore 	int		 i;
39095c635efSGarrett D'Amore 
39195c635efSGarrett D'Amore 	if (NULL == n->args)
39295c635efSGarrett D'Amore 		return;
39395c635efSGarrett D'Amore 
39495c635efSGarrett D'Amore 	assert(n->args->argc);
39595c635efSGarrett D'Amore 	for (i = 0; i < (int)n->args->argc; i++)
396698f87a4SGarrett D'Amore 		check_argv(mdoc, n, &n->args->argv[i]);
39795c635efSGarrett D'Amore }
39895c635efSGarrett D'Amore 
39995c635efSGarrett D'Amore static void
check_argv(struct roff_man * mdoc,struct roff_node * n,struct mdoc_argv * v)400371584c2SYuri Pankov check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
40195c635efSGarrett D'Amore {
40295c635efSGarrett D'Amore 	int		 i;
40395c635efSGarrett D'Amore 
40495c635efSGarrett D'Amore 	for (i = 0; i < (int)v->sz; i++)
405698f87a4SGarrett D'Amore 		check_text(mdoc, v->line, v->pos, v->value[i]);
40695c635efSGarrett D'Amore }
40795c635efSGarrett D'Amore 
40895c635efSGarrett D'Amore static void
check_text(struct roff_man * mdoc,int ln,int pos,char * p)409371584c2SYuri Pankov check_text(struct roff_man *mdoc, int ln, int pos, char *p)
41095c635efSGarrett D'Amore {
41195c635efSGarrett D'Amore 	char		*cp;
41295c635efSGarrett D'Amore 
413cec8643bSMichal Nowak 	if (mdoc->last->flags & NODE_NOFILL)
41495c635efSGarrett D'Amore 		return;
41595c635efSGarrett D'Amore 
41695c635efSGarrett D'Amore 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
417cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
41895c635efSGarrett D'Amore }
41995c635efSGarrett D'Amore 
4206640c13bSYuri Pankov static void
check_text_em(struct roff_man * mdoc,int ln,int pos,char * p)4216640c13bSYuri Pankov check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
4226640c13bSYuri Pankov {
4236640c13bSYuri Pankov 	const struct roff_node	*np, *nn;
4246640c13bSYuri Pankov 	char			*cp;
4256640c13bSYuri Pankov 
4266640c13bSYuri Pankov 	np = mdoc->last->prev;
4276640c13bSYuri Pankov 	nn = mdoc->last->next;
4286640c13bSYuri Pankov 
4296640c13bSYuri Pankov 	/* Look for em-dashes wrongly encoded as "--". */
4306640c13bSYuri Pankov 
4316640c13bSYuri Pankov 	for (cp = p; *cp != '\0'; cp++) {
4326640c13bSYuri Pankov 		if (cp[0] != '-' || cp[1] != '-')
4336640c13bSYuri Pankov 			continue;
4346640c13bSYuri Pankov 		cp++;
4356640c13bSYuri Pankov 
4366640c13bSYuri Pankov 		/* Skip input sequences of more than two '-'. */
4376640c13bSYuri Pankov 
4386640c13bSYuri Pankov 		if (cp[1] == '-') {
4396640c13bSYuri Pankov 			while (cp[1] == '-')
4406640c13bSYuri Pankov 				cp++;
4416640c13bSYuri Pankov 			continue;
4426640c13bSYuri Pankov 		}
4436640c13bSYuri Pankov 
4446640c13bSYuri Pankov 		/* Skip "--" directly attached to something else. */
4456640c13bSYuri Pankov 
4466640c13bSYuri Pankov 		if ((cp - p > 1 && cp[-2] != ' ') ||
4476640c13bSYuri Pankov 		    (cp[1] != '\0' && cp[1] != ' '))
4486640c13bSYuri Pankov 			continue;
4496640c13bSYuri Pankov 
4506640c13bSYuri Pankov 		/* Require a letter right before or right afterwards. */
4516640c13bSYuri Pankov 
4526640c13bSYuri Pankov 		if ((cp - p > 2 ?
4536640c13bSYuri Pankov 		     isalpha((unsigned char)cp[-3]) :
4546640c13bSYuri Pankov 		     np != NULL &&
4556640c13bSYuri Pankov 		     np->type == ROFFT_TEXT &&
4566640c13bSYuri Pankov 		     *np->string != '\0' &&
4576640c13bSYuri Pankov 		     isalpha((unsigned char)np->string[
4586640c13bSYuri Pankov 		       strlen(np->string) - 1])) ||
4596640c13bSYuri Pankov 		    (cp[1] != '\0' && cp[2] != '\0' ?
4606640c13bSYuri Pankov 		     isalpha((unsigned char)cp[2]) :
4616640c13bSYuri Pankov 		     nn != NULL &&
4626640c13bSYuri Pankov 		     nn->type == ROFFT_TEXT &&
4636640c13bSYuri Pankov 		     isalpha((unsigned char)*nn->string))) {
464cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_DASHDASH,
4656640c13bSYuri Pankov 			    ln, pos + (int)(cp - p) - 1, NULL);
4666640c13bSYuri Pankov 			break;
4676640c13bSYuri Pankov 		}
4686640c13bSYuri Pankov 	}
4696640c13bSYuri Pankov }
4706640c13bSYuri Pankov 
471c66b8046SYuri Pankov static void
check_toptext(struct roff_man * mdoc,int ln,int pos,const char * p)472c66b8046SYuri Pankov check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
473c66b8046SYuri Pankov {
474c66b8046SYuri Pankov 	const char	*cp, *cpr;
475c66b8046SYuri Pankov 
476c66b8046SYuri Pankov 	if (*p == '\0')
477c66b8046SYuri Pankov 		return;
478c66b8046SYuri Pankov 
479c66b8046SYuri Pankov 	if ((cp = strstr(p, "OpenBSD")) != NULL)
480cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
481c66b8046SYuri Pankov 	if ((cp = strstr(p, "NetBSD")) != NULL)
482cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
483c66b8046SYuri Pankov 	if ((cp = strstr(p, "FreeBSD")) != NULL)
484cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
485c66b8046SYuri Pankov 	if ((cp = strstr(p, "DragonFly")) != NULL)
486cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
487c66b8046SYuri Pankov 
488c66b8046SYuri Pankov 	cp = p;
489c66b8046SYuri Pankov 	while ((cp = strstr(cp + 1, "()")) != NULL) {
490c66b8046SYuri Pankov 		for (cpr = cp - 1; cpr >= p; cpr--)
491c66b8046SYuri Pankov 			if (*cpr != '_' && !isalnum((unsigned char)*cpr))
492c66b8046SYuri Pankov 				break;
493c66b8046SYuri Pankov 		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
494c66b8046SYuri Pankov 			cpr++;
495cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
496c66b8046SYuri Pankov 			    "%.*s()", (int)(cp - cpr), cpr);
497c66b8046SYuri Pankov 		}
498c66b8046SYuri Pankov 	}
499c66b8046SYuri Pankov }
500c66b8046SYuri Pankov 
501cec8643bSMichal Nowak static void
post_abort(POST_ARGS)502cec8643bSMichal Nowak post_abort(POST_ARGS)
503cec8643bSMichal Nowak {
504cec8643bSMichal Nowak 	abort();
505cec8643bSMichal Nowak }
506cec8643bSMichal Nowak 
507c66b8046SYuri Pankov static void
post_delim(POST_ARGS)508c66b8046SYuri Pankov post_delim(POST_ARGS)
509c66b8046SYuri Pankov {
510c66b8046SYuri Pankov 	const struct roff_node	*nch;
511c66b8046SYuri Pankov 	const char		*lc;
512c66b8046SYuri Pankov 	enum mdelim		 delim;
513c66b8046SYuri Pankov 	enum roff_tok		 tok;
514c66b8046SYuri Pankov 
515c66b8046SYuri Pankov 	tok = mdoc->last->tok;
516c66b8046SYuri Pankov 	nch = mdoc->last->last;
517c66b8046SYuri Pankov 	if (nch == NULL || nch->type != ROFFT_TEXT)
518c66b8046SYuri Pankov 		return;
519c66b8046SYuri Pankov 	lc = strchr(nch->string, '\0') - 1;
520c66b8046SYuri Pankov 	if (lc < nch->string)
521c66b8046SYuri Pankov 		return;
522c66b8046SYuri Pankov 	delim = mdoc_isdelim(lc);
523c66b8046SYuri Pankov 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
524c66b8046SYuri Pankov 		return;
525c66b8046SYuri Pankov 	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
526c66b8046SYuri Pankov 	    tok == MDOC_Ss || tok == MDOC_Fo))
527c66b8046SYuri Pankov 		return;
528c66b8046SYuri Pankov 
529cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_DELIM, nch->line,
530cec8643bSMichal Nowak 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
531c66b8046SYuri Pankov 	    nch == mdoc->last->child ? "" : " ...", nch->string);
532c66b8046SYuri Pankov }
533c66b8046SYuri Pankov 
534c66b8046SYuri Pankov static void
post_delim_nb(POST_ARGS)535c66b8046SYuri Pankov post_delim_nb(POST_ARGS)
536c66b8046SYuri Pankov {
537c66b8046SYuri Pankov 	const struct roff_node	*nch;
538c66b8046SYuri Pankov 	const char		*lc, *cp;
539c66b8046SYuri Pankov 	int			 nw;
540c66b8046SYuri Pankov 	enum mdelim		 delim;
541c66b8046SYuri Pankov 	enum roff_tok		 tok;
542c66b8046SYuri Pankov 
543c66b8046SYuri Pankov 	/*
544c66b8046SYuri Pankov 	 * Find candidates: at least two bytes,
545c66b8046SYuri Pankov 	 * the last one a closing or middle delimiter.
546c66b8046SYuri Pankov 	 */
547c66b8046SYuri Pankov 
548c66b8046SYuri Pankov 	tok = mdoc->last->tok;
549c66b8046SYuri Pankov 	nch = mdoc->last->last;
550c66b8046SYuri Pankov 	if (nch == NULL || nch->type != ROFFT_TEXT)
551c66b8046SYuri Pankov 		return;
552c66b8046SYuri Pankov 	lc = strchr(nch->string, '\0') - 1;
553c66b8046SYuri Pankov 	if (lc <= nch->string)
554c66b8046SYuri Pankov 		return;
555c66b8046SYuri Pankov 	delim = mdoc_isdelim(lc);
556c66b8046SYuri Pankov 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
557c66b8046SYuri Pankov 		return;
558c66b8046SYuri Pankov 
559c66b8046SYuri Pankov 	/*
560c66b8046SYuri Pankov 	 * Reduce false positives by allowing various cases.
561c66b8046SYuri Pankov 	 */
562c66b8046SYuri Pankov 
563c66b8046SYuri Pankov 	/* Escaped delimiters. */
564c66b8046SYuri Pankov 	if (lc > nch->string + 1 && lc[-2] == '\\' &&
565c66b8046SYuri Pankov 	    (lc[-1] == '&' || lc[-1] == 'e'))
566c66b8046SYuri Pankov 		return;
567c66b8046SYuri Pankov 
568c66b8046SYuri Pankov 	/* Specific byte sequences. */
569c66b8046SYuri Pankov 	switch (*lc) {
570c66b8046SYuri Pankov 	case ')':
571c66b8046SYuri Pankov 		for (cp = lc; cp >= nch->string; cp--)
572c66b8046SYuri Pankov 			if (*cp == '(')
573c66b8046SYuri Pankov 				return;
574c66b8046SYuri Pankov 		break;
575c66b8046SYuri Pankov 	case '.':
576c66b8046SYuri Pankov 		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
577c66b8046SYuri Pankov 			return;
578c66b8046SYuri Pankov 		if (lc[-1] == '.')
579c66b8046SYuri Pankov 			return;
580c66b8046SYuri Pankov 		break;
581c66b8046SYuri Pankov 	case ';':
582c66b8046SYuri Pankov 		if (tok == MDOC_Vt)
583c66b8046SYuri Pankov 			return;
584c66b8046SYuri Pankov 		break;
585c66b8046SYuri Pankov 	case '?':
586c66b8046SYuri Pankov 		if (lc[-1] == '?')
587c66b8046SYuri Pankov 			return;
588c66b8046SYuri Pankov 		break;
589c66b8046SYuri Pankov 	case ']':
590c66b8046SYuri Pankov 		for (cp = lc; cp >= nch->string; cp--)
591c66b8046SYuri Pankov 			if (*cp == '[')
592c66b8046SYuri Pankov 				return;
593c66b8046SYuri Pankov 		break;
594c66b8046SYuri Pankov 	case '|':
595c66b8046SYuri Pankov 		if (lc == nch->string + 1 && lc[-1] == '|')
596c66b8046SYuri Pankov 			return;
597c66b8046SYuri Pankov 	default:
598c66b8046SYuri Pankov 		break;
599c66b8046SYuri Pankov 	}
600c66b8046SYuri Pankov 
601c66b8046SYuri Pankov 	/* Exactly two non-alphanumeric bytes. */
602c66b8046SYuri Pankov 	if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
603c66b8046SYuri Pankov 		return;
604c66b8046SYuri Pankov 
605c66b8046SYuri Pankov 	/* At least three alphabetic words with a sentence ending. */
606c66b8046SYuri Pankov 	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
6076640c13bSYuri Pankov 	    tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
608c66b8046SYuri Pankov 		nw = 0;
609c66b8046SYuri Pankov 		for (cp = lc - 1; cp >= nch->string; cp--) {
610c66b8046SYuri Pankov 			if (*cp == ' ') {
611c66b8046SYuri Pankov 				nw++;
612c66b8046SYuri Pankov 				if (cp > nch->string && cp[-1] == ',')
613c66b8046SYuri Pankov 					cp--;
614c66b8046SYuri Pankov 			} else if (isalpha((unsigned int)*cp)) {
615c66b8046SYuri Pankov 				if (nw > 1)
616c66b8046SYuri Pankov 					return;
617c66b8046SYuri Pankov 			} else
618c66b8046SYuri Pankov 				break;
619c66b8046SYuri Pankov 		}
620c66b8046SYuri Pankov 	}
621c66b8046SYuri Pankov 
622cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
623cec8643bSMichal Nowak 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
624c66b8046SYuri Pankov 	    nch == mdoc->last->child ? "" : " ...", nch->string);
625c66b8046SYuri Pankov }
626c66b8046SYuri Pankov 
627260e9a87SYuri Pankov static void
post_bl_norm(POST_ARGS)628371584c2SYuri Pankov post_bl_norm(POST_ARGS)
62995c635efSGarrett D'Amore {
630371584c2SYuri Pankov 	struct roff_node *n;
631260e9a87SYuri Pankov 	struct mdoc_argv *argv, *wa;
632260e9a87SYuri Pankov 	int		  i;
633260e9a87SYuri Pankov 	enum mdocargt	  mdoclt;
63495c635efSGarrett D'Amore 	enum mdoc_list	  lt;
63595c635efSGarrett D'Amore 
636371584c2SYuri Pankov 	n = mdoc->last->parent;
637371584c2SYuri Pankov 	n->norm->Bl.type = LIST__NONE;
63895c635efSGarrett D'Amore 
639260e9a87SYuri Pankov 	/*
64095c635efSGarrett D'Amore 	 * First figure out which kind of list to use: bind ourselves to
64195c635efSGarrett D'Amore 	 * the first mentioned list type and warn about any remaining
64295c635efSGarrett D'Amore 	 * ones.  If we find no list type, we default to LIST_item.
64395c635efSGarrett D'Amore 	 */
64495c635efSGarrett D'Amore 
645260e9a87SYuri Pankov 	wa = (n->args == NULL) ? NULL : n->args->argv;
646260e9a87SYuri Pankov 	mdoclt = MDOC_ARG_MAX;
64795c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
648260e9a87SYuri Pankov 		argv = n->args->argv + i;
64995c635efSGarrett D'Amore 		lt = LIST__NONE;
650260e9a87SYuri Pankov 		switch (argv->arg) {
65195c635efSGarrett D'Amore 		/* Set list types. */
652260e9a87SYuri Pankov 		case MDOC_Bullet:
65395c635efSGarrett D'Amore 			lt = LIST_bullet;
65495c635efSGarrett D'Amore 			break;
655260e9a87SYuri Pankov 		case MDOC_Dash:
65695c635efSGarrett D'Amore 			lt = LIST_dash;
65795c635efSGarrett D'Amore 			break;
658260e9a87SYuri Pankov 		case MDOC_Enum:
65995c635efSGarrett D'Amore 			lt = LIST_enum;
66095c635efSGarrett D'Amore 			break;
661260e9a87SYuri Pankov 		case MDOC_Hyphen:
66295c635efSGarrett D'Amore 			lt = LIST_hyphen;
66395c635efSGarrett D'Amore 			break;
664260e9a87SYuri Pankov 		case MDOC_Item:
66595c635efSGarrett D'Amore 			lt = LIST_item;
66695c635efSGarrett D'Amore 			break;
667260e9a87SYuri Pankov 		case MDOC_Tag:
66895c635efSGarrett D'Amore 			lt = LIST_tag;
66995c635efSGarrett D'Amore 			break;
670260e9a87SYuri Pankov 		case MDOC_Diag:
67195c635efSGarrett D'Amore 			lt = LIST_diag;
67295c635efSGarrett D'Amore 			break;
673260e9a87SYuri Pankov 		case MDOC_Hang:
67495c635efSGarrett D'Amore 			lt = LIST_hang;
67595c635efSGarrett D'Amore 			break;
676260e9a87SYuri Pankov 		case MDOC_Ohang:
67795c635efSGarrett D'Amore 			lt = LIST_ohang;
67895c635efSGarrett D'Amore 			break;
679260e9a87SYuri Pankov 		case MDOC_Inset:
68095c635efSGarrett D'Amore 			lt = LIST_inset;
68195c635efSGarrett D'Amore 			break;
682260e9a87SYuri Pankov 		case MDOC_Column:
68395c635efSGarrett D'Amore 			lt = LIST_column;
68495c635efSGarrett D'Amore 			break;
68595c635efSGarrett D'Amore 		/* Set list arguments. */
686260e9a87SYuri Pankov 		case MDOC_Compact:
687260e9a87SYuri Pankov 			if (n->norm->Bl.comp)
688260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
689cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bl -compact");
690260e9a87SYuri Pankov 			n->norm->Bl.comp = 1;
69195c635efSGarrett D'Amore 			break;
692260e9a87SYuri Pankov 		case MDOC_Width:
693260e9a87SYuri Pankov 			wa = argv;
694260e9a87SYuri Pankov 			if (0 == argv->sz) {
695260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
696cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bl -width");
697260e9a87SYuri Pankov 				n->norm->Bl.width = "0n";
69895c635efSGarrett D'Amore 				break;
69995c635efSGarrett D'Amore 			}
700260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.width)
701cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_REP,
702cec8643bSMichal Nowak 				    argv->line, argv->pos,
703cec8643bSMichal Nowak 				    "Bl -width %s", argv->value[0]);
704c66b8046SYuri Pankov 			rewrite_macro2len(mdoc, argv->value);
705260e9a87SYuri Pankov 			n->norm->Bl.width = argv->value[0];
70695c635efSGarrett D'Amore 			break;
707260e9a87SYuri Pankov 		case MDOC_Offset:
708260e9a87SYuri Pankov 			if (0 == argv->sz) {
709260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
710cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bl -offset");
71195c635efSGarrett D'Amore 				break;
71295c635efSGarrett D'Amore 			}
713260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.offs)
714cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_REP,
715cec8643bSMichal Nowak 				    argv->line, argv->pos,
716cec8643bSMichal Nowak 				    "Bl -offset %s", argv->value[0]);
717c66b8046SYuri Pankov 			rewrite_macro2len(mdoc, argv->value);
718260e9a87SYuri Pankov 			n->norm->Bl.offs = argv->value[0];
71995c635efSGarrett D'Amore 			break;
72095c635efSGarrett D'Amore 		default:
72195c635efSGarrett D'Amore 			continue;
72295c635efSGarrett D'Amore 		}
723260e9a87SYuri Pankov 		if (LIST__NONE == lt)
724260e9a87SYuri Pankov 			continue;
725260e9a87SYuri Pankov 		mdoclt = argv->arg;
72695c635efSGarrett D'Amore 
72795c635efSGarrett D'Amore 		/* Check: multiple list types. */
72895c635efSGarrett D'Amore 
729260e9a87SYuri Pankov 		if (LIST__NONE != n->norm->Bl.type) {
730cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
731260e9a87SYuri Pankov 			    "Bl -%s", mdoc_argnames[argv->arg]);
732260e9a87SYuri Pankov 			continue;
73395c635efSGarrett D'Amore 		}
73495c635efSGarrett D'Amore 
73595c635efSGarrett D'Amore 		/* The list type should come first. */
73695c635efSGarrett D'Amore 
737260e9a87SYuri Pankov 		if (n->norm->Bl.width ||
738260e9a87SYuri Pankov 		    n->norm->Bl.offs ||
739260e9a87SYuri Pankov 		    n->norm->Bl.comp)
740cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_LATETYPE,
741cec8643bSMichal Nowak 			    n->line, n->pos, "Bl -%s",
742260e9a87SYuri Pankov 			    mdoc_argnames[n->args->argv[0].arg]);
743260e9a87SYuri Pankov 
744260e9a87SYuri Pankov 		n->norm->Bl.type = lt;
745260e9a87SYuri Pankov 		if (LIST_column == lt) {
746260e9a87SYuri Pankov 			n->norm->Bl.ncols = argv->sz;
747260e9a87SYuri Pankov 			n->norm->Bl.cols = (void *)argv->value;
748260e9a87SYuri Pankov 		}
74995c635efSGarrett D'Amore 	}
75095c635efSGarrett D'Amore 
75195c635efSGarrett D'Amore 	/* Allow lists to default to LIST_item. */
75295c635efSGarrett D'Amore 
75395c635efSGarrett D'Amore 	if (LIST__NONE == n->norm->Bl.type) {
754cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
75595c635efSGarrett D'Amore 		n->norm->Bl.type = LIST_item;
756a40ea1a7SYuri Pankov 		mdoclt = MDOC_Item;
75795c635efSGarrett D'Amore 	}
75895c635efSGarrett D'Amore 
759260e9a87SYuri Pankov 	/*
76095c635efSGarrett D'Amore 	 * Validate the width field.  Some list types don't need width
76195c635efSGarrett D'Amore 	 * types and should be warned about them.  Others should have it
762698f87a4SGarrett D'Amore 	 * and must also be warned.  Yet others have a default and need
763698f87a4SGarrett D'Amore 	 * no warning.
76495c635efSGarrett D'Amore 	 */
76595c635efSGarrett D'Amore 
76695c635efSGarrett D'Amore 	switch (n->norm->Bl.type) {
767260e9a87SYuri Pankov 	case LIST_tag:
768c66b8046SYuri Pankov 		if (n->norm->Bl.width == NULL)
769cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_NOWIDTH,
770260e9a87SYuri Pankov 			    n->line, n->pos, "Bl -tag");
77195c635efSGarrett D'Amore 		break;
772260e9a87SYuri Pankov 	case LIST_column:
773260e9a87SYuri Pankov 	case LIST_diag:
774260e9a87SYuri Pankov 	case LIST_ohang:
775260e9a87SYuri Pankov 	case LIST_inset:
776260e9a87SYuri Pankov 	case LIST_item:
777c66b8046SYuri Pankov 		if (n->norm->Bl.width != NULL)
778cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
779cec8643bSMichal Nowak 			    "Bl -%s", mdoc_argnames[mdoclt]);
780c66b8046SYuri Pankov 		n->norm->Bl.width = NULL;
78195c635efSGarrett D'Amore 		break;
782260e9a87SYuri Pankov 	case LIST_bullet:
783260e9a87SYuri Pankov 	case LIST_dash:
784260e9a87SYuri Pankov 	case LIST_hyphen:
785c66b8046SYuri Pankov 		if (n->norm->Bl.width == NULL)
786698f87a4SGarrett D'Amore 			n->norm->Bl.width = "2n";
787698f87a4SGarrett D'Amore 		break;
788260e9a87SYuri Pankov 	case LIST_enum:
789c66b8046SYuri Pankov 		if (n->norm->Bl.width == NULL)
790698f87a4SGarrett D'Amore 			n->norm->Bl.width = "3n";
791698f87a4SGarrett D'Amore 		break;
79295c635efSGarrett D'Amore 	default:
79395c635efSGarrett D'Amore 		break;
79495c635efSGarrett D'Amore 	}
79595c635efSGarrett D'Amore }
79695c635efSGarrett D'Amore 
797260e9a87SYuri Pankov static void
post_bd(POST_ARGS)798371584c2SYuri Pankov post_bd(POST_ARGS)
79995c635efSGarrett D'Amore {
800371584c2SYuri Pankov 	struct roff_node *n;
801260e9a87SYuri Pankov 	struct mdoc_argv *argv;
802260e9a87SYuri Pankov 	int		  i;
803260e9a87SYuri Pankov 	enum mdoc_disp	  dt;
80495c635efSGarrett D'Amore 
805371584c2SYuri Pankov 	n = mdoc->last;
80695c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
807260e9a87SYuri Pankov 		argv = n->args->argv + i;
80895c635efSGarrett D'Amore 		dt = DISP__NONE;
80995c635efSGarrett D'Amore 
810260e9a87SYuri Pankov 		switch (argv->arg) {
811260e9a87SYuri Pankov 		case MDOC_Centred:
812260e9a87SYuri Pankov 			dt = DISP_centered;
81395c635efSGarrett D'Amore 			break;
814260e9a87SYuri Pankov 		case MDOC_Ragged:
815