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