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