mdoc_validate.c revision 1.94
1/*	$Id: mdoc_validate.c,v 1.94 2011/09/18 10:25:28 schwarze 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#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 *);
71
72static	int	 concat(struct mdoc *, char *,
73			const struct mdoc_node *, size_t);
74static	enum mdoc_sec	a2sec(const char *);
75static	size_t		macro2len(enum mdoct);
76
77static	int	 ebool(POST_ARGS);
78static	int	 berr_ge1(POST_ARGS);
79static	int	 bwarn_ge1(POST_ARGS);
80static	int	 ewarn_eq0(POST_ARGS);
81static	int	 ewarn_eq1(POST_ARGS);
82static	int	 ewarn_ge1(POST_ARGS);
83static	int	 ewarn_le1(POST_ARGS);
84static	int	 hwarn_eq0(POST_ARGS);
85static	int	 hwarn_eq1(POST_ARGS);
86static	int	 hwarn_ge1(POST_ARGS);
87static	int	 hwarn_le1(POST_ARGS);
88
89static	int	 post_an(POST_ARGS);
90static	int	 post_at(POST_ARGS);
91static	int	 post_bf(POST_ARGS);
92static	int	 post_bl(POST_ARGS);
93static	int	 post_bl_block(POST_ARGS);
94static	int	 post_bl_block_width(POST_ARGS);
95static	int	 post_bl_block_tag(POST_ARGS);
96static	int	 post_bl_head(POST_ARGS);
97static	int	 post_bx(POST_ARGS);
98static	int	 post_dd(POST_ARGS);
99static	int	 post_dt(POST_ARGS);
100static	int	 post_defaults(POST_ARGS);
101static	int	 post_literal(POST_ARGS);
102static	int	 post_eoln(POST_ARGS);
103static	int	 post_it(POST_ARGS);
104static	int	 post_lb(POST_ARGS);
105static	int	 post_nm(POST_ARGS);
106static	int	 post_ns(POST_ARGS);
107static	int	 post_os(POST_ARGS);
108static	int	 post_ignpar(POST_ARGS);
109static	int	 post_prol(POST_ARGS);
110static	int	 post_root(POST_ARGS);
111static	int	 post_rs(POST_ARGS);
112static	int	 post_sh(POST_ARGS);
113static	int	 post_sh_body(POST_ARGS);
114static	int	 post_sh_head(POST_ARGS);
115static	int	 post_st(POST_ARGS);
116static	int	 post_std(POST_ARGS);
117static	int	 post_vt(POST_ARGS);
118static	int	 pre_an(PRE_ARGS);
119static	int	 pre_bd(PRE_ARGS);
120static	int	 pre_bl(PRE_ARGS);
121static	int	 pre_dd(PRE_ARGS);
122static	int	 pre_display(PRE_ARGS);
123static	int	 pre_dt(PRE_ARGS);
124static	int	 pre_it(PRE_ARGS);
125static	int	 pre_literal(PRE_ARGS);
126static	int	 pre_os(PRE_ARGS);
127static	int	 pre_par(PRE_ARGS);
128static	int	 pre_sh(PRE_ARGS);
129static	int	 pre_ss(PRE_ARGS);
130static	int	 pre_std(PRE_ARGS);
131
132static	v_post	 posts_an[] = { post_an, NULL };
133static	v_post	 posts_at[] = { post_at, post_defaults, NULL };
134static	v_post	 posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
135static	v_post	 posts_bf[] = { hwarn_le1, post_bf, NULL };
136static	v_post	 posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
137static	v_post	 posts_bl[] = { bwarn_ge1, post_bl, NULL };
138static	v_post	 posts_bx[] = { post_bx, NULL };
139static	v_post	 posts_bool[] = { ebool, NULL };
140static	v_post	 posts_eoln[] = { post_eoln, NULL };
141static	v_post	 posts_defaults[] = { post_defaults, NULL };
142static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
143static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
144static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
145static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
146static	v_post	 posts_it[] = { post_it, NULL };
147static	v_post	 posts_lb[] = { post_lb, NULL };
148static	v_post	 posts_nd[] = { berr_ge1, NULL };
149static	v_post	 posts_nm[] = { post_nm, NULL };
150static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
151static	v_post	 posts_ns[] = { post_ns, NULL };
152static	v_post	 posts_os[] = { post_os, post_prol, 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[] = { 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_notext },		/* 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	{ NULL, posts_notext },			/* 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_notext },			/* br */
299	{ pres_pp, 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__P,
316	MDOC__Q,
317	MDOC__D,
318	MDOC__O,
319	MDOC__C,
320	MDOC__U
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		*cpp, *pp;
545	size_t		 sz;
546
547	while ('\0' != *p) {
548		sz = strcspn(p, "\t\\");
549
550		p += (int)sz;
551		pos += (int)sz;
552
553		if ('\t' == *p) {
554			if ( ! (MDOC_LITERAL & m->flags))
555				mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB);
556			p++;
557			pos++;
558			continue;
559		} else if ('\0' == *p)
560			break;
561
562		pos++;
563		pp = ++p;
564
565		if (ESCAPE_ERROR == mandoc_escape
566				((const char **)&pp, NULL, NULL)) {
567			mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
568			break;
569		}
570
571		cpp = p;
572		while (NULL != (cpp = memchr(cpp, ASCII_HYPH, pp - cpp)))
573			*cpp = '-';
574
575		pos += pp - p;
576		p = pp;
577	}
578}
579
580static int
581check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
582{
583
584	assert(n->parent);
585	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
586			(t == n->parent->type))
587		return(1);
588
589	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
590			n->pos, "want parent %s", MDOC_ROOT == t ?
591			"<root>" : mdoc_macronames[tok]);
592	return(0);
593}
594
595
596static int
597pre_display(PRE_ARGS)
598{
599	struct mdoc_node *node;
600
601	if (MDOC_BLOCK != n->type)
602		return(1);
603
604	for (node = mdoc->last->parent; node; node = node->parent)
605		if (MDOC_BLOCK == node->type)
606			if (MDOC_Bd == node->tok)
607				break;
608
609	if (node)
610		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
611
612	return(1);
613}
614
615
616static int
617pre_bl(PRE_ARGS)
618{
619	int		  i, comp, dup;
620	const char	 *offs, *width;
621	enum mdoc_list	  lt;
622	struct mdoc_node *np;
623
624	if (MDOC_BLOCK != n->type) {
625		if (ENDBODY_NOT != n->end) {
626			assert(n->pending);
627			np = n->pending->parent;
628		} else
629			np = n->parent;
630
631		assert(np);
632		assert(MDOC_BLOCK == np->type);
633		assert(MDOC_Bl == np->tok);
634		return(1);
635	}
636
637	/*
638	 * First figure out which kind of list to use: bind ourselves to
639	 * the first mentioned list type and warn about any remaining
640	 * ones.  If we find no list type, we default to LIST_item.
641	 */
642
643	/* LINTED */
644	for (i = 0; n->args && i < (int)n->args->argc; i++) {
645		lt = LIST__NONE;
646		dup = comp = 0;
647		width = offs = NULL;
648		switch (n->args->argv[i].arg) {
649		/* Set list types. */
650		case (MDOC_Bullet):
651			lt = LIST_bullet;
652			break;
653		case (MDOC_Dash):
654			lt = LIST_dash;
655			break;
656		case (MDOC_Enum):
657			lt = LIST_enum;
658			break;
659		case (MDOC_Hyphen):
660			lt = LIST_hyphen;
661			break;
662		case (MDOC_Item):
663			lt = LIST_item;
664			break;
665		case (MDOC_Tag):
666			lt = LIST_tag;
667			break;
668		case (MDOC_Diag):
669			lt = LIST_diag;
670			break;
671		case (MDOC_Hang):
672			lt = LIST_hang;
673			break;
674		case (MDOC_Ohang):
675			lt = LIST_ohang;
676			break;
677		case (MDOC_Inset):
678			lt = LIST_inset;
679			break;
680		case (MDOC_Column):
681			lt = LIST_column;
682			break;
683		/* Set list arguments. */
684		case (MDOC_Compact):
685			dup = n->norm->Bl.comp;
686			comp = 1;
687			break;
688		case (MDOC_Width):
689			dup = (NULL != n->norm->Bl.width);
690			width = n->args->argv[i].value[0];
691			break;
692		case (MDOC_Offset):
693			/* NB: this can be empty! */
694			if (n->args->argv[i].sz) {
695				offs = n->args->argv[i].value[0];
696				dup = (NULL != n->norm->Bl.offs);
697				break;
698			}
699			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
700			break;
701		default:
702			continue;
703		}
704
705		/* Check: duplicate auxiliary arguments. */
706
707		if (dup)
708			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
709
710		if (comp && ! dup)
711			n->norm->Bl.comp = comp;
712		if (offs && ! dup)
713			n->norm->Bl.offs = offs;
714		if (width && ! dup)
715			n->norm->Bl.width = width;
716
717		/* Check: multiple list types. */
718
719		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
720			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
721
722		/* Assign list type. */
723
724		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
725			n->norm->Bl.type = lt;
726			/* Set column information, too. */
727			if (LIST_column == lt) {
728				n->norm->Bl.ncols =
729					n->args->argv[i].sz;
730				n->norm->Bl.cols = (const char **)
731					n->args->argv[i].value;
732			}
733		}
734
735		/* The list type should come first. */
736
737		if (n->norm->Bl.type == LIST__NONE)
738			if (n->norm->Bl.width ||
739					n->norm->Bl.offs ||
740					n->norm->Bl.comp)
741				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
742
743		continue;
744	}
745
746	/* Allow lists to default to LIST_item. */
747
748	if (LIST__NONE == n->norm->Bl.type) {
749		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
750		n->norm->Bl.type = LIST_item;
751	}
752
753	/*
754	 * Validate the width field.  Some list types don't need width
755	 * types and should be warned about them.  Others should have it
756	 * and must also be warned.
757	 */
758
759	switch (n->norm->Bl.type) {
760	case (LIST_tag):
761		if (n->norm->Bl.width)
762			break;
763		mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
764		break;
765	case (LIST_column):
766		/* FALLTHROUGH */
767	case (LIST_diag):
768		/* FALLTHROUGH */
769	case (LIST_ohang):
770		/* FALLTHROUGH */
771	case (LIST_inset):
772		/* FALLTHROUGH */
773	case (LIST_item):
774		if (n->norm->Bl.width)
775			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
776		break;
777	default:
778		break;
779	}
780
781	return(1);
782}
783
784
785static int
786pre_bd(PRE_ARGS)
787{
788	int		  i, dup, comp;
789	enum mdoc_disp 	  dt;
790	const char	 *offs;
791	struct mdoc_node *np;
792
793	if (MDOC_BLOCK != n->type) {
794		if (ENDBODY_NOT != n->end) {
795			assert(n->pending);
796			np = n->pending->parent;
797		} else
798			np = n->parent;
799
800		assert(np);
801		assert(MDOC_BLOCK == np->type);
802		assert(MDOC_Bd == np->tok);
803		return(1);
804	}
805
806	/* LINTED */
807	for (i = 0; n->args && i < (int)n->args->argc; i++) {
808		dt = DISP__NONE;
809		dup = comp = 0;
810		offs = NULL;
811
812		switch (n->args->argv[i].arg) {
813		case (MDOC_Centred):
814			dt = DISP_centred;
815			break;
816		case (MDOC_Ragged):
817			dt = DISP_ragged;
818			break;
819		case (MDOC_Unfilled):
820			dt = DISP_unfilled;
821			break;
822		case (MDOC_Filled):
823			dt = DISP_filled;
824			break;
825		case (MDOC_Literal):
826			dt = DISP_literal;
827			break;
828		case (MDOC_File):
829			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
830			return(0);
831		case (MDOC_Offset):
832			/* NB: this can be empty! */
833			if (n->args->argv[i].sz) {
834				offs = n->args->argv[i].value[0];
835				dup = (NULL != n->norm->Bd.offs);
836				break;
837			}
838			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
839			break;
840		case (MDOC_Compact):
841			comp = 1;
842			dup = n->norm->Bd.comp;
843			break;
844		default:
845			abort();
846			/* NOTREACHED */
847		}
848
849		/* Check whether we have duplicates. */
850
851		if (dup)
852			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
853
854		/* Make our auxiliary assignments. */
855
856		if (offs && ! dup)
857			n->norm->Bd.offs = offs;
858		if (comp && ! dup)
859			n->norm->Bd.comp = comp;
860
861		/* Check whether a type has already been assigned. */
862
863		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
864			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
865
866		/* Make our type assignment. */
867
868		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
869			n->norm->Bd.type = dt;
870	}
871
872	if (DISP__NONE == n->norm->Bd.type) {
873		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
874		n->norm->Bd.type = DISP_ragged;
875	}
876
877	return(1);
878}
879
880
881static int
882pre_ss(PRE_ARGS)
883{
884
885	if (MDOC_BLOCK != n->type)
886		return(1);
887	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
888}
889
890
891static int
892pre_sh(PRE_ARGS)
893{
894
895	if (MDOC_BLOCK != n->type)
896		return(1);
897
898	roff_regunset(mdoc->roff, REG_nS);
899	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
900}
901
902
903static int
904pre_it(PRE_ARGS)
905{
906
907	if (MDOC_BLOCK != n->type)
908		return(1);
909
910	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
911}
912
913
914static int
915pre_an(PRE_ARGS)
916{
917	int		 i;
918
919	if (NULL == n->args)
920		return(1);
921
922	for (i = 1; i < (int)n->args->argc; i++)
923		mdoc_pmsg(mdoc, n->args->argv[i].line,
924			n->args->argv[i].pos, MANDOCERR_IGNARGV);
925
926	if (MDOC_Split == n->args->argv[0].arg)
927		n->norm->An.auth = AUTH_split;
928	else if (MDOC_Nosplit == n->args->argv[0].arg)
929		n->norm->An.auth = AUTH_nosplit;
930	else
931		abort();
932
933	return(1);
934}
935
936static int
937pre_std(PRE_ARGS)
938{
939
940	if (n->args && 1 == n->args->argc)
941		if (MDOC_Std == n->args->argv[0].arg)
942			return(1);
943
944	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
945	return(1);
946}
947
948static int
949pre_dt(PRE_ARGS)
950{
951
952	if (NULL == mdoc->meta.date || mdoc->meta.os)
953		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
954
955	if (mdoc->meta.title)
956		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
957
958	return(1);
959}
960
961static int
962pre_os(PRE_ARGS)
963{
964
965	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
966		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
967
968	if (mdoc->meta.os)
969		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
970
971	return(1);
972}
973
974static int
975pre_dd(PRE_ARGS)
976{
977
978	if (mdoc->meta.title || mdoc->meta.os)
979		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
980
981	if (mdoc->meta.date)
982		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
983
984	return(1);
985}
986
987
988static int
989post_bf(POST_ARGS)
990{
991	struct mdoc_node *np;
992	enum mdocargt	  arg;
993
994	/*
995	 * Unlike other data pointers, these are "housed" by the HEAD
996	 * element, which contains the goods.
997	 */
998
999	if (MDOC_HEAD != mdoc->last->type) {
1000		if (ENDBODY_NOT != mdoc->last->end) {
1001			assert(mdoc->last->pending);
1002			np = mdoc->last->pending->parent->head;
1003		} else if (MDOC_BLOCK != mdoc->last->type) {
1004			np = mdoc->last->parent->head;
1005		} else
1006			np = mdoc->last->head;
1007
1008		assert(np);
1009		assert(MDOC_HEAD == np->type);
1010		assert(MDOC_Bf == np->tok);
1011		return(1);
1012	}
1013
1014	np = mdoc->last;
1015	assert(MDOC_BLOCK == np->parent->type);
1016	assert(MDOC_Bf == np->parent->tok);
1017
1018	/*
1019	 * Cannot have both argument and parameter.
1020	 * If neither is specified, let it through with a warning.
1021	 */
1022
1023	if (np->parent->args && np->child) {
1024		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1025		return(0);
1026	} else if (NULL == np->parent->args && NULL == np->child) {
1027		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1028		return(1);
1029	}
1030
1031	/* Extract argument into data. */
1032
1033	if (np->parent->args) {
1034		arg = np->parent->args->argv[0].arg;
1035		if (MDOC_Emphasis == arg)
1036			np->norm->Bf.font = FONT_Em;
1037		else if (MDOC_Literal == arg)
1038			np->norm->Bf.font = FONT_Li;
1039		else if (MDOC_Symbolic == arg)
1040			np->norm->Bf.font = FONT_Sy;
1041		else
1042			abort();
1043		return(1);
1044	}
1045
1046	/* Extract parameter into data. */
1047
1048	if (0 == strcmp(np->child->string, "Em"))
1049		np->norm->Bf.font = FONT_Em;
1050	else if (0 == strcmp(np->child->string, "Li"))
1051		np->norm->Bf.font = FONT_Li;
1052	else if (0 == strcmp(np->child->string, "Sy"))
1053		np->norm->Bf.font = FONT_Sy;
1054	else
1055		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1056
1057	return(1);
1058}
1059
1060static int
1061post_lb(POST_ARGS)
1062{
1063	const char	*p;
1064	char		*buf;
1065	size_t		 sz;
1066
1067	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1068
1069	assert(mdoc->last->child);
1070	assert(MDOC_TEXT == mdoc->last->child->type);
1071
1072	p = mdoc_a2lib(mdoc->last->child->string);
1073
1074	/* If lookup ok, replace with table value. */
1075
1076	if (p) {
1077		free(mdoc->last->child->string);
1078		mdoc->last->child->string = mandoc_strdup(p);
1079		return(1);
1080	}
1081
1082	/* If not, use "library ``xxxx''. */
1083
1084	sz = strlen(mdoc->last->child->string) +
1085		2 + strlen("\\(lqlibrary\\(rq");
1086	buf = mandoc_malloc(sz);
1087	snprintf(buf, sz, "library \\(lq%s\\(rq",
1088			mdoc->last->child->string);
1089	free(mdoc->last->child->string);
1090	mdoc->last->child->string = buf;
1091	return(1);
1092}
1093
1094static int
1095post_eoln(POST_ARGS)
1096{
1097
1098	if (mdoc->last->child)
1099		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1100	return(1);
1101}
1102
1103
1104static int
1105post_vt(POST_ARGS)
1106{
1107	const struct mdoc_node *n;
1108
1109	/*
1110	 * The Vt macro comes in both ELEM and BLOCK form, both of which
1111	 * have different syntaxes (yet more context-sensitive
1112	 * behaviour).  ELEM types must have a child, which is already
1113	 * guaranteed by the in_line parsing routine; BLOCK types,
1114	 * specifically the BODY, should only have TEXT children.
1115	 */
1116
1117	if (MDOC_BODY != mdoc->last->type)
1118		return(1);
1119
1120	for (n = mdoc->last->child; n; n = n->next)
1121		if (MDOC_TEXT != n->type)
1122			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1123
1124	return(1);
1125}
1126
1127
1128static int
1129post_nm(POST_ARGS)
1130{
1131	char		 buf[BUFSIZ];
1132
1133	/* If no child specified, make sure we have the meta name. */
1134
1135	if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1136		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1137		return(1);
1138	} else if (mdoc->meta.name)
1139		return(1);
1140
1141	/* If no meta name, set it from the child. */
1142
1143	if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1144		return(0);
1145
1146	mdoc->meta.name = mandoc_strdup(buf);
1147
1148	return(1);
1149}
1150
1151static int
1152post_literal(POST_ARGS)
1153{
1154
1155	/*
1156	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1157	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1158	 * this in literal mode, but it doesn't hurt to just switch it
1159	 * off in general since displays can't be nested.
1160	 */
1161
1162	if (MDOC_BODY == mdoc->last->type)
1163		mdoc->flags &= ~MDOC_LITERAL;
1164
1165	return(1);
1166}
1167
1168static int
1169post_defaults(POST_ARGS)
1170{
1171	struct mdoc_node *nn;
1172
1173	/*
1174	 * The `Ar' defaults to "file ..." if no value is provided as an
1175	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1176	 * gets an empty string.
1177	 */
1178
1179	if (mdoc->last->child)
1180		return(1);
1181
1182	nn = mdoc->last;
1183	mdoc->next = MDOC_NEXT_CHILD;
1184
1185	switch (nn->tok) {
1186	case (MDOC_Ar):
1187		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1188			return(0);
1189		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1190			return(0);
1191		break;
1192	case (MDOC_At):
1193		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1194			return(0);
1195		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1196			return(0);
1197		break;
1198	case (MDOC_Li):
1199		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1200			return(0);
1201		break;
1202	case (MDOC_Pa):
1203		/* FALLTHROUGH */
1204	case (MDOC_Mt):
1205		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1206			return(0);
1207		break;
1208	default:
1209		abort();
1210		/* NOTREACHED */
1211	}
1212
1213	mdoc->last = nn;
1214	return(1);
1215}
1216
1217static int
1218post_at(POST_ARGS)
1219{
1220	const char	 *p, *q;
1221	char		 *buf;
1222	size_t		  sz;
1223
1224	/*
1225	 * If we have a child, look it up in the standard keys.  If a
1226	 * key exist, use that instead of the child; if it doesn't,
1227	 * prefix "AT&T UNIX " to the existing data.
1228	 */
1229
1230	if (NULL == mdoc->last->child)
1231		return(1);
1232
1233	assert(MDOC_TEXT == mdoc->last->child->type);
1234	p = mdoc_a2att(mdoc->last->child->string);
1235
1236	if (p) {
1237		free(mdoc->last->child->string);
1238		mdoc->last->child->string = mandoc_strdup(p);
1239	} else {
1240		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1241		p = "AT&T UNIX ";
1242		q = mdoc->last->child->string;
1243		sz = strlen(p) + strlen(q) + 1;
1244		buf = mandoc_malloc(sz);
1245		strlcpy(buf, p, sz);
1246		strlcat(buf, q, sz);
1247		free(mdoc->last->child->string);
1248		mdoc->last->child->string = buf;
1249	}
1250
1251	return(1);
1252}
1253
1254static int
1255post_an(POST_ARGS)
1256{
1257	struct mdoc_node *np;
1258
1259	np = mdoc->last;
1260	if (AUTH__NONE == np->norm->An.auth) {
1261		if (0 == np->child)
1262			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1263	} else if (np->child)
1264		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1265
1266	return(1);
1267}
1268
1269
1270static int
1271post_it(POST_ARGS)
1272{
1273	int		  i, cols;
1274	enum mdoc_list	  lt;
1275	struct mdoc_node *n, *c;
1276	enum mandocerr	  er;
1277
1278	if (MDOC_BLOCK != mdoc->last->type)
1279		return(1);
1280
1281	n = mdoc->last->parent->parent;
1282	lt = n->norm->Bl.type;
1283
1284	if (LIST__NONE == lt) {
1285		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1286		return(1);
1287	}
1288
1289	switch (lt) {
1290	case (LIST_tag):
1291		if (mdoc->last->head->child)
1292			break;
1293		/* FIXME: give this a dummy value. */
1294		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1295		break;
1296	case (LIST_hang):
1297		/* FALLTHROUGH */
1298	case (LIST_ohang):
1299		/* FALLTHROUGH */
1300	case (LIST_inset):
1301		/* FALLTHROUGH */
1302	case (LIST_diag):
1303		if (NULL == mdoc->last->head->child)
1304			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1305		break;
1306	case (LIST_bullet):
1307		/* FALLTHROUGH */
1308	case (LIST_dash):
1309		/* FALLTHROUGH */
1310	case (LIST_enum):
1311		/* FALLTHROUGH */
1312	case (LIST_hyphen):
1313		if (NULL == mdoc->last->body->child)
1314			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1315		/* FALLTHROUGH */
1316	case (LIST_item):
1317		if (mdoc->last->head->child)
1318			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1319		break;
1320	case (LIST_column):
1321		cols = (int)n->norm->Bl.ncols;
1322
1323		assert(NULL == mdoc->last->head->child);
1324
1325		if (NULL == mdoc->last->body->child)
1326			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1327
1328		for (i = 0, c = mdoc->last->child; c; c = c->next)
1329			if (MDOC_BODY == c->type)
1330				i++;
1331
1332		if (i < cols)
1333			er = MANDOCERR_ARGCOUNT;
1334		else if (i == cols || i == cols + 1)
1335			break;
1336		else
1337			er = MANDOCERR_SYNTARGCOUNT;
1338
1339		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1340				mdoc->last->pos,
1341				"columns == %d (have %d)", cols, i);
1342		return(MANDOCERR_ARGCOUNT == er);
1343	default:
1344		break;
1345	}
1346
1347	return(1);
1348}
1349
1350static int
1351post_bl_block(POST_ARGS)
1352{
1353	struct mdoc_node *n;
1354
1355	/*
1356	 * These are fairly complicated, so we've broken them into two
1357	 * functions.  post_bl_block_tag() is called when a -tag is
1358	 * specified, but no -width (it must be guessed).  The second
1359	 * when a -width is specified (macro indicators must be
1360	 * rewritten into real lengths).
1361	 */
1362
1363	n = mdoc->last;
1364
1365	if (LIST_tag == n->norm->Bl.type &&
1366			NULL == n->norm->Bl.width) {
1367		if ( ! post_bl_block_tag(mdoc))
1368			return(0);
1369	} else if (NULL != n->norm->Bl.width) {
1370		if ( ! post_bl_block_width(mdoc))
1371			return(0);
1372	} else
1373		return(1);
1374
1375	assert(n->norm->Bl.width);
1376	return(1);
1377}
1378
1379static int
1380post_bl_block_width(POST_ARGS)
1381{
1382	size_t		  width;
1383	int		  i;
1384	enum mdoct	  tok;
1385	struct mdoc_node *n;
1386	char		  buf[NUMSIZ];
1387
1388	n = mdoc->last;
1389
1390	/*
1391	 * Calculate the real width of a list from the -width string,
1392	 * which may contain a macro (with a known default width), a
1393	 * literal string, or a scaling width.
1394	 *
1395	 * If the value to -width is a macro, then we re-write it to be
1396	 * the macro's width as set in share/tmac/mdoc/doc-common.
1397	 */
1398
1399	if (0 == strcmp(n->norm->Bl.width, "Ds"))
1400		width = 6;
1401	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1402		return(1);
1403	else if (0 == (width = macro2len(tok)))  {
1404		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1405		return(1);
1406	}
1407
1408	/* The value already exists: free and reallocate it. */
1409
1410	assert(n->args);
1411
1412	for (i = 0; i < (int)n->args->argc; i++)
1413		if (MDOC_Width == n->args->argv[i].arg)
1414			break;
1415
1416	assert(i < (int)n->args->argc);
1417
1418	snprintf(buf, NUMSIZ, "%zun", width);
1419	free(n->args->argv[i].value[0]);
1420	n->args->argv[i].value[0] = mandoc_strdup(buf);
1421
1422	/* Set our width! */
1423	n->norm->Bl.width = n->args->argv[i].value[0];
1424	return(1);
1425}
1426
1427static int
1428post_bl_block_tag(POST_ARGS)
1429{
1430	struct mdoc_node *n, *nn;
1431	size_t		  sz, ssz;
1432	int		  i;
1433	char		  buf[NUMSIZ];
1434
1435	/*
1436	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1437	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1438	 * ONLY if the -width argument has NOT been provided.  See
1439	 * post_bl_block_width() for converting the -width string.
1440	 */
1441
1442	sz = 10;
1443	n = mdoc->last;
1444
1445	for (nn = n->body->child; nn; nn = nn->next) {
1446		if (MDOC_It != nn->tok)
1447			continue;
1448
1449		assert(MDOC_BLOCK == nn->type);
1450		nn = nn->head->child;
1451
1452		if (nn == NULL)
1453			break;
1454
1455		if (MDOC_TEXT == nn->type) {
1456			sz = strlen(nn->string) + 1;
1457			break;
1458		}
1459
1460		if (0 != (ssz = macro2len(nn->tok)))
1461			sz = ssz;
1462
1463		break;
1464	}
1465
1466	/* Defaults to ten ens. */
1467
1468	snprintf(buf, NUMSIZ, "%zun", sz);
1469
1470	/*
1471	 * We have to dynamically add this to the macro's argument list.
1472	 * We're guaranteed that a MDOC_Width doesn't already exist.
1473	 */
1474
1475	assert(n->args);
1476	i = (int)(n->args->argc)++;
1477
1478	n->args->argv = mandoc_realloc(n->args->argv,
1479			n->args->argc * sizeof(struct mdoc_argv));
1480
1481	n->args->argv[i].arg = MDOC_Width;
1482	n->args->argv[i].line = n->line;
1483	n->args->argv[i].pos = n->pos;
1484	n->args->argv[i].sz = 1;
1485	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1486	n->args->argv[i].value[0] = mandoc_strdup(buf);
1487
1488	/* Set our width! */
1489	n->norm->Bl.width = n->args->argv[i].value[0];
1490	return(1);
1491}
1492
1493
1494static int
1495post_bl_head(POST_ARGS)
1496{
1497	struct mdoc_node *np, *nn, *nnp;
1498	int		  i, j;
1499
1500	if (LIST_column != mdoc->last->norm->Bl.type)
1501		/* FIXME: this should be ERROR class... */
1502		return(hwarn_eq0(mdoc));
1503
1504	/*
1505	 * Convert old-style lists, where the column width specifiers
1506	 * trail as macro parameters, to the new-style ("normal-form")
1507	 * lists where they're argument values following -column.
1508	 */
1509
1510	/* First, disallow both types and allow normal-form. */
1511
1512	/*
1513	 * TODO: technically, we can accept both and just merge the two
1514	 * lists, but I'll leave that for another day.
1515	 */
1516
1517	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1518		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1519		return(0);
1520	} else if (NULL == mdoc->last->child)
1521		return(1);
1522
1523	np = mdoc->last->parent;
1524	assert(np->args);
1525
1526	for (j = 0; j < (int)np->args->argc; j++)
1527		if (MDOC_Column == np->args->argv[j].arg)
1528			break;
1529
1530	assert(j < (int)np->args->argc);
1531	assert(0 == np->args->argv[j].sz);
1532
1533	/*
1534	 * Accommodate for new-style groff column syntax.  Shuffle the
1535	 * child nodes, all of which must be TEXT, as arguments for the
1536	 * column field.  Then, delete the head children.
1537	 */
1538
1539	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1540	np->args->argv[j].value = mandoc_malloc
1541		((size_t)mdoc->last->nchild * sizeof(char *));
1542
1543	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1544	mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value;
1545
1546	for (i = 0, nn = mdoc->last->child; nn; i++) {
1547		np->args->argv[j].value[i] = nn->string;
1548		nn->string = NULL;
1549		nnp = nn;
1550		nn = nn->next;
1551		mdoc_node_delete(NULL, nnp);
1552	}
1553
1554	mdoc->last->nchild = 0;
1555	mdoc->last->child = NULL;
1556
1557	return(1);
1558}
1559
1560static int
1561post_bl(POST_ARGS)
1562{
1563	struct mdoc_node	*n;
1564
1565	if (MDOC_HEAD == mdoc->last->type)
1566		return(post_bl_head(mdoc));
1567	if (MDOC_BLOCK == mdoc->last->type)
1568		return(post_bl_block(mdoc));
1569	if (MDOC_BODY != mdoc->last->type)
1570		return(1);
1571
1572	for (n = mdoc->last->child; n; n = n->next) {
1573		switch (n->tok) {
1574		case (MDOC_Lp):
1575			/* FALLTHROUGH */
1576		case (MDOC_Pp):
1577			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1578			/* FALLTHROUGH */
1579		case (MDOC_It):
1580			/* FALLTHROUGH */
1581		case (MDOC_Sm):
1582			continue;
1583		default:
1584			break;
1585		}
1586
1587		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1588		return(0);
1589	}
1590
1591	return(1);
1592}
1593
1594static int
1595ebool(struct mdoc *mdoc)
1596{
1597
1598	if (NULL == mdoc->last->child) {
1599		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1600		mdoc_node_delete(mdoc, mdoc->last);
1601		return(1);
1602	}
1603	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1604
1605	assert(MDOC_TEXT == mdoc->last->child->type);
1606
1607	if (0 == strcmp(mdoc->last->child->string, "on"))
1608		return(1);
1609	if (0 == strcmp(mdoc->last->child->string, "off"))
1610		return(1);
1611
1612	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1613	return(1);
1614}
1615
1616static int
1617post_root(POST_ARGS)
1618{
1619	int		  erc;
1620	struct mdoc_node *n;
1621
1622	erc = 0;
1623
1624	/* Check that we have a finished prologue. */
1625
1626	if ( ! (MDOC_PBODY & mdoc->flags)) {
1627		erc++;
1628		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1629	}
1630
1631	n = mdoc->first;
1632	assert(n);
1633
1634	/* Check that we begin with a proper `Sh'. */
1635
1636	if (NULL == n->child) {
1637		erc++;
1638		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1639	} else if (MDOC_BLOCK != n->child->type ||
1640			MDOC_Sh != n->child->tok) {
1641		erc++;
1642		/* Can this be lifted?  See rxdebug.1 for example. */
1643		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1644	}
1645
1646	return(erc ? 0 : 1);
1647}
1648
1649static int
1650post_st(POST_ARGS)
1651{
1652	struct mdoc_node	 *ch;
1653	const char		 *p;
1654
1655	if (NULL == (ch = mdoc->last->child)) {
1656		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1657		mdoc_node_delete(mdoc, mdoc->last);
1658		return(1);
1659	}
1660
1661	assert(MDOC_TEXT == ch->type);
1662
1663	if (NULL == (p = mdoc_a2st(ch->string))) {
1664		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1665		mdoc_node_delete(mdoc, mdoc->last);
1666	} else {
1667		free(ch->string);
1668		ch->string = mandoc_strdup(p);
1669	}
1670
1671	return(1);
1672}
1673
1674static int
1675post_rs(POST_ARGS)
1676{
1677	struct mdoc_node *nn, *next, *prev;
1678	int		  i, j;
1679
1680	switch (mdoc->last->type) {
1681	case (MDOC_HEAD):
1682		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1683		return(1);
1684	case (MDOC_BODY):
1685		if (mdoc->last->child)
1686			break;
1687		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1688		return(1);
1689	default:
1690		return(1);
1691	}
1692
1693	/*
1694	 * Make sure only certain types of nodes are allowed within the
1695	 * the `Rs' body.  Delete offending nodes and raise a warning.
1696	 * Do this before re-ordering for the sake of clarity.
1697	 */
1698
1699	next = NULL;
1700	for (nn = mdoc->last->child; nn; nn = next) {
1701		for (i = 0; i < RSORD_MAX; i++)
1702			if (nn->tok == rsord[i])
1703				break;
1704
1705		if (i < RSORD_MAX) {
1706			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1707				mdoc->last->norm->Rs.quote_T++;
1708			next = nn->next;
1709			continue;
1710		}
1711
1712		next = nn->next;
1713		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1714		mdoc_node_delete(mdoc, nn);
1715	}
1716
1717	/*
1718	 * The full `Rs' block needs special handling to order the
1719	 * sub-elements according to `rsord'.  Pick through each element
1720	 * and correctly order it.  This is a insertion sort.
1721	 */
1722
1723	next = NULL;
1724	for (nn = mdoc->last->child->next; nn; nn = next) {
1725		/* Determine order of `nn'. */
1726		for (i = 0; i < RSORD_MAX; i++)
1727			if (rsord[i] == nn->tok)
1728				break;
1729
1730		/*
1731		 * Remove `nn' from the chain.  This somewhat
1732		 * repeats mdoc_node_unlink(), but since we're
1733		 * just re-ordering, there's no need for the
1734		 * full unlink process.
1735		 */
1736
1737		if (NULL != (next = nn->next))
1738			next->prev = nn->prev;
1739
1740		if (NULL != (prev = nn->prev))
1741			prev->next = nn->next;
1742
1743		nn->prev = nn->next = NULL;
1744
1745		/*
1746		 * Scan back until we reach a node that's
1747		 * ordered before `nn'.
1748		 */
1749
1750		for ( ; prev ; prev = prev->prev) {
1751			/* Determine order of `prev'. */
1752			for (j = 0; j < RSORD_MAX; j++)
1753				if (rsord[j] == prev->tok)
1754					break;
1755
1756			if (j <= i)
1757				break;
1758		}
1759
1760		/*
1761		 * Set `nn' back into its correct place in front
1762		 * of the `prev' node.
1763		 */
1764
1765		nn->prev = prev;
1766
1767		if (prev) {
1768			if (prev->next)
1769				prev->next->prev = nn;
1770			nn->next = prev->next;
1771			prev->next = nn;
1772		} else {
1773			mdoc->last->child->prev = nn;
1774			nn->next = mdoc->last->child;
1775			mdoc->last->child = nn;
1776		}
1777	}
1778
1779	return(1);
1780}
1781
1782static int
1783post_ns(POST_ARGS)
1784{
1785
1786	if (MDOC_LINE & mdoc->last->flags)
1787		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1788	return(1);
1789}
1790
1791static int
1792post_sh(POST_ARGS)
1793{
1794
1795	if (MDOC_HEAD == mdoc->last->type)
1796		return(post_sh_head(mdoc));
1797	if (MDOC_BODY == mdoc->last->type)
1798		return(post_sh_body(mdoc));
1799
1800	return(1);
1801}
1802
1803static int
1804post_sh_body(POST_ARGS)
1805{
1806	struct mdoc_node *n;
1807
1808	if (SEC_NAME != mdoc->lastsec)
1809		return(1);
1810
1811	/*
1812	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1813	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1814	 * children of the BODY declaration can also be "text".
1815	 */
1816
1817	if (NULL == (n = mdoc->last->child)) {
1818		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1819		return(1);
1820	}
1821
1822	for ( ; n && n->next; n = n->next) {
1823		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1824			continue;
1825		if (MDOC_TEXT == n->type)
1826			continue;
1827		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1828	}
1829
1830	assert(n);
1831	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1832		return(1);
1833
1834	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1835	return(1);
1836}
1837
1838static int
1839post_sh_head(POST_ARGS)
1840{
1841	char		 buf[BUFSIZ];
1842	enum mdoc_sec	 sec;
1843
1844	/*
1845	 * Process a new section.  Sections are either "named" or
1846	 * "custom".  Custom sections are user-defined, while named ones
1847	 * follow a conventional order and may only appear in certain
1848	 * manual sections.
1849	 */
1850
1851	if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1852		return(0);
1853
1854	sec = a2sec(buf);
1855
1856	/* The NAME should be first. */
1857
1858	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1859		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1860
1861	/* The SYNOPSIS gets special attention in other areas. */
1862
1863	if (SEC_SYNOPSIS == sec)
1864		mdoc->flags |= MDOC_SYNOPSIS;
1865	else
1866		mdoc->flags &= ~MDOC_SYNOPSIS;
1867
1868	/* Mark our last section. */
1869
1870	mdoc->lastsec = sec;
1871
1872	/* We don't care about custom sections after this. */
1873
1874	if (SEC_CUSTOM == sec)
1875		return(1);
1876
1877	/*
1878	 * Check whether our non-custom section is being repeated or is
1879	 * out of order.
1880	 */
1881
1882	if (sec == mdoc->lastnamed)
1883		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1884
1885	if (sec < mdoc->lastnamed)
1886		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1887
1888	/* Mark the last named section. */
1889
1890	mdoc->lastnamed = sec;
1891
1892	/* Check particular section/manual conventions. */
1893
1894	assert(mdoc->meta.msec);
1895
1896	switch (sec) {
1897	case (SEC_RETURN_VALUES):
1898		/* FALLTHROUGH */
1899	case (SEC_ERRORS):
1900		/* FALLTHROUGH */
1901	case (SEC_LIBRARY):
1902		if (*mdoc->meta.msec == '2')
1903			break;
1904		if (*mdoc->meta.msec == '3')
1905			break;
1906		if (*mdoc->meta.msec == '9')
1907			break;
1908		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1909		break;
1910	default:
1911		break;
1912	}
1913
1914	return(1);
1915}
1916
1917static int
1918post_ignpar(POST_ARGS)
1919{
1920	struct mdoc_node *np;
1921
1922	if (MDOC_BODY != mdoc->last->type)
1923		return(1);
1924
1925	if (NULL != (np = mdoc->last->child))
1926		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1927			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1928			mdoc_node_delete(mdoc, np);
1929		}
1930
1931	if (NULL != (np = mdoc->last->last))
1932		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1933			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1934			mdoc_node_delete(mdoc, np);
1935		}
1936
1937	return(1);
1938}
1939
1940static int
1941pre_par(PRE_ARGS)
1942{
1943
1944	if (NULL == mdoc->last)
1945		return(1);
1946	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1947		return(1);
1948
1949	/*
1950	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1951	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1952	 */
1953
1954	if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1955		return(1);
1956	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1957		return(1);
1958	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1959		return(1);
1960	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1961		return(1);
1962
1963	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1964	mdoc_node_delete(mdoc, mdoc->last);
1965	return(1);
1966}
1967
1968static int
1969pre_literal(PRE_ARGS)
1970{
1971
1972	if (MDOC_BODY != n->type)
1973		return(1);
1974
1975	/*
1976	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1977	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1978	 */
1979
1980	switch (n->tok) {
1981	case (MDOC_Dl):
1982		mdoc->flags |= MDOC_LITERAL;
1983		break;
1984	case (MDOC_Bd):
1985		if (DISP_literal == n->norm->Bd.type)
1986			mdoc->flags |= MDOC_LITERAL;
1987		if (DISP_unfilled == n->norm->Bd.type)
1988			mdoc->flags |= MDOC_LITERAL;
1989		break;
1990	default:
1991		abort();
1992		/* NOTREACHED */
1993	}
1994
1995	return(1);
1996}
1997
1998static int
1999post_dd(POST_ARGS)
2000{
2001	char		  buf[DATESIZE];
2002	struct mdoc_node *n;
2003
2004	if (mdoc->meta.date)
2005		free(mdoc->meta.date);
2006
2007	n = mdoc->last;
2008	if (NULL == n->child || '\0' == n->child->string[0]) {
2009		mdoc->meta.date = mandoc_normdate
2010			(mdoc->parse, NULL, n->line, n->pos);
2011		return(1);
2012	}
2013
2014	if ( ! concat(mdoc, buf, n->child, DATESIZE))
2015		return(0);
2016
2017	mdoc->meta.date = mandoc_normdate
2018		(mdoc->parse, buf, n->line, n->pos);
2019
2020	return(1);
2021}
2022
2023static int
2024post_dt(POST_ARGS)
2025{
2026	struct mdoc_node *nn, *n;
2027	const char	 *cp;
2028	char		 *p;
2029
2030	n = mdoc->last;
2031
2032	if (mdoc->meta.title)
2033		free(mdoc->meta.title);
2034	if (mdoc->meta.vol)
2035		free(mdoc->meta.vol);
2036	if (mdoc->meta.arch)
2037		free(mdoc->meta.arch);
2038
2039	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2040
2041	/* First make all characters uppercase. */
2042
2043	if (NULL != (nn = n->child))
2044		for (p = nn->string; *p; p++) {
2045			if (toupper((u_char)*p) == *p)
2046				continue;
2047
2048			/*
2049			 * FIXME: don't be lazy: have this make all
2050			 * characters be uppercase and just warn once.
2051			 */
2052			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2053			break;
2054		}
2055
2056	/* Handles: `.Dt'
2057	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
2058	 */
2059
2060	if (NULL == (nn = n->child)) {
2061		/* XXX: make these macro values. */
2062		/* FIXME: warn about missing values. */
2063		mdoc->meta.title = mandoc_strdup("UNKNOWN");
2064		mdoc->meta.vol = mandoc_strdup("LOCAL");
2065		mdoc->meta.msec = mandoc_strdup("1");
2066		return(1);
2067	}
2068
2069	/* Handles: `.Dt TITLE'
2070	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2071	 */
2072
2073	mdoc->meta.title = mandoc_strdup
2074		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2075
2076	if (NULL == (nn = nn->next)) {
2077		/* FIXME: warn about missing msec. */
2078		/* XXX: make this a macro value. */
2079		mdoc->meta.vol = mandoc_strdup("LOCAL");
2080		mdoc->meta.msec = mandoc_strdup("1");
2081		return(1);
2082	}
2083
2084	/* Handles: `.Dt TITLE SEC'
2085	 *   --> title = TITLE, volume = SEC is msec ?
2086	 *           format(msec) : SEC,
2087	 *       msec = SEC is msec ? atoi(msec) : 0,
2088	 *       arch = NULL
2089	 */
2090
2091	cp = mdoc_a2msec(nn->string);
2092	if (cp) {
2093		mdoc->meta.vol = mandoc_strdup(cp);
2094		mdoc->meta.msec = mandoc_strdup(nn->string);
2095	} else {
2096		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2097		mdoc->meta.vol = mandoc_strdup(nn->string);
2098		mdoc->meta.msec = mandoc_strdup(nn->string);
2099	}
2100
2101	if (NULL == (nn = nn->next))
2102		return(1);
2103
2104	/* Handles: `.Dt TITLE SEC VOL'
2105	 *   --> title = TITLE, volume = VOL is vol ?
2106	 *       format(VOL) :
2107	 *           VOL is arch ? format(arch) :
2108	 *               VOL
2109	 */
2110
2111	cp = mdoc_a2vol(nn->string);
2112	if (cp) {
2113		free(mdoc->meta.vol);
2114		mdoc->meta.vol = mandoc_strdup(cp);
2115	} else {
2116		/* FIXME: warn about bad arch. */
2117		cp = mdoc_a2arch(nn->string);
2118		if (NULL == cp) {
2119			free(mdoc->meta.vol);
2120			mdoc->meta.vol = mandoc_strdup(nn->string);
2121		} else
2122			mdoc->meta.arch = mandoc_strdup(cp);
2123	}
2124
2125	/* Ignore any subsequent parameters... */
2126	/* FIXME: warn about subsequent parameters. */
2127
2128	return(1);
2129}
2130
2131static int
2132post_prol(POST_ARGS)
2133{
2134	/*
2135	 * Remove prologue macros from the document after they're
2136	 * processed.  The final document uses mdoc_meta for these
2137	 * values and discards the originals.
2138	 */
2139
2140	mdoc_node_delete(mdoc, mdoc->last);
2141	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2142		mdoc->flags |= MDOC_PBODY;
2143
2144	return(1);
2145}
2146
2147static int
2148post_bx(POST_ARGS)
2149{
2150	struct mdoc_node	*n;
2151
2152	/*
2153	 * Make `Bx's second argument always start with an uppercase
2154	 * letter.  Groff checks if it's an "accepted" term, but we just
2155	 * uppercase blindly.
2156	 */
2157
2158	n = mdoc->last->child;
2159	if (n && NULL != (n = n->next))
2160		*n->string = (char)toupper
2161			((unsigned char)*n->string);
2162
2163	return(1);
2164}
2165
2166static int
2167post_os(POST_ARGS)
2168{
2169	struct mdoc_node *n;
2170	char		  buf[BUFSIZ];
2171#ifndef OSNAME
2172	struct utsname	  utsname;
2173#endif
2174
2175	n = mdoc->last;
2176
2177	/*
2178	 * Set the operating system by way of the `Os' macro.  Note that
2179	 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2180	 * provided during compilation, this value will be used instead
2181	 * of filling in "sysname release" from uname().
2182 	 */
2183
2184	if (mdoc->meta.os)
2185		free(mdoc->meta.os);
2186
2187	if ( ! concat(mdoc, buf, n->child, BUFSIZ))
2188		return(0);
2189
2190	/* XXX: yes, these can all be dynamically-adjusted buffers, but
2191	 * it's really not worth the extra hackery.
2192	 */
2193
2194	if ('\0' == buf[0]) {
2195#ifdef OSNAME
2196		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2197			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2198			return(0);
2199		}
2200#else /*!OSNAME */
2201		if (-1 == uname(&utsname)) {
2202			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2203                        mdoc->meta.os = mandoc_strdup("UNKNOWN");
2204                        return(post_prol(mdoc));
2205                }
2206
2207		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2208			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2209			return(0);
2210		}
2211		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2212			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2213			return(0);
2214		}
2215		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2216			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2217			return(0);
2218		}
2219#endif /*!OSNAME*/
2220	}
2221
2222	mdoc->meta.os = mandoc_strdup(buf);
2223	return(1);
2224}
2225
2226static int
2227post_std(POST_ARGS)
2228{
2229	struct mdoc_node *nn, *n;
2230
2231	n = mdoc->last;
2232
2233	/*
2234	 * Macros accepting `-std' as an argument have the name of the
2235	 * current document (`Nm') filled in as the argument if it's not
2236	 * provided.
2237	 */
2238
2239	if (n->child)
2240		return(1);
2241
2242	if (NULL == mdoc->meta.name)
2243		return(1);
2244
2245	nn = n;
2246	mdoc->next = MDOC_NEXT_CHILD;
2247
2248	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2249		return(0);
2250
2251	mdoc->last = nn;
2252	return(1);
2253}
2254
2255static int
2256concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
2257{
2258
2259	p[0] = '\0';
2260
2261	/*
2262	 * Concatenate sibling nodes together.  All siblings must be of
2263	 * type MDOC_TEXT or an assertion is raised.  Concatenation is
2264	 * separated by a single whitespace.  Returns 0 on fatal (string
2265	 * overrun) error.
2266	 */
2267
2268	for ( ; n; n = n->next) {
2269		assert(MDOC_TEXT == n->type);
2270
2271		if (strlcat(p, n->string, sz) >= sz) {
2272			mdoc_nmsg(m, n, MANDOCERR_MEM);
2273			return(0);
2274		}
2275
2276		if (NULL == n->next)
2277			continue;
2278
2279		if (strlcat(p, " ", sz) >= sz) {
2280			mdoc_nmsg(m, n, MANDOCERR_MEM);
2281			return(0);
2282		}
2283	}
2284
2285	return(1);
2286}
2287
2288static enum mdoc_sec
2289a2sec(const char *p)
2290{
2291	int		 i;
2292
2293	for (i = 0; i < (int)SEC__MAX; i++)
2294		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2295			return((enum mdoc_sec)i);
2296
2297	return(SEC_CUSTOM);
2298}
2299
2300static size_t
2301macro2len(enum mdoct macro)
2302{
2303
2304	switch (macro) {
2305	case(MDOC_Ad):
2306		return(12);
2307	case(MDOC_Ao):
2308		return(12);
2309	case(MDOC_An):
2310		return(12);
2311	case(MDOC_Aq):
2312		return(12);
2313	case(MDOC_Ar):
2314		return(12);
2315	case(MDOC_Bo):
2316		return(12);
2317	case(MDOC_Bq):
2318		return(12);
2319	case(MDOC_Cd):
2320		return(12);
2321	case(MDOC_Cm):
2322		return(10);
2323	case(MDOC_Do):
2324		return(10);
2325	case(MDOC_Dq):
2326		return(12);
2327	case(MDOC_Dv):
2328		return(12);
2329	case(MDOC_Eo):
2330		return(12);
2331	case(MDOC_Em):
2332		return(10);
2333	case(MDOC_Er):
2334		return(17);
2335	case(MDOC_Ev):
2336		return(15);
2337	case(MDOC_Fa):
2338		return(12);
2339	case(MDOC_Fl):
2340		return(10);
2341	case(MDOC_Fo):
2342		return(16);
2343	case(MDOC_Fn):
2344		return(16);
2345	case(MDOC_Ic):
2346		return(10);
2347	case(MDOC_Li):
2348		return(16);
2349	case(MDOC_Ms):
2350		return(6);
2351	case(MDOC_Nm):
2352		return(10);
2353	case(MDOC_No):
2354		return(12);
2355	case(MDOC_Oo):
2356		return(10);
2357	case(MDOC_Op):
2358		return(14);
2359	case(MDOC_Pa):
2360		return(32);
2361	case(MDOC_Pf):
2362		return(12);
2363	case(MDOC_Po):
2364		return(12);
2365	case(MDOC_Pq):
2366		return(12);
2367	case(MDOC_Ql):
2368		return(16);
2369	case(MDOC_Qo):
2370		return(12);
2371	case(MDOC_So):
2372		return(12);
2373	case(MDOC_Sq):
2374		return(12);
2375	case(MDOC_Sy):
2376		return(6);
2377	case(MDOC_Sx):
2378		return(16);
2379	case(MDOC_Tn):
2380		return(10);
2381	case(MDOC_Va):
2382		return(12);
2383	case(MDOC_Vt):
2384		return(12);
2385	case(MDOC_Xr):
2386		return(10);
2387	default:
2388		break;
2389	};
2390	return(0);
2391}
2392