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