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