mdoc_validate.c revision 1.107
1/*	$Id: mdoc_validate.c,v 1.107 2012/07/18 11:09:30 schwarze Exp $ */
2/*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18#ifndef        OSNAME
19#include <sys/utsname.h>
20#endif
21
22#include <sys/types.h>
23
24#include <assert.h>
25#include <ctype.h>
26#include <limits.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <time.h>
31
32#include "mdoc.h"
33#include "mandoc.h"
34#include "libmdoc.h"
35#include "libmandoc.h"
36
37/* FIXME: .Bl -diag can't have non-text children in HEAD. */
38
39#define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
40#define	POST_ARGS struct mdoc *mdoc
41
42#define	NUMSIZ	  32
43#define	DATESIZE  32
44
45enum	check_ineq {
46	CHECK_LT,
47	CHECK_GT,
48	CHECK_EQ
49};
50
51enum	check_lvl {
52	CHECK_WARN,
53	CHECK_ERROR,
54};
55
56typedef	int	(*v_pre)(PRE_ARGS);
57typedef	int	(*v_post)(POST_ARGS);
58
59struct	valids {
60	v_pre	*pre;
61	v_post	*post;
62};
63
64static	int	 check_count(struct mdoc *, enum mdoc_type,
65			enum check_lvl, enum check_ineq, int);
66static	int	 check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
67static	void	 check_text(struct mdoc *, int, int, char *);
68static	void	 check_argv(struct mdoc *,
69			struct mdoc_node *, struct mdoc_argv *);
70static	void	 check_args(struct mdoc *, struct mdoc_node *);
71static	int	 concat(char *, const struct mdoc_node *, size_t);
72static	enum mdoc_sec	a2sec(const char *);
73static	size_t		macro2len(enum mdoct);
74
75static	int	 ebool(POST_ARGS);
76static	int	 berr_ge1(POST_ARGS);
77static	int	 bwarn_ge1(POST_ARGS);
78static	int	 ewarn_eq0(POST_ARGS);
79static	int	 ewarn_eq1(POST_ARGS);
80static	int	 ewarn_ge1(POST_ARGS);
81static	int	 ewarn_le1(POST_ARGS);
82static	int	 hwarn_eq0(POST_ARGS);
83static	int	 hwarn_eq1(POST_ARGS);
84static	int	 hwarn_ge1(POST_ARGS);
85static	int	 hwarn_le1(POST_ARGS);
86
87static	int	 post_an(POST_ARGS);
88static	int	 post_at(POST_ARGS);
89static	int	 post_bf(POST_ARGS);
90static	int	 post_bl(POST_ARGS);
91static	int	 post_bl_block(POST_ARGS);
92static	int	 post_bl_block_width(POST_ARGS);
93static	int	 post_bl_block_tag(POST_ARGS);
94static	int	 post_bl_head(POST_ARGS);
95static	int	 post_bx(POST_ARGS);
96static	int	 post_dd(POST_ARGS);
97static	int	 post_dt(POST_ARGS);
98static	int	 post_defaults(POST_ARGS);
99static	int	 post_literal(POST_ARGS);
100static	int	 post_eoln(POST_ARGS);
101static	int	 post_it(POST_ARGS);
102static	int	 post_lb(POST_ARGS);
103static	int	 post_nm(POST_ARGS);
104static	int	 post_ns(POST_ARGS);
105static	int	 post_os(POST_ARGS);
106static	int	 post_par(POST_ARGS);
107static	int	 post_ignpar(POST_ARGS);
108static	int	 post_prol(POST_ARGS);
109static	int	 post_root(POST_ARGS);
110static	int	 post_rs(POST_ARGS);
111static	int	 post_sh(POST_ARGS);
112static	int	 post_sh_body(POST_ARGS);
113static	int	 post_sh_head(POST_ARGS);
114static	int	 post_st(POST_ARGS);
115static	int	 post_std(POST_ARGS);
116static	int	 post_vt(POST_ARGS);
117static	int	 pre_an(PRE_ARGS);
118static	int	 pre_bd(PRE_ARGS);
119static	int	 pre_bl(PRE_ARGS);
120static	int	 pre_dd(PRE_ARGS);
121static	int	 pre_display(PRE_ARGS);
122static	int	 pre_dt(PRE_ARGS);
123static	int	 pre_it(PRE_ARGS);
124static	int	 pre_literal(PRE_ARGS);
125static	int	 pre_os(PRE_ARGS);
126static	int	 pre_par(PRE_ARGS);
127static	int	 pre_sh(PRE_ARGS);
128static	int	 pre_ss(PRE_ARGS);
129static	int	 pre_std(PRE_ARGS);
130
131static	v_post	 posts_an[] = { post_an, NULL };
132static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
133static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
134static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
135static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
136static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
137static	v_post	 posts_bx[] = { post_bx, NULL };
138static	v_post	 posts_bool[] = { ebool, NULL };
139static	v_post	 posts_eoln[] = { post_eoln, NULL };
140static	v_post	 posts_defaults[] = { post_defaults, NULL };
141static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
142static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
143static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
144static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
145static	v_post	 posts_it[] = { post_it, NULL };
146static	v_post	 posts_lb[] = { post_lb, NULL };
147static	v_post	 posts_nd[] = { berr_ge1, NULL };
148static	v_post	 posts_nm[] = { post_nm, NULL };
149static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
150static	v_post	 posts_ns[] = { post_ns, NULL };
151static	v_post	 posts_os[] = { post_os, post_prol, NULL };
152static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
153static	v_post	 posts_rs[] = { post_rs, NULL };
154static	v_post	 posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL };
155static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
156static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, NULL };
157static	v_post	 posts_st[] = { post_st, NULL };
158static	v_post	 posts_std[] = { post_std, NULL };
159static	v_post	 posts_text[] = { ewarn_ge1, NULL };
160static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
161static	v_post	 posts_vt[] = { post_vt, NULL };
162static	v_post	 posts_wline[] = { bwarn_ge1, NULL };
163static	v_pre	 pres_an[] = { pre_an, NULL };
164static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
165static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
166static	v_pre	 pres_d1[] = { pre_display, NULL };
167static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
168static	v_pre	 pres_dd[] = { pre_dd, NULL };
169static	v_pre	 pres_dt[] = { pre_dt, NULL };
170static	v_pre	 pres_er[] = { NULL, NULL };
171static	v_pre	 pres_fd[] = { NULL, NULL };
172static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
173static	v_pre	 pres_os[] = { pre_os, NULL };
174static	v_pre	 pres_pp[] = { pre_par, NULL };
175static	v_pre	 pres_sh[] = { pre_sh, NULL };
176static	v_pre	 pres_ss[] = { pre_ss, NULL };
177static	v_pre	 pres_std[] = { pre_std, NULL };
178
179static	const struct valids mdoc_valids[MDOC_MAX] = {
180	{ NULL, NULL },				/* Ap */
181	{ pres_dd, posts_dd },			/* Dd */
182	{ pres_dt, posts_dt },			/* Dt */
183	{ pres_os, posts_os },			/* Os */
184	{ pres_sh, posts_sh },			/* Sh */
185	{ pres_ss, posts_ss },			/* Ss */
186	{ pres_pp, posts_pp },			/* Pp */
187	{ pres_d1, posts_wline },		/* D1 */
188	{ pres_dl, posts_dl },			/* Dl */
189	{ pres_bd, posts_bd },			/* Bd */
190	{ NULL, NULL },				/* Ed */
191	{ pres_bl, posts_bl },			/* Bl */
192	{ NULL, NULL },				/* El */
193	{ pres_it, posts_it },			/* It */
194	{ NULL, NULL },				/* Ad */
195	{ pres_an, posts_an },			/* An */
196	{ NULL, posts_defaults },		/* Ar */
197	{ NULL, NULL },				/* Cd */
198	{ NULL, NULL },				/* Cm */
199	{ NULL, NULL },				/* Dv */
200	{ pres_er, NULL },			/* Er */
201	{ NULL, NULL },				/* Ev */
202	{ pres_std, posts_std },		/* Ex */
203	{ NULL, NULL },				/* Fa */
204	{ pres_fd, posts_text },		/* Fd */
205	{ NULL, NULL },				/* Fl */
206	{ NULL, NULL },				/* Fn */
207	{ NULL, NULL },				/* Ft */
208	{ NULL, NULL },				/* Ic */
209	{ NULL, posts_text1 },			/* In */
210	{ NULL, posts_defaults },		/* Li */
211	{ NULL, posts_nd },			/* Nd */
212	{ NULL, posts_nm },			/* Nm */
213	{ NULL, NULL },				/* Op */
214	{ NULL, NULL },				/* Ot */
215	{ NULL, posts_defaults },		/* Pa */
216	{ pres_std, posts_std },		/* Rv */
217	{ NULL, posts_st },			/* St */
218	{ NULL, NULL },				/* Va */
219	{ NULL, posts_vt },			/* Vt */
220	{ NULL, posts_text },			/* Xr */
221	{ NULL, posts_text },			/* %A */
222	{ NULL, posts_text },			/* %B */ /* FIXME: can be used outside Rs/Re. */
223	{ NULL, posts_text },			/* %D */
224	{ NULL, posts_text },			/* %I */
225	{ NULL, posts_text },			/* %J */
226	{ NULL, posts_text },			/* %N */
227	{ NULL, posts_text },			/* %O */
228	{ NULL, posts_text },			/* %P */
229	{ NULL, posts_text },			/* %R */
230	{ NULL, posts_text },			/* %T */ /* FIXME: can be used outside Rs/Re. */
231	{ NULL, posts_text },			/* %V */
232	{ NULL, NULL },				/* Ac */
233	{ NULL, NULL },				/* Ao */
234	{ NULL, NULL },				/* Aq */
235	{ NULL, posts_at },			/* At */
236	{ NULL, NULL },				/* Bc */
237	{ NULL, posts_bf },			/* Bf */
238	{ NULL, NULL },				/* Bo */
239	{ NULL, NULL },				/* Bq */
240	{ NULL, NULL },				/* Bsx */
241	{ NULL, posts_bx },			/* Bx */
242	{ NULL, posts_bool },			/* Db */
243	{ NULL, NULL },				/* Dc */
244	{ NULL, NULL },				/* Do */
245	{ NULL, NULL },				/* Dq */
246	{ NULL, NULL },				/* Ec */
247	{ NULL, NULL },				/* Ef */
248	{ NULL, NULL },				/* Em */
249	{ NULL, NULL },				/* Eo */
250	{ NULL, NULL },				/* Fx */
251	{ NULL, NULL },				/* Ms */
252	{ NULL, posts_notext },			/* No */
253	{ NULL, posts_ns },			/* Ns */
254	{ NULL, NULL },				/* Nx */
255	{ NULL, NULL },				/* Ox */
256	{ NULL, NULL },				/* Pc */
257	{ NULL, posts_text1 },			/* Pf */
258	{ NULL, NULL },				/* Po */
259	{ NULL, NULL },				/* Pq */
260	{ NULL, NULL },				/* Qc */
261	{ NULL, NULL },				/* Ql */
262	{ NULL, NULL },				/* Qo */
263	{ NULL, NULL },				/* Qq */
264	{ NULL, NULL },				/* Re */
265	{ NULL, posts_rs },			/* Rs */
266	{ NULL, NULL },				/* Sc */
267	{ NULL, NULL },				/* So */
268	{ NULL, NULL },				/* Sq */
269	{ NULL, posts_bool },			/* Sm */
270	{ NULL, NULL },				/* Sx */
271	{ NULL, NULL },				/* Sy */
272	{ NULL, NULL },				/* Tn */
273	{ NULL, NULL },				/* Ux */
274	{ NULL, NULL },				/* Xc */
275	{ NULL, NULL },				/* Xo */
276	{ NULL, posts_fo },			/* Fo */
277	{ NULL, NULL },				/* Fc */
278	{ NULL, NULL },				/* Oo */
279	{ NULL, NULL },				/* Oc */
280	{ NULL, posts_bk },			/* Bk */
281	{ NULL, NULL },				/* Ek */
282	{ NULL, posts_eoln },			/* Bt */
283	{ NULL, NULL },				/* Hf */
284	{ NULL, NULL },				/* Fr */
285	{ NULL, posts_eoln },			/* Ud */
286	{ NULL, posts_lb },			/* Lb */
287	{ pres_pp, posts_pp },			/* Lp */
288	{ NULL, NULL },				/* Lk */
289	{ NULL, posts_defaults },		/* Mt */
290	{ NULL, NULL },				/* Brq */
291	{ NULL, NULL },				/* Bro */
292	{ NULL, NULL },				/* Brc */
293	{ NULL, posts_text },			/* %C */
294	{ NULL, NULL },				/* Es */
295	{ NULL, NULL },				/* En */
296	{ NULL, NULL },				/* Dx */
297	{ NULL, posts_text },			/* %Q */
298	{ NULL, posts_pp },			/* br */
299	{ NULL, posts_sp },			/* sp */
300	{ NULL, posts_text1 },			/* %U */
301	{ NULL, NULL },				/* Ta */
302};
303
304#define	RSORD_MAX 14 /* Number of `Rs' blocks. */
305
306static	const enum mdoct rsord[RSORD_MAX] = {
307	MDOC__A,
308	MDOC__T,
309	MDOC__B,
310	MDOC__I,
311	MDOC__J,
312	MDOC__R,
313	MDOC__N,
314	MDOC__V,
315	MDOC__U,
316	MDOC__P,
317	MDOC__Q,
318	MDOC__D,
319	MDOC__O,
320	MDOC__C
321};
322
323static	const char * const secnames[SEC__MAX] = {
324	NULL,
325	"NAME",
326	"LIBRARY",
327	"SYNOPSIS",
328	"DESCRIPTION",
329	"IMPLEMENTATION NOTES",
330	"RETURN VALUES",
331	"ENVIRONMENT",
332	"FILES",
333	"EXIT STATUS",
334	"EXAMPLES",
335	"DIAGNOSTICS",
336	"COMPATIBILITY",
337	"ERRORS",
338	"SEE ALSO",
339	"STANDARDS",
340	"HISTORY",
341	"AUTHORS",
342	"CAVEATS",
343	"BUGS",
344	"SECURITY CONSIDERATIONS",
345	NULL
346};
347
348int
349mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
350{
351	v_pre		*p;
352	int		 line, pos;
353	char		*tp;
354
355	switch (n->type) {
356	case (MDOC_TEXT):
357		tp = n->string;
358		line = n->line;
359		pos = n->pos;
360		check_text(mdoc, line, pos, tp);
361		/* FALLTHROUGH */
362	case (MDOC_TBL):
363		/* FALLTHROUGH */
364	case (MDOC_EQN):
365		/* FALLTHROUGH */
366	case (MDOC_ROOT):
367		return(1);
368	default:
369		break;
370	}
371
372	check_args(mdoc, n);
373
374	if (NULL == mdoc_valids[n->tok].pre)
375		return(1);
376	for (p = mdoc_valids[n->tok].pre; *p; p++)
377		if ( ! (*p)(mdoc, n))
378			return(0);
379	return(1);
380}
381
382
383int
384mdoc_valid_post(struct mdoc *mdoc)
385{
386	v_post		*p;
387
388	if (MDOC_VALID & mdoc->last->flags)
389		return(1);
390	mdoc->last->flags |= MDOC_VALID;
391
392	switch (mdoc->last->type) {
393	case (MDOC_TEXT):
394		/* FALLTHROUGH */
395	case (MDOC_EQN):
396		/* FALLTHROUGH */
397	case (MDOC_TBL):
398		return(1);
399	case (MDOC_ROOT):
400		return(post_root(mdoc));
401	default:
402		break;
403	}
404
405	if (NULL == mdoc_valids[mdoc->last->tok].post)
406		return(1);
407	for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
408		if ( ! (*p)(mdoc))
409			return(0);
410
411	return(1);
412}
413
414static int
415check_count(struct mdoc *m, enum mdoc_type type,
416		enum check_lvl lvl, enum check_ineq ineq, int val)
417{
418	const char	*p;
419	enum mandocerr	 t;
420
421	if (m->last->type != type)
422		return(1);
423
424	switch (ineq) {
425	case (CHECK_LT):
426		p = "less than ";
427		if (m->last->nchild < val)
428			return(1);
429		break;
430	case (CHECK_GT):
431		p = "more than ";
432		if (m->last->nchild > val)
433			return(1);
434		break;
435	case (CHECK_EQ):
436		p = "";
437		if (val == m->last->nchild)
438			return(1);
439		break;
440	default:
441		abort();
442		/* NOTREACHED */
443	}
444
445	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
446	mandoc_vmsg(t, m->parse, m->last->line, m->last->pos,
447			"want %s%d children (have %d)",
448			p, val, m->last->nchild);
449	return(1);
450}
451
452static int
453berr_ge1(POST_ARGS)
454{
455
456	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
457}
458
459static int
460bwarn_ge1(POST_ARGS)
461{
462	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
463}
464
465static int
466ewarn_eq0(POST_ARGS)
467{
468	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
469}
470
471static int
472ewarn_eq1(POST_ARGS)
473{
474	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
475}
476
477static int
478ewarn_ge1(POST_ARGS)
479{
480	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
481}
482
483static int
484ewarn_le1(POST_ARGS)
485{
486	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
487}
488
489static int
490hwarn_eq0(POST_ARGS)
491{
492	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
493}
494
495static int
496hwarn_eq1(POST_ARGS)
497{
498	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
499}
500
501static int
502hwarn_ge1(POST_ARGS)
503{
504	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
505}
506
507static int
508hwarn_le1(POST_ARGS)
509{
510	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
511}
512
513static void
514check_args(struct mdoc *m, struct mdoc_node *n)
515{
516	int		 i;
517
518	if (NULL == n->args)
519		return;
520
521	assert(n->args->argc);
522	for (i = 0; i < (int)n->args->argc; i++)
523		check_argv(m, n, &n->args->argv[i]);
524}
525
526static void
527check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
528{
529	int		 i;
530
531	for (i = 0; i < (int)v->sz; i++)
532		check_text(m, v->line, v->pos, v->value[i]);
533
534	/* FIXME: move to post_std(). */
535
536	if (MDOC_Std == v->arg)
537		if ( ! (v->sz || m->meta.name))
538			mdoc_nmsg(m, n, MANDOCERR_NONAME);
539}
540
541static void
542check_text(struct mdoc *m, int ln, int pos, char *p)
543{
544	char		*cp;
545
546	if (MDOC_LITERAL & m->flags)
547		return;
548
549	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
550		mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
551}
552
553static int
554check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
555{
556
557	assert(n->parent);
558	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
559			(t == n->parent->type))
560		return(1);
561
562	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
563			n->pos, "want parent %s", MDOC_ROOT == t ?
564			"<root>" : mdoc_macronames[tok]);
565	return(0);
566}
567
568
569static int
570pre_display(PRE_ARGS)
571{
572	struct mdoc_node *node;
573
574	if (MDOC_BLOCK != n->type)
575		return(1);
576
577	for (node = mdoc->last->parent; node; node = node->parent)
578		if (MDOC_BLOCK == node->type)
579			if (MDOC_Bd == node->tok)
580				break;
581
582	if (node)
583		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
584
585	return(1);
586}
587
588
589static int
590pre_bl(PRE_ARGS)
591{
592	int		  i, comp, dup;
593	const char	 *offs, *width;
594	enum mdoc_list	  lt;
595	struct mdoc_node *np;
596
597	if (MDOC_BLOCK != n->type) {
598		if (ENDBODY_NOT != n->end) {
599			assert(n->pending);
600			np = n->pending->parent;
601		} else
602			np = n->parent;
603
604		assert(np);
605		assert(MDOC_BLOCK == np->type);
606		assert(MDOC_Bl == np->tok);
607		return(1);
608	}
609
610	/*
611	 * First figure out which kind of list to use: bind ourselves to
612	 * the first mentioned list type and warn about any remaining
613	 * ones.  If we find no list type, we default to LIST_item.
614	 */
615
616	/* LINTED */
617	for (i = 0; n->args && i < (int)n->args->argc; i++) {
618		lt = LIST__NONE;
619		dup = comp = 0;
620		width = offs = NULL;
621		switch (n->args->argv[i].arg) {
622		/* Set list types. */
623		case (MDOC_Bullet):
624			lt = LIST_bullet;
625			break;
626		case (MDOC_Dash):
627			lt = LIST_dash;
628			break;
629		case (MDOC_Enum):
630			lt = LIST_enum;
631			break;
632		case (MDOC_Hyphen):
633			lt = LIST_hyphen;
634			break;
635		case (MDOC_Item):
636			lt = LIST_item;
637			break;
638		case (MDOC_Tag):
639			lt = LIST_tag;
640			break;
641		case (MDOC_Diag):
642			lt = LIST_diag;
643			break;
644		case (MDOC_Hang):
645			lt = LIST_hang;
646			break;
647		case (MDOC_Ohang):
648			lt = LIST_ohang;
649			break;
650		case (MDOC_Inset):
651			lt = LIST_inset;
652			break;
653		case (MDOC_Column):
654			lt = LIST_column;
655			break;
656		/* Set list arguments. */
657		case (MDOC_Compact):
658			dup = n->norm->Bl.comp;
659			comp = 1;
660			break;
661		case (MDOC_Width):
662			/* NB: this can be empty! */
663			if (n->args->argv[i].sz) {
664				width = n->args->argv[i].value[0];
665				dup = (NULL != n->norm->Bl.width);
666				break;
667			}
668			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
669			break;
670		case (MDOC_Offset):
671			/* NB: this can be empty! */
672			if (n->args->argv[i].sz) {
673				offs = n->args->argv[i].value[0];
674				dup = (NULL != n->norm->Bl.offs);
675				break;
676			}
677			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
678			break;
679		default:
680			continue;
681		}
682
683		/* Check: duplicate auxiliary arguments. */
684
685		if (dup)
686			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
687
688		if (comp && ! dup)
689			n->norm->Bl.comp = comp;
690		if (offs && ! dup)
691			n->norm->Bl.offs = offs;
692		if (width && ! dup)
693			n->norm->Bl.width = width;
694
695		/* Check: multiple list types. */
696
697		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
698			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
699
700		/* Assign list type. */
701
702		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
703			n->norm->Bl.type = lt;
704			/* Set column information, too. */
705			if (LIST_column == lt) {
706				n->norm->Bl.ncols =
707					n->args->argv[i].sz;
708				n->norm->Bl.cols = (void *)
709					n->args->argv[i].value;
710			}
711		}
712
713		/* The list type should come first. */
714
715		if (n->norm->Bl.type == LIST__NONE)
716			if (n->norm->Bl.width ||
717					n->norm->Bl.offs ||
718					n->norm->Bl.comp)
719				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
720
721		continue;
722	}
723
724	/* Allow lists to default to LIST_item. */
725
726	if (LIST__NONE == n->norm->Bl.type) {
727		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
728		n->norm->Bl.type = LIST_item;
729	}
730
731	/*
732	 * Validate the width field.  Some list types don't need width
733	 * types and should be warned about them.  Others should have it
734	 * and must also be warned.  Yet others have a default and need
735	 * no warning.
736	 */
737
738	switch (n->norm->Bl.type) {
739	case (LIST_tag):
740		if (NULL == n->norm->Bl.width)
741			mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
742		break;
743	case (LIST_column):
744		/* FALLTHROUGH */
745	case (LIST_diag):
746		/* FALLTHROUGH */
747	case (LIST_ohang):
748		/* FALLTHROUGH */
749	case (LIST_inset):
750		/* FALLTHROUGH */
751	case (LIST_item):
752		if (n->norm->Bl.width)
753			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
754		break;
755	case (LIST_bullet):
756		/* FALLTHROUGH */
757	case (LIST_dash):
758		/* FALLTHROUGH */
759	case (LIST_hyphen):
760		if (NULL == n->norm->Bl.width)
761			n->norm->Bl.width = "2n";
762		break;
763	case (LIST_enum):
764		if (NULL == n->norm->Bl.width)
765			n->norm->Bl.width = "3n";
766		break;
767	default:
768		break;
769	}
770
771	return(1);
772}
773
774
775static int
776pre_bd(PRE_ARGS)
777{
778	int		  i, dup, comp;
779	enum mdoc_disp 	  dt;
780	const char	 *offs;
781	struct mdoc_node *np;
782
783	if (MDOC_BLOCK != n->type) {
784		if (ENDBODY_NOT != n->end) {
785			assert(n->pending);
786			np = n->pending->parent;
787		} else
788			np = n->parent;
789
790		assert(np);
791		assert(MDOC_BLOCK == np->type);
792		assert(MDOC_Bd == np->tok);
793		return(1);
794	}
795
796	/* LINTED */
797	for (i = 0; n->args && i < (int)n->args->argc; i++) {
798		dt = DISP__NONE;
799		dup = comp = 0;
800		offs = NULL;
801
802		switch (n->args->argv[i].arg) {
803		case (MDOC_Centred):
804			dt = DISP_centred;
805			break;
806		case (MDOC_Ragged):
807			dt = DISP_ragged;
808			break;
809		case (MDOC_Unfilled):
810			dt = DISP_unfilled;
811			break;
812		case (MDOC_Filled):
813			dt = DISP_filled;
814			break;
815		case (MDOC_Literal):
816			dt = DISP_literal;
817			break;
818		case (MDOC_File):
819			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
820			return(0);
821		case (MDOC_Offset):
822			/* NB: this can be empty! */
823			if (n->args->argv[i].sz) {
824				offs = n->args->argv[i].value[0];
825				dup = (NULL != n->norm->Bd.offs);
826				break;
827			}
828			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
829			break;
830		case (MDOC_Compact):
831			comp = 1;
832			dup = n->norm->Bd.comp;
833			break;
834		default:
835			abort();
836			/* NOTREACHED */
837		}
838
839		/* Check whether we have duplicates. */
840
841		if (dup)
842			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
843
844		/* Make our auxiliary assignments. */
845
846		if (offs && ! dup)
847			n->norm->Bd.offs = offs;
848		if (comp && ! dup)
849			n->norm->Bd.comp = comp;
850
851		/* Check whether a type has already been assigned. */
852
853		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
854			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
855
856		/* Make our type assignment. */
857
858		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
859			n->norm->Bd.type = dt;
860	}
861
862	if (DISP__NONE == n->norm->Bd.type) {
863		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
864		n->norm->Bd.type = DISP_ragged;
865	}
866
867	return(1);
868}
869
870
871static int
872pre_ss(PRE_ARGS)
873{
874
875	if (MDOC_BLOCK != n->type)
876		return(1);
877	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
878}
879
880
881static int
882pre_sh(PRE_ARGS)
883{
884
885	if (MDOC_BLOCK != n->type)
886		return(1);
887
888	roff_regunset(mdoc->roff, REG_nS);
889	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
890}
891
892
893static int
894pre_it(PRE_ARGS)
895{
896
897	if (MDOC_BLOCK != n->type)
898		return(1);
899
900	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
901}
902
903
904static int
905pre_an(PRE_ARGS)
906{
907	int		 i;
908
909	if (NULL == n->args)
910		return(1);
911
912	for (i = 1; i < (int)n->args->argc; i++)
913		mdoc_pmsg(mdoc, n->args->argv[i].line,
914			n->args->argv[i].pos, MANDOCERR_IGNARGV);
915
916	if (MDOC_Split == n->args->argv[0].arg)
917		n->norm->An.auth = AUTH_split;
918	else if (MDOC_Nosplit == n->args->argv[0].arg)
919		n->norm->An.auth = AUTH_nosplit;
920	else
921		abort();
922
923	return(1);
924}
925
926static int
927pre_std(PRE_ARGS)
928{
929
930	if (n->args && 1 == n->args->argc)
931		if (MDOC_Std == n->args->argv[0].arg)
932			return(1);
933
934	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
935	return(1);
936}
937
938static int
939pre_dt(PRE_ARGS)
940{
941
942	if (NULL == mdoc->meta.date || mdoc->meta.os)
943		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
944
945	if (mdoc->meta.title)
946		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
947
948	return(1);
949}
950
951static int
952pre_os(PRE_ARGS)
953{
954
955	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
956		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
957
958	if (mdoc->meta.os)
959		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
960
961	return(1);
962}
963
964static int
965pre_dd(PRE_ARGS)
966{
967
968	if (mdoc->meta.title || mdoc->meta.os)
969		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
970
971	if (mdoc->meta.date)
972		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
973
974	return(1);
975}
976
977
978static int
979post_bf(POST_ARGS)
980{
981	struct mdoc_node *np;
982	enum mdocargt	  arg;
983
984	/*
985	 * Unlike other data pointers, these are "housed" by the HEAD
986	 * element, which contains the goods.
987	 */
988
989	if (MDOC_HEAD != mdoc->last->type) {
990		if (ENDBODY_NOT != mdoc->last->end) {
991			assert(mdoc->last->pending);
992			np = mdoc->last->pending->parent->head;
993		} else if (MDOC_BLOCK != mdoc->last->type) {
994			np = mdoc->last->parent->head;
995		} else
996			np = mdoc->last->head;
997
998		assert(np);
999		assert(MDOC_HEAD == np->type);
1000		assert(MDOC_Bf == np->tok);
1001		return(1);
1002	}
1003
1004	np = mdoc->last;
1005	assert(MDOC_BLOCK == np->parent->type);
1006	assert(MDOC_Bf == np->parent->tok);
1007
1008	/*
1009	 * Cannot have both argument and parameter.
1010	 * If neither is specified, let it through with a warning.
1011	 */
1012
1013	if (np->parent->args && np->child) {
1014		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1015		return(0);
1016	} else if (NULL == np->parent->args && NULL == np->child) {
1017		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1018		return(1);
1019	}
1020
1021	/* Extract argument into data. */
1022
1023	if (np->parent->args) {
1024		arg = np->parent->args->argv[0].arg;
1025		if (MDOC_Emphasis == arg)
1026			np->norm->Bf.font = FONT_Em;
1027		else if (MDOC_Literal == arg)
1028			np->norm->Bf.font = FONT_Li;
1029		else if (MDOC_Symbolic == arg)
1030			np->norm->Bf.font = FONT_Sy;
1031		else
1032			abort();
1033		return(1);
1034	}
1035
1036	/* Extract parameter into data. */
1037
1038	if (0 == strcmp(np->child->string, "Em"))
1039		np->norm->Bf.font = FONT_Em;
1040	else if (0 == strcmp(np->child->string, "Li"))
1041		np->norm->Bf.font = FONT_Li;
1042	else if (0 == strcmp(np->child->string, "Sy"))
1043		np->norm->Bf.font = FONT_Sy;
1044	else
1045		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1046
1047	return(1);
1048}
1049
1050static int
1051post_lb(POST_ARGS)
1052{
1053	const char	*p;
1054	char		*buf;
1055	size_t		 sz;
1056
1057	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1058
1059	assert(mdoc->last->child);
1060	assert(MDOC_TEXT == mdoc->last->child->type);
1061
1062	p = mdoc_a2lib(mdoc->last->child->string);
1063
1064	/* If lookup ok, replace with table value. */
1065
1066	if (p) {
1067		free(mdoc->last->child->string);
1068		mdoc->last->child->string = mandoc_strdup(p);
1069		return(1);
1070	}
1071
1072	/* If not, use "library ``xxxx''. */
1073
1074	sz = strlen(mdoc->last->child->string) +
1075		2 + strlen("\\(lqlibrary\\(rq");
1076	buf = mandoc_malloc(sz);
1077	snprintf(buf, sz, "library \\(lq%s\\(rq",
1078			mdoc->last->child->string);
1079	free(mdoc->last->child->string);
1080	mdoc->last->child->string = buf;
1081	return(1);
1082}
1083
1084static int
1085post_eoln(POST_ARGS)
1086{
1087
1088	if (mdoc->last->child)
1089		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1090	return(1);
1091}
1092
1093
1094static int
1095post_vt(POST_ARGS)
1096{
1097	const struct mdoc_node *n;
1098
1099	/*
1100	 * The Vt macro comes in both ELEM and BLOCK form, both of which
1101	 * have different syntaxes (yet more context-sensitive
1102	 * behaviour).  ELEM types must have a child, which is already
1103	 * guaranteed by the in_line parsing routine; BLOCK types,
1104	 * specifically the BODY, should only have TEXT children.
1105	 */
1106
1107	if (MDOC_BODY != mdoc->last->type)
1108		return(1);
1109
1110	for (n = mdoc->last->child; n; n = n->next)
1111		if (MDOC_TEXT != n->type)
1112			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1113
1114	return(1);
1115}
1116
1117
1118static int
1119post_nm(POST_ARGS)
1120{
1121	char		 buf[BUFSIZ];
1122	int		 c;
1123
1124	if (NULL != mdoc->meta.name)
1125		return(1);
1126
1127	/* Try to use our children for setting the meta name. */
1128
1129	if (NULL != mdoc->last->child) {
1130		buf[0] = '\0';
1131		c = concat(buf, mdoc->last->child, BUFSIZ);
1132	} else
1133		c = 0;
1134
1135	switch (c) {
1136	case (-1):
1137		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1138		return(0);
1139	case (0):
1140		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1141		mdoc->meta.name = mandoc_strdup("UNKNOWN");
1142		break;
1143	default:
1144		mdoc->meta.name = mandoc_strdup(buf);
1145		break;
1146	}
1147	return(1);
1148}
1149
1150static int
1151post_literal(POST_ARGS)
1152{
1153
1154	/*
1155	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1156	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1157	 * this in literal mode, but it doesn't hurt to just switch it
1158	 * off in general since displays can't be nested.
1159	 */
1160
1161	if (MDOC_BODY == mdoc->last->type)
1162		mdoc->flags &= ~MDOC_LITERAL;
1163
1164	return(1);
1165}
1166
1167static int
1168post_defaults(POST_ARGS)
1169{
1170	struct mdoc_node *nn;
1171
1172	/*
1173	 * The `Ar' defaults to "file ..." if no value is provided as an
1174	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1175	 * gets an empty string.
1176	 */
1177
1178	if (mdoc->last->child)
1179		return(1);
1180
1181	nn = mdoc->last;
1182	mdoc->next = MDOC_NEXT_CHILD;
1183
1184	switch (nn->tok) {
1185	case (MDOC_Ar):
1186		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1187			return(0);
1188		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1189			return(0);
1190		break;
1191	case (MDOC_At):
1192		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1193			return(0);
1194		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1195			return(0);
1196		break;
1197	case (MDOC_Li):
1198		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1199			return(0);
1200		break;
1201	case (MDOC_Pa):
1202		/* FALLTHROUGH */
1203	case (MDOC_Mt):
1204		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1205			return(0);
1206		break;
1207	default:
1208		abort();
1209		/* NOTREACHED */
1210	}
1211
1212	mdoc->last = nn;
1213	return(1);
1214}
1215
1216static int
1217post_at(POST_ARGS)
1218{
1219	const char	 *p, *q;
1220	char		 *buf;
1221	size_t		  sz;
1222
1223	/*
1224	 * If we have a child, look it up in the standard keys.  If a
1225	 * key exist, use that instead of the child; if it doesn't,
1226	 * prefix "AT&T UNIX " to the existing data.
1227	 */
1228
1229	if (NULL == mdoc->last->child)
1230		return(1);
1231
1232	assert(MDOC_TEXT == mdoc->last->child->type);
1233	p = mdoc_a2att(mdoc->last->child->string);
1234
1235	if (p) {
1236		free(mdoc->last->child->string);
1237		mdoc->last->child->string = mandoc_strdup(p);
1238	} else {
1239		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1240		p = "AT&T UNIX ";
1241		q = mdoc->last->child->string;
1242		sz = strlen(p) + strlen(q) + 1;
1243		buf = mandoc_malloc(sz);
1244		strlcpy(buf, p, sz);
1245		strlcat(buf, q, sz);
1246		free(mdoc->last->child->string);
1247		mdoc->last->child->string = buf;
1248	}
1249
1250	return(1);
1251}
1252
1253static int
1254post_an(POST_ARGS)
1255{
1256	struct mdoc_node *np;
1257
1258	np = mdoc->last;
1259	if (AUTH__NONE == np->norm->An.auth) {
1260		if (0 == np->child)
1261			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1262	} else if (np->child)
1263		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1264
1265	return(1);
1266}
1267
1268
1269static int
1270post_it(POST_ARGS)
1271{
1272	int		  i, cols;
1273	enum mdoc_list	  lt;
1274	struct mdoc_node *n, *c;
1275	enum mandocerr	  er;
1276
1277	if (MDOC_BLOCK != mdoc->last->type)
1278		return(1);
1279
1280	n = mdoc->last->parent->parent;
1281	lt = n->norm->Bl.type;
1282
1283	if (LIST__NONE == lt) {
1284		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1285		return(1);
1286	}
1287
1288	switch (lt) {
1289	case (LIST_tag):
1290		if (mdoc->last->head->child)
1291			break;
1292		/* FIXME: give this a dummy value. */
1293		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1294		break;
1295	case (LIST_hang):
1296		/* FALLTHROUGH */
1297	case (LIST_ohang):
1298		/* FALLTHROUGH */
1299	case (LIST_inset):
1300		/* FALLTHROUGH */
1301	case (LIST_diag):
1302		if (NULL == mdoc->last->head->child)
1303			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1304		break;
1305	case (LIST_bullet):
1306		/* FALLTHROUGH */
1307	case (LIST_dash):
1308		/* FALLTHROUGH */
1309	case (LIST_enum):
1310		/* FALLTHROUGH */
1311	case (LIST_hyphen):
1312		if (NULL == mdoc->last->body->child)
1313			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1314		/* FALLTHROUGH */
1315	case (LIST_item):
1316		if (mdoc->last->head->child)
1317			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1318		break;
1319	case (LIST_column):
1320		cols = (int)n->norm->Bl.ncols;
1321
1322		assert(NULL == mdoc->last->head->child);
1323
1324		if (NULL == mdoc->last->body->child)
1325			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1326
1327		for (i = 0, c = mdoc->last->child; c; c = c->next)
1328			if (MDOC_BODY == c->type)
1329				i++;
1330
1331		if (i < cols)
1332			er = MANDOCERR_ARGCOUNT;
1333		else if (i == cols || i == cols + 1)
1334			break;
1335		else
1336			er = MANDOCERR_SYNTARGCOUNT;
1337
1338		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1339				mdoc->last->pos,
1340				"columns == %d (have %d)", cols, i);
1341		return(MANDOCERR_ARGCOUNT == er);
1342	default:
1343		break;
1344	}
1345
1346	return(1);
1347}
1348
1349static int
1350post_bl_block(POST_ARGS)
1351{
1352	struct mdoc_node *n, *ni, *nc;
1353
1354	/*
1355	 * These are fairly complicated, so we've broken them into two
1356	 * functions.  post_bl_block_tag() is called when a -tag is
1357	 * specified, but no -width (it must be guessed).  The second
1358	 * when a -width is specified (macro indicators must be
1359	 * rewritten into real lengths).
1360	 */
1361
1362	n = mdoc->last;
1363
1364	if (LIST_tag == n->norm->Bl.type &&
1365			NULL == n->norm->Bl.width) {
1366		if ( ! post_bl_block_tag(mdoc))
1367			return(0);
1368		assert(n->norm->Bl.width);
1369	} else if (NULL != n->norm->Bl.width) {
1370		if ( ! post_bl_block_width(mdoc))
1371			return(0);
1372		assert(n->norm->Bl.width);
1373	}
1374
1375	for (ni = n->body->child; ni; ni = ni->next) {
1376		if (NULL == ni->body)
1377			continue;
1378		nc = ni->body->last;
1379		while (NULL != nc) {
1380			switch (nc->tok) {
1381			case (MDOC_Pp):
1382				/* FALLTHROUGH */
1383			case (MDOC_Lp):
1384				/* FALLTHROUGH */
1385			case (MDOC_br):
1386				break;
1387			default:
1388				nc = NULL;
1389				continue;
1390			}
1391			if (NULL == ni->next) {
1392				mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
1393				if ( ! mdoc_node_relink(mdoc, nc))
1394					return(0);
1395			} else if (0 == n->norm->Bl.comp &&
1396			    LIST_column != n->norm->Bl.type) {
1397				mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
1398				mdoc_node_delete(mdoc, nc);
1399			} else
1400				break;
1401			nc = ni->body->last;
1402		}
1403	}
1404	return(1);
1405}
1406
1407static int
1408post_bl_block_width(POST_ARGS)
1409{
1410	size_t		  width;
1411	int		  i;
1412	enum mdoct	  tok;
1413	struct mdoc_node *n;
1414	char		  buf[NUMSIZ];
1415
1416	n = mdoc->last;
1417
1418	/*
1419	 * Calculate the real width of a list from the -width string,
1420	 * which may contain a macro (with a known default width), a
1421	 * literal string, or a scaling width.
1422	 *
1423	 * If the value to -width is a macro, then we re-write it to be
1424	 * the macro's width as set in share/tmac/mdoc/doc-common.
1425	 */
1426
1427	if (0 == strcmp(n->norm->Bl.width, "Ds"))
1428		width = 6;
1429	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1430		return(1);
1431	else if (0 == (width = macro2len(tok)))  {
1432		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1433		return(1);
1434	}
1435
1436	/* The value already exists: free and reallocate it. */
1437
1438	assert(n->args);
1439
1440	for (i = 0; i < (int)n->args->argc; i++)
1441		if (MDOC_Width == n->args->argv[i].arg)
1442			break;
1443
1444	assert(i < (int)n->args->argc);
1445
1446	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1447	free(n->args->argv[i].value[0]);
1448	n->args->argv[i].value[0] = mandoc_strdup(buf);
1449
1450	/* Set our width! */
1451	n->norm->Bl.width = n->args->argv[i].value[0];
1452	return(1);
1453}
1454
1455static int
1456post_bl_block_tag(POST_ARGS)
1457{
1458	struct mdoc_node *n, *nn;
1459	size_t		  sz, ssz;
1460	int		  i;
1461	char		  buf[NUMSIZ];
1462
1463	/*
1464	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1465	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1466	 * ONLY if the -width argument has NOT been provided.  See
1467	 * post_bl_block_width() for converting the -width string.
1468	 */
1469
1470	sz = 10;
1471	n = mdoc->last;
1472
1473	for (nn = n->body->child; nn; nn = nn->next) {
1474		if (MDOC_It != nn->tok)
1475			continue;
1476
1477		assert(MDOC_BLOCK == nn->type);
1478		nn = nn->head->child;
1479
1480		if (nn == NULL)
1481			break;
1482
1483		if (MDOC_TEXT == nn->type) {
1484			sz = strlen(nn->string) + 1;
1485			break;
1486		}
1487
1488		if (0 != (ssz = macro2len(nn->tok)))
1489			sz = ssz;
1490
1491		break;
1492	}
1493
1494	/* Defaults to ten ens. */
1495
1496	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1497
1498	/*
1499	 * We have to dynamically add this to the macro's argument list.
1500	 * We're guaranteed that a MDOC_Width doesn't already exist.
1501	 */
1502
1503	assert(n->args);
1504	i = (int)(n->args->argc)++;
1505
1506	n->args->argv = mandoc_realloc(n->args->argv,
1507			n->args->argc * sizeof(struct mdoc_argv));
1508
1509	n->args->argv[i].arg = MDOC_Width;
1510	n->args->argv[i].line = n->line;
1511	n->args->argv[i].pos = n->pos;
1512	n->args->argv[i].sz = 1;
1513	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1514	n->args->argv[i].value[0] = mandoc_strdup(buf);
1515
1516	/* Set our width! */
1517	n->norm->Bl.width = n->args->argv[i].value[0];
1518	return(1);
1519}
1520
1521
1522static int
1523post_bl_head(POST_ARGS)
1524{
1525	struct mdoc_node *np, *nn, *nnp;
1526	int		  i, j;
1527
1528	if (LIST_column != mdoc->last->norm->Bl.type)
1529		/* FIXME: this should be ERROR class... */
1530		return(hwarn_eq0(mdoc));
1531
1532	/*
1533	 * Convert old-style lists, where the column width specifiers
1534	 * trail as macro parameters, to the new-style ("normal-form")
1535	 * lists where they're argument values following -column.
1536	 */
1537
1538	/* First, disallow both types and allow normal-form. */
1539
1540	/*
1541	 * TODO: technically, we can accept both and just merge the two
1542	 * lists, but I'll leave that for another day.
1543	 */
1544
1545	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1546		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1547		return(0);
1548	} else if (NULL == mdoc->last->child)
1549		return(1);
1550
1551	np = mdoc->last->parent;
1552	assert(np->args);
1553
1554	for (j = 0; j < (int)np->args->argc; j++)
1555		if (MDOC_Column == np->args->argv[j].arg)
1556			break;
1557
1558	assert(j < (int)np->args->argc);
1559	assert(0 == np->args->argv[j].sz);
1560
1561	/*
1562	 * Accommodate for new-style groff column syntax.  Shuffle the
1563	 * child nodes, all of which must be TEXT, as arguments for the
1564	 * column field.  Then, delete the head children.
1565	 */
1566
1567	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1568	np->args->argv[j].value = mandoc_malloc
1569		((size_t)mdoc->last->nchild * sizeof(char *));
1570
1571	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1572	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1573
1574	for (i = 0, nn = mdoc->last->child; nn; i++) {
1575		np->args->argv[j].value[i] = nn->string;
1576		nn->string = NULL;
1577		nnp = nn;
1578		nn = nn->next;
1579		mdoc_node_delete(NULL, nnp);
1580	}
1581
1582	mdoc->last->nchild = 0;
1583	mdoc->last->child = NULL;
1584
1585	return(1);
1586}
1587
1588static int
1589post_bl(POST_ARGS)
1590{
1591	struct mdoc_node	*n;
1592
1593	if (MDOC_HEAD == mdoc->last->type)
1594		return(post_bl_head(mdoc));
1595	if (MDOC_BLOCK == mdoc->last->type)
1596		return(post_bl_block(mdoc));
1597	if (MDOC_BODY != mdoc->last->type)
1598		return(1);
1599
1600	for (n = mdoc->last->child; n; n = n->next) {
1601		switch (n->tok) {
1602		case (MDOC_Lp):
1603			/* FALLTHROUGH */
1604		case (MDOC_Pp):
1605			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1606			/* FALLTHROUGH */
1607		case (MDOC_It):
1608			/* FALLTHROUGH */
1609		case (MDOC_Sm):
1610			continue;
1611		default:
1612			break;
1613		}
1614
1615		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1616		return(0);
1617	}
1618
1619	return(1);
1620}
1621
1622static int
1623ebool(struct mdoc *mdoc)
1624{
1625
1626	if (NULL == mdoc->last->child) {
1627		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1628		mdoc_node_delete(mdoc, mdoc->last);
1629		return(1);
1630	}
1631	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1632
1633	assert(MDOC_TEXT == mdoc->last->child->type);
1634
1635	if (0 == strcmp(mdoc->last->child->string, "on"))
1636		return(1);
1637	if (0 == strcmp(mdoc->last->child->string, "off"))
1638		return(1);
1639
1640	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1641	return(1);
1642}
1643
1644static int
1645post_root(POST_ARGS)
1646{
1647	int		  erc;
1648	struct mdoc_node *n;
1649
1650	erc = 0;
1651
1652	/* Check that we have a finished prologue. */
1653
1654	if ( ! (MDOC_PBODY & mdoc->flags)) {
1655		erc++;
1656		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1657	}
1658
1659	n = mdoc->first;
1660	assert(n);
1661
1662	/* Check that we begin with a proper `Sh'. */
1663
1664	if (NULL == n->child) {
1665		erc++;
1666		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1667	} else if (MDOC_BLOCK != n->child->type ||
1668			MDOC_Sh != n->child->tok) {
1669		erc++;
1670		/* Can this be lifted?  See rxdebug.1 for example. */
1671		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1672	}
1673
1674	return(erc ? 0 : 1);
1675}
1676
1677static int
1678post_st(POST_ARGS)
1679{
1680	struct mdoc_node	 *ch;
1681	const char		 *p;
1682
1683	if (NULL == (ch = mdoc->last->child)) {
1684		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1685		mdoc_node_delete(mdoc, mdoc->last);
1686		return(1);
1687	}
1688
1689	assert(MDOC_TEXT == ch->type);
1690
1691	if (NULL == (p = mdoc_a2st(ch->string))) {
1692		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1693		mdoc_node_delete(mdoc, mdoc->last);
1694	} else {
1695		free(ch->string);
1696		ch->string = mandoc_strdup(p);
1697	}
1698
1699	return(1);
1700}
1701
1702static int
1703post_rs(POST_ARGS)
1704{
1705	struct mdoc_node *nn, *next, *prev;
1706	int		  i, j;
1707
1708	switch (mdoc->last->type) {
1709	case (MDOC_HEAD):
1710		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1711		return(1);
1712	case (MDOC_BODY):
1713		if (mdoc->last->child)
1714			break;
1715		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1716		return(1);
1717	default:
1718		return(1);
1719	}
1720
1721	/*
1722	 * Make sure only certain types of nodes are allowed within the
1723	 * the `Rs' body.  Delete offending nodes and raise a warning.
1724	 * Do this before re-ordering for the sake of clarity.
1725	 */
1726
1727	next = NULL;
1728	for (nn = mdoc->last->child; nn; nn = next) {
1729		for (i = 0; i < RSORD_MAX; i++)
1730			if (nn->tok == rsord[i])
1731				break;
1732
1733		if (i < RSORD_MAX) {
1734			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1735				mdoc->last->norm->Rs.quote_T++;
1736			next = nn->next;
1737			continue;
1738		}
1739
1740		next = nn->next;
1741		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1742		mdoc_node_delete(mdoc, nn);
1743	}
1744
1745	/*
1746	 * Nothing to sort if only invalid nodes were found
1747	 * inside the `Rs' body.
1748	 */
1749
1750	if (NULL == mdoc->last->child)
1751		return(1);
1752
1753	/*
1754	 * The full `Rs' block needs special handling to order the
1755	 * sub-elements according to `rsord'.  Pick through each element
1756	 * and correctly order it.  This is a insertion sort.
1757	 */
1758
1759	next = NULL;
1760	for (nn = mdoc->last->child->next; nn; nn = next) {
1761		/* Determine order of `nn'. */
1762		for (i = 0; i < RSORD_MAX; i++)
1763			if (rsord[i] == nn->tok)
1764				break;
1765
1766		/*
1767		 * Remove `nn' from the chain.  This somewhat
1768		 * repeats mdoc_node_unlink(), but since we're
1769		 * just re-ordering, there's no need for the
1770		 * full unlink process.
1771		 */
1772
1773		if (NULL != (next = nn->next))
1774			next->prev = nn->prev;
1775
1776		if (NULL != (prev = nn->prev))
1777			prev->next = nn->next;
1778
1779		nn->prev = nn->next = NULL;
1780
1781		/*
1782		 * Scan back until we reach a node that's
1783		 * ordered before `nn'.
1784		 */
1785
1786		for ( ; prev ; prev = prev->prev) {
1787			/* Determine order of `prev'. */
1788			for (j = 0; j < RSORD_MAX; j++)
1789				if (rsord[j] == prev->tok)
1790					break;
1791
1792			if (j <= i)
1793				break;
1794		}
1795
1796		/*
1797		 * Set `nn' back into its correct place in front
1798		 * of the `prev' node.
1799		 */
1800
1801		nn->prev = prev;
1802
1803		if (prev) {
1804			if (prev->next)
1805				prev->next->prev = nn;
1806			nn->next = prev->next;
1807			prev->next = nn;
1808		} else {
1809			mdoc->last->child->prev = nn;
1810			nn->next = mdoc->last->child;
1811			mdoc->last->child = nn;
1812		}
1813	}
1814
1815	return(1);
1816}
1817
1818static int
1819post_ns(POST_ARGS)
1820{
1821
1822	if (MDOC_LINE & mdoc->last->flags)
1823		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1824	return(1);
1825}
1826
1827static int
1828post_sh(POST_ARGS)
1829{
1830
1831	if (MDOC_HEAD == mdoc->last->type)
1832		return(post_sh_head(mdoc));
1833	if (MDOC_BODY == mdoc->last->type)
1834		return(post_sh_body(mdoc));
1835
1836	return(1);
1837}
1838
1839static int
1840post_sh_body(POST_ARGS)
1841{
1842	struct mdoc_node *n;
1843
1844	if (SEC_NAME != mdoc->lastsec)
1845		return(1);
1846
1847	/*
1848	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1849	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1850	 * children of the BODY declaration can also be "text".
1851	 */
1852
1853	if (NULL == (n = mdoc->last->child)) {
1854		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1855		return(1);
1856	}
1857
1858	for ( ; n && n->next; n = n->next) {
1859		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1860			continue;
1861		if (MDOC_TEXT == n->type)
1862			continue;
1863		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1864	}
1865
1866	assert(n);
1867	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1868		return(1);
1869
1870	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1871	return(1);
1872}
1873
1874static int
1875post_sh_head(POST_ARGS)
1876{
1877	char		 buf[BUFSIZ];
1878	struct mdoc_node *n;
1879	enum mdoc_sec	 sec;
1880	int		 c;
1881
1882	/*
1883	 * Process a new section.  Sections are either "named" or
1884	 * "custom".  Custom sections are user-defined, while named ones
1885	 * follow a conventional order and may only appear in certain
1886	 * manual sections.
1887	 */
1888
1889	sec = SEC_CUSTOM;
1890	buf[0] = '\0';
1891	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1892		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1893		return(0);
1894	} else if (1 == c)
1895		sec = a2sec(buf);
1896
1897	/* The NAME should be first. */
1898
1899	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1900		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1901
1902	/* The SYNOPSIS gets special attention in other areas. */
1903
1904	if (SEC_SYNOPSIS == sec)
1905		mdoc->flags |= MDOC_SYNOPSIS;
1906	else
1907		mdoc->flags &= ~MDOC_SYNOPSIS;
1908
1909	/* Mark our last section. */
1910
1911	mdoc->lastsec = sec;
1912
1913	/*
1914	 * Set the section attribute for the current HEAD, for its
1915	 * parent BLOCK, and for the HEAD children; the latter can
1916	 * only be TEXT nodes, so no recursion is needed.
1917	 * For other blocks and elements, including .Sh BODY, this is
1918	 * done when allocating the node data structures, but for .Sh
1919	 * BLOCK and HEAD, the section is still unknown at that time.
1920	 */
1921
1922	mdoc->last->parent->sec = sec;
1923	mdoc->last->sec = sec;
1924	for (n = mdoc->last->child; n; n = n->next)
1925		n->sec = sec;
1926
1927	/* We don't care about custom sections after this. */
1928
1929	if (SEC_CUSTOM == sec)
1930		return(1);
1931
1932	/*
1933	 * Check whether our non-custom section is being repeated or is
1934	 * out of order.
1935	 */
1936
1937	if (sec == mdoc->lastnamed)
1938		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1939
1940	if (sec < mdoc->lastnamed)
1941		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1942
1943	/* Mark the last named section. */
1944
1945	mdoc->lastnamed = sec;
1946
1947	/* Check particular section/manual conventions. */
1948
1949	assert(mdoc->meta.msec);
1950
1951	switch (sec) {
1952	case (SEC_RETURN_VALUES):
1953		/* FALLTHROUGH */
1954	case (SEC_ERRORS):
1955		/* FALLTHROUGH */
1956	case (SEC_LIBRARY):
1957		if (*mdoc->meta.msec == '2')
1958			break;
1959		if (*mdoc->meta.msec == '3')
1960			break;
1961		if (*mdoc->meta.msec == '9')
1962			break;
1963		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1964		break;
1965	default:
1966		break;
1967	}
1968
1969	return(1);
1970}
1971
1972static int
1973post_ignpar(POST_ARGS)
1974{
1975	struct mdoc_node *np;
1976
1977	if (MDOC_BODY != mdoc->last->type)
1978		return(1);
1979
1980	if (NULL != (np = mdoc->last->child))
1981		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1982			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1983			mdoc_node_delete(mdoc, np);
1984		}
1985
1986	if (NULL != (np = mdoc->last->last))
1987		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1988			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1989			mdoc_node_delete(mdoc, np);
1990		}
1991
1992	return(1);
1993}
1994
1995static int
1996pre_par(PRE_ARGS)
1997{
1998
1999	if (NULL == mdoc->last)
2000		return(1);
2001	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
2002		return(1);
2003
2004	/*
2005	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2006	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2007	 */
2008
2009	if (MDOC_Pp != mdoc->last->tok &&
2010	    MDOC_Lp != mdoc->last->tok &&
2011	    MDOC_br != mdoc->last->tok)
2012		return(1);
2013	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
2014		return(1);
2015	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
2016		return(1);
2017	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
2018		return(1);
2019
2020	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2021	mdoc_node_delete(mdoc, mdoc->last);
2022	return(1);
2023}
2024
2025static int
2026post_par(POST_ARGS)
2027{
2028
2029	if (MDOC_ELEM != mdoc->last->type &&
2030	    MDOC_BLOCK != mdoc->last->type)
2031		return(1);
2032
2033	if (NULL == mdoc->last->prev) {
2034		if (MDOC_Sh != mdoc->last->parent->tok &&
2035		    MDOC_Ss != mdoc->last->parent->tok)
2036			return(1);
2037	} else {
2038		if (MDOC_Pp != mdoc->last->prev->tok &&
2039		    MDOC_Lp != mdoc->last->prev->tok &&
2040		    (MDOC_br != mdoc->last->tok ||
2041		     (MDOC_sp != mdoc->last->prev->tok &&
2042		      MDOC_br != mdoc->last->prev->tok)))
2043			return(1);
2044	}
2045
2046	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
2047	mdoc_node_delete(mdoc, mdoc->last);
2048	return(1);
2049}
2050
2051static int
2052pre_literal(PRE_ARGS)
2053{
2054
2055	if (MDOC_BODY != n->type)
2056		return(1);
2057
2058	/*
2059	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
2060	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
2061	 */
2062
2063	switch (n->tok) {
2064	case (MDOC_Dl):
2065		mdoc->flags |= MDOC_LITERAL;
2066		break;
2067	case (MDOC_Bd):
2068		if (DISP_literal == n->norm->Bd.type)
2069			mdoc->flags |= MDOC_LITERAL;
2070		if (DISP_unfilled == n->norm->Bd.type)
2071			mdoc->flags |= MDOC_LITERAL;
2072		break;
2073	default:
2074		abort();
2075		/* NOTREACHED */
2076	}
2077
2078	return(1);
2079}
2080
2081static int
2082post_dd(POST_ARGS)
2083{
2084	char		  buf[DATESIZE];
2085	struct mdoc_node *n;
2086	int		  c;
2087
2088	if (mdoc->meta.date)
2089		free(mdoc->meta.date);
2090
2091	n = mdoc->last;
2092	if (NULL == n->child || '\0' == n->child->string[0]) {
2093		mdoc->meta.date = mandoc_normdate
2094			(mdoc->parse, NULL, n->line, n->pos);
2095		return(1);
2096	}
2097
2098	buf[0] = '\0';
2099	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2100		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2101		return(0);
2102	}
2103
2104	assert(c);
2105	mdoc->meta.date = mandoc_normdate
2106		(mdoc->parse, buf, n->line, n->pos);
2107
2108	return(1);
2109}
2110
2111static int
2112post_dt(POST_ARGS)
2113{
2114	struct mdoc_node *nn, *n;
2115	const char	 *cp;
2116	char		 *p;
2117
2118	n = mdoc->last;
2119
2120	if (mdoc->meta.title)
2121		free(mdoc->meta.title);
2122	if (mdoc->meta.vol)
2123		free(mdoc->meta.vol);
2124	if (mdoc->meta.arch)
2125		free(mdoc->meta.arch);
2126
2127	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2128
2129	/* First make all characters uppercase. */
2130
2131	if (NULL != (nn = n->child))
2132		for (p = nn->string; *p; p++) {
2133			if (toupper((unsigned char)*p) == *p)
2134				continue;
2135
2136			/*
2137			 * FIXME: don't be lazy: have this make all
2138			 * characters be uppercase and just warn once.
2139			 */
2140			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2141			break;
2142		}
2143
2144	/* Handles: `.Dt'
2145	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
2146	 */
2147
2148	if (NULL == (nn = n->child)) {
2149		/* XXX: make these macro values. */
2150		/* FIXME: warn about missing values. */
2151		mdoc->meta.title = mandoc_strdup("UNKNOWN");
2152		mdoc->meta.vol = mandoc_strdup("LOCAL");
2153		mdoc->meta.msec = mandoc_strdup("1");
2154		return(1);
2155	}
2156
2157	/* Handles: `.Dt TITLE'
2158	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2159	 */
2160
2161	mdoc->meta.title = mandoc_strdup
2162		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2163
2164	if (NULL == (nn = nn->next)) {
2165		/* FIXME: warn about missing msec. */
2166		/* XXX: make this a macro value. */
2167		mdoc->meta.vol = mandoc_strdup("LOCAL");
2168		mdoc->meta.msec = mandoc_strdup("1");
2169		return(1);
2170	}
2171
2172	/* Handles: `.Dt TITLE SEC'
2173	 *   --> title = TITLE, volume = SEC is msec ?
2174	 *           format(msec) : SEC,
2175	 *       msec = SEC is msec ? atoi(msec) : 0,
2176	 *       arch = NULL
2177	 */
2178
2179	cp = mandoc_a2msec(nn->string);
2180	if (cp) {
2181		mdoc->meta.vol = mandoc_strdup(cp);
2182		mdoc->meta.msec = mandoc_strdup(nn->string);
2183	} else {
2184		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2185		mdoc->meta.vol = mandoc_strdup(nn->string);
2186		mdoc->meta.msec = mandoc_strdup(nn->string);
2187	}
2188
2189	if (NULL == (nn = nn->next))
2190		return(1);
2191
2192	/* Handles: `.Dt TITLE SEC VOL'
2193	 *   --> title = TITLE, volume = VOL is vol ?
2194	 *       format(VOL) :
2195	 *           VOL is arch ? format(arch) :
2196	 *               VOL
2197	 */
2198
2199	cp = mdoc_a2vol(nn->string);
2200	if (cp) {
2201		free(mdoc->meta.vol);
2202		mdoc->meta.vol = mandoc_strdup(cp);
2203	} else {
2204		/* FIXME: warn about bad arch. */
2205		cp = mdoc_a2arch(nn->string);
2206		if (NULL == cp) {
2207			free(mdoc->meta.vol);
2208			mdoc->meta.vol = mandoc_strdup(nn->string);
2209		} else
2210			mdoc->meta.arch = mandoc_strdup(cp);
2211	}
2212
2213	/* Ignore any subsequent parameters... */
2214	/* FIXME: warn about subsequent parameters. */
2215
2216	return(1);
2217}
2218
2219static int
2220post_prol(POST_ARGS)
2221{
2222	/*
2223	 * Remove prologue macros from the document after they're
2224	 * processed.  The final document uses mdoc_meta for these
2225	 * values and discards the originals.
2226	 */
2227
2228	mdoc_node_delete(mdoc, mdoc->last);
2229	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2230		mdoc->flags |= MDOC_PBODY;
2231
2232	return(1);
2233}
2234
2235static int
2236post_bx(POST_ARGS)
2237{
2238	struct mdoc_node	*n;
2239
2240	/*
2241	 * Make `Bx's second argument always start with an uppercase
2242	 * letter.  Groff checks if it's an "accepted" term, but we just
2243	 * uppercase blindly.
2244	 */
2245
2246	n = mdoc->last->child;
2247	if (n && NULL != (n = n->next))
2248		*n->string = (char)toupper
2249			((unsigned char)*n->string);
2250
2251	return(1);
2252}
2253
2254static int
2255post_os(POST_ARGS)
2256{
2257	struct mdoc_node *n;
2258	char		  buf[BUFSIZ];
2259	int		  c;
2260#ifndef OSNAME
2261	struct utsname	  utsname;
2262#endif
2263
2264	n = mdoc->last;
2265
2266	/*
2267	 * Set the operating system by way of the `Os' macro.
2268	 * The order of precedence is:
2269	 * 1. the argument of the `Os' macro, unless empty
2270	 * 2. the -Ios=foo command line argument, if provided
2271	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2272	 * 4. "sysname release" from uname(3)
2273 	 */
2274
2275	free(mdoc->meta.os);
2276
2277	buf[0] = '\0';
2278	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2279		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2280		return(0);
2281	}
2282
2283	assert(c);
2284
2285	if ('\0' == buf[0]) {
2286		if (mdoc->defos) {
2287			mdoc->meta.os = mandoc_strdup(mdoc->defos);
2288			return(1);
2289		}
2290#ifdef OSNAME
2291		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2292			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2293			return(0);
2294		}
2295#else /*!OSNAME */
2296		if (-1 == uname(&utsname)) {
2297			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2298                        mdoc->meta.os = mandoc_strdup("UNKNOWN");
2299                        return(post_prol(mdoc));
2300                }
2301
2302		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2303			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2304			return(0);
2305		}
2306		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2307			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2308			return(0);
2309		}
2310		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2311			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2312			return(0);
2313		}
2314#endif /*!OSNAME*/
2315	}
2316
2317	mdoc->meta.os = mandoc_strdup(buf);
2318	return(1);
2319}
2320
2321static int
2322post_std(POST_ARGS)
2323{
2324	struct mdoc_node *nn, *n;
2325
2326	n = mdoc->last;
2327
2328	/*
2329	 * Macros accepting `-std' as an argument have the name of the
2330	 * current document (`Nm') filled in as the argument if it's not
2331	 * provided.
2332	 */
2333
2334	if (n->child)
2335		return(1);
2336
2337	if (NULL == mdoc->meta.name)
2338		return(1);
2339
2340	nn = n;
2341	mdoc->next = MDOC_NEXT_CHILD;
2342
2343	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2344		return(0);
2345
2346	mdoc->last = nn;
2347	return(1);
2348}
2349
2350/*
2351 * Concatenate a node, stopping at the first non-text.
2352 * Concatenation is separated by a single whitespace.
2353 * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2354 * encountered, 1 otherwise.
2355 */
2356static int
2357concat(char *p, const struct mdoc_node *n, size_t sz)
2358{
2359
2360	for ( ; NULL != n; n = n->next) {
2361		if (MDOC_TEXT != n->type)
2362			return(0);
2363		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2364			return(-1);
2365		if (strlcat(p, n->string, sz) >= sz)
2366			return(-1);
2367		concat(p, n->child, sz);
2368	}
2369
2370	return(1);
2371}
2372
2373static enum mdoc_sec
2374a2sec(const char *p)
2375{
2376	int		 i;
2377
2378	for (i = 0; i < (int)SEC__MAX; i++)
2379		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2380			return((enum mdoc_sec)i);
2381
2382	return(SEC_CUSTOM);
2383}
2384
2385static size_t
2386macro2len(enum mdoct macro)
2387{
2388
2389	switch (macro) {
2390	case(MDOC_Ad):
2391		return(12);
2392	case(MDOC_Ao):
2393		return(12);
2394	case(MDOC_An):
2395		return(12);
2396	case(MDOC_Aq):
2397		return(12);
2398	case(MDOC_Ar):
2399		return(12);
2400	case(MDOC_Bo):
2401		return(12);
2402	case(MDOC_Bq):
2403		return(12);
2404	case(MDOC_Cd):
2405		return(12);
2406	case(MDOC_Cm):
2407		return(10);
2408	case(MDOC_Do):
2409		return(10);
2410	case(MDOC_Dq):
2411		return(12);
2412	case(MDOC_Dv):
2413		return(12);
2414	case(MDOC_Eo):
2415		return(12);
2416	case(MDOC_Em):
2417		return(10);
2418	case(MDOC_Er):
2419		return(17);
2420	case(MDOC_Ev):
2421		return(15);
2422	case(MDOC_Fa):
2423		return(12);
2424	case(MDOC_Fl):
2425		return(10);
2426	case(MDOC_Fo):
2427		return(16);
2428	case(MDOC_Fn):
2429		return(16);
2430	case(MDOC_Ic):
2431		return(10);
2432	case(MDOC_Li):
2433		return(16);
2434	case(MDOC_Ms):
2435		return(6);
2436	case(MDOC_Nm):
2437		return(10);
2438	case(MDOC_No):
2439		return(12);
2440	case(MDOC_Oo):
2441		return(10);
2442	case(MDOC_Op):
2443		return(14);
2444	case(MDOC_Pa):
2445		return(32);
2446	case(MDOC_Pf):
2447		return(12);
2448	case(MDOC_Po):
2449		return(12);
2450	case(MDOC_Pq):
2451		return(12);
2452	case(MDOC_Ql):
2453		return(16);
2454	case(MDOC_Qo):
2455		return(12);
2456	case(MDOC_So):
2457		return(12);
2458	case(MDOC_Sq):
2459		return(12);
2460	case(MDOC_Sy):
2461		return(6);
2462	case(MDOC_Sx):
2463		return(16);
2464	case(MDOC_Tn):
2465		return(10);
2466	case(MDOC_Va):
2467		return(12);
2468	case(MDOC_Vt):
2469		return(12);
2470	case(MDOC_Xr):
2471		return(10);
2472	default:
2473		break;
2474	};
2475	return(0);
2476}
2477