1*cec8643bSMichal 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>
4*cec8643bSMichal Nowak  * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
5260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6f6b3f249SJason King  * Copyright 2018, 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 *);
67*cec8643bSMichal 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 
121*cec8643bSMichal 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 */
156*cec8643bSMichal 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 */
229*cec8643bSMichal 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 
290*cec8643bSMichal Nowak /* Validate the subtree rooted at mdoc->last. */
291260e9a87SYuri Pankov void
292*cec8643bSMichal 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 
297*cec8643bSMichal Nowak 	/*
298*cec8643bSMichal Nowak 	 * Translate obsolete macros to modern macros first
299*cec8643bSMichal Nowak 	 * such that later code does not need to look
300*cec8643bSMichal Nowak 	 * for the obsolete versions.
301*cec8643bSMichal Nowak 	 */
302*cec8643bSMichal Nowak 
303371584c2SYuri Pankov 	n = mdoc->last;
304*cec8643bSMichal Nowak 	switch (n->tok) {
305*cec8643bSMichal Nowak 	case MDOC_Lp:
306*cec8643bSMichal Nowak 		n->tok = MDOC_Pp;
307*cec8643bSMichal Nowak 		break;
308*cec8643bSMichal Nowak 	case MDOC_Ot:
309*cec8643bSMichal Nowak 		post_obsolete(mdoc);
310*cec8643bSMichal Nowak 		n->tok = MDOC_Ft;
311*cec8643bSMichal Nowak 		break;
312*cec8643bSMichal Nowak 	default:
313*cec8643bSMichal Nowak 		break;
314*cec8643bSMichal Nowak 	}
315*cec8643bSMichal Nowak 
316*cec8643bSMichal Nowak 	/*
317*cec8643bSMichal Nowak 	 * Iterate over all children, recursing into each one
318*cec8643bSMichal Nowak 	 * in turn, depth-first.
319*cec8643bSMichal Nowak 	 */
320*cec8643bSMichal Nowak 
321371584c2SYuri Pankov 	mdoc->last = mdoc->last->child;
322371584c2SYuri Pankov 	while (mdoc->last != NULL) {
323*cec8643bSMichal 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 
330*cec8643bSMichal Nowak 	/* Finally validate the macro itself. */
331*cec8643bSMichal 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);
340*cec8643bSMichal 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) {
372*cec8643bSMichal Nowak 			roff_validate(mdoc);
373c66b8046SYuri Pankov 			break;
374c66b8046SYuri Pankov 		}
375c66b8046SYuri Pankov 
376c66b8046SYuri Pankov 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
377*cec8643bSMichal 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
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
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
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 
413*cec8643bSMichal 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++)
417*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
41895c635efSGarrett D'Amore }
41995c635efSGarrett D'Amore 
4206640c13bSYuri Pankov static void
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))) {
464*cec8643bSMichal 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
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)
480*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
481c66b8046SYuri Pankov 	if ((cp = strstr(p, "NetBSD")) != NULL)
482*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
483c66b8046SYuri Pankov 	if ((cp = strstr(p, "FreeBSD")) != NULL)
484*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
485c66b8046SYuri Pankov 	if ((cp = strstr(p, "DragonFly")) != NULL)
486*cec8643bSMichal 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++;
495*cec8643bSMichal 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 
501*cec8643bSMichal Nowak static void
502*cec8643bSMichal Nowak post_abort(POST_ARGS)
503*cec8643bSMichal Nowak {
504*cec8643bSMichal Nowak 	abort();
505*cec8643bSMichal Nowak }
506*cec8643bSMichal Nowak 
507c66b8046SYuri Pankov static void
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 
529*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_DELIM, nch->line,
530*cec8643bSMichal 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
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 
622*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
623*cec8643bSMichal 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
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,
689*cec8643bSMichal 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,
696*cec8643bSMichal 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)
701*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_REP,
702*cec8643bSMichal Nowak 				    argv->line, argv->pos,
703*cec8643bSMichal 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,
710*cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bl -offset");
71195c635efSGarrett D'Amore 				break;
71295c635efSGarrett D'Amore 			}
713260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.offs)
714*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_REP,
715*cec8643bSMichal Nowak 				    argv->line, argv->pos,
716*cec8643bSMichal 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) {
730*cec8643bSMichal 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)
740*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_LATETYPE,
741*cec8643bSMichal 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) {
754*cec8643bSMichal 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)
769*cec8643bSMichal 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)
778*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
779*cec8643bSMichal 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
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:
81595c635efSGarrett D'Amore 			dt = DISP_ragged;
81695c635efSGarrett D'Amore 			break;
817260e9a87SYuri Pankov 		case MDOC_Unfilled:
81895c635efSGarrett D'Amore 			dt = DISP_unfilled;
81995c635efSGarrett D'Amore 			break;
820260e9a87SYuri Pankov 		case MDOC_Filled:
82195c635efSGarrett D'Amore 			dt = DISP_filled;
82295c635efSGarrett D'Amore 			break;
823260e9a87SYuri Pankov 		case MDOC_Literal:
82495c635efSGarrett D'Amore 			dt = DISP_literal;
82595c635efSGarrett D'Amore 			break;
826260e9a87SYuri Pankov 		case MDOC_File:
827*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL);
828260e9a87SYuri Pankov 			break;
829260e9a87SYuri Pankov 		case MDOC_Offset:
830260e9a87SYuri Pankov 			if (0 == argv->sz) {
831260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
832*cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bd -offset");
83395c635efSGarrett D'Amore 				break;
83495c635efSGarrett D'Amore 			}
835260e9a87SYuri Pankov 			if (NULL != n->norm->Bd.offs)
836*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ARG_REP,
837*cec8643bSMichal Nowak 				    argv->line, argv->pos,
838*cec8643bSMichal Nowak 				    "Bd -offset %s", argv->value[0]);
839c66b8046SYuri Pankov 			rewrite_macro2len(mdoc, argv->value);
840260e9a87SYuri Pankov 			n->norm->Bd.offs = argv->value[0];
84195c635efSGarrett D'Amore 			break;
842260e9a87SYuri Pankov 		case MDOC_Compact:
843260e9a87SYuri Pankov 			if (n->norm->Bd.comp)
844260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
845*cec8643bSMichal Nowak 				    argv->line, argv->pos, "Bd -compact");
846260e9a87SYuri Pankov 			n->norm->Bd.comp = 1;
84795c635efSGarrett D'Amore 			break;
84895c635efSGarrett D'Amore 		default:
84995c635efSGarrett D'Amore 			abort();
85095c635efSGarrett D'Amore 		}
851260e9a87SYuri Pankov 		if (DISP__NONE == dt)
852260e9a87SYuri Pankov 			continue;
85395c635efSGarrett D'Amore 
854260e9a87SYuri Pankov 		if (DISP__NONE == n->norm->Bd.type)
85595c635efSGarrett D'Amore 			n->norm->Bd.type = dt;
856260e9a87SYuri Pankov 		else
857*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
858260e9a87SYuri Pankov 			    "Bd -%s", mdoc_argnames[argv->arg]);
85995c635efSGarrett D'Amore 	}
86095c635efSGarrett D'Amore 
86195c635efSGarrett D'Amore 	if (DISP__NONE == n->norm->Bd.type) {
862*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
86395c635efSGarrett D'Amore 		n->norm->Bd.type = DISP_ragged;
86495c635efSGarrett D'Amore 	}
86595c635efSGarrett D'Amore }
86695c635efSGarrett D'Amore 
867a40ea1a7SYuri Pankov /*
868a40ea1a7SYuri Pankov  * Stand-alone line macros.
869a40ea1a7SYuri Pankov  */
870a40ea1a7SYuri Pankov 
871260e9a87SYuri Pankov static void
872371584c2SYuri Pankov post_an_norm(POST_ARGS)
87395c635efSGarrett D'Amore {
874371584c2SYuri Pankov 	struct roff_node *n;
875260e9a87SYuri Pankov 	struct mdoc_argv *argv;
876260e9a87SYuri Pankov 	size_t	 i;
87795c635efSGarrett D'Amore 
878371584c2SYuri Pankov 	n = mdoc->last;
879260e9a87SYuri Pankov 	if (n->args == NULL)
880260e9a87SYuri Pankov 		return;
88195c635efSGarrett D'Amore 
882260e9a87SYuri Pankov 	for (i = 1; i < n->args->argc; i++) {
883260e9a87SYuri Pankov 		argv = n->args->argv + i;
884*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
885260e9a87SYuri Pankov 		    "An -%s", mdoc_argnames[argv->arg]);
886260e9a87SYuri Pankov 	}
887260e9a87SYuri Pankov 
888260e9a87SYuri Pankov 	argv = n->args->argv;
889260e9a87SYuri Pankov 	if (argv->arg == MDOC_Split)
89095c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_split;
891260e9a87SYuri Pankov 	else if (argv->arg == MDOC_Nosplit)
89295c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_nosplit;
89395c635efSGarrett D'Amore 	else
89495c635efSGarrett D'Amore 		abort();
89595c635efSGarrett D'Amore }
89695c635efSGarrett D'Amore 
897a40ea1a7SYuri Pankov static void
898a40ea1a7SYuri Pankov post_eoln(POST_ARGS)
899a40ea1a7SYuri Pankov {
900a40ea1a7SYuri Pankov 	struct roff_node	*n;
901a40ea1a7SYuri Pankov 
902c66b8046SYuri Pankov 	post_useless(mdoc);
903a40ea1a7SYuri Pankov 	n = mdoc->last;
904a40ea1a7SYuri Pankov 	if (n->child != NULL)
905*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
906c66b8046SYuri Pankov 		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
907a40ea1a7SYuri Pankov 
908a40ea1a7SYuri Pankov 	while (n->child != NULL)
909a40ea1a7SYuri Pankov 		roff_node_delete(mdoc, n->child);
910a40ea1a7SYuri Pankov 
911a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
912a40ea1a7SYuri Pankov 	    "is currently in beta test." : "currently under development.");
913a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
914a40ea1a7SYuri Pankov 	mdoc->last = n;
915a40ea1a7SYuri Pankov }
916a40ea1a7SYuri Pankov 
917a40ea1a7SYuri Pankov static int
918a40ea1a7SYuri Pankov build_list(struct roff_man *mdoc, int tok)
919a40ea1a7SYuri Pankov {
920a40ea1a7SYuri Pankov 	struct roff_node	*n;
921a40ea1a7SYuri Pankov 	int			 ic;
922a40ea1a7SYuri Pankov 
923a40ea1a7SYuri Pankov 	n = mdoc->last->next;
924a40ea1a7SYuri Pankov 	for (ic = 1;; ic++) {
925a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, tok);
926a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
927*cec8643bSMichal Nowak 		roff_node_relink(mdoc, n);
928a40ea1a7SYuri Pankov 		n = mdoc->last = mdoc->last->parent;
929a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
930a40ea1a7SYuri Pankov 		if (n->next == NULL)
931a40ea1a7SYuri Pankov 			return ic;
932a40ea1a7SYuri Pankov 		if (ic > 1 || n->next->next != NULL) {
933a40ea1a7SYuri Pankov 			roff_word_alloc(mdoc, n->line, n->pos, ",");
934a40ea1a7SYuri Pankov 			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
935a40ea1a7SYuri Pankov 		}
936a40ea1a7SYuri Pankov 		n = mdoc->last->next;
937a40ea1a7SYuri Pankov 		if (n->next == NULL) {
938a40ea1a7SYuri Pankov 			roff_word_alloc(mdoc, n->line, n->pos, "and");
939a40ea1a7SYuri Pankov 			mdoc->last->flags |= NODE_NOSRC;
940a40ea1a7SYuri Pankov 		}
941a40ea1a7SYuri Pankov 	}
942a40ea1a7SYuri Pankov }
943a40ea1a7SYuri Pankov 
944a40ea1a7SYuri Pankov static void
945a40ea1a7SYuri Pankov post_ex(POST_ARGS)
946a40ea1a7SYuri Pankov {
947a40ea1a7SYuri Pankov 	struct roff_node	*n;
948a40ea1a7SYuri Pankov 	int			 ic;
949a40ea1a7SYuri Pankov 
950a40ea1a7SYuri Pankov 	post_std(mdoc);
951a40ea1a7SYuri Pankov 
952a40ea1a7SYuri Pankov 	n = mdoc->last;
953a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
954a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "The");
955a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
956a40ea1a7SYuri Pankov 
957a40ea1a7SYuri Pankov 	if (mdoc->last->next != NULL)
958a40ea1a7SYuri Pankov 		ic = build_list(mdoc, MDOC_Nm);
959a40ea1a7SYuri Pankov 	else if (mdoc->meta.name != NULL) {
960a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
961a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
962a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
963a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
964a40ea1a7SYuri Pankov 		mdoc->last = mdoc->last->parent;
965a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
966a40ea1a7SYuri Pankov 		ic = 1;
967a40ea1a7SYuri Pankov 	} else {
968*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
969a40ea1a7SYuri Pankov 		ic = 0;
970a40ea1a7SYuri Pankov 	}
971a40ea1a7SYuri Pankov 
972a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
973a40ea1a7SYuri Pankov 	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
974a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
975a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
976a40ea1a7SYuri Pankov 	    "on success, and\\~>0 if an error occurs.");
977a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
978a40ea1a7SYuri Pankov 	mdoc->last = n;
979a40ea1a7SYuri Pankov }
980a40ea1a7SYuri Pankov 
981a40ea1a7SYuri Pankov static void
982a40ea1a7SYuri Pankov post_lb(POST_ARGS)
983a40ea1a7SYuri Pankov {
984a40ea1a7SYuri Pankov 	struct roff_node	*n;
985a40ea1a7SYuri Pankov 	const char		*p;
986a40ea1a7SYuri Pankov 
987c66b8046SYuri Pankov 	post_delim_nb(mdoc);
988c66b8046SYuri Pankov 
989a40ea1a7SYuri Pankov 	n = mdoc->last;
990a40ea1a7SYuri Pankov 	assert(n->child->type == ROFFT_TEXT);
991a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
992a40ea1a7SYuri Pankov 
993a40ea1a7SYuri Pankov 	if ((p = mdoc_a2lib(n->child->string)) != NULL) {
994a40ea1a7SYuri Pankov 		n->child->flags |= NODE_NOPRT;
995a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, p);
996a40ea1a7SYuri Pankov 		mdoc->last->flags = NODE_NOSRC;
997a40ea1a7SYuri Pankov 		mdoc->last = n;
998a40ea1a7SYuri Pankov 		return;
999a40ea1a7SYuri Pankov 	}
1000a40ea1a7SYuri Pankov 
1001*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_LB_BAD, n->child->line,
1002c66b8046SYuri Pankov 	    n->child->pos, "Lb %s", n->child->string);
1003c66b8046SYuri Pankov 
1004a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "library");
1005a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_NOSRC;
10066640c13bSYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
1007a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
1008a40ea1a7SYuri Pankov 	mdoc->last = mdoc->last->next;
10096640c13bSYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
1010a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
1011a40ea1a7SYuri Pankov 	mdoc->last = n;
1012a40ea1a7SYuri Pankov }
1013a40ea1a7SYuri Pankov 
1014a40ea1a7SYuri Pankov static void
1015a40ea1a7SYuri Pankov post_rv(POST_ARGS)
1016a40ea1a7SYuri Pankov {
1017a40ea1a7SYuri Pankov 	struct roff_node	*n;
1018a40ea1a7SYuri Pankov 	int			 ic;
1019a40ea1a7SYuri Pankov 
1020a40ea1a7SYuri Pankov 	post_std(mdoc);
1021a40ea1a7SYuri Pankov 
1022a40ea1a7SYuri Pankov 	n = mdoc->last;
1023a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1024a40ea1a7SYuri Pankov 	if (n->child != NULL) {
1025a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "The");
1026a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
1027a40ea1a7SYuri Pankov 		ic = build_list(mdoc, MDOC_Fn);
1028a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos,
1029a40ea1a7SYuri Pankov 		    ic > 1 ? "functions return" : "function returns");
1030a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
1031a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos,
1032a40ea1a7SYuri Pankov 		    "the value\\~0 if successful;");
1033a40ea1a7SYuri Pankov 	} else
1034a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
1035a40ea1a7SYuri Pankov 		    "completion, the value\\~0 is returned;");
1036a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1037a40ea1a7SYuri Pankov 
1038a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
1039a40ea1a7SYuri Pankov 	    "the value\\~\\-1 is returned and the global variable");
1040a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1041a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
1042a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1043a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "errno");
1044a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1045a40ea1a7SYuri Pankov 	mdoc->last = mdoc->last->parent;
1046a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
1047a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
1048a40ea1a7SYuri Pankov 	    "is set to indicate the error.");
1049a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
1050a40ea1a7SYuri Pankov 	mdoc->last = n;
1051a40ea1a7SYuri Pankov }
1052a40ea1a7SYuri Pankov 
1053260e9a87SYuri Pankov static void
1054371584c2SYuri Pankov post_std(POST_ARGS)
105595c635efSGarrett D'Amore {
1056371584c2SYuri Pankov 	struct roff_node *n;
105795c635efSGarrett D'Amore 
1058c66b8046SYuri Pankov 	post_delim(mdoc);
1059c66b8046SYuri Pankov 
1060371584c2SYuri Pankov 	n = mdoc->last;
1061371584c2SYuri Pankov 	if (n->args && n->args->argc == 1)
1062371584c2SYuri Pankov 		if (n->args->argv[0].arg == MDOC_Std)
1063260e9a87SYuri Pankov 			return;
106495c635efSGarrett D'Amore 
1065*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
1066*cec8643bSMichal Nowak 	    "%s", roff_name[n->tok]);
106795c635efSGarrett D'Amore }
106895c635efSGarrett D'Amore 
1069a40ea1a7SYuri Pankov static void
1070a40ea1a7SYuri Pankov post_st(POST_ARGS)
1071a40ea1a7SYuri Pankov {
1072a40ea1a7SYuri Pankov 	struct roff_node	 *n, *nch;
1073a40ea1a7SYuri Pankov 	const char		 *p;
1074a40ea1a7SYuri Pankov 
1075a40ea1a7SYuri Pankov 	n = mdoc->last;
1076a40ea1a7SYuri Pankov 	nch = n->child;
1077a40ea1a7SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
1078a40ea1a7SYuri Pankov 
1079a40ea1a7SYuri Pankov 	if ((p = mdoc_a2st(nch->string)) == NULL) {
1080*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ST_BAD,
1081a40ea1a7SYuri Pankov 		    nch->line, nch->pos, "St %s", nch->string);
1082a40ea1a7SYuri Pankov 		roff_node_delete(mdoc, n);
1083a40ea1a7SYuri Pankov 		return;
1084a40ea1a7SYuri Pankov 	}
1085a40ea1a7SYuri Pankov 
1086a40ea1a7SYuri Pankov 	nch->flags |= NODE_NOPRT;
1087a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1088a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, nch->line, nch->pos, p);
1089a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1090a40ea1a7SYuri Pankov 	mdoc->last= n;
1091a40ea1a7SYuri Pankov }
1092a40ea1a7SYuri Pankov 
1093260e9a87SYuri Pankov static void
1094371584c2SYuri Pankov post_obsolete(POST_ARGS)
109595c635efSGarrett D'Amore {
1096371584c2SYuri Pankov 	struct roff_node *n;
109795c635efSGarrett D'Amore 
1098371584c2SYuri Pankov 	n = mdoc->last;
1099371584c2SYuri Pankov 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1100*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
1101*cec8643bSMichal Nowak 		    "%s", roff_name[n->tok]);
1102c66b8046SYuri Pankov }
1103c66b8046SYuri Pankov 
1104c66b8046SYuri Pankov static void
1105c66b8046SYuri Pankov post_useless(POST_ARGS)
1106c66b8046SYuri Pankov {
1107c66b8046SYuri Pankov 	struct roff_node *n;
1108c66b8046SYuri Pankov 
1109c66b8046SYuri Pankov 	n = mdoc->last;
1110*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
1111*cec8643bSMichal Nowak 	    "%s", roff_name[n->tok]);
1112260e9a87SYuri Pankov }
111395c635efSGarrett D'Amore 
1114a40ea1a7SYuri Pankov /*
1115a40ea1a7SYuri Pankov  * Block macros.
1116a40ea1a7SYuri Pankov  */
1117a40ea1a7SYuri Pankov 
1118260e9a87SYuri Pankov static void
111995c635efSGarrett D'Amore post_bf(POST_ARGS)
112095c635efSGarrett D'Amore {
1121371584c2SYuri Pankov 	struct roff_node *np, *nch;
112295c635efSGarrett D'Amore 
112395c635efSGarrett D'Amore 	/*
112495c635efSGarrett D'Amore 	 * Unlike other data pointers, these are "housed" by the HEAD
112595c635efSGarrett D'Amore 	 * element, which contains the goods.
112695c635efSGarrett D'Amore 	 */
112795c635efSGarrett D'Amore 
112895c635efSGarrett D'Amore 	np = mdoc->last;
1129371584c2SYuri Pankov 	if (np->type != ROFFT_HEAD)
1130260e9a87SYuri Pankov 		return;
1131260e9a87SYuri Pankov 
1132371584c2SYuri Pankov 	assert(np->parent->type == ROFFT_BLOCK);
1133371584c2SYuri Pankov 	assert(np->parent->tok == MDOC_Bf);
113495c635efSGarrett D'Amore 
1135260e9a87SYuri Pankov 	/* Check the number of arguments. */
113695c635efSGarrett D'Amore 
1137260e9a87SYuri Pankov 	nch = np->child;
1138371584c2SYuri Pankov 	if (np->parent->args == NULL) {
1139371584c2SYuri Pankov 		if (nch == NULL) {
1140*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BF_NOFONT,
1141260e9a87SYuri Pankov 			    np->line, np->pos, "Bf");
1142260e9a87SYuri Pankov 			return;
1143260e9a87SYuri Pankov 		}
1144260e9a87SYuri Pankov 		nch = nch->next;
114595c635efSGarrett D'Amore 	}
1146371584c2SYuri Pankov 	if (nch != NULL)
1147*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
1148260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bf ... %s", nch->string);
114995c635efSGarrett D'Amore 
115095c635efSGarrett D'Amore 	/* Extract argument into data. */
1151260e9a87SYuri Pankov 
1152371584c2SYuri Pankov 	if (np->parent->args != NULL) {
1153371584c2SYuri Pankov 		switch (np->parent->args->argv[0].arg) {
1154371584c2SYuri Pankov 		case MDOC_Emphasis:
115595c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Em;
1156371584c2SYuri Pankov 			break;
1157371584c2SYuri Pankov 		case MDOC_Literal:
115895c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Li;
1159371584c2SYuri Pankov 			break;
1160371584c2SYuri Pankov 		case MDOC_Symbolic:
116195c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Sy;
1162371584c2SYuri Pankov 			break;
1163371584c2SYuri Pankov 		default:
116495c635efSGarrett D'Amore 			abort();
1165371584c2SYuri Pankov 		}
1166260e9a87SYuri Pankov 		return;
116795c635efSGarrett D'Amore 	}
116895c635efSGarrett D'Amore 
116995c635efSGarrett D'Amore 	/* Extract parameter into data. */
117095c635efSGarrett D'Amore 
1171371584c2SYuri Pankov 	if ( ! strcmp(np->child->string, "Em"))
117295c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Em;
1173371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Li"))
117495c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Li;
1175371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Sy"))
117695c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Sy;
1177260e9a87SYuri Pankov 	else
1178*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
1179*cec8643bSMichal Nowak 		    np->child->pos, "Bf %s", np->child->string);
118095c635efSGarrett D'Amore }
118195c635efSGarrett D'Amore 
1182260e9a87SYuri Pankov static void
1183260e9a87SYuri Pankov post_fname(POST_ARGS)
1184260e9a87SYuri Pankov {
1185371584c2SYuri Pankov 	const struct roff_node	*n;
1186260e9a87SYuri Pankov 	const char		*cp;
1187260e9a87SYuri Pankov 	size_t			 pos;
118895c635efSGarrett D'Amore 
1189260e9a87SYuri Pankov 	n = mdoc->last->child;
1190260e9a87SYuri Pankov 	pos = strcspn(n->string, "()");
1191260e9a87SYuri Pankov 	cp = n->string + pos;
1192260e9a87SYuri Pankov 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
1193*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos,
1194*cec8643bSMichal Nowak 		    "%s", n->string);
119595c635efSGarrett D'Amore }
119695c635efSGarrett D'Amore 
1197260e9a87SYuri Pankov static void
1198260e9a87SYuri Pankov post_fn(POST_ARGS)
119995c635efSGarrett D'Amore {
120095c635efSGarrett D'Amore 
1201260e9a87SYuri Pankov 	post_fname(mdoc);
1202260e9a87SYuri Pankov 	post_fa(mdoc);
120395c635efSGarrett D'Amore }
120495c635efSGarrett D'Amore 
1205260e9a87SYuri Pankov static void
1206260e9a87SYuri Pankov post_fo(POST_ARGS)
1207260e9a87SYuri Pankov {
1208371584c2SYuri Pankov 	const struct roff_node	*n;
120995c635efSGarrett D'Amore 
1210260e9a87SYuri Pankov 	n = mdoc->last;
1211260e9a87SYuri Pankov 
1212371584c2SYuri Pankov 	if (n->type != ROFFT_HEAD)
1213260e9a87SYuri Pankov 		return;
1214260e9a87SYuri Pankov 
1215260e9a87SYuri Pankov 	if (n->child == NULL) {
1216*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
1217260e9a87SYuri Pankov 		return;
1218260e9a87SYuri Pankov 	}
1219260e9a87SYuri Pankov 	if (n->child != n->last) {
1220*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
1221260e9a87SYuri Pankov 		    n->child->next->line, n->child->next->pos,
1222260e9a87SYuri Pankov 		    "Fo ... %s", n->child->next->string);
1223260e9a87SYuri Pankov 		while (n->child != n->last)
1224371584c2SYuri Pankov 			roff_node_delete(mdoc, n->last);
1225c66b8046SYuri Pankov 	} else
1226c66b8046SYuri Pankov 		post_delim(mdoc);
1227260e9a87SYuri Pankov 
1228260e9a87SYuri Pankov 	post_fname(mdoc);
1229260e9a87SYuri Pankov }
1230260e9a87SYuri Pankov 
1231260e9a87SYuri Pankov static void
1232260e9a87SYuri Pankov post_fa(POST_ARGS)
1233260e9a87SYuri Pankov {
1234371584c2SYuri Pankov 	const struct roff_node *n;
1235260e9a87SYuri Pankov 	const char *cp;
1236260e9a87SYuri Pankov 
1237260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1238260e9a87SYuri Pankov 		for (cp = n->string; *cp != '\0'; cp++) {
1239260e9a87SYuri Pankov 			/* Ignore callbacks and alterations. */
1240260e9a87SYuri Pankov 			if (*cp == '(' || *cp == '{')
1241260e9a87SYuri Pankov 				break;
1242260e9a87SYuri Pankov 			if (*cp != ',')
1243260e9a87SYuri Pankov 				continue;
1244*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_FA_COMMA, n->line,
1245*cec8643bSMichal Nowak 			    n->pos + (int)(cp - n->string), "%s", n->string);
1246260e9a87SYuri Pankov 			break;
1247260e9a87SYuri Pankov 		}
1248260e9a87SYuri Pankov 	}
1249c66b8046SYuri Pankov 	post_delim_nb(mdoc);
1250260e9a87SYuri Pankov }
1251260e9a87SYuri Pankov 
1252260e9a87SYuri Pankov static void
125395c635efSGarrett D'Amore post_nm(POST_ARGS)
125495c635efSGarrett D'Amore {
1255371584c2SYuri Pankov 	struct roff_node	*n;
1256260e9a87SYuri Pankov 
1257260e9a87SYuri Pankov 	n = mdoc->last;
1258260e9a87SYuri Pankov 
1259c66b8046SYuri Pankov 	if (n->sec == SEC_NAME && n->child != NULL &&
1260c66b8046SYuri Pankov 	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
1261c66b8046SYuri Pankov 		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
1262c66b8046SYuri Pankov 
1263*cec8643bSMichal Nowak 	if (n->last != NULL && n->last->tok == MDOC_Pp)
1264*cec8643bSMichal Nowak 		roff_node_relink(mdoc, n->last);
126595c635efSGarrett D'Amore 
1266371584c2SYuri Pankov 	if (mdoc->meta.name == NULL)
1267a40ea1a7SYuri Pankov 		deroff(&mdoc->meta.name, n);
1268a40ea1a7SYuri Pankov 
1269a40ea1a7SYuri Pankov 	if (mdoc->meta.name == NULL ||
1270a40ea1a7SYuri Pankov 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1271*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
1272a40ea1a7SYuri Pankov 
1273c66b8046SYuri Pankov 	switch (n->type) {
1274c66b8046SYuri Pankov 	case ROFFT_ELEM:
1275c66b8046SYuri Pankov 		post_delim_nb(mdoc);
1276c66b8046SYuri Pankov 		break;
1277c66b8046SYuri Pankov 	case ROFFT_HEAD:
1278c66b8046SYuri Pankov 		post_delim(mdoc);
1279c66b8046SYuri Pankov 		break;
1280c66b8046SYuri Pankov 	default:
1281c66b8046SYuri Pankov 		return;
1282c66b8046SYuri Pankov 	}
1283c66b8046SYuri Pankov 
1284c66b8046SYuri Pankov 	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
1285a40ea1a7SYuri Pankov 	    mdoc->meta.name == NULL)
1286a40ea1a7SYuri Pankov 		return;
1287a40ea1a7SYuri Pankov 
1288a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1289a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1290a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1291a40ea1a7SYuri Pankov 	mdoc->last = n;
129295c635efSGarrett D'Amore }
129395c635efSGarrett D'Amore 
1294260e9a87SYuri Pankov static void
1295260e9a87SYuri Pankov post_nd(POST_ARGS)
1296260e9a87SYuri Pankov {
1297371584c2SYuri Pankov 	struct roff_node	*n;
1298260e9a87SYuri Pankov 
1299260e9a87SYuri Pankov 	n = mdoc->last;
1300260e9a87SYuri Pankov 
1301371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
1302260e9a87SYuri Pankov 		return;
1303260e9a87SYuri Pankov 
1304c66b8046SYuri Pankov 	if (n->sec != SEC_NAME)
1305*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
1306c66b8046SYuri Pankov 
1307260e9a87SYuri Pankov 	if (n->child == NULL)
1308*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
1309c66b8046SYuri Pankov 	else
1310c66b8046SYuri Pankov 		post_delim(mdoc);
1311260e9a87SYuri Pankov 
1312260e9a87SYuri Pankov 	post_hyph(mdoc);
1313260e9a87SYuri Pankov }
1314260e9a87SYuri Pankov 
1315260e9a87SYuri Pankov static void
1316371584c2SYuri Pankov post_display(POST_ARGS)
1317260e9a87SYuri Pankov {
1318371584c2SYuri Pankov 	struct roff_node *n, *np;
1319260e9a87SYuri Pankov 
1320260e9a87SYuri Pankov 	n = mdoc->last;
1321371584c2SYuri Pankov 	switch (n->type) {
1322371584c2SYuri Pankov 	case ROFFT_BODY:
1323a40ea1a7SYuri Pankov 		if (n->end != ENDBODY_NOT) {
1324a40ea1a7SYuri Pankov 			if (n->tok == MDOC_Bd &&
1325a40ea1a7SYuri Pankov 			    n->body->parent->args == NULL)
1326a40ea1a7SYuri Pankov 				roff_node_delete(mdoc, n);
1327a40ea1a7SYuri Pankov 		} else if (n->child == NULL)
1328*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
1329*cec8643bSMichal Nowak 			    "%s", roff_name[n->tok]);
1330371584c2SYuri Pankov 		else if (n->tok == MDOC_D1)
1331371584c2SYuri Pankov 			post_hyph(mdoc);
1332371584c2SYuri Pankov 		break;
1333371584c2SYuri Pankov 	case ROFFT_BLOCK:
1334371584c2SYuri Pankov 		if (n->tok == MDOC_Bd) {
1335371584c2SYuri Pankov 			if (n->args == NULL) {
1336371584c2SYuri Pankov 				mandoc_msg(MANDOCERR_BD_NOARG,
1337*cec8643bSMichal Nowak 				    n->line, n->pos, "Bd");
1338371584c2SYuri Pankov 				mdoc->next = ROFF_NEXT_SIBLING;
1339371584c2SYuri Pankov 				while (n->body->child != NULL)
1340*cec8643bSMichal Nowak 					roff_node_relink(mdoc,
1341371584c2SYuri Pankov 					    n->body->child);
1342371584c2SYuri Pankov 				roff_node_delete(mdoc, n);
1343371584c2SYuri Pankov 				break;
1344371584c2SYuri Pankov 			}
1345371584c2SYuri Pankov 			post_bd(mdoc);
1346371584c2SYuri Pankov 			post_prevpar(mdoc);
1347371584c2SYuri Pankov 		}
1348371584c2SYuri Pankov 		for (np = n->parent; np != NULL; np = np->parent) {
1349371584c2SYuri Pankov 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1350*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_BD_NEST, n->line,
1351*cec8643bSMichal Nowak 				    n->pos, "%s in Bd", roff_name[n->tok]);
1352371584c2SYuri Pankov 				break;
1353371584c2SYuri Pankov 			}
1354371584c2SYuri Pankov 		}
1355371584c2SYuri Pankov 		break;
1356371584c2SYuri Pankov 	default:
1357371584c2SYuri Pankov 		break;
1358371584c2SYuri Pankov 	}
135995c635efSGarrett D'Amore }
136095c635efSGarrett D'Amore 
1361260e9a87SYuri Pankov static void
136295c635efSGarrett D'Amore post_defaults(POST_ARGS)
136395c635efSGarrett D'Amore {
1364371584c2SYuri Pankov 	struct roff_node *nn;
136595c635efSGarrett D'Amore 
1366c66b8046SYuri Pankov 	if (mdoc->last->child != NULL) {
1367c66b8046SYuri Pankov 		post_delim_nb(mdoc);
1368c66b8046SYuri Pankov 		return;
1369c66b8046SYuri Pankov 	}
1370c66b8046SYuri Pankov 
137195c635efSGarrett D'Amore 	/*
137295c635efSGarrett D'Amore 	 * The `Ar' defaults to "file ..." if no value is provided as an
137395c635efSGarrett D'Amore 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
137495c635efSGarrett D'Amore 	 * gets an empty string.
137595c635efSGarrett D'Amore 	 */
137695c635efSGarrett D'Amore 
137795c635efSGarrett D'Amore 	nn = mdoc->last;
137895c635efSGarrett D'Amore 	switch (nn->tok) {
1379260e9a87SYuri Pankov 	case MDOC_Ar:
1380371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
1381371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1382a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
1383371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1384a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
138595c635efSGarrett D'Amore 		break;
1386260e9a87SYuri Pankov 	case MDOC_Pa:
1387260e9a87SYuri Pankov 	case MDOC_Mt:
1388371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
1389371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1390a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
139195c635efSGarrett D'Amore 		break;
139295c635efSGarrett D'Amore 	default:
139395c635efSGarrett D'Amore 		abort();
1394260e9a87SYuri Pankov 	}
139595c635efSGarrett D'Amore 	mdoc->last = nn;
139695c635efSGarrett D'Amore }
139795c635efSGarrett D'Amore 
1398260e9a87SYuri Pankov static void
139995c635efSGarrett D'Amore post_at(POST_ARGS)
140095c635efSGarrett D'Amore {
1401a40ea1a7SYuri Pankov 	struct roff_node	*n, *nch;
1402a40ea1a7SYuri Pankov 	const char		*att;
1403260e9a87SYuri Pankov 
1404260e9a87SYuri Pankov 	n = mdoc->last;
1405a40ea1a7SYuri Pankov 	nch = n->child;
140695c635efSGarrett D'Amore 
140795c635efSGarrett D'Amore 	/*
140895c635efSGarrett D'Amore 	 * If we have a child, look it up in the standard keys.  If a
140995c635efSGarrett D'Amore 	 * key exist, use that instead of the child; if it doesn't,
141095c635efSGarrett D'Amore 	 * prefix "AT&T UNIX " to the existing data.
141195c635efSGarrett D'Amore 	 */
141295c635efSGarrett D'Amore 
1413a40ea1a7SYuri Pankov 	att = NULL;
1414a40ea1a7SYuri Pankov 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1415*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_AT_BAD,
1416a40ea1a7SYuri Pankov 		    nch->line, nch->pos, "At %s", nch->string);
141795c635efSGarrett D'Amore 
1418a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1419a40ea1a7SYuri Pankov 	if (att != NULL) {
1420a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
1421a40ea1a7SYuri Pankov 		nch->flags |= NODE_NOPRT;
1422a40ea1a7SYuri Pankov 	} else
1423a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1424a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1425a40ea1a7SYuri Pankov 	mdoc->last = n;
142695c635efSGarrett D'Amore }
142795c635efSGarrett D'Amore 
1428260e9a87SYuri Pankov static void
142995c635efSGarrett D'Amore post_an(POST_ARGS)
143095c635efSGarrett D'Amore {
1431371584c2SYuri Pankov 	struct roff_node *np, *nch;
1432371584c2SYuri Pankov 
1433371584c2SYuri Pankov 	post_an_norm(mdoc);
143495c635efSGarrett D'Amore 
143595c635efSGarrett D'Amore 	np = mdoc->last;
1436260e9a87SYuri Pankov 	nch = np->child;
1437260e9a87SYuri Pankov 	if (np->norm->An.auth == AUTH__NONE) {
1438260e9a87SYuri Pankov 		if (nch == NULL)
1439*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
1440260e9a87SYuri Pankov 			    np->line, np->pos, "An");
1441c66b8046SYuri Pankov 		else
1442c66b8046SYuri Pankov 			post_delim_nb(mdoc);
1443260e9a87SYuri Pankov 	} else if (nch != NULL)
1444*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
1445260e9a87SYuri Pankov 		    nch->line, nch->pos, "An ... %s", nch->string);
1446260e9a87SYuri Pankov }
1447260e9a87SYuri Pankov 
1448260e9a87SYuri Pankov static void
1449260e9a87SYuri Pankov post_en(POST_ARGS)
1450260e9a87SYuri Pankov {
145195c635efSGarrett D'Amore 
1452371584c2SYuri Pankov 	post_obsolete(mdoc);
1453371584c2SYuri Pankov 	if (mdoc->last->type == ROFFT_BLOCK)
1454260e9a87SYuri Pankov 		mdoc->last->norm->Es = mdoc->last_es;
145595c635efSGarrett D'Amore }
145695c635efSGarrett D'Amore 
1457260e9a87SYuri Pankov static void
1458260e9a87SYuri Pankov post_es(POST_ARGS)
1459260e9a87SYuri Pankov {
146095c635efSGarrett D'Amore 
1461371584c2SYuri Pankov 	post_obsolete(mdoc);
1462260e9a87SYuri Pankov 	mdoc->last_es = mdoc->last;
1463260e9a87SYuri Pankov }
1464260e9a87SYuri Pankov 
1465a40ea1a7SYuri Pankov static void
1466a40ea1a7SYuri Pankov post_xx(POST_ARGS)
1467a40ea1a7SYuri Pankov {
1468a40ea1a7SYuri Pankov 	struct roff_node	*n;
1469a40ea1a7SYuri Pankov 	const char		*os;
1470c66b8046SYuri Pankov 	char			*v;
1471c66b8046SYuri Pankov 
1472c66b8046SYuri Pankov 	post_delim_nb(mdoc);
1473a40ea1a7SYuri Pankov 
1474a40ea1a7SYuri Pankov 	n = mdoc->last;
1475a40ea1a7SYuri Pankov 	switch (n->tok) {
1476a40ea1a7SYuri Pankov 	case MDOC_Bsx:
1477a40ea1a7SYuri Pankov 		os = "BSD/OS";
1478a40ea1a7SYuri Pankov 		break;
1479a40ea1a7SYuri Pankov 	case MDOC_Dx:
1480a40ea1a7SYuri Pankov 		os = "DragonFly";
1481a40ea1a7SYuri Pankov 		break;
1482a40ea1a7SYuri Pankov 	case MDOC_Fx:
1483a40ea1a7SYuri Pankov 		os = "FreeBSD";
1484a40ea1a7SYuri Pankov 		break;
1485a40ea1a7SYuri Pankov 	case MDOC_Nx:
1486a40ea1a7SYuri Pankov 		os = "NetBSD";
1487c66b8046SYuri Pankov 		if (n->child == NULL)
1488c66b8046SYuri Pankov 			break;
1489c66b8046SYuri Pankov 		v = n->child->string;
1490c66b8046SYuri Pankov 		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
1491c66b8046SYuri Pankov 		    v[2] < '0' || v[2] > '9' ||
1492c66b8046SYuri Pankov 		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1493c66b8046SYuri Pankov 			break;
1494c66b8046SYuri Pankov 		n->child->flags |= NODE_NOPRT;
1495c66b8046SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
1496c66b8046SYuri Pankov 		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1497c66b8046SYuri Pankov 		v = mdoc->last->string;
1498c66b8046SYuri Pankov 		v[3] = toupper((unsigned char)v[3]);
1499c66b8046SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
1500c66b8046SYuri Pankov 		mdoc->last = n;
1501a40ea1a7SYuri Pankov 		break;
1502a40ea1a7SYuri Pankov 	case MDOC_Ox:
1503a40ea1a7SYuri Pankov 		os = "OpenBSD";
1504a40ea1a7SYuri Pankov 		break;
1505a40ea1a7SYuri Pankov 	case MDOC_Ux:
1506a40ea1a7SYuri Pankov 		os = "UNIX";
1507a40ea1a7SYuri Pankov 		break;
1508a40ea1a7SYuri Pankov 	default:
1509a40ea1a7SYuri Pankov 		abort();
1510a40ea1a7SYuri Pankov 	}
1511a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1512a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, os);
1513a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1514a40ea1a7SYuri Pankov 	mdoc->last = n;
1515a40ea1a7SYuri Pankov }
1516a40ea1a7SYuri Pankov 
1517260e9a87SYuri Pankov static void
151895c635efSGarrett D'Amore post_it(POST_ARGS)
151995c635efSGarrett D'Amore {
1520371584c2SYuri Pankov 	struct roff_node *nbl, *nit, *nch;
152195c635efSGarrett D'Amore 	int		  i, cols;
152295c635efSGarrett D'Amore 	enum mdoc_list	  lt;
1523371584c2SYuri Pankov 
1524371584c2SYuri Pankov 	post_prevpar(mdoc);
152595c635efSGarrett D'Amore 
1526260e9a87SYuri Pankov 	nit = mdoc->last;
1527371584c2SYuri Pankov 	if (nit->type != ROFFT_BLOCK)
1528260e9a87SYuri Pankov 		return;
152995c635efSGarrett D'Amore 
1530260e9a87SYuri Pankov 	nbl = nit->parent->parent;
1531260e9a87SYuri Pankov 	lt = nbl->norm->Bl.type;
153295c635efSGarrett D'Amore 
153395c635efSGarrett D'Amore 	switch (lt) {
1534260e9a87SYuri Pankov 	case LIST_tag:
1535260e9a87SYuri Pankov 	case LIST_hang:
1536260e9a87SYuri Pankov 	case LIST_ohang:
1537260e9a87SYuri Pankov 	case LIST_inset:
1538260e9a87SYuri Pankov 	case LIST_diag:
1539260e9a87SYuri Pankov 		if (nit->head->child == NULL)
1540*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_IT_NOHEAD,
1541*cec8643bSMichal Nowak 			    nit->line, nit->pos, "Bl -%s It",
1542260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
154395c635efSGarrett D'Amore 		break;
1544260e9a87SYuri Pankov 	case LIST_bullet:
1545260e9a87SYuri Pankov 	case LIST_dash:
1546260e9a87SYuri Pankov 	case LIST_enum:
1547260e9a87SYuri Pankov 	case LIST_hyphen:
1548260e9a87SYuri Pankov 		if (nit->body == NULL || nit->body->child == NULL)
1549*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_IT_NOBODY,
1550*cec8643bSMichal Nowak 			    nit->line, nit->pos, "Bl -%s It",
1551260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
155295c635efSGarrett D'Amore 		/* FALLTHROUGH */
1553260e9a87SYuri Pankov 	case LIST_item:
1554a40ea1a7SYuri Pankov 		if ((nch = nit->head->child) != NULL)
1555*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_ARG_SKIP,
1556c66b8046SYuri Pankov 			    nit->line, nit->pos, "It %s",
1557c66b8046SYuri Pankov 			    nch->string == NULL ? roff_name[nch->tok] :
1558c66b8046SYuri Pankov 			    nch->string);
155995c635efSGarrett D'Amore 		break;
1560260e9a87SYuri Pankov 	case LIST_column:
1561260e9a87SYuri Pankov 		cols = (int)nbl->norm->Bl.ncols;
156295c635efSGarrett D'Amore 
1563260e9a87SYuri Pankov 		assert(nit->head->child == NULL);
156495c635efSGarrett D'Amore 
1565c66b8046SYuri Pankov 		if (nit->head->next->child == NULL &&
1566c66b8046SYuri Pankov 		    nit->head->next->next == NULL) {
1567*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
1568c66b8046SYuri Pankov 			    nit->line, nit->pos, "It");
1569c66b8046SYuri Pankov 			roff_node_delete(mdoc, nit);
1570c66b8046SYuri Pankov 			break;
1571c66b8046SYuri Pankov 		}
157295c635efSGarrett D'Amore 
1573c66b8046SYuri Pankov 		i = 0;
1574c66b8046SYuri Pankov 		for (nch = nit->child; nch != NULL; nch = nch->next) {
1575c66b8046SYuri Pankov 			if (nch->type != ROFFT_BODY)
1576c66b8046SYuri Pankov 				continue;
1577c66b8046SYuri Pankov 			if (i++ && nch->flags & NODE_LINE)
1578*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_TA_LINE,
1579c66b8046SYuri Pankov 				    nch->line, nch->pos, "Ta");
1580c66b8046SYuri Pankov 		}
1581260e9a87SYuri Pankov 		if (i < cols || i > cols + 1)
1582*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
1583260e9a87SYuri Pankov 			    "%d columns, %d cells", cols, i);
1584c66b8046SYuri Pankov 		else if (nit->head->next->child != NULL &&
1585*cec8643bSMichal Nowak 		    nit->head->next->child->flags & NODE_LINE)
1586*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_IT_NOARG,
1587c66b8046SYuri Pankov 			    nit->line, nit->pos, "Bl -column It");
158895c635efSGarrett D'Amore 		break;
1589260e9a87SYuri Pankov 	default:
1590260e9a87SYuri Pankov 		abort();
159195c635efSGarrett D'Amore 	}
159295c635efSGarrett D'Amore }
159395c635efSGarrett D'Amore 
1594260e9a87SYuri Pankov static void
1595260e9a87SYuri Pankov post_bl_block(POST_ARGS)
159695c635efSGarrett D'Amore {
1597371584c2SYuri Pankov 	struct roff_node *n, *ni, *nc;
1598371584c2SYuri Pankov 
1599371584c2SYuri Pankov 	post_prevpar(mdoc);
160095c635efSGarrett D'Amore 
160195c635efSGarrett D'Amore 	n = mdoc->last;
1602371584c2SYuri Pankov 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1603371584c2SYuri Pankov 		if (ni->body == NULL)
1604698f87a4SGarrett D'Amore 			continue;
1605698f87a4SGarrett D'Amore 		nc = ni->body->last;
1606371584c2SYuri Pankov 		while (nc != NULL) {
1607698f87a4SGarrett D'Amore 			switch (nc->tok) {
1608260e9a87SYuri Pankov 			case MDOC_Pp:
1609c66b8046SYuri Pankov 			case ROFF_br:
1610698f87a4SGarrett D'Amore 				break;
1611698f87a4SGarrett D'Amore 			default:
1612698f87a4SGarrett D'Amore 				nc = NULL;
1613698f87a4SGarrett D'Amore 				continue;
1614698f87a4SGarrett D'Amore 			}
1615371584c2SYuri Pankov 			if (ni->next == NULL) {
1616*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
1617*cec8643bSMichal Nowak 				    nc->pos, "%s", roff_name[nc->tok]);
1618*cec8643bSMichal Nowak 				roff_node_relink(mdoc, nc);
1619371584c2SYuri Pankov 			} else if (n->norm->Bl.comp == 0 &&
1620371584c2SYuri Pankov 			    n->norm->Bl.type != LIST_column) {
1621*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_PAR_SKIP,
1622*cec8643bSMichal Nowak 				    nc->line, nc->pos,
1623c66b8046SYuri Pankov 				    "%s before It", roff_name[nc->tok]);
1624371584c2SYuri Pankov 				roff_node_delete(mdoc, nc);
1625698f87a4SGarrett D'Amore 			} else
1626698f87a4SGarrett D'Amore 				break;
1627698f87a4SGarrett D'Amore 			nc = ni->body->last;
1628698f87a4SGarrett D'Amore 		}
1629698f87a4SGarrett D'Amore 	}
163095c635efSGarrett D'Amore }
163195c635efSGarrett D'Amore 
1632260e9a87SYuri Pankov /*
1633260e9a87SYuri Pankov  * If the argument of -offset or -width is a macro,
1634260e9a87SYuri Pankov  * replace it with the associated default width.
1635260e9a87SYuri Pankov  */
1636c66b8046SYuri Pankov static void
1637c66b8046SYuri Pankov rewrite_macro2len(struct roff_man *mdoc, char **arg)
163895c635efSGarrett D'Amore {
163995c635efSGarrett D'Amore 	size_t		  width;
1640c66b8046SYuri Pankov 	enum roff_tok	  tok;
164195c635efSGarrett D'Amore 
1642260e9a87SYuri Pankov 	if (*arg == NULL)
1643260e9a87SYuri Pankov 		return;
1644260e9a87SYuri Pankov 	else if ( ! strcmp(*arg, "Ds"))
164595c635efSGarrett D'Amore 		width = 6;
1646c66b8046SYuri Pankov 	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1647260e9a87SYuri Pankov 		return;
1648260e9a87SYuri Pankov 	else
1649260e9a87SYuri Pankov 		width = macro2len(tok);
165095c635efSGarrett D'Amore 
1651260e9a87SYuri Pankov 	free(*arg);
1652260e9a87SYuri Pankov 	mandoc_asprintf(arg, "%zun", width);
165395c635efSGarrett D'Amore }
165495c635efSGarrett D'Amore 
1655260e9a87SYuri Pankov static void
1656260e9a87SYuri Pankov post_bl_head(POST_ARGS)
165795c635efSGarrett D'Amore {
1658371584c2SYuri Pankov 	struct roff_node *nbl, *nh, *nch, *nnext;
1659260e9a87SYuri Pankov 	struct mdoc_argv *argv;
166095c635efSGarrett D'Amore 	int		  i, j;
166195c635efSGarrett D'Amore 
1662371584c2SYuri Pankov 	post_bl_norm(mdoc);
1663260e9a87SYuri Pankov 
1664371584c2SYuri Pankov 	nh = mdoc->last;
1665260e9a87SYuri Pankov 	if (nh->norm->Bl.type != LIST_column) {
1666260e9a87SYuri Pankov 		if ((nch = nh->child) == NULL)
1667260e9a87SYuri Pankov 			return;
1668*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
1669260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1670260e9a87SYuri Pankov 		while (nch != NULL) {
1671371584c2SYuri Pankov 			roff_node_delete(mdoc, nch);
1672260e9a87SYuri Pankov 			nch = nh->child;
1673260e9a87SYuri Pankov 		}
1674260e9a87SYuri Pankov 		return;
1675260e9a87SYuri Pankov 	}
167695c635efSGarrett D'Amore 
167795c635efSGarrett D'Amore 	/*
1678260e9a87SYuri Pankov 	 * Append old-style lists, where the column width specifiers
167995c635efSGarrett D'Amore 	 * trail as macro parameters, to the new-style ("normal-form")
168095c635efSGarrett D'Amore 	 * lists where they're argument values following -column.
168195c635efSGarrett D'Amore 	 */
168295c635efSGarrett D'Amore 
1683260e9a87SYuri Pankov 	if (nh->child == NULL)
1684260e9a87SYuri Pankov 		return;
168595c635efSGarrett D'Amore 
1686260e9a87SYuri Pankov 	nbl = nh->parent;
1687260e9a87SYuri Pankov 	for (j = 0; j < (int)nbl->args->argc; j++)
1688260e9a87SYuri Pankov 		if (nbl->args->argv[j].arg == MDOC_Column)
168995c635efSGarrett D'Amore 			break;
169095c635efSGarrett D'Amore 
1691260e9a87SYuri Pankov 	assert(j < (int)nbl->args->argc);
169295c635efSGarrett D'Amore 
169395c635efSGarrett D'Amore 	/*
169495c635efSGarrett D'Amore 	 * Accommodate for new-style groff column syntax.  Shuffle the
169595c635efSGarrett D'Amore 	 * child nodes, all of which must be TEXT, as arguments for the
169695c635efSGarrett D'Amore 	 * column field.  Then, delete the head children.
169795c635efSGarrett D'Amore 	 */
169895c635efSGarrett D'Amore 
1699260e9a87SYuri Pankov 	argv = nbl->args->argv + j;
1700260e9a87SYuri Pankov 	i = argv->sz;
1701371584c2SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nch->next)
1702371584c2SYuri Pankov 		argv->sz++;
1703260e9a87SYuri Pankov 	argv->value = mandoc_reallocarray(argv->value,
1704260e9a87SYuri Pankov 	    argv->sz, sizeof(char *));
170595c635efSGarrett D'Amore 
1706260e9a87SYuri Pankov 	nh->norm->Bl.ncols = argv->sz;
1707260e9a87SYuri Pankov 	nh->norm->Bl.cols = (void *)argv->value;
170895c635efSGarrett D'Amore 
1709260e9a87SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nnext) {
1710260e9a87SYuri Pankov 		argv->value[i++] = nch->string;
1711260e9a87SYuri Pankov 		nch->string = NULL;
1712260e9a87SYuri Pankov 		nnext = nch->next;
1713371584c2SYuri Pankov 		roff_node_delete(NULL, nch);
171495c635efSGarrett D'Amore 	}
1715260e9a87SYuri Pankov 	nh->child = NULL;
171695c635efSGarrett D'Amore }
171795c635efSGarrett D'Amore 
1718260e9a87SYuri Pankov static void
171995c635efSGarrett D'Amore post_bl(POST_ARGS)
172095c635efSGarrett D'Amore {
1721371584c2SYuri Pankov 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1722371584c2SYuri Pankov 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1723371584c2SYuri Pankov 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1724c66b8046SYuri Pankov 	const char		*prev_Er;
1725c66b8046SYuri Pankov 	int			 order;
172695c635efSGarrett D'Amore 
1727698f87a4SGarrett D'Amore 	nbody = mdoc->last;
1728698f87a4SGarrett D'Amore 	switch (nbody->type) {
1729371584c2SYuri Pankov 	case ROFFT_BLOCK:
1730260e9a87SYuri Pankov 		post_bl_block(mdoc);
1731260e9a87SYuri Pankov 		return;
1732371584c2SYuri Pankov 	case ROFFT_HEAD:
1733260e9a87SYuri Pankov 		post_bl_head(mdoc);
1734260e9a87SYuri Pankov 		return;
1735371584c2SYuri Pankov 	case ROFFT_BODY:
1736698f87a4SGarrett D'Amore 		break;
1737698f87a4SGarrett D'Amore 	default:
1738260e9a87SYuri Pankov 		return;
1739698f87a4SGarrett D'Amore 	}
1740371584c2SYuri Pankov 	if (nbody->end != ENDBODY_NOT)
1741371584c2SYuri Pankov 		return;
174295c635efSGarrett D'Amore 
1743698f87a4SGarrett D'Amore 	nchild = nbody->child;
1744260e9a87SYuri Pankov 	if (nchild == NULL) {
1745*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1746260e9a87SYuri Pankov 		    nbody->line, nbody->pos, "Bl");
1747260e9a87SYuri Pankov 		return;
1748260e9a87SYuri Pankov 	}
1749260e9a87SYuri Pankov 	while (nchild != NULL) {
1750a40ea1a7SYuri Pankov 		nnext = nchild->next;
1751260e9a87SYuri Pankov 		if (nchild->tok == MDOC_It ||
1752260e9a87SYuri Pankov 		    (nchild->tok == MDOC_Sm &&
1753a40ea1a7SYuri Pankov 		     nnext != NULL && nnext->tok == MDOC_It)) {
1754a40ea1a7SYuri Pankov 			nchild = nnext;
1755a40ea1a7SYuri Pankov 			continue;
1756a40ea1a7SYuri Pankov 		}
1757a40ea1a7SYuri Pankov 
1758a40ea1a7SYuri Pankov 		/*
1759a40ea1a7SYuri Pankov 		 * In .Bl -column, the first rows may be implicit,
1760a40ea1a7SYuri Pankov 		 * that is, they may not start with .It macros.
1761a40ea1a7SYuri Pankov 		 * Such rows may be followed by nodes generated on the
1762a40ea1a7SYuri Pankov 		 * roff level, for example .TS, which cannot be moved
1763a40ea1a7SYuri Pankov 		 * out of the list.  In that case, wrap such roff nodes
1764a40ea1a7SYuri Pankov 		 * into an implicit row.
1765a40ea1a7SYuri Pankov 		 */
1766a40ea1a7SYuri Pankov 
1767a40ea1a7SYuri Pankov 		if (nchild->prev != NULL) {
1768a40ea1a7SYuri Pankov 			mdoc->last = nchild;
1769a40ea1a7SYuri Pankov 			mdoc->next = ROFF_NEXT_SIBLING;
1770a40ea1a7SYuri Pankov 			roff_block_alloc(mdoc, nchild->line,
1771a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1772a40ea1a7SYuri Pankov 			roff_head_alloc(mdoc, nchild->line,
1773a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1774a40ea1a7SYuri Pankov 			mdoc->next = ROFF_NEXT_SIBLING;
1775a40ea1a7SYuri Pankov 			roff_body_alloc(mdoc, nchild->line,
1776a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1777a40ea1a7SYuri Pankov 			while (nchild->tok != MDOC_It) {
1778*cec8643bSMichal Nowak 				roff_node_relink(mdoc, nchild);
1779a40ea1a7SYuri Pankov 				if ((nchild = nnext) == NULL)
1780a40ea1a7SYuri Pankov 					break;
1781a40ea1a7SYuri Pankov 				nnext = nchild->next;
1782a40ea1a7SYuri Pankov 				mdoc->next = ROFF_NEXT_SIBLING;
1783a40ea1a7SYuri Pankov 			}
1784a40ea1a7SYuri Pankov 			mdoc->last = nbody;
178595c635efSGarrett D'Amore 			continue;
178695c635efSGarrett D'Amore 		}
178795c635efSGarrett D'Amore 
1788*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos,
1789*cec8643bSMichal Nowak 		    "%s", roff_name[nchild->tok]);
1790698f87a4SGarrett D'Amore 
1791698f87a4SGarrett D'Amore 		/*
1792698f87a4SGarrett D'Amore 		 * Move the node out of the Bl block.
1793698f87a4SGarrett D'Amore 		 * First, collect all required node pointers.
1794698f87a4SGarrett D'Amore 		 */
1795698f87a4SGarrett D'Amore 
1796698f87a4SGarrett D'Amore 		nblock  = nbody->parent;
1797698f87a4SGarrett D'Amore 		nprev   = nblock->prev;
1798698f87a4SGarrett D'Amore 		nparent = nblock->parent;
1799698f87a4SGarrett D'Amore 
1800698f87a4SGarrett D'Amore 		/*
1801698f87a4SGarrett D'Amore 		 * Unlink this child.
1802698f87a4SGarrett D'Amore 		 */
1803698f87a4SGarrett D'Amore 
1804371584c2SYuri Pankov 		nbody->child = nnext;
1805371584c2SYuri Pankov 		if (nnext == NULL)
1806698f87a4SGarrett D'Amore 			nbody->last  = NULL;
1807371584c2SYuri Pankov 		else
1808698f87a4SGarrett D'Amore 			nnext->prev = NULL;
1809698f87a4SGarrett D'Amore 
1810698f87a4SGarrett D'Amore 		/*
1811698f87a4SGarrett D'Amore 		 * Relink this child.
1812698f87a4SGarrett D'Amore 		 */
1813698f87a4SGarrett D'Amore 
1814698f87a4SGarrett D'Amore 		nchild->parent = nparent;
1815698f87a4SGarrett D'Amore 		nchild->prev   = nprev;
1816698f87a4SGarrett D'Amore 		nchild->next   = nblock;
1817698f87a4SGarrett D'Amore 
1818698f87a4SGarrett D'Amore 		nblock->prev = nchild;
1819371584c2SYuri Pankov 		if (nprev == NULL)
1820698f87a4SGarrett D'Amore 			nparent->child = nchild;
1821698f87a4SGarrett D'Amore 		else
1822698f87a4SGarrett D'Amore 			nprev->next = nchild;
1823698f87a4SGarrett D'Amore 
1824698f87a4SGarrett D'Amore 		nchild = nnext;
182595c635efSGarrett D'Amore 	}
1826c66b8046SYuri Pankov 
1827c66b8046SYuri Pankov 	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1828c66b8046SYuri Pankov 		return;
1829c66b8046SYuri Pankov 
1830c66b8046SYuri Pankov 	prev_Er = NULL;
1831c66b8046SYuri Pankov 	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1832c66b8046SYuri Pankov 		if (nchild->tok != MDOC_It)
1833c66b8046SYuri Pankov 			continue;
1834c66b8046SYuri Pankov 		if ((nnext = nchild->head->child) == NULL)
1835c66b8046SYuri Pankov 			continue;
1836c66b8046SYuri Pankov 		if (nnext->type == ROFFT_BLOCK)
1837c66b8046SYuri Pankov 			nnext = nnext->body->child;
1838c66b8046SYuri Pankov 		if (nnext == NULL || nnext->tok != MDOC_Er)
1839c66b8046SYuri Pankov 			continue;
1840c66b8046SYuri Pankov 		nnext = nnext->child;
1841c66b8046SYuri Pankov 		if (prev_Er != NULL) {
1842c66b8046SYuri Pankov 			order = strcmp(prev_Er, nnext->string);
1843c66b8046SYuri Pankov 			if (order > 0)
1844*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ER_ORDER,
1845*cec8643bSMichal Nowak 				    nnext->line, nnext->pos,
1846c66b8046SYuri Pankov 				    "Er %s %s (NetBSD)",
1847c66b8046SYuri Pankov 				    prev_Er, nnext->string);
1848c66b8046SYuri Pankov 			else if (order == 0)
1849*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_ER_REP,
1850*cec8643bSMichal Nowak 				    nnext->line, nnext->pos,
1851c66b8046SYuri Pankov 				    "Er %s (NetBSD)", prev_Er);
1852c66b8046SYuri Pankov 		}
1853c66b8046SYuri Pankov 		prev_Er = nnext->string;
1854c66b8046SYuri Pankov 	}
1855260e9a87SYuri Pankov }
1856260e9a87SYuri Pankov 
1857260e9a87SYuri Pankov static void
1858260e9a87SYuri Pankov post_bk(POST_ARGS)
1859260e9a87SYuri Pankov {
1860371584c2SYuri Pankov 	struct roff_node	*n;
1861260e9a87SYuri Pankov 
1862260e9a87SYuri Pankov 	n = mdoc->last;
186395c635efSGarrett D'Amore 
1864371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1865*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
1866371584c2SYuri Pankov 		roff_node_delete(mdoc, n);
1867260e9a87SYuri Pankov 	}
186895c635efSGarrett D'Amore }
186995c635efSGarrett D'Amore 
1870260e9a87SYuri Pankov static void
1871371584c2SYuri Pankov post_sm(POST_ARGS)
187295c635efSGarrett D'Amore {
1873371584c2SYuri Pankov 	struct roff_node	*nch;
187495c635efSGarrett D'Amore 
1875260e9a87SYuri Pankov 	nch = mdoc->last->child;
1876260e9a87SYuri Pankov 
1877260e9a87SYuri Pankov 	if (nch == NULL) {
1878260e9a87SYuri Pankov 		mdoc->flags ^= MDOC_SMOFF;
1879260e9a87SYuri Pankov 		return;
188095c635efSGarrett D'Amore 	}
188195c635efSGarrett D'Amore 
1882371584c2SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
188395c635efSGarrett D'Amore 
1884260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "on")) {
1885260e9a87SYuri Pankov 		mdoc->flags &= ~MDOC_SMOFF;
1886260e9a87SYuri Pankov 		return;
1887698f87a4SGarrett D'Amore 	}
1888260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "off")) {
1889260e9a87SYuri Pankov 		mdoc->flags |= MDOC_SMOFF;
1890260e9a87SYuri Pankov 		return;
1891698f87a4SGarrett D'Amore 	}
189295c635efSGarrett D'Amore 
1893*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
1894c66b8046SYuri Pankov 	    "%s %s", roff_name[mdoc->last->tok], nch->string);
1895*cec8643bSMichal Nowak 	roff_node_relink(mdoc, nch);
1896260e9a87SYuri Pankov 	return;
189795c635efSGarrett D'Amore }
189895c635efSGarrett D'Amore 
1899260e9a87SYuri Pankov static void
190095c635efSGarrett D'Amore post_root(POST_ARGS)
190195c635efSGarrett D'Amore {
1902371584c2SYuri Pankov 	struct roff_node *n;
190395c635efSGarrett D'Amore 
1904260e9a87SYuri Pankov 	/* Add missing prologue data. */
190595c635efSGarrett D'Amore 
1906260e9a87SYuri Pankov 	if (mdoc->meta.date == NULL)
1907c66b8046SYuri Pankov 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1908c66b8046SYuri Pankov 		    mandoc_normdate(mdoc, NULL, 0, 0);
190995c635efSGarrett D'Amore 
1910260e9a87SYuri Pankov 	if (mdoc->meta.title == NULL) {
1911*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
1912260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
191395c635efSGarrett D'Amore 	}
191495c635efSGarrett D'Amore 
1915260e9a87SYuri Pankov 	if (mdoc->meta.vol == NULL)
1916260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup("LOCAL");
191795c635efSGarrett D'Amore 
1918260e9a87SYuri Pankov 	if (mdoc->meta.os == NULL) {
1919*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
1920260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup("");
1921c66b8046SYuri Pankov 	} else if (mdoc->meta.os_e &&
1922c66b8046SYuri Pankov 	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1923*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
1924c66b8046SYuri Pankov 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1925c66b8046SYuri Pankov 		    "(OpenBSD)" : "(NetBSD)");
1926c66b8046SYuri Pankov 
1927c66b8046SYuri Pankov 	if (mdoc->meta.arch != NULL &&
1928*cec8643bSMichal Nowak 	    arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
1929*cec8643bSMichal Nowak 		n = mdoc->meta.first->child;
1930*cec8643bSMichal Nowak 		while (n->tok != MDOC_Dt ||
1931*cec8643bSMichal Nowak 		    n->child == NULL ||
1932*cec8643bSMichal Nowak 		    n->child->next == NULL ||
1933*cec8643bSMichal Nowak 		    n->child->next->next == NULL)
1934*cec8643bSMichal Nowak 			n = n->next;
1935*cec8643bSMichal Nowak 		n = n->child->next->next;
1936*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
1937*cec8643bSMichal Nowak 		    "Dt ... %s %s", mdoc->meta.arch,
1938*cec8643bSMichal Nowak 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1939*cec8643bSMichal Nowak 		    "(OpenBSD)" : "(NetBSD)");
194095c635efSGarrett D'Amore 	}
194195c635efSGarrett D'Amore 
1942260e9a87SYuri Pankov 	/* Check that we begin with a proper `Sh'. */
1943260e9a87SYuri Pankov 
1944*cec8643bSMichal Nowak 	n = mdoc->meta.first->child;
19456640c13bSYuri Pankov 	while (n != NULL &&
19466640c13bSYuri Pankov 	    (n->type == ROFFT_COMMENT ||
19476640c13bSYuri Pankov 	     (n->tok >= MDOC_Dd &&
1948*cec8643bSMichal Nowak 	      mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
1949260e9a87SYuri Pankov 		n = n->next;
1950260e9a87SYuri Pankov 
1951260e9a87SYuri Pankov 	if (n == NULL)
1952*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
1953260e9a87SYuri Pankov 	else if (n->tok != MDOC_Sh)
1954*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
1955*cec8643bSMichal Nowak 		    "%s", roff_name[n->tok]);
195695c635efSGarrett D'Amore }
195795c635efSGarrett D'Amore 
1958260e9a87SYuri Pankov static void
195995c635efSGarrett D'Amore post_rs(POST_ARGS)
196095c635efSGarrett D'Amore {
1961371584c2SYuri Pankov 	struct roff_node *np, *nch, *next, *prev;
196295c635efSGarrett D'Amore 	int		  i, j;
196395c635efSGarrett D'Amore 
1964260e9a87SYuri Pankov 	np = mdoc->last;
196595c635efSGarrett D'Amore 
1966371584c2SYuri Pankov 	if (np->type != ROFFT_BODY)
1967260e9a87SYuri Pankov 		return;
196895c635efSGarrett D'Amore 
1969260e9a87SYuri Pankov 	if (np->child == NULL) {
1970*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
1971260e9a87SYuri Pankov 		return;
197295c635efSGarrett D'Amore 	}
197395c635efSGarrett D'Amore 
197495c635efSGarrett D'Amore 	/*
197595c635efSGarrett D'Amore 	 * The full `Rs' block needs special handling to order the
197695c635efSGarrett D'Amore 	 * sub-elements according to `rsord'.  Pick through each element
1977260e9a87SYuri Pankov 	 * and correctly order it.  This is an insertion sort.
197895c635efSGarrett D'Amore 	 */
197995c635efSGarrett D'Amore 
198095c635efSGarrett D'Amore 	next = NULL;
1981260e9a87SYuri Pankov 	for (nch = np->child->next; nch != NULL; nch = next) {
1982260e9a87SYuri Pankov 		/* Determine order number of this child. */
198395c635efSGarrett D'Amore 		for (i = 0; i < RSORD_MAX; i++)
1984260e9a87SYuri Pankov 			if (rsord[i] == nch->tok)
198595c635efSGarrett D'Amore 				break;
198695c635efSGarrett D'Amore 
1987260e9a87SYuri Pankov 		if (i == RSORD_MAX) {
1988*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
1989*cec8643bSMichal Nowak 			    "%s", roff_name[nch->tok]);
1990260e9a87SYuri Pankov 			i = -1;
1991260e9a87SYuri Pankov 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1992260e9a87SYuri Pankov 			np->norm->Rs.quote_T++;
1993260e9a87SYuri Pankov 
1994260e9a87SYuri Pankov 		/*
1995260e9a87SYuri Pankov 		 * Remove this child from the chain.  This somewhat
1996371584c2SYuri Pankov 		 * repeats roff_node_unlink(), but since we're
199795c635efSGarrett D'Amore 		 * just re-ordering, there's no need for the
199895c635efSGarrett D'Amore 		 * full unlink process.
199995c635efSGarrett D'Amore 		 */
200095c635efSGarrett D'Amore 
2001260e9a87SYuri Pankov 		if ((next = nch->next) != NULL)
2002260e9a87SYuri Pankov 			next->prev = nch->prev;
200395c635efSGarrett D'Amore 
2004260e9a87SYuri Pankov 		if ((prev = nch->prev) != NULL)
2005260e9a87SYuri Pankov 			prev->next = nch->next;
200695c635efSGarrett D'Amore 
2007260e9a87SYuri Pankov 		nch->prev = nch->next = NULL;
2008260e9a87SYuri Pankov 
2009260e9a87SYuri Pankov 		/*
201095c635efSGarrett D'Amore 		 * Scan back until we reach a node that's
2011260e9a87SYuri Pankov 		 * to be ordered before this child.
201295c635efSGarrett D'Amore 		 */
201395c635efSGarrett D'Amore 
201495c635efSGarrett D'Amore 		for ( ; prev ; prev = prev->prev) {
201595c635efSGarrett D'Amore 			/* Determine order of `prev'. */
201695c635efSGarrett D'Amore 			for (j = 0; j < RSORD_MAX; j++)
201795c635efSGarrett D'Amore 				if (rsord[j] == prev->tok)
201895c635efSGarrett D'Amore 					break;
2019260e9a87SYuri Pankov 			if (j == RSORD_MAX)
2020260e9a87SYuri Pankov 				j = -1;
202195c635efSGarrett D'Amore 
202295c635efSGarrett D'Amore 			if (j <= i)
202395c635efSGarrett D'Amore 				break;
202495c635efSGarrett D'Amore 		}
202595c635efSGarrett D'Amore 
202695c635efSGarrett D'Amore 		/*
2027260e9a87SYuri Pankov 		 * Set this child back into its correct place
2028260e9a87SYuri Pankov 		 * in front of the `prev' node.
202995c635efSGarrett D'Amore 		 */
203095c635efSGarrett D'Amore 
2031260e9a87SYuri Pankov 		nch->prev = prev;
203295c635efSGarrett D'Amore 
2033260e9a87SYuri Pankov 		if (prev == NULL) {
2034260e9a87SYuri Pankov 			np->child->prev = nch;
2035260e9a87SYuri Pankov 			nch->next = np->child;
2036260e9a87SYuri Pankov 			np->child = nch;
203795c635efSGarrett D'Amore 		} else {
2038260e9a87SYuri Pankov 			if (prev->next)
2039260e9a87SYuri Pankov 				prev->next->prev = nch;
2040260e9a87SYuri Pankov 			nch->next = prev->next;
2041260e9a87SYuri Pankov 			prev->next = nch;
204295c635efSGarrett D'Amore 		}
204395c635efSGarrett D'Amore 	}
204495c635efSGarrett D'Amore }
204595c635efSGarrett D'Amore 
2046698f87a4SGarrett D'Amore /*
2047698f87a4SGarrett D'Amore  * For some arguments of some macros,
2048698f87a4SGarrett D'Amore  * convert all breakable hyphens into ASCII_HYPH.
2049698f87a4SGarrett D'Amore  */
2050260e9a87SYuri Pankov static void
2051698f87a4SGarrett D'Amore post_hyph(POST_ARGS)
2052698f87a4SGarrett D'Amore {
2053371584c2SYuri Pankov 	struct roff_node	*nch;
2054698f87a4SGarrett D'Amore 	char			*cp;
2055698f87a4SGarrett D'Amore 
2056260e9a87SYuri Pankov 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2057371584c2SYuri Pankov 		if (nch->type != ROFFT_TEXT)
2058698f87a4SGarrett D'Amore 			continue;
2059698f87a4SGarrett D'Amore 		cp = nch->string;
2060260e9a87SYuri Pankov 		if (*cp == '\0')
2061698f87a4SGarrett D'Amore 			continue;
2062260e9a87SYuri Pankov 		while (*(++cp) != '\0')
2063260e9a87SYuri Pankov 			if (*cp == '-' &&
2064698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[-1]) &&
2065698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[1]))
2066698f87a4SGarrett D'Amore 				*cp = ASCII_HYPH;
2067698f87a4SGarrett D'Amore 	}
2068698f87a4SGarrett D'Amore }
2069698f87a4SGarrett D'Amore 
2070260e9a87SYuri Pankov static void
207195c635efSGarrett D'Amore post_ns(POST_ARGS)
207295c635efSGarrett D'Amore {
2073c66b8046SYuri Pankov 	struct roff_node	*n;
207495c635efSGarrett D'Amore 
2075c66b8046SYuri Pankov 	n = mdoc->last;
2076c66b8046SYuri Pankov 	if (n->flags & NODE_LINE ||
2077c66b8046SYuri Pankov 	    (n->next != NULL && n->next->flags & NODE_DELIMC))
2078*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
2079c66b8046SYuri Pankov }
2080c66b8046SYuri Pankov 
2081c66b8046SYuri Pankov static void
2082c66b8046SYuri Pankov post_sx(POST_ARGS)
2083c66b8046SYuri Pankov {
2084c66b8046SYuri Pankov 	post_delim(mdoc);
2085c66b8046SYuri Pankov 	post_hyph(mdoc);
208695c635efSGarrett D'Amore }
208795c635efSGarrett D'Amore 
2088260e9a87SYuri Pankov static void
208995c635efSGarrett D'Amore post_sh(POST_ARGS)
209095c635efSGarrett D'Amore {
209195c635efSGarrett D'Amore 
2092260e9a87SYuri Pankov 	post_ignpar(mdoc);
209395c635efSGarrett D'Amore 
2094260e9a87SYuri Pankov 	switch (mdoc->last->type) {
2095371584c2SYuri Pankov 	case ROFFT_HEAD:
2096260e9a87SYuri Pankov 		post_sh_head(mdoc);
2097260e9a87SYuri Pankov 		break;
2098371584c2SYuri Pankov 	case ROFFT_BODY:
2099260e9a87SYuri Pankov 		switch (mdoc->lastsec)  {
2100260e9a87SYuri Pankov 		case SEC_NAME:
2101260e9a87SYuri Pankov 			post_sh_name(mdoc);
2102260e9a87SYuri Pankov 			break;
2103260e9a87SYuri Pankov 		case SEC_SEE_ALSO:
2104260e9a87SYuri Pankov 			post_sh_see_also(mdoc);
2105260e9a87SYuri Pankov 			break;
2106260e9a87SYuri Pankov 		case SEC_AUTHORS:
2107260e9a87SYuri Pankov 			post_sh_authors(mdoc);
2108260e9a87SYuri Pankov 			break;
2109260e9a87SYuri Pankov 		default:
2110260e9a87SYuri Pankov 			break;
2111260e9a87SYuri Pankov 		}
2112260e9a87SYuri Pankov 		break;
2113260e9a87SYuri Pankov 	default:
2114260e9a87SYuri Pankov 		break;
2115260e9a87SYuri Pankov 	}
211695c635efSGarrett D'Amore }
211795c635efSGarrett D'Amore 
2118260e9a87SYuri Pankov static void
2119260e9a87SYuri Pankov post_sh_name(POST_ARGS)
212095c635efSGarrett D'Amore {
2121371584c2SYuri Pankov 	struct roff_node *n;
2122260e9a87SYuri Pankov 	int hasnm, hasnd;
212395c635efSGarrett D'Amore 
2124260e9a87SYuri Pankov 	hasnm = hasnd = 0;
212595c635efSGarrett D'Amore 
2126260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
2127260e9a87SYuri Pankov 		switch (n->tok) {
2128260e9a87SYuri Pankov 		case MDOC_Nm:
2129a40ea1a7SYuri Pankov 			if (hasnm && n->child != NULL)
2130*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
2131*cec8643bSMichal Nowak 				    n->line, n->pos,
2132a40ea1a7SYuri Pankov 				    "Nm %s", n->child->string);
2133260e9a87SYuri Pankov 			hasnm = 1;
2134a40ea1a7SYuri Pankov 			continue;
2135260e9a87SYuri Pankov 		case MDOC_Nd:
2136260e9a87SYuri Pankov 			hasnd = 1;
2137260e9a87SYuri Pankov 			if (n->next != NULL)
2138260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_NAMESEC_ND,
2139*cec8643bSMichal Nowak 				    n->line, n->pos, NULL);
2140260e9a87SYuri Pankov 			break;
2141371584c2SYuri Pankov 		case TOKEN_NONE:
2142a40ea1a7SYuri Pankov 			if (n->type == ROFFT_TEXT &&
2143a40ea1a7SYuri Pankov 			    n->string[0] == ',' && n->string[1] == '\0' &&
2144a40ea1a7SYuri Pankov 			    n->next != NULL && n->next->tok == MDOC_Nm) {
2145a40ea1a7SYuri Pankov 				n = n->next;
2146a40ea1a7SYuri Pankov 				continue;
2147a40ea1a7SYuri Pankov 			}
2148260e9a87SYuri Pankov 			/* FALLTHROUGH */
2149260e9a87SYuri Pankov 		default:
2150*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_NAMESEC_BAD,
2151*cec8643bSMichal Nowak 			    n->line, n->pos, "%s", roff_name[n->tok]);
2152a40ea1a7SYuri Pankov 			continue;
2153260e9a87SYuri Pankov 		}
2154a40ea1a7SYuri Pankov 		break;
215595c635efSGarrett D'Amore 	}
215695c635efSGarrett D'Amore 
2157260e9a87SYuri Pankov 	if ( ! hasnm)
2158*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NAMESEC_NONM,
2159260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
2160260e9a87SYuri Pankov 	if ( ! hasnd)
2161*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NAMESEC_NOND,
2162260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
2163260e9a87SYuri Pankov }
2164260e9a87SYuri Pankov 
2165260e9a87SYuri Pankov static void
2166260e9a87SYuri Pankov post_sh_see_also(POST_ARGS)
2167260e9a87SYuri Pankov {
2168371584c2SYuri Pankov 	const struct roff_node	*n;
2169260e9a87SYuri Pankov 	const char		*name, *sec;
2170260e9a87SYuri Pankov 	const char		*lastname, *lastsec, *lastpunct;
2171260e9a87SYuri Pankov 	int			 cmp;
2172260e9a87SYuri Pankov 
2173260e9a87SYuri Pankov 	n = mdoc->last->child;
2174260e9a87SYuri Pankov 	lastname = lastsec = lastpunct = NULL;
2175260e9a87SYuri Pankov 	while (n != NULL) {
2176371584c2SYuri Pankov 		if (n->tok != MDOC_Xr ||
2177371584c2SYuri Pankov 		    n->child == NULL ||
2178371584c2SYuri Pankov 		    n->child->next == NULL)
2179260e9a87SYuri Pankov 			break;
2180260e9a87SYuri Pankov 
2181260e9a87SYuri Pankov 		/* Process one .Xr node. */
2182260e9a87SYuri Pankov 
2183260e9a87SYuri Pankov 		name = n->child->string;
2184260e9a87SYuri Pankov 		sec = n->child->next->string;
2185260e9a87SYuri Pankov 		if (lastsec != NULL) {
2186260e9a87SYuri Pankov 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2187*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2188*cec8643bSMichal Nowak 				    n->pos, "%s before %s(%s)",
2189*cec8643bSMichal Nowak 				    lastpunct, name, sec);
2190260e9a87SYuri Pankov 			cmp = strcmp(lastsec, sec);
2191260e9a87SYuri Pankov 			if (cmp > 0)
2192*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2193*cec8643bSMichal Nowak 				    n->pos, "%s(%s) after %s(%s)",
2194*cec8643bSMichal Nowak 				    name, sec, lastname, lastsec);
2195260e9a87SYuri Pankov 			else if (cmp == 0 &&
2196260e9a87SYuri Pankov 			    strcasecmp(lastname, name) > 0)
2197*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2198*cec8643bSMichal Nowak 				    n->pos, "%s after %s", name, lastname);
2199260e9a87SYuri Pankov 		}
2200260e9a87SYuri Pankov 		lastname = name;
2201260e9a87SYuri Pankov 		lastsec = sec;
2202260e9a87SYuri Pankov 
2203260e9a87SYuri Pankov 		/* Process the following node. */
2204260e9a87SYuri Pankov 
2205260e9a87SYuri Pankov 		n = n->next;
2206260e9a87SYuri Pankov 		if (n == NULL)
2207260e9a87SYuri Pankov 			break;
2208260e9a87SYuri Pankov 		if (n->tok == MDOC_Xr) {
2209260e9a87SYuri Pankov 			lastpunct = "none";
221095c635efSGarrett D'Amore 			continue;
2211260e9a87SYuri Pankov 		}
2212371584c2SYuri Pankov 		if (n->type != ROFFT_TEXT)
2213260e9a87SYuri Pankov 			break;
2214260e9a87SYuri Pankov 		for (name = n->string; *name != '\0'; name++)
2215260e9a87SYuri Pankov 			if (isalpha((const unsigned char)*name))
2216260e9a87SYuri Pankov 				return;
2217260e9a87SYuri Pankov 		lastpunct = n->string;
2218c66b8046SYuri Pankov 		if (n->next == NULL || n->next->tok == MDOC_Rs)
2219*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2220*cec8643bSMichal Nowak 			    n->pos, "%s after %s(%s)",
2221260e9a87SYuri Pankov 			    lastpunct, lastname, lastsec);
2222260e9a87SYuri Pankov 		n = n->next;
222395c635efSGarrett D'Amore 	}
2224260e9a87SYuri Pankov }
222595c635efSGarrett D'Amore 
2226260e9a87SYuri Pankov static int
2227371584c2SYuri Pankov child_an(const struct roff_node *n)
2228260e9a87SYuri Pankov {
222995c635efSGarrett D'Amore 
2230260e9a87SYuri Pankov 	for (n = n->child; n != NULL; n = n->next)
2231371584c2SYuri Pankov 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2232371584c2SYuri Pankov 			return 1;
2233371584c2SYuri Pankov 	return 0;
223495c635efSGarrett D'Amore }
223595c635efSGarrett D'Amore 
2236260e9a87SYuri Pankov static void
2237260e9a87SYuri Pankov post_sh_authors(POST_ARGS)
2238260e9a87SYuri Pankov {
2239260e9a87SYuri Pankov 
2240260e9a87SYuri Pankov 	if ( ! child_an(mdoc->last))
2241*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_AN_MISSING,
2242260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
2243260e9a87SYuri Pankov }
2244260e9a87SYuri Pankov 
2245c66b8046SYuri Pankov /*
2246c66b8046SYuri Pankov  * Return an upper bound for the string distance (allowing
2247c66b8046SYuri Pankov  * transpositions).  Not a full Levenshtein implementation
2248c66b8046SYuri Pankov  * because Levenshtein is quadratic in the string length
2249c66b8046SYuri Pankov  * and this function is called for every standard name,
2250c66b8046SYuri Pankov  * so the check for each custom name would be cubic.
2251c66b8046SYuri Pankov  * The following crude heuristics is linear, resulting
2252c66b8046SYuri Pankov  * in quadratic behaviour for checking one custom name,
2253c66b8046SYuri Pankov  * which does not cause measurable slowdown.
2254c66b8046SYuri Pankov  */
2255c66b8046SYuri Pankov static int
2256c66b8046SYuri Pankov similar(const char *s1, const char *s2)
2257c66b8046SYuri Pankov {
2258c66b8046SYuri Pankov 	const int	maxdist = 3;
2259c66b8046SYuri Pankov 	int		dist = 0;
2260c66b8046SYuri Pankov 
2261c66b8046SYuri Pankov 	while (s1[0] != '\0' && s2[0] != '\0') {
2262c66b8046SYuri Pankov 		if (s1[0] == s2[0]) {
2263c66b8046SYuri Pankov 			s1++;
2264c66b8046SYuri Pankov 			s2++;
2265c66b8046SYuri Pankov 			continue;
2266c66b8046SYuri Pankov 		}
2267c66b8046SYuri Pankov 		if (++dist > maxdist)
2268c66b8046SYuri Pankov 			return INT_MAX;
2269c66b8046SYuri Pankov 		if (s1[1] == s2[1]) {  /* replacement */
2270c66b8046SYuri Pankov 			s1++;
2271c66b8046SYuri Pankov 			s2++;
2272c66b8046SYuri Pankov 		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2273c66b8046SYuri Pankov 			s1 += 2;	/* transposition */
2274c66b8046SYuri Pankov 			s2 += 2;
2275c66b8046SYuri Pankov 		} else if (s1[0] == s2[1])  /* insertion */
2276c66b8046SYuri Pankov 			s2++;
2277c66b8046SYuri Pankov 		else if (s1[1] == s2[0])  /* deletion */
2278c66b8046SYuri Pankov 			s1++;
2279c66b8046SYuri Pankov 		else
2280c66b8046SYuri Pankov 			return INT_MAX;
2281c66b8046SYuri Pankov 	}
2282c66b8046SYuri Pankov 	dist += strlen(s1) + strlen(s2);
2283c66b8046SYuri Pankov 	return dist > maxdist ? INT_MAX : dist;
2284c66b8046SYuri Pankov }
2285c66b8046SYuri Pankov 
2286260e9a87SYuri Pankov static void
228795c635efSGarrett D'Amore post_sh_head(POST_ARGS)
228895c635efSGarrett D'Amore {
2289a40ea1a7SYuri Pankov 	struct roff_node	*nch;
2290a40ea1a7SYuri Pankov 	const char		*goodsec;
2291c66b8046SYuri Pankov 	const char *const	*testsec;
2292c66b8046SYuri Pankov 	int			 dist, mindist;
2293a40ea1a7SYuri Pankov 	enum roff_sec		 sec;
229495c635efSGarrett D'Amore 
229595c635efSGarrett D'Amore 	/*
229695c635efSGarrett D'Amore 	 * Process a new section.  Sections are either "named" or
229795c635efSGarrett D'Amore 	 * "custom".  Custom sections are user-defined, while named ones
229895c635efSGarrett D'Amore 	 * follow a conventional order and may only appear in certain
229995c635efSGarrett D'Amore 	 * manual sections.
230095c635efSGarrett D'Amore 	 */
230195c635efSGarrett D'Amore 
2302371584c2SYuri Pankov 	sec = mdoc->last->sec;
230395c635efSGarrett D'Amore 
230495c635efSGarrett D'Amore 	/* The NAME should be first. */
230595c635efSGarrett D'Amore 
2306a40ea1a7SYuri Pankov 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2307*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_NAMESEC_FIRST,
2308a40ea1a7SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
2309a40ea1a7SYuri Pankov 		    sec != SEC_CUSTOM ? secnames[sec] :
2310a40ea1a7SYuri Pankov 		    (nch = mdoc->last->child) == NULL ? "" :
2311a40ea1a7SYuri Pankov 		    nch->type == ROFFT_TEXT ? nch->string :
2312c66b8046SYuri Pankov 		    roff_name[nch->tok]);
231395c635efSGarrett D'Amore 
231495c635efSGarrett D'Amore 	/* The SYNOPSIS gets special attention in other areas. */
231595c635efSGarrett D'Amore 
2316371584c2SYuri Pankov 	if (sec == SEC_SYNOPSIS) {
2317698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 1, '=');
231895c635efSGarrett D'Amore 		mdoc->flags |= MDOC_SYNOPSIS;
2319698f87a4SGarrett D'Amore 	} else {
2320698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 0, '=');
232195c635efSGarrett D'Amore 		mdoc->flags &= ~MDOC_SYNOPSIS;
2322698f87a4SGarrett D'Amore 	}
232395c635efSGarrett D'Amore 
232495c635efSGarrett D'Amore 	/* Mark our last section. */
232595c635efSGarrett D'Amore 
232695c635efSGarrett D'Amore 	mdoc->lastsec = sec;
232795c635efSGarrett D'Amore 
232895c635efSGarrett D'Amore 	/* We don't care about custom sections after this. */
232995c635efSGarrett D'Amore 
2330c66b8046SYuri Pankov 	if (sec == SEC_CUSTOM) {
2331c66b8046SYuri Pankov 		if ((nch = mdoc->last->child) == NULL ||
2332c66b8046SYuri Pankov 		    nch->type != ROFFT_TEXT || nch->next != NULL)
2333c66b8046SYuri Pankov 			return;
2334c66b8046SYuri Pankov 		goodsec = NULL;
2335c66b8046SYuri Pankov 		mindist = INT_MAX;
2336c66b8046SYuri Pankov 		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
2337c66b8046SYuri Pankov 			dist = similar(nch->string, *testsec);
2338c66b8046SYuri Pankov 			if (dist < mindist) {
2339c66b8046SYuri Pankov 				goodsec = *testsec;
2340c66b8046SYuri Pankov 				mindist = dist;
2341c66b8046SYuri Pankov 			}
2342c66b8046SYuri Pankov 		}
2343c66b8046SYuri Pankov 		if (goodsec != NULL)
2344*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
2345*cec8643bSMichal Nowak 			    "Sh %s instead of %s", nch->string, goodsec);
2346260e9a87SYuri Pankov 		return;
2347c66b8046SYuri Pankov 	}
234895c635efSGarrett D'Amore 
234995c635efSGarrett D'Amore 	/*
235095c635efSGarrett D'Amore 	 * Check whether our non-custom section is being repeated or is
235195c635efSGarrett D'Amore 	 * out of order.
235295c635efSGarrett D'Amore 	 */
235395c635efSGarrett D'Amore 
235495c635efSGarrett D'Amore 	if (sec == mdoc->lastnamed)
2355*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
2356*cec8643bSMichal Nowak 		    mdoc->last->pos, "Sh %s", secnames[sec]);
235795c635efSGarrett D'Amore 
235895c635efSGarrett D'Amore 	if (sec < mdoc->lastnamed)
2359*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
2360*cec8643bSMichal Nowak 		    mdoc->last->pos, "Sh %s", secnames[sec]);
236195c635efSGarrett D'Amore 
236295c635efSGarrett D'Amore 	/* Mark the last named section. */
236395c635efSGarrett D'Amore 
236495c635efSGarrett D'Amore 	mdoc->lastnamed = sec;
236595c635efSGarrett D'Amore 
236695c635efSGarrett D'Amore 	/* Check particular section/manual conventions. */
236795c635efSGarrett D'Amore 
2368371584c2SYuri Pankov 	if (mdoc->meta.msec == NULL)
2369260e9a87SYuri Pankov 		return;
237095c635efSGarrett D'Amore 
2371260e9a87SYuri Pankov 	goodsec = NULL;
237295c635efSGarrett D'Amore 	switch (sec) {
2373260e9a87SYuri Pankov 	case SEC_ERRORS:
2374260e9a87SYuri Pankov 		if (*mdoc->meta.msec == '4')
2375260e9a87SYuri Pankov 			break;
2376f6b3f249SJason King 		if (*mdoc->meta.msec == '7' && *(mdoc->meta.msec + 1) == 'I')
2377f6b3f249SJason King 			break;
2378f6b3f249SJason King 		goodsec = "2, 3, 4, 7I, 9";
237995c635efSGarrett D'Amore 		/* FALLTHROUGH */
2380260e9a87SYuri Pankov 	case SEC_RETURN_VALUES:
2381260e9a87SYuri Pankov 	case SEC_LIBRARY:
238295c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '2')
238395c635efSGarrett D'Amore 			break;
238495c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '3')
238595c635efSGarrett D'Amore 			break;
2386260e9a87SYuri Pankov 		if (NULL == goodsec)
2387260e9a87SYuri Pankov 			goodsec = "2, 3, 9";
2388260e9a87SYuri Pankov 		/* FALLTHROUGH */
2389260e9a87SYuri Pankov 	case SEC_CONTEXT:
239095c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '9')
239195c635efSGarrett D'Amore 			break;
2392260e9a87SYuri Pankov 		if (NULL == goodsec)
2393260e9a87SYuri Pankov 			goodsec = "9";
2394*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_SEC_MSEC,
2395260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
2396371584c2SYuri Pankov 		    "Sh %s for %s only", secnames[sec], goodsec);
239795c635efSGarrett D'Amore 		break;
239895c635efSGarrett D'Amore 	default:
239995c635efSGarrett D'Amore 		break;
240095c635efSGarrett D'Amore 	}
240195c635efSGarrett D'Amore }
240295c635efSGarrett D'Amore 
2403a40ea1a7SYuri Pankov static void
2404a40ea1a7SYuri Pankov post_xr(POST_ARGS)
2405a40ea1a7SYuri Pankov {
2406a40ea1a7SYuri Pankov 	struct roff_node *n, *nch;
2407a40ea1a7SYuri Pankov 
2408a40ea1a7SYuri Pankov 	n = mdoc->last;
2409a40ea1a7SYuri Pankov 	nch = n->child;
2410a40ea1a7SYuri Pankov 	if (nch->next == NULL) {
2411*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_XR_NOSEC,
2412a40ea1a7SYuri Pankov 		    n->line, n->pos, "Xr %s", nch->string);
2413c66b8046SYuri Pankov 	} else {
2414c66b8046SYuri Pankov 		assert(nch->next == n->last);
2415c66b8046SYuri Pankov 		if(mandoc_xr_add(nch->next->string, nch->string,
2416c66b8046SYuri Pankov 		    nch->line, nch->pos))
2417*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_XR_SELF,
2418c66b8046SYuri Pankov 			    nch->line, nch->pos, "Xr %s %s",
2419c66b8046SYuri Pankov 			    nch->string, nch->next->string);
2420a40ea1a7SYuri Pankov 	}
2421c66b8046SYuri Pankov 	post_delim_nb(mdoc);
2422a40ea1a7SYuri Pankov }
2423a40ea1a7SYuri Pankov 
2424260e9a87SYuri Pankov static void
242595c635efSGarrett D'Amore post_ignpar(POST_ARGS)
242695c635efSGarrett D'Amore {
2427371584c2SYuri Pankov 	struct roff_node *np;
242895c635efSGarrett D'Amore 
2429260e9a87SYuri Pankov 	switch (mdoc->last->type) {
2430c66b8046SYuri Pankov 	case ROFFT_BLOCK:
2431c66b8046SYuri Pankov 		post_prevpar(mdoc);
2432c66b8046SYuri Pankov 		return;
2433371584c2SYuri Pankov 	case ROFFT_HEAD:
2434c66b8046SYuri Pankov 		post_delim(mdoc);
2435260e9a87SYuri Pankov 		post_hyph(mdoc);
2436260e9a87SYuri Pankov 		return;
2437371584c2SYuri Pankov 	case ROFFT_BODY:
2438260e9a87SYuri Pankov 		break;
2439260e9a87SYuri Pankov 	default:
2440260e9a87SYuri Pankov 		return;
2441260e9a87SYuri Pankov 	}
244295c635efSGarrett D'Amore 
2443371584c2SYuri Pankov 	if ((np = mdoc->last->child) != NULL)
2444*cec8643bSMichal Nowak 		if (np->tok == MDOC_Pp ||
2445*cec8643bSMichal Nowak 		    np->tok == ROFF_br || np->tok == ROFF_sp) {
2446*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
2447c66b8046SYuri Pankov 			    "%s after %s", roff_name[np->tok],
2448c66b8046SYuri Pankov 			    roff_name[mdoc->last->tok]);
2449371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
245095c635efSGarrett D'Amore 		}
245195c635efSGarrett D'Amore 
2452371584c2SYuri Pankov 	if ((np = mdoc->last->last) != NULL)
2453*cec8643bSMichal Nowak 		if (np->tok == MDOC_Pp || np->tok == ROFF_br) {
2454*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
2455*cec8643bSMichal Nowak 			    "%s at the end of %s", roff_name[np->tok],
2456c66b8046SYuri Pankov 			    roff_name[mdoc->last->tok]);
2457371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
245895c635efSGarrett D'Amore 		}
245995c635efSGarrett D'Amore }
246095c635efSGarrett D'Amore 
2461260e9a87SYuri Pankov static void
2462371584c2SYuri Pankov post_prevpar(POST_ARGS)
246395c635efSGarrett D'Amore {
2464371584c2SYuri Pankov 	struct roff_node *n;
246595c635efSGarrett D'Amore 
2466371584c2SYuri Pankov 	n = mdoc->last;
2467371584c2SYuri Pankov 	if (NULL == n->prev)
2468260e9a87SYuri Pankov 		return;
2469371584c2SYuri Pankov 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2470260e9a87SYuri Pankov 		return;
247195c635efSGarrett D'Amore 
2472260e9a87SYuri Pankov 	/*
2473*cec8643bSMichal Nowak 	 * Don't allow `Pp' prior to a paragraph-type
2474*cec8643bSMichal Nowak 	 * block: `Pp' or non-compact `Bd' or `Bl'.
247595c635efSGarrett D'Amore 	 */
247695c635efSGarrett D'Amore 
2477*cec8643bSMichal Nowak 	if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br)
2478260e9a87SYuri Pankov 		return;
2479371584c2SYuri Pankov 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2480260e9a87SYuri Pankov 		return;
2481371584c2SYuri Pankov 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2482260e9a87SYuri Pankov 		return;
2483371584c2SYuri Pankov 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2484260e9a87SYuri Pankov 		return;
248595c635efSGarrett D'Amore 
2486*cec8643bSMichal Nowak 	mandoc_msg(MANDOCERR_PAR_SKIP, n->prev->line, n->prev->pos,
2487*cec8643bSMichal Nowak 	    "%s before %s", roff_name[n->prev->tok], roff_name[n->tok]);
2488371584c2SYuri Pankov 	roff_node_delete(mdoc, n->prev);
248995c635efSGarrett D'Amore }
249095c635efSGarrett D'Amore 
2491260e9a87SYuri Pankov static void
2492698f87a4SGarrett D'Amore post_par(POST_ARGS)
2493698f87a4SGarrett D'Amore {
2494371584c2SYuri Pankov 	struct roff_node *np;
2495698f87a4SGarrett D'Amore 
2496*cec8643bSMichal Nowak 	post_prevpar(mdoc);
2497698f87a4SGarrett D'Amore 
2498*cec8643bSMichal Nowak 	np = mdoc->last;
2499*cec8643bSMichal Nowak 	if (np->child != NULL)
2500*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
2501*cec8643bSMichal Nowak 		    "%s %s", roff_name[np->tok], np->child->string);
250295c635efSGarrett D'Amore }
250395c635efSGarrett D'Amore 
2504260e9a87SYuri Pankov static void
250595c635efSGarrett D'Amore post_dd(POST_ARGS)
250695c635efSGarrett D'Amore {
2507371584c2SYuri Pankov 	struct roff_node *n;
2508260e9a87SYuri Pankov 	char		 *datestr;
250995c635efSGarrett D'Amore 
2510371584c2SYuri Pankov 	n = mdoc->last;
2511a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2512a40ea1a7SYuri Pankov 
2513371584c2SYuri Pankov 	if (mdoc->meta.date != NULL) {
2514*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
251595c635efSGarrett D'Amore 		free(mdoc->meta.date);
2516371584c2SYuri Pankov 	} else if (mdoc->flags & MDOC_PBODY)
2517*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
2518371584c2SYuri Pankov 	else if (mdoc->meta.title != NULL)
2519*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
2520371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Dt");
2521371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
2522*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
2523371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Os");
252495c635efSGarrett D'Amore 
2525371584c2SYuri Pankov 	if (n->child == NULL || n->child->string[0] == '\0') {
2526260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2527c66b8046SYuri Pankov 		    mandoc_normdate(mdoc, NULL, n->line, n->pos);
2528a40ea1a7SYuri Pankov 		return;
252995c635efSGarrett D'Amore 	}
253095c635efSGarrett D'Amore 
2531260e9a87SYuri Pankov 	datestr = NULL;
2532371584c2SYuri Pankov 	deroff(&datestr, n);
2533260e9a87SYuri Pankov 	if (mdoc->quick)
2534260e9a87SYuri Pankov 		mdoc->meta.date = datestr;
2535260e9a87SYuri Pankov 	else {
2536c66b8046SYuri Pankov 		mdoc->meta.date = mandoc_normdate(mdoc,
2537260e9a87SYuri Pankov 		    datestr, n->line, n->pos);
2538260e9a87SYuri Pankov 		free(datestr);
253995c635efSGarrett D'Amore 	}
254095c635efSGarrett D'Amore }
254195c635efSGarrett D'Amore 
2542260e9a87SYuri Pankov static void
254395c635efSGarrett D'Amore post_dt(POST_ARGS)
254495c635efSGarrett D'Amore {
2545371584c2SYuri Pankov 	struct roff_node *nn, *n;
254695c635efSGarrett D'Amore 	const char	 *cp;
254795c635efSGarrett D'Amore 	char		 *p;
254895c635efSGarrett D'Amore 
254995c635efSGarrett D'Amore 	n = mdoc->last;
2550a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2551a40ea1a7SYuri Pankov 
2552371584c2SYuri Pankov 	if (mdoc->flags & MDOC_PBODY) {
2553*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
2554a40ea1a7SYuri Pankov 		return;
2555371584c2SYuri Pankov 	}
2556371584c2SYuri Pankov 
2557371584c2SYuri Pankov 	if (mdoc->meta.title != NULL)
2558*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
2559371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
2560*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
2561371584c2SYuri Pankov 		    n->line, n->pos, "Dt after Os");
256295c635efSGarrett D'Amore 
2563260e9a87SYuri Pankov 	free(mdoc->meta.title);
2564260e9a87SYuri Pankov 	free(mdoc->meta.msec);
2565260e9a87SYuri Pankov 	free(mdoc->meta.vol);
2566260e9a87SYuri Pankov 	free(mdoc->meta.arch);
256795c635efSGarrett D'Amore 
2568260e9a87SYuri Pankov 	mdoc->meta.title = NULL;
2569260e9a87SYuri Pankov 	mdoc->meta.msec = NULL;
2570260e9a87SYuri Pankov 	mdoc->meta.vol = NULL;
2571260e9a87SYuri Pankov 	mdoc->meta.arch = NULL;
257295c635efSGarrett D'Amore 
2573260e9a87SYuri Pankov 	/* Mandatory first argument: title. */
257495c635efSGarrett D'Amore 
2575260e9a87SYuri Pankov 	nn = n->child;
2576260e9a87SYuri Pankov 	if (nn == NULL || *nn->string == '\0') {
2577*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
2578260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2579260e9a87SYuri Pankov 	} else {
2580260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup(nn->string);
258195c635efSGarrett D'Amore 
2582260e9a87SYuri Pankov 		/* Check that all characters are uppercase. */
258395c635efSGarrett D'Amore 
2584260e9a87SYuri Pankov 		for (p = nn->string; *p != '\0'; p++)
2585260e9a87SYuri Pankov 			if (islower((unsigned char)*p)) {
2586*cec8643bSMichal Nowak 				mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
2587*cec8643bSMichal Nowak 				    nn->pos + (int)(p - nn->string),
2588260e9a87SYuri Pankov 				    "Dt %s", nn->string);
2589260e9a87SYuri Pankov 				break;
2590260e9a87SYuri Pankov 			}
259195c635efSGarrett D'Amore 	}
259295c635efSGarrett D'Amore 
2593a40ea1a7SYuri Pankov 	/* Mandatory second argument: section. */
259495c635efSGarrett D'Amore 
2595260e9a87SYuri Pankov 	if (nn != NULL)
2596260e9a87SYuri Pankov 		nn = nn->next;
259795c635efSGarrett D'Amore 
2598260e9a87SYuri Pankov 	if (nn == NULL) {
2599*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
2600260e9a87SYuri Pankov 		    "Dt %s", mdoc->meta.title);
260195c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2602a40ea1a7SYuri Pankov 		return;  /* msec and arch remain NULL. */
260395c635efSGarrett D'Amore 	}
260495c635efSGarrett D'Amore 
2605260e9a87SYuri Pankov 	mdoc->meta.msec = mandoc_strdup(nn->string);
2606260e9a87SYuri Pankov 
2607260e9a87SYuri Pankov 	/* Infer volume title from section number. */
260895c635efSGarrett D'Amore 
260995c635efSGarrett D'Amore 	cp = mandoc_a2msec(nn->string);
2610260e9a87SYuri Pankov 	if (cp == NULL) {
2611*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_MSEC_BAD,
2612260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
261395c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup(nn->string);
2614260e9a87SYuri Pankov 	} else
2615260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup(cp);
261695c635efSGarrett D'Amore 
2617260e9a87SYuri Pankov 	/* Optional third argument: architecture. */
261895c635efSGarrett D'Amore 
2619260e9a87SYuri Pankov 	if ((nn = nn->next) == NULL)
2620a40ea1a7SYuri Pankov 		return;
262195c635efSGarrett D'Amore 
2622260e9a87SYuri Pankov 	for (p = nn->string; *p != '\0'; p++)
2623260e9a87SYuri Pankov 		*p = tolower((unsigned char)*p);
2624260e9a87SYuri Pankov 	mdoc->meta.arch = mandoc_strdup(nn->string);
262595c635efSGarrett D'Amore 
2626260e9a87SYuri Pankov 	/* Ignore fourth and later arguments. */
262795c635efSGarrett D'Amore 
2628260e9a87SYuri Pankov 	if ((nn = nn->next) != NULL)
2629*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_ARG_EXCESS,
2630260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
263195c635efSGarrett D'Amore }
263295c635efSGarrett D'Amore 
2633260e9a87SYuri Pankov static void
263495c635efSGarrett D'Amore post_bx(POST_ARGS)
263595c635efSGarrett D'Amore {
2636a40ea1a7SYuri Pankov 	struct roff_node	*n, *nch;
2637c66b8046SYuri Pankov 	const char		*macro;
2638c66b8046SYuri Pankov 
2639c66b8046SYuri Pankov 	post_delim_nb(mdoc);
2640a40ea1a7SYuri Pankov 
2641a40ea1a7SYuri Pankov 	n = mdoc->last;
2642a40ea1a7SYuri Pankov 	nch = n->child;
2643a40ea1a7SYuri Pankov 
2644a40ea1a7SYuri Pankov 	if (nch != NULL) {
2645c66b8046SYuri Pankov 		macro = !strcmp(nch->string, "Open") ? "Ox" :
2646c66b8046SYuri Pankov 		    !strcmp(nch->string, "Net") ? "Nx" :
2647c66b8046SYuri Pankov 		    !strcmp(nch->string, "Free") ? "Fx" :
2648c66b8046SYuri Pankov 		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2649c66b8046SYuri Pankov 		if (macro != NULL)
2650*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_BX,
2651*cec8643bSMichal Nowak 			    n->line, n->pos, "%s", macro);
2652a40ea1a7SYuri Pankov 		mdoc->last = nch;
2653a40ea1a7SYuri Pankov 		nch = nch->next;
2654a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
2655a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2656a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
2657a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
2658a40ea1a7SYuri Pankov 	} else
2659a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
2660a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2661a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2662a40ea1a7SYuri Pankov 
2663a40ea1a7SYuri Pankov 	if (nch == NULL) {
2664a40ea1a7SYuri Pankov 		mdoc->last = n;
2665a40ea1a7SYuri Pankov 		return;
2666a40ea1a7SYuri Pankov 	}
2667a40ea1a7SYuri Pankov 
2668a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2669a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2670a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
2671a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "-");
2672a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2673a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2674a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2675a40ea1a7SYuri Pankov 	mdoc->last = n;
267695c635efSGarrett D'Amore 
2677260e9a87SYuri Pankov 	/*
267895c635efSGarrett D'Amore 	 * Make `Bx's second argument always start with an uppercase
267995c635efSGarrett D'Amore 	 * letter.  Groff checks if it's an "accepted" term, but we just
268095c635efSGarrett D'Amore 	 * uppercase blindly.
268195c635efSGarrett D'Amore 	 */
268295c635efSGarrett D'Amore 
2683a40ea1a7SYuri Pankov 	*nch->string = (char)toupper((unsigned char)*nch->string);
268495c635efSGarrett D'Amore }
268595c635efSGarrett D'Amore 
2686260e9a87SYuri Pankov static void
268795c635efSGarrett D'Amore post_os(POST_ARGS)
268895c635efSGarrett D'Amore {
268995c635efSGarrett D'Amore #ifndef OSNAME
269095c635efSGarrett D'Amore 	struct utsname	  utsname;
2691260e9a87SYuri Pankov 	static char	 *defbuf;
269295c635efSGarrett D'Amore #endif
2693371584c2SYuri Pankov 	struct roff_node *n;
269495c635efSGarrett D'Amore 
269595c635efSGarrett D'Amore 	n = mdoc->last;
2696a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2697a40ea1a7SYuri Pankov 
2698371584c2SYuri Pankov 	if (mdoc->meta.os != NULL)
2699*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
2700371584c2SYuri Pankov 	else if (mdoc->flags & MDOC_PBODY)
2701*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
270295c635efSGarrett D'Amore 
2703c66b8046SYuri Pankov 	post_delim(mdoc);
2704c66b8046SYuri Pankov 
270595c635efSGarrett D'Amore 	/*
2706698f87a4SGarrett D'Amore 	 * Set the operating system by way of the `Os' macro.
2707698f87a4SGarrett D'Amore 	 * The order of precedence is:
2708698f87a4SGarrett D'Amore 	 * 1. the argument of the `Os' macro, unless empty
2709698f87a4SGarrett D'Amore 	 * 2. the -Ios=foo command line argument, if provided
2710698f87a4SGarrett D'Amore 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2711698f87a4SGarrett D'Amore 	 * 4. "sysname release" from uname(3)
2712260e9a87SYuri Pankov 	 */
271395c635efSGarrett D'Amore 
2714698f87a4SGarrett D'Amore 	free(mdoc->meta.os);
2715260e9a87SYuri Pankov 	mdoc->meta.os = NULL;
2716371584c2SYuri Pankov 	deroff(&mdoc->meta.os, n);
2717260e9a87SYuri Pankov 	if (mdoc->meta.os)
2718c66b8046SYuri Pankov 		goto out;
271995c635efSGarrett D'Amore 
2720c66b8046SYuri Pankov 	if (mdoc->os_s != NULL) {
2721c66b8046SYuri Pankov 		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2722c66b8046SYuri Pankov 		goto out;
272395c635efSGarrett D'Amore 	}
272495c635efSGarrett D'Amore 
272595c635efSGarrett D'Amore #ifdef OSNAME
2726260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(OSNAME);
272795c635efSGarrett D'Amore #else /*!OSNAME */
2728371584c2SYuri Pankov 	if (defbuf == NULL) {
2729371584c2SYuri Pankov 		if (uname(&utsname) == -1) {
2730*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
2731260e9a87SYuri Pankov 			defbuf = mandoc_strdup("UNKNOWN");
2732260e9a87SYuri Pankov 		} else
2733260e9a87SYuri Pankov 			mandoc_asprintf(&defbuf, "%s %s",
2734260e9a87SYuri Pankov 			    utsname.sysname, utsname.release);
273595c635efSGarrett D'Amore 	}
2736260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(defbuf);
2737260e9a87SYuri Pankov #endif /*!OSNAME*/
2738c66b8046SYuri Pankov 
2739c66b8046SYuri Pankov out:
2740c66b8046SYuri Pankov 	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2741c66b8046SYuri Pankov 		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2742c66b8046SYuri Pankov 			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2743c66b8046SYuri Pankov 		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2744c66b8046SYuri Pankov 			mdoc->meta.os_e = MANDOC_OS_NETBSD;
2745c66b8046SYuri Pankov 	}
2746c66b8046SYuri Pankov 
2747c66b8046SYuri Pankov 	/*
2748c66b8046SYuri Pankov 	 * This is the earliest point where we can check
2749c66b8046SYuri Pankov 	 * Mdocdate conventions because we don't know
2750c66b8046SYuri Pankov 	 * the operating system earlier.
2751c66b8046SYuri Pankov 	 */
2752c66b8046SYuri Pankov 
2753c66b8046SYuri Pankov 	if (n->child != NULL)
2754*cec8643bSMichal Nowak 		mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
2755c66b8046SYuri Pankov 		    "Os %s (%s)", n->child->string,
2756c66b8046SYuri Pankov 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2757c66b8046SYuri Pankov 		    "OpenBSD" : "NetBSD");
2758c66b8046SYuri Pankov 
2759c66b8046SYuri Pankov 	while (n->tok != MDOC_Dd)
2760c66b8046SYuri Pankov 		if ((n = n->prev) == NULL)
2761c66b8046SYuri Pankov 			return;
2762c66b8046SYuri Pankov 	if ((n = n->child) == NULL)
2763c66b8046SYuri Pankov 		return;
2764c66b8046SYuri Pankov 	if (strncmp(n->string, "$" "Mdocdate", 9)) {
2765c66b8046SYuri Pankov 		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2766*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
2767*cec8643bSMichal Nowak 			    n->pos, "Dd %s (OpenBSD)", n->string);
2768c66b8046SYuri Pankov 	} else {
2769c66b8046SYuri Pankov 		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2770*cec8643bSMichal Nowak 			mandoc_msg(MANDOCERR_MDOCDATE, n->line,
2771*cec8643bSMichal Nowak 			    n->pos, "Dd %s (NetBSD)", n->string);
2772c66b8046SYuri Pankov 	}
277395c635efSGarrett D'Amore }
277495c635efSGarrett D'Amore 
2775371584c2SYuri Pankov enum roff_sec
2776371584c2SYuri Pankov mdoc_a2sec(const char *p)
277795c635efSGarrett D'Amore {
277895c635efSGarrett D'Amore 	int		 i;
277995c635efSGarrett D'Amore 
2780260e9a87SYuri Pankov 	for (i = 0; i < (int)SEC__MAX; i++)
278195c635efSGarrett D'Amore 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2782371584c2SYuri Pankov 			return (enum roff_sec)i;
278395c635efSGarrett D'Amore 
2784371584c2SYuri Pankov 	return SEC_CUSTOM;
278595c635efSGarrett D'Amore }
278695c635efSGarrett D'Amore 
278795c635efSGarrett D'Amore static size_t
2788c66b8046SYuri Pankov macro2len(enum roff_tok macro)
278995c635efSGarrett D'Amore {
279095c635efSGarrett D'Amore 
279195c635efSGarrett D'Amore 	switch (macro) {
2792260e9a87SYuri Pankov 	case MDOC_Ad:
2793371584c2SYuri Pankov 		return 12;
2794260e9a87SYuri Pankov 	case MDOC_Ao:
2795371584c2SYuri Pankov 		return 12;
2796260e9a87SYuri Pankov 	case MDOC_An:
2797371584c2SYuri Pankov 		return 12;
2798260e9a87SYuri Pankov 	case MDOC_Aq:
2799371584c2SYuri Pankov 		return 12;
2800260e9a87SYuri Pankov 	case MDOC_Ar:
2801371584c2SYuri Pankov 		return 12;
2802260e9a87SYuri Pankov 	case MDOC_Bo:
2803371584c2SYuri Pankov 		return 12;
2804260e9a87SYuri Pankov 	case MDOC_Bq:
2805371584c2SYuri Pankov 		return 12;
2806260e9a87SYuri Pankov 	case MDOC_Cd:
2807371584c2SYuri Pankov 		return 12;
2808260e9a87SYuri Pankov 	case MDOC_Cm:
2809371584c2SYuri Pankov 		return 10;
2810260e9a87SYuri Pankov 	case MDOC_Do:
2811371584c2SYuri Pankov 		return 10;
2812260e9a87SYuri Pankov 	case MDOC_Dq:
2813371584c2SYuri Pankov 		return 12;
2814260e9a87SYuri Pankov 	case MDOC_Dv:
2815371584c2SYuri Pankov 		return 12;
2816260e9a87SYuri Pankov 	case MDOC_Eo:
2817371584c2SYuri Pankov 		return 12;
2818260e9a87SYuri Pankov 	case MDOC_Em:
2819371584c2SYuri Pankov 		return 10;
2820260e9a87SYuri Pankov 	case MDOC_Er:
2821371584c2SYuri Pankov 		return 17;
2822260e9a87SYuri Pankov 	case MDOC_Ev:
2823371584c2SYuri Pankov 		return 15;
2824260e9a87SYuri Pankov 	case MDOC_Fa:
2825371584c2SYuri Pankov 		return 12;
2826260e9a87SYuri Pankov 	case MDOC_Fl:
2827371584c2SYuri Pankov 		return 10;
2828260e9a87SYuri Pankov 	case MDOC_Fo:
2829371584c2SYuri Pankov 		return 16;
2830260e9a87SYuri Pankov 	case MDOC_Fn:
2831371584c2SYuri Pankov 		return 16;
2832260e9a87SYuri Pankov 	case MDOC_Ic:
2833371584c2SYuri Pankov 		return 10;
2834260e9a87SYuri Pankov 	case MDOC_Li:
2835371584c2SYuri Pankov 		return 16;
2836260e9a87SYuri Pankov 	case MDOC_Ms:
2837371584c2SYuri Pankov 		return 6;
2838260e9a87SYuri Pankov 	case MDOC_Nm:
2839371584c2SYuri Pankov 		return 10;
2840260e9a87SYuri Pankov 	case MDOC_No:
2841371584c2SYuri Pankov 		return 12;
2842260e9a87SYuri Pankov 	case MDOC_Oo:
2843371584c2SYuri Pankov 		return 10;
2844260e9a87SYuri Pankov 	case MDOC_Op:
2845371584c2SYuri Pankov 		return 14;
2846260e9a87SYuri Pankov 	case MDOC_Pa:
2847371584c2SYuri Pankov 		return 32;
2848260e9a87SYuri Pankov 	case MDOC_Pf:
2849371584c2SYuri Pankov 		return 12;
2850260e9a87SYuri Pankov 	case MDOC_Po:
2851371584c2SYuri Pankov 		return 12;
2852260e9a87SYuri Pankov 	case MDOC_Pq:
2853371584c2SYuri Pankov 		return 12;
2854260e9a87SYuri Pankov 	case MDOC_Ql:
2855371584c2SYuri Pankov 		return 16;
2856260e9a87SYuri Pankov 	case MDOC_Qo:
2857371584c2SYuri Pankov 		return 12;
2858260e9a87SYuri Pankov 	case MDOC_So:
2859371584c2SYuri Pankov 		return 12;
2860260e9a87SYuri Pankov 	case MDOC_Sq:
2861371584c2SYuri Pankov 		return 12;
2862260e9a87SYuri Pankov 	case MDOC_Sy:
2863371584c2SYuri Pankov 		return 6;
2864260e9a87SYuri Pankov 	case MDOC_Sx:
2865371584c2SYuri Pankov 		return 16;
2866260e9a87SYuri Pankov 	case MDOC_Tn:
2867371584c2SYuri Pankov 		return 10;
2868260e9a87SYuri Pankov 	case MDOC_Va:
2869371584c2SYuri Pankov 		return 12;
2870260e9a87SYuri Pankov 	case MDOC_Vt:
2871371584c2SYuri Pankov 		return 12;
2872260e9a87SYuri Pankov 	case MDOC_Xr:
2873371584c2SYuri Pankov 		return 10;
287495c635efSGarrett D'Amore 	default:
287595c635efSGarrett D'Amore 		break;
287695c635efSGarrett D'Amore 	};
2877371584c2SYuri Pankov 	return 0;
287895c635efSGarrett D'Amore }
2879