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