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