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