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