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