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