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