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 	}
972 
973 	post_fname(mdoc);
974 }
975 
976 static void
977 post_fa(POST_ARGS)
978 {
979 	const struct roff_node *n;
980 	const char *cp;
981 
982 	for (n = mdoc->last->child; n != NULL; n = n->next) {
983 		for (cp = n->string; *cp != '\0'; cp++) {
984 			/* Ignore callbacks and alterations. */
985 			if (*cp == '(' || *cp == '{')
986 				break;
987 			if (*cp != ',')
988 				continue;
989 			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
990 			    n->line, n->pos + (cp - n->string),
991 			    n->string);
992 			break;
993 		}
994 	}
995 }
996 
997 static void
998 post_nm(POST_ARGS)
999 {
1000 	struct roff_node	*n;
1001 
1002 	n = mdoc->last;
1003 
1004 	if (n->last != NULL &&
1005 	    (n->last->tok == MDOC_Pp ||
1006 	     n->last->tok == MDOC_Lp))
1007 		mdoc_node_relink(mdoc, n->last);
1008 
1009 	if (mdoc->meta.name == NULL)
1010 		deroff(&mdoc->meta.name, n);
1011 
1012 	if (mdoc->meta.name == NULL ||
1013 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
1014 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1015 		    n->line, n->pos, "Nm");
1016 
1017 	if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) ||
1018 	    (n->child != NULL && n->child->type == ROFFT_TEXT) ||
1019 	    mdoc->meta.name == NULL)
1020 		return;
1021 
1022 	mdoc->next = ROFF_NEXT_CHILD;
1023 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1024 	mdoc->last->flags |= NODE_NOSRC;
1025 	mdoc->last = n;
1026 }
1027 
1028 static void
1029 post_nd(POST_ARGS)
1030 {
1031 	struct roff_node	*n;
1032 
1033 	n = mdoc->last;
1034 
1035 	if (n->type != ROFFT_BODY)
1036 		return;
1037 
1038 	if (n->child == NULL)
1039 		mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1040 		    n->line, n->pos, "Nd");
1041 
1042 	post_hyph(mdoc);
1043 }
1044 
1045 static void
1046 post_display(POST_ARGS)
1047 {
1048 	struct roff_node *n, *np;
1049 
1050 	n = mdoc->last;
1051 	switch (n->type) {
1052 	case ROFFT_BODY:
1053 		if (n->end != ENDBODY_NOT) {
1054 			if (n->tok == MDOC_Bd &&
1055 			    n->body->parent->args == NULL)
1056 				roff_node_delete(mdoc, n);
1057 		} else if (n->child == NULL)
1058 			mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1059 			    n->line, n->pos, mdoc_macronames[n->tok]);
1060 		else if (n->tok == MDOC_D1)
1061 			post_hyph(mdoc);
1062 		break;
1063 	case ROFFT_BLOCK:
1064 		if (n->tok == MDOC_Bd) {
1065 			if (n->args == NULL) {
1066 				mandoc_msg(MANDOCERR_BD_NOARG,
1067 				    mdoc->parse, n->line, n->pos, "Bd");
1068 				mdoc->next = ROFF_NEXT_SIBLING;
1069 				while (n->body->child != NULL)
1070 					mdoc_node_relink(mdoc,
1071 					    n->body->child);
1072 				roff_node_delete(mdoc, n);
1073 				break;
1074 			}
1075 			post_bd(mdoc);
1076 			post_prevpar(mdoc);
1077 		}
1078 		for (np = n->parent; np != NULL; np = np->parent) {
1079 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1080 				mandoc_vmsg(MANDOCERR_BD_NEST,
1081 				    mdoc->parse, n->line, n->pos,
1082 				    "%s in Bd", mdoc_macronames[n->tok]);
1083 				break;
1084 			}
1085 		}
1086 		break;
1087 	default:
1088 		break;
1089 	}
1090 }
1091 
1092 static void
1093 post_defaults(POST_ARGS)
1094 {
1095 	struct roff_node *nn;
1096 
1097 	/*
1098 	 * The `Ar' defaults to "file ..." if no value is provided as an
1099 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1100 	 * gets an empty string.
1101 	 */
1102 
1103 	if (mdoc->last->child != NULL)
1104 		return;
1105 
1106 	nn = mdoc->last;
1107 
1108 	switch (nn->tok) {
1109 	case MDOC_Ar:
1110 		mdoc->next = ROFF_NEXT_CHILD;
1111 		roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1112 		mdoc->last->flags |= NODE_NOSRC;
1113 		roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1114 		mdoc->last->flags |= NODE_NOSRC;
1115 		break;
1116 	case MDOC_Pa:
1117 	case MDOC_Mt:
1118 		mdoc->next = ROFF_NEXT_CHILD;
1119 		roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1120 		mdoc->last->flags |= NODE_NOSRC;
1121 		break;
1122 	default:
1123 		abort();
1124 	}
1125 	mdoc->last = nn;
1126 }
1127 
1128 static void
1129 post_at(POST_ARGS)
1130 {
1131 	struct roff_node	*n, *nch;
1132 	const char		*att;
1133 
1134 	n = mdoc->last;
1135 	nch = n->child;
1136 
1137 	/*
1138 	 * If we have a child, look it up in the standard keys.  If a
1139 	 * key exist, use that instead of the child; if it doesn't,
1140 	 * prefix "AT&T UNIX " to the existing data.
1141 	 */
1142 
1143 	att = NULL;
1144 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1145 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1146 		    nch->line, nch->pos, "At %s", nch->string);
1147 
1148 	mdoc->next = ROFF_NEXT_CHILD;
1149 	if (att != NULL) {
1150 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
1151 		nch->flags |= NODE_NOPRT;
1152 	} else
1153 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1154 	mdoc->last->flags |= NODE_NOSRC;
1155 	mdoc->last = n;
1156 }
1157 
1158 static void
1159 post_an(POST_ARGS)
1160 {
1161 	struct roff_node *np, *nch;
1162 
1163 	post_an_norm(mdoc);
1164 
1165 	np = mdoc->last;
1166 	nch = np->child;
1167 	if (np->norm->An.auth == AUTH__NONE) {
1168 		if (nch == NULL)
1169 			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1170 			    np->line, np->pos, "An");
1171 	} else if (nch != NULL)
1172 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1173 		    nch->line, nch->pos, "An ... %s", nch->string);
1174 }
1175 
1176 static void
1177 post_en(POST_ARGS)
1178 {
1179 
1180 	post_obsolete(mdoc);
1181 	if (mdoc->last->type == ROFFT_BLOCK)
1182 		mdoc->last->norm->Es = mdoc->last_es;
1183 }
1184 
1185 static void
1186 post_es(POST_ARGS)
1187 {
1188 
1189 	post_obsolete(mdoc);
1190 	mdoc->last_es = mdoc->last;
1191 }
1192 
1193 static void
1194 post_xx(POST_ARGS)
1195 {
1196 	struct roff_node	*n;
1197 	const char		*os;
1198 
1199 	n = mdoc->last;
1200 	switch (n->tok) {
1201 	case MDOC_Bsx:
1202 		os = "BSD/OS";
1203 		break;
1204 	case MDOC_Dx:
1205 		os = "DragonFly";
1206 		break;
1207 	case MDOC_Fx:
1208 		os = "FreeBSD";
1209 		break;
1210 	case MDOC_Nx:
1211 		os = "NetBSD";
1212 		break;
1213 	case MDOC_Ox:
1214 		os = "OpenBSD";
1215 		break;
1216 	case MDOC_Ux:
1217 		os = "UNIX";
1218 		break;
1219 	default:
1220 		abort();
1221 	}
1222 	mdoc->next = ROFF_NEXT_CHILD;
1223 	roff_word_alloc(mdoc, n->line, n->pos, os);
1224 	mdoc->last->flags |= NODE_NOSRC;
1225 	mdoc->last = n;
1226 }
1227 
1228 static void
1229 post_it(POST_ARGS)
1230 {
1231 	struct roff_node *nbl, *nit, *nch;
1232 	int		  i, cols;
1233 	enum mdoc_list	  lt;
1234 
1235 	post_prevpar(mdoc);
1236 
1237 	nit = mdoc->last;
1238 	if (nit->type != ROFFT_BLOCK)
1239 		return;
1240 
1241 	nbl = nit->parent->parent;
1242 	lt = nbl->norm->Bl.type;
1243 
1244 	switch (lt) {
1245 	case LIST_tag:
1246 	case LIST_hang:
1247 	case LIST_ohang:
1248 	case LIST_inset:
1249 	case LIST_diag:
1250 		if (nit->head->child == NULL)
1251 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1252 			    mdoc->parse, nit->line, nit->pos,
1253 			    "Bl -%s It",
1254 			    mdoc_argnames[nbl->args->argv[0].arg]);
1255 		break;
1256 	case LIST_bullet:
1257 	case LIST_dash:
1258 	case LIST_enum:
1259 	case LIST_hyphen:
1260 		if (nit->body == NULL || nit->body->child == NULL)
1261 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
1262 			    mdoc->parse, nit->line, nit->pos,
1263 			    "Bl -%s It",
1264 			    mdoc_argnames[nbl->args->argv[0].arg]);
1265 		/* FALLTHROUGH */
1266 	case LIST_item:
1267 		if ((nch = nit->head->child) != NULL)
1268 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
1269 			    mdoc->parse, nit->line, nit->pos,
1270 			    "It %s", nch->string == NULL ?
1271 			    mdoc_macronames[nch->tok] : nch->string);
1272 		break;
1273 	case LIST_column:
1274 		cols = (int)nbl->norm->Bl.ncols;
1275 
1276 		assert(nit->head->child == NULL);
1277 
1278 		i = 0;
1279 		for (nch = nit->child; nch != NULL; nch = nch->next)
1280 			if (nch->type == ROFFT_BODY)
1281 				i++;
1282 
1283 		if (i < cols || i > cols + 1)
1284 			mandoc_vmsg(MANDOCERR_BL_COL,
1285 			    mdoc->parse, nit->line, nit->pos,
1286 			    "%d columns, %d cells", cols, i);
1287 		break;
1288 	default:
1289 		abort();
1290 	}
1291 }
1292 
1293 static void
1294 post_bl_block(POST_ARGS)
1295 {
1296 	struct roff_node *n, *ni, *nc;
1297 
1298 	post_prevpar(mdoc);
1299 
1300 	n = mdoc->last;
1301 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
1302 		if (ni->body == NULL)
1303 			continue;
1304 		nc = ni->body->last;
1305 		while (nc != NULL) {
1306 			switch (nc->tok) {
1307 			case MDOC_Pp:
1308 			case MDOC_Lp:
1309 			case MDOC_br:
1310 				break;
1311 			default:
1312 				nc = NULL;
1313 				continue;
1314 			}
1315 			if (ni->next == NULL) {
1316 				mandoc_msg(MANDOCERR_PAR_MOVE,
1317 				    mdoc->parse, nc->line, nc->pos,
1318 				    mdoc_macronames[nc->tok]);
1319 				mdoc_node_relink(mdoc, nc);
1320 			} else if (n->norm->Bl.comp == 0 &&
1321 			    n->norm->Bl.type != LIST_column) {
1322 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
1323 				    mdoc->parse, nc->line, nc->pos,
1324 				    "%s before It",
1325 				    mdoc_macronames[nc->tok]);
1326 				roff_node_delete(mdoc, nc);
1327 			} else
1328 				break;
1329 			nc = ni->body->last;
1330 		}
1331 	}
1332 }
1333 
1334 /*
1335  * If the argument of -offset or -width is a macro,
1336  * replace it with the associated default width.
1337  */
1338 void
1339 rewrite_macro2len(char **arg)
1340 {
1341 	size_t		  width;
1342 	int		  tok;
1343 
1344 	if (*arg == NULL)
1345 		return;
1346 	else if ( ! strcmp(*arg, "Ds"))
1347 		width = 6;
1348 	else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE)
1349 		return;
1350 	else
1351 		width = macro2len(tok);
1352 
1353 	free(*arg);
1354 	mandoc_asprintf(arg, "%zun", width);
1355 }
1356 
1357 static void
1358 post_bl_head(POST_ARGS)
1359 {
1360 	struct roff_node *nbl, *nh, *nch, *nnext;
1361 	struct mdoc_argv *argv;
1362 	int		  i, j;
1363 
1364 	post_bl_norm(mdoc);
1365 
1366 	nh = mdoc->last;
1367 	if (nh->norm->Bl.type != LIST_column) {
1368 		if ((nch = nh->child) == NULL)
1369 			return;
1370 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1371 		    nch->line, nch->pos, "Bl ... %s", nch->string);
1372 		while (nch != NULL) {
1373 			roff_node_delete(mdoc, nch);
1374 			nch = nh->child;
1375 		}
1376 		return;
1377 	}
1378 
1379 	/*
1380 	 * Append old-style lists, where the column width specifiers
1381 	 * trail as macro parameters, to the new-style ("normal-form")
1382 	 * lists where they're argument values following -column.
1383 	 */
1384 
1385 	if (nh->child == NULL)
1386 		return;
1387 
1388 	nbl = nh->parent;
1389 	for (j = 0; j < (int)nbl->args->argc; j++)
1390 		if (nbl->args->argv[j].arg == MDOC_Column)
1391 			break;
1392 
1393 	assert(j < (int)nbl->args->argc);
1394 
1395 	/*
1396 	 * Accommodate for new-style groff column syntax.  Shuffle the
1397 	 * child nodes, all of which must be TEXT, as arguments for the
1398 	 * column field.  Then, delete the head children.
1399 	 */
1400 
1401 	argv = nbl->args->argv + j;
1402 	i = argv->sz;
1403 	for (nch = nh->child; nch != NULL; nch = nch->next)
1404 		argv->sz++;
1405 	argv->value = mandoc_reallocarray(argv->value,
1406 	    argv->sz, sizeof(char *));
1407 
1408 	nh->norm->Bl.ncols = argv->sz;
1409 	nh->norm->Bl.cols = (void *)argv->value;
1410 
1411 	for (nch = nh->child; nch != NULL; nch = nnext) {
1412 		argv->value[i++] = nch->string;
1413 		nch->string = NULL;
1414 		nnext = nch->next;
1415 		roff_node_delete(NULL, nch);
1416 	}
1417 	nh->child = NULL;
1418 }
1419 
1420 static void
1421 post_bl(POST_ARGS)
1422 {
1423 	struct roff_node	*nparent, *nprev; /* of the Bl block */
1424 	struct roff_node	*nblock, *nbody;  /* of the Bl */
1425 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
1426 
1427 	nbody = mdoc->last;
1428 	switch (nbody->type) {
1429 	case ROFFT_BLOCK:
1430 		post_bl_block(mdoc);
1431 		return;
1432 	case ROFFT_HEAD:
1433 		post_bl_head(mdoc);
1434 		return;
1435 	case ROFFT_BODY:
1436 		break;
1437 	default:
1438 		return;
1439 	}
1440 	if (nbody->end != ENDBODY_NOT)
1441 		return;
1442 
1443 	nchild = nbody->child;
1444 	if (nchild == NULL) {
1445 		mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1446 		    nbody->line, nbody->pos, "Bl");
1447 		return;
1448 	}
1449 	while (nchild != NULL) {
1450 		nnext = nchild->next;
1451 		if (nchild->tok == MDOC_It ||
1452 		    (nchild->tok == MDOC_Sm &&
1453 		     nnext != NULL && nnext->tok == MDOC_It)) {
1454 			nchild = nnext;
1455 			continue;
1456 		}
1457 
1458 		/*
1459 		 * In .Bl -column, the first rows may be implicit,
1460 		 * that is, they may not start with .It macros.
1461 		 * Such rows may be followed by nodes generated on the
1462 		 * roff level, for example .TS, which cannot be moved
1463 		 * out of the list.  In that case, wrap such roff nodes
1464 		 * into an implicit row.
1465 		 */
1466 
1467 		if (nchild->prev != NULL) {
1468 			mdoc->last = nchild;
1469 			mdoc->next = ROFF_NEXT_SIBLING;
1470 			roff_block_alloc(mdoc, nchild->line,
1471 			    nchild->pos, MDOC_It);
1472 			roff_head_alloc(mdoc, nchild->line,
1473 			    nchild->pos, MDOC_It);
1474 			mdoc->next = ROFF_NEXT_SIBLING;
1475 			roff_body_alloc(mdoc, nchild->line,
1476 			    nchild->pos, MDOC_It);
1477 			while (nchild->tok != MDOC_It) {
1478 				mdoc_node_relink(mdoc, nchild);
1479 				if ((nchild = nnext) == NULL)
1480 					break;
1481 				nnext = nchild->next;
1482 				mdoc->next = ROFF_NEXT_SIBLING;
1483 			}
1484 			mdoc->last = nbody;
1485 			continue;
1486 		}
1487 
1488 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1489 		    nchild->line, nchild->pos,
1490 		    mdoc_macronames[nchild->tok]);
1491 
1492 		/*
1493 		 * Move the node out of the Bl block.
1494 		 * First, collect all required node pointers.
1495 		 */
1496 
1497 		nblock  = nbody->parent;
1498 		nprev   = nblock->prev;
1499 		nparent = nblock->parent;
1500 
1501 		/*
1502 		 * Unlink this child.
1503 		 */
1504 
1505 		nbody->child = nnext;
1506 		if (nnext == NULL)
1507 			nbody->last  = NULL;
1508 		else
1509 			nnext->prev = NULL;
1510 
1511 		/*
1512 		 * Relink this child.
1513 		 */
1514 
1515 		nchild->parent = nparent;
1516 		nchild->prev   = nprev;
1517 		nchild->next   = nblock;
1518 
1519 		nblock->prev = nchild;
1520 		if (nprev == NULL)
1521 			nparent->child = nchild;
1522 		else
1523 			nprev->next = nchild;
1524 
1525 		nchild = nnext;
1526 	}
1527 }
1528 
1529 static void
1530 post_bk(POST_ARGS)
1531 {
1532 	struct roff_node	*n;
1533 
1534 	n = mdoc->last;
1535 
1536 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1537 		mandoc_msg(MANDOCERR_BLK_EMPTY,
1538 		    mdoc->parse, n->line, n->pos, "Bk");
1539 		roff_node_delete(mdoc, n);
1540 	}
1541 }
1542 
1543 static void
1544 post_sm(POST_ARGS)
1545 {
1546 	struct roff_node	*nch;
1547 
1548 	nch = mdoc->last->child;
1549 
1550 	if (nch == NULL) {
1551 		mdoc->flags ^= MDOC_SMOFF;
1552 		return;
1553 	}
1554 
1555 	assert(nch->type == ROFFT_TEXT);
1556 
1557 	if ( ! strcmp(nch->string, "on")) {
1558 		mdoc->flags &= ~MDOC_SMOFF;
1559 		return;
1560 	}
1561 	if ( ! strcmp(nch->string, "off")) {
1562 		mdoc->flags |= MDOC_SMOFF;
1563 		return;
1564 	}
1565 
1566 	mandoc_vmsg(MANDOCERR_SM_BAD,
1567 	    mdoc->parse, nch->line, nch->pos,
1568 	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
1569 	mdoc_node_relink(mdoc, nch);
1570 	return;
1571 }
1572 
1573 static void
1574 post_root(POST_ARGS)
1575 {
1576 	struct roff_node *n;
1577 
1578 	/* Add missing prologue data. */
1579 
1580 	if (mdoc->meta.date == NULL)
1581 		mdoc->meta.date = mdoc->quick ?
1582 		    mandoc_strdup("") :
1583 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
1584 
1585 	if (mdoc->meta.title == NULL) {
1586 		mandoc_msg(MANDOCERR_DT_NOTITLE,
1587 		    mdoc->parse, 0, 0, "EOF");
1588 		mdoc->meta.title = mandoc_strdup("UNTITLED");
1589 	}
1590 
1591 	if (mdoc->meta.vol == NULL)
1592 		mdoc->meta.vol = mandoc_strdup("LOCAL");
1593 
1594 	if (mdoc->meta.os == NULL) {
1595 		mandoc_msg(MANDOCERR_OS_MISSING,
1596 		    mdoc->parse, 0, 0, NULL);
1597 		mdoc->meta.os = mandoc_strdup("");
1598 	}
1599 
1600 	/* Check that we begin with a proper `Sh'. */
1601 
1602 	n = mdoc->first->child;
1603 	while (n != NULL && n->tok != TOKEN_NONE &&
1604 	    mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1605 		n = n->next;
1606 
1607 	if (n == NULL)
1608 		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1609 	else if (n->tok != MDOC_Sh)
1610 		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1611 		    n->line, n->pos, mdoc_macronames[n->tok]);
1612 }
1613 
1614 static void
1615 post_rs(POST_ARGS)
1616 {
1617 	struct roff_node *np, *nch, *next, *prev;
1618 	int		  i, j;
1619 
1620 	np = mdoc->last;
1621 
1622 	if (np->type != ROFFT_BODY)
1623 		return;
1624 
1625 	if (np->child == NULL) {
1626 		mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
1627 		    np->line, np->pos, "Rs");
1628 		return;
1629 	}
1630 
1631 	/*
1632 	 * The full `Rs' block needs special handling to order the
1633 	 * sub-elements according to `rsord'.  Pick through each element
1634 	 * and correctly order it.  This is an insertion sort.
1635 	 */
1636 
1637 	next = NULL;
1638 	for (nch = np->child->next; nch != NULL; nch = next) {
1639 		/* Determine order number of this child. */
1640 		for (i = 0; i < RSORD_MAX; i++)
1641 			if (rsord[i] == nch->tok)
1642 				break;
1643 
1644 		if (i == RSORD_MAX) {
1645 			mandoc_msg(MANDOCERR_RS_BAD,
1646 			    mdoc->parse, nch->line, nch->pos,
1647 			    mdoc_macronames[nch->tok]);
1648 			i = -1;
1649 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
1650 			np->norm->Rs.quote_T++;
1651 
1652 		/*
1653 		 * Remove this child from the chain.  This somewhat
1654 		 * repeats roff_node_unlink(), but since we're
1655 		 * just re-ordering, there's no need for the
1656 		 * full unlink process.
1657 		 */
1658 
1659 		if ((next = nch->next) != NULL)
1660 			next->prev = nch->prev;
1661 
1662 		if ((prev = nch->prev) != NULL)
1663 			prev->next = nch->next;
1664 
1665 		nch->prev = nch->next = NULL;
1666 
1667 		/*
1668 		 * Scan back until we reach a node that's
1669 		 * to be ordered before this child.
1670 		 */
1671 
1672 		for ( ; prev ; prev = prev->prev) {
1673 			/* Determine order of `prev'. */
1674 			for (j = 0; j < RSORD_MAX; j++)
1675 				if (rsord[j] == prev->tok)
1676 					break;
1677 			if (j == RSORD_MAX)
1678 				j = -1;
1679 
1680 			if (j <= i)
1681 				break;
1682 		}
1683 
1684 		/*
1685 		 * Set this child back into its correct place
1686 		 * in front of the `prev' node.
1687 		 */
1688 
1689 		nch->prev = prev;
1690 
1691 		if (prev == NULL) {
1692 			np->child->prev = nch;
1693 			nch->next = np->child;
1694 			np->child = nch;
1695 		} else {
1696 			if (prev->next)
1697 				prev->next->prev = nch;
1698 			nch->next = prev->next;
1699 			prev->next = nch;
1700 		}
1701 	}
1702 }
1703 
1704 /*
1705  * For some arguments of some macros,
1706  * convert all breakable hyphens into ASCII_HYPH.
1707  */
1708 static void
1709 post_hyph(POST_ARGS)
1710 {
1711 	struct roff_node	*nch;
1712 	char			*cp;
1713 
1714 	for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1715 		if (nch->type != ROFFT_TEXT)
1716 			continue;
1717 		cp = nch->string;
1718 		if (*cp == '\0')
1719 			continue;
1720 		while (*(++cp) != '\0')
1721 			if (*cp == '-' &&
1722 			    isalpha((unsigned char)cp[-1]) &&
1723 			    isalpha((unsigned char)cp[1]))
1724 				*cp = ASCII_HYPH;
1725 	}
1726 }
1727 
1728 static void
1729 post_ns(POST_ARGS)
1730 {
1731 
1732 	if (mdoc->last->flags & NODE_LINE)
1733 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1734 		    mdoc->last->line, mdoc->last->pos, NULL);
1735 }
1736 
1737 static void
1738 post_sh(POST_ARGS)
1739 {
1740 
1741 	post_ignpar(mdoc);
1742 
1743 	switch (mdoc->last->type) {
1744 	case ROFFT_HEAD:
1745 		post_sh_head(mdoc);
1746 		break;
1747 	case ROFFT_BODY:
1748 		switch (mdoc->lastsec)  {
1749 		case SEC_NAME:
1750 			post_sh_name(mdoc);
1751 			break;
1752 		case SEC_SEE_ALSO:
1753 			post_sh_see_also(mdoc);
1754 			break;
1755 		case SEC_AUTHORS:
1756 			post_sh_authors(mdoc);
1757 			break;
1758 		default:
1759 			break;
1760 		}
1761 		break;
1762 	default:
1763 		break;
1764 	}
1765 }
1766 
1767 static void
1768 post_sh_name(POST_ARGS)
1769 {
1770 	struct roff_node *n;
1771 	int hasnm, hasnd;
1772 
1773 	hasnm = hasnd = 0;
1774 
1775 	for (n = mdoc->last->child; n != NULL; n = n->next) {
1776 		switch (n->tok) {
1777 		case MDOC_Nm:
1778 			if (hasnm && n->child != NULL)
1779 				mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
1780 				    mdoc->parse, n->line, n->pos,
1781 				    "Nm %s", n->child->string);
1782 			hasnm = 1;
1783 			continue;
1784 		case MDOC_Nd:
1785 			hasnd = 1;
1786 			if (n->next != NULL)
1787 				mandoc_msg(MANDOCERR_NAMESEC_ND,
1788 				    mdoc->parse, n->line, n->pos, NULL);
1789 			break;
1790 		case TOKEN_NONE:
1791 			if (n->type == ROFFT_TEXT &&
1792 			    n->string[0] == ',' && n->string[1] == '\0' &&
1793 			    n->next != NULL && n->next->tok == MDOC_Nm) {
1794 				n = n->next;
1795 				continue;
1796 			}
1797 			/* FALLTHROUGH */
1798 		default:
1799 			mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1800 			    n->line, n->pos, mdoc_macronames[n->tok]);
1801 			continue;
1802 		}
1803 		break;
1804 	}
1805 
1806 	if ( ! hasnm)
1807 		mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
1808 		    mdoc->last->line, mdoc->last->pos, NULL);
1809 	if ( ! hasnd)
1810 		mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
1811 		    mdoc->last->line, mdoc->last->pos, NULL);
1812 }
1813 
1814 static void
1815 post_sh_see_also(POST_ARGS)
1816 {
1817 	const struct roff_node	*n;
1818 	const char		*name, *sec;
1819 	const char		*lastname, *lastsec, *lastpunct;
1820 	int			 cmp;
1821 
1822 	n = mdoc->last->child;
1823 	lastname = lastsec = lastpunct = NULL;
1824 	while (n != NULL) {
1825 		if (n->tok != MDOC_Xr ||
1826 		    n->child == NULL ||
1827 		    n->child->next == NULL)
1828 			break;
1829 
1830 		/* Process one .Xr node. */
1831 
1832 		name = n->child->string;
1833 		sec = n->child->next->string;
1834 		if (lastsec != NULL) {
1835 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
1836 				mandoc_vmsg(MANDOCERR_XR_PUNCT,
1837 				    mdoc->parse, n->line, n->pos,
1838 				    "%s before %s(%s)", lastpunct,
1839 				    name, sec);
1840 			cmp = strcmp(lastsec, sec);
1841 			if (cmp > 0)
1842 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1843 				    mdoc->parse, n->line, n->pos,
1844 				    "%s(%s) after %s(%s)", name,
1845 				    sec, lastname, lastsec);
1846 			else if (cmp == 0 &&
1847 			    strcasecmp(lastname, name) > 0)
1848 				mandoc_vmsg(MANDOCERR_XR_ORDER,
1849 				    mdoc->parse, n->line, n->pos,
1850 				    "%s after %s", name, lastname);
1851 		}
1852 		lastname = name;
1853 		lastsec = sec;
1854 
1855 		/* Process the following node. */
1856 
1857 		n = n->next;
1858 		if (n == NULL)
1859 			break;
1860 		if (n->tok == MDOC_Xr) {
1861 			lastpunct = "none";
1862 			continue;
1863 		}
1864 		if (n->type != ROFFT_TEXT)
1865 			break;
1866 		for (name = n->string; *name != '\0'; name++)
1867 			if (isalpha((const unsigned char)*name))
1868 				return;
1869 		lastpunct = n->string;
1870 		if (n->next == NULL)
1871 			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
1872 			    n->line, n->pos, "%s after %s(%s)",
1873 			    lastpunct, lastname, lastsec);
1874 		n = n->next;
1875 	}
1876 }
1877 
1878 static int
1879 child_an(const struct roff_node *n)
1880 {
1881 
1882 	for (n = n->child; n != NULL; n = n->next)
1883 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1884 			return 1;
1885 	return 0;
1886 }
1887 
1888 static void
1889 post_sh_authors(POST_ARGS)
1890 {
1891 
1892 	if ( ! child_an(mdoc->last))
1893 		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
1894 		    mdoc->last->line, mdoc->last->pos, NULL);
1895 }
1896 
1897 static void
1898 post_sh_head(POST_ARGS)
1899 {
1900 	struct roff_node	*nch;
1901 	const char		*goodsec;
1902 	enum roff_sec		 sec;
1903 
1904 	/*
1905 	 * Process a new section.  Sections are either "named" or
1906 	 * "custom".  Custom sections are user-defined, while named ones
1907 	 * follow a conventional order and may only appear in certain
1908 	 * manual sections.
1909 	 */
1910 
1911 	sec = mdoc->last->sec;
1912 
1913 	/* The NAME should be first. */
1914 
1915 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1916 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1917 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
1918 		    sec != SEC_CUSTOM ? secnames[sec] :
1919 		    (nch = mdoc->last->child) == NULL ? "" :
1920 		    nch->type == ROFFT_TEXT ? nch->string :
1921 		    mdoc_macronames[nch->tok]);
1922 
1923 	/* The SYNOPSIS gets special attention in other areas. */
1924 
1925 	if (sec == SEC_SYNOPSIS) {
1926 		roff_setreg(mdoc->roff, "nS", 1, '=');
1927 		mdoc->flags |= MDOC_SYNOPSIS;
1928 	} else {
1929 		roff_setreg(mdoc->roff, "nS", 0, '=');
1930 		mdoc->flags &= ~MDOC_SYNOPSIS;
1931 	}
1932 
1933 	/* Mark our last section. */
1934 
1935 	mdoc->lastsec = sec;
1936 
1937 	/* We don't care about custom sections after this. */
1938 
1939 	if (sec == SEC_CUSTOM)
1940 		return;
1941 
1942 	/*
1943 	 * Check whether our non-custom section is being repeated or is
1944 	 * out of order.
1945 	 */
1946 
1947 	if (sec == mdoc->lastnamed)
1948 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
1949 		    mdoc->last->line, mdoc->last->pos,
1950 		    "Sh %s", secnames[sec]);
1951 
1952 	if (sec < mdoc->lastnamed)
1953 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
1954 		    mdoc->last->line, mdoc->last->pos,
1955 		    "Sh %s", secnames[sec]);
1956 
1957 	/* Mark the last named section. */
1958 
1959 	mdoc->lastnamed = sec;
1960 
1961 	/* Check particular section/manual conventions. */
1962 
1963 	if (mdoc->meta.msec == NULL)
1964 		return;
1965 
1966 	goodsec = NULL;
1967 	switch (sec) {
1968 	case SEC_ERRORS:
1969 		if (*mdoc->meta.msec == '4')
1970 			break;
1971 		goodsec = "2, 3, 4, 9";
1972 		/* FALLTHROUGH */
1973 	case SEC_RETURN_VALUES:
1974 	case SEC_LIBRARY:
1975 		if (*mdoc->meta.msec == '2')
1976 			break;
1977 		if (*mdoc->meta.msec == '3')
1978 			break;
1979 		if (NULL == goodsec)
1980 			goodsec = "2, 3, 9";
1981 		/* FALLTHROUGH */
1982 	case SEC_CONTEXT:
1983 		if (*mdoc->meta.msec == '9')
1984 			break;
1985 		if (NULL == goodsec)
1986 			goodsec = "9";
1987 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
1988 		    mdoc->last->line, mdoc->last->pos,
1989 		    "Sh %s for %s only", secnames[sec], goodsec);
1990 		break;
1991 	default:
1992 		break;
1993 	}
1994 }
1995 
1996 static void
1997 post_xr(POST_ARGS)
1998 {
1999 	struct roff_node *n, *nch;
2000 
2001 	n = mdoc->last;
2002 	nch = n->child;
2003 	if (nch->next == NULL) {
2004 		mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2005 		    n->line, n->pos, "Xr %s", nch->string);
2006 		return;
2007 	}
2008 	assert(nch->next == n->last);
2009 }
2010 
2011 static void
2012 post_ignpar(POST_ARGS)
2013 {
2014 	struct roff_node *np;
2015 
2016 	switch (mdoc->last->type) {
2017 	case ROFFT_HEAD:
2018 		post_hyph(mdoc);
2019 		return;
2020 	case ROFFT_BODY:
2021 		break;
2022 	default:
2023 		return;
2024 	}
2025 
2026 	if ((np = mdoc->last->child) != NULL)
2027 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2028 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
2029 			    mdoc->parse, np->line, np->pos,
2030 			    "%s after %s", mdoc_macronames[np->tok],
2031 			    mdoc_macronames[mdoc->last->tok]);
2032 			roff_node_delete(mdoc, np);
2033 		}
2034 
2035 	if ((np = mdoc->last->last) != NULL)
2036 		if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) {
2037 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2038 			    np->line, np->pos, "%s at the end of %s",
2039 			    mdoc_macronames[np->tok],
2040 			    mdoc_macronames[mdoc->last->tok]);
2041 			roff_node_delete(mdoc, np);
2042 		}
2043 }
2044 
2045 static void
2046 post_prevpar(POST_ARGS)
2047 {
2048 	struct roff_node *n;
2049 
2050 	n = mdoc->last;
2051 	if (NULL == n->prev)
2052 		return;
2053 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
2054 		return;
2055 
2056 	/*
2057 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
2058 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
2059 	 */
2060 
2061 	if (n->prev->tok != MDOC_Pp &&
2062 	    n->prev->tok != MDOC_Lp &&
2063 	    n->prev->tok != MDOC_br)
2064 		return;
2065 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
2066 		return;
2067 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
2068 		return;
2069 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
2070 		return;
2071 
2072 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2073 	    n->prev->line, n->prev->pos,
2074 	    "%s before %s", mdoc_macronames[n->prev->tok],
2075 	    mdoc_macronames[n->tok]);
2076 	roff_node_delete(mdoc, n->prev);
2077 }
2078 
2079 static void
2080 post_par(POST_ARGS)
2081 {
2082 	struct roff_node *np;
2083 
2084 	np = mdoc->last;
2085 	if (np->tok != MDOC_br && np->tok != MDOC_sp)
2086 		post_prevpar(mdoc);
2087 
2088 	if (np->tok == MDOC_sp) {
2089 		if (np->child != NULL && np->child->next != NULL)
2090 			mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2091 			    np->child->next->line, np->child->next->pos,
2092 			    "sp ... %s", np->child->next->string);
2093 	} else if (np->child != NULL)
2094 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
2095 		    mdoc->parse, np->line, np->pos, "%s %s",
2096 		    mdoc_macronames[np->tok], np->child->string);
2097 
2098 	if ((np = mdoc->last->prev) == NULL) {
2099 		np = mdoc->last->parent;
2100 		if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
2101 			return;
2102 	} else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp &&
2103 	    (mdoc->last->tok != MDOC_br ||
2104 	     (np->tok != MDOC_sp && np->tok != MDOC_br)))
2105 		return;
2106 
2107 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2108 	    mdoc->last->line, mdoc->last->pos,
2109 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
2110 	    mdoc_macronames[np->tok]);
2111 	roff_node_delete(mdoc, mdoc->last);
2112 }
2113 
2114 static void
2115 post_dd(POST_ARGS)
2116 {
2117 	struct roff_node *n;
2118 	char		 *datestr;
2119 
2120 	n = mdoc->last;
2121 	n->flags |= NODE_NOPRT;
2122 
2123 	if (mdoc->meta.date != NULL) {
2124 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2125 		    n->line, n->pos, "Dd");
2126 		free(mdoc->meta.date);
2127 	} else if (mdoc->flags & MDOC_PBODY)
2128 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2129 		    n->line, n->pos, "Dd");
2130 	else if (mdoc->meta.title != NULL)
2131 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2132 		    n->line, n->pos, "Dd after Dt");
2133 	else if (mdoc->meta.os != NULL)
2134 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2135 		    n->line, n->pos, "Dd after Os");
2136 
2137 	if (n->child == NULL || n->child->string[0] == '\0') {
2138 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
2139 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
2140 		return;
2141 	}
2142 
2143 	datestr = NULL;
2144 	deroff(&datestr, n);
2145 	if (mdoc->quick)
2146 		mdoc->meta.date = datestr;
2147 	else {
2148 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
2149 		    datestr, n->line, n->pos);
2150 		free(datestr);
2151 	}
2152 }
2153 
2154 static void
2155 post_dt(POST_ARGS)
2156 {
2157 	struct roff_node *nn, *n;
2158 	const char	 *cp;
2159 	char		 *p;
2160 
2161 	n = mdoc->last;
2162 	n->flags |= NODE_NOPRT;
2163 
2164 	if (mdoc->flags & MDOC_PBODY) {
2165 		mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2166 		    n->line, n->pos, "Dt");
2167 		return;
2168 	}
2169 
2170 	if (mdoc->meta.title != NULL)
2171 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2172 		    n->line, n->pos, "Dt");
2173 	else if (mdoc->meta.os != NULL)
2174 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2175 		    n->line, n->pos, "Dt after Os");
2176 
2177 	free(mdoc->meta.title);
2178 	free(mdoc->meta.msec);
2179 	free(mdoc->meta.vol);
2180 	free(mdoc->meta.arch);
2181 
2182 	mdoc->meta.title = NULL;
2183 	mdoc->meta.msec = NULL;
2184 	mdoc->meta.vol = NULL;
2185 	mdoc->meta.arch = NULL;
2186 
2187 	/* Mandatory first argument: title. */
2188 
2189 	nn = n->child;
2190 	if (nn == NULL || *nn->string == '\0') {
2191 		mandoc_msg(MANDOCERR_DT_NOTITLE,
2192 		    mdoc->parse, n->line, n->pos, "Dt");
2193 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2194 	} else {
2195 		mdoc->meta.title = mandoc_strdup(nn->string);
2196 
2197 		/* Check that all characters are uppercase. */
2198 
2199 		for (p = nn->string; *p != '\0'; p++)
2200 			if (islower((unsigned char)*p)) {
2201 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
2202 				    mdoc->parse, nn->line,
2203 				    nn->pos + (p - nn->string),
2204 				    "Dt %s", nn->string);
2205 				break;
2206 			}
2207 	}
2208 
2209 	/* Mandatory second argument: section. */
2210 
2211 	if (nn != NULL)
2212 		nn = nn->next;
2213 
2214 	if (nn == NULL) {
2215 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2216 		    mdoc->parse, n->line, n->pos,
2217 		    "Dt %s", mdoc->meta.title);
2218 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2219 		return;  /* msec and arch remain NULL. */
2220 	}
2221 
2222 	mdoc->meta.msec = mandoc_strdup(nn->string);
2223 
2224 	/* Infer volume title from section number. */
2225 
2226 	cp = mandoc_a2msec(nn->string);
2227 	if (cp == NULL) {
2228 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2229 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2230 		mdoc->meta.vol = mandoc_strdup(nn->string);
2231 	} else
2232 		mdoc->meta.vol = mandoc_strdup(cp);
2233 
2234 	/* Optional third argument: architecture. */
2235 
2236 	if ((nn = nn->next) == NULL)
2237 		return;
2238 
2239 	for (p = nn->string; *p != '\0'; p++)
2240 		*p = tolower((unsigned char)*p);
2241 	mdoc->meta.arch = mandoc_strdup(nn->string);
2242 
2243 	/* Ignore fourth and later arguments. */
2244 
2245 	if ((nn = nn->next) != NULL)
2246 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2247 		    nn->line, nn->pos, "Dt ... %s", nn->string);
2248 }
2249 
2250 static void
2251 post_bx(POST_ARGS)
2252 {
2253 	struct roff_node	*n, *nch;
2254 
2255 	n = mdoc->last;
2256 	nch = n->child;
2257 
2258 	if (nch != NULL) {
2259 		mdoc->last = nch;
2260 		nch = nch->next;
2261 		mdoc->next = ROFF_NEXT_SIBLING;
2262 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2263 		mdoc->last->flags |= NODE_NOSRC;
2264 		mdoc->next = ROFF_NEXT_SIBLING;
2265 	} else
2266 		mdoc->next = ROFF_NEXT_CHILD;
2267 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2268 	mdoc->last->flags |= NODE_NOSRC;
2269 
2270 	if (nch == NULL) {
2271 		mdoc->last = n;
2272 		return;
2273 	}
2274 
2275 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2276 	mdoc->last->flags |= NODE_NOSRC;
2277 	mdoc->next = ROFF_NEXT_SIBLING;
2278 	roff_word_alloc(mdoc, n->line, n->pos, "-");
2279 	mdoc->last->flags |= NODE_NOSRC;
2280 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2281 	mdoc->last->flags |= NODE_NOSRC;
2282 	mdoc->last = n;
2283 
2284 	/*
2285 	 * Make `Bx's second argument always start with an uppercase
2286 	 * letter.  Groff checks if it's an "accepted" term, but we just
2287 	 * uppercase blindly.
2288 	 */
2289 
2290 	*nch->string = (char)toupper((unsigned char)*nch->string);
2291 }
2292 
2293 static void
2294 post_os(POST_ARGS)
2295 {
2296 #ifndef OSNAME
2297 	struct utsname	  utsname;
2298 	static char	 *defbuf;
2299 #endif
2300 	struct roff_node *n;
2301 
2302 	n = mdoc->last;
2303 	n->flags |= NODE_NOPRT;
2304 
2305 	if (mdoc->meta.os != NULL)
2306 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2307 		    n->line, n->pos, "Os");
2308 	else if (mdoc->flags & MDOC_PBODY)
2309 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2310 		    n->line, n->pos, "Os");
2311 
2312 	/*
2313 	 * Set the operating system by way of the `Os' macro.
2314 	 * The order of precedence is:
2315 	 * 1. the argument of the `Os' macro, unless empty
2316 	 * 2. the -Ios=foo command line argument, if provided
2317 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2318 	 * 4. "sysname release" from uname(3)
2319 	 */
2320 
2321 	free(mdoc->meta.os);
2322 	mdoc->meta.os = NULL;
2323 	deroff(&mdoc->meta.os, n);
2324 	if (mdoc->meta.os)
2325 		return;
2326 
2327 	if (mdoc->defos) {
2328 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
2329 		return;
2330 	}
2331 
2332 #ifdef OSNAME
2333 	mdoc->meta.os = mandoc_strdup(OSNAME);
2334 #else /*!OSNAME */
2335 	if (defbuf == NULL) {
2336 		if (uname(&utsname) == -1) {
2337 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2338 			    n->line, n->pos, "Os");
2339 			defbuf = mandoc_strdup("UNKNOWN");
2340 		} else
2341 			mandoc_asprintf(&defbuf, "%s %s",
2342 			    utsname.sysname, utsname.release);
2343 	}
2344 	mdoc->meta.os = mandoc_strdup(defbuf);
2345 #endif /*!OSNAME*/
2346 }
2347 
2348 enum roff_sec
2349 mdoc_a2sec(const char *p)
2350 {
2351 	int		 i;
2352 
2353 	for (i = 0; i < (int)SEC__MAX; i++)
2354 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
2355 			return (enum roff_sec)i;
2356 
2357 	return SEC_CUSTOM;
2358 }
2359 
2360 static size_t
2361 macro2len(int macro)
2362 {
2363 
2364 	switch (macro) {
2365 	case MDOC_Ad:
2366 		return 12;
2367 	case MDOC_Ao:
2368 		return 12;
2369 	case MDOC_An:
2370 		return 12;
2371 	case MDOC_Aq:
2372 		return 12;
2373 	case MDOC_Ar:
2374 		return 12;
2375 	case MDOC_Bo:
2376 		return 12;
2377 	case MDOC_Bq:
2378 		return 12;
2379 	case MDOC_Cd:
2380 		return 12;
2381 	case MDOC_Cm:
2382 		return 10;
2383 	case MDOC_Do:
2384 		return 10;
2385 	case MDOC_Dq:
2386 		return 12;
2387 	case MDOC_Dv:
2388 		return 12;
2389 	case MDOC_Eo:
2390 		return 12;
2391 	case MDOC_Em:
2392 		return 10;
2393 	case MDOC_Er:
2394 		return 17;
2395 	case MDOC_Ev:
2396 		return 15;
2397 	case MDOC_Fa:
2398 		return 12;
2399 	case MDOC_Fl:
2400 		return 10;
2401 	case MDOC_Fo:
2402 		return 16;
2403 	case MDOC_Fn:
2404 		return 16;
2405 	case MDOC_Ic:
2406 		return 10;
2407 	case MDOC_Li:
2408 		return 16;
2409 	case MDOC_Ms:
2410 		return 6;
2411 	case MDOC_Nm:
2412 		return 10;
2413 	case MDOC_No:
2414 		return 12;
2415 	case MDOC_Oo:
2416 		return 10;
2417 	case MDOC_Op:
2418 		return 14;
2419 	case MDOC_Pa:
2420 		return 32;
2421 	case MDOC_Pf:
2422 		return 12;
2423 	case MDOC_Po:
2424 		return 12;
2425 	case MDOC_Pq:
2426 		return 12;
2427 	case MDOC_Ql:
2428 		return 16;
2429 	case MDOC_Qo:
2430 		return 12;
2431 	case MDOC_So:
2432 		return 12;
2433 	case MDOC_Sq:
2434 		return 12;
2435 	case MDOC_Sy:
2436 		return 6;
2437 	case MDOC_Sx:
2438 		return 16;
2439 	case MDOC_Tn:
2440 		return 10;
2441 	case MDOC_Va:
2442 		return 12;
2443 	case MDOC_Vt:
2444 		return 12;
2445 	case MDOC_Xr:
2446 		return 10;
2447 	default:
2448 		break;
2449 	};
2450 	return 0;
2451 }
2452