1*a40ea1a7SYuri Pankov /*	$Id: mdoc_validate.c,v 1.318 2017/02/06 03:44:58 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3698f87a4SGarrett D'Amore  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*a40ea1a7SYuri Pankov  * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
5260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
695c635efSGarrett D'Amore  *
795c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
895c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
995c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1095c635efSGarrett D'Amore  *
11371584c2SYuri Pankov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1295c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13371584c2SYuri Pankov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1495c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1595c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1695c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1795c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1895c635efSGarrett D'Amore  */
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore 
21260e9a87SYuri Pankov #include <sys/types.h>
22698f87a4SGarrett D'Amore #ifndef OSNAME
2395c635efSGarrett D'Amore #include <sys/utsname.h>
2495c635efSGarrett D'Amore #endif
2595c635efSGarrett D'Amore 
2695c635efSGarrett D'Amore #include <assert.h>
2795c635efSGarrett D'Amore #include <ctype.h>
2895c635efSGarrett D'Amore #include <limits.h>
2995c635efSGarrett D'Amore #include <stdio.h>
3095c635efSGarrett D'Amore #include <stdlib.h>
3195c635efSGarrett D'Amore #include <string.h>
3295c635efSGarrett D'Amore #include <time.h>
3395c635efSGarrett D'Amore 
34260e9a87SYuri Pankov #include "mandoc_aux.h"
35371584c2SYuri Pankov #include "mandoc.h"
36371584c2SYuri Pankov #include "roff.h"
37371584c2SYuri Pankov #include "mdoc.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
39371584c2SYuri Pankov #include "roff_int.h"
40371584c2SYuri Pankov #include "libmdoc.h"
4195c635efSGarrett D'Amore 
4295c635efSGarrett D'Amore /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4395c635efSGarrett D'Amore 
44371584c2SYuri Pankov #define	POST_ARGS struct roff_man *mdoc
4595c635efSGarrett D'Amore 
4695c635efSGarrett D'Amore enum	check_ineq {
4795c635efSGarrett D'Amore 	CHECK_LT,
4895c635efSGarrett D'Amore 	CHECK_GT,
4995c635efSGarrett D'Amore 	CHECK_EQ
5095c635efSGarrett D'Amore };
5195c635efSGarrett D'Amore 
52260e9a87SYuri Pankov typedef	void	(*v_post)(POST_ARGS);
5395c635efSGarrett D'Amore 
54*a40ea1a7SYuri Pankov static	int	 build_list(struct roff_man *, int);
55371584c2SYuri Pankov static	void	 check_text(struct roff_man *, int, int, char *);
56371584c2SYuri Pankov static	void	 check_argv(struct roff_man *,
57371584c2SYuri Pankov 			struct roff_node *, struct mdoc_argv *);
58371584c2SYuri Pankov static	void	 check_args(struct roff_man *, struct roff_node *);
59371584c2SYuri Pankov static	int	 child_an(const struct roff_node *);
60371584c2SYuri Pankov static	size_t		macro2len(int);
61260e9a87SYuri Pankov static	void	 rewrite_macro2len(char **);
62260e9a87SYuri Pankov 
63260e9a87SYuri Pankov static	void	 post_an(POST_ARGS);
64371584c2SYuri Pankov static	void	 post_an_norm(POST_ARGS);
65260e9a87SYuri Pankov static	void	 post_at(POST_ARGS);
66371584c2SYuri Pankov static	void	 post_bd(POST_ARGS);
67260e9a87SYuri Pankov static	void	 post_bf(POST_ARGS);
68260e9a87SYuri Pankov static	void	 post_bk(POST_ARGS);
69260e9a87SYuri Pankov static	void	 post_bl(POST_ARGS);
70260e9a87SYuri Pankov static	void	 post_bl_block(POST_ARGS);
71260e9a87SYuri Pankov static	void	 post_bl_head(POST_ARGS);
72371584c2SYuri Pankov static	void	 post_bl_norm(POST_ARGS);
73260e9a87SYuri Pankov static	void	 post_bx(POST_ARGS);
74260e9a87SYuri Pankov static	void	 post_defaults(POST_ARGS);
75371584c2SYuri Pankov static	void	 post_display(POST_ARGS);
76260e9a87SYuri Pankov static	void	 post_dd(POST_ARGS);
77260e9a87SYuri Pankov static	void	 post_dt(POST_ARGS);
78260e9a87SYuri Pankov static	void	 post_en(POST_ARGS);
79260e9a87SYuri Pankov static	void	 post_es(POST_ARGS);
80260e9a87SYuri Pankov static	void	 post_eoln(POST_ARGS);
81260e9a87SYuri Pankov static	void	 post_ex(POST_ARGS);
82260e9a87SYuri Pankov static	void	 post_fa(POST_ARGS);
83260e9a87SYuri Pankov static	void	 post_fn(POST_ARGS);
84260e9a87SYuri Pankov static	void	 post_fname(POST_ARGS);
85260e9a87SYuri Pankov static	void	 post_fo(POST_ARGS);
86260e9a87SYuri Pankov static	void	 post_hyph(POST_ARGS);
87260e9a87SYuri Pankov static	void	 post_ignpar(POST_ARGS);
88260e9a87SYuri Pankov static	void	 post_it(POST_ARGS);
89260e9a87SYuri Pankov static	void	 post_lb(POST_ARGS);
90260e9a87SYuri Pankov static	void	 post_nd(POST_ARGS);
91260e9a87SYuri Pankov static	void	 post_nm(POST_ARGS);
92260e9a87SYuri Pankov static	void	 post_ns(POST_ARGS);
93371584c2SYuri Pankov static	void	 post_obsolete(POST_ARGS);
94260e9a87SYuri Pankov static	void	 post_os(POST_ARGS);
95260e9a87SYuri Pankov static	void	 post_par(POST_ARGS);
96371584c2SYuri Pankov static	void	 post_prevpar(POST_ARGS);
97260e9a87SYuri Pankov static	void	 post_root(POST_ARGS);
98260e9a87SYuri Pankov static	void	 post_rs(POST_ARGS);
99*a40ea1a7SYuri Pankov static	void	 post_rv(POST_ARGS);
100260e9a87SYuri Pankov static	void	 post_sh(POST_ARGS);
101260e9a87SYuri Pankov static	void	 post_sh_head(POST_ARGS);
102260e9a87SYuri Pankov static	void	 post_sh_name(POST_ARGS);
103260e9a87SYuri Pankov static	void	 post_sh_see_also(POST_ARGS);
104260e9a87SYuri Pankov static	void	 post_sh_authors(POST_ARGS);
105260e9a87SYuri Pankov static	void	 post_sm(POST_ARGS);
106260e9a87SYuri Pankov static	void	 post_st(POST_ARGS);
107371584c2SYuri Pankov static	void	 post_std(POST_ARGS);
108*a40ea1a7SYuri Pankov static	void	 post_xr(POST_ARGS);
109*a40ea1a7SYuri Pankov static	void	 post_xx(POST_ARGS);
110371584c2SYuri Pankov 
111371584c2SYuri Pankov static	v_post mdoc_valids[MDOC_MAX] = {
112371584c2SYuri Pankov 	NULL,		/* Ap */
113371584c2SYuri Pankov 	post_dd,	/* Dd */
114371584c2SYuri Pankov 	post_dt,	/* Dt */
115371584c2SYuri Pankov 	post_os,	/* Os */
116371584c2SYuri Pankov 	post_sh,	/* Sh */
117371584c2SYuri Pankov 	post_ignpar,	/* Ss */
118371584c2SYuri Pankov 	post_par,	/* Pp */
119371584c2SYuri Pankov 	post_display,	/* D1 */
120371584c2SYuri Pankov 	post_display,	/* Dl */
121371584c2SYuri Pankov 	post_display,	/* Bd */
122371584c2SYuri Pankov 	NULL,		/* Ed */
123371584c2SYuri Pankov 	post_bl,	/* Bl */
124371584c2SYuri Pankov 	NULL,		/* El */
125371584c2SYuri Pankov 	post_it,	/* It */
126371584c2SYuri Pankov 	NULL,		/* Ad */
127371584c2SYuri Pankov 	post_an,	/* An */
128371584c2SYuri Pankov 	post_defaults,	/* Ar */
129371584c2SYuri Pankov 	NULL,		/* Cd */
130371584c2SYuri Pankov 	NULL,		/* Cm */
131371584c2SYuri Pankov 	NULL,		/* Dv */
132371584c2SYuri Pankov 	NULL,		/* Er */
133371584c2SYuri Pankov 	NULL,		/* Ev */
134371584c2SYuri Pankov 	post_ex,	/* Ex */
135371584c2SYuri Pankov 	post_fa,	/* Fa */
136371584c2SYuri Pankov 	NULL,		/* Fd */
137371584c2SYuri Pankov 	NULL,		/* Fl */
138371584c2SYuri Pankov 	post_fn,	/* Fn */
139371584c2SYuri Pankov 	NULL,		/* Ft */
140371584c2SYuri Pankov 	NULL,		/* Ic */
141371584c2SYuri Pankov 	NULL,		/* In */
142371584c2SYuri Pankov 	post_defaults,	/* Li */
143371584c2SYuri Pankov 	post_nd,	/* Nd */
144371584c2SYuri Pankov 	post_nm,	/* Nm */
145371584c2SYuri Pankov 	NULL,		/* Op */
146371584c2SYuri Pankov 	post_obsolete,	/* Ot */
147371584c2SYuri Pankov 	post_defaults,	/* Pa */
148*a40ea1a7SYuri Pankov 	post_rv,	/* Rv */
149371584c2SYuri Pankov 	post_st,	/* St */
150371584c2SYuri Pankov 	NULL,		/* Va */
151371584c2SYuri Pankov 	NULL,		/* Vt */
152*a40ea1a7SYuri Pankov 	post_xr,	/* Xr */
153371584c2SYuri Pankov 	NULL,		/* %A */
154371584c2SYuri Pankov 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
155371584c2SYuri Pankov 	NULL,		/* %D */
156371584c2SYuri Pankov 	NULL,		/* %I */
157371584c2SYuri Pankov 	NULL,		/* %J */
158371584c2SYuri Pankov 	post_hyph,	/* %N */
159371584c2SYuri Pankov 	post_hyph,	/* %O */
160371584c2SYuri Pankov 	NULL,		/* %P */
161371584c2SYuri Pankov 	post_hyph,	/* %R */
162371584c2SYuri Pankov 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
163371584c2SYuri Pankov 	NULL,		/* %V */
164371584c2SYuri Pankov 	NULL,		/* Ac */
165371584c2SYuri Pankov 	NULL,		/* Ao */
166371584c2SYuri Pankov 	NULL,		/* Aq */
167371584c2SYuri Pankov 	post_at,	/* At */
168371584c2SYuri Pankov 	NULL,		/* Bc */
169371584c2SYuri Pankov 	post_bf,	/* Bf */
170371584c2SYuri Pankov 	NULL,		/* Bo */
171371584c2SYuri Pankov 	NULL,		/* Bq */
172*a40ea1a7SYuri Pankov 	post_xx,	/* Bsx */
173371584c2SYuri Pankov 	post_bx,	/* Bx */
174371584c2SYuri Pankov 	post_obsolete,	/* Db */
175371584c2SYuri Pankov 	NULL,		/* Dc */
176371584c2SYuri Pankov 	NULL,		/* Do */
177371584c2SYuri Pankov 	NULL,		/* Dq */
178371584c2SYuri Pankov 	NULL,		/* Ec */
179371584c2SYuri Pankov 	NULL,		/* Ef */
180371584c2SYuri Pankov 	NULL,		/* Em */
181371584c2SYuri Pankov 	NULL,		/* Eo */
182*a40ea1a7SYuri Pankov 	post_xx,	/* Fx */
183371584c2SYuri Pankov 	NULL,		/* Ms */
184371584c2SYuri Pankov 	NULL,		/* No */
185371584c2SYuri Pankov 	post_ns,	/* Ns */
186*a40ea1a7SYuri Pankov 	post_xx,	/* Nx */
187*a40ea1a7SYuri Pankov 	post_xx,	/* Ox */
188371584c2SYuri Pankov 	NULL,		/* Pc */
189371584c2SYuri Pankov 	NULL,		/* Pf */
190371584c2SYuri Pankov 	NULL,		/* Po */
191371584c2SYuri Pankov 	NULL,		/* Pq */
192371584c2SYuri Pankov 	NULL,		/* Qc */
193371584c2SYuri Pankov 	NULL,		/* Ql */
194371584c2SYuri Pankov 	NULL,		/* Qo */
195371584c2SYuri Pankov 	NULL,		/* Qq */
196371584c2SYuri Pankov 	NULL,		/* Re */
197371584c2SYuri Pankov 	post_rs,	/* Rs */
198371584c2SYuri Pankov 	NULL,		/* Sc */
199371584c2SYuri Pankov 	NULL,		/* So */
200371584c2SYuri Pankov 	NULL,		/* Sq */
201371584c2SYuri Pankov 	post_sm,	/* Sm */
202371584c2SYuri Pankov 	post_hyph,	/* Sx */
203371584c2SYuri Pankov 	NULL,		/* Sy */
204371584c2SYuri Pankov 	NULL,		/* Tn */
205*a40ea1a7SYuri Pankov 	post_xx,	/* Ux */
206371584c2SYuri Pankov 	NULL,		/* Xc */
207371584c2SYuri Pankov 	NULL,		/* Xo */
208371584c2SYuri Pankov 	post_fo,	/* Fo */
209371584c2SYuri Pankov 	NULL,		/* Fc */
210371584c2SYuri Pankov 	NULL,		/* Oo */
211371584c2SYuri Pankov 	NULL,		/* Oc */
212371584c2SYuri Pankov 	post_bk,	/* Bk */
213371584c2SYuri Pankov 	NULL,		/* Ek */
214371584c2SYuri Pankov 	post_eoln,	/* Bt */
215371584c2SYuri Pankov 	NULL,		/* Hf */
216371584c2SYuri Pankov 	post_obsolete,	/* Fr */
217371584c2SYuri Pankov 	post_eoln,	/* Ud */
218371584c2SYuri Pankov 	post_lb,	/* Lb */
219371584c2SYuri Pankov 	post_par,	/* Lp */
220371584c2SYuri Pankov 	NULL,		/* Lk */
221371584c2SYuri Pankov 	post_defaults,	/* Mt */
222371584c2SYuri Pankov 	NULL,		/* Brq */
223371584c2SYuri Pankov 	NULL,		/* Bro */
224371584c2SYuri Pankov 	NULL,		/* Brc */
225371584c2SYuri Pankov 	NULL,		/* %C */
226371584c2SYuri Pankov 	post_es,	/* Es */
227371584c2SYuri Pankov 	post_en,	/* En */
228*a40ea1a7SYuri Pankov 	post_xx,	/* Dx */
229371584c2SYuri Pankov 	NULL,		/* %Q */
230371584c2SYuri Pankov 	post_par,	/* br */
231371584c2SYuri Pankov 	post_par,	/* sp */
232371584c2SYuri Pankov 	NULL,		/* %U */
233371584c2SYuri Pankov 	NULL,		/* Ta */
234371584c2SYuri Pankov 	NULL,		/* ll */
23595c635efSGarrett D'Amore };
23695c635efSGarrett D'Amore 
23795c635efSGarrett D'Amore #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
23895c635efSGarrett D'Amore 
239371584c2SYuri Pankov static	const int rsord[RSORD_MAX] = {
24095c635efSGarrett D'Amore 	MDOC__A,
24195c635efSGarrett D'Amore 	MDOC__T,
24295c635efSGarrett D'Amore 	MDOC__B,
24395c635efSGarrett D'Amore 	MDOC__I,
24495c635efSGarrett D'Amore 	MDOC__J,
24595c635efSGarrett D'Amore 	MDOC__R,
24695c635efSGarrett D'Amore 	MDOC__N,
24795c635efSGarrett D'Amore 	MDOC__V,
248698f87a4SGarrett D'Amore 	MDOC__U,
24995c635efSGarrett D'Amore 	MDOC__P,
25095c635efSGarrett D'Amore 	MDOC__Q,
25195c635efSGarrett D'Amore 	MDOC__C,
252698f87a4SGarrett D'Amore 	MDOC__D,
253698f87a4SGarrett D'Amore 	MDOC__O
25495c635efSGarrett D'Amore };
25595c635efSGarrett D'Amore 
25695c635efSGarrett D'Amore static	const char * const secnames[SEC__MAX] = {
25795c635efSGarrett D'Amore 	NULL,
25895c635efSGarrett D'Amore 	"NAME",
25995c635efSGarrett D'Amore 	"LIBRARY",
26095c635efSGarrett D'Amore 	"SYNOPSIS",
26195c635efSGarrett D'Amore 	"DESCRIPTION",
262260e9a87SYuri Pankov 	"CONTEXT",
26395c635efSGarrett D'Amore 	"IMPLEMENTATION NOTES",
26495c635efSGarrett D'Amore 	"RETURN VALUES",
26595c635efSGarrett D'Amore 	"ENVIRONMENT",
26695c635efSGarrett D'Amore 	"FILES",
26795c635efSGarrett D'Amore 	"EXIT STATUS",
26895c635efSGarrett D'Amore 	"EXAMPLES",
26995c635efSGarrett D'Amore 	"DIAGNOSTICS",
27095c635efSGarrett D'Amore 	"COMPATIBILITY",
27195c635efSGarrett D'Amore 	"ERRORS",
27295c635efSGarrett D'Amore 	"SEE ALSO",
27395c635efSGarrett D'Amore 	"STANDARDS",
27495c635efSGarrett D'Amore 	"HISTORY",
27595c635efSGarrett D'Amore 	"AUTHORS",
27695c635efSGarrett D'Amore 	"CAVEATS",
27795c635efSGarrett D'Amore 	"BUGS",
27895c635efSGarrett D'Amore 	"SECURITY CONSIDERATIONS",
27995c635efSGarrett D'Amore 	NULL
28095c635efSGarrett D'Amore };
28195c635efSGarrett D'Amore 
282260e9a87SYuri Pankov 
283260e9a87SYuri Pankov void
284371584c2SYuri Pankov mdoc_node_validate(struct roff_man *mdoc)
28595c635efSGarrett D'Amore {
286371584c2SYuri Pankov 	struct roff_node *n;
287371584c2SYuri Pankov 	v_post *p;
28895c635efSGarrett D'Amore 
289371584c2SYuri Pankov 	n = mdoc->last;
290371584c2SYuri Pankov 	mdoc->last = mdoc->last->child;
291371584c2SYuri Pankov 	while (mdoc->last != NULL) {
292371584c2SYuri Pankov 		mdoc_node_validate(mdoc);
293371584c2SYuri Pankov 		if (mdoc->last == n)
294371584c2SYuri Pankov 			mdoc->last = mdoc->last->child;
295371584c2SYuri Pankov 		else
296371584c2SYuri Pankov 			mdoc->last = mdoc->last->next;
297371584c2SYuri Pankov 	}
298371584c2SYuri Pankov 
299371584c2SYuri Pankov 	mdoc->last = n;
300371584c2SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
30195c635efSGarrett D'Amore 	switch (n->type) {
302371584c2SYuri Pankov 	case ROFFT_TEXT:
303*a40ea1a7SYuri Pankov 		if (n->sec != SEC_SYNOPSIS ||
304*a40ea1a7SYuri Pankov 		    (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
305260e9a87SYuri Pankov 			check_text(mdoc, n->line, n->pos, n->string);
30695c635efSGarrett D'Amore 		break;
307371584c2SYuri Pankov 	case ROFFT_EQN:
308371584c2SYuri Pankov 	case ROFFT_TBL:
30995c635efSGarrett D'Amore 		break;
310371584c2SYuri Pankov 	case ROFFT_ROOT:
311260e9a87SYuri Pankov 		post_root(mdoc);
31295c635efSGarrett D'Amore 		break;
31395c635efSGarrett D'Amore 	default:
314371584c2SYuri Pankov 		check_args(mdoc, mdoc->last);
31595c635efSGarrett D'Amore 
316260e9a87SYuri Pankov 		/*
317260e9a87SYuri Pankov 		 * Closing delimiters are not special at the
318260e9a87SYuri Pankov 		 * beginning of a block, opening delimiters
319260e9a87SYuri Pankov 		 * are not special at the end.
320260e9a87SYuri Pankov 		 */
32195c635efSGarrett D'Amore 
322260e9a87SYuri Pankov 		if (n->child != NULL)
323*a40ea1a7SYuri Pankov 			n->child->flags &= ~NODE_DELIMC;
324260e9a87SYuri Pankov 		if (n->last != NULL)
325*a40ea1a7SYuri Pankov 			n->last->flags &= ~NODE_DELIMO;
32695c635efSGarrett D'Amore 
327260e9a87SYuri Pankov 		/* Call the macro's postprocessor. */
32895c635efSGarrett D'Amore 
329371584c2SYuri Pankov 		p = mdoc_valids + n->tok;
330260e9a87SYuri Pankov 		if (*p)
331260e9a87SYuri Pankov 			(*p)(mdoc);
332371584c2SYuri Pankov 		if (mdoc->last == n)
333371584c2SYuri Pankov 			mdoc_state(mdoc, n);
334260e9a87SYuri Pankov 		break;
335260e9a87SYuri Pankov 	}
33695c635efSGarrett D'Amore }
33795c635efSGarrett D'Amore 
33895c635efSGarrett D'Amore static void
339371584c2SYuri Pankov check_args(struct roff_man *mdoc, struct roff_node *n)
34095c635efSGarrett D'Amore {
34195c635efSGarrett D'Amore 	int		 i;
34295c635efSGarrett D'Amore 
34395c635efSGarrett D'Amore 	if (NULL == n->args)
34495c635efSGarrett D'Amore 		return;
34595c635efSGarrett D'Amore 
34695c635efSGarrett D'Amore 	assert(n->args->argc);
34795c635efSGarrett D'Amore 	for (i = 0; i < (int)n->args->argc; i++)
348698f87a4SGarrett D'Amore 		check_argv(mdoc, n, &n->args->argv[i]);
34995c635efSGarrett D'Amore }
35095c635efSGarrett D'Amore 
35195c635efSGarrett D'Amore static void
352371584c2SYuri Pankov check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
35395c635efSGarrett D'Amore {
35495c635efSGarrett D'Amore 	int		 i;
35595c635efSGarrett D'Amore 
35695c635efSGarrett D'Amore 	for (i = 0; i < (int)v->sz; i++)
357698f87a4SGarrett D'Amore 		check_text(mdoc, v->line, v->pos, v->value[i]);
35895c635efSGarrett D'Amore }
35995c635efSGarrett D'Amore 
36095c635efSGarrett D'Amore static void
361371584c2SYuri Pankov check_text(struct roff_man *mdoc, int ln, int pos, char *p)
36295c635efSGarrett D'Amore {
36395c635efSGarrett D'Amore 	char		*cp;
36495c635efSGarrett D'Amore 
365698f87a4SGarrett D'Amore 	if (MDOC_LITERAL & mdoc->flags)
36695c635efSGarrett D'Amore 		return;
36795c635efSGarrett D'Amore 
36895c635efSGarrett D'Amore 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
369260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
370260e9a87SYuri Pankov 		    ln, pos + (int)(p - cp), NULL);
37195c635efSGarrett D'Amore }
37295c635efSGarrett D'Amore 
373260e9a87SYuri Pankov static void
374371584c2SYuri Pankov post_bl_norm(POST_ARGS)
37595c635efSGarrett D'Amore {
376371584c2SYuri Pankov 	struct roff_node *n;
377260e9a87SYuri Pankov 	struct mdoc_argv *argv, *wa;
378260e9a87SYuri Pankov 	int		  i;
379260e9a87SYuri Pankov 	enum mdocargt	  mdoclt;
38095c635efSGarrett D'Amore 	enum mdoc_list	  lt;
38195c635efSGarrett D'Amore 
382371584c2SYuri Pankov 	n = mdoc->last->parent;
383371584c2SYuri Pankov 	n->norm->Bl.type = LIST__NONE;
38495c635efSGarrett D'Amore 
385260e9a87SYuri Pankov 	/*
38695c635efSGarrett D'Amore 	 * First figure out which kind of list to use: bind ourselves to
38795c635efSGarrett D'Amore 	 * the first mentioned list type and warn about any remaining
38895c635efSGarrett D'Amore 	 * ones.  If we find no list type, we default to LIST_item.
38995c635efSGarrett D'Amore 	 */
39095c635efSGarrett D'Amore 
391260e9a87SYuri Pankov 	wa = (n->args == NULL) ? NULL : n->args->argv;
392260e9a87SYuri Pankov 	mdoclt = MDOC_ARG_MAX;
39395c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
394260e9a87SYuri Pankov 		argv = n->args->argv + i;
39595c635efSGarrett D'Amore 		lt = LIST__NONE;
396260e9a87SYuri Pankov 		switch (argv->arg) {
39795c635efSGarrett D'Amore 		/* Set list types. */
398260e9a87SYuri Pankov 		case MDOC_Bullet:
39995c635efSGarrett D'Amore 			lt = LIST_bullet;
40095c635efSGarrett D'Amore 			break;
401260e9a87SYuri Pankov 		case MDOC_Dash:
40295c635efSGarrett D'Amore 			lt = LIST_dash;
40395c635efSGarrett D'Amore 			break;
404260e9a87SYuri Pankov 		case MDOC_Enum:
40595c635efSGarrett D'Amore 			lt = LIST_enum;
40695c635efSGarrett D'Amore 			break;
407260e9a87SYuri Pankov 		case MDOC_Hyphen:
40895c635efSGarrett D'Amore 			lt = LIST_hyphen;
40995c635efSGarrett D'Amore 			break;
410260e9a87SYuri Pankov 		case MDOC_Item:
41195c635efSGarrett D'Amore 			lt = LIST_item;
41295c635efSGarrett D'Amore 			break;
413260e9a87SYuri Pankov 		case MDOC_Tag:
41495c635efSGarrett D'Amore 			lt = LIST_tag;
41595c635efSGarrett D'Amore 			break;
416260e9a87SYuri Pankov 		case MDOC_Diag:
41795c635efSGarrett D'Amore 			lt = LIST_diag;
41895c635efSGarrett D'Amore 			break;
419260e9a87SYuri Pankov 		case MDOC_Hang:
42095c635efSGarrett D'Amore 			lt = LIST_hang;
42195c635efSGarrett D'Amore 			break;
422260e9a87SYuri Pankov 		case MDOC_Ohang:
42395c635efSGarrett D'Amore 			lt = LIST_ohang;
42495c635efSGarrett D'Amore 			break;
425260e9a87SYuri Pankov 		case MDOC_Inset:
42695c635efSGarrett D'Amore 			lt = LIST_inset;
42795c635efSGarrett D'Amore 			break;
428260e9a87SYuri Pankov 		case MDOC_Column:
42995c635efSGarrett D'Amore 			lt = LIST_column;
43095c635efSGarrett D'Amore 			break;
43195c635efSGarrett D'Amore 		/* Set list arguments. */
432260e9a87SYuri Pankov 		case MDOC_Compact:
433260e9a87SYuri Pankov 			if (n->norm->Bl.comp)
434260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
435260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
436260e9a87SYuri Pankov 				    argv->pos, "Bl -compact");
437260e9a87SYuri Pankov 			n->norm->Bl.comp = 1;
43895c635efSGarrett D'Amore 			break;
439260e9a87SYuri Pankov 		case MDOC_Width:
440260e9a87SYuri Pankov 			wa = argv;
441260e9a87SYuri Pankov 			if (0 == argv->sz) {
442260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
443260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
444260e9a87SYuri Pankov 				    argv->pos, "Bl -width");
445260e9a87SYuri Pankov 				n->norm->Bl.width = "0n";
44695c635efSGarrett D'Amore 				break;
44795c635efSGarrett D'Amore 			}
448260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.width)
449260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
450260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
451260e9a87SYuri Pankov 				    argv->pos, "Bl -width %s",
452260e9a87SYuri Pankov 				    argv->value[0]);
453260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
454260e9a87SYuri Pankov 			n->norm->Bl.width = argv->value[0];
45595c635efSGarrett D'Amore 			break;
456260e9a87SYuri Pankov 		case MDOC_Offset:
457260e9a87SYuri Pankov 			if (0 == argv->sz) {
458260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
459260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
460260e9a87SYuri Pankov 				    argv->pos, "Bl -offset");
46195c635efSGarrett D'Amore 				break;
46295c635efSGarrett D'Amore 			}
463260e9a87SYuri Pankov 			if (NULL != n->norm->Bl.offs)
464260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
465260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
466260e9a87SYuri Pankov 				    argv->pos, "Bl -offset %s",
467260e9a87SYuri Pankov 				    argv->value[0]);
468260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
469260e9a87SYuri Pankov 			n->norm->Bl.offs = argv->value[0];
47095c635efSGarrett D'Amore 			break;
47195c635efSGarrett D'Amore 		default:
47295c635efSGarrett D'Amore 			continue;
47395c635efSGarrett D'Amore 		}
474260e9a87SYuri Pankov 		if (LIST__NONE == lt)
475260e9a87SYuri Pankov 			continue;
476260e9a87SYuri Pankov 		mdoclt = argv->arg;
47795c635efSGarrett D'Amore 
47895c635efSGarrett D'Amore 		/* Check: multiple list types. */
47995c635efSGarrett D'Amore 
480260e9a87SYuri Pankov 		if (LIST__NONE != n->norm->Bl.type) {
481260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_REP,
482260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
483260e9a87SYuri Pankov 			    "Bl -%s", mdoc_argnames[argv->arg]);
484260e9a87SYuri Pankov 			continue;
48595c635efSGarrett D'Amore 		}
48695c635efSGarrett D'Amore 
48795c635efSGarrett D'Amore 		/* The list type should come first. */
48895c635efSGarrett D'Amore 
489260e9a87SYuri Pankov 		if (n->norm->Bl.width ||
490260e9a87SYuri Pankov 		    n->norm->Bl.offs ||
491260e9a87SYuri Pankov 		    n->norm->Bl.comp)
492260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
493260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos, "Bl -%s",
494260e9a87SYuri Pankov 			    mdoc_argnames[n->args->argv[0].arg]);
495260e9a87SYuri Pankov 
496260e9a87SYuri Pankov 		n->norm->Bl.type = lt;
497260e9a87SYuri Pankov 		if (LIST_column == lt) {
498260e9a87SYuri Pankov 			n->norm->Bl.ncols = argv->sz;
499260e9a87SYuri Pankov 			n->norm->Bl.cols = (void *)argv->value;
500260e9a87SYuri Pankov 		}
50195c635efSGarrett D'Amore 	}
50295c635efSGarrett D'Amore 
50395c635efSGarrett D'Amore 	/* Allow lists to default to LIST_item. */
50495c635efSGarrett D'Amore 
50595c635efSGarrett D'Amore 	if (LIST__NONE == n->norm->Bl.type) {
506260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
507260e9a87SYuri Pankov 		    n->line, n->pos, "Bl");
50895c635efSGarrett D'Amore 		n->norm->Bl.type = LIST_item;
509*a40ea1a7SYuri Pankov 		mdoclt = MDOC_Item;
51095c635efSGarrett D'Amore 	}
51195c635efSGarrett D'Amore 
512260e9a87SYuri Pankov 	/*
51395c635efSGarrett D'Amore 	 * Validate the width field.  Some list types don't need width
51495c635efSGarrett D'Amore 	 * types and should be warned about them.  Others should have it
515698f87a4SGarrett D'Amore 	 * and must also be warned.  Yet others have a default and need
516698f87a4SGarrett D'Amore 	 * no warning.
51795c635efSGarrett D'Amore 	 */
51895c635efSGarrett D'Amore 
51995c635efSGarrett D'Amore 	switch (n->norm->Bl.type) {
520260e9a87SYuri Pankov 	case LIST_tag:
521698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
522260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
523260e9a87SYuri Pankov 			    n->line, n->pos, "Bl -tag");
52495c635efSGarrett D'Amore 		break;
525260e9a87SYuri Pankov 	case LIST_column:
526260e9a87SYuri Pankov 	case LIST_diag:
527260e9a87SYuri Pankov 	case LIST_ohang:
528260e9a87SYuri Pankov 	case LIST_inset:
529260e9a87SYuri Pankov 	case LIST_item:
53095c635efSGarrett D'Amore 		if (n->norm->Bl.width)
531260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
532260e9a87SYuri Pankov 			    wa->line, wa->pos, "Bl -%s",
533260e9a87SYuri Pankov 			    mdoc_argnames[mdoclt]);
53495c635efSGarrett D'Amore 		break;
535260e9a87SYuri Pankov 	case LIST_bullet:
536260e9a87SYuri Pankov 	case LIST_dash:
537260e9a87SYuri Pankov 	case LIST_hyphen:
538698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
539698f87a4SGarrett D'Amore 			n->norm->Bl.width = "2n";
540698f87a4SGarrett D'Amore 		break;
541260e9a87SYuri Pankov 	case LIST_enum:
542698f87a4SGarrett D'Amore 		if (NULL == n->norm->Bl.width)
543698f87a4SGarrett D'Amore 			n->norm->Bl.width = "3n";
544698f87a4SGarrett D'Amore 		break;
54595c635efSGarrett D'Amore 	default:
54695c635efSGarrett D'Amore 		break;
54795c635efSGarrett D'Amore 	}
54895c635efSGarrett D'Amore }
54995c635efSGarrett D'Amore 
550260e9a87SYuri Pankov static void
551371584c2SYuri Pankov post_bd(POST_ARGS)
55295c635efSGarrett D'Amore {
553371584c2SYuri Pankov 	struct roff_node *n;
554260e9a87SYuri Pankov 	struct mdoc_argv *argv;
555260e9a87SYuri Pankov 	int		  i;
556260e9a87SYuri Pankov 	enum mdoc_disp	  dt;
55795c635efSGarrett D'Amore 
558371584c2SYuri Pankov 	n = mdoc->last;
55995c635efSGarrett D'Amore 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
560260e9a87SYuri Pankov 		argv = n->args->argv + i;
56195c635efSGarrett D'Amore 		dt = DISP__NONE;
56295c635efSGarrett D'Amore 
563260e9a87SYuri Pankov 		switch (argv->arg) {
564260e9a87SYuri Pankov 		case MDOC_Centred:
565260e9a87SYuri Pankov 			dt = DISP_centered;
56695c635efSGarrett D'Amore 			break;
567260e9a87SYuri Pankov 		case MDOC_Ragged:
56895c635efSGarrett D'Amore 			dt = DISP_ragged;
56995c635efSGarrett D'Amore 			break;
570260e9a87SYuri Pankov 		case MDOC_Unfilled:
57195c635efSGarrett D'Amore 			dt = DISP_unfilled;
57295c635efSGarrett D'Amore 			break;
573260e9a87SYuri Pankov 		case MDOC_Filled:
57495c635efSGarrett D'Amore 			dt = DISP_filled;
57595c635efSGarrett D'Amore 			break;
576260e9a87SYuri Pankov 		case MDOC_Literal:
57795c635efSGarrett D'Amore 			dt = DISP_literal;
57895c635efSGarrett D'Amore 			break;
579260e9a87SYuri Pankov 		case MDOC_File:
580260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
581260e9a87SYuri Pankov 			    n->line, n->pos, NULL);
582260e9a87SYuri Pankov 			break;
583260e9a87SYuri Pankov 		case MDOC_Offset:
584260e9a87SYuri Pankov 			if (0 == argv->sz) {
585260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_EMPTY,
586260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
587260e9a87SYuri Pankov 				    argv->pos, "Bd -offset");
58895c635efSGarrett D'Amore 				break;
58995c635efSGarrett D'Amore 			}
590260e9a87SYuri Pankov 			if (NULL != n->norm->Bd.offs)
591260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_ARG_REP,
592260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
593260e9a87SYuri Pankov 				    argv->pos, "Bd -offset %s",
594260e9a87SYuri Pankov 				    argv->value[0]);
595260e9a87SYuri Pankov 			rewrite_macro2len(argv->value);
596260e9a87SYuri Pankov 			n->norm->Bd.offs = argv->value[0];
59795c635efSGarrett D'Amore 			break;
598260e9a87SYuri Pankov 		case MDOC_Compact:
599260e9a87SYuri Pankov 			if (n->norm->Bd.comp)
600260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_REP,
601260e9a87SYuri Pankov 				    mdoc->parse, argv->line,
602260e9a87SYuri Pankov 				    argv->pos, "Bd -compact");
603260e9a87SYuri Pankov 			n->norm->Bd.comp = 1;
60495c635efSGarrett D'Amore 			break;
60595c635efSGarrett D'Amore 		default:
60695c635efSGarrett D'Amore 			abort();
60795c635efSGarrett D'Amore 		}
608260e9a87SYuri Pankov 		if (DISP__NONE == dt)
609260e9a87SYuri Pankov 			continue;
61095c635efSGarrett D'Amore 
611260e9a87SYuri Pankov 		if (DISP__NONE == n->norm->Bd.type)
61295c635efSGarrett D'Amore 			n->norm->Bd.type = dt;
613260e9a87SYuri Pankov 		else
614260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BD_REP,
615260e9a87SYuri Pankov 			    mdoc->parse, n->line, n->pos,
616260e9a87SYuri Pankov 			    "Bd -%s", mdoc_argnames[argv->arg]);
61795c635efSGarrett D'Amore 	}
61895c635efSGarrett D'Amore 
61995c635efSGarrett D'Amore 	if (DISP__NONE == n->norm->Bd.type) {
620260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
621260e9a87SYuri Pankov 		    n->line, n->pos, "Bd");
62295c635efSGarrett D'Amore 		n->norm->Bd.type = DISP_ragged;
62395c635efSGarrett D'Amore 	}
62495c635efSGarrett D'Amore }
62595c635efSGarrett D'Amore 
626*a40ea1a7SYuri Pankov /*
627*a40ea1a7SYuri Pankov  * Stand-alone line macros.
628*a40ea1a7SYuri Pankov  */
629*a40ea1a7SYuri Pankov 
630260e9a87SYuri Pankov static void
631371584c2SYuri Pankov post_an_norm(POST_ARGS)
63295c635efSGarrett D'Amore {
633371584c2SYuri Pankov 	struct roff_node *n;
634260e9a87SYuri Pankov 	struct mdoc_argv *argv;
635260e9a87SYuri Pankov 	size_t	 i;
63695c635efSGarrett D'Amore 
637371584c2SYuri Pankov 	n = mdoc->last;
638260e9a87SYuri Pankov 	if (n->args == NULL)
639260e9a87SYuri Pankov 		return;
64095c635efSGarrett D'Amore 
641260e9a87SYuri Pankov 	for (i = 1; i < n->args->argc; i++) {
642260e9a87SYuri Pankov 		argv = n->args->argv + i;
643260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AN_REP,
644260e9a87SYuri Pankov 		    mdoc->parse, argv->line, argv->pos,
645260e9a87SYuri Pankov 		    "An -%s", mdoc_argnames[argv->arg]);
646260e9a87SYuri Pankov 	}
647260e9a87SYuri Pankov 
648260e9a87SYuri Pankov 	argv = n->args->argv;
649260e9a87SYuri Pankov 	if (argv->arg == MDOC_Split)
65095c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_split;
651260e9a87SYuri Pankov 	else if (argv->arg == MDOC_Nosplit)
65295c635efSGarrett D'Amore 		n->norm->An.auth = AUTH_nosplit;
65395c635efSGarrett D'Amore 	else
65495c635efSGarrett D'Amore 		abort();
65595c635efSGarrett D'Amore }
65695c635efSGarrett D'Amore 
657*a40ea1a7SYuri Pankov static void
658*a40ea1a7SYuri Pankov post_eoln(POST_ARGS)
659*a40ea1a7SYuri Pankov {
660*a40ea1a7SYuri Pankov 	struct roff_node	*n;
661*a40ea1a7SYuri Pankov 
662*a40ea1a7SYuri Pankov 	n = mdoc->last;
663*a40ea1a7SYuri Pankov 	if (n->child != NULL)
664*a40ea1a7SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
665*a40ea1a7SYuri Pankov 		    n->line, n->pos, "%s %s",
666*a40ea1a7SYuri Pankov 		    mdoc_macronames[n->tok], n->child->string);
667*a40ea1a7SYuri Pankov 
668*a40ea1a7SYuri Pankov 	while (n->child != NULL)
669*a40ea1a7SYuri Pankov 		roff_node_delete(mdoc, n->child);
670*a40ea1a7SYuri Pankov 
671*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
672*a40ea1a7SYuri Pankov 	    "is currently in beta test." : "currently under development.");
673*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
674*a40ea1a7SYuri Pankov 	mdoc->last = n;
675*a40ea1a7SYuri Pankov }
676*a40ea1a7SYuri Pankov 
677*a40ea1a7SYuri Pankov static int
678*a40ea1a7SYuri Pankov build_list(struct roff_man *mdoc, int tok)
679*a40ea1a7SYuri Pankov {
680*a40ea1a7SYuri Pankov 	struct roff_node	*n;
681*a40ea1a7SYuri Pankov 	int			 ic;
682*a40ea1a7SYuri Pankov 
683*a40ea1a7SYuri Pankov 	n = mdoc->last->next;
684*a40ea1a7SYuri Pankov 	for (ic = 1;; ic++) {
685*a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, tok);
686*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
687*a40ea1a7SYuri Pankov 		mdoc_node_relink(mdoc, n);
688*a40ea1a7SYuri Pankov 		n = mdoc->last = mdoc->last->parent;
689*a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
690*a40ea1a7SYuri Pankov 		if (n->next == NULL)
691*a40ea1a7SYuri Pankov 			return ic;
692*a40ea1a7SYuri Pankov 		if (ic > 1 || n->next->next != NULL) {
693*a40ea1a7SYuri Pankov 			roff_word_alloc(mdoc, n->line, n->pos, ",");
694*a40ea1a7SYuri Pankov 			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
695*a40ea1a7SYuri Pankov 		}
696*a40ea1a7SYuri Pankov 		n = mdoc->last->next;
697*a40ea1a7SYuri Pankov 		if (n->next == NULL) {
698*a40ea1a7SYuri Pankov 			roff_word_alloc(mdoc, n->line, n->pos, "and");
699*a40ea1a7SYuri Pankov 			mdoc->last->flags |= NODE_NOSRC;
700*a40ea1a7SYuri Pankov 		}
701*a40ea1a7SYuri Pankov 	}
702*a40ea1a7SYuri Pankov }
703*a40ea1a7SYuri Pankov 
704*a40ea1a7SYuri Pankov static void
705*a40ea1a7SYuri Pankov post_ex(POST_ARGS)
706*a40ea1a7SYuri Pankov {
707*a40ea1a7SYuri Pankov 	struct roff_node	*n;
708*a40ea1a7SYuri Pankov 	int			 ic;
709*a40ea1a7SYuri Pankov 
710*a40ea1a7SYuri Pankov 	post_std(mdoc);
711*a40ea1a7SYuri Pankov 
712*a40ea1a7SYuri Pankov 	n = mdoc->last;
713*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
714*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "The");
715*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
716*a40ea1a7SYuri Pankov 
717*a40ea1a7SYuri Pankov 	if (mdoc->last->next != NULL)
718*a40ea1a7SYuri Pankov 		ic = build_list(mdoc, MDOC_Nm);
719*a40ea1a7SYuri Pankov 	else if (mdoc->meta.name != NULL) {
720*a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
721*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
722*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
723*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
724*a40ea1a7SYuri Pankov 		mdoc->last = mdoc->last->parent;
725*a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
726*a40ea1a7SYuri Pankov 		ic = 1;
727*a40ea1a7SYuri Pankov 	} else {
728*a40ea1a7SYuri Pankov 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
729*a40ea1a7SYuri Pankov 		    n->line, n->pos, "Ex");
730*a40ea1a7SYuri Pankov 		ic = 0;
731*a40ea1a7SYuri Pankov 	}
732*a40ea1a7SYuri Pankov 
733*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
734*a40ea1a7SYuri Pankov 	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
735*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
736*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
737*a40ea1a7SYuri Pankov 	    "on success, and\\~>0 if an error occurs.");
738*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
739*a40ea1a7SYuri Pankov 	mdoc->last = n;
740*a40ea1a7SYuri Pankov }
741*a40ea1a7SYuri Pankov 
742*a40ea1a7SYuri Pankov static void
743*a40ea1a7SYuri Pankov post_lb(POST_ARGS)
744*a40ea1a7SYuri Pankov {
745*a40ea1a7SYuri Pankov 	struct roff_node	*n;
746*a40ea1a7SYuri Pankov 	const char		*p;
747*a40ea1a7SYuri Pankov 
748*a40ea1a7SYuri Pankov 	n = mdoc->last;
749*a40ea1a7SYuri Pankov 	assert(n->child->type == ROFFT_TEXT);
750*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
751*a40ea1a7SYuri Pankov 
752*a40ea1a7SYuri Pankov 	if ((p = mdoc_a2lib(n->child->string)) != NULL) {
753*a40ea1a7SYuri Pankov 		n->child->flags |= NODE_NOPRT;
754*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, p);
755*a40ea1a7SYuri Pankov 		mdoc->last->flags = NODE_NOSRC;
756*a40ea1a7SYuri Pankov 		mdoc->last = n;
757*a40ea1a7SYuri Pankov 		return;
758*a40ea1a7SYuri Pankov 	}
759*a40ea1a7SYuri Pankov 
760*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "library");
761*a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_NOSRC;
762*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
763*a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
764*a40ea1a7SYuri Pankov 	mdoc->last = mdoc->last->next;
765*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
766*a40ea1a7SYuri Pankov 	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
767*a40ea1a7SYuri Pankov 	mdoc->last = n;
768*a40ea1a7SYuri Pankov }
769*a40ea1a7SYuri Pankov 
770*a40ea1a7SYuri Pankov static void
771*a40ea1a7SYuri Pankov post_rv(POST_ARGS)
772*a40ea1a7SYuri Pankov {
773*a40ea1a7SYuri Pankov 	struct roff_node	*n;
774*a40ea1a7SYuri Pankov 	int			 ic;
775*a40ea1a7SYuri Pankov 
776*a40ea1a7SYuri Pankov 	post_std(mdoc);
777*a40ea1a7SYuri Pankov 
778*a40ea1a7SYuri Pankov 	n = mdoc->last;
779*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
780*a40ea1a7SYuri Pankov 	if (n->child != NULL) {
781*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "The");
782*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
783*a40ea1a7SYuri Pankov 		ic = build_list(mdoc, MDOC_Fn);
784*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos,
785*a40ea1a7SYuri Pankov 		    ic > 1 ? "functions return" : "function returns");
786*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
787*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos,
788*a40ea1a7SYuri Pankov 		    "the value\\~0 if successful;");
789*a40ea1a7SYuri Pankov 	} else
790*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
791*a40ea1a7SYuri Pankov 		    "completion, the value\\~0 is returned;");
792*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
793*a40ea1a7SYuri Pankov 
794*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
795*a40ea1a7SYuri Pankov 	    "the value\\~\\-1 is returned and the global variable");
796*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
797*a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
798*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
799*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "errno");
800*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
801*a40ea1a7SYuri Pankov 	mdoc->last = mdoc->last->parent;
802*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
803*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos,
804*a40ea1a7SYuri Pankov 	    "is set to indicate the error.");
805*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
806*a40ea1a7SYuri Pankov 	mdoc->last = n;
807*a40ea1a7SYuri Pankov }
808*a40ea1a7SYuri Pankov 
809260e9a87SYuri Pankov static void
810371584c2SYuri Pankov post_std(POST_ARGS)
81195c635efSGarrett D'Amore {
812371584c2SYuri Pankov 	struct roff_node *n;
81395c635efSGarrett D'Amore 
814371584c2SYuri Pankov 	n = mdoc->last;
815371584c2SYuri Pankov 	if (n->args && n->args->argc == 1)
816371584c2SYuri Pankov 		if (n->args->argv[0].arg == MDOC_Std)
817260e9a87SYuri Pankov 			return;
81895c635efSGarrett D'Amore 
819260e9a87SYuri Pankov 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
820260e9a87SYuri Pankov 	    n->line, n->pos, mdoc_macronames[n->tok]);
82195c635efSGarrett D'Amore }
82295c635efSGarrett D'Amore 
823*a40ea1a7SYuri Pankov static void
824*a40ea1a7SYuri Pankov post_st(POST_ARGS)
825*a40ea1a7SYuri Pankov {
826*a40ea1a7SYuri Pankov 	struct roff_node	 *n, *nch;
827*a40ea1a7SYuri Pankov 	const char		 *p;
828*a40ea1a7SYuri Pankov 
829*a40ea1a7SYuri Pankov 	n = mdoc->last;
830*a40ea1a7SYuri Pankov 	nch = n->child;
831*a40ea1a7SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
832*a40ea1a7SYuri Pankov 
833*a40ea1a7SYuri Pankov 	if ((p = mdoc_a2st(nch->string)) == NULL) {
834*a40ea1a7SYuri Pankov 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
835*a40ea1a7SYuri Pankov 		    nch->line, nch->pos, "St %s", nch->string);
836*a40ea1a7SYuri Pankov 		roff_node_delete(mdoc, n);
837*a40ea1a7SYuri Pankov 		return;
838*a40ea1a7SYuri Pankov 	}
839*a40ea1a7SYuri Pankov 
840*a40ea1a7SYuri Pankov 	nch->flags |= NODE_NOPRT;
841*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
842*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, nch->line, nch->pos, p);
843*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
844*a40ea1a7SYuri Pankov 	mdoc->last= n;
845*a40ea1a7SYuri Pankov }
846*a40ea1a7SYuri Pankov 
847260e9a87SYuri Pankov static void
848371584c2SYuri Pankov post_obsolete(POST_ARGS)
84995c635efSGarrett D'Amore {
850371584c2SYuri Pankov 	struct roff_node *n;
85195c635efSGarrett D'Amore 
852371584c2SYuri Pankov 	n = mdoc->last;
853371584c2SYuri Pankov 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
854260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
855260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
856260e9a87SYuri Pankov }
85795c635efSGarrett D'Amore 
858*a40ea1a7SYuri Pankov /*
859*a40ea1a7SYuri Pankov  * Block macros.
860*a40ea1a7SYuri Pankov  */
861*a40ea1a7SYuri Pankov 
862260e9a87SYuri Pankov static void
86395c635efSGarrett D'Amore post_bf(POST_ARGS)
86495c635efSGarrett D'Amore {
865371584c2SYuri Pankov 	struct roff_node *np, *nch;
86695c635efSGarrett D'Amore 
86795c635efSGarrett D'Amore 	/*
86895c635efSGarrett D'Amore 	 * Unlike other data pointers, these are "housed" by the HEAD
86995c635efSGarrett D'Amore 	 * element, which contains the goods.
87095c635efSGarrett D'Amore 	 */
87195c635efSGarrett D'Amore 
87295c635efSGarrett D'Amore 	np = mdoc->last;
873371584c2SYuri Pankov 	if (np->type != ROFFT_HEAD)
874260e9a87SYuri Pankov 		return;
875260e9a87SYuri Pankov 
876371584c2SYuri Pankov 	assert(np->parent->type == ROFFT_BLOCK);
877371584c2SYuri Pankov 	assert(np->parent->tok == MDOC_Bf);
87895c635efSGarrett D'Amore 
879260e9a87SYuri Pankov 	/* Check the number of arguments. */
88095c635efSGarrett D'Amore 
881260e9a87SYuri Pankov 	nch = np->child;
882371584c2SYuri Pankov 	if (np->parent->args == NULL) {
883371584c2SYuri Pankov 		if (nch == NULL) {
884260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
885260e9a87SYuri Pankov 			    np->line, np->pos, "Bf");
886260e9a87SYuri Pankov 			return;
887260e9a87SYuri Pankov 		}
888260e9a87SYuri Pankov 		nch = nch->next;
88995c635efSGarrett D'Amore 	}
890371584c2SYuri Pankov 	if (nch != NULL)
891260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
892260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bf ... %s", nch->string);
89395c635efSGarrett D'Amore 
89495c635efSGarrett D'Amore 	/* Extract argument into data. */
895260e9a87SYuri Pankov 
896371584c2SYuri Pankov 	if (np->parent->args != NULL) {
897371584c2SYuri Pankov 		switch (np->parent->args->argv[0].arg) {
898371584c2SYuri Pankov 		case MDOC_Emphasis:
89995c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Em;
900371584c2SYuri Pankov 			break;
901371584c2SYuri Pankov 		case MDOC_Literal:
90295c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Li;
903371584c2SYuri Pankov 			break;
904371584c2SYuri Pankov 		case MDOC_Symbolic:
90595c635efSGarrett D'Amore 			np->norm->Bf.font = FONT_Sy;
906371584c2SYuri Pankov 			break;
907371584c2SYuri Pankov 		default:
90895c635efSGarrett D'Amore 			abort();
909371584c2SYuri Pankov 		}
910260e9a87SYuri Pankov 		return;
91195c635efSGarrett D'Amore 	}
91295c635efSGarrett D'Amore 
91395c635efSGarrett D'Amore 	/* Extract parameter into data. */
91495c635efSGarrett D'Amore 
915371584c2SYuri Pankov 	if ( ! strcmp(np->child->string, "Em"))
91695c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Em;
917371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Li"))
91895c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Li;
919371584c2SYuri Pankov 	else if ( ! strcmp(np->child->string, "Sy"))
92095c635efSGarrett D'Amore 		np->norm->Bf.font = FONT_Sy;
921260e9a87SYuri Pankov 	else
922260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
923260e9a87SYuri Pankov 		    np->child->line, np->child->pos,
924260e9a87SYuri Pankov 		    "Bf %s", np->child->string);
92595c635efSGarrett D'Amore }
92695c635efSGarrett D'Amore 
927260e9a87SYuri Pankov static void
928260e9a87SYuri Pankov post_fname(POST_ARGS)
929260e9a87SYuri Pankov {
930371584c2SYuri Pankov 	const struct roff_node	*n;
931260e9a87SYuri Pankov 	const char		*cp;
932260e9a87SYuri Pankov 	size_t			 pos;
93395c635efSGarrett D'Amore 
934260e9a87SYuri Pankov 	n = mdoc->last->child;
935260e9a87SYuri Pankov 	pos = strcspn(n->string, "()");
936260e9a87SYuri Pankov 	cp = n->string + pos;
937260e9a87SYuri Pankov 	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
938260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
939260e9a87SYuri Pankov 		    n->line, n->pos + pos, n->string);
94095c635efSGarrett D'Amore }
94195c635efSGarrett D'Amore 
942260e9a87SYuri Pankov static void
943260e9a87SYuri Pankov post_fn(POST_ARGS)
94495c635efSGarrett D'Amore {
94595c635efSGarrett D'Amore 
946260e9a87SYuri Pankov 	post_fname(mdoc);
947260e9a87SYuri Pankov 	post_fa(mdoc);
94895c635efSGarrett D'Amore }
94995c635efSGarrett D'Amore 
950260e9a87SYuri Pankov static void
951260e9a87SYuri Pankov post_fo(POST_ARGS)
952260e9a87SYuri Pankov {
953371584c2SYuri Pankov 	const struct roff_node	*n;
95495c635efSGarrett D'Amore 
955260e9a87SYuri Pankov 	n = mdoc->last;
956260e9a87SYuri Pankov 
957371584c2SYuri Pankov 	if (n->type != ROFFT_HEAD)
958260e9a87SYuri Pankov 		return;
959260e9a87SYuri Pankov 
960260e9a87SYuri Pankov 	if (n->child == NULL) {
961260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
962260e9a87SYuri Pankov 		    n->line, n->pos, "Fo");
963260e9a87SYuri Pankov 		return;
964260e9a87SYuri Pankov 	}
965260e9a87SYuri Pankov 	if (n->child != n->last) {
966260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
967260e9a87SYuri Pankov 		    n->child->next->line, n->child->next->pos,
968260e9a87SYuri Pankov 		    "Fo ... %s", n->child->next->string);
969260e9a87SYuri Pankov 		while (n->child != n->last)
970371584c2SYuri Pankov 			roff_node_delete(mdoc, n->last);
971260e9a87SYuri Pankov 	}
972260e9a87SYuri Pankov 
973260e9a87SYuri Pankov 	post_fname(mdoc);
974260e9a87SYuri Pankov }
975260e9a87SYuri Pankov 
976260e9a87SYuri Pankov static void
977260e9a87SYuri Pankov post_fa(POST_ARGS)
978260e9a87SYuri Pankov {
979371584c2SYuri Pankov 	const struct roff_node *n;
980260e9a87SYuri Pankov 	const char *cp;
981260e9a87SYuri Pankov 
982260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
983260e9a87SYuri Pankov 		for (cp = n->string; *cp != '\0'; cp++) {
984260e9a87SYuri Pankov 			/* Ignore callbacks and alterations. */
985260e9a87SYuri Pankov 			if (*cp == '(' || *cp == '{')
986260e9a87SYuri Pankov 				break;
987260e9a87SYuri Pankov 			if (*cp != ',')
988260e9a87SYuri Pankov 				continue;
989260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
990260e9a87SYuri Pankov 			    n->line, n->pos + (cp - n->string),
991260e9a87SYuri Pankov 			    n->string);
992260e9a87SYuri Pankov 			break;
993260e9a87SYuri Pankov 		}
994260e9a87SYuri Pankov 	}
995260e9a87SYuri Pankov }
996260e9a87SYuri Pankov 
997260e9a87SYuri Pankov static void
99895c635efSGarrett D'Amore post_nm(POST_ARGS)
99995c635efSGarrett D'Amore {
1000371584c2SYuri Pankov 	struct roff_node	*n;
1001260e9a87SYuri Pankov 
1002260e9a87SYuri Pankov 	n = mdoc->last;
1003260e9a87SYuri Pankov 
1004260e9a87SYuri Pankov 	if (n->last != NULL &&
1005260e9a87SYuri Pankov 	    (n->last->tok == MDOC_Pp ||
1006260e9a87SYuri Pankov 	     n->last->tok == MDOC_Lp))
1007260e9a87SYuri Pankov 		mdoc_node_relink(mdoc, n->last);
100895c635efSGarrett D'Amore 
1009371584c2SYuri Pankov 	if (mdoc->meta.name == NULL)
1010*a40ea1a7SYuri Pankov 		deroff(&mdoc->meta.name, n);
1011*a40ea1a7SYuri Pankov 
1012*a40ea1a7SYuri Pankov 	if (mdoc->meta.name == NULL ||
1013*a40ea1a7SYuri Pankov 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1014260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1015260e9a87SYuri Pankov 		    n->line, n->pos, "Nm");
1016*a40ea1a7SYuri Pankov 
1017*a40ea1a7SYuri Pankov 	if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) ||
1018*a40ea1a7SYuri Pankov 	    (n->child != NULL && n->child->type == ROFFT_TEXT) ||
1019*a40ea1a7SYuri Pankov 	    mdoc->meta.name == NULL)
1020*a40ea1a7SYuri Pankov 		return;
1021*a40ea1a7SYuri Pankov 
1022*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1023*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1024*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1025*a40ea1a7SYuri Pankov 	mdoc->last = n;
102695c635efSGarrett D'Amore }
102795c635efSGarrett D'Amore 
1028260e9a87SYuri Pankov static void
1029260e9a87SYuri Pankov post_nd(POST_ARGS)
1030260e9a87SYuri Pankov {
1031371584c2SYuri Pankov 	struct roff_node	*n;
1032260e9a87SYuri Pankov 
1033260e9a87SYuri Pankov 	n = mdoc->last;
1034260e9a87SYuri Pankov 
1035371584c2SYuri Pankov 	if (n->type != ROFFT_BODY)
1036260e9a87SYuri Pankov 		return;
1037260e9a87SYuri Pankov 
1038260e9a87SYuri Pankov 	if (n->child == NULL)
1039260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1040260e9a87SYuri Pankov 		    n->line, n->pos, "Nd");
1041260e9a87SYuri Pankov 
1042260e9a87SYuri Pankov 	post_hyph(mdoc);
1043260e9a87SYuri Pankov }
1044260e9a87SYuri Pankov 
1045260e9a87SYuri Pankov static void
1046371584c2SYuri Pankov post_display(POST_ARGS)
1047260e9a87SYuri Pankov {
1048371584c2SYuri Pankov 	struct roff_node *n, *np;
1049260e9a87SYuri Pankov 
1050260e9a87SYuri Pankov 	n = mdoc->last;
1051371584c2SYuri Pankov 	switch (n->type) {
1052371584c2SYuri Pankov 	case ROFFT_BODY:
1053*a40ea1a7SYuri Pankov 		if (n->end != ENDBODY_NOT) {
1054*a40ea1a7SYuri Pankov 			if (n->tok == MDOC_Bd &&
1055*a40ea1a7SYuri Pankov 			    n->body->parent->args == NULL)
1056*a40ea1a7SYuri Pankov 				roff_node_delete(mdoc, n);
1057*a40ea1a7SYuri Pankov 		} else if (n->child == NULL)
1058371584c2SYuri Pankov 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1059371584c2SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
1060371584c2SYuri Pankov 		else if (n->tok == MDOC_D1)
1061371584c2SYuri Pankov 			post_hyph(mdoc);
1062371584c2SYuri Pankov 		break;
1063371584c2SYuri Pankov 	case ROFFT_BLOCK:
1064371584c2SYuri Pankov 		if (n->tok == MDOC_Bd) {
1065371584c2SYuri Pankov 			if (n->args == NULL) {
1066371584c2SYuri Pankov 				mandoc_msg(MANDOCERR_BD_NOARG,
1067371584c2SYuri Pankov 				    mdoc->parse, n->line, n->pos, "Bd");
1068371584c2SYuri Pankov 				mdoc->next = ROFF_NEXT_SIBLING;
1069371584c2SYuri Pankov 				while (n->body->child != NULL)
1070371584c2SYuri Pankov 					mdoc_node_relink(mdoc,
1071371584c2SYuri Pankov 					    n->body->child);
1072371584c2SYuri Pankov 				roff_node_delete(mdoc, n);
1073371584c2SYuri Pankov 				break;
1074371584c2SYuri Pankov 			}
1075371584c2SYuri Pankov 			post_bd(mdoc);
1076371584c2SYuri Pankov 			post_prevpar(mdoc);
1077371584c2SYuri Pankov 		}
1078371584c2SYuri Pankov 		for (np = n->parent; np != NULL; np = np->parent) {
1079371584c2SYuri Pankov 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1080371584c2SYuri Pankov 				mandoc_vmsg(MANDOCERR_BD_NEST,
1081371584c2SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1082371584c2SYuri Pankov 				    "%s in Bd", mdoc_macronames[n->tok]);
1083371584c2SYuri Pankov 				break;
1084371584c2SYuri Pankov 			}
1085371584c2SYuri Pankov 		}
1086371584c2SYuri Pankov 		break;
1087371584c2SYuri Pankov 	default:
1088371584c2SYuri Pankov 		break;
1089371584c2SYuri Pankov 	}
109095c635efSGarrett D'Amore }
109195c635efSGarrett D'Amore 
1092260e9a87SYuri Pankov static void
109395c635efSGarrett D'Amore post_defaults(POST_ARGS)
109495c635efSGarrett D'Amore {
1095371584c2SYuri Pankov 	struct roff_node *nn;
109695c635efSGarrett D'Amore 
109795c635efSGarrett D'Amore 	/*
109895c635efSGarrett D'Amore 	 * The `Ar' defaults to "file ..." if no value is provided as an
109995c635efSGarrett D'Amore 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
110095c635efSGarrett D'Amore 	 * gets an empty string.
110195c635efSGarrett D'Amore 	 */
110295c635efSGarrett D'Amore 
1103371584c2SYuri Pankov 	if (mdoc->last->child != NULL)
1104260e9a87SYuri Pankov 		return;
1105260e9a87SYuri Pankov 
110695c635efSGarrett D'Amore 	nn = mdoc->last;
110795c635efSGarrett D'Amore 
110895c635efSGarrett D'Amore 	switch (nn->tok) {
1109260e9a87SYuri Pankov 	case MDOC_Ar:
1110371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
1111371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1112*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
1113371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1114*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
111595c635efSGarrett D'Amore 		break;
1116260e9a87SYuri Pankov 	case MDOC_Pa:
1117260e9a87SYuri Pankov 	case MDOC_Mt:
1118371584c2SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
1119371584c2SYuri Pankov 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1120*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
112195c635efSGarrett D'Amore 		break;
112295c635efSGarrett D'Amore 	default:
112395c635efSGarrett D'Amore 		abort();
1124260e9a87SYuri Pankov 	}
112595c635efSGarrett D'Amore 	mdoc->last = nn;
112695c635efSGarrett D'Amore }
112795c635efSGarrett D'Amore 
1128260e9a87SYuri Pankov static void
112995c635efSGarrett D'Amore post_at(POST_ARGS)
113095c635efSGarrett D'Amore {
1131*a40ea1a7SYuri Pankov 	struct roff_node	*n, *nch;
1132*a40ea1a7SYuri Pankov 	const char		*att;
1133260e9a87SYuri Pankov 
1134260e9a87SYuri Pankov 	n = mdoc->last;
1135*a40ea1a7SYuri Pankov 	nch = n->child;
113695c635efSGarrett D'Amore 
113795c635efSGarrett D'Amore 	/*
113895c635efSGarrett D'Amore 	 * If we have a child, look it up in the standard keys.  If a
113995c635efSGarrett D'Amore 	 * key exist, use that instead of the child; if it doesn't,
114095c635efSGarrett D'Amore 	 * prefix "AT&T UNIX " to the existing data.
114195c635efSGarrett D'Amore 	 */
114295c635efSGarrett D'Amore 
1143*a40ea1a7SYuri Pankov 	att = NULL;
1144*a40ea1a7SYuri Pankov 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1145260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1146*a40ea1a7SYuri Pankov 		    nch->line, nch->pos, "At %s", nch->string);
114795c635efSGarrett D'Amore 
1148*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1149*a40ea1a7SYuri Pankov 	if (att != NULL) {
1150*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
1151*a40ea1a7SYuri Pankov 		nch->flags |= NODE_NOPRT;
1152*a40ea1a7SYuri Pankov 	} else
1153*a40ea1a7SYuri Pankov 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1154*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1155*a40ea1a7SYuri Pankov 	mdoc->last = n;
115695c635efSGarrett D'Amore }
115795c635efSGarrett D'Amore 
1158260e9a87SYuri Pankov static void
115995c635efSGarrett D'Amore post_an(POST_ARGS)
116095c635efSGarrett D'Amore {
1161371584c2SYuri Pankov 	struct roff_node *np, *nch;
1162371584c2SYuri Pankov 
1163371584c2SYuri Pankov 	post_an_norm(mdoc);
116495c635efSGarrett D'Amore 
116595c635efSGarrett D'Amore 	np = mdoc->last;
1166260e9a87SYuri Pankov 	nch = np->child;
1167260e9a87SYuri Pankov 	if (np->norm->An.auth == AUTH__NONE) {
1168260e9a87SYuri Pankov 		if (nch == NULL)
1169260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1170260e9a87SYuri Pankov 			    np->line, np->pos, "An");
1171260e9a87SYuri Pankov 	} else if (nch != NULL)
1172260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1173260e9a87SYuri Pankov 		    nch->line, nch->pos, "An ... %s", nch->string);
1174260e9a87SYuri Pankov }
1175260e9a87SYuri Pankov 
1176260e9a87SYuri Pankov static void
1177260e9a87SYuri Pankov post_en(POST_ARGS)
1178260e9a87SYuri Pankov {
117995c635efSGarrett D'Amore 
1180371584c2SYuri Pankov 	post_obsolete(mdoc);
1181371584c2SYuri Pankov 	if (mdoc->last->type == ROFFT_BLOCK)
1182260e9a87SYuri Pankov 		mdoc->last->norm->Es = mdoc->last_es;
118395c635efSGarrett D'Amore }
118495c635efSGarrett D'Amore 
1185260e9a87SYuri Pankov static void
1186260e9a87SYuri Pankov post_es(POST_ARGS)
1187260e9a87SYuri Pankov {
118895c635efSGarrett D'Amore 
1189371584c2SYuri Pankov 	post_obsolete(mdoc);
1190260e9a87SYuri Pankov 	mdoc->last_es = mdoc->last;
1191260e9a87SYuri Pankov }
1192260e9a87SYuri Pankov 
1193*a40ea1a7SYuri Pankov static void
1194*a40ea1a7SYuri Pankov post_xx(POST_ARGS)
1195*a40ea1a7SYuri Pankov {
1196*a40ea1a7SYuri Pankov 	struct roff_node	*n;
1197*a40ea1a7SYuri Pankov 	const char		*os;
1198*a40ea1a7SYuri Pankov 
1199*a40ea1a7SYuri Pankov 	n = mdoc->last;
1200*a40ea1a7SYuri Pankov 	switch (n->tok) {
1201*a40ea1a7SYuri Pankov 	case MDOC_Bsx:
1202*a40ea1a7SYuri Pankov 		os = "BSD/OS";
1203*a40ea1a7SYuri Pankov 		break;
1204*a40ea1a7SYuri Pankov 	case MDOC_Dx:
1205*a40ea1a7SYuri Pankov 		os = "DragonFly";
1206*a40ea1a7SYuri Pankov 		break;
1207*a40ea1a7SYuri Pankov 	case MDOC_Fx:
1208*a40ea1a7SYuri Pankov 		os = "FreeBSD";
1209*a40ea1a7SYuri Pankov 		break;
1210*a40ea1a7SYuri Pankov 	case MDOC_Nx:
1211*a40ea1a7SYuri Pankov 		os = "NetBSD";
1212*a40ea1a7SYuri Pankov 		break;
1213*a40ea1a7SYuri Pankov 	case MDOC_Ox:
1214*a40ea1a7SYuri Pankov 		os = "OpenBSD";
1215*a40ea1a7SYuri Pankov 		break;
1216*a40ea1a7SYuri Pankov 	case MDOC_Ux:
1217*a40ea1a7SYuri Pankov 		os = "UNIX";
1218*a40ea1a7SYuri Pankov 		break;
1219*a40ea1a7SYuri Pankov 	default:
1220*a40ea1a7SYuri Pankov 		abort();
1221*a40ea1a7SYuri Pankov 	}
1222*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_CHILD;
1223*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, os);
1224*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
1225*a40ea1a7SYuri Pankov 	mdoc->last = n;
1226*a40ea1a7SYuri Pankov }
1227*a40ea1a7SYuri Pankov 
1228260e9a87SYuri Pankov static void
122995c635efSGarrett D'Amore post_it(POST_ARGS)
123095c635efSGarrett D'Amore {
1231371584c2SYuri Pankov 	struct roff_node *nbl, *nit, *nch;
123295c635efSGarrett D'Amore 	int		  i, cols;
123395c635efSGarrett D'Amore 	enum mdoc_list	  lt;
1234371584c2SYuri Pankov 
1235371584c2SYuri Pankov 	post_prevpar(mdoc);
123695c635efSGarrett D'Amore 
1237260e9a87SYuri Pankov 	nit = mdoc->last;
1238371584c2SYuri Pankov 	if (nit->type != ROFFT_BLOCK)
1239260e9a87SYuri Pankov 		return;
124095c635efSGarrett D'Amore 
1241260e9a87SYuri Pankov 	nbl = nit->parent->parent;
1242260e9a87SYuri Pankov 	lt = nbl->norm->Bl.type;
124395c635efSGarrett D'Amore 
124495c635efSGarrett D'Amore 	switch (lt) {
1245260e9a87SYuri Pankov 	case LIST_tag:
1246260e9a87SYuri Pankov 	case LIST_hang:
1247260e9a87SYuri Pankov 	case LIST_ohang:
1248260e9a87SYuri Pankov 	case LIST_inset:
1249260e9a87SYuri Pankov 	case LIST_diag:
1250260e9a87SYuri Pankov 		if (nit->head->child == NULL)
1251260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1252260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1253260e9a87SYuri Pankov 			    "Bl -%s It",
1254260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
125595c635efSGarrett D'Amore 		break;
1256260e9a87SYuri Pankov 	case LIST_bullet:
1257260e9a87SYuri Pankov 	case LIST_dash:
1258260e9a87SYuri Pankov 	case LIST_enum:
1259260e9a87SYuri Pankov 	case LIST_hyphen:
1260260e9a87SYuri Pankov 		if (nit->body == NULL || nit->body->child == NULL)
1261260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1262260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1263260e9a87SYuri Pankov 			    "Bl -%s It",
1264260e9a87SYuri Pankov 			    mdoc_argnames[nbl->args->argv[0].arg]);
126595c635efSGarrett D'Amore 		/* FALLTHROUGH */
1266260e9a87SYuri Pankov 	case LIST_item:
1267*a40ea1a7SYuri Pankov 		if ((nch = nit->head->child) != NULL)
1268260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1269260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1270*a40ea1a7SYuri Pankov 			    "It %s", nch->string == NULL ?
1271*a40ea1a7SYuri Pankov 			    mdoc_macronames[nch->tok] : nch->string);
127295c635efSGarrett D'Amore 		break;
1273260e9a87SYuri Pankov 	case LIST_column:
1274260e9a87SYuri Pankov 		cols = (int)nbl->norm->Bl.ncols;
127595c635efSGarrett D'Amore 
1276260e9a87SYuri Pankov 		assert(nit->head->child == NULL);
127795c635efSGarrett D'Amore 
1278371584c2SYuri Pankov 		i = 0;
1279371584c2SYuri Pankov 		for (nch = nit->child; nch != NULL; nch = nch->next)
1280371584c2SYuri Pankov 			if (nch->type == ROFFT_BODY)
128195c635efSGarrett D'Amore 				i++;
128295c635efSGarrett D'Amore 
1283260e9a87SYuri Pankov 		if (i < cols || i > cols + 1)
1284260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_BL_COL,
1285260e9a87SYuri Pankov 			    mdoc->parse, nit->line, nit->pos,
1286260e9a87SYuri Pankov 			    "%d columns, %d cells", cols, i);
128795c635efSGarrett D'Amore 		break;
1288260e9a87SYuri Pankov 	default:
1289260e9a87SYuri Pankov 		abort();
129095c635efSGarrett D'Amore 	}
129195c635efSGarrett D'Amore }
129295c635efSGarrett D'Amore 
1293260e9a87SYuri Pankov static void
1294260e9a87SYuri Pankov post_bl_block(POST_ARGS)
129595c635efSGarrett D'Amore {
1296371584c2SYuri Pankov 	struct roff_node *n, *ni, *nc;
1297371584c2SYuri Pankov 
1298371584c2SYuri Pankov 	post_prevpar(mdoc);
129995c635efSGarrett D'Amore 
130095c635efSGarrett D'Amore 	n = mdoc->last;
1301371584c2SYuri Pankov 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1302371584c2SYuri Pankov 		if (ni->body == NULL)
1303698f87a4SGarrett D'Amore 			continue;
1304698f87a4SGarrett D'Amore 		nc = ni->body->last;
1305371584c2SYuri Pankov 		while (nc != NULL) {
1306698f87a4SGarrett D'Amore 			switch (nc->tok) {
1307260e9a87SYuri Pankov 			case MDOC_Pp:
1308260e9a87SYuri Pankov 			case MDOC_Lp:
1309260e9a87SYuri Pankov 			case MDOC_br:
1310698f87a4SGarrett D'Amore 				break;
1311698f87a4SGarrett D'Amore 			default:
1312698f87a4SGarrett D'Amore 				nc = NULL;
1313698f87a4SGarrett D'Amore 				continue;
1314698f87a4SGarrett D'Amore 			}
1315371584c2SYuri Pankov 			if (ni->next == NULL) {
1316260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_PAR_MOVE,
1317260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1318260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1319260e9a87SYuri Pankov 				mdoc_node_relink(mdoc, nc);
1320371584c2SYuri Pankov 			} else if (n->norm->Bl.comp == 0 &&
1321371584c2SYuri Pankov 			    n->norm->Bl.type != LIST_column) {
1322260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1323260e9a87SYuri Pankov 				    mdoc->parse, nc->line, nc->pos,
1324260e9a87SYuri Pankov 				    "%s before It",
1325260e9a87SYuri Pankov 				    mdoc_macronames[nc->tok]);
1326371584c2SYuri Pankov 				roff_node_delete(mdoc, nc);
1327698f87a4SGarrett D'Amore 			} else
1328698f87a4SGarrett D'Amore 				break;
1329698f87a4SGarrett D'Amore 			nc = ni->body->last;
1330698f87a4SGarrett D'Amore 		}
1331698f87a4SGarrett D'Amore 	}
133295c635efSGarrett D'Amore }
133395c635efSGarrett D'Amore 
1334260e9a87SYuri Pankov /*
1335260e9a87SYuri Pankov  * If the argument of -offset or -width is a macro,
1336260e9a87SYuri Pankov  * replace it with the associated default width.
1337260e9a87SYuri Pankov  */
1338260e9a87SYuri Pankov void
1339260e9a87SYuri Pankov rewrite_macro2len(char **arg)
134095c635efSGarrett D'Amore {
134195c635efSGarrett D'Amore 	size_t		  width;
1342371584c2SYuri Pankov 	int		  tok;
134395c635efSGarrett D'Amore 
1344260e9a87SYuri Pankov 	if (*arg == NULL)
1345260e9a87SYuri Pankov 		return;
1346260e9a87SYuri Pankov 	else if ( ! strcmp(*arg, "Ds"))
134795c635efSGarrett D'Amore 		width = 6;
1348371584c2SYuri Pankov 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1349260e9a87SYuri Pankov 		return;
1350260e9a87SYuri Pankov 	else
1351260e9a87SYuri Pankov 		width = macro2len(tok);
135295c635efSGarrett D'Amore 
1353260e9a87SYuri Pankov 	free(*arg);
1354260e9a87SYuri Pankov 	mandoc_asprintf(arg, "%zun", width);
135595c635efSGarrett D'Amore }
135695c635efSGarrett D'Amore 
1357260e9a87SYuri Pankov static void
1358260e9a87SYuri Pankov post_bl_head(POST_ARGS)
135995c635efSGarrett D'Amore {
1360371584c2SYuri Pankov 	struct roff_node *nbl, *nh, *nch, *nnext;
1361260e9a87SYuri Pankov 	struct mdoc_argv *argv;
136295c635efSGarrett D'Amore 	int		  i, j;
136395c635efSGarrett D'Amore 
1364371584c2SYuri Pankov 	post_bl_norm(mdoc);
1365260e9a87SYuri Pankov 
1366371584c2SYuri Pankov 	nh = mdoc->last;
1367260e9a87SYuri Pankov 	if (nh->norm->Bl.type != LIST_column) {
1368260e9a87SYuri Pankov 		if ((nch = nh->child) == NULL)
1369260e9a87SYuri Pankov 			return;
1370260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1371260e9a87SYuri Pankov 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1372260e9a87SYuri Pankov 		while (nch != NULL) {
1373371584c2SYuri Pankov 			roff_node_delete(mdoc, nch);
1374260e9a87SYuri Pankov 			nch = nh->child;
1375260e9a87SYuri Pankov 		}
1376260e9a87SYuri Pankov 		return;
1377260e9a87SYuri Pankov 	}
137895c635efSGarrett D'Amore 
137995c635efSGarrett D'Amore 	/*
1380260e9a87SYuri Pankov 	 * Append old-style lists, where the column width specifiers
138195c635efSGarrett D'Amore 	 * trail as macro parameters, to the new-style ("normal-form")
138295c635efSGarrett D'Amore 	 * lists where they're argument values following -column.
138395c635efSGarrett D'Amore 	 */
138495c635efSGarrett D'Amore 
1385260e9a87SYuri Pankov 	if (nh->child == NULL)
1386260e9a87SYuri Pankov 		return;
138795c635efSGarrett D'Amore 
1388260e9a87SYuri Pankov 	nbl = nh->parent;
1389260e9a87SYuri Pankov 	for (j = 0; j < (int)nbl->args->argc; j++)
1390260e9a87SYuri Pankov 		if (nbl->args->argv[j].arg == MDOC_Column)
139195c635efSGarrett D'Amore 			break;
139295c635efSGarrett D'Amore 
1393260e9a87SYuri Pankov 	assert(j < (int)nbl->args->argc);
139495c635efSGarrett D'Amore 
139595c635efSGarrett D'Amore 	/*
139695c635efSGarrett D'Amore 	 * Accommodate for new-style groff column syntax.  Shuffle the
139795c635efSGarrett D'Amore 	 * child nodes, all of which must be TEXT, as arguments for the
139895c635efSGarrett D'Amore 	 * column field.  Then, delete the head children.
139995c635efSGarrett D'Amore 	 */
140095c635efSGarrett D'Amore 
1401260e9a87SYuri Pankov 	argv = nbl->args->argv + j;
1402260e9a87SYuri Pankov 	i = argv->sz;
1403371584c2SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nch->next)
1404371584c2SYuri Pankov 		argv->sz++;
1405260e9a87SYuri Pankov 	argv->value = mandoc_reallocarray(argv->value,
1406260e9a87SYuri Pankov 	    argv->sz, sizeof(char *));
140795c635efSGarrett D'Amore 
1408260e9a87SYuri Pankov 	nh->norm->Bl.ncols = argv->sz;
1409260e9a87SYuri Pankov 	nh->norm->Bl.cols = (void *)argv->value;
141095c635efSGarrett D'Amore 
1411260e9a87SYuri Pankov 	for (nch = nh->child; nch != NULL; nch = nnext) {
1412260e9a87SYuri Pankov 		argv->value[i++] = nch->string;
1413260e9a87SYuri Pankov 		nch->string = NULL;
1414260e9a87SYuri Pankov 		nnext = nch->next;
1415371584c2SYuri Pankov 		roff_node_delete(NULL, nch);
141695c635efSGarrett D'Amore 	}
1417260e9a87SYuri Pankov 	nh->child = NULL;
141895c635efSGarrett D'Amore }
141995c635efSGarrett D'Amore 
1420260e9a87SYuri Pankov static void
142195c635efSGarrett D'Amore post_bl(POST_ARGS)
142295c635efSGarrett D'Amore {
1423371584c2SYuri Pankov 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1424371584c2SYuri Pankov 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1425371584c2SYuri Pankov 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
142695c635efSGarrett D'Amore 
1427698f87a4SGarrett D'Amore 	nbody = mdoc->last;
1428698f87a4SGarrett D'Amore 	switch (nbody->type) {
1429371584c2SYuri Pankov 	case ROFFT_BLOCK:
1430260e9a87SYuri Pankov 		post_bl_block(mdoc);
1431260e9a87SYuri Pankov 		return;
1432371584c2SYuri Pankov 	case ROFFT_HEAD:
1433260e9a87SYuri Pankov 		post_bl_head(mdoc);
1434260e9a87SYuri Pankov 		return;
1435371584c2SYuri Pankov 	case ROFFT_BODY:
1436698f87a4SGarrett D'Amore 		break;
1437698f87a4SGarrett D'Amore 	default:
1438260e9a87SYuri Pankov 		return;
1439698f87a4SGarrett D'Amore 	}
1440371584c2SYuri Pankov 	if (nbody->end != ENDBODY_NOT)
1441371584c2SYuri Pankov 		return;
144295c635efSGarrett D'Amore 
1443698f87a4SGarrett D'Amore 	nchild = nbody->child;
1444260e9a87SYuri Pankov 	if (nchild == NULL) {
1445260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1446260e9a87SYuri Pankov 		    nbody->line, nbody->pos, "Bl");
1447260e9a87SYuri Pankov 		return;
1448260e9a87SYuri Pankov 	}
1449260e9a87SYuri Pankov 	while (nchild != NULL) {
1450*a40ea1a7SYuri Pankov 		nnext = nchild->next;
1451260e9a87SYuri Pankov 		if (nchild->tok == MDOC_It ||
1452260e9a87SYuri Pankov 		    (nchild->tok == MDOC_Sm &&
1453*a40ea1a7SYuri Pankov 		     nnext != NULL && nnext->tok == MDOC_It)) {
1454*a40ea1a7SYuri Pankov 			nchild = nnext;
1455*a40ea1a7SYuri Pankov 			continue;
1456*a40ea1a7SYuri Pankov 		}
1457*a40ea1a7SYuri Pankov 
1458*a40ea1a7SYuri Pankov 		/*
1459*a40ea1a7SYuri Pankov 		 * In .Bl -column, the first rows may be implicit,
1460*a40ea1a7SYuri Pankov 		 * that is, they may not start with .It macros.
1461*a40ea1a7SYuri Pankov 		 * Such rows may be followed by nodes generated on the
1462*a40ea1a7SYuri Pankov 		 * roff level, for example .TS, which cannot be moved
1463*a40ea1a7SYuri Pankov 		 * out of the list.  In that case, wrap such roff nodes
1464*a40ea1a7SYuri Pankov 		 * into an implicit row.
1465*a40ea1a7SYuri Pankov 		 */
1466*a40ea1a7SYuri Pankov 
1467*a40ea1a7SYuri Pankov 		if (nchild->prev != NULL) {
1468*a40ea1a7SYuri Pankov 			mdoc->last = nchild;
1469*a40ea1a7SYuri Pankov 			mdoc->next = ROFF_NEXT_SIBLING;
1470*a40ea1a7SYuri Pankov 			roff_block_alloc(mdoc, nchild->line,
1471*a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1472*a40ea1a7SYuri Pankov 			roff_head_alloc(mdoc, nchild->line,
1473*a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1474*a40ea1a7SYuri Pankov 			mdoc->next = ROFF_NEXT_SIBLING;
1475*a40ea1a7SYuri Pankov 			roff_body_alloc(mdoc, nchild->line,
1476*a40ea1a7SYuri Pankov 			    nchild->pos, MDOC_It);
1477*a40ea1a7SYuri Pankov 			while (nchild->tok != MDOC_It) {
1478*a40ea1a7SYuri Pankov 				mdoc_node_relink(mdoc, nchild);
1479*a40ea1a7SYuri Pankov 				if ((nchild = nnext) == NULL)
1480*a40ea1a7SYuri Pankov 					break;
1481*a40ea1a7SYuri Pankov 				nnext = nchild->next;
1482*a40ea1a7SYuri Pankov 				mdoc->next = ROFF_NEXT_SIBLING;
1483*a40ea1a7SYuri Pankov 			}
1484*a40ea1a7SYuri Pankov 			mdoc->last = nbody;
148595c635efSGarrett D'Amore 			continue;
148695c635efSGarrett D'Amore 		}
148795c635efSGarrett D'Amore 
1488260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1489260e9a87SYuri Pankov 		    nchild->line, nchild->pos,
1490260e9a87SYuri Pankov 		    mdoc_macronames[nchild->tok]);
1491698f87a4SGarrett D'Amore 
1492698f87a4SGarrett D'Amore 		/*
1493698f87a4SGarrett D'Amore 		 * Move the node out of the Bl block.
1494698f87a4SGarrett D'Amore 		 * First, collect all required node pointers.
1495698f87a4SGarrett D'Amore 		 */
1496698f87a4SGarrett D'Amore 
1497698f87a4SGarrett D'Amore 		nblock  = nbody->parent;
1498698f87a4SGarrett D'Amore 		nprev   = nblock->prev;
1499698f87a4SGarrett D'Amore 		nparent = nblock->parent;
1500698f87a4SGarrett D'Amore 
1501698f87a4SGarrett D'Amore 		/*
1502698f87a4SGarrett D'Amore 		 * Unlink this child.
1503698f87a4SGarrett D'Amore 		 */
1504698f87a4SGarrett D'Amore 
1505371584c2SYuri Pankov 		nbody->child = nnext;
1506371584c2SYuri Pankov 		if (nnext == NULL)
1507698f87a4SGarrett D'Amore 			nbody->last  = NULL;
1508371584c2SYuri Pankov 		else
1509698f87a4SGarrett D'Amore 			nnext->prev = NULL;
1510698f87a4SGarrett D'Amore 
1511698f87a4SGarrett D'Amore 		/*
1512698f87a4SGarrett D'Amore 		 * Relink this child.
1513698f87a4SGarrett D'Amore 		 */
1514698f87a4SGarrett D'Amore 
1515698f87a4SGarrett D'Amore 		nchild->parent = nparent;
1516698f87a4SGarrett D'Amore 		nchild->prev   = nprev;
1517698f87a4SGarrett D'Amore 		nchild->next   = nblock;
1518698f87a4SGarrett D'Amore 
1519698f87a4SGarrett D'Amore 		nblock->prev = nchild;
1520371584c2SYuri Pankov 		if (nprev == NULL)
1521698f87a4SGarrett D'Amore 			nparent->child = nchild;
1522698f87a4SGarrett D'Amore 		else
1523698f87a4SGarrett D'Amore 			nprev->next = nchild;
1524698f87a4SGarrett D'Amore 
1525698f87a4SGarrett D'Amore 		nchild = nnext;
152695c635efSGarrett D'Amore 	}
1527260e9a87SYuri Pankov }
1528260e9a87SYuri Pankov 
1529260e9a87SYuri Pankov static void
1530260e9a87SYuri Pankov post_bk(POST_ARGS)
1531260e9a87SYuri Pankov {
1532371584c2SYuri Pankov 	struct roff_node	*n;
1533260e9a87SYuri Pankov 
1534260e9a87SYuri Pankov 	n = mdoc->last;
153595c635efSGarrett D'Amore 
1536371584c2SYuri Pankov 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1537260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1538260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Bk");
1539371584c2SYuri Pankov 		roff_node_delete(mdoc, n);
1540260e9a87SYuri Pankov 	}
154195c635efSGarrett D'Amore }
154295c635efSGarrett D'Amore 
1543260e9a87SYuri Pankov static void
1544371584c2SYuri Pankov post_sm(POST_ARGS)
154595c635efSGarrett D'Amore {
1546371584c2SYuri Pankov 	struct roff_node	*nch;
154795c635efSGarrett D'Amore 
1548260e9a87SYuri Pankov 	nch = mdoc->last->child;
1549260e9a87SYuri Pankov 
1550260e9a87SYuri Pankov 	if (nch == NULL) {
1551260e9a87SYuri Pankov 		mdoc->flags ^= MDOC_SMOFF;
1552260e9a87SYuri Pankov 		return;
155395c635efSGarrett D'Amore 	}
155495c635efSGarrett D'Amore 
1555371584c2SYuri Pankov 	assert(nch->type == ROFFT_TEXT);
155695c635efSGarrett D'Amore 
1557260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "on")) {
1558260e9a87SYuri Pankov 		mdoc->flags &= ~MDOC_SMOFF;
1559260e9a87SYuri Pankov 		return;
1560698f87a4SGarrett D'Amore 	}
1561260e9a87SYuri Pankov 	if ( ! strcmp(nch->string, "off")) {
1562260e9a87SYuri Pankov 		mdoc->flags |= MDOC_SMOFF;
1563260e9a87SYuri Pankov 		return;
1564698f87a4SGarrett D'Amore 	}
156595c635efSGarrett D'Amore 
1566260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_SM_BAD,
1567260e9a87SYuri Pankov 	    mdoc->parse, nch->line, nch->pos,
1568260e9a87SYuri Pankov 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1569260e9a87SYuri Pankov 	mdoc_node_relink(mdoc, nch);
1570260e9a87SYuri Pankov 	return;
157195c635efSGarrett D'Amore }
157295c635efSGarrett D'Amore 
1573260e9a87SYuri Pankov static void
157495c635efSGarrett D'Amore post_root(POST_ARGS)
157595c635efSGarrett D'Amore {
1576371584c2SYuri Pankov 	struct roff_node *n;
157795c635efSGarrett D'Amore 
1578260e9a87SYuri Pankov 	/* Add missing prologue data. */
157995c635efSGarrett D'Amore 
1580260e9a87SYuri Pankov 	if (mdoc->meta.date == NULL)
1581260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ?
1582260e9a87SYuri Pankov 		    mandoc_strdup("") :
1583260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
158495c635efSGarrett D'Amore 
1585260e9a87SYuri Pankov 	if (mdoc->meta.title == NULL) {
1586260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1587260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, "EOF");
1588260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
158995c635efSGarrett D'Amore 	}
159095c635efSGarrett D'Amore 
1591260e9a87SYuri Pankov 	if (mdoc->meta.vol == NULL)
1592260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup("LOCAL");
159395c635efSGarrett D'Amore 
1594260e9a87SYuri Pankov 	if (mdoc->meta.os == NULL) {
1595260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_OS_MISSING,
1596260e9a87SYuri Pankov 		    mdoc->parse, 0, 0, NULL);
1597260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup("");
159895c635efSGarrett D'Amore 	}
159995c635efSGarrett D'Amore 
1600260e9a87SYuri Pankov 	/* Check that we begin with a proper `Sh'. */
1601260e9a87SYuri Pankov 
1602260e9a87SYuri Pankov 	n = mdoc->first->child;
1603371584c2SYuri Pankov 	while (n != NULL && n->tok != TOKEN_NONE &&
1604371584c2SYuri Pankov 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1605260e9a87SYuri Pankov 		n = n->next;
1606260e9a87SYuri Pankov 
1607260e9a87SYuri Pankov 	if (n == NULL)
1608260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1609260e9a87SYuri Pankov 	else if (n->tok != MDOC_Sh)
1610260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1611260e9a87SYuri Pankov 		    n->line, n->pos, mdoc_macronames[n->tok]);
161295c635efSGarrett D'Amore }
161395c635efSGarrett D'Amore 
1614260e9a87SYuri Pankov static void
161595c635efSGarrett D'Amore post_rs(POST_ARGS)
161695c635efSGarrett D'Amore {
1617371584c2SYuri Pankov 	struct roff_node *np, *nch, *next, *prev;
161895c635efSGarrett D'Amore 	int		  i, j;
161995c635efSGarrett D'Amore 
1620260e9a87SYuri Pankov 	np = mdoc->last;
162195c635efSGarrett D'Amore 
1622371584c2SYuri Pankov 	if (np->type != ROFFT_BODY)
1623260e9a87SYuri Pankov 		return;
162495c635efSGarrett D'Amore 
1625260e9a87SYuri Pankov 	if (np->child == NULL) {
1626260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1627260e9a87SYuri Pankov 		    np->line, np->pos, "Rs");
1628260e9a87SYuri Pankov 		return;
162995c635efSGarrett D'Amore 	}
163095c635efSGarrett D'Amore 
163195c635efSGarrett D'Amore 	/*
163295c635efSGarrett D'Amore 	 * The full `Rs' block needs special handling to order the
163395c635efSGarrett D'Amore 	 * sub-elements according to `rsord'.  Pick through each element
1634260e9a87SYuri Pankov 	 * and correctly order it.  This is an insertion sort.
163595c635efSGarrett D'Amore 	 */
163695c635efSGarrett D'Amore 
163795c635efSGarrett D'Amore 	next = NULL;
1638260e9a87SYuri Pankov 	for (nch = np->child->next; nch != NULL; nch = next) {
1639260e9a87SYuri Pankov 		/* Determine order number of this child. */
164095c635efSGarrett D'Amore 		for (i = 0; i < RSORD_MAX; i++)
1641260e9a87SYuri Pankov 			if (rsord[i] == nch->tok)
164295c635efSGarrett D'Amore 				break;
164395c635efSGarrett D'Amore 
1644260e9a87SYuri Pankov 		if (i == RSORD_MAX) {
1645260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_RS_BAD,
1646260e9a87SYuri Pankov 			    mdoc->parse, nch->line, nch->pos,
1647260e9a87SYuri Pankov 			    mdoc_macronames[nch->tok]);
1648260e9a87SYuri Pankov 			i = -1;
1649260e9a87SYuri Pankov 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1650260e9a87SYuri Pankov 			np->norm->Rs.quote_T++;
1651260e9a87SYuri Pankov 
1652260e9a87SYuri Pankov 		/*
1653260e9a87SYuri Pankov 		 * Remove this child from the chain.  This somewhat
1654371584c2SYuri Pankov 		 * repeats roff_node_unlink(), but since we're
165595c635efSGarrett D'Amore 		 * just re-ordering, there's no need for the
165695c635efSGarrett D'Amore 		 * full unlink process.
165795c635efSGarrett D'Amore 		 */
165895c635efSGarrett D'Amore 
1659260e9a87SYuri Pankov 		if ((next = nch->next) != NULL)
1660260e9a87SYuri Pankov 			next->prev = nch->prev;
166195c635efSGarrett D'Amore 
1662260e9a87SYuri Pankov 		if ((prev = nch->prev) != NULL)
1663260e9a87SYuri Pankov 			prev->next = nch->next;
166495c635efSGarrett D'Amore 
1665260e9a87SYuri Pankov 		nch->prev = nch->next = NULL;
1666260e9a87SYuri Pankov 
1667260e9a87SYuri Pankov 		/*
166895c635efSGarrett D'Amore 		 * Scan back until we reach a node that's
1669260e9a87SYuri Pankov 		 * to be ordered before this child.
167095c635efSGarrett D'Amore 		 */
167195c635efSGarrett D'Amore 
167295c635efSGarrett D'Amore 		for ( ; prev ; prev = prev->prev) {
167395c635efSGarrett D'Amore 			/* Determine order of `prev'. */
167495c635efSGarrett D'Amore 			for (j = 0; j < RSORD_MAX; j++)
167595c635efSGarrett D'Amore 				if (rsord[j] == prev->tok)
167695c635efSGarrett D'Amore 					break;
1677260e9a87SYuri Pankov 			if (j == RSORD_MAX)
1678260e9a87SYuri Pankov 				j = -1;
167995c635efSGarrett D'Amore 
168095c635efSGarrett D'Amore 			if (j <= i)
168195c635efSGarrett D'Amore 				break;
168295c635efSGarrett D'Amore 		}
168395c635efSGarrett D'Amore 
168495c635efSGarrett D'Amore 		/*
1685260e9a87SYuri Pankov 		 * Set this child back into its correct place
1686260e9a87SYuri Pankov 		 * in front of the `prev' node.
168795c635efSGarrett D'Amore 		 */
168895c635efSGarrett D'Amore 
1689260e9a87SYuri Pankov 		nch->prev = prev;
169095c635efSGarrett D'Amore 
1691260e9a87SYuri Pankov 		if (prev == NULL) {
1692260e9a87SYuri Pankov 			np->child->prev = nch;
1693260e9a87SYuri Pankov 			nch->next = np->child;
1694260e9a87SYuri Pankov 			np->child = nch;
169595c635efSGarrett D'Amore 		} else {
1696260e9a87SYuri Pankov 			if (prev->next)
1697260e9a87SYuri Pankov 				prev->next->prev = nch;
1698260e9a87SYuri Pankov 			nch->next = prev->next;
1699260e9a87SYuri Pankov 			prev->next = nch;
170095c635efSGarrett D'Amore 		}
170195c635efSGarrett D'Amore 	}
170295c635efSGarrett D'Amore }
170395c635efSGarrett D'Amore 
1704698f87a4SGarrett D'Amore /*
1705698f87a4SGarrett D'Amore  * For some arguments of some macros,
1706698f87a4SGarrett D'Amore  * convert all breakable hyphens into ASCII_HYPH.
1707698f87a4SGarrett D'Amore  */
1708260e9a87SYuri Pankov static void
1709698f87a4SGarrett D'Amore post_hyph(POST_ARGS)
1710698f87a4SGarrett D'Amore {
1711371584c2SYuri Pankov 	struct roff_node	*nch;
1712698f87a4SGarrett D'Amore 	char			*cp;
1713698f87a4SGarrett D'Amore 
1714260e9a87SYuri Pankov 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1715371584c2SYuri Pankov 		if (nch->type != ROFFT_TEXT)
1716698f87a4SGarrett D'Amore 			continue;
1717698f87a4SGarrett D'Amore 		cp = nch->string;
1718260e9a87SYuri Pankov 		if (*cp == '\0')
1719698f87a4SGarrett D'Amore 			continue;
1720260e9a87SYuri Pankov 		while (*(++cp) != '\0')
1721260e9a87SYuri Pankov 			if (*cp == '-' &&
1722698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[-1]) &&
1723698f87a4SGarrett D'Amore 			    isalpha((unsigned char)cp[1]))
1724698f87a4SGarrett D'Amore 				*cp = ASCII_HYPH;
1725698f87a4SGarrett D'Amore 	}
1726698f87a4SGarrett D'Amore }
1727698f87a4SGarrett D'Amore 
1728260e9a87SYuri Pankov static void
172995c635efSGarrett D'Amore post_ns(POST_ARGS)
173095c635efSGarrett D'Amore {
173195c635efSGarrett D'Amore 
1732*a40ea1a7SYuri Pankov 	if (mdoc->last->flags & NODE_LINE)
1733260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1734260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
173595c635efSGarrett D'Amore }
173695c635efSGarrett D'Amore 
1737260e9a87SYuri Pankov static void
173895c635efSGarrett D'Amore post_sh(POST_ARGS)
173995c635efSGarrett D'Amore {
174095c635efSGarrett D'Amore 
1741260e9a87SYuri Pankov 	post_ignpar(mdoc);
174295c635efSGarrett D'Amore 
1743260e9a87SYuri Pankov 	switch (mdoc->last->type) {
1744371584c2SYuri Pankov 	case ROFFT_HEAD:
1745260e9a87SYuri Pankov 		post_sh_head(mdoc);
1746260e9a87SYuri Pankov 		break;
1747371584c2SYuri Pankov 	case ROFFT_BODY:
1748260e9a87SYuri Pankov 		switch (mdoc->lastsec)  {
1749260e9a87SYuri Pankov 		case SEC_NAME:
1750260e9a87SYuri Pankov 			post_sh_name(mdoc);
1751260e9a87SYuri Pankov 			break;
1752260e9a87SYuri Pankov 		case SEC_SEE_ALSO:
1753260e9a87SYuri Pankov 			post_sh_see_also(mdoc);
1754260e9a87SYuri Pankov 			break;
1755260e9a87SYuri Pankov 		case SEC_AUTHORS:
1756260e9a87SYuri Pankov 			post_sh_authors(mdoc);
1757260e9a87SYuri Pankov 			break;
1758260e9a87SYuri Pankov 		default:
1759260e9a87SYuri Pankov 			break;
1760260e9a87SYuri Pankov 		}
1761260e9a87SYuri Pankov 		break;
1762260e9a87SYuri Pankov 	default:
1763260e9a87SYuri Pankov 		break;
1764260e9a87SYuri Pankov 	}
176595c635efSGarrett D'Amore }
176695c635efSGarrett D'Amore 
1767260e9a87SYuri Pankov static void
1768260e9a87SYuri Pankov post_sh_name(POST_ARGS)
176995c635efSGarrett D'Amore {
1770371584c2SYuri Pankov 	struct roff_node *n;
1771260e9a87SYuri Pankov 	int hasnm, hasnd;
177295c635efSGarrett D'Amore 
1773260e9a87SYuri Pankov 	hasnm = hasnd = 0;
177495c635efSGarrett D'Amore 
1775260e9a87SYuri Pankov 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1776260e9a87SYuri Pankov 		switch (n->tok) {
1777260e9a87SYuri Pankov 		case MDOC_Nm:
1778*a40ea1a7SYuri Pankov 			if (hasnm && n->child != NULL)
1779*a40ea1a7SYuri Pankov 				mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
1780*a40ea1a7SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1781*a40ea1a7SYuri Pankov 				    "Nm %s", n->child->string);
1782260e9a87SYuri Pankov 			hasnm = 1;
1783*a40ea1a7SYuri Pankov 			continue;
1784260e9a87SYuri Pankov 		case MDOC_Nd:
1785260e9a87SYuri Pankov 			hasnd = 1;
1786260e9a87SYuri Pankov 			if (n->next != NULL)
1787260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1788260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos, NULL);
1789260e9a87SYuri Pankov 			break;
1790371584c2SYuri Pankov 		case TOKEN_NONE:
1791*a40ea1a7SYuri Pankov 			if (n->type == ROFFT_TEXT &&
1792*a40ea1a7SYuri Pankov 			    n->string[0] == ',' && n->string[1] == '\0' &&
1793*a40ea1a7SYuri Pankov 			    n->next != NULL && n->next->tok == MDOC_Nm) {
1794*a40ea1a7SYuri Pankov 				n = n->next;
1795*a40ea1a7SYuri Pankov 				continue;
1796*a40ea1a7SYuri Pankov 			}
1797260e9a87SYuri Pankov 			/* FALLTHROUGH */
1798260e9a87SYuri Pankov 		default:
1799260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1800260e9a87SYuri Pankov 			    n->line, n->pos, mdoc_macronames[n->tok]);
1801*a40ea1a7SYuri Pankov 			continue;
1802260e9a87SYuri Pankov 		}
1803*a40ea1a7SYuri Pankov 		break;
180495c635efSGarrett D'Amore 	}
180595c635efSGarrett D'Amore 
1806260e9a87SYuri Pankov 	if ( ! hasnm)
1807260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1808260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1809260e9a87SYuri Pankov 	if ( ! hasnd)
1810260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1811260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1812260e9a87SYuri Pankov }
1813260e9a87SYuri Pankov 
1814260e9a87SYuri Pankov static void
1815260e9a87SYuri Pankov post_sh_see_also(POST_ARGS)
1816260e9a87SYuri Pankov {
1817371584c2SYuri Pankov 	const struct roff_node	*n;
1818260e9a87SYuri Pankov 	const char		*name, *sec;
1819260e9a87SYuri Pankov 	const char		*lastname, *lastsec, *lastpunct;
1820260e9a87SYuri Pankov 	int			 cmp;
1821260e9a87SYuri Pankov 
1822260e9a87SYuri Pankov 	n = mdoc->last->child;
1823260e9a87SYuri Pankov 	lastname = lastsec = lastpunct = NULL;
1824260e9a87SYuri Pankov 	while (n != NULL) {
1825371584c2SYuri Pankov 		if (n->tok != MDOC_Xr ||
1826371584c2SYuri Pankov 		    n->child == NULL ||
1827371584c2SYuri Pankov 		    n->child->next == NULL)
1828260e9a87SYuri Pankov 			break;
1829260e9a87SYuri Pankov 
1830260e9a87SYuri Pankov 		/* Process one .Xr node. */
1831260e9a87SYuri Pankov 
1832260e9a87SYuri Pankov 		name = n->child->string;
1833260e9a87SYuri Pankov 		sec = n->child->next->string;
1834260e9a87SYuri Pankov 		if (lastsec != NULL) {
1835260e9a87SYuri Pankov 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1836260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1837260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1838260e9a87SYuri Pankov 				    "%s before %s(%s)", lastpunct,
1839260e9a87SYuri Pankov 				    name, sec);
1840260e9a87SYuri Pankov 			cmp = strcmp(lastsec, sec);
1841260e9a87SYuri Pankov 			if (cmp > 0)
1842260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1843260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1844260e9a87SYuri Pankov 				    "%s(%s) after %s(%s)", name,
1845260e9a87SYuri Pankov 				    sec, lastname, lastsec);
1846260e9a87SYuri Pankov 			else if (cmp == 0 &&
1847260e9a87SYuri Pankov 			    strcasecmp(lastname, name) > 0)
1848260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1849260e9a87SYuri Pankov 				    mdoc->parse, n->line, n->pos,
1850260e9a87SYuri Pankov 				    "%s after %s", name, lastname);
1851260e9a87SYuri Pankov 		}
1852260e9a87SYuri Pankov 		lastname = name;
1853260e9a87SYuri Pankov 		lastsec = sec;
1854260e9a87SYuri Pankov 
1855260e9a87SYuri Pankov 		/* Process the following node. */
1856260e9a87SYuri Pankov 
1857260e9a87SYuri Pankov 		n = n->next;
1858260e9a87SYuri Pankov 		if (n == NULL)
1859260e9a87SYuri Pankov 			break;
1860260e9a87SYuri Pankov 		if (n->tok == MDOC_Xr) {
1861260e9a87SYuri Pankov 			lastpunct = "none";
186295c635efSGarrett D'Amore 			continue;
1863260e9a87SYuri Pankov 		}
1864371584c2SYuri Pankov 		if (n->type != ROFFT_TEXT)
1865260e9a87SYuri Pankov 			break;
1866260e9a87SYuri Pankov 		for (name = n->string; *name != '\0'; name++)
1867260e9a87SYuri Pankov 			if (isalpha((const unsigned char)*name))
1868260e9a87SYuri Pankov 				return;
1869260e9a87SYuri Pankov 		lastpunct = n->string;
1870260e9a87SYuri Pankov 		if (n->next == NULL)
1871260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1872260e9a87SYuri Pankov 			    n->line, n->pos, "%s after %s(%s)",
1873260e9a87SYuri Pankov 			    lastpunct, lastname, lastsec);
1874260e9a87SYuri Pankov 		n = n->next;
187595c635efSGarrett D'Amore 	}
1876260e9a87SYuri Pankov }
187795c635efSGarrett D'Amore 
1878260e9a87SYuri Pankov static int
1879371584c2SYuri Pankov child_an(const struct roff_node *n)
1880260e9a87SYuri Pankov {
188195c635efSGarrett D'Amore 
1882260e9a87SYuri Pankov 	for (n = n->child; n != NULL; n = n->next)
1883371584c2SYuri Pankov 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1884371584c2SYuri Pankov 			return 1;
1885371584c2SYuri Pankov 	return 0;
188695c635efSGarrett D'Amore }
188795c635efSGarrett D'Amore 
1888260e9a87SYuri Pankov static void
1889260e9a87SYuri Pankov post_sh_authors(POST_ARGS)
1890260e9a87SYuri Pankov {
1891260e9a87SYuri Pankov 
1892260e9a87SYuri Pankov 	if ( ! child_an(mdoc->last))
1893260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1894260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, NULL);
1895260e9a87SYuri Pankov }
1896260e9a87SYuri Pankov 
1897260e9a87SYuri Pankov static void
189895c635efSGarrett D'Amore post_sh_head(POST_ARGS)
189995c635efSGarrett D'Amore {
1900*a40ea1a7SYuri Pankov 	struct roff_node	*nch;
1901*a40ea1a7SYuri Pankov 	const char		*goodsec;
1902*a40ea1a7SYuri Pankov 	enum roff_sec		 sec;
190395c635efSGarrett D'Amore 
190495c635efSGarrett D'Amore 	/*
190595c635efSGarrett D'Amore 	 * Process a new section.  Sections are either "named" or
190695c635efSGarrett D'Amore 	 * "custom".  Custom sections are user-defined, while named ones
190795c635efSGarrett D'Amore 	 * follow a conventional order and may only appear in certain
190895c635efSGarrett D'Amore 	 * manual sections.
190995c635efSGarrett D'Amore 	 */
191095c635efSGarrett D'Amore 
1911371584c2SYuri Pankov 	sec = mdoc->last->sec;
191295c635efSGarrett D'Amore 
191395c635efSGarrett D'Amore 	/* The NAME should be first. */
191495c635efSGarrett D'Amore 
1915*a40ea1a7SYuri Pankov 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1916260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1917*a40ea1a7SYuri Pankov 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
1918*a40ea1a7SYuri Pankov 		    sec != SEC_CUSTOM ? secnames[sec] :
1919*a40ea1a7SYuri Pankov 		    (nch = mdoc->last->child) == NULL ? "" :
1920*a40ea1a7SYuri Pankov 		    nch->type == ROFFT_TEXT ? nch->string :
1921*a40ea1a7SYuri Pankov 		    mdoc_macronames[nch->tok]);
192295c635efSGarrett D'Amore 
192395c635efSGarrett D'Amore 	/* The SYNOPSIS gets special attention in other areas. */
192495c635efSGarrett D'Amore 
1925371584c2SYuri Pankov 	if (sec == SEC_SYNOPSIS) {
1926698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 1, '=');
192795c635efSGarrett D'Amore 		mdoc->flags |= MDOC_SYNOPSIS;
1928698f87a4SGarrett D'Amore 	} else {
1929698f87a4SGarrett D'Amore 		roff_setreg(mdoc->roff, "nS", 0, '=');
193095c635efSGarrett D'Amore 		mdoc->flags &= ~MDOC_SYNOPSIS;
1931698f87a4SGarrett D'Amore 	}
193295c635efSGarrett D'Amore 
193395c635efSGarrett D'Amore 	/* Mark our last section. */
193495c635efSGarrett D'Amore 
193595c635efSGarrett D'Amore 	mdoc->lastsec = sec;
193695c635efSGarrett D'Amore 
193795c635efSGarrett D'Amore 	/* We don't care about custom sections after this. */
193895c635efSGarrett D'Amore 
1939371584c2SYuri Pankov 	if (sec == SEC_CUSTOM)
1940260e9a87SYuri Pankov 		return;
194195c635efSGarrett D'Amore 
194295c635efSGarrett D'Amore 	/*
194395c635efSGarrett D'Amore 	 * Check whether our non-custom section is being repeated or is
194495c635efSGarrett D'Amore 	 * out of order.
194595c635efSGarrett D'Amore 	 */
194695c635efSGarrett D'Amore 
194795c635efSGarrett D'Amore 	if (sec == mdoc->lastnamed)
1948260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1949260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1950371584c2SYuri Pankov 		    "Sh %s", secnames[sec]);
195195c635efSGarrett D'Amore 
195295c635efSGarrett D'Amore 	if (sec < mdoc->lastnamed)
1953260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1954260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1955371584c2SYuri Pankov 		    "Sh %s", secnames[sec]);
195695c635efSGarrett D'Amore 
195795c635efSGarrett D'Amore 	/* Mark the last named section. */
195895c635efSGarrett D'Amore 
195995c635efSGarrett D'Amore 	mdoc->lastnamed = sec;
196095c635efSGarrett D'Amore 
196195c635efSGarrett D'Amore 	/* Check particular section/manual conventions. */
196295c635efSGarrett D'Amore 
1963371584c2SYuri Pankov 	if (mdoc->meta.msec == NULL)
1964260e9a87SYuri Pankov 		return;
196595c635efSGarrett D'Amore 
1966260e9a87SYuri Pankov 	goodsec = NULL;
196795c635efSGarrett D'Amore 	switch (sec) {
1968260e9a87SYuri Pankov 	case SEC_ERRORS:
1969260e9a87SYuri Pankov 		if (*mdoc->meta.msec == '4')
1970260e9a87SYuri Pankov 			break;
1971260e9a87SYuri Pankov 		goodsec = "2, 3, 4, 9";
197295c635efSGarrett D'Amore 		/* FALLTHROUGH */
1973260e9a87SYuri Pankov 	case SEC_RETURN_VALUES:
1974260e9a87SYuri Pankov 	case SEC_LIBRARY:
197595c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '2')
197695c635efSGarrett D'Amore 			break;
197795c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '3')
197895c635efSGarrett D'Amore 			break;
1979260e9a87SYuri Pankov 		if (NULL == goodsec)
1980260e9a87SYuri Pankov 			goodsec = "2, 3, 9";
1981260e9a87SYuri Pankov 		/* FALLTHROUGH */
1982260e9a87SYuri Pankov 	case SEC_CONTEXT:
198395c635efSGarrett D'Amore 		if (*mdoc->meta.msec == '9')
198495c635efSGarrett D'Amore 			break;
1985260e9a87SYuri Pankov 		if (NULL == goodsec)
1986260e9a87SYuri Pankov 			goodsec = "9";
1987260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1988260e9a87SYuri Pankov 		    mdoc->last->line, mdoc->last->pos,
1989371584c2SYuri Pankov 		    "Sh %s for %s only", secnames[sec], goodsec);
199095c635efSGarrett D'Amore 		break;
199195c635efSGarrett D'Amore 	default:
199295c635efSGarrett D'Amore 		break;
199395c635efSGarrett D'Amore 	}
199495c635efSGarrett D'Amore }
199595c635efSGarrett D'Amore 
1996*a40ea1a7SYuri Pankov static void
1997*a40ea1a7SYuri Pankov post_xr(POST_ARGS)
1998*a40ea1a7SYuri Pankov {
1999*a40ea1a7SYuri Pankov 	struct roff_node *n, *nch;
2000*a40ea1a7SYuri Pankov 
2001*a40ea1a7SYuri Pankov 	n = mdoc->last;
2002*a40ea1a7SYuri Pankov 	nch = n->child;
2003*a40ea1a7SYuri Pankov 	if (nch->next == NULL) {
2004*a40ea1a7SYuri Pankov 		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2005*a40ea1a7SYuri Pankov 		    n->line, n->pos, "Xr %s", nch->string);
2006*a40ea1a7SYuri Pankov 		return;
2007*a40ea1a7SYuri Pankov 	}
2008*a40ea1a7SYuri Pankov 	assert(nch->next == n->last);
2009*a40ea1a7SYuri Pankov }
2010*a40ea1a7SYuri Pankov 
2011260e9a87SYuri Pankov static void
201295c635efSGarrett D'Amore post_ignpar(POST_ARGS)
201395c635efSGarrett D'Amore {
2014371584c2SYuri Pankov 	struct roff_node *np;
201595c635efSGarrett D'Amore 
2016260e9a87SYuri Pankov 	switch (mdoc->last->type) {
2017371584c2SYuri Pankov 	case ROFFT_HEAD:
2018260e9a87SYuri Pankov 		post_hyph(mdoc);
2019260e9a87SYuri Pankov 		return;
2020371584c2SYuri Pankov 	case ROFFT_BODY:
2021260e9a87SYuri Pankov 		break;
2022260e9a87SYuri Pankov 	default:
2023260e9a87SYuri Pankov 		return;
2024260e9a87SYuri Pankov 	}
202595c635efSGarrett D'Amore 
2026371584c2SYuri Pankov 	if ((np = mdoc->last->child) != NULL)
2027371584c2SYuri Pankov 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2028260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
2029260e9a87SYuri Pankov 			    mdoc->parse, np->line, np->pos,
2030260e9a87SYuri Pankov 			    "%s after %s", mdoc_macronames[np->tok],
2031260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
2032371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
203395c635efSGarrett D'Amore 		}
203495c635efSGarrett D'Amore 
2035371584c2SYuri Pankov 	if ((np = mdoc->last->last) != NULL)
2036371584c2SYuri Pankov 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2037260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2038260e9a87SYuri Pankov 			    np->line, np->pos, "%s at the end of %s",
2039260e9a87SYuri Pankov 			    mdoc_macronames[np->tok],
2040260e9a87SYuri Pankov 			    mdoc_macronames[mdoc->last->tok]);
2041371584c2SYuri Pankov 			roff_node_delete(mdoc, np);
204295c635efSGarrett D'Amore 		}
204395c635efSGarrett D'Amore }
204495c635efSGarrett D'Amore 
2045260e9a87SYuri Pankov static void
2046371584c2SYuri Pankov post_prevpar(POST_ARGS)
204795c635efSGarrett D'Amore {
2048371584c2SYuri Pankov 	struct roff_node *n;
204995c635efSGarrett D'Amore 
2050371584c2SYuri Pankov 	n = mdoc->last;
2051371584c2SYuri Pankov 	if (NULL == n->prev)
2052260e9a87SYuri Pankov 		return;
2053371584c2SYuri Pankov 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2054260e9a87SYuri Pankov 		return;
205595c635efSGarrett D'Amore 
2056260e9a87SYuri Pankov 	/*
205795c635efSGarrett D'Amore 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
205895c635efSGarrett D'Amore 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
205995c635efSGarrett D'Amore 	 */
206095c635efSGarrett D'Amore 
2061371584c2SYuri Pankov 	if (n->prev->tok != MDOC_Pp &&
2062371584c2SYuri Pankov 	    n->prev->tok != MDOC_Lp &&
2063371584c2SYuri Pankov 	    n->prev->tok != MDOC_br)
2064260e9a87SYuri Pankov 		return;
2065371584c2SYuri Pankov 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2066260e9a87SYuri Pankov 		return;
2067371584c2SYuri Pankov 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2068260e9a87SYuri Pankov 		return;
2069371584c2SYuri Pankov 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2070260e9a87SYuri Pankov 		return;
207195c635efSGarrett D'Amore 
2072260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2073371584c2SYuri Pankov 	    n->prev->line, n->prev->pos,
2074371584c2SYuri Pankov 	    "%s before %s", mdoc_macronames[n->prev->tok],
2075260e9a87SYuri Pankov 	    mdoc_macronames[n->tok]);
2076371584c2SYuri Pankov 	roff_node_delete(mdoc, n->prev);
207795c635efSGarrett D'Amore }
207895c635efSGarrett D'Amore 
2079260e9a87SYuri Pankov static void
2080698f87a4SGarrett D'Amore post_par(POST_ARGS)
2081698f87a4SGarrett D'Amore {
2082371584c2SYuri Pankov 	struct roff_node *np;
2083698f87a4SGarrett D'Amore 
2084260e9a87SYuri Pankov 	np = mdoc->last;
2085371584c2SYuri Pankov 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
2086371584c2SYuri Pankov 		post_prevpar(mdoc);
2087698f87a4SGarrett D'Amore 
2088260e9a87SYuri Pankov 	if (np->tok == MDOC_sp) {
2089371584c2SYuri Pankov 		if (np->child != NULL && np->child->next != NULL)
2090260e9a87SYuri Pankov 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2091260e9a87SYuri Pankov 			    np->child->next->line, np->child->next->pos,
2092260e9a87SYuri Pankov 			    "sp ... %s", np->child->next->string);
2093260e9a87SYuri Pankov 	} else if (np->child != NULL)
2094260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2095260e9a87SYuri Pankov 		    mdoc->parse, np->line, np->pos, "%s %s",
2096260e9a87SYuri Pankov 		    mdoc_macronames[np->tok], np->child->string);
2097260e9a87SYuri Pankov 
2098371584c2SYuri Pankov 	if ((np = mdoc->last->prev) == NULL) {
2099260e9a87SYuri Pankov 		np = mdoc->last->parent;
2100371584c2SYuri Pankov 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2101260e9a87SYuri Pankov 			return;
2102371584c2SYuri Pankov 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2103371584c2SYuri Pankov 	    (mdoc->last->tok != MDOC_br ||
2104371584c2SYuri Pankov 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
2105260e9a87SYuri Pankov 		return;
2106698f87a4SGarrett D'Amore 
2107260e9a87SYuri Pankov 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2108260e9a87SYuri Pankov 	    mdoc->last->line, mdoc->last->pos,
2109260e9a87SYuri Pankov 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
2110260e9a87SYuri Pankov 	    mdoc_macronames[np->tok]);
2111371584c2SYuri Pankov 	roff_node_delete(mdoc, mdoc->last);
211295c635efSGarrett D'Amore }
211395c635efSGarrett D'Amore 
2114260e9a87SYuri Pankov static void
211595c635efSGarrett D'Amore post_dd(POST_ARGS)
211695c635efSGarrett D'Amore {
2117371584c2SYuri Pankov 	struct roff_node *n;
2118260e9a87SYuri Pankov 	char		 *datestr;
211995c635efSGarrett D'Amore 
2120371584c2SYuri Pankov 	n = mdoc->last;
2121*a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2122*a40ea1a7SYuri Pankov 
2123371584c2SYuri Pankov 	if (mdoc->meta.date != NULL) {
2124371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2125371584c2SYuri Pankov 		    n->line, n->pos, "Dd");
212695c635efSGarrett D'Amore 		free(mdoc->meta.date);
2127371584c2SYuri Pankov 	} else if (mdoc->flags & MDOC_PBODY)
2128371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2129371584c2SYuri Pankov 		    n->line, n->pos, "Dd");
2130371584c2SYuri Pankov 	else if (mdoc->meta.title != NULL)
2131371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2132371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Dt");
2133371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
2134371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2135371584c2SYuri Pankov 		    n->line, n->pos, "Dd after Os");
213695c635efSGarrett D'Amore 
2137371584c2SYuri Pankov 	if (n->child == NULL || n->child->string[0] == '\0') {
2138260e9a87SYuri Pankov 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2139260e9a87SYuri Pankov 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2140*a40ea1a7SYuri Pankov 		return;
214195c635efSGarrett D'Amore 	}
214295c635efSGarrett D'Amore 
2143260e9a87SYuri Pankov 	datestr = NULL;
2144371584c2SYuri Pankov 	deroff(&datestr, n);
2145260e9a87SYuri Pankov 	if (mdoc->quick)
2146260e9a87SYuri Pankov 		mdoc->meta.date = datestr;
2147260e9a87SYuri Pankov 	else {
2148260e9a87SYuri Pankov 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
2149260e9a87SYuri Pankov 		    datestr, n->line, n->pos);
2150260e9a87SYuri Pankov 		free(datestr);
215195c635efSGarrett D'Amore 	}
215295c635efSGarrett D'Amore }
215395c635efSGarrett D'Amore 
2154260e9a87SYuri Pankov static void
215595c635efSGarrett D'Amore post_dt(POST_ARGS)
215695c635efSGarrett D'Amore {
2157371584c2SYuri Pankov 	struct roff_node *nn, *n;
215895c635efSGarrett D'Amore 	const char	 *cp;
215995c635efSGarrett D'Amore 	char		 *p;
216095c635efSGarrett D'Amore 
216195c635efSGarrett D'Amore 	n = mdoc->last;
2162*a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2163*a40ea1a7SYuri Pankov 
2164371584c2SYuri Pankov 	if (mdoc->flags & MDOC_PBODY) {
2165371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2166371584c2SYuri Pankov 		    n->line, n->pos, "Dt");
2167*a40ea1a7SYuri Pankov 		return;
2168371584c2SYuri Pankov 	}
2169371584c2SYuri Pankov 
2170371584c2SYuri Pankov 	if (mdoc->meta.title != NULL)
2171371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2172371584c2SYuri Pankov 		    n->line, n->pos, "Dt");
2173371584c2SYuri Pankov 	else if (mdoc->meta.os != NULL)
2174371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2175371584c2SYuri Pankov 		    n->line, n->pos, "Dt after Os");
217695c635efSGarrett D'Amore 
2177260e9a87SYuri Pankov 	free(mdoc->meta.title);
2178260e9a87SYuri Pankov 	free(mdoc->meta.msec);
2179260e9a87SYuri Pankov 	free(mdoc->meta.vol);
2180260e9a87SYuri Pankov 	free(mdoc->meta.arch);
218195c635efSGarrett D'Amore 
2182260e9a87SYuri Pankov 	mdoc->meta.title = NULL;
2183260e9a87SYuri Pankov 	mdoc->meta.msec = NULL;
2184260e9a87SYuri Pankov 	mdoc->meta.vol = NULL;
2185260e9a87SYuri Pankov 	mdoc->meta.arch = NULL;
218695c635efSGarrett D'Amore 
2187260e9a87SYuri Pankov 	/* Mandatory first argument: title. */
218895c635efSGarrett D'Amore 
2189260e9a87SYuri Pankov 	nn = n->child;
2190260e9a87SYuri Pankov 	if (nn == NULL || *nn->string == '\0') {
2191260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2192260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos, "Dt");
2193260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2194260e9a87SYuri Pankov 	} else {
2195260e9a87SYuri Pankov 		mdoc->meta.title = mandoc_strdup(nn->string);
219695c635efSGarrett D'Amore 
2197260e9a87SYuri Pankov 		/* Check that all characters are uppercase. */
219895c635efSGarrett D'Amore 
2199260e9a87SYuri Pankov 		for (p = nn->string; *p != '\0'; p++)
2200260e9a87SYuri Pankov 			if (islower((unsigned char)*p)) {
2201260e9a87SYuri Pankov 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2202260e9a87SYuri Pankov 				    mdoc->parse, nn->line,
2203260e9a87SYuri Pankov 				    nn->pos + (p - nn->string),
2204260e9a87SYuri Pankov 				    "Dt %s", nn->string);
2205260e9a87SYuri Pankov 				break;
2206260e9a87SYuri Pankov 			}
220795c635efSGarrett D'Amore 	}
220895c635efSGarrett D'Amore 
2209*a40ea1a7SYuri Pankov 	/* Mandatory second argument: section. */
221095c635efSGarrett D'Amore 
2211260e9a87SYuri Pankov 	if (nn != NULL)
2212260e9a87SYuri Pankov 		nn = nn->next;
221395c635efSGarrett D'Amore 
2214260e9a87SYuri Pankov 	if (nn == NULL) {
2215260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2216260e9a87SYuri Pankov 		    mdoc->parse, n->line, n->pos,
2217260e9a87SYuri Pankov 		    "Dt %s", mdoc->meta.title);
221895c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2219*a40ea1a7SYuri Pankov 		return;  /* msec and arch remain NULL. */
222095c635efSGarrett D'Amore 	}
222195c635efSGarrett D'Amore 
2222260e9a87SYuri Pankov 	mdoc->meta.msec = mandoc_strdup(nn->string);
2223260e9a87SYuri Pankov 
2224260e9a87SYuri Pankov 	/* Infer volume title from section number. */
222595c635efSGarrett D'Amore 
222695c635efSGarrett D'Amore 	cp = mandoc_a2msec(nn->string);
2227260e9a87SYuri Pankov 	if (cp == NULL) {
2228260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2229260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
223095c635efSGarrett D'Amore 		mdoc->meta.vol = mandoc_strdup(nn->string);
2231260e9a87SYuri Pankov 	} else
2232260e9a87SYuri Pankov 		mdoc->meta.vol = mandoc_strdup(cp);
223395c635efSGarrett D'Amore 
2234260e9a87SYuri Pankov 	/* Optional third argument: architecture. */
223595c635efSGarrett D'Amore 
2236260e9a87SYuri Pankov 	if ((nn = nn->next) == NULL)
2237*a40ea1a7SYuri Pankov 		return;
223895c635efSGarrett D'Amore 
2239260e9a87SYuri Pankov 	for (p = nn->string; *p != '\0'; p++)
2240260e9a87SYuri Pankov 		*p = tolower((unsigned char)*p);
2241260e9a87SYuri Pankov 	mdoc->meta.arch = mandoc_strdup(nn->string);
224295c635efSGarrett D'Amore 
2243260e9a87SYuri Pankov 	/* Ignore fourth and later arguments. */
224495c635efSGarrett D'Amore 
2245260e9a87SYuri Pankov 	if ((nn = nn->next) != NULL)
2246260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2247260e9a87SYuri Pankov 		    nn->line, nn->pos, "Dt ... %s", nn->string);
224895c635efSGarrett D'Amore }
224995c635efSGarrett D'Amore 
2250260e9a87SYuri Pankov static void
225195c635efSGarrett D'Amore post_bx(POST_ARGS)
225295c635efSGarrett D'Amore {
2253*a40ea1a7SYuri Pankov 	struct roff_node	*n, *nch;
2254*a40ea1a7SYuri Pankov 
2255*a40ea1a7SYuri Pankov 	n = mdoc->last;
2256*a40ea1a7SYuri Pankov 	nch = n->child;
2257*a40ea1a7SYuri Pankov 
2258*a40ea1a7SYuri Pankov 	if (nch != NULL) {
2259*a40ea1a7SYuri Pankov 		mdoc->last = nch;
2260*a40ea1a7SYuri Pankov 		nch = nch->next;
2261*a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
2262*a40ea1a7SYuri Pankov 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2263*a40ea1a7SYuri Pankov 		mdoc->last->flags |= NODE_NOSRC;
2264*a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_SIBLING;
2265*a40ea1a7SYuri Pankov 	} else
2266*a40ea1a7SYuri Pankov 		mdoc->next = ROFF_NEXT_CHILD;
2267*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2268*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2269*a40ea1a7SYuri Pankov 
2270*a40ea1a7SYuri Pankov 	if (nch == NULL) {
2271*a40ea1a7SYuri Pankov 		mdoc->last = n;
2272*a40ea1a7SYuri Pankov 		return;
2273*a40ea1a7SYuri Pankov 	}
2274*a40ea1a7SYuri Pankov 
2275*a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2276*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2277*a40ea1a7SYuri Pankov 	mdoc->next = ROFF_NEXT_SIBLING;
2278*a40ea1a7SYuri Pankov 	roff_word_alloc(mdoc, n->line, n->pos, "-");
2279*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2280*a40ea1a7SYuri Pankov 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2281*a40ea1a7SYuri Pankov 	mdoc->last->flags |= NODE_NOSRC;
2282*a40ea1a7SYuri Pankov 	mdoc->last = n;
228395c635efSGarrett D'Amore 
2284260e9a87SYuri Pankov 	/*
228595c635efSGarrett D'Amore 	 * Make `Bx's second argument always start with an uppercase
228695c635efSGarrett D'Amore 	 * letter.  Groff checks if it's an "accepted" term, but we just
228795c635efSGarrett D'Amore 	 * uppercase blindly.
228895c635efSGarrett D'Amore 	 */
228995c635efSGarrett D'Amore 
2290*a40ea1a7SYuri Pankov 	*nch->string = (char)toupper((unsigned char)*nch->string);
229195c635efSGarrett D'Amore }
229295c635efSGarrett D'Amore 
2293260e9a87SYuri Pankov static void
229495c635efSGarrett D'Amore post_os(POST_ARGS)
229595c635efSGarrett D'Amore {
229695c635efSGarrett D'Amore #ifndef OSNAME
229795c635efSGarrett D'Amore 	struct utsname	  utsname;
2298260e9a87SYuri Pankov 	static char	 *defbuf;
229995c635efSGarrett D'Amore #endif
2300371584c2SYuri Pankov 	struct roff_node *n;
230195c635efSGarrett D'Amore 
230295c635efSGarrett D'Amore 	n = mdoc->last;
2303*a40ea1a7SYuri Pankov 	n->flags |= NODE_NOPRT;
2304*a40ea1a7SYuri Pankov 
2305371584c2SYuri Pankov 	if (mdoc->meta.os != NULL)
2306371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2307371584c2SYuri Pankov 		    n->line, n->pos, "Os");
2308371584c2SYuri Pankov 	else if (mdoc->flags & MDOC_PBODY)
2309371584c2SYuri Pankov 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2310371584c2SYuri Pankov 		    n->line, n->pos, "Os");
231195c635efSGarrett D'Amore 
231295c635efSGarrett D'Amore 	/*
2313698f87a4SGarrett D'Amore 	 * Set the operating system by way of the `Os' macro.
2314698f87a4SGarrett D'Amore 	 * The order of precedence is:
2315698f87a4SGarrett D'Amore 	 * 1. the argument of the `Os' macro, unless empty
2316698f87a4SGarrett D'Amore 	 * 2. the -Ios=foo command line argument, if provided
2317698f87a4SGarrett D'Amore 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2318698f87a4SGarrett D'Amore 	 * 4. "sysname release" from uname(3)
2319260e9a87SYuri Pankov 	 */
232095c635efSGarrett D'Amore 
2321698f87a4SGarrett D'Amore 	free(mdoc->meta.os);
2322260e9a87SYuri Pankov 	mdoc->meta.os = NULL;
2323371584c2SYuri Pankov 	deroff(&mdoc->meta.os, n);
2324260e9a87SYuri Pankov 	if (mdoc->meta.os)
2325*a40ea1a7SYuri Pankov 		return;
232695c635efSGarrett D'Amore 
2327260e9a87SYuri Pankov 	if (mdoc->defos) {
2328260e9a87SYuri Pankov 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2329*a40ea1a7SYuri Pankov 		return;
233095c635efSGarrett D'Amore 	}
233195c635efSGarrett D'Amore 
233295c635efSGarrett D'Amore #ifdef OSNAME
2333260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(OSNAME);
233495c635efSGarrett D'Amore #else /*!OSNAME */
2335371584c2SYuri Pankov 	if (defbuf == NULL) {
2336371584c2SYuri Pankov 		if (uname(&utsname) == -1) {
2337260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2338260e9a87SYuri Pankov 			    n->line, n->pos, "Os");
2339260e9a87SYuri Pankov 			defbuf = mandoc_strdup("UNKNOWN");
2340260e9a87SYuri Pankov 		} else
2341260e9a87SYuri Pankov 			mandoc_asprintf(&defbuf, "%s %s",
2342260e9a87SYuri Pankov 			    utsname.sysname, utsname.release);
234395c635efSGarrett D'Amore 	}
2344260e9a87SYuri Pankov 	mdoc->meta.os = mandoc_strdup(defbuf);
2345260e9a87SYuri Pankov #endif /*!OSNAME*/
234695c635efSGarrett D'Amore }
234795c635efSGarrett D'Amore 
2348371584c2SYuri Pankov enum roff_sec
2349371584c2SYuri Pankov mdoc_a2sec(const char *p)
235095c635efSGarrett D'Amore {
235195c635efSGarrett D'Amore 	int		 i;
235295c635efSGarrett D'Amore 
2353260e9a87SYuri Pankov 	for (i = 0; i < (int)SEC__MAX; i++)
235495c635efSGarrett D'Amore 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2355371584c2SYuri Pankov 			return (enum roff_sec)i;
235695c635efSGarrett D'Amore 
2357371584c2SYuri Pankov 	return SEC_CUSTOM;
235895c635efSGarrett D'Amore }
235995c635efSGarrett D'Amore 
236095c635efSGarrett D'Amore static size_t
2361371584c2SYuri Pankov macro2len(int macro)
236295c635efSGarrett D'Amore {
236395c635efSGarrett D'Amore 
236495c635efSGarrett D'Amore 	switch (macro) {
2365260e9a87SYuri Pankov 	case MDOC_Ad:
2366371584c2SYuri Pankov 		return 12;
2367260e9a87SYuri Pankov 	case MDOC_Ao:
2368371584c2SYuri Pankov 		return 12;
2369260e9a87SYuri Pankov 	case MDOC_An:
2370371584c2SYuri Pankov 		return 12;
2371260e9a87SYuri Pankov 	case MDOC_Aq:
2372371584c2SYuri Pankov 		return 12;
2373260e9a87SYuri Pankov 	case MDOC_Ar:
2374371584c2SYuri Pankov 		return 12;
2375260e9a87SYuri Pankov 	case MDOC_Bo:
2376371584c2SYuri Pankov 		return 12;
2377260e9a87SYuri Pankov 	case MDOC_Bq:
2378371584c2SYuri Pankov 		return 12;
2379260e9a87SYuri Pankov 	case MDOC_Cd:
2380371584c2SYuri Pankov 		return 12;
2381260e9a87SYuri Pankov 	case MDOC_Cm:
2382371584c2SYuri Pankov 		return 10;
2383260e9a87SYuri Pankov 	case MDOC_Do:
2384371584c2SYuri Pankov 		return 10;
2385260e9a87SYuri Pankov 	case MDOC_Dq:
2386371584c2SYuri Pankov 		return 12;
2387260e9a87SYuri Pankov 	case MDOC_Dv:
2388371584c2SYuri Pankov 		return 12;
2389260e9a87SYuri Pankov 	case MDOC_Eo:
2390371584c2SYuri Pankov 		return 12;
2391260e9a87SYuri Pankov 	case MDOC_Em:
2392371584c2SYuri Pankov 		return 10;
2393260e9a87SYuri Pankov 	case MDOC_Er:
2394371584c2SYuri Pankov 		return 17;
2395260e9a87SYuri Pankov 	case MDOC_Ev:
2396371584c2SYuri Pankov 		return 15;
2397260e9a87SYuri Pankov 	case MDOC_Fa:
2398371584c2SYuri Pankov 		return 12;
2399260e9a87SYuri Pankov 	case MDOC_Fl:
2400371584c2SYuri Pankov 		return 10;
2401260e9a87SYuri Pankov 	case MDOC_Fo:
2402371584c2SYuri Pankov 		return 16;
2403260e9a87SYuri Pankov 	case MDOC_Fn:
2404371584c2SYuri Pankov 		return 16;
2405260e9a87SYuri Pankov 	case MDOC_Ic:
2406371584c2SYuri Pankov 		return 10;
2407260e9a87SYuri Pankov 	case MDOC_Li:
2408371584c2SYuri Pankov 		return 16;
2409260e9a87SYuri Pankov 	case MDOC_Ms:
2410371584c2SYuri Pankov 		return 6;
2411260e9a87SYuri Pankov 	case MDOC_Nm:
2412371584c2SYuri Pankov 		return 10;
2413260e9a87SYuri Pankov 	case MDOC_No:
2414371584c2SYuri Pankov 		return 12;
2415260e9a87SYuri Pankov 	case MDOC_Oo:
2416371584c2SYuri Pankov 		return 10;
2417260e9a87SYuri Pankov 	case MDOC_Op:
2418371584c2SYuri Pankov 		return 14;
2419260e9a87SYuri Pankov 	case MDOC_Pa:
2420371584c2SYuri Pankov 		return 32;
2421260e9a87SYuri Pankov 	case MDOC_Pf:
2422371584c2SYuri Pankov 		return 12;
2423260e9a87SYuri Pankov 	case MDOC_Po:
2424371584c2SYuri Pankov 		return 12;
2425260e9a87SYuri Pankov 	case MDOC_Pq:
2426371584c2SYuri Pankov 		return 12;
2427260e9a87SYuri Pankov 	case MDOC_Ql:
2428371584c2SYuri Pankov 		return 16;
2429260e9a87SYuri Pankov 	case MDOC_Qo:
2430371584c2SYuri Pankov 		return 12;
2431260e9a87SYuri Pankov 	case MDOC_So:
2432371584c2SYuri Pankov 		return 12;
2433260e9a87SYuri Pankov 	case MDOC_Sq:
2434371584c2SYuri Pankov 		return 12;
2435260e9a87SYuri Pankov 	case MDOC_Sy:
2436371584c2SYuri Pankov 		return 6;
2437260e9a87SYuri Pankov 	case MDOC_Sx:
2438371584c2SYuri Pankov 		return 16;
2439260e9a87SYuri Pankov 	case MDOC_Tn:
2440371584c2SYuri Pankov 		return 10;
2441260e9a87SYuri Pankov 	case MDOC_Va:
2442371584c2SYuri Pankov 		return 12;
2443260e9a87SYuri Pankov 	case MDOC_Vt:
2444371584c2SYuri Pankov 		return 12;
2445260e9a87SYuri Pankov 	case MDOC_Xr:
2446371584c2SYuri Pankov 		return 10;
244795c635efSGarrett D'Amore 	default:
244895c635efSGarrett D'Amore 		break;
244995c635efSGarrett D'Amore 	};
2450371584c2SYuri Pankov 	return 0;
245195c635efSGarrett D'Amore }
2452