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