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