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