1241675Suqs/*	$Id: mdoc_validate.c,v 1.182 2012/03/23 05:50:25 kristaps Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#ifdef HAVE_CONFIG_H
19241675Suqs#include "config.h"
20241675Suqs#endif
21241675Suqs
22241675Suqs#ifndef	OSNAME
23241675Suqs#include <sys/utsname.h>
24241675Suqs#endif
25241675Suqs
26241675Suqs#include <sys/types.h>
27241675Suqs
28241675Suqs#include <assert.h>
29241675Suqs#include <ctype.h>
30241675Suqs#include <limits.h>
31241675Suqs#include <stdio.h>
32241675Suqs#include <stdlib.h>
33241675Suqs#include <string.h>
34241675Suqs#include <time.h>
35241675Suqs
36241675Suqs#include "mdoc.h"
37241675Suqs#include "mandoc.h"
38241675Suqs#include "libmdoc.h"
39241675Suqs#include "libmandoc.h"
40241675Suqs
41241675Suqs/* FIXME: .Bl -diag can't have non-text children in HEAD. */
42241675Suqs
43241675Suqs#define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
44241675Suqs#define	POST_ARGS struct mdoc *mdoc
45241675Suqs
46241675Suqs#define	NUMSIZ	  32
47241675Suqs#define	DATESIZE  32
48241675Suqs
49241675Suqsenum	check_ineq {
50241675Suqs	CHECK_LT,
51241675Suqs	CHECK_GT,
52241675Suqs	CHECK_EQ
53241675Suqs};
54241675Suqs
55241675Suqsenum	check_lvl {
56241675Suqs	CHECK_WARN,
57241675Suqs	CHECK_ERROR,
58241675Suqs};
59241675Suqs
60241675Suqstypedef	int	(*v_pre)(PRE_ARGS);
61241675Suqstypedef	int	(*v_post)(POST_ARGS);
62241675Suqs
63241675Suqsstruct	valids {
64241675Suqs	v_pre	*pre;
65241675Suqs	v_post	*post;
66241675Suqs};
67241675Suqs
68241675Suqsstatic	int	 check_count(struct mdoc *, enum mdoc_type,
69241675Suqs			enum check_lvl, enum check_ineq, int);
70241675Suqsstatic	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
71241675Suqsstatic	void	 check_text(struct mdoc *, int, int, char *);
72241675Suqsstatic	void	 check_argv(struct mdoc *,
73241675Suqs			struct mdoc_node *, struct mdoc_argv *);
74241675Suqsstatic	void	 check_args(struct mdoc *, struct mdoc_node *);
75241675Suqsstatic	int	 concat(char *, const struct mdoc_node *, size_t);
76241675Suqsstatic	enum mdoc_sec	a2sec(const char *);
77241675Suqsstatic	size_t		macro2len(enum mdoct);
78241675Suqs
79241675Suqsstatic	int	 ebool(POST_ARGS);
80241675Suqsstatic	int	 berr_ge1(POST_ARGS);
81241675Suqsstatic	int	 bwarn_ge1(POST_ARGS);
82241675Suqsstatic	int	 ewarn_eq0(POST_ARGS);
83241675Suqsstatic	int	 ewarn_eq1(POST_ARGS);
84241675Suqsstatic	int	 ewarn_ge1(POST_ARGS);
85241675Suqsstatic	int	 ewarn_le1(POST_ARGS);
86241675Suqsstatic	int	 hwarn_eq0(POST_ARGS);
87241675Suqsstatic	int	 hwarn_eq1(POST_ARGS);
88241675Suqsstatic	int	 hwarn_ge1(POST_ARGS);
89241675Suqsstatic	int	 hwarn_le1(POST_ARGS);
90241675Suqs
91241675Suqsstatic	int	 post_an(POST_ARGS);
92241675Suqsstatic	int	 post_at(POST_ARGS);
93241675Suqsstatic	int	 post_bf(POST_ARGS);
94241675Suqsstatic	int	 post_bl(POST_ARGS);
95241675Suqsstatic	int	 post_bl_block(POST_ARGS);
96241675Suqsstatic	int	 post_bl_block_width(POST_ARGS);
97241675Suqsstatic	int	 post_bl_block_tag(POST_ARGS);
98241675Suqsstatic	int	 post_bl_head(POST_ARGS);
99241675Suqsstatic	int	 post_bx(POST_ARGS);
100241675Suqsstatic	int	 post_dd(POST_ARGS);
101241675Suqsstatic	int	 post_dt(POST_ARGS);
102241675Suqsstatic	int	 post_defaults(POST_ARGS);
103241675Suqsstatic	int	 post_literal(POST_ARGS);
104241675Suqsstatic	int	 post_eoln(POST_ARGS);
105241675Suqsstatic	int	 post_it(POST_ARGS);
106241675Suqsstatic	int	 post_lb(POST_ARGS);
107241675Suqsstatic	int	 post_nm(POST_ARGS);
108241675Suqsstatic	int	 post_ns(POST_ARGS);
109241675Suqsstatic	int	 post_os(POST_ARGS);
110241675Suqsstatic	int	 post_ignpar(POST_ARGS);
111241675Suqsstatic	int	 post_prol(POST_ARGS);
112241675Suqsstatic	int	 post_root(POST_ARGS);
113241675Suqsstatic	int	 post_rs(POST_ARGS);
114241675Suqsstatic	int	 post_sh(POST_ARGS);
115241675Suqsstatic	int	 post_sh_body(POST_ARGS);
116241675Suqsstatic	int	 post_sh_head(POST_ARGS);
117241675Suqsstatic	int	 post_st(POST_ARGS);
118241675Suqsstatic	int	 post_std(POST_ARGS);
119241675Suqsstatic	int	 post_vt(POST_ARGS);
120241675Suqsstatic	int	 pre_an(PRE_ARGS);
121241675Suqsstatic	int	 pre_bd(PRE_ARGS);
122241675Suqsstatic	int	 pre_bl(PRE_ARGS);
123241675Suqsstatic	int	 pre_dd(PRE_ARGS);
124241675Suqsstatic	int	 pre_display(PRE_ARGS);
125241675Suqsstatic	int	 pre_dt(PRE_ARGS);
126241675Suqsstatic	int	 pre_it(PRE_ARGS);
127241675Suqsstatic	int	 pre_literal(PRE_ARGS);
128241675Suqsstatic	int	 pre_os(PRE_ARGS);
129241675Suqsstatic	int	 pre_par(PRE_ARGS);
130241675Suqsstatic	int	 pre_sh(PRE_ARGS);
131241675Suqsstatic	int	 pre_ss(PRE_ARGS);
132241675Suqsstatic	int	 pre_std(PRE_ARGS);
133241675Suqs
134241675Suqsstatic	v_post	 posts_an[] = { post_an, NULL };
135241675Suqsstatic	v_post	 posts_at[] = { post_at, post_defaults, NULL };
136241675Suqsstatic	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
137241675Suqsstatic	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
138241675Suqsstatic	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
139241675Suqsstatic	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
140241675Suqsstatic	v_post	 posts_bx[] = { post_bx, NULL };
141241675Suqsstatic	v_post	 posts_bool[] = { ebool, NULL };
142241675Suqsstatic	v_post	 posts_eoln[] = { post_eoln, NULL };
143241675Suqsstatic	v_post	 posts_defaults[] = { post_defaults, NULL };
144241675Suqsstatic	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
145241675Suqsstatic	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
146241675Suqsstatic	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
147241675Suqsstatic	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
148241675Suqsstatic	v_post	 posts_it[] = { post_it, NULL };
149241675Suqsstatic	v_post	 posts_lb[] = { post_lb, NULL };
150241675Suqsstatic	v_post	 posts_nd[] = { berr_ge1, NULL };
151241675Suqsstatic	v_post	 posts_nm[] = { post_nm, NULL };
152241675Suqsstatic	v_post	 posts_notext[] = { ewarn_eq0, NULL };
153241675Suqsstatic	v_post	 posts_ns[] = { post_ns, NULL };
154241675Suqsstatic	v_post	 posts_os[] = { post_os, post_prol, NULL };
155241675Suqsstatic	v_post	 posts_rs[] = { post_rs, NULL };
156241675Suqsstatic	v_post	 posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL };
157241675Suqsstatic	v_post	 posts_sp[] = { ewarn_le1, NULL };
158241675Suqsstatic	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, NULL };
159241675Suqsstatic	v_post	 posts_st[] = { post_st, NULL };
160241675Suqsstatic	v_post	 posts_std[] = { post_std, NULL };
161241675Suqsstatic	v_post	 posts_text[] = { ewarn_ge1, NULL };
162241675Suqsstatic	v_post	 posts_text1[] = { ewarn_eq1, NULL };
163241675Suqsstatic	v_post	 posts_vt[] = { post_vt, NULL };
164241675Suqsstatic	v_post	 posts_wline[] = { bwarn_ge1, NULL };
165241675Suqsstatic	v_pre	 pres_an[] = { pre_an, NULL };
166241675Suqsstatic	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
167241675Suqsstatic	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
168241675Suqsstatic	v_pre	 pres_d1[] = { pre_display, NULL };
169241675Suqsstatic	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
170241675Suqsstatic	v_pre	 pres_dd[] = { pre_dd, NULL };
171241675Suqsstatic	v_pre	 pres_dt[] = { pre_dt, NULL };
172241675Suqsstatic	v_pre	 pres_er[] = { NULL, NULL };
173241675Suqsstatic	v_pre	 pres_fd[] = { NULL, NULL };
174241675Suqsstatic	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
175241675Suqsstatic	v_pre	 pres_os[] = { pre_os, NULL };
176241675Suqsstatic	v_pre	 pres_pp[] = { pre_par, NULL };
177241675Suqsstatic	v_pre	 pres_sh[] = { pre_sh, NULL };
178241675Suqsstatic	v_pre	 pres_ss[] = { pre_ss, NULL };
179241675Suqsstatic	v_pre	 pres_std[] = { pre_std, NULL };
180241675Suqs
181241675Suqsstatic	const struct valids mdoc_valids[MDOC_MAX] = {
182241675Suqs	{ NULL, NULL },				/* Ap */
183241675Suqs	{ pres_dd, posts_dd },			/* Dd */
184241675Suqs	{ pres_dt, posts_dt },			/* Dt */
185241675Suqs	{ pres_os, posts_os },			/* Os */
186241675Suqs	{ pres_sh, posts_sh },			/* Sh */
187241675Suqs	{ pres_ss, posts_ss },			/* Ss */
188241675Suqs	{ pres_pp, posts_notext },		/* Pp */
189241675Suqs	{ pres_d1, posts_wline },		/* D1 */
190241675Suqs	{ pres_dl, posts_dl },			/* Dl */
191241675Suqs	{ pres_bd, posts_bd },			/* Bd */
192241675Suqs	{ NULL, NULL },				/* Ed */
193241675Suqs	{ pres_bl, posts_bl },			/* Bl */
194241675Suqs	{ NULL, NULL },				/* El */
195241675Suqs	{ pres_it, posts_it },			/* It */
196241675Suqs	{ NULL, NULL },				/* Ad */
197241675Suqs	{ pres_an, posts_an },			/* An */
198241675Suqs	{ NULL, posts_defaults },		/* Ar */
199241675Suqs	{ NULL, NULL },				/* Cd */
200241675Suqs	{ NULL, NULL },				/* Cm */
201241675Suqs	{ NULL, NULL },				/* Dv */
202241675Suqs	{ pres_er, NULL },			/* Er */
203241675Suqs	{ NULL, NULL },				/* Ev */
204241675Suqs	{ pres_std, posts_std },		/* Ex */
205241675Suqs	{ NULL, NULL },				/* Fa */
206241675Suqs	{ pres_fd, posts_text },		/* Fd */
207241675Suqs	{ NULL, NULL },				/* Fl */
208241675Suqs	{ NULL, NULL },				/* Fn */
209241675Suqs	{ NULL, NULL },				/* Ft */
210241675Suqs	{ NULL, NULL },				/* Ic */
211241675Suqs	{ NULL, posts_text1 },			/* In */
212241675Suqs	{ NULL, posts_defaults },		/* Li */
213241675Suqs	{ NULL, posts_nd },			/* Nd */
214241675Suqs	{ NULL, posts_nm },			/* Nm */
215241675Suqs	{ NULL, NULL },				/* Op */
216241675Suqs	{ NULL, NULL },				/* Ot */
217241675Suqs	{ NULL, posts_defaults },		/* Pa */
218241675Suqs	{ pres_std, posts_std },		/* Rv */
219241675Suqs	{ NULL, posts_st },			/* St */
220241675Suqs	{ NULL, NULL },				/* Va */
221241675Suqs	{ NULL, posts_vt },			/* Vt */
222241675Suqs	{ NULL, posts_text },			/* Xr */
223241675Suqs	{ NULL, posts_text },			/* %A */
224241675Suqs	{ NULL, posts_text },			/* %B */ /* FIXME: can be used outside Rs/Re. */
225241675Suqs	{ NULL, posts_text },			/* %D */
226241675Suqs	{ NULL, posts_text },			/* %I */
227241675Suqs	{ NULL, posts_text },			/* %J */
228241675Suqs	{ NULL, posts_text },			/* %N */
229241675Suqs	{ NULL, posts_text },			/* %O */
230241675Suqs	{ NULL, posts_text },			/* %P */
231241675Suqs	{ NULL, posts_text },			/* %R */
232241675Suqs	{ NULL, posts_text },			/* %T */ /* FIXME: can be used outside Rs/Re. */
233241675Suqs	{ NULL, posts_text },			/* %V */
234241675Suqs	{ NULL, NULL },				/* Ac */
235241675Suqs	{ NULL, NULL },				/* Ao */
236241675Suqs	{ NULL, NULL },				/* Aq */
237241675Suqs	{ NULL, posts_at },			/* At */
238241675Suqs	{ NULL, NULL },				/* Bc */
239241675Suqs	{ NULL, posts_bf },			/* Bf */
240241675Suqs	{ NULL, NULL },				/* Bo */
241241675Suqs	{ NULL, NULL },				/* Bq */
242241675Suqs	{ NULL, NULL },				/* Bsx */
243241675Suqs	{ NULL, posts_bx },			/* Bx */
244241675Suqs	{ NULL, posts_bool },			/* Db */
245241675Suqs	{ NULL, NULL },				/* Dc */
246241675Suqs	{ NULL, NULL },				/* Do */
247241675Suqs	{ NULL, NULL },				/* Dq */
248241675Suqs	{ NULL, NULL },				/* Ec */
249241675Suqs	{ NULL, NULL },				/* Ef */
250241675Suqs	{ NULL, NULL },				/* Em */
251241675Suqs	{ NULL, NULL },				/* Eo */
252241675Suqs	{ NULL, NULL },				/* Fx */
253241675Suqs	{ NULL, NULL },				/* Ms */
254241675Suqs	{ NULL, posts_notext },			/* No */
255241675Suqs	{ NULL, posts_ns },			/* Ns */
256241675Suqs	{ NULL, NULL },				/* Nx */
257241675Suqs	{ NULL, NULL },				/* Ox */
258241675Suqs	{ NULL, NULL },				/* Pc */
259241675Suqs	{ NULL, posts_text1 },			/* Pf */
260241675Suqs	{ NULL, NULL },				/* Po */
261241675Suqs	{ NULL, NULL },				/* Pq */
262241675Suqs	{ NULL, NULL },				/* Qc */
263241675Suqs	{ NULL, NULL },				/* Ql */
264241675Suqs	{ NULL, NULL },				/* Qo */
265241675Suqs	{ NULL, NULL },				/* Qq */
266241675Suqs	{ NULL, NULL },				/* Re */
267241675Suqs	{ NULL, posts_rs },			/* Rs */
268241675Suqs	{ NULL, NULL },				/* Sc */
269241675Suqs	{ NULL, NULL },				/* So */
270241675Suqs	{ NULL, NULL },				/* Sq */
271241675Suqs	{ NULL, posts_bool },			/* Sm */
272241675Suqs	{ NULL, NULL },				/* Sx */
273241675Suqs	{ NULL, NULL },				/* Sy */
274241675Suqs	{ NULL, NULL },				/* Tn */
275241675Suqs	{ NULL, NULL },				/* Ux */
276241675Suqs	{ NULL, NULL },				/* Xc */
277241675Suqs	{ NULL, NULL },				/* Xo */
278241675Suqs	{ NULL, posts_fo },			/* Fo */
279241675Suqs	{ NULL, NULL },				/* Fc */
280241675Suqs	{ NULL, NULL },				/* Oo */
281241675Suqs	{ NULL, NULL },				/* Oc */
282241675Suqs	{ NULL, posts_bk },			/* Bk */
283241675Suqs	{ NULL, NULL },				/* Ek */
284241675Suqs	{ NULL, posts_eoln },			/* Bt */
285241675Suqs	{ NULL, NULL },				/* Hf */
286241675Suqs	{ NULL, NULL },				/* Fr */
287241675Suqs	{ NULL, posts_eoln },			/* Ud */
288241675Suqs	{ NULL, posts_lb },			/* Lb */
289241675Suqs	{ NULL, posts_notext },			/* Lp */
290241675Suqs	{ NULL, NULL },				/* Lk */
291241675Suqs	{ NULL, posts_defaults },		/* Mt */
292241675Suqs	{ NULL, NULL },				/* Brq */
293241675Suqs	{ NULL, NULL },				/* Bro */
294241675Suqs	{ NULL, NULL },				/* Brc */
295241675Suqs	{ NULL, posts_text },			/* %C */
296241675Suqs	{ NULL, NULL },				/* Es */
297241675Suqs	{ NULL, NULL },				/* En */
298241675Suqs	{ NULL, NULL },				/* Dx */
299241675Suqs	{ NULL, posts_text },			/* %Q */
300241675Suqs	{ NULL, posts_notext },			/* br */
301241675Suqs	{ pres_pp, posts_sp },			/* sp */
302241675Suqs	{ NULL, posts_text1 },			/* %U */
303241675Suqs	{ NULL, NULL },				/* Ta */
304241675Suqs};
305241675Suqs
306241675Suqs#define	RSORD_MAX 14 /* Number of `Rs' blocks. */
307241675Suqs
308241675Suqsstatic	const enum mdoct rsord[RSORD_MAX] = {
309241675Suqs	MDOC__A,
310241675Suqs	MDOC__T,
311241675Suqs	MDOC__B,
312241675Suqs	MDOC__I,
313241675Suqs	MDOC__J,
314241675Suqs	MDOC__R,
315241675Suqs	MDOC__N,
316241675Suqs	MDOC__V,
317241675Suqs	MDOC__P,
318241675Suqs	MDOC__Q,
319241675Suqs	MDOC__D,
320241675Suqs	MDOC__O,
321241675Suqs	MDOC__C,
322241675Suqs	MDOC__U
323241675Suqs};
324241675Suqs
325241675Suqsstatic	const char * const secnames[SEC__MAX] = {
326241675Suqs	NULL,
327241675Suqs	"NAME",
328241675Suqs	"LIBRARY",
329241675Suqs	"SYNOPSIS",
330241675Suqs	"DESCRIPTION",
331241675Suqs	"IMPLEMENTATION NOTES",
332241675Suqs	"RETURN VALUES",
333241675Suqs	"ENVIRONMENT",
334241675Suqs	"FILES",
335241675Suqs	"EXIT STATUS",
336241675Suqs	"EXAMPLES",
337241675Suqs	"DIAGNOSTICS",
338241675Suqs	"COMPATIBILITY",
339241675Suqs	"ERRORS",
340241675Suqs	"SEE ALSO",
341241675Suqs	"STANDARDS",
342241675Suqs	"HISTORY",
343241675Suqs	"AUTHORS",
344241675Suqs	"CAVEATS",
345241675Suqs	"BUGS",
346241675Suqs	"SECURITY CONSIDERATIONS",
347241675Suqs	NULL
348241675Suqs};
349241675Suqs
350241675Suqsint
351241675Suqsmdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
352241675Suqs{
353241675Suqs	v_pre		*p;
354241675Suqs	int		 line, pos;
355241675Suqs	char		*tp;
356241675Suqs
357241675Suqs	switch (n->type) {
358241675Suqs	case (MDOC_TEXT):
359241675Suqs		tp = n->string;
360241675Suqs		line = n->line;
361241675Suqs		pos = n->pos;
362241675Suqs		check_text(mdoc, line, pos, tp);
363241675Suqs		/* FALLTHROUGH */
364241675Suqs	case (MDOC_TBL):
365241675Suqs		/* FALLTHROUGH */
366241675Suqs	case (MDOC_EQN):
367241675Suqs		/* FALLTHROUGH */
368241675Suqs	case (MDOC_ROOT):
369241675Suqs		return(1);
370241675Suqs	default:
371241675Suqs		break;
372241675Suqs	}
373241675Suqs
374241675Suqs	check_args(mdoc, n);
375241675Suqs
376241675Suqs	if (NULL == mdoc_valids[n->tok].pre)
377241675Suqs		return(1);
378241675Suqs	for (p = mdoc_valids[n->tok].pre; *p; p++)
379241675Suqs		if ( ! (*p)(mdoc, n))
380241675Suqs			return(0);
381241675Suqs	return(1);
382241675Suqs}
383241675Suqs
384241675Suqs
385241675Suqsint
386241675Suqsmdoc_valid_post(struct mdoc *mdoc)
387241675Suqs{
388241675Suqs	v_post		*p;
389241675Suqs
390241675Suqs	if (MDOC_VALID & mdoc->last->flags)
391241675Suqs		return(1);
392241675Suqs	mdoc->last->flags |= MDOC_VALID;
393241675Suqs
394241675Suqs	switch (mdoc->last->type) {
395241675Suqs	case (MDOC_TEXT):
396241675Suqs		/* FALLTHROUGH */
397241675Suqs	case (MDOC_EQN):
398241675Suqs		/* FALLTHROUGH */
399241675Suqs	case (MDOC_TBL):
400241675Suqs		return(1);
401241675Suqs	case (MDOC_ROOT):
402241675Suqs		return(post_root(mdoc));
403241675Suqs	default:
404241675Suqs		break;
405241675Suqs	}
406241675Suqs
407241675Suqs	if (NULL == mdoc_valids[mdoc->last->tok].post)
408241675Suqs		return(1);
409241675Suqs	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
410241675Suqs		if ( ! (*p)(mdoc))
411241675Suqs			return(0);
412241675Suqs
413241675Suqs	return(1);
414241675Suqs}
415241675Suqs
416241675Suqsstatic int
417241675Suqscheck_count(struct mdoc *m, enum mdoc_type type,
418241675Suqs		enum check_lvl lvl, enum check_ineq ineq, int val)
419241675Suqs{
420241675Suqs	const char	*p;
421241675Suqs	enum mandocerr	 t;
422241675Suqs
423241675Suqs	if (m->last->type != type)
424241675Suqs		return(1);
425241675Suqs
426241675Suqs	switch (ineq) {
427241675Suqs	case (CHECK_LT):
428241675Suqs		p = "less than ";
429241675Suqs		if (m->last->nchild < val)
430241675Suqs			return(1);
431241675Suqs		break;
432241675Suqs	case (CHECK_GT):
433241675Suqs		p = "more than ";
434241675Suqs		if (m->last->nchild > val)
435241675Suqs			return(1);
436241675Suqs		break;
437241675Suqs	case (CHECK_EQ):
438241675Suqs		p = "";
439241675Suqs		if (val == m->last->nchild)
440241675Suqs			return(1);
441241675Suqs		break;
442241675Suqs	default:
443241675Suqs		abort();
444241675Suqs		/* NOTREACHED */
445241675Suqs	}
446241675Suqs
447241675Suqs	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
448241675Suqs	mandoc_vmsg(t, m->parse, m->last->line, m->last->pos,
449241675Suqs			"want %s%d children (have %d)",
450241675Suqs			p, val, m->last->nchild);
451241675Suqs	return(1);
452241675Suqs}
453241675Suqs
454241675Suqsstatic int
455241675Suqsberr_ge1(POST_ARGS)
456241675Suqs{
457241675Suqs
458241675Suqs	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
459241675Suqs}
460241675Suqs
461241675Suqsstatic int
462241675Suqsbwarn_ge1(POST_ARGS)
463241675Suqs{
464241675Suqs	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
465241675Suqs}
466241675Suqs
467241675Suqsstatic int
468241675Suqsewarn_eq0(POST_ARGS)
469241675Suqs{
470241675Suqs	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
471241675Suqs}
472241675Suqs
473241675Suqsstatic int
474241675Suqsewarn_eq1(POST_ARGS)
475241675Suqs{
476241675Suqs	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
477241675Suqs}
478241675Suqs
479241675Suqsstatic int
480241675Suqsewarn_ge1(POST_ARGS)
481241675Suqs{
482241675Suqs	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
483241675Suqs}
484241675Suqs
485241675Suqsstatic int
486241675Suqsewarn_le1(POST_ARGS)
487241675Suqs{
488241675Suqs	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
489241675Suqs}
490241675Suqs
491241675Suqsstatic int
492241675Suqshwarn_eq0(POST_ARGS)
493241675Suqs{
494241675Suqs	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
495241675Suqs}
496241675Suqs
497241675Suqsstatic int
498241675Suqshwarn_eq1(POST_ARGS)
499241675Suqs{
500241675Suqs	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
501241675Suqs}
502241675Suqs
503241675Suqsstatic int
504241675Suqshwarn_ge1(POST_ARGS)
505241675Suqs{
506241675Suqs	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
507241675Suqs}
508241675Suqs
509241675Suqsstatic int
510241675Suqshwarn_le1(POST_ARGS)
511241675Suqs{
512241675Suqs	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
513241675Suqs}
514241675Suqs
515241675Suqsstatic void
516241675Suqscheck_args(struct mdoc *m, struct mdoc_node *n)
517241675Suqs{
518241675Suqs	int		 i;
519241675Suqs
520241675Suqs	if (NULL == n->args)
521241675Suqs		return;
522241675Suqs
523241675Suqs	assert(n->args->argc);
524241675Suqs	for (i = 0; i < (int)n->args->argc; i++)
525241675Suqs		check_argv(m, n, &n->args->argv[i]);
526241675Suqs}
527241675Suqs
528241675Suqsstatic void
529241675Suqscheck_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
530241675Suqs{
531241675Suqs	int		 i;
532241675Suqs
533241675Suqs	for (i = 0; i < (int)v->sz; i++)
534241675Suqs		check_text(m, v->line, v->pos, v->value[i]);
535241675Suqs
536241675Suqs	/* FIXME: move to post_std(). */
537241675Suqs
538241675Suqs	if (MDOC_Std == v->arg)
539241675Suqs		if ( ! (v->sz || m->meta.name))
540241675Suqs			mdoc_nmsg(m, n, MANDOCERR_NONAME);
541241675Suqs}
542241675Suqs
543241675Suqsstatic void
544241675Suqscheck_text(struct mdoc *m, int ln, int pos, char *p)
545241675Suqs{
546241675Suqs	char		*cp;
547241675Suqs
548241675Suqs	if (MDOC_LITERAL & m->flags)
549241675Suqs		return;
550241675Suqs
551241675Suqs	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
552241675Suqs		mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
553241675Suqs}
554241675Suqs
555241675Suqsstatic int
556241675Suqscheck_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
557241675Suqs{
558241675Suqs
559241675Suqs	assert(n->parent);
560241675Suqs	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
561241675Suqs			(t == n->parent->type))
562241675Suqs		return(1);
563241675Suqs
564241675Suqs	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
565241675Suqs			n->pos, "want parent %s", MDOC_ROOT == t ?
566241675Suqs			"<root>" : mdoc_macronames[tok]);
567241675Suqs	return(0);
568241675Suqs}
569241675Suqs
570241675Suqs
571241675Suqsstatic int
572241675Suqspre_display(PRE_ARGS)
573241675Suqs{
574241675Suqs	struct mdoc_node *node;
575241675Suqs
576241675Suqs	if (MDOC_BLOCK != n->type)
577241675Suqs		return(1);
578241675Suqs
579241675Suqs	for (node = mdoc->last->parent; node; node = node->parent)
580241675Suqs		if (MDOC_BLOCK == node->type)
581241675Suqs			if (MDOC_Bd == node->tok)
582241675Suqs				break;
583241675Suqs
584241675Suqs	if (node)
585241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
586241675Suqs
587241675Suqs	return(1);
588241675Suqs}
589241675Suqs
590241675Suqs
591241675Suqsstatic int
592241675Suqspre_bl(PRE_ARGS)
593241675Suqs{
594241675Suqs	int		  i, comp, dup;
595241675Suqs	const char	 *offs, *width;
596241675Suqs	enum mdoc_list	  lt;
597241675Suqs	struct mdoc_node *np;
598241675Suqs
599241675Suqs	if (MDOC_BLOCK != n->type) {
600241675Suqs		if (ENDBODY_NOT != n->end) {
601241675Suqs			assert(n->pending);
602241675Suqs			np = n->pending->parent;
603241675Suqs		} else
604241675Suqs			np = n->parent;
605241675Suqs
606241675Suqs		assert(np);
607241675Suqs		assert(MDOC_BLOCK == np->type);
608241675Suqs		assert(MDOC_Bl == np->tok);
609241675Suqs		return(1);
610241675Suqs	}
611241675Suqs
612241675Suqs	/*
613241675Suqs	 * First figure out which kind of list to use: bind ourselves to
614241675Suqs	 * the first mentioned list type and warn about any remaining
615241675Suqs	 * ones.  If we find no list type, we default to LIST_item.
616241675Suqs	 */
617241675Suqs
618241675Suqs	/* LINTED */
619241675Suqs	for (i = 0; n->args && i < (int)n->args->argc; i++) {
620241675Suqs		lt = LIST__NONE;
621241675Suqs		dup = comp = 0;
622241675Suqs		width = offs = NULL;
623241675Suqs		switch (n->args->argv[i].arg) {
624241675Suqs		/* Set list types. */
625241675Suqs		case (MDOC_Bullet):
626241675Suqs			lt = LIST_bullet;
627241675Suqs			break;
628241675Suqs		case (MDOC_Dash):
629241675Suqs			lt = LIST_dash;
630241675Suqs			break;
631241675Suqs		case (MDOC_Enum):
632241675Suqs			lt = LIST_enum;
633241675Suqs			break;
634241675Suqs		case (MDOC_Hyphen):
635241675Suqs			lt = LIST_hyphen;
636241675Suqs			break;
637241675Suqs		case (MDOC_Item):
638241675Suqs			lt = LIST_item;
639241675Suqs			break;
640241675Suqs		case (MDOC_Tag):
641241675Suqs			lt = LIST_tag;
642241675Suqs			break;
643241675Suqs		case (MDOC_Diag):
644241675Suqs			lt = LIST_diag;
645241675Suqs			break;
646241675Suqs		case (MDOC_Hang):
647241675Suqs			lt = LIST_hang;
648241675Suqs			break;
649241675Suqs		case (MDOC_Ohang):
650241675Suqs			lt = LIST_ohang;
651241675Suqs			break;
652241675Suqs		case (MDOC_Inset):
653241675Suqs			lt = LIST_inset;
654241675Suqs			break;
655241675Suqs		case (MDOC_Column):
656241675Suqs			lt = LIST_column;
657241675Suqs			break;
658241675Suqs		/* Set list arguments. */
659241675Suqs		case (MDOC_Compact):
660241675Suqs			dup = n->norm->Bl.comp;
661241675Suqs			comp = 1;
662241675Suqs			break;
663241675Suqs		case (MDOC_Width):
664241675Suqs			/* NB: this can be empty! */
665241675Suqs			if (n->args->argv[i].sz) {
666241675Suqs				width = n->args->argv[i].value[0];
667241675Suqs				dup = (NULL != n->norm->Bl.width);
668241675Suqs				break;
669241675Suqs			}
670241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
671241675Suqs			break;
672241675Suqs		case (MDOC_Offset):
673241675Suqs			/* NB: this can be empty! */
674241675Suqs			if (n->args->argv[i].sz) {
675241675Suqs				offs = n->args->argv[i].value[0];
676241675Suqs				dup = (NULL != n->norm->Bl.offs);
677241675Suqs				break;
678241675Suqs			}
679241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
680241675Suqs			break;
681241675Suqs		default:
682241675Suqs			continue;
683241675Suqs		}
684241675Suqs
685241675Suqs		/* Check: duplicate auxiliary arguments. */
686241675Suqs
687241675Suqs		if (dup)
688241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
689241675Suqs
690241675Suqs		if (comp && ! dup)
691241675Suqs			n->norm->Bl.comp = comp;
692241675Suqs		if (offs && ! dup)
693241675Suqs			n->norm->Bl.offs = offs;
694241675Suqs		if (width && ! dup)
695241675Suqs			n->norm->Bl.width = width;
696241675Suqs
697241675Suqs		/* Check: multiple list types. */
698241675Suqs
699241675Suqs		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
700241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
701241675Suqs
702241675Suqs		/* Assign list type. */
703241675Suqs
704241675Suqs		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
705241675Suqs			n->norm->Bl.type = lt;
706241675Suqs			/* Set column information, too. */
707241675Suqs			if (LIST_column == lt) {
708241675Suqs				n->norm->Bl.ncols =
709241675Suqs					n->args->argv[i].sz;
710241675Suqs				n->norm->Bl.cols = (void *)
711241675Suqs					n->args->argv[i].value;
712241675Suqs			}
713241675Suqs		}
714241675Suqs
715241675Suqs		/* The list type should come first. */
716241675Suqs
717241675Suqs		if (n->norm->Bl.type == LIST__NONE)
718241675Suqs			if (n->norm->Bl.width ||
719241675Suqs					n->norm->Bl.offs ||
720241675Suqs					n->norm->Bl.comp)
721241675Suqs				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
722241675Suqs
723241675Suqs		continue;
724241675Suqs	}
725241675Suqs
726241675Suqs	/* Allow lists to default to LIST_item. */
727241675Suqs
728241675Suqs	if (LIST__NONE == n->norm->Bl.type) {
729241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
730241675Suqs		n->norm->Bl.type = LIST_item;
731241675Suqs	}
732241675Suqs
733241675Suqs	/*
734241675Suqs	 * Validate the width field.  Some list types don't need width
735241675Suqs	 * types and should be warned about them.  Others should have it
736241675Suqs	 * and must also be warned.
737241675Suqs	 */
738241675Suqs
739241675Suqs	switch (n->norm->Bl.type) {
740241675Suqs	case (LIST_tag):
741241675Suqs		if (n->norm->Bl.width)
742241675Suqs			break;
743241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
744241675Suqs		break;
745241675Suqs	case (LIST_column):
746241675Suqs		/* FALLTHROUGH */
747241675Suqs	case (LIST_diag):
748241675Suqs		/* FALLTHROUGH */
749241675Suqs	case (LIST_ohang):
750241675Suqs		/* FALLTHROUGH */
751241675Suqs	case (LIST_inset):
752241675Suqs		/* FALLTHROUGH */
753241675Suqs	case (LIST_item):
754241675Suqs		if (n->norm->Bl.width)
755241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
756241675Suqs		break;
757241675Suqs	default:
758241675Suqs		break;
759241675Suqs	}
760241675Suqs
761241675Suqs	return(1);
762241675Suqs}
763241675Suqs
764241675Suqs
765241675Suqsstatic int
766241675Suqspre_bd(PRE_ARGS)
767241675Suqs{
768241675Suqs	int		  i, dup, comp;
769241675Suqs	enum mdoc_disp 	  dt;
770241675Suqs	const char	 *offs;
771241675Suqs	struct mdoc_node *np;
772241675Suqs
773241675Suqs	if (MDOC_BLOCK != n->type) {
774241675Suqs		if (ENDBODY_NOT != n->end) {
775241675Suqs			assert(n->pending);
776241675Suqs			np = n->pending->parent;
777241675Suqs		} else
778241675Suqs			np = n->parent;
779241675Suqs
780241675Suqs		assert(np);
781241675Suqs		assert(MDOC_BLOCK == np->type);
782241675Suqs		assert(MDOC_Bd == np->tok);
783241675Suqs		return(1);
784241675Suqs	}
785241675Suqs
786241675Suqs	/* LINTED */
787241675Suqs	for (i = 0; n->args && i < (int)n->args->argc; i++) {
788241675Suqs		dt = DISP__NONE;
789241675Suqs		dup = comp = 0;
790241675Suqs		offs = NULL;
791241675Suqs
792241675Suqs		switch (n->args->argv[i].arg) {
793241675Suqs		case (MDOC_Centred):
794241675Suqs			dt = DISP_centred;
795241675Suqs			break;
796241675Suqs		case (MDOC_Ragged):
797241675Suqs			dt = DISP_ragged;
798241675Suqs			break;
799241675Suqs		case (MDOC_Unfilled):
800241675Suqs			dt = DISP_unfilled;
801241675Suqs			break;
802241675Suqs		case (MDOC_Filled):
803241675Suqs			dt = DISP_filled;
804241675Suqs			break;
805241675Suqs		case (MDOC_Literal):
806241675Suqs			dt = DISP_literal;
807241675Suqs			break;
808241675Suqs		case (MDOC_File):
809241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
810241675Suqs			return(0);
811241675Suqs		case (MDOC_Offset):
812241675Suqs			/* NB: this can be empty! */
813241675Suqs			if (n->args->argv[i].sz) {
814241675Suqs				offs = n->args->argv[i].value[0];
815241675Suqs				dup = (NULL != n->norm->Bd.offs);
816241675Suqs				break;
817241675Suqs			}
818241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
819241675Suqs			break;
820241675Suqs		case (MDOC_Compact):
821241675Suqs			comp = 1;
822241675Suqs			dup = n->norm->Bd.comp;
823241675Suqs			break;
824241675Suqs		default:
825241675Suqs			abort();
826241675Suqs			/* NOTREACHED */
827241675Suqs		}
828241675Suqs
829241675Suqs		/* Check whether we have duplicates. */
830241675Suqs
831241675Suqs		if (dup)
832241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
833241675Suqs
834241675Suqs		/* Make our auxiliary assignments. */
835241675Suqs
836241675Suqs		if (offs && ! dup)
837241675Suqs			n->norm->Bd.offs = offs;
838241675Suqs		if (comp && ! dup)
839241675Suqs			n->norm->Bd.comp = comp;
840241675Suqs
841241675Suqs		/* Check whether a type has already been assigned. */
842241675Suqs
843241675Suqs		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
844241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
845241675Suqs
846241675Suqs		/* Make our type assignment. */
847241675Suqs
848241675Suqs		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
849241675Suqs			n->norm->Bd.type = dt;
850241675Suqs	}
851241675Suqs
852241675Suqs	if (DISP__NONE == n->norm->Bd.type) {
853241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
854241675Suqs		n->norm->Bd.type = DISP_ragged;
855241675Suqs	}
856241675Suqs
857241675Suqs	return(1);
858241675Suqs}
859241675Suqs
860241675Suqs
861241675Suqsstatic int
862241675Suqspre_ss(PRE_ARGS)
863241675Suqs{
864241675Suqs
865241675Suqs	if (MDOC_BLOCK != n->type)
866241675Suqs		return(1);
867241675Suqs	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
868241675Suqs}
869241675Suqs
870241675Suqs
871241675Suqsstatic int
872241675Suqspre_sh(PRE_ARGS)
873241675Suqs{
874241675Suqs
875241675Suqs	if (MDOC_BLOCK != n->type)
876241675Suqs		return(1);
877241675Suqs
878241675Suqs	roff_regunset(mdoc->roff, REG_nS);
879241675Suqs	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
880241675Suqs}
881241675Suqs
882241675Suqs
883241675Suqsstatic int
884241675Suqspre_it(PRE_ARGS)
885241675Suqs{
886241675Suqs
887241675Suqs	if (MDOC_BLOCK != n->type)
888241675Suqs		return(1);
889241675Suqs
890241675Suqs	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
891241675Suqs}
892241675Suqs
893241675Suqs
894241675Suqsstatic int
895241675Suqspre_an(PRE_ARGS)
896241675Suqs{
897241675Suqs	int		 i;
898241675Suqs
899241675Suqs	if (NULL == n->args)
900241675Suqs		return(1);
901241675Suqs
902241675Suqs	for (i = 1; i < (int)n->args->argc; i++)
903241675Suqs		mdoc_pmsg(mdoc, n->args->argv[i].line,
904241675Suqs			n->args->argv[i].pos, MANDOCERR_IGNARGV);
905241675Suqs
906241675Suqs	if (MDOC_Split == n->args->argv[0].arg)
907241675Suqs		n->norm->An.auth = AUTH_split;
908241675Suqs	else if (MDOC_Nosplit == n->args->argv[0].arg)
909241675Suqs		n->norm->An.auth = AUTH_nosplit;
910241675Suqs	else
911241675Suqs		abort();
912241675Suqs
913241675Suqs	return(1);
914241675Suqs}
915241675Suqs
916241675Suqsstatic int
917241675Suqspre_std(PRE_ARGS)
918241675Suqs{
919241675Suqs
920241675Suqs	if (n->args && 1 == n->args->argc)
921241675Suqs		if (MDOC_Std == n->args->argv[0].arg)
922241675Suqs			return(1);
923241675Suqs
924241675Suqs	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
925241675Suqs	return(1);
926241675Suqs}
927241675Suqs
928241675Suqsstatic int
929241675Suqspre_dt(PRE_ARGS)
930241675Suqs{
931241675Suqs
932241675Suqs	if (NULL == mdoc->meta.date || mdoc->meta.os)
933241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
934241675Suqs
935241675Suqs	if (mdoc->meta.title)
936241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
937241675Suqs
938241675Suqs	return(1);
939241675Suqs}
940241675Suqs
941241675Suqsstatic int
942241675Suqspre_os(PRE_ARGS)
943241675Suqs{
944241675Suqs
945241675Suqs	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
946241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
947241675Suqs
948241675Suqs	if (mdoc->meta.os)
949241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
950241675Suqs
951241675Suqs	return(1);
952241675Suqs}
953241675Suqs
954241675Suqsstatic int
955241675Suqspre_dd(PRE_ARGS)
956241675Suqs{
957241675Suqs
958241675Suqs	if (mdoc->meta.title || mdoc->meta.os)
959241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
960241675Suqs
961241675Suqs	if (mdoc->meta.date)
962241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
963241675Suqs
964241675Suqs	return(1);
965241675Suqs}
966241675Suqs
967241675Suqs
968241675Suqsstatic int
969241675Suqspost_bf(POST_ARGS)
970241675Suqs{
971241675Suqs	struct mdoc_node *np;
972241675Suqs	enum mdocargt	  arg;
973241675Suqs
974241675Suqs	/*
975241675Suqs	 * Unlike other data pointers, these are "housed" by the HEAD
976241675Suqs	 * element, which contains the goods.
977241675Suqs	 */
978241675Suqs
979241675Suqs	if (MDOC_HEAD != mdoc->last->type) {
980241675Suqs		if (ENDBODY_NOT != mdoc->last->end) {
981241675Suqs			assert(mdoc->last->pending);
982241675Suqs			np = mdoc->last->pending->parent->head;
983241675Suqs		} else if (MDOC_BLOCK != mdoc->last->type) {
984241675Suqs			np = mdoc->last->parent->head;
985241675Suqs		} else
986241675Suqs			np = mdoc->last->head;
987241675Suqs
988241675Suqs		assert(np);
989241675Suqs		assert(MDOC_HEAD == np->type);
990241675Suqs		assert(MDOC_Bf == np->tok);
991241675Suqs		return(1);
992241675Suqs	}
993241675Suqs
994241675Suqs	np = mdoc->last;
995241675Suqs	assert(MDOC_BLOCK == np->parent->type);
996241675Suqs	assert(MDOC_Bf == np->parent->tok);
997241675Suqs
998241675Suqs	/*
999241675Suqs	 * Cannot have both argument and parameter.
1000241675Suqs	 * If neither is specified, let it through with a warning.
1001241675Suqs	 */
1002241675Suqs
1003241675Suqs	if (np->parent->args && np->child) {
1004241675Suqs		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1005241675Suqs		return(0);
1006241675Suqs	} else if (NULL == np->parent->args && NULL == np->child) {
1007241675Suqs		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1008241675Suqs		return(1);
1009241675Suqs	}
1010241675Suqs
1011241675Suqs	/* Extract argument into data. */
1012241675Suqs
1013241675Suqs	if (np->parent->args) {
1014241675Suqs		arg = np->parent->args->argv[0].arg;
1015241675Suqs		if (MDOC_Emphasis == arg)
1016241675Suqs			np->norm->Bf.font = FONT_Em;
1017241675Suqs		else if (MDOC_Literal == arg)
1018241675Suqs			np->norm->Bf.font = FONT_Li;
1019241675Suqs		else if (MDOC_Symbolic == arg)
1020241675Suqs			np->norm->Bf.font = FONT_Sy;
1021241675Suqs		else
1022241675Suqs			abort();
1023241675Suqs		return(1);
1024241675Suqs	}
1025241675Suqs
1026241675Suqs	/* Extract parameter into data. */
1027241675Suqs
1028241675Suqs	if (0 == strcmp(np->child->string, "Em"))
1029241675Suqs		np->norm->Bf.font = FONT_Em;
1030241675Suqs	else if (0 == strcmp(np->child->string, "Li"))
1031241675Suqs		np->norm->Bf.font = FONT_Li;
1032241675Suqs	else if (0 == strcmp(np->child->string, "Sy"))
1033241675Suqs		np->norm->Bf.font = FONT_Sy;
1034241675Suqs	else
1035241675Suqs		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1036241675Suqs
1037241675Suqs	return(1);
1038241675Suqs}
1039241675Suqs
1040241675Suqsstatic int
1041241675Suqspost_lb(POST_ARGS)
1042241675Suqs{
1043241675Suqs	const char	*p;
1044241675Suqs	char		*buf;
1045241675Suqs	size_t		 sz;
1046241675Suqs
1047241675Suqs	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1048241675Suqs
1049241675Suqs	assert(mdoc->last->child);
1050241675Suqs	assert(MDOC_TEXT == mdoc->last->child->type);
1051241675Suqs
1052241675Suqs	p = mdoc_a2lib(mdoc->last->child->string);
1053241675Suqs
1054241675Suqs	/* If lookup ok, replace with table value. */
1055241675Suqs
1056241675Suqs	if (p) {
1057241675Suqs		free(mdoc->last->child->string);
1058241675Suqs		mdoc->last->child->string = mandoc_strdup(p);
1059241675Suqs		return(1);
1060241675Suqs	}
1061241675Suqs
1062241675Suqs	/* If not, use "library ``xxxx''. */
1063241675Suqs
1064241675Suqs	sz = strlen(mdoc->last->child->string) +
1065241675Suqs		2 + strlen("\\(lqlibrary\\(rq");
1066241675Suqs	buf = mandoc_malloc(sz);
1067241675Suqs	snprintf(buf, sz, "library \\(lq%s\\(rq",
1068241675Suqs			mdoc->last->child->string);
1069241675Suqs	free(mdoc->last->child->string);
1070241675Suqs	mdoc->last->child->string = buf;
1071241675Suqs	return(1);
1072241675Suqs}
1073241675Suqs
1074241675Suqsstatic int
1075241675Suqspost_eoln(POST_ARGS)
1076241675Suqs{
1077241675Suqs
1078241675Suqs	if (mdoc->last->child)
1079241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1080241675Suqs	return(1);
1081241675Suqs}
1082241675Suqs
1083241675Suqs
1084241675Suqsstatic int
1085241675Suqspost_vt(POST_ARGS)
1086241675Suqs{
1087241675Suqs	const struct mdoc_node *n;
1088241675Suqs
1089241675Suqs	/*
1090241675Suqs	 * The Vt macro comes in both ELEM and BLOCK form, both of which
1091241675Suqs	 * have different syntaxes (yet more context-sensitive
1092241675Suqs	 * behaviour).  ELEM types must have a child, which is already
1093241675Suqs	 * guaranteed by the in_line parsing routine; BLOCK types,
1094241675Suqs	 * specifically the BODY, should only have TEXT children.
1095241675Suqs	 */
1096241675Suqs
1097241675Suqs	if (MDOC_BODY != mdoc->last->type)
1098241675Suqs		return(1);
1099241675Suqs
1100241675Suqs	for (n = mdoc->last->child; n; n = n->next)
1101241675Suqs		if (MDOC_TEXT != n->type)
1102241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1103241675Suqs
1104241675Suqs	return(1);
1105241675Suqs}
1106241675Suqs
1107241675Suqs
1108241675Suqsstatic int
1109241675Suqspost_nm(POST_ARGS)
1110241675Suqs{
1111241675Suqs	char		 buf[BUFSIZ];
1112241675Suqs	int		 c;
1113241675Suqs
1114241675Suqs	/* If no child specified, make sure we have the meta name. */
1115241675Suqs
1116241675Suqs	if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1117241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1118241675Suqs		return(1);
1119241675Suqs	} else if (mdoc->meta.name)
1120241675Suqs		return(1);
1121241675Suqs
1122241675Suqs	/* If no meta name, set it from the child. */
1123241675Suqs
1124241675Suqs	buf[0] = '\0';
1125241675Suqs	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1126241675Suqs		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1127241675Suqs		return(0);
1128241675Suqs	}
1129241675Suqs
1130241675Suqs	assert(c);
1131241675Suqs	mdoc->meta.name = mandoc_strdup(buf);
1132241675Suqs	return(1);
1133241675Suqs}
1134241675Suqs
1135241675Suqsstatic int
1136241675Suqspost_literal(POST_ARGS)
1137241675Suqs{
1138241675Suqs
1139241675Suqs	/*
1140241675Suqs	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1141241675Suqs	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1142241675Suqs	 * this in literal mode, but it doesn't hurt to just switch it
1143241675Suqs	 * off in general since displays can't be nested.
1144241675Suqs	 */
1145241675Suqs
1146241675Suqs	if (MDOC_BODY == mdoc->last->type)
1147241675Suqs		mdoc->flags &= ~MDOC_LITERAL;
1148241675Suqs
1149241675Suqs	return(1);
1150241675Suqs}
1151241675Suqs
1152241675Suqsstatic int
1153241675Suqspost_defaults(POST_ARGS)
1154241675Suqs{
1155241675Suqs	struct mdoc_node *nn;
1156241675Suqs
1157241675Suqs	/*
1158241675Suqs	 * The `Ar' defaults to "file ..." if no value is provided as an
1159241675Suqs	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1160241675Suqs	 * gets an empty string.
1161241675Suqs	 */
1162241675Suqs
1163241675Suqs	if (mdoc->last->child)
1164241675Suqs		return(1);
1165241675Suqs
1166241675Suqs	nn = mdoc->last;
1167241675Suqs	mdoc->next = MDOC_NEXT_CHILD;
1168241675Suqs
1169241675Suqs	switch (nn->tok) {
1170241675Suqs	case (MDOC_Ar):
1171241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1172241675Suqs			return(0);
1173241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1174241675Suqs			return(0);
1175241675Suqs		break;
1176241675Suqs	case (MDOC_At):
1177241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1178241675Suqs			return(0);
1179241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1180241675Suqs			return(0);
1181241675Suqs		break;
1182241675Suqs	case (MDOC_Li):
1183241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1184241675Suqs			return(0);
1185241675Suqs		break;
1186241675Suqs	case (MDOC_Pa):
1187241675Suqs		/* FALLTHROUGH */
1188241675Suqs	case (MDOC_Mt):
1189241675Suqs		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1190241675Suqs			return(0);
1191241675Suqs		break;
1192241675Suqs	default:
1193241675Suqs		abort();
1194241675Suqs		/* NOTREACHED */
1195241675Suqs	}
1196241675Suqs
1197241675Suqs	mdoc->last = nn;
1198241675Suqs	return(1);
1199241675Suqs}
1200241675Suqs
1201241675Suqsstatic int
1202241675Suqspost_at(POST_ARGS)
1203241675Suqs{
1204241675Suqs	const char	 *p, *q;
1205241675Suqs	char		 *buf;
1206241675Suqs	size_t		  sz;
1207241675Suqs
1208241675Suqs	/*
1209241675Suqs	 * If we have a child, look it up in the standard keys.  If a
1210241675Suqs	 * key exist, use that instead of the child; if it doesn't,
1211241675Suqs	 * prefix "AT&T UNIX " to the existing data.
1212241675Suqs	 */
1213241675Suqs
1214241675Suqs	if (NULL == mdoc->last->child)
1215241675Suqs		return(1);
1216241675Suqs
1217241675Suqs	assert(MDOC_TEXT == mdoc->last->child->type);
1218241675Suqs	p = mdoc_a2att(mdoc->last->child->string);
1219241675Suqs
1220241675Suqs	if (p) {
1221241675Suqs		free(mdoc->last->child->string);
1222241675Suqs		mdoc->last->child->string = mandoc_strdup(p);
1223241675Suqs	} else {
1224241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1225241675Suqs		p = "AT&T UNIX ";
1226241675Suqs		q = mdoc->last->child->string;
1227241675Suqs		sz = strlen(p) + strlen(q) + 1;
1228241675Suqs		buf = mandoc_malloc(sz);
1229241675Suqs		strlcpy(buf, p, sz);
1230241675Suqs		strlcat(buf, q, sz);
1231241675Suqs		free(mdoc->last->child->string);
1232241675Suqs		mdoc->last->child->string = buf;
1233241675Suqs	}
1234241675Suqs
1235241675Suqs	return(1);
1236241675Suqs}
1237241675Suqs
1238241675Suqsstatic int
1239241675Suqspost_an(POST_ARGS)
1240241675Suqs{
1241241675Suqs	struct mdoc_node *np;
1242241675Suqs
1243241675Suqs	np = mdoc->last;
1244241675Suqs	if (AUTH__NONE == np->norm->An.auth) {
1245241675Suqs		if (0 == np->child)
1246241675Suqs			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1247241675Suqs	} else if (np->child)
1248241675Suqs		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1249241675Suqs
1250241675Suqs	return(1);
1251241675Suqs}
1252241675Suqs
1253241675Suqs
1254241675Suqsstatic int
1255241675Suqspost_it(POST_ARGS)
1256241675Suqs{
1257241675Suqs	int		  i, cols;
1258241675Suqs	enum mdoc_list	  lt;
1259241675Suqs	struct mdoc_node *n, *c;
1260241675Suqs	enum mandocerr	  er;
1261241675Suqs
1262241675Suqs	if (MDOC_BLOCK != mdoc->last->type)
1263241675Suqs		return(1);
1264241675Suqs
1265241675Suqs	n = mdoc->last->parent->parent;
1266241675Suqs	lt = n->norm->Bl.type;
1267241675Suqs
1268241675Suqs	if (LIST__NONE == lt) {
1269241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1270241675Suqs		return(1);
1271241675Suqs	}
1272241675Suqs
1273241675Suqs	switch (lt) {
1274241675Suqs	case (LIST_tag):
1275241675Suqs		if (mdoc->last->head->child)
1276241675Suqs			break;
1277241675Suqs		/* FIXME: give this a dummy value. */
1278241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1279241675Suqs		break;
1280241675Suqs	case (LIST_hang):
1281241675Suqs		/* FALLTHROUGH */
1282241675Suqs	case (LIST_ohang):
1283241675Suqs		/* FALLTHROUGH */
1284241675Suqs	case (LIST_inset):
1285241675Suqs		/* FALLTHROUGH */
1286241675Suqs	case (LIST_diag):
1287241675Suqs		if (NULL == mdoc->last->head->child)
1288241675Suqs			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1289241675Suqs		break;
1290241675Suqs	case (LIST_bullet):
1291241675Suqs		/* FALLTHROUGH */
1292241675Suqs	case (LIST_dash):
1293241675Suqs		/* FALLTHROUGH */
1294241675Suqs	case (LIST_enum):
1295241675Suqs		/* FALLTHROUGH */
1296241675Suqs	case (LIST_hyphen):
1297241675Suqs		if (NULL == mdoc->last->body->child)
1298241675Suqs			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1299241675Suqs		/* FALLTHROUGH */
1300241675Suqs	case (LIST_item):
1301241675Suqs		if (mdoc->last->head->child)
1302241675Suqs			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1303241675Suqs		break;
1304241675Suqs	case (LIST_column):
1305241675Suqs		cols = (int)n->norm->Bl.ncols;
1306241675Suqs
1307241675Suqs		assert(NULL == mdoc->last->head->child);
1308241675Suqs
1309241675Suqs		if (NULL == mdoc->last->body->child)
1310241675Suqs			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1311241675Suqs
1312241675Suqs		for (i = 0, c = mdoc->last->child; c; c = c->next)
1313241675Suqs			if (MDOC_BODY == c->type)
1314241675Suqs				i++;
1315241675Suqs
1316241675Suqs		if (i < cols)
1317241675Suqs			er = MANDOCERR_ARGCOUNT;
1318241675Suqs		else if (i == cols || i == cols + 1)
1319241675Suqs			break;
1320241675Suqs		else
1321241675Suqs			er = MANDOCERR_SYNTARGCOUNT;
1322241675Suqs
1323241675Suqs		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1324241675Suqs				mdoc->last->pos,
1325241675Suqs				"columns == %d (have %d)", cols, i);
1326241675Suqs		return(MANDOCERR_ARGCOUNT == er);
1327241675Suqs	default:
1328241675Suqs		break;
1329241675Suqs	}
1330241675Suqs
1331241675Suqs	return(1);
1332241675Suqs}
1333241675Suqs
1334241675Suqsstatic int
1335241675Suqspost_bl_block(POST_ARGS)
1336241675Suqs{
1337241675Suqs	struct mdoc_node *n;
1338241675Suqs
1339241675Suqs	/*
1340241675Suqs	 * These are fairly complicated, so we've broken them into two
1341241675Suqs	 * functions.  post_bl_block_tag() is called when a -tag is
1342241675Suqs	 * specified, but no -width (it must be guessed).  The second
1343241675Suqs	 * when a -width is specified (macro indicators must be
1344241675Suqs	 * rewritten into real lengths).
1345241675Suqs	 */
1346241675Suqs
1347241675Suqs	n = mdoc->last;
1348241675Suqs
1349241675Suqs	if (LIST_tag == n->norm->Bl.type &&
1350241675Suqs			NULL == n->norm->Bl.width) {
1351241675Suqs		if ( ! post_bl_block_tag(mdoc))
1352241675Suqs			return(0);
1353241675Suqs	} else if (NULL != n->norm->Bl.width) {
1354241675Suqs		if ( ! post_bl_block_width(mdoc))
1355241675Suqs			return(0);
1356241675Suqs	} else
1357241675Suqs		return(1);
1358241675Suqs
1359241675Suqs	assert(n->norm->Bl.width);
1360241675Suqs	return(1);
1361241675Suqs}
1362241675Suqs
1363241675Suqsstatic int
1364241675Suqspost_bl_block_width(POST_ARGS)
1365241675Suqs{
1366241675Suqs	size_t		  width;
1367241675Suqs	int		  i;
1368241675Suqs	enum mdoct	  tok;
1369241675Suqs	struct mdoc_node *n;
1370241675Suqs	char		  buf[NUMSIZ];
1371241675Suqs
1372241675Suqs	n = mdoc->last;
1373241675Suqs
1374241675Suqs	/*
1375241675Suqs	 * Calculate the real width of a list from the -width string,
1376241675Suqs	 * which may contain a macro (with a known default width), a
1377241675Suqs	 * literal string, or a scaling width.
1378241675Suqs	 *
1379241675Suqs	 * If the value to -width is a macro, then we re-write it to be
1380241675Suqs	 * the macro's width as set in share/tmac/mdoc/doc-common.
1381241675Suqs	 */
1382241675Suqs
1383241675Suqs	if (0 == strcmp(n->norm->Bl.width, "Ds"))
1384241675Suqs		width = 6;
1385241675Suqs	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1386241675Suqs		return(1);
1387241675Suqs	else if (0 == (width = macro2len(tok)))  {
1388241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1389241675Suqs		return(1);
1390241675Suqs	}
1391241675Suqs
1392241675Suqs	/* The value already exists: free and reallocate it. */
1393241675Suqs
1394241675Suqs	assert(n->args);
1395241675Suqs
1396241675Suqs	for (i = 0; i < (int)n->args->argc; i++)
1397241675Suqs		if (MDOC_Width == n->args->argv[i].arg)
1398241675Suqs			break;
1399241675Suqs
1400241675Suqs	assert(i < (int)n->args->argc);
1401241675Suqs
1402241675Suqs	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1403241675Suqs	free(n->args->argv[i].value[0]);
1404241675Suqs	n->args->argv[i].value[0] = mandoc_strdup(buf);
1405241675Suqs
1406241675Suqs	/* Set our width! */
1407241675Suqs	n->norm->Bl.width = n->args->argv[i].value[0];
1408241675Suqs	return(1);
1409241675Suqs}
1410241675Suqs
1411241675Suqsstatic int
1412241675Suqspost_bl_block_tag(POST_ARGS)
1413241675Suqs{
1414241675Suqs	struct mdoc_node *n, *nn;
1415241675Suqs	size_t		  sz, ssz;
1416241675Suqs	int		  i;
1417241675Suqs	char		  buf[NUMSIZ];
1418241675Suqs
1419241675Suqs	/*
1420241675Suqs	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1421241675Suqs	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1422241675Suqs	 * ONLY if the -width argument has NOT been provided.  See
1423241675Suqs	 * post_bl_block_width() for converting the -width string.
1424241675Suqs	 */
1425241675Suqs
1426241675Suqs	sz = 10;
1427241675Suqs	n = mdoc->last;
1428241675Suqs
1429241675Suqs	for (nn = n->body->child; nn; nn = nn->next) {
1430241675Suqs		if (MDOC_It != nn->tok)
1431241675Suqs			continue;
1432241675Suqs
1433241675Suqs		assert(MDOC_BLOCK == nn->type);
1434241675Suqs		nn = nn->head->child;
1435241675Suqs
1436241675Suqs		if (nn == NULL)
1437241675Suqs			break;
1438241675Suqs
1439241675Suqs		if (MDOC_TEXT == nn->type) {
1440241675Suqs			sz = strlen(nn->string) + 1;
1441241675Suqs			break;
1442241675Suqs		}
1443241675Suqs
1444241675Suqs		if (0 != (ssz = macro2len(nn->tok)))
1445241675Suqs			sz = ssz;
1446241675Suqs
1447241675Suqs		break;
1448241675Suqs	}
1449241675Suqs
1450241675Suqs	/* Defaults to ten ens. */
1451241675Suqs
1452241675Suqs	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1453241675Suqs
1454241675Suqs	/*
1455241675Suqs	 * We have to dynamically add this to the macro's argument list.
1456241675Suqs	 * We're guaranteed that a MDOC_Width doesn't already exist.
1457241675Suqs	 */
1458241675Suqs
1459241675Suqs	assert(n->args);
1460241675Suqs	i = (int)(n->args->argc)++;
1461241675Suqs
1462241675Suqs	n->args->argv = mandoc_realloc(n->args->argv,
1463241675Suqs			n->args->argc * sizeof(struct mdoc_argv));
1464241675Suqs
1465241675Suqs	n->args->argv[i].arg = MDOC_Width;
1466241675Suqs	n->args->argv[i].line = n->line;
1467241675Suqs	n->args->argv[i].pos = n->pos;
1468241675Suqs	n->args->argv[i].sz = 1;
1469241675Suqs	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1470241675Suqs	n->args->argv[i].value[0] = mandoc_strdup(buf);
1471241675Suqs
1472241675Suqs	/* Set our width! */
1473241675Suqs	n->norm->Bl.width = n->args->argv[i].value[0];
1474241675Suqs	return(1);
1475241675Suqs}
1476241675Suqs
1477241675Suqs
1478241675Suqsstatic int
1479241675Suqspost_bl_head(POST_ARGS)
1480241675Suqs{
1481241675Suqs	struct mdoc_node *np, *nn, *nnp;
1482241675Suqs	int		  i, j;
1483241675Suqs
1484241675Suqs	if (LIST_column != mdoc->last->norm->Bl.type)
1485241675Suqs		/* FIXME: this should be ERROR class... */
1486241675Suqs		return(hwarn_eq0(mdoc));
1487241675Suqs
1488241675Suqs	/*
1489241675Suqs	 * Convert old-style lists, where the column width specifiers
1490241675Suqs	 * trail as macro parameters, to the new-style ("normal-form")
1491241675Suqs	 * lists where they're argument values following -column.
1492241675Suqs	 */
1493241675Suqs
1494241675Suqs	/* First, disallow both types and allow normal-form. */
1495241675Suqs
1496241675Suqs	/*
1497241675Suqs	 * TODO: technically, we can accept both and just merge the two
1498241675Suqs	 * lists, but I'll leave that for another day.
1499241675Suqs	 */
1500241675Suqs
1501241675Suqs	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1502241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1503241675Suqs		return(0);
1504241675Suqs	} else if (NULL == mdoc->last->child)
1505241675Suqs		return(1);
1506241675Suqs
1507241675Suqs	np = mdoc->last->parent;
1508241675Suqs	assert(np->args);
1509241675Suqs
1510241675Suqs	for (j = 0; j < (int)np->args->argc; j++)
1511241675Suqs		if (MDOC_Column == np->args->argv[j].arg)
1512241675Suqs			break;
1513241675Suqs
1514241675Suqs	assert(j < (int)np->args->argc);
1515241675Suqs	assert(0 == np->args->argv[j].sz);
1516241675Suqs
1517241675Suqs	/*
1518241675Suqs	 * Accommodate for new-style groff column syntax.  Shuffle the
1519241675Suqs	 * child nodes, all of which must be TEXT, as arguments for the
1520241675Suqs	 * column field.  Then, delete the head children.
1521241675Suqs	 */
1522241675Suqs
1523241675Suqs	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1524241675Suqs	np->args->argv[j].value = mandoc_malloc
1525241675Suqs		((size_t)mdoc->last->nchild * sizeof(char *));
1526241675Suqs
1527241675Suqs	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1528241675Suqs	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1529241675Suqs
1530241675Suqs	for (i = 0, nn = mdoc->last->child; nn; i++) {
1531241675Suqs		np->args->argv[j].value[i] = nn->string;
1532241675Suqs		nn->string = NULL;
1533241675Suqs		nnp = nn;
1534241675Suqs		nn = nn->next;
1535241675Suqs		mdoc_node_delete(NULL, nnp);
1536241675Suqs	}
1537241675Suqs
1538241675Suqs	mdoc->last->nchild = 0;
1539241675Suqs	mdoc->last->child = NULL;
1540241675Suqs
1541241675Suqs	return(1);
1542241675Suqs}
1543241675Suqs
1544241675Suqsstatic int
1545241675Suqspost_bl(POST_ARGS)
1546241675Suqs{
1547241675Suqs	struct mdoc_node	*n;
1548241675Suqs
1549241675Suqs	if (MDOC_HEAD == mdoc->last->type)
1550241675Suqs		return(post_bl_head(mdoc));
1551241675Suqs	if (MDOC_BLOCK == mdoc->last->type)
1552241675Suqs		return(post_bl_block(mdoc));
1553241675Suqs	if (MDOC_BODY != mdoc->last->type)
1554241675Suqs		return(1);
1555241675Suqs
1556241675Suqs	for (n = mdoc->last->child; n; n = n->next) {
1557241675Suqs		switch (n->tok) {
1558241675Suqs		case (MDOC_Lp):
1559241675Suqs			/* FALLTHROUGH */
1560241675Suqs		case (MDOC_Pp):
1561241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1562241675Suqs			/* FALLTHROUGH */
1563241675Suqs		case (MDOC_It):
1564241675Suqs			/* FALLTHROUGH */
1565241675Suqs		case (MDOC_Sm):
1566241675Suqs			continue;
1567241675Suqs		default:
1568241675Suqs			break;
1569241675Suqs		}
1570241675Suqs
1571241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1572241675Suqs		return(0);
1573241675Suqs	}
1574241675Suqs
1575241675Suqs	return(1);
1576241675Suqs}
1577241675Suqs
1578241675Suqsstatic int
1579241675Suqsebool(struct mdoc *mdoc)
1580241675Suqs{
1581241675Suqs
1582241675Suqs	if (NULL == mdoc->last->child) {
1583241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1584241675Suqs		mdoc_node_delete(mdoc, mdoc->last);
1585241675Suqs		return(1);
1586241675Suqs	}
1587241675Suqs	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1588241675Suqs
1589241675Suqs	assert(MDOC_TEXT == mdoc->last->child->type);
1590241675Suqs
1591241675Suqs	if (0 == strcmp(mdoc->last->child->string, "on"))
1592241675Suqs		return(1);
1593241675Suqs	if (0 == strcmp(mdoc->last->child->string, "off"))
1594241675Suqs		return(1);
1595241675Suqs
1596241675Suqs	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1597241675Suqs	return(1);
1598241675Suqs}
1599241675Suqs
1600241675Suqsstatic int
1601241675Suqspost_root(POST_ARGS)
1602241675Suqs{
1603241675Suqs	int		  erc;
1604241675Suqs	struct mdoc_node *n;
1605241675Suqs
1606241675Suqs	erc = 0;
1607241675Suqs
1608241675Suqs	/* Check that we have a finished prologue. */
1609241675Suqs
1610241675Suqs	if ( ! (MDOC_PBODY & mdoc->flags)) {
1611241675Suqs		erc++;
1612241675Suqs		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1613241675Suqs	}
1614241675Suqs
1615241675Suqs	n = mdoc->first;
1616241675Suqs	assert(n);
1617241675Suqs
1618241675Suqs	/* Check that we begin with a proper `Sh'. */
1619241675Suqs
1620241675Suqs	if (NULL == n->child) {
1621241675Suqs		erc++;
1622241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1623241675Suqs	} else if (MDOC_BLOCK != n->child->type ||
1624241675Suqs			MDOC_Sh != n->child->tok) {
1625241675Suqs		erc++;
1626241675Suqs		/* Can this be lifted?  See rxdebug.1 for example. */
1627241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1628241675Suqs	}
1629241675Suqs
1630241675Suqs	return(erc ? 0 : 1);
1631241675Suqs}
1632241675Suqs
1633241675Suqsstatic int
1634241675Suqspost_st(POST_ARGS)
1635241675Suqs{
1636241675Suqs	struct mdoc_node	 *ch;
1637241675Suqs	const char		 *p;
1638241675Suqs
1639241675Suqs	if (NULL == (ch = mdoc->last->child)) {
1640241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1641241675Suqs		mdoc_node_delete(mdoc, mdoc->last);
1642241675Suqs		return(1);
1643241675Suqs	}
1644241675Suqs
1645241675Suqs	assert(MDOC_TEXT == ch->type);
1646241675Suqs
1647241675Suqs	if (NULL == (p = mdoc_a2st(ch->string))) {
1648241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1649241675Suqs		mdoc_node_delete(mdoc, mdoc->last);
1650241675Suqs	} else {
1651241675Suqs		free(ch->string);
1652241675Suqs		ch->string = mandoc_strdup(p);
1653241675Suqs	}
1654241675Suqs
1655241675Suqs	return(1);
1656241675Suqs}
1657241675Suqs
1658241675Suqsstatic int
1659241675Suqspost_rs(POST_ARGS)
1660241675Suqs{
1661241675Suqs	struct mdoc_node *nn, *next, *prev;
1662241675Suqs	int		  i, j;
1663241675Suqs
1664241675Suqs	switch (mdoc->last->type) {
1665241675Suqs	case (MDOC_HEAD):
1666241675Suqs		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1667241675Suqs		return(1);
1668241675Suqs	case (MDOC_BODY):
1669241675Suqs		if (mdoc->last->child)
1670241675Suqs			break;
1671241675Suqs		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1672241675Suqs		return(1);
1673241675Suqs	default:
1674241675Suqs		return(1);
1675241675Suqs	}
1676241675Suqs
1677241675Suqs	/*
1678241675Suqs	 * Make sure only certain types of nodes are allowed within the
1679241675Suqs	 * the `Rs' body.  Delete offending nodes and raise a warning.
1680241675Suqs	 * Do this before re-ordering for the sake of clarity.
1681241675Suqs	 */
1682241675Suqs
1683241675Suqs	next = NULL;
1684241675Suqs	for (nn = mdoc->last->child; nn; nn = next) {
1685241675Suqs		for (i = 0; i < RSORD_MAX; i++)
1686241675Suqs			if (nn->tok == rsord[i])
1687241675Suqs				break;
1688241675Suqs
1689241675Suqs		if (i < RSORD_MAX) {
1690241675Suqs			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1691241675Suqs				mdoc->last->norm->Rs.quote_T++;
1692241675Suqs			next = nn->next;
1693241675Suqs			continue;
1694241675Suqs		}
1695241675Suqs
1696241675Suqs		next = nn->next;
1697241675Suqs		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1698241675Suqs		mdoc_node_delete(mdoc, nn);
1699241675Suqs	}
1700241675Suqs
1701241675Suqs	/*
1702241675Suqs	 * Nothing to sort if only invalid nodes were found
1703241675Suqs	 * inside the `Rs' body.
1704241675Suqs	 */
1705241675Suqs
1706241675Suqs	if (NULL == mdoc->last->child)
1707241675Suqs		return(1);
1708241675Suqs
1709241675Suqs	/*
1710241675Suqs	 * The full `Rs' block needs special handling to order the
1711241675Suqs	 * sub-elements according to `rsord'.  Pick through each element
1712241675Suqs	 * and correctly order it.  This is a insertion sort.
1713241675Suqs	 */
1714241675Suqs
1715241675Suqs	next = NULL;
1716241675Suqs	for (nn = mdoc->last->child->next; nn; nn = next) {
1717241675Suqs		/* Determine order of `nn'. */
1718241675Suqs		for (i = 0; i < RSORD_MAX; i++)
1719241675Suqs			if (rsord[i] == nn->tok)
1720241675Suqs				break;
1721241675Suqs
1722241675Suqs		/*
1723241675Suqs		 * Remove `nn' from the chain.  This somewhat
1724241675Suqs		 * repeats mdoc_node_unlink(), but since we're
1725241675Suqs		 * just re-ordering, there's no need for the
1726241675Suqs		 * full unlink process.
1727241675Suqs		 */
1728241675Suqs
1729241675Suqs		if (NULL != (next = nn->next))
1730241675Suqs			next->prev = nn->prev;
1731241675Suqs
1732241675Suqs		if (NULL != (prev = nn->prev))
1733241675Suqs			prev->next = nn->next;
1734241675Suqs
1735241675Suqs		nn->prev = nn->next = NULL;
1736241675Suqs
1737241675Suqs		/*
1738241675Suqs		 * Scan back until we reach a node that's
1739241675Suqs		 * ordered before `nn'.
1740241675Suqs		 */
1741241675Suqs
1742241675Suqs		for ( ; prev ; prev = prev->prev) {
1743241675Suqs			/* Determine order of `prev'. */
1744241675Suqs			for (j = 0; j < RSORD_MAX; j++)
1745241675Suqs				if (rsord[j] == prev->tok)
1746241675Suqs					break;
1747241675Suqs
1748241675Suqs			if (j <= i)
1749241675Suqs				break;
1750241675Suqs		}
1751241675Suqs
1752241675Suqs		/*
1753241675Suqs		 * Set `nn' back into its correct place in front
1754241675Suqs		 * of the `prev' node.
1755241675Suqs		 */
1756241675Suqs
1757241675Suqs		nn->prev = prev;
1758241675Suqs
1759241675Suqs		if (prev) {
1760241675Suqs			if (prev->next)
1761241675Suqs				prev->next->prev = nn;
1762241675Suqs			nn->next = prev->next;
1763241675Suqs			prev->next = nn;
1764241675Suqs		} else {
1765241675Suqs			mdoc->last->child->prev = nn;
1766241675Suqs			nn->next = mdoc->last->child;
1767241675Suqs			mdoc->last->child = nn;
1768241675Suqs		}
1769241675Suqs	}
1770241675Suqs
1771241675Suqs	return(1);
1772241675Suqs}
1773241675Suqs
1774241675Suqsstatic int
1775241675Suqspost_ns(POST_ARGS)
1776241675Suqs{
1777241675Suqs
1778241675Suqs	if (MDOC_LINE & mdoc->last->flags)
1779241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1780241675Suqs	return(1);
1781241675Suqs}
1782241675Suqs
1783241675Suqsstatic int
1784241675Suqspost_sh(POST_ARGS)
1785241675Suqs{
1786241675Suqs
1787241675Suqs	if (MDOC_HEAD == mdoc->last->type)
1788241675Suqs		return(post_sh_head(mdoc));
1789241675Suqs	if (MDOC_BODY == mdoc->last->type)
1790241675Suqs		return(post_sh_body(mdoc));
1791241675Suqs
1792241675Suqs	return(1);
1793241675Suqs}
1794241675Suqs
1795241675Suqsstatic int
1796241675Suqspost_sh_body(POST_ARGS)
1797241675Suqs{
1798241675Suqs	struct mdoc_node *n;
1799241675Suqs
1800241675Suqs	if (SEC_NAME != mdoc->lastsec)
1801241675Suqs		return(1);
1802241675Suqs
1803241675Suqs	/*
1804241675Suqs	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1805241675Suqs	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1806241675Suqs	 * children of the BODY declaration can also be "text".
1807241675Suqs	 */
1808241675Suqs
1809241675Suqs	if (NULL == (n = mdoc->last->child)) {
1810241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1811241675Suqs		return(1);
1812241675Suqs	}
1813241675Suqs
1814241675Suqs	for ( ; n && n->next; n = n->next) {
1815241675Suqs		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1816241675Suqs			continue;
1817241675Suqs		if (MDOC_TEXT == n->type)
1818241675Suqs			continue;
1819241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1820241675Suqs	}
1821241675Suqs
1822241675Suqs	assert(n);
1823241675Suqs	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1824241675Suqs		return(1);
1825241675Suqs
1826241675Suqs	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1827241675Suqs	return(1);
1828241675Suqs}
1829241675Suqs
1830241675Suqsstatic int
1831241675Suqspost_sh_head(POST_ARGS)
1832241675Suqs{
1833241675Suqs	char		 buf[BUFSIZ];
1834241675Suqs	struct mdoc_node *n;
1835241675Suqs	enum mdoc_sec	 sec;
1836241675Suqs	int		 c;
1837241675Suqs
1838241675Suqs	/*
1839241675Suqs	 * Process a new section.  Sections are either "named" or
1840241675Suqs	 * "custom".  Custom sections are user-defined, while named ones
1841241675Suqs	 * follow a conventional order and may only appear in certain
1842241675Suqs	 * manual sections.
1843241675Suqs	 */
1844241675Suqs
1845241675Suqs	sec = SEC_CUSTOM;
1846241675Suqs	buf[0] = '\0';
1847241675Suqs	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1848241675Suqs		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1849241675Suqs		return(0);
1850241675Suqs	} else if (1 == c)
1851241675Suqs		sec = a2sec(buf);
1852241675Suqs
1853241675Suqs	/* The NAME should be first. */
1854241675Suqs
1855241675Suqs	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1856241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1857241675Suqs
1858241675Suqs	/* The SYNOPSIS gets special attention in other areas. */
1859241675Suqs
1860241675Suqs	if (SEC_SYNOPSIS == sec)
1861241675Suqs		mdoc->flags |= MDOC_SYNOPSIS;
1862241675Suqs	else
1863241675Suqs		mdoc->flags &= ~MDOC_SYNOPSIS;
1864241675Suqs
1865241675Suqs	/* Mark our last section. */
1866241675Suqs
1867241675Suqs	mdoc->lastsec = sec;
1868241675Suqs
1869241675Suqs	/*
1870241675Suqs	 * Set the section attribute for the current HEAD, for its
1871241675Suqs	 * parent BLOCK, and for the HEAD children; the latter can
1872241675Suqs	 * only be TEXT nodes, so no recursion is needed.
1873241675Suqs	 * For other blocks and elements, including .Sh BODY, this is
1874241675Suqs	 * done when allocating the node data structures, but for .Sh
1875241675Suqs	 * BLOCK and HEAD, the section is still unknown at that time.
1876241675Suqs	 */
1877241675Suqs
1878241675Suqs	mdoc->last->parent->sec = sec;
1879241675Suqs	mdoc->last->sec = sec;
1880241675Suqs	for (n = mdoc->last->child; n; n = n->next)
1881241675Suqs		n->sec = sec;
1882241675Suqs
1883241675Suqs	/* We don't care about custom sections after this. */
1884241675Suqs
1885241675Suqs	if (SEC_CUSTOM == sec)
1886241675Suqs		return(1);
1887241675Suqs
1888241675Suqs	/*
1889241675Suqs	 * Check whether our non-custom section is being repeated or is
1890241675Suqs	 * out of order.
1891241675Suqs	 */
1892241675Suqs
1893241675Suqs	if (sec == mdoc->lastnamed)
1894241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1895241675Suqs
1896241675Suqs	if (sec < mdoc->lastnamed)
1897241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1898241675Suqs
1899241675Suqs	/* Mark the last named section. */
1900241675Suqs
1901241675Suqs	mdoc->lastnamed = sec;
1902241675Suqs
1903241675Suqs	/* Check particular section/manual conventions. */
1904241675Suqs
1905241675Suqs	assert(mdoc->meta.msec);
1906241675Suqs
1907241675Suqs	switch (sec) {
1908241675Suqs	case (SEC_RETURN_VALUES):
1909241675Suqs		/* FALLTHROUGH */
1910241675Suqs	case (SEC_ERRORS):
1911241675Suqs		/* FALLTHROUGH */
1912241675Suqs	case (SEC_LIBRARY):
1913241675Suqs		if (*mdoc->meta.msec == '2')
1914241675Suqs			break;
1915241675Suqs		if (*mdoc->meta.msec == '3')
1916241675Suqs			break;
1917241675Suqs		if (*mdoc->meta.msec == '9')
1918241675Suqs			break;
1919241675Suqs		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1920241675Suqs		break;
1921241675Suqs	default:
1922241675Suqs		break;
1923241675Suqs	}
1924241675Suqs
1925241675Suqs	return(1);
1926241675Suqs}
1927241675Suqs
1928241675Suqsstatic int
1929241675Suqspost_ignpar(POST_ARGS)
1930241675Suqs{
1931241675Suqs	struct mdoc_node *np;
1932241675Suqs
1933241675Suqs	if (MDOC_BODY != mdoc->last->type)
1934241675Suqs		return(1);
1935241675Suqs
1936241675Suqs	if (NULL != (np = mdoc->last->child))
1937241675Suqs		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1938241675Suqs			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1939241675Suqs			mdoc_node_delete(mdoc, np);
1940241675Suqs		}
1941241675Suqs
1942241675Suqs	if (NULL != (np = mdoc->last->last))
1943241675Suqs		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1944241675Suqs			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1945241675Suqs			mdoc_node_delete(mdoc, np);
1946241675Suqs		}
1947241675Suqs
1948241675Suqs	return(1);
1949241675Suqs}
1950241675Suqs
1951241675Suqsstatic int
1952241675Suqspre_par(PRE_ARGS)
1953241675Suqs{
1954241675Suqs
1955241675Suqs	if (NULL == mdoc->last)
1956241675Suqs		return(1);
1957241675Suqs	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1958241675Suqs		return(1);
1959241675Suqs
1960241675Suqs	/*
1961241675Suqs	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1962241675Suqs	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1963241675Suqs	 */
1964241675Suqs
1965241675Suqs	if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1966241675Suqs		return(1);
1967241675Suqs	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1968241675Suqs		return(1);
1969241675Suqs	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1970241675Suqs		return(1);
1971241675Suqs	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1972241675Suqs		return(1);
1973241675Suqs
1974241675Suqs	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1975241675Suqs	mdoc_node_delete(mdoc, mdoc->last);
1976241675Suqs	return(1);
1977241675Suqs}
1978241675Suqs
1979241675Suqsstatic int
1980241675Suqspre_literal(PRE_ARGS)
1981241675Suqs{
1982241675Suqs
1983241675Suqs	if (MDOC_BODY != n->type)
1984241675Suqs		return(1);
1985241675Suqs
1986241675Suqs	/*
1987241675Suqs	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1988241675Suqs	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1989241675Suqs	 */
1990241675Suqs
1991241675Suqs	switch (n->tok) {
1992241675Suqs	case (MDOC_Dl):
1993241675Suqs		mdoc->flags |= MDOC_LITERAL;
1994241675Suqs		break;
1995241675Suqs	case (MDOC_Bd):
1996241675Suqs		if (DISP_literal == n->norm->Bd.type)
1997241675Suqs			mdoc->flags |= MDOC_LITERAL;
1998241675Suqs		if (DISP_unfilled == n->norm->Bd.type)
1999241675Suqs			mdoc->flags |= MDOC_LITERAL;
2000241675Suqs		break;
2001241675Suqs	default:
2002241675Suqs		abort();
2003241675Suqs		/* NOTREACHED */
2004241675Suqs	}
2005241675Suqs
2006241675Suqs	return(1);
2007241675Suqs}
2008241675Suqs
2009241675Suqsstatic int
2010241675Suqspost_dd(POST_ARGS)
2011241675Suqs{
2012241675Suqs	char		  buf[DATESIZE];
2013241675Suqs	struct mdoc_node *n;
2014241675Suqs	int		  c;
2015241675Suqs
2016241675Suqs	if (mdoc->meta.date)
2017241675Suqs		free(mdoc->meta.date);
2018241675Suqs
2019241675Suqs	n = mdoc->last;
2020241675Suqs	if (NULL == n->child || '\0' == n->child->string[0]) {
2021241675Suqs		mdoc->meta.date = mandoc_normdate
2022241675Suqs			(mdoc->parse, NULL, n->line, n->pos);
2023241675Suqs		return(1);
2024241675Suqs	}
2025241675Suqs
2026241675Suqs	buf[0] = '\0';
2027241675Suqs	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2028241675Suqs		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2029241675Suqs		return(0);
2030241675Suqs	}
2031241675Suqs
2032241675Suqs	assert(c);
2033241675Suqs	mdoc->meta.date = mandoc_normdate
2034241675Suqs		(mdoc->parse, buf, n->line, n->pos);
2035241675Suqs
2036241675Suqs	return(1);
2037241675Suqs}
2038241675Suqs
2039241675Suqsstatic int
2040241675Suqspost_dt(POST_ARGS)
2041241675Suqs{
2042241675Suqs	struct mdoc_node *nn, *n;
2043241675Suqs	const char	 *cp;
2044241675Suqs	char		 *p;
2045241675Suqs
2046241675Suqs	n = mdoc->last;
2047241675Suqs
2048241675Suqs	if (mdoc->meta.title)
2049241675Suqs		free(mdoc->meta.title);
2050241675Suqs	if (mdoc->meta.vol)
2051241675Suqs		free(mdoc->meta.vol);
2052241675Suqs	if (mdoc->meta.arch)
2053241675Suqs		free(mdoc->meta.arch);
2054241675Suqs
2055241675Suqs	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2056241675Suqs
2057241675Suqs	/* First make all characters uppercase. */
2058241675Suqs
2059241675Suqs	if (NULL != (nn = n->child))
2060241675Suqs		for (p = nn->string; *p; p++) {
2061241675Suqs			if (toupper((unsigned char)*p) == *p)
2062241675Suqs				continue;
2063241675Suqs
2064241675Suqs			/*
2065241675Suqs			 * FIXME: don't be lazy: have this make all
2066241675Suqs			 * characters be uppercase and just warn once.
2067241675Suqs			 */
2068241675Suqs			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2069241675Suqs			break;
2070241675Suqs		}
2071241675Suqs
2072241675Suqs	/* Handles: `.Dt'
2073241675Suqs	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
2074241675Suqs	 */
2075241675Suqs
2076241675Suqs	if (NULL == (nn = n->child)) {
2077241675Suqs		/* XXX: make these macro values. */
2078241675Suqs		/* FIXME: warn about missing values. */
2079241675Suqs		mdoc->meta.title = mandoc_strdup("UNKNOWN");
2080241675Suqs		mdoc->meta.vol = mandoc_strdup("LOCAL");
2081241675Suqs		mdoc->meta.msec = mandoc_strdup("1");
2082241675Suqs		return(1);
2083241675Suqs	}
2084241675Suqs
2085241675Suqs	/* Handles: `.Dt TITLE'
2086241675Suqs	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2087241675Suqs	 */
2088241675Suqs
2089241675Suqs	mdoc->meta.title = mandoc_strdup
2090241675Suqs		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2091241675Suqs
2092241675Suqs	if (NULL == (nn = nn->next)) {
2093241675Suqs		/* FIXME: warn about missing msec. */
2094241675Suqs		/* XXX: make this a macro value. */
2095241675Suqs		mdoc->meta.vol = mandoc_strdup("LOCAL");
2096241675Suqs		mdoc->meta.msec = mandoc_strdup("1");
2097241675Suqs		return(1);
2098241675Suqs	}
2099241675Suqs
2100241675Suqs	/* Handles: `.Dt TITLE SEC'
2101241675Suqs	 *   --> title = TITLE, volume = SEC is msec ?
2102241675Suqs	 *           format(msec) : SEC,
2103241675Suqs	 *       msec = SEC is msec ? atoi(msec) : 0,
2104241675Suqs	 *       arch = NULL
2105241675Suqs	 */
2106241675Suqs
2107241675Suqs	cp = mandoc_a2msec(nn->string);
2108241675Suqs	if (cp) {
2109241675Suqs		mdoc->meta.vol = mandoc_strdup(cp);
2110241675Suqs		mdoc->meta.msec = mandoc_strdup(nn->string);
2111241675Suqs	} else {
2112241675Suqs		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2113241675Suqs		mdoc->meta.vol = mandoc_strdup(nn->string);
2114241675Suqs		mdoc->meta.msec = mandoc_strdup(nn->string);
2115241675Suqs	}
2116241675Suqs
2117241675Suqs	if (NULL == (nn = nn->next))
2118241675Suqs		return(1);
2119241675Suqs
2120241675Suqs	/* Handles: `.Dt TITLE SEC VOL'
2121241675Suqs	 *   --> title = TITLE, volume = VOL is vol ?
2122241675Suqs	 *       format(VOL) :
2123241675Suqs	 *           VOL is arch ? format(arch) :
2124241675Suqs	 *               VOL
2125241675Suqs	 */
2126241675Suqs
2127241675Suqs	cp = mdoc_a2vol(nn->string);
2128241675Suqs	if (cp) {
2129241675Suqs		free(mdoc->meta.vol);
2130241675Suqs		mdoc->meta.vol = mandoc_strdup(cp);
2131241675Suqs	} else {
2132241675Suqs		/* FIXME: warn about bad arch. */
2133241675Suqs		cp = mdoc_a2arch(nn->string);
2134241675Suqs		if (NULL == cp) {
2135241675Suqs			free(mdoc->meta.vol);
2136241675Suqs			mdoc->meta.vol = mandoc_strdup(nn->string);
2137241675Suqs		} else
2138241675Suqs			mdoc->meta.arch = mandoc_strdup(cp);
2139241675Suqs	}
2140241675Suqs
2141241675Suqs	/* Ignore any subsequent parameters... */
2142241675Suqs	/* FIXME: warn about subsequent parameters. */
2143241675Suqs
2144241675Suqs	return(1);
2145241675Suqs}
2146241675Suqs
2147241675Suqsstatic int
2148241675Suqspost_prol(POST_ARGS)
2149241675Suqs{
2150241675Suqs	/*
2151241675Suqs	 * Remove prologue macros from the document after they're
2152241675Suqs	 * processed.  The final document uses mdoc_meta for these
2153241675Suqs	 * values and discards the originals.
2154241675Suqs	 */
2155241675Suqs
2156241675Suqs	mdoc_node_delete(mdoc, mdoc->last);
2157241675Suqs	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2158241675Suqs		mdoc->flags |= MDOC_PBODY;
2159241675Suqs
2160241675Suqs	return(1);
2161241675Suqs}
2162241675Suqs
2163241675Suqsstatic int
2164241675Suqspost_bx(POST_ARGS)
2165241675Suqs{
2166241675Suqs	struct mdoc_node	*n;
2167241675Suqs
2168241675Suqs	/*
2169241675Suqs	 * Make `Bx's second argument always start with an uppercase
2170241675Suqs	 * letter.  Groff checks if it's an "accepted" term, but we just
2171241675Suqs	 * uppercase blindly.
2172241675Suqs	 */
2173241675Suqs
2174241675Suqs	n = mdoc->last->child;
2175241675Suqs	if (n && NULL != (n = n->next))
2176241675Suqs		*n->string = (char)toupper
2177241675Suqs			((unsigned char)*n->string);
2178241675Suqs
2179241675Suqs	return(1);
2180241675Suqs}
2181241675Suqs
2182241675Suqsstatic int
2183241675Suqspost_os(POST_ARGS)
2184241675Suqs{
2185241675Suqs	struct mdoc_node *n;
2186241675Suqs	char		  buf[BUFSIZ];
2187241675Suqs	int		  c;
2188241675Suqs#ifndef OSNAME
2189241675Suqs	struct utsname	  utsname;
2190241675Suqs#endif
2191241675Suqs
2192241675Suqs	n = mdoc->last;
2193241675Suqs
2194241675Suqs	/*
2195241675Suqs	 * Set the operating system by way of the `Os' macro.  Note that
2196241675Suqs	 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2197241675Suqs	 * provided during compilation, this value will be used instead
2198241675Suqs	 * of filling in "sysname release" from uname().
2199241675Suqs 	 */
2200241675Suqs
2201241675Suqs	if (mdoc->meta.os)
2202241675Suqs		free(mdoc->meta.os);
2203241675Suqs
2204241675Suqs	buf[0] = '\0';
2205241675Suqs	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2206241675Suqs		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2207241675Suqs		return(0);
2208241675Suqs	}
2209241675Suqs
2210241675Suqs	assert(c);
2211241675Suqs
2212241675Suqs	/* XXX: yes, these can all be dynamically-adjusted buffers, but
2213241675Suqs	 * it's really not worth the extra hackery.
2214241675Suqs	 */
2215241675Suqs
2216241675Suqs	if ('\0' == buf[0]) {
2217241675Suqs#ifdef OSNAME
2218241675Suqs		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2219241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2220241675Suqs			return(0);
2221241675Suqs		}
2222241675Suqs#else /*!OSNAME */
2223241675Suqs		if (-1 == uname(&utsname)) {
2224241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2225241675Suqs                        mdoc->meta.os = mandoc_strdup("UNKNOWN");
2226241675Suqs                        return(post_prol(mdoc));
2227241675Suqs                }
2228241675Suqs
2229241675Suqs		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2230241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2231241675Suqs			return(0);
2232241675Suqs		}
2233241675Suqs		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2234241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2235241675Suqs			return(0);
2236241675Suqs		}
2237241775Suqs		if (0 == strcmp(utsname.sysname, "FreeBSD"))
2238241774Suqs			strtok(utsname.release, "-");
2239241675Suqs		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2240241675Suqs			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2241241675Suqs			return(0);
2242241675Suqs		}
2243241675Suqs#endif /*!OSNAME*/
2244241675Suqs	}
2245241675Suqs
2246241675Suqs	mdoc->meta.os = mandoc_strdup(buf);
2247241675Suqs	return(1);
2248241675Suqs}
2249241675Suqs
2250241675Suqsstatic int
2251241675Suqspost_std(POST_ARGS)
2252241675Suqs{
2253241675Suqs	struct mdoc_node *nn, *n;
2254241675Suqs
2255241675Suqs	n = mdoc->last;
2256241675Suqs
2257241675Suqs	/*
2258241675Suqs	 * Macros accepting `-std' as an argument have the name of the
2259241675Suqs	 * current document (`Nm') filled in as the argument if it's not
2260241675Suqs	 * provided.
2261241675Suqs	 */
2262241675Suqs
2263241675Suqs	if (n->child)
2264241675Suqs		return(1);
2265241675Suqs
2266241675Suqs	if (NULL == mdoc->meta.name)
2267241675Suqs		return(1);
2268241675Suqs
2269241675Suqs	nn = n;
2270241675Suqs	mdoc->next = MDOC_NEXT_CHILD;
2271241675Suqs
2272241675Suqs	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2273241675Suqs		return(0);
2274241675Suqs
2275241675Suqs	mdoc->last = nn;
2276241675Suqs	return(1);
2277241675Suqs}
2278241675Suqs
2279241675Suqs/*
2280241675Suqs * Concatenate a node, stopping at the first non-text.
2281241675Suqs * Concatenation is separated by a single whitespace.
2282241675Suqs * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2283241675Suqs * encountered, 1 otherwise.
2284241675Suqs */
2285241675Suqsstatic int
2286241675Suqsconcat(char *p, const struct mdoc_node *n, size_t sz)
2287241675Suqs{
2288241675Suqs
2289241675Suqs	for ( ; NULL != n; n = n->next) {
2290241675Suqs		if (MDOC_TEXT != n->type)
2291241675Suqs			return(0);
2292241675Suqs		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2293241675Suqs			return(-1);
2294241675Suqs		if (strlcat(p, n->string, sz) >= sz)
2295241675Suqs			return(-1);
2296241675Suqs		concat(p, n->child, sz);
2297241675Suqs	}
2298241675Suqs
2299241675Suqs	return(1);
2300241675Suqs}
2301241675Suqs
2302241675Suqsstatic enum mdoc_sec
2303241675Suqsa2sec(const char *p)
2304241675Suqs{
2305241675Suqs	int		 i;
2306241675Suqs
2307241675Suqs	for (i = 0; i < (int)SEC__MAX; i++)
2308241675Suqs		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2309241675Suqs			return((enum mdoc_sec)i);
2310241675Suqs
2311241675Suqs	return(SEC_CUSTOM);
2312241675Suqs}
2313241675Suqs
2314241675Suqsstatic size_t
2315241675Suqsmacro2len(enum mdoct macro)
2316241675Suqs{
2317241675Suqs
2318241675Suqs	switch (macro) {
2319241675Suqs	case(MDOC_Ad):
2320241675Suqs		return(12);
2321241675Suqs	case(MDOC_Ao):
2322241675Suqs		return(12);
2323241675Suqs	case(MDOC_An):
2324241675Suqs		return(12);
2325241675Suqs	case(MDOC_Aq):
2326241675Suqs		return(12);
2327241675Suqs	case(MDOC_Ar):
2328241675Suqs		return(12);
2329241675Suqs	case(MDOC_Bo):
2330241675Suqs		return(12);
2331241675Suqs	case(MDOC_Bq):
2332241675Suqs		return(12);
2333241675Suqs	case(MDOC_Cd):
2334241675Suqs		return(12);
2335241675Suqs	case(MDOC_Cm):
2336241675Suqs		return(10);
2337241675Suqs	case(MDOC_Do):
2338241675Suqs		return(10);
2339241675Suqs	case(MDOC_Dq):
2340241675Suqs		return(12);
2341241675Suqs	case(MDOC_Dv):
2342241675Suqs		return(12);
2343241675Suqs	case(MDOC_Eo):
2344241675Suqs		return(12);
2345241675Suqs	case(MDOC_Em):
2346241675Suqs		return(10);
2347241675Suqs	case(MDOC_Er):
2348241675Suqs		return(17);
2349241675Suqs	case(MDOC_Ev):
2350241675Suqs		return(15);
2351241675Suqs	case(MDOC_Fa):
2352241675Suqs		return(12);
2353241675Suqs	case(MDOC_Fl):
2354241675Suqs		return(10);
2355241675Suqs	case(MDOC_Fo):
2356241675Suqs		return(16);
2357241675Suqs	case(MDOC_Fn):
2358241675Suqs		return(16);
2359241675Suqs	case(MDOC_Ic):
2360241675Suqs		return(10);
2361241675Suqs	case(MDOC_Li):
2362241675Suqs		return(16);
2363241675Suqs	case(MDOC_Ms):
2364241675Suqs		return(6);
2365241675Suqs	case(MDOC_Nm):
2366241675Suqs		return(10);
2367241675Suqs	case(MDOC_No):
2368241675Suqs		return(12);
2369241675Suqs	case(MDOC_Oo):
2370241675Suqs		return(10);
2371241675Suqs	case(MDOC_Op):
2372241675Suqs		return(14);
2373241675Suqs	case(MDOC_Pa):
2374241675Suqs		return(32);
2375241675Suqs	case(MDOC_Pf):
2376241675Suqs		return(12);
2377241675Suqs	case(MDOC_Po):
2378241675Suqs		return(12);
2379241675Suqs	case(MDOC_Pq):
2380241675Suqs		return(12);
2381241675Suqs	case(MDOC_Ql):
2382241675Suqs		return(16);
2383241675Suqs	case(MDOC_Qo):
2384241675Suqs		return(12);
2385241675Suqs	case(MDOC_So):
2386241675Suqs		return(12);
2387241675Suqs	case(MDOC_Sq):
2388241675Suqs		return(12);
2389241675Suqs	case(MDOC_Sy):
2390241675Suqs		return(6);
2391241675Suqs	case(MDOC_Sx):
2392241675Suqs		return(16);
2393241675Suqs	case(MDOC_Tn):
2394241675Suqs		return(10);
2395241675Suqs	case(MDOC_Va):
2396241675Suqs		return(12);
2397241675Suqs	case(MDOC_Vt):
2398241675Suqs		return(12);
2399241675Suqs	case(MDOC_Xr):
2400241675Suqs		return(10);
2401241675Suqs	default:
2402241675Suqs		break;
2403241675Suqs	};
2404241675Suqs	return(0);
2405241675Suqs}
2406