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