xref: /netbsd-src/external/bsd/mdocml/dist/mdoc_validate.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$Vendor-Id: mdoc_validate.c,v 1.176 2011/09/02 19:40:18 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 	cp = p;
549 	for (cp = p; NULL != (p = strchr(p, '\t')); p++) {
550 		if (MDOC_LITERAL & m->flags)
551 			continue;
552 		mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
553 	}
554 }
555 
556 static int
557 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
558 {
559 
560 	assert(n->parent);
561 	if ((MDOC_ROOT == t || tok == n->parent->tok) &&
562 			(t == n->parent->type))
563 		return(1);
564 
565 	mandoc_vmsg(MANDOCERR_SYNTCHILD, mdoc->parse, n->line,
566 			n->pos, "want parent %s", MDOC_ROOT == t ?
567 			"<root>" : mdoc_macronames[tok]);
568 	return(0);
569 }
570 
571 
572 static int
573 pre_display(PRE_ARGS)
574 {
575 	struct mdoc_node *node;
576 
577 	if (MDOC_BLOCK != n->type)
578 		return(1);
579 
580 	for (node = mdoc->last->parent; node; node = node->parent)
581 		if (MDOC_BLOCK == node->type)
582 			if (MDOC_Bd == node->tok)
583 				break;
584 
585 	if (node)
586 		mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
587 
588 	return(1);
589 }
590 
591 
592 static int
593 pre_bl(PRE_ARGS)
594 {
595 	int		  i, comp, dup;
596 	const char	 *offs, *width;
597 	enum mdoc_list	  lt;
598 	struct mdoc_node *np;
599 
600 	if (MDOC_BLOCK != n->type) {
601 		if (ENDBODY_NOT != n->end) {
602 			assert(n->pending);
603 			np = n->pending->parent;
604 		} else
605 			np = n->parent;
606 
607 		assert(np);
608 		assert(MDOC_BLOCK == np->type);
609 		assert(MDOC_Bl == np->tok);
610 		return(1);
611 	}
612 
613 	/*
614 	 * First figure out which kind of list to use: bind ourselves to
615 	 * the first mentioned list type and warn about any remaining
616 	 * ones.  If we find no list type, we default to LIST_item.
617 	 */
618 
619 	/* LINTED */
620 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
621 		lt = LIST__NONE;
622 		dup = comp = 0;
623 		width = offs = NULL;
624 		switch (n->args->argv[i].arg) {
625 		/* Set list types. */
626 		case (MDOC_Bullet):
627 			lt = LIST_bullet;
628 			break;
629 		case (MDOC_Dash):
630 			lt = LIST_dash;
631 			break;
632 		case (MDOC_Enum):
633 			lt = LIST_enum;
634 			break;
635 		case (MDOC_Hyphen):
636 			lt = LIST_hyphen;
637 			break;
638 		case (MDOC_Item):
639 			lt = LIST_item;
640 			break;
641 		case (MDOC_Tag):
642 			lt = LIST_tag;
643 			break;
644 		case (MDOC_Diag):
645 			lt = LIST_diag;
646 			break;
647 		case (MDOC_Hang):
648 			lt = LIST_hang;
649 			break;
650 		case (MDOC_Ohang):
651 			lt = LIST_ohang;
652 			break;
653 		case (MDOC_Inset):
654 			lt = LIST_inset;
655 			break;
656 		case (MDOC_Column):
657 			lt = LIST_column;
658 			break;
659 		/* Set list arguments. */
660 		case (MDOC_Compact):
661 			dup = n->norm->Bl.comp;
662 			comp = 1;
663 			break;
664 		case (MDOC_Width):
665 			dup = (NULL != n->norm->Bl.width);
666 			width = n->args->argv[i].value[0];
667 			break;
668 		case (MDOC_Offset):
669 			/* NB: this can be empty! */
670 			if (n->args->argv[i].sz) {
671 				offs = n->args->argv[i].value[0];
672 				dup = (NULL != n->norm->Bl.offs);
673 				break;
674 			}
675 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
676 			break;
677 		default:
678 			continue;
679 		}
680 
681 		/* Check: duplicate auxiliary arguments. */
682 
683 		if (dup)
684 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
685 
686 		if (comp && ! dup)
687 			n->norm->Bl.comp = comp;
688 		if (offs && ! dup)
689 			n->norm->Bl.offs = offs;
690 		if (width && ! dup)
691 			n->norm->Bl.width = width;
692 
693 		/* Check: multiple list types. */
694 
695 		if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
696 			mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
697 
698 		/* Assign list type. */
699 
700 		if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
701 			n->norm->Bl.type = lt;
702 			/* Set column information, too. */
703 			if (LIST_column == lt) {
704 				n->norm->Bl.ncols =
705 					n->args->argv[i].sz;
706 				n->norm->Bl.cols = (void *)
707 					n->args->argv[i].value;
708 			}
709 		}
710 
711 		/* The list type should come first. */
712 
713 		if (n->norm->Bl.type == LIST__NONE)
714 			if (n->norm->Bl.width ||
715 					n->norm->Bl.offs ||
716 					n->norm->Bl.comp)
717 				mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
718 
719 		continue;
720 	}
721 
722 	/* Allow lists to default to LIST_item. */
723 
724 	if (LIST__NONE == n->norm->Bl.type) {
725 		mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
726 		n->norm->Bl.type = LIST_item;
727 	}
728 
729 	/*
730 	 * Validate the width field.  Some list types don't need width
731 	 * types and should be warned about them.  Others should have it
732 	 * and must also be warned.
733 	 */
734 
735 	switch (n->norm->Bl.type) {
736 	case (LIST_tag):
737 		if (n->norm->Bl.width)
738 			break;
739 		mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
740 		break;
741 	case (LIST_column):
742 		/* FALLTHROUGH */
743 	case (LIST_diag):
744 		/* FALLTHROUGH */
745 	case (LIST_ohang):
746 		/* FALLTHROUGH */
747 	case (LIST_inset):
748 		/* FALLTHROUGH */
749 	case (LIST_item):
750 		if (n->norm->Bl.width)
751 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
752 		break;
753 	default:
754 		break;
755 	}
756 
757 	return(1);
758 }
759 
760 
761 static int
762 pre_bd(PRE_ARGS)
763 {
764 	int		  i, dup, comp;
765 	enum mdoc_disp 	  dt;
766 	const char	 *offs;
767 	struct mdoc_node *np;
768 
769 	if (MDOC_BLOCK != n->type) {
770 		if (ENDBODY_NOT != n->end) {
771 			assert(n->pending);
772 			np = n->pending->parent;
773 		} else
774 			np = n->parent;
775 
776 		assert(np);
777 		assert(MDOC_BLOCK == np->type);
778 		assert(MDOC_Bd == np->tok);
779 		return(1);
780 	}
781 
782 	/* LINTED */
783 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
784 		dt = DISP__NONE;
785 		dup = comp = 0;
786 		offs = NULL;
787 
788 		switch (n->args->argv[i].arg) {
789 		case (MDOC_Centred):
790 			dt = DISP_centred;
791 			break;
792 		case (MDOC_Ragged):
793 			dt = DISP_ragged;
794 			break;
795 		case (MDOC_Unfilled):
796 			dt = DISP_unfilled;
797 			break;
798 		case (MDOC_Filled):
799 			dt = DISP_filled;
800 			break;
801 		case (MDOC_Literal):
802 			dt = DISP_literal;
803 			break;
804 		case (MDOC_File):
805 			mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
806 			return(0);
807 		case (MDOC_Offset):
808 			/* NB: this can be empty! */
809 			if (n->args->argv[i].sz) {
810 				offs = n->args->argv[i].value[0];
811 				dup = (NULL != n->norm->Bd.offs);
812 				break;
813 			}
814 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
815 			break;
816 		case (MDOC_Compact):
817 			comp = 1;
818 			dup = n->norm->Bd.comp;
819 			break;
820 		default:
821 			abort();
822 			/* NOTREACHED */
823 		}
824 
825 		/* Check whether we have duplicates. */
826 
827 		if (dup)
828 			mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
829 
830 		/* Make our auxiliary assignments. */
831 
832 		if (offs && ! dup)
833 			n->norm->Bd.offs = offs;
834 		if (comp && ! dup)
835 			n->norm->Bd.comp = comp;
836 
837 		/* Check whether a type has already been assigned. */
838 
839 		if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
840 			mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
841 
842 		/* Make our type assignment. */
843 
844 		if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
845 			n->norm->Bd.type = dt;
846 	}
847 
848 	if (DISP__NONE == n->norm->Bd.type) {
849 		mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
850 		n->norm->Bd.type = DISP_ragged;
851 	}
852 
853 	return(1);
854 }
855 
856 
857 static int
858 pre_ss(PRE_ARGS)
859 {
860 
861 	if (MDOC_BLOCK != n->type)
862 		return(1);
863 	return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
864 }
865 
866 
867 static int
868 pre_sh(PRE_ARGS)
869 {
870 
871 	if (MDOC_BLOCK != n->type)
872 		return(1);
873 
874 	roff_regunset(mdoc->roff, REG_nS);
875 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
876 }
877 
878 
879 static int
880 pre_it(PRE_ARGS)
881 {
882 
883 	if (MDOC_BLOCK != n->type)
884 		return(1);
885 
886 	return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
887 }
888 
889 
890 static int
891 pre_an(PRE_ARGS)
892 {
893 	int		 i;
894 
895 	if (NULL == n->args)
896 		return(1);
897 
898 	for (i = 1; i < (int)n->args->argc; i++)
899 		mdoc_pmsg(mdoc, n->args->argv[i].line,
900 			n->args->argv[i].pos, MANDOCERR_IGNARGV);
901 
902 	if (MDOC_Split == n->args->argv[0].arg)
903 		n->norm->An.auth = AUTH_split;
904 	else if (MDOC_Nosplit == n->args->argv[0].arg)
905 		n->norm->An.auth = AUTH_nosplit;
906 	else
907 		abort();
908 
909 	return(1);
910 }
911 
912 static int
913 pre_std(PRE_ARGS)
914 {
915 
916 	if (n->args && 1 == n->args->argc)
917 		if (MDOC_Std == n->args->argv[0].arg)
918 			return(1);
919 
920 	mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
921 	return(1);
922 }
923 
924 static int
925 pre_dt(PRE_ARGS)
926 {
927 
928 	if (NULL == mdoc->meta.date || mdoc->meta.os)
929 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
930 
931 	if (mdoc->meta.title)
932 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
933 
934 	return(1);
935 }
936 
937 static int
938 pre_os(PRE_ARGS)
939 {
940 
941 	if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
942 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
943 
944 	if (mdoc->meta.os)
945 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
946 
947 	return(1);
948 }
949 
950 static int
951 pre_dd(PRE_ARGS)
952 {
953 
954 	if (mdoc->meta.title || mdoc->meta.os)
955 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
956 
957 	if (mdoc->meta.date)
958 		mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
959 
960 	return(1);
961 }
962 
963 
964 static int
965 post_bf(POST_ARGS)
966 {
967 	struct mdoc_node *np;
968 	enum mdocargt	  arg;
969 
970 	/*
971 	 * Unlike other data pointers, these are "housed" by the HEAD
972 	 * element, which contains the goods.
973 	 */
974 
975 	if (MDOC_HEAD != mdoc->last->type) {
976 		if (ENDBODY_NOT != mdoc->last->end) {
977 			assert(mdoc->last->pending);
978 			np = mdoc->last->pending->parent->head;
979 		} else if (MDOC_BLOCK != mdoc->last->type) {
980 			np = mdoc->last->parent->head;
981 		} else
982 			np = mdoc->last->head;
983 
984 		assert(np);
985 		assert(MDOC_HEAD == np->type);
986 		assert(MDOC_Bf == np->tok);
987 		return(1);
988 	}
989 
990 	np = mdoc->last;
991 	assert(MDOC_BLOCK == np->parent->type);
992 	assert(MDOC_Bf == np->parent->tok);
993 
994 	/*
995 	 * Cannot have both argument and parameter.
996 	 * If neither is specified, let it through with a warning.
997 	 */
998 
999 	if (np->parent->args && np->child) {
1000 		mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1001 		return(0);
1002 	} else if (NULL == np->parent->args && NULL == np->child) {
1003 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1004 		return(1);
1005 	}
1006 
1007 	/* Extract argument into data. */
1008 
1009 	if (np->parent->args) {
1010 		arg = np->parent->args->argv[0].arg;
1011 		if (MDOC_Emphasis == arg)
1012 			np->norm->Bf.font = FONT_Em;
1013 		else if (MDOC_Literal == arg)
1014 			np->norm->Bf.font = FONT_Li;
1015 		else if (MDOC_Symbolic == arg)
1016 			np->norm->Bf.font = FONT_Sy;
1017 		else
1018 			abort();
1019 		return(1);
1020 	}
1021 
1022 	/* Extract parameter into data. */
1023 
1024 	if (0 == strcmp(np->child->string, "Em"))
1025 		np->norm->Bf.font = FONT_Em;
1026 	else if (0 == strcmp(np->child->string, "Li"))
1027 		np->norm->Bf.font = FONT_Li;
1028 	else if (0 == strcmp(np->child->string, "Sy"))
1029 		np->norm->Bf.font = FONT_Sy;
1030 	else
1031 		mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1032 
1033 	return(1);
1034 }
1035 
1036 static int
1037 post_lb(POST_ARGS)
1038 {
1039 	const char	*p;
1040 	char		*buf;
1041 	size_t		 sz;
1042 
1043 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1044 
1045 	assert(mdoc->last->child);
1046 	assert(MDOC_TEXT == mdoc->last->child->type);
1047 
1048 	p = mdoc_a2lib(mdoc->last->child->string);
1049 
1050 	/* If lookup ok, replace with table value. */
1051 
1052 	if (p) {
1053 		free(mdoc->last->child->string);
1054 		mdoc->last->child->string = mandoc_strdup(p);
1055 		return(1);
1056 	}
1057 
1058 	/* If not, use "library ``xxxx''. */
1059 
1060 	sz = strlen(mdoc->last->child->string) +
1061 		2 + strlen("\\(lqlibrary\\(rq");
1062 	buf = mandoc_malloc(sz);
1063 	snprintf(buf, sz, "library \\(lq%s\\(rq",
1064 			mdoc->last->child->string);
1065 	free(mdoc->last->child->string);
1066 	mdoc->last->child->string = buf;
1067 	return(1);
1068 }
1069 
1070 static int
1071 post_eoln(POST_ARGS)
1072 {
1073 
1074 	if (mdoc->last->child)
1075 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1076 	return(1);
1077 }
1078 
1079 
1080 static int
1081 post_vt(POST_ARGS)
1082 {
1083 	const struct mdoc_node *n;
1084 
1085 	/*
1086 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
1087 	 * have different syntaxes (yet more context-sensitive
1088 	 * behaviour).  ELEM types must have a child, which is already
1089 	 * guaranteed by the in_line parsing routine; BLOCK types,
1090 	 * specifically the BODY, should only have TEXT children.
1091 	 */
1092 
1093 	if (MDOC_BODY != mdoc->last->type)
1094 		return(1);
1095 
1096 	for (n = mdoc->last->child; n; n = n->next)
1097 		if (MDOC_TEXT != n->type)
1098 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1099 
1100 	return(1);
1101 }
1102 
1103 
1104 static int
1105 post_nm(POST_ARGS)
1106 {
1107 	char		 buf[BUFSIZ];
1108 	int		 c;
1109 
1110 	/* If no child specified, make sure we have the meta name. */
1111 
1112 	if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1113 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1114 		return(1);
1115 	} else if (mdoc->meta.name)
1116 		return(1);
1117 
1118 	/* If no meta name, set it from the child. */
1119 
1120 	buf[0] = '\0';
1121 	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1122 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1123 		return(0);
1124 	}
1125 
1126 	assert(c);
1127 	mdoc->meta.name = mandoc_strdup(buf);
1128 	return(1);
1129 }
1130 
1131 static int
1132 post_literal(POST_ARGS)
1133 {
1134 
1135 	/*
1136 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1137 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
1138 	 * this in literal mode, but it doesn't hurt to just switch it
1139 	 * off in general since displays can't be nested.
1140 	 */
1141 
1142 	if (MDOC_BODY == mdoc->last->type)
1143 		mdoc->flags &= ~MDOC_LITERAL;
1144 
1145 	return(1);
1146 }
1147 
1148 static int
1149 post_defaults(POST_ARGS)
1150 {
1151 	struct mdoc_node *nn;
1152 
1153 	/*
1154 	 * The `Ar' defaults to "file ..." if no value is provided as an
1155 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1156 	 * gets an empty string.
1157 	 */
1158 
1159 	if (mdoc->last->child)
1160 		return(1);
1161 
1162 	nn = mdoc->last;
1163 	mdoc->next = MDOC_NEXT_CHILD;
1164 
1165 	switch (nn->tok) {
1166 	case (MDOC_Ar):
1167 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1168 			return(0);
1169 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1170 			return(0);
1171 		break;
1172 	case (MDOC_At):
1173 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1174 			return(0);
1175 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1176 			return(0);
1177 		break;
1178 	case (MDOC_Li):
1179 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1180 			return(0);
1181 		break;
1182 	case (MDOC_Pa):
1183 		/* FALLTHROUGH */
1184 	case (MDOC_Mt):
1185 		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1186 			return(0);
1187 		break;
1188 	default:
1189 		abort();
1190 		/* NOTREACHED */
1191 	}
1192 
1193 	mdoc->last = nn;
1194 	return(1);
1195 }
1196 
1197 static int
1198 post_at(POST_ARGS)
1199 {
1200 	const char	 *p, *q;
1201 	char		 *buf;
1202 	size_t		  sz;
1203 
1204 	/*
1205 	 * If we have a child, look it up in the standard keys.  If a
1206 	 * key exist, use that instead of the child; if it doesn't,
1207 	 * prefix "AT&T UNIX " to the existing data.
1208 	 */
1209 
1210 	if (NULL == mdoc->last->child)
1211 		return(1);
1212 
1213 	assert(MDOC_TEXT == mdoc->last->child->type);
1214 	p = mdoc_a2att(mdoc->last->child->string);
1215 
1216 	if (p) {
1217 		free(mdoc->last->child->string);
1218 		mdoc->last->child->string = mandoc_strdup(p);
1219 	} else {
1220 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1221 		p = "AT&T UNIX ";
1222 		q = mdoc->last->child->string;
1223 		sz = strlen(p) + strlen(q) + 1;
1224 		buf = mandoc_malloc(sz);
1225 		strlcpy(buf, p, sz);
1226 		strlcat(buf, q, sz);
1227 		free(mdoc->last->child->string);
1228 		mdoc->last->child->string = buf;
1229 	}
1230 
1231 	return(1);
1232 }
1233 
1234 static int
1235 post_an(POST_ARGS)
1236 {
1237 	struct mdoc_node *np;
1238 
1239 	np = mdoc->last;
1240 	if (AUTH__NONE == np->norm->An.auth) {
1241 		if (0 == np->child)
1242 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1243 	} else if (np->child)
1244 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1245 
1246 	return(1);
1247 }
1248 
1249 
1250 static int
1251 post_it(POST_ARGS)
1252 {
1253 	int		  i, cols;
1254 	enum mdoc_list	  lt;
1255 	struct mdoc_node *n, *c;
1256 	enum mandocerr	  er;
1257 
1258 	if (MDOC_BLOCK != mdoc->last->type)
1259 		return(1);
1260 
1261 	n = mdoc->last->parent->parent;
1262 	lt = n->norm->Bl.type;
1263 
1264 	if (LIST__NONE == lt) {
1265 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1266 		return(1);
1267 	}
1268 
1269 	switch (lt) {
1270 	case (LIST_tag):
1271 		if (mdoc->last->head->child)
1272 			break;
1273 		/* FIXME: give this a dummy value. */
1274 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1275 		break;
1276 	case (LIST_hang):
1277 		/* FALLTHROUGH */
1278 	case (LIST_ohang):
1279 		/* FALLTHROUGH */
1280 	case (LIST_inset):
1281 		/* FALLTHROUGH */
1282 	case (LIST_diag):
1283 		if (NULL == mdoc->last->head->child)
1284 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1285 		break;
1286 	case (LIST_bullet):
1287 		/* FALLTHROUGH */
1288 	case (LIST_dash):
1289 		/* FALLTHROUGH */
1290 	case (LIST_enum):
1291 		/* FALLTHROUGH */
1292 	case (LIST_hyphen):
1293 		if (NULL == mdoc->last->body->child)
1294 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1295 		/* FALLTHROUGH */
1296 	case (LIST_item):
1297 		if (mdoc->last->head->child)
1298 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1299 		break;
1300 	case (LIST_column):
1301 		cols = (int)n->norm->Bl.ncols;
1302 
1303 		assert(NULL == mdoc->last->head->child);
1304 
1305 		if (NULL == mdoc->last->body->child)
1306 			mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1307 
1308 		for (i = 0, c = mdoc->last->child; c; c = c->next)
1309 			if (MDOC_BODY == c->type)
1310 				i++;
1311 
1312 		if (i < cols)
1313 			er = MANDOCERR_ARGCOUNT;
1314 		else if (i == cols || i == cols + 1)
1315 			break;
1316 		else
1317 			er = MANDOCERR_SYNTARGCOUNT;
1318 
1319 		mandoc_vmsg(er, mdoc->parse, mdoc->last->line,
1320 				mdoc->last->pos,
1321 				"columns == %d (have %d)", cols, i);
1322 		return(MANDOCERR_ARGCOUNT == er);
1323 	default:
1324 		break;
1325 	}
1326 
1327 	return(1);
1328 }
1329 
1330 static int
1331 post_bl_block(POST_ARGS)
1332 {
1333 	struct mdoc_node *n;
1334 
1335 	/*
1336 	 * These are fairly complicated, so we've broken them into two
1337 	 * functions.  post_bl_block_tag() is called when a -tag is
1338 	 * specified, but no -width (it must be guessed).  The second
1339 	 * when a -width is specified (macro indicators must be
1340 	 * rewritten into real lengths).
1341 	 */
1342 
1343 	n = mdoc->last;
1344 
1345 	if (LIST_tag == n->norm->Bl.type &&
1346 			NULL == n->norm->Bl.width) {
1347 		if ( ! post_bl_block_tag(mdoc))
1348 			return(0);
1349 	} else if (NULL != n->norm->Bl.width) {
1350 		if ( ! post_bl_block_width(mdoc))
1351 			return(0);
1352 	} else
1353 		return(1);
1354 
1355 	assert(n->norm->Bl.width);
1356 	return(1);
1357 }
1358 
1359 static int
1360 post_bl_block_width(POST_ARGS)
1361 {
1362 	size_t		  width;
1363 	int		  i;
1364 	enum mdoct	  tok;
1365 	struct mdoc_node *n;
1366 	char		  buf[NUMSIZ];
1367 
1368 	n = mdoc->last;
1369 
1370 	/*
1371 	 * Calculate the real width of a list from the -width string,
1372 	 * which may contain a macro (with a known default width), a
1373 	 * literal string, or a scaling width.
1374 	 *
1375 	 * If the value to -width is a macro, then we re-write it to be
1376 	 * the macro's width as set in share/tmac/mdoc/doc-common.
1377 	 */
1378 
1379 	if (0 == strcmp(n->norm->Bl.width, "Ds"))
1380 		width = 6;
1381 	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1382 		return(1);
1383 	else if (0 == (width = macro2len(tok)))  {
1384 		mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1385 		return(1);
1386 	}
1387 
1388 	/* The value already exists: free and reallocate it. */
1389 
1390 	assert(n->args);
1391 
1392 	for (i = 0; i < (int)n->args->argc; i++)
1393 		if (MDOC_Width == n->args->argv[i].arg)
1394 			break;
1395 
1396 	assert(i < (int)n->args->argc);
1397 
1398 	snprintf(buf, NUMSIZ, "%un", (unsigned int)width);
1399 	free(n->args->argv[i].value[0]);
1400 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1401 
1402 	/* Set our width! */
1403 	n->norm->Bl.width = n->args->argv[i].value[0];
1404 	return(1);
1405 }
1406 
1407 static int
1408 post_bl_block_tag(POST_ARGS)
1409 {
1410 	struct mdoc_node *n, *nn;
1411 	size_t		  sz, ssz;
1412 	int		  i;
1413 	char		  buf[NUMSIZ];
1414 
1415 	/*
1416 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
1417 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
1418 	 * ONLY if the -width argument has NOT been provided.  See
1419 	 * post_bl_block_width() for converting the -width string.
1420 	 */
1421 
1422 	sz = 10;
1423 	n = mdoc->last;
1424 
1425 	for (nn = n->body->child; nn; nn = nn->next) {
1426 		if (MDOC_It != nn->tok)
1427 			continue;
1428 
1429 		assert(MDOC_BLOCK == nn->type);
1430 		nn = nn->head->child;
1431 
1432 		if (nn == NULL)
1433 			break;
1434 
1435 		if (MDOC_TEXT == nn->type) {
1436 			sz = strlen(nn->string) + 1;
1437 			break;
1438 		}
1439 
1440 		if (0 != (ssz = macro2len(nn->tok)))
1441 			sz = ssz;
1442 
1443 		break;
1444 	}
1445 
1446 	/* Defaults to ten ens. */
1447 
1448 	snprintf(buf, NUMSIZ, "%un", (unsigned int)sz);
1449 
1450 	/*
1451 	 * We have to dynamically add this to the macro's argument list.
1452 	 * We're guaranteed that a MDOC_Width doesn't already exist.
1453 	 */
1454 
1455 	assert(n->args);
1456 	i = (int)(n->args->argc)++;
1457 
1458 	n->args->argv = mandoc_realloc(n->args->argv,
1459 			n->args->argc * sizeof(struct mdoc_argv));
1460 
1461 	n->args->argv[i].arg = MDOC_Width;
1462 	n->args->argv[i].line = n->line;
1463 	n->args->argv[i].pos = n->pos;
1464 	n->args->argv[i].sz = 1;
1465 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1466 	n->args->argv[i].value[0] = mandoc_strdup(buf);
1467 
1468 	/* Set our width! */
1469 	n->norm->Bl.width = n->args->argv[i].value[0];
1470 	return(1);
1471 }
1472 
1473 
1474 static int
1475 post_bl_head(POST_ARGS)
1476 {
1477 	struct mdoc_node *np, *nn, *nnp;
1478 	int		  i, j;
1479 
1480 	if (LIST_column != mdoc->last->norm->Bl.type)
1481 		/* FIXME: this should be ERROR class... */
1482 		return(hwarn_eq0(mdoc));
1483 
1484 	/*
1485 	 * Convert old-style lists, where the column width specifiers
1486 	 * trail as macro parameters, to the new-style ("normal-form")
1487 	 * lists where they're argument values following -column.
1488 	 */
1489 
1490 	/* First, disallow both types and allow normal-form. */
1491 
1492 	/*
1493 	 * TODO: technically, we can accept both and just merge the two
1494 	 * lists, but I'll leave that for another day.
1495 	 */
1496 
1497 	if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1498 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1499 		return(0);
1500 	} else if (NULL == mdoc->last->child)
1501 		return(1);
1502 
1503 	np = mdoc->last->parent;
1504 	assert(np->args);
1505 
1506 	for (j = 0; j < (int)np->args->argc; j++)
1507 		if (MDOC_Column == np->args->argv[j].arg)
1508 			break;
1509 
1510 	assert(j < (int)np->args->argc);
1511 	assert(0 == np->args->argv[j].sz);
1512 
1513 	/*
1514 	 * Accommodate for new-style groff column syntax.  Shuffle the
1515 	 * child nodes, all of which must be TEXT, as arguments for the
1516 	 * column field.  Then, delete the head children.
1517 	 */
1518 
1519 	np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1520 	np->args->argv[j].value = mandoc_malloc
1521 		((size_t)mdoc->last->nchild * sizeof(char *));
1522 
1523 	mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1524 	mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value;
1525 
1526 	for (i = 0, nn = mdoc->last->child; nn; i++) {
1527 		np->args->argv[j].value[i] = nn->string;
1528 		nn->string = NULL;
1529 		nnp = nn;
1530 		nn = nn->next;
1531 		mdoc_node_delete(NULL, nnp);
1532 	}
1533 
1534 	mdoc->last->nchild = 0;
1535 	mdoc->last->child = NULL;
1536 
1537 	return(1);
1538 }
1539 
1540 static int
1541 post_bl(POST_ARGS)
1542 {
1543 	struct mdoc_node	*n;
1544 
1545 	if (MDOC_HEAD == mdoc->last->type)
1546 		return(post_bl_head(mdoc));
1547 	if (MDOC_BLOCK == mdoc->last->type)
1548 		return(post_bl_block(mdoc));
1549 	if (MDOC_BODY != mdoc->last->type)
1550 		return(1);
1551 
1552 	for (n = mdoc->last->child; n; n = n->next) {
1553 		switch (n->tok) {
1554 		case (MDOC_Lp):
1555 			/* FALLTHROUGH */
1556 		case (MDOC_Pp):
1557 			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1558 			/* FALLTHROUGH */
1559 		case (MDOC_It):
1560 			/* FALLTHROUGH */
1561 		case (MDOC_Sm):
1562 			continue;
1563 		default:
1564 			break;
1565 		}
1566 
1567 		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1568 		return(0);
1569 	}
1570 
1571 	return(1);
1572 }
1573 
1574 static int
1575 ebool(struct mdoc *mdoc)
1576 {
1577 
1578 	if (NULL == mdoc->last->child) {
1579 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1580 		mdoc_node_delete(mdoc, mdoc->last);
1581 		return(1);
1582 	}
1583 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1584 
1585 	assert(MDOC_TEXT == mdoc->last->child->type);
1586 
1587 	if (0 == strcmp(mdoc->last->child->string, "on"))
1588 		return(1);
1589 	if (0 == strcmp(mdoc->last->child->string, "off"))
1590 		return(1);
1591 
1592 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1593 	return(1);
1594 }
1595 
1596 static int
1597 post_root(POST_ARGS)
1598 {
1599 	int		  erc;
1600 	struct mdoc_node *n;
1601 
1602 	erc = 0;
1603 
1604 	/* Check that we have a finished prologue. */
1605 
1606 	if ( ! (MDOC_PBODY & mdoc->flags)) {
1607 		erc++;
1608 		mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1609 	}
1610 
1611 	n = mdoc->first;
1612 	assert(n);
1613 
1614 	/* Check that we begin with a proper `Sh'. */
1615 
1616 	if (NULL == n->child) {
1617 		erc++;
1618 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1619 	} else if (MDOC_BLOCK != n->child->type ||
1620 			MDOC_Sh != n->child->tok) {
1621 		erc++;
1622 		/* Can this be lifted?  See rxdebug.1 for example. */
1623 		mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1624 	}
1625 
1626 	return(erc ? 0 : 1);
1627 }
1628 
1629 static int
1630 post_st(POST_ARGS)
1631 {
1632 	struct mdoc_node	 *ch;
1633 	const char		 *p;
1634 
1635 	if (NULL == (ch = mdoc->last->child)) {
1636 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1637 		mdoc_node_delete(mdoc, mdoc->last);
1638 		return(1);
1639 	}
1640 
1641 	assert(MDOC_TEXT == ch->type);
1642 
1643 	if (NULL == (p = mdoc_a2st(ch->string))) {
1644 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1645 		mdoc_node_delete(mdoc, mdoc->last);
1646 	} else {
1647 		free(ch->string);
1648 		ch->string = mandoc_strdup(p);
1649 	}
1650 
1651 	return(1);
1652 }
1653 
1654 static int
1655 post_rs(POST_ARGS)
1656 {
1657 	struct mdoc_node *nn, *next, *prev;
1658 	int		  i, j;
1659 
1660 	switch (mdoc->last->type) {
1661 	case (MDOC_HEAD):
1662 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1663 		return(1);
1664 	case (MDOC_BODY):
1665 		if (mdoc->last->child)
1666 			break;
1667 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1668 		return(1);
1669 	default:
1670 		return(1);
1671 	}
1672 
1673 	/*
1674 	 * Make sure only certain types of nodes are allowed within the
1675 	 * the `Rs' body.  Delete offending nodes and raise a warning.
1676 	 * Do this before re-ordering for the sake of clarity.
1677 	 */
1678 
1679 	next = NULL;
1680 	for (nn = mdoc->last->child; nn; nn = next) {
1681 		for (i = 0; i < RSORD_MAX; i++)
1682 			if (nn->tok == rsord[i])
1683 				break;
1684 
1685 		if (i < RSORD_MAX) {
1686 			if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1687 				mdoc->last->norm->Rs.quote_T++;
1688 			next = nn->next;
1689 			continue;
1690 		}
1691 
1692 		next = nn->next;
1693 		mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1694 		mdoc_node_delete(mdoc, nn);
1695 	}
1696 
1697 	/*
1698 	 * The full `Rs' block needs special handling to order the
1699 	 * sub-elements according to `rsord'.  Pick through each element
1700 	 * and correctly order it.  This is a insertion sort.
1701 	 */
1702 
1703 	next = NULL;
1704 	for (nn = mdoc->last->child->next; nn; nn = next) {
1705 		/* Determine order of `nn'. */
1706 		for (i = 0; i < RSORD_MAX; i++)
1707 			if (rsord[i] == nn->tok)
1708 				break;
1709 
1710 		/*
1711 		 * Remove `nn' from the chain.  This somewhat
1712 		 * repeats mdoc_node_unlink(), but since we're
1713 		 * just re-ordering, there's no need for the
1714 		 * full unlink process.
1715 		 */
1716 
1717 		if (NULL != (next = nn->next))
1718 			next->prev = nn->prev;
1719 
1720 		if (NULL != (prev = nn->prev))
1721 			prev->next = nn->next;
1722 
1723 		nn->prev = nn->next = NULL;
1724 
1725 		/*
1726 		 * Scan back until we reach a node that's
1727 		 * ordered before `nn'.
1728 		 */
1729 
1730 		for ( ; prev ; prev = prev->prev) {
1731 			/* Determine order of `prev'. */
1732 			for (j = 0; j < RSORD_MAX; j++)
1733 				if (rsord[j] == prev->tok)
1734 					break;
1735 
1736 			if (j <= i)
1737 				break;
1738 		}
1739 
1740 		/*
1741 		 * Set `nn' back into its correct place in front
1742 		 * of the `prev' node.
1743 		 */
1744 
1745 		nn->prev = prev;
1746 
1747 		if (prev) {
1748 			if (prev->next)
1749 				prev->next->prev = nn;
1750 			nn->next = prev->next;
1751 			prev->next = nn;
1752 		} else {
1753 			mdoc->last->child->prev = nn;
1754 			nn->next = mdoc->last->child;
1755 			mdoc->last->child = nn;
1756 		}
1757 	}
1758 
1759 	return(1);
1760 }
1761 
1762 static int
1763 post_ns(POST_ARGS)
1764 {
1765 
1766 	if (MDOC_LINE & mdoc->last->flags)
1767 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1768 	return(1);
1769 }
1770 
1771 static int
1772 post_sh(POST_ARGS)
1773 {
1774 
1775 	if (MDOC_HEAD == mdoc->last->type)
1776 		return(post_sh_head(mdoc));
1777 	if (MDOC_BODY == mdoc->last->type)
1778 		return(post_sh_body(mdoc));
1779 
1780 	return(1);
1781 }
1782 
1783 static int
1784 post_sh_body(POST_ARGS)
1785 {
1786 	struct mdoc_node *n;
1787 
1788 	if (SEC_NAME != mdoc->lastsec)
1789 		return(1);
1790 
1791 	/*
1792 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1793 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
1794 	 * children of the BODY declaration can also be "text".
1795 	 */
1796 
1797 	if (NULL == (n = mdoc->last->child)) {
1798 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1799 		return(1);
1800 	}
1801 
1802 	for ( ; n && n->next; n = n->next) {
1803 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1804 			continue;
1805 		if (MDOC_TEXT == n->type)
1806 			continue;
1807 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1808 	}
1809 
1810 	assert(n);
1811 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1812 		return(1);
1813 
1814 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1815 	return(1);
1816 }
1817 
1818 static int
1819 post_sh_head(POST_ARGS)
1820 {
1821 	char		 buf[BUFSIZ];
1822 	enum mdoc_sec	 sec;
1823 	int		 c;
1824 
1825 	/*
1826 	 * Process a new section.  Sections are either "named" or
1827 	 * "custom".  Custom sections are user-defined, while named ones
1828 	 * follow a conventional order and may only appear in certain
1829 	 * manual sections.
1830 	 */
1831 
1832 	sec = SEC_CUSTOM;
1833 	buf[0] = '\0';
1834 	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
1835 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
1836 		return(0);
1837 	} else if (1 == c)
1838 		sec = a2sec(buf);
1839 
1840 	/* The NAME should be first. */
1841 
1842 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1843 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1844 
1845 	/* The SYNOPSIS gets special attention in other areas. */
1846 
1847 	if (SEC_SYNOPSIS == sec)
1848 		mdoc->flags |= MDOC_SYNOPSIS;
1849 	else
1850 		mdoc->flags &= ~MDOC_SYNOPSIS;
1851 
1852 	/* Mark our last section. */
1853 
1854 	mdoc->lastsec = sec;
1855 
1856 	/* We don't care about custom sections after this. */
1857 
1858 	if (SEC_CUSTOM == sec)
1859 		return(1);
1860 
1861 	/*
1862 	 * Check whether our non-custom section is being repeated or is
1863 	 * out of order.
1864 	 */
1865 
1866 	if (sec == mdoc->lastnamed)
1867 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1868 
1869 	if (sec < mdoc->lastnamed)
1870 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1871 
1872 	/* Mark the last named section. */
1873 
1874 	mdoc->lastnamed = sec;
1875 
1876 	/* Check particular section/manual conventions. */
1877 
1878 	assert(mdoc->meta.msec);
1879 
1880 	switch (sec) {
1881 	case (SEC_RETURN_VALUES):
1882 		/* FALLTHROUGH */
1883 	case (SEC_ERRORS):
1884 		/* FALLTHROUGH */
1885 	case (SEC_LIBRARY):
1886 		if (*mdoc->meta.msec == '2')
1887 			break;
1888 		if (*mdoc->meta.msec == '3')
1889 			break;
1890 		if (*mdoc->meta.msec == '9')
1891 			break;
1892 		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1893 		break;
1894 	default:
1895 		break;
1896 	}
1897 
1898 	return(1);
1899 }
1900 
1901 static int
1902 post_ignpar(POST_ARGS)
1903 {
1904 	struct mdoc_node *np;
1905 
1906 	if (MDOC_BODY != mdoc->last->type)
1907 		return(1);
1908 
1909 	if (NULL != (np = mdoc->last->child))
1910 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1911 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1912 			mdoc_node_delete(mdoc, np);
1913 		}
1914 
1915 	if (NULL != (np = mdoc->last->last))
1916 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1917 			mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1918 			mdoc_node_delete(mdoc, np);
1919 		}
1920 
1921 	return(1);
1922 }
1923 
1924 static int
1925 pre_par(PRE_ARGS)
1926 {
1927 
1928 	if (NULL == mdoc->last)
1929 		return(1);
1930 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1931 		return(1);
1932 
1933 	/*
1934 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1935 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
1936 	 */
1937 
1938 	if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1939 		return(1);
1940 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1941 		return(1);
1942 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1943 		return(1);
1944 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1945 		return(1);
1946 
1947 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1948 	mdoc_node_delete(mdoc, mdoc->last);
1949 	return(1);
1950 }
1951 
1952 static int
1953 pre_literal(PRE_ARGS)
1954 {
1955 
1956 	if (MDOC_BODY != n->type)
1957 		return(1);
1958 
1959 	/*
1960 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1961 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1962 	 */
1963 
1964 	switch (n->tok) {
1965 	case (MDOC_Dl):
1966 		mdoc->flags |= MDOC_LITERAL;
1967 		break;
1968 	case (MDOC_Bd):
1969 		if (DISP_literal == n->norm->Bd.type)
1970 			mdoc->flags |= MDOC_LITERAL;
1971 		if (DISP_unfilled == n->norm->Bd.type)
1972 			mdoc->flags |= MDOC_LITERAL;
1973 		break;
1974 	default:
1975 		abort();
1976 		/* NOTREACHED */
1977 	}
1978 
1979 	return(1);
1980 }
1981 
1982 static int
1983 post_dd(POST_ARGS)
1984 {
1985 	char		  buf[DATESIZE];
1986 	struct mdoc_node *n;
1987 	int		  c;
1988 
1989 	if (mdoc->meta.date)
1990 		free(mdoc->meta.date);
1991 
1992 	n = mdoc->last;
1993 	if (NULL == n->child || '\0' == n->child->string[0]) {
1994 		mdoc->meta.date = mandoc_normdate
1995 			(mdoc->parse, NULL, n->line, n->pos);
1996 		return(1);
1997 	}
1998 
1999 	buf[0] = '\0';
2000 	if (-1 == (c = concat(buf, n->child, DATESIZE))) {
2001 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2002 		return(0);
2003 	}
2004 
2005 	assert(c);
2006 	mdoc->meta.date = mandoc_normdate
2007 		(mdoc->parse, buf, n->line, n->pos);
2008 
2009 	return(1);
2010 }
2011 
2012 static int
2013 post_dt(POST_ARGS)
2014 {
2015 	struct mdoc_node *nn, *n;
2016 	const char	 *cp;
2017 	char		 *p;
2018 
2019 	n = mdoc->last;
2020 
2021 	if (mdoc->meta.title)
2022 		free(mdoc->meta.title);
2023 	if (mdoc->meta.vol)
2024 		free(mdoc->meta.vol);
2025 	if (mdoc->meta.arch)
2026 		free(mdoc->meta.arch);
2027 
2028 	mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2029 
2030 	/* First make all characters uppercase. */
2031 
2032 	if (NULL != (nn = n->child))
2033 		for (p = nn->string; *p; p++) {
2034 			if (toupper((unsigned char)*p) == *p)
2035 				continue;
2036 
2037 			/*
2038 			 * FIXME: don't be lazy: have this make all
2039 			 * characters be uppercase and just warn once.
2040 			 */
2041 			mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2042 			break;
2043 		}
2044 
2045 	/* Handles: `.Dt'
2046 	 *   --> title = unknown, volume = local, msec = 0, arch = NULL
2047 	 */
2048 
2049 	if (NULL == (nn = n->child)) {
2050 		/* XXX: make these macro values. */
2051 		/* FIXME: warn about missing values. */
2052 		mdoc->meta.title = mandoc_strdup("UNKNOWN");
2053 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2054 		mdoc->meta.msec = mandoc_strdup("1");
2055 		return(1);
2056 	}
2057 
2058 	/* Handles: `.Dt TITLE'
2059 	 *   --> title = TITLE, volume = local, msec = 0, arch = NULL
2060 	 */
2061 
2062 	mdoc->meta.title = mandoc_strdup
2063 		('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2064 
2065 	if (NULL == (nn = nn->next)) {
2066 		/* FIXME: warn about missing msec. */
2067 		/* XXX: make this a macro value. */
2068 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2069 		mdoc->meta.msec = mandoc_strdup("1");
2070 		return(1);
2071 	}
2072 
2073 	/* Handles: `.Dt TITLE SEC'
2074 	 *   --> title = TITLE, volume = SEC is msec ?
2075 	 *           format(msec) : SEC,
2076 	 *       msec = SEC is msec ? atoi(msec) : 0,
2077 	 *       arch = NULL
2078 	 */
2079 
2080 	cp = mdoc_a2msec(nn->string);
2081 	if (cp) {
2082 		mdoc->meta.vol = mandoc_strdup(cp);
2083 		mdoc->meta.msec = mandoc_strdup(nn->string);
2084 	} else {
2085 		mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2086 		mdoc->meta.vol = mandoc_strdup(nn->string);
2087 		mdoc->meta.msec = mandoc_strdup(nn->string);
2088 	}
2089 
2090 	if (NULL == (nn = nn->next))
2091 		return(1);
2092 
2093 	/* Handles: `.Dt TITLE SEC VOL'
2094 	 *   --> title = TITLE, volume = VOL is vol ?
2095 	 *       format(VOL) :
2096 	 *           VOL is arch ? format(arch) :
2097 	 *               VOL
2098 	 */
2099 
2100 	cp = mdoc_a2vol(nn->string);
2101 	if (cp) {
2102 		free(mdoc->meta.vol);
2103 		mdoc->meta.vol = mandoc_strdup(cp);
2104 	} else {
2105 		/* FIXME: warn about bad arch. */
2106 		cp = mdoc_a2arch(nn->string);
2107 		if (NULL == cp) {
2108 			free(mdoc->meta.vol);
2109 			mdoc->meta.vol = mandoc_strdup(nn->string);
2110 		} else
2111 			mdoc->meta.arch = mandoc_strdup(cp);
2112 	}
2113 
2114 	/* Ignore any subsequent parameters... */
2115 	/* FIXME: warn about subsequent parameters. */
2116 
2117 	return(1);
2118 }
2119 
2120 static int
2121 post_prol(POST_ARGS)
2122 {
2123 	/*
2124 	 * Remove prologue macros from the document after they're
2125 	 * processed.  The final document uses mdoc_meta for these
2126 	 * values and discards the originals.
2127 	 */
2128 
2129 	mdoc_node_delete(mdoc, mdoc->last);
2130 	if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2131 		mdoc->flags |= MDOC_PBODY;
2132 
2133 	return(1);
2134 }
2135 
2136 static int
2137 post_bx(POST_ARGS)
2138 {
2139 	struct mdoc_node	*n;
2140 
2141 	/*
2142 	 * Make `Bx's second argument always start with an uppercase
2143 	 * letter.  Groff checks if it's an "accepted" term, but we just
2144 	 * uppercase blindly.
2145 	 */
2146 
2147 	n = mdoc->last->child;
2148 	if (n && NULL != (n = n->next))
2149 		*n->string = (char)toupper
2150 			((unsigned char)*n->string);
2151 
2152 	return(1);
2153 }
2154 
2155 static int
2156 post_os(POST_ARGS)
2157 {
2158 	struct mdoc_node *n;
2159 	char		  buf[BUFSIZ];
2160 	int		  c;
2161 #ifndef OSNAME
2162 	struct utsname	  utsname;
2163 #endif
2164 
2165 	n = mdoc->last;
2166 
2167 	/*
2168 	 * Set the operating system by way of the `Os' macro.  Note that
2169 	 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2170 	 * provided during compilation, this value will be used instead
2171 	 * of filling in "sysname release" from uname().
2172  	 */
2173 
2174 	if (mdoc->meta.os)
2175 		free(mdoc->meta.os);
2176 
2177 	buf[0] = '\0';
2178 	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
2179 		mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM);
2180 		return(0);
2181 	}
2182 
2183 	assert(c);
2184 
2185 	/* XXX: yes, these can all be dynamically-adjusted buffers, but
2186 	 * it's really not worth the extra hackery.
2187 	 */
2188 
2189 	if ('\0' == buf[0]) {
2190 #ifdef OSNAME
2191 		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2192 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2193 			return(0);
2194 		}
2195 #else /*!OSNAME */
2196 		if (-1 == uname(&utsname)) {
2197 			mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2198                         mdoc->meta.os = mandoc_strdup("UNKNOWN");
2199                         return(post_prol(mdoc));
2200                 }
2201 
2202 		if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2203 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2204 			return(0);
2205 		}
2206 		if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2207 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2208 			return(0);
2209 		}
2210 		if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2211 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2212 			return(0);
2213 		}
2214 #endif /*!OSNAME*/
2215 	}
2216 
2217 	mdoc->meta.os = mandoc_strdup(buf);
2218 	return(1);
2219 }
2220 
2221 static int
2222 post_std(POST_ARGS)
2223 {
2224 	struct mdoc_node *nn, *n;
2225 
2226 	n = mdoc->last;
2227 
2228 	/*
2229 	 * Macros accepting `-std' as an argument have the name of the
2230 	 * current document (`Nm') filled in as the argument if it's not
2231 	 * provided.
2232 	 */
2233 
2234 	if (n->child)
2235 		return(1);
2236 
2237 	if (NULL == mdoc->meta.name)
2238 		return(1);
2239 
2240 	nn = n;
2241 	mdoc->next = MDOC_NEXT_CHILD;
2242 
2243 	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2244 		return(0);
2245 
2246 	mdoc->last = nn;
2247 	return(1);
2248 }
2249 
2250 /*
2251  * Concatenate a node, stopping at the first non-text.
2252  * Concatenation is separated by a single whitespace.
2253  * Returns -1 on fatal (string overrun) error, 0 if child nodes were
2254  * encountered, 1 otherwise.
2255  */
2256 static int
2257 concat(char *p, const struct mdoc_node *n, size_t sz)
2258 {
2259 
2260 	for ( ; NULL != n; n = n->next) {
2261 		if (MDOC_TEXT != n->type)
2262 			return(0);
2263 		if ('\0' != p[0] && strlcat(p, " ", sz) >= sz)
2264 			return(-1);
2265 		if (strlcat(p, n->string, sz) >= sz)
2266 			return(-1);
2267 		concat(p, n->child, sz);
2268 	}
2269 
2270 	return(1);
2271 }
2272 
2273 static enum mdoc_sec
2274 a2sec(const char *p)
2275 {
2276 	int		 i;
2277 
2278 	for (i = 0; i < (int)SEC__MAX; i++)
2279 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2280 			return((enum mdoc_sec)i);
2281 
2282 	return(SEC_CUSTOM);
2283 }
2284 
2285 static size_t
2286 macro2len(enum mdoct macro)
2287 {
2288 
2289 	switch (macro) {
2290 	case(MDOC_Ad):
2291 		return(12);
2292 	case(MDOC_Ao):
2293 		return(12);
2294 	case(MDOC_An):
2295 		return(12);
2296 	case(MDOC_Aq):
2297 		return(12);
2298 	case(MDOC_Ar):
2299 		return(12);
2300 	case(MDOC_Bo):
2301 		return(12);
2302 	case(MDOC_Bq):
2303 		return(12);
2304 	case(MDOC_Cd):
2305 		return(12);
2306 	case(MDOC_Cm):
2307 		return(10);
2308 	case(MDOC_Do):
2309 		return(10);
2310 	case(MDOC_Dq):
2311 		return(12);
2312 	case(MDOC_Dv):
2313 		return(12);
2314 	case(MDOC_Eo):
2315 		return(12);
2316 	case(MDOC_Em):
2317 		return(10);
2318 	case(MDOC_Er):
2319 		return(17);
2320 	case(MDOC_Ev):
2321 		return(15);
2322 	case(MDOC_Fa):
2323 		return(12);
2324 	case(MDOC_Fl):
2325 		return(10);
2326 	case(MDOC_Fo):
2327 		return(16);
2328 	case(MDOC_Fn):
2329 		return(16);
2330 	case(MDOC_Ic):
2331 		return(10);
2332 	case(MDOC_Li):
2333 		return(16);
2334 	case(MDOC_Ms):
2335 		return(6);
2336 	case(MDOC_Nm):
2337 		return(10);
2338 	case(MDOC_No):
2339 		return(12);
2340 	case(MDOC_Oo):
2341 		return(10);
2342 	case(MDOC_Op):
2343 		return(14);
2344 	case(MDOC_Pa):
2345 		return(32);
2346 	case(MDOC_Pf):
2347 		return(12);
2348 	case(MDOC_Po):
2349 		return(12);
2350 	case(MDOC_Pq):
2351 		return(12);
2352 	case(MDOC_Ql):
2353 		return(16);
2354 	case(MDOC_Qo):
2355 		return(12);
2356 	case(MDOC_So):
2357 		return(12);
2358 	case(MDOC_Sq):
2359 		return(12);
2360 	case(MDOC_Sy):
2361 		return(6);
2362 	case(MDOC_Sx):
2363 		return(16);
2364 	case(MDOC_Tn):
2365 		return(10);
2366 	case(MDOC_Va):
2367 		return(12);
2368 	case(MDOC_Vt):
2369 		return(12);
2370 	case(MDOC_Xr):
2371 		return(10);
2372 	default:
2373 		break;
2374 	};
2375 	return(0);
2376 }
2377