xref: /dflybsd-src/contrib/mdocml/mdoc_validate.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1*99db7d0eSSascha Wildner /* $Id: mdoc_validate.c,v 1.389 2021/07/18 11:41:23 schwarze Exp $ */
280387638SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org>
4f88b6c16SFranco Fichtner  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
5070c62a6SFranco Fichtner  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
680387638SSascha Wildner  *
780387638SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
880387638SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
980387638SSascha Wildner  * copyright notice and this permission notice appear in all copies.
1080387638SSascha Wildner  *
1154ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1280387638SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1354ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1480387638SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1580387638SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1680387638SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1780387638SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*99db7d0eSSascha Wildner  *
19*99db7d0eSSascha Wildner  * Validation module for mdoc(7) syntax trees used by mandoc(1).
2080387638SSascha Wildner  */
2180387638SSascha Wildner #include "config.h"
2280387638SSascha Wildner 
2354ba9607SSascha Wildner #include <sys/types.h>
2480387638SSascha Wildner #ifndef OSNAME
2580387638SSascha Wildner #include <sys/utsname.h>
2680387638SSascha Wildner #endif
2780387638SSascha Wildner 
2880387638SSascha Wildner #include <assert.h>
2980387638SSascha Wildner #include <ctype.h>
3080387638SSascha Wildner #include <limits.h>
3180387638SSascha Wildner #include <stdio.h>
3280387638SSascha Wildner #include <stdlib.h>
3380387638SSascha Wildner #include <string.h>
3480387638SSascha Wildner #include <time.h>
3580387638SSascha Wildner 
36070c62a6SFranco Fichtner #include "mandoc_aux.h"
3754ba9607SSascha Wildner #include "mandoc.h"
3854ba9607SSascha Wildner #include "mandoc_xr.h"
3954ba9607SSascha Wildner #include "roff.h"
4054ba9607SSascha Wildner #include "mdoc.h"
4180387638SSascha Wildner #include "libmandoc.h"
4254ba9607SSascha Wildner #include "roff_int.h"
4354ba9607SSascha Wildner #include "libmdoc.h"
44*99db7d0eSSascha Wildner #include "tag.h"
4580387638SSascha Wildner 
4680387638SSascha Wildner /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4780387638SSascha Wildner 
4854ba9607SSascha Wildner #define	POST_ARGS struct roff_man *mdoc
4980387638SSascha Wildner 
5080387638SSascha Wildner enum	check_ineq {
5180387638SSascha Wildner 	CHECK_LT,
5280387638SSascha Wildner 	CHECK_GT,
5380387638SSascha Wildner 	CHECK_EQ
5480387638SSascha Wildner };
5580387638SSascha Wildner 
5654ba9607SSascha Wildner typedef	void	(*v_post)(POST_ARGS);
5780387638SSascha Wildner 
5854ba9607SSascha Wildner static	int	 build_list(struct roff_man *, int);
5954ba9607SSascha Wildner static	void	 check_argv(struct roff_man *,
6054ba9607SSascha Wildner 			struct roff_node *, struct mdoc_argv *);
6154ba9607SSascha Wildner static	void	 check_args(struct roff_man *, struct roff_node *);
6254ba9607SSascha Wildner static	void	 check_text(struct roff_man *, int, int, char *);
6354ba9607SSascha Wildner static	void	 check_text_em(struct roff_man *, int, int, char *);
6454ba9607SSascha Wildner static	void	 check_toptext(struct roff_man *, int, int, const char *);
6554ba9607SSascha Wildner static	int	 child_an(const struct roff_node *);
6654ba9607SSascha Wildner static	size_t		macro2len(enum roff_tok);
6754ba9607SSascha Wildner static	void	 rewrite_macro2len(struct roff_man *, char **);
6854ba9607SSascha Wildner static	int	 similar(const char *, const char *);
6980387638SSascha Wildner 
70*99db7d0eSSascha Wildner static	void	 post_abort(POST_ARGS) __attribute__((__noreturn__));
7154ba9607SSascha Wildner static	void	 post_an(POST_ARGS);
7254ba9607SSascha Wildner static	void	 post_an_norm(POST_ARGS);
7354ba9607SSascha Wildner static	void	 post_at(POST_ARGS);
7454ba9607SSascha Wildner static	void	 post_bd(POST_ARGS);
7554ba9607SSascha Wildner static	void	 post_bf(POST_ARGS);
7654ba9607SSascha Wildner static	void	 post_bk(POST_ARGS);
7754ba9607SSascha Wildner static	void	 post_bl(POST_ARGS);
7854ba9607SSascha Wildner static	void	 post_bl_block(POST_ARGS);
7954ba9607SSascha Wildner static	void	 post_bl_head(POST_ARGS);
8054ba9607SSascha Wildner static	void	 post_bl_norm(POST_ARGS);
8154ba9607SSascha Wildner static	void	 post_bx(POST_ARGS);
8254ba9607SSascha Wildner static	void	 post_defaults(POST_ARGS);
8354ba9607SSascha Wildner static	void	 post_display(POST_ARGS);
8454ba9607SSascha Wildner static	void	 post_dd(POST_ARGS);
8554ba9607SSascha Wildner static	void	 post_delim(POST_ARGS);
8654ba9607SSascha Wildner static	void	 post_delim_nb(POST_ARGS);
8754ba9607SSascha Wildner static	void	 post_dt(POST_ARGS);
88*99db7d0eSSascha Wildner static	void	 post_em(POST_ARGS);
8954ba9607SSascha Wildner static	void	 post_en(POST_ARGS);
90*99db7d0eSSascha Wildner static	void	 post_er(POST_ARGS);
9154ba9607SSascha Wildner static	void	 post_es(POST_ARGS);
9254ba9607SSascha Wildner static	void	 post_eoln(POST_ARGS);
9354ba9607SSascha Wildner static	void	 post_ex(POST_ARGS);
9454ba9607SSascha Wildner static	void	 post_fa(POST_ARGS);
95*99db7d0eSSascha Wildner static	void	 post_fl(POST_ARGS);
9654ba9607SSascha Wildner static	void	 post_fn(POST_ARGS);
9754ba9607SSascha Wildner static	void	 post_fname(POST_ARGS);
9854ba9607SSascha Wildner static	void	 post_fo(POST_ARGS);
9954ba9607SSascha Wildner static	void	 post_hyph(POST_ARGS);
10054ba9607SSascha Wildner static	void	 post_it(POST_ARGS);
10154ba9607SSascha Wildner static	void	 post_lb(POST_ARGS);
10254ba9607SSascha Wildner static	void	 post_nd(POST_ARGS);
10354ba9607SSascha Wildner static	void	 post_nm(POST_ARGS);
10454ba9607SSascha Wildner static	void	 post_ns(POST_ARGS);
10554ba9607SSascha Wildner static	void	 post_obsolete(POST_ARGS);
10654ba9607SSascha Wildner static	void	 post_os(POST_ARGS);
10754ba9607SSascha Wildner static	void	 post_par(POST_ARGS);
10854ba9607SSascha Wildner static	void	 post_prevpar(POST_ARGS);
10954ba9607SSascha Wildner static	void	 post_root(POST_ARGS);
11054ba9607SSascha Wildner static	void	 post_rs(POST_ARGS);
11154ba9607SSascha Wildner static	void	 post_rv(POST_ARGS);
112*99db7d0eSSascha Wildner static	void	 post_section(POST_ARGS);
11354ba9607SSascha Wildner static	void	 post_sh(POST_ARGS);
11454ba9607SSascha Wildner static	void	 post_sh_head(POST_ARGS);
11554ba9607SSascha Wildner static	void	 post_sh_name(POST_ARGS);
11654ba9607SSascha Wildner static	void	 post_sh_see_also(POST_ARGS);
11754ba9607SSascha Wildner static	void	 post_sh_authors(POST_ARGS);
11854ba9607SSascha Wildner static	void	 post_sm(POST_ARGS);
11954ba9607SSascha Wildner static	void	 post_st(POST_ARGS);
12054ba9607SSascha Wildner static	void	 post_std(POST_ARGS);
12154ba9607SSascha Wildner static	void	 post_sx(POST_ARGS);
122*99db7d0eSSascha Wildner static	void	 post_tag(POST_ARGS);
123*99db7d0eSSascha Wildner static	void	 post_tg(POST_ARGS);
12454ba9607SSascha Wildner static	void	 post_useless(POST_ARGS);
12554ba9607SSascha Wildner static	void	 post_xr(POST_ARGS);
12654ba9607SSascha Wildner static	void	 post_xx(POST_ARGS);
12780387638SSascha Wildner 
12854ba9607SSascha Wildner static	const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
12954ba9607SSascha Wildner 	post_dd,	/* Dd */
13054ba9607SSascha Wildner 	post_dt,	/* Dt */
13154ba9607SSascha Wildner 	post_os,	/* Os */
13254ba9607SSascha Wildner 	post_sh,	/* Sh */
133*99db7d0eSSascha Wildner 	post_section,	/* Ss */
13454ba9607SSascha Wildner 	post_par,	/* Pp */
13554ba9607SSascha Wildner 	post_display,	/* D1 */
13654ba9607SSascha Wildner 	post_display,	/* Dl */
13754ba9607SSascha Wildner 	post_display,	/* Bd */
13854ba9607SSascha Wildner 	NULL,		/* Ed */
13954ba9607SSascha Wildner 	post_bl,	/* Bl */
14054ba9607SSascha Wildner 	NULL,		/* El */
14154ba9607SSascha Wildner 	post_it,	/* It */
14254ba9607SSascha Wildner 	post_delim_nb,	/* Ad */
14354ba9607SSascha Wildner 	post_an,	/* An */
14454ba9607SSascha Wildner 	NULL,		/* Ap */
14554ba9607SSascha Wildner 	post_defaults,	/* Ar */
14654ba9607SSascha Wildner 	NULL,		/* Cd */
147*99db7d0eSSascha Wildner 	post_tag,	/* Cm */
148*99db7d0eSSascha Wildner 	post_tag,	/* Dv */
149*99db7d0eSSascha Wildner 	post_er,	/* Er */
150*99db7d0eSSascha Wildner 	post_tag,	/* Ev */
15154ba9607SSascha Wildner 	post_ex,	/* Ex */
15254ba9607SSascha Wildner 	post_fa,	/* Fa */
15354ba9607SSascha Wildner 	NULL,		/* Fd */
154*99db7d0eSSascha Wildner 	post_fl,	/* Fl */
15554ba9607SSascha Wildner 	post_fn,	/* Fn */
15654ba9607SSascha Wildner 	post_delim_nb,	/* Ft */
157*99db7d0eSSascha Wildner 	post_tag,	/* Ic */
15854ba9607SSascha Wildner 	post_delim_nb,	/* In */
159*99db7d0eSSascha Wildner 	post_tag,	/* Li */
16054ba9607SSascha Wildner 	post_nd,	/* Nd */
16154ba9607SSascha Wildner 	post_nm,	/* Nm */
16254ba9607SSascha Wildner 	post_delim_nb,	/* Op */
16354ba9607SSascha Wildner 	post_abort,	/* Ot */
16454ba9607SSascha Wildner 	post_defaults,	/* Pa */
16554ba9607SSascha Wildner 	post_rv,	/* Rv */
16654ba9607SSascha Wildner 	post_st,	/* St */
167*99db7d0eSSascha Wildner 	post_tag,	/* Va */
16854ba9607SSascha Wildner 	post_delim_nb,	/* Vt */
16954ba9607SSascha Wildner 	post_xr,	/* Xr */
17054ba9607SSascha Wildner 	NULL,		/* %A */
17154ba9607SSascha Wildner 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
17254ba9607SSascha Wildner 	NULL,		/* %D */
17354ba9607SSascha Wildner 	NULL,		/* %I */
17454ba9607SSascha Wildner 	NULL,		/* %J */
17554ba9607SSascha Wildner 	post_hyph,	/* %N */
17654ba9607SSascha Wildner 	post_hyph,	/* %O */
17754ba9607SSascha Wildner 	NULL,		/* %P */
17854ba9607SSascha Wildner 	post_hyph,	/* %R */
17954ba9607SSascha Wildner 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
18054ba9607SSascha Wildner 	NULL,		/* %V */
18154ba9607SSascha Wildner 	NULL,		/* Ac */
18254ba9607SSascha Wildner 	NULL,		/* Ao */
18354ba9607SSascha Wildner 	post_delim_nb,	/* Aq */
18454ba9607SSascha Wildner 	post_at,	/* At */
18554ba9607SSascha Wildner 	NULL,		/* Bc */
18654ba9607SSascha Wildner 	post_bf,	/* Bf */
18754ba9607SSascha Wildner 	NULL,		/* Bo */
18854ba9607SSascha Wildner 	NULL,		/* Bq */
18954ba9607SSascha Wildner 	post_xx,	/* Bsx */
19054ba9607SSascha Wildner 	post_bx,	/* Bx */
19154ba9607SSascha Wildner 	post_obsolete,	/* Db */
19254ba9607SSascha Wildner 	NULL,		/* Dc */
19354ba9607SSascha Wildner 	NULL,		/* Do */
19454ba9607SSascha Wildner 	NULL,		/* Dq */
19554ba9607SSascha Wildner 	NULL,		/* Ec */
19654ba9607SSascha Wildner 	NULL,		/* Ef */
197*99db7d0eSSascha Wildner 	post_em,	/* Em */
19854ba9607SSascha Wildner 	NULL,		/* Eo */
19954ba9607SSascha Wildner 	post_xx,	/* Fx */
200*99db7d0eSSascha Wildner 	post_tag,	/* Ms */
201*99db7d0eSSascha Wildner 	post_tag,	/* No */
20254ba9607SSascha Wildner 	post_ns,	/* Ns */
20354ba9607SSascha Wildner 	post_xx,	/* Nx */
20454ba9607SSascha Wildner 	post_xx,	/* Ox */
20554ba9607SSascha Wildner 	NULL,		/* Pc */
20654ba9607SSascha Wildner 	NULL,		/* Pf */
20754ba9607SSascha Wildner 	NULL,		/* Po */
20854ba9607SSascha Wildner 	post_delim_nb,	/* Pq */
20954ba9607SSascha Wildner 	NULL,		/* Qc */
21054ba9607SSascha Wildner 	post_delim_nb,	/* Ql */
21154ba9607SSascha Wildner 	NULL,		/* Qo */
21254ba9607SSascha Wildner 	post_delim_nb,	/* Qq */
21354ba9607SSascha Wildner 	NULL,		/* Re */
21454ba9607SSascha Wildner 	post_rs,	/* Rs */
21554ba9607SSascha Wildner 	NULL,		/* Sc */
21654ba9607SSascha Wildner 	NULL,		/* So */
21754ba9607SSascha Wildner 	post_delim_nb,	/* Sq */
21854ba9607SSascha Wildner 	post_sm,	/* Sm */
21954ba9607SSascha Wildner 	post_sx,	/* Sx */
220*99db7d0eSSascha Wildner 	post_em,	/* Sy */
22154ba9607SSascha Wildner 	post_useless,	/* Tn */
22254ba9607SSascha Wildner 	post_xx,	/* Ux */
22354ba9607SSascha Wildner 	NULL,		/* Xc */
22454ba9607SSascha Wildner 	NULL,		/* Xo */
22554ba9607SSascha Wildner 	post_fo,	/* Fo */
22654ba9607SSascha Wildner 	NULL,		/* Fc */
22754ba9607SSascha Wildner 	NULL,		/* Oo */
22854ba9607SSascha Wildner 	NULL,		/* Oc */
22954ba9607SSascha Wildner 	post_bk,	/* Bk */
23054ba9607SSascha Wildner 	NULL,		/* Ek */
23154ba9607SSascha Wildner 	post_eoln,	/* Bt */
23254ba9607SSascha Wildner 	post_obsolete,	/* Hf */
23354ba9607SSascha Wildner 	post_obsolete,	/* Fr */
23454ba9607SSascha Wildner 	post_eoln,	/* Ud */
23554ba9607SSascha Wildner 	post_lb,	/* Lb */
23654ba9607SSascha Wildner 	post_abort,	/* Lp */
23754ba9607SSascha Wildner 	post_delim_nb,	/* Lk */
23854ba9607SSascha Wildner 	post_defaults,	/* Mt */
23954ba9607SSascha Wildner 	post_delim_nb,	/* Brq */
24054ba9607SSascha Wildner 	NULL,		/* Bro */
24154ba9607SSascha Wildner 	NULL,		/* Brc */
24254ba9607SSascha Wildner 	NULL,		/* %C */
24354ba9607SSascha Wildner 	post_es,	/* Es */
24454ba9607SSascha Wildner 	post_en,	/* En */
24554ba9607SSascha Wildner 	post_xx,	/* Dx */
24654ba9607SSascha Wildner 	NULL,		/* %Q */
24754ba9607SSascha Wildner 	NULL,		/* %U */
24854ba9607SSascha Wildner 	NULL,		/* Ta */
249*99db7d0eSSascha Wildner 	post_tg,	/* Tg */
25080387638SSascha Wildner };
25180387638SSascha Wildner 
25280387638SSascha Wildner #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
25380387638SSascha Wildner 
25454ba9607SSascha Wildner static	const enum roff_tok rsord[RSORD_MAX] = {
25580387638SSascha Wildner 	MDOC__A,
25680387638SSascha Wildner 	MDOC__T,
25780387638SSascha Wildner 	MDOC__B,
25880387638SSascha Wildner 	MDOC__I,
25980387638SSascha Wildner 	MDOC__J,
26080387638SSascha Wildner 	MDOC__R,
26180387638SSascha Wildner 	MDOC__N,
26280387638SSascha Wildner 	MDOC__V,
263f88b6c16SFranco Fichtner 	MDOC__U,
26480387638SSascha Wildner 	MDOC__P,
26580387638SSascha Wildner 	MDOC__Q,
26680387638SSascha Wildner 	MDOC__C,
267f88b6c16SFranco Fichtner 	MDOC__D,
268f88b6c16SFranco Fichtner 	MDOC__O
26980387638SSascha Wildner };
27080387638SSascha Wildner 
27160e1e752SSascha Wildner static	const char * const secnames[SEC__MAX] = {
27260e1e752SSascha Wildner 	NULL,
27360e1e752SSascha Wildner 	"NAME",
27460e1e752SSascha Wildner 	"LIBRARY",
27560e1e752SSascha Wildner 	"SYNOPSIS",
27660e1e752SSascha Wildner 	"DESCRIPTION",
277070c62a6SFranco Fichtner 	"CONTEXT",
27860e1e752SSascha Wildner 	"IMPLEMENTATION NOTES",
27960e1e752SSascha Wildner 	"RETURN VALUES",
28060e1e752SSascha Wildner 	"ENVIRONMENT",
28160e1e752SSascha Wildner 	"FILES",
28260e1e752SSascha Wildner 	"EXIT STATUS",
28360e1e752SSascha Wildner 	"EXAMPLES",
28460e1e752SSascha Wildner 	"DIAGNOSTICS",
28560e1e752SSascha Wildner 	"COMPATIBILITY",
28660e1e752SSascha Wildner 	"ERRORS",
28760e1e752SSascha Wildner 	"SEE ALSO",
28860e1e752SSascha Wildner 	"STANDARDS",
28960e1e752SSascha Wildner 	"HISTORY",
29060e1e752SSascha Wildner 	"AUTHORS",
29160e1e752SSascha Wildner 	"CAVEATS",
29260e1e752SSascha Wildner 	"BUGS",
29360e1e752SSascha Wildner 	"SECURITY CONSIDERATIONS",
29460e1e752SSascha Wildner 	NULL
29560e1e752SSascha Wildner };
29680387638SSascha Wildner 
297*99db7d0eSSascha Wildner static	int	  fn_prio = TAG_STRONG;
298*99db7d0eSSascha Wildner 
299070c62a6SFranco Fichtner 
30054ba9607SSascha Wildner /* Validate the subtree rooted at mdoc->last. */
30154ba9607SSascha Wildner void
mdoc_validate(struct roff_man * mdoc)30254ba9607SSascha Wildner mdoc_validate(struct roff_man *mdoc)
30380387638SSascha Wildner {
30454ba9607SSascha Wildner 	struct roff_node *n, *np;
30554ba9607SSascha Wildner 	const v_post *p;
30680387638SSascha Wildner 
30754ba9607SSascha Wildner 	/*
30854ba9607SSascha Wildner 	 * Translate obsolete macros to modern macros first
30954ba9607SSascha Wildner 	 * such that later code does not need to look
31054ba9607SSascha Wildner 	 * for the obsolete versions.
31154ba9607SSascha Wildner 	 */
31280387638SSascha Wildner 
313070c62a6SFranco Fichtner 	n = mdoc->last;
31454ba9607SSascha Wildner 	switch (n->tok) {
31554ba9607SSascha Wildner 	case MDOC_Lp:
31654ba9607SSascha Wildner 		n->tok = MDOC_Pp;
31754ba9607SSascha Wildner 		break;
31854ba9607SSascha Wildner 	case MDOC_Ot:
31954ba9607SSascha Wildner 		post_obsolete(mdoc);
32054ba9607SSascha Wildner 		n->tok = MDOC_Ft;
32154ba9607SSascha Wildner 		break;
32254ba9607SSascha Wildner 	default:
32354ba9607SSascha Wildner 		break;
32454ba9607SSascha Wildner 	}
32580387638SSascha Wildner 
32654ba9607SSascha Wildner 	/*
32754ba9607SSascha Wildner 	 * Iterate over all children, recursing into each one
32854ba9607SSascha Wildner 	 * in turn, depth-first.
32954ba9607SSascha Wildner 	 */
33054ba9607SSascha Wildner 
33154ba9607SSascha Wildner 	mdoc->last = mdoc->last->child;
33254ba9607SSascha Wildner 	while (mdoc->last != NULL) {
33354ba9607SSascha Wildner 		mdoc_validate(mdoc);
33454ba9607SSascha Wildner 		if (mdoc->last == n)
33554ba9607SSascha Wildner 			mdoc->last = mdoc->last->child;
33654ba9607SSascha Wildner 		else
33754ba9607SSascha Wildner 			mdoc->last = mdoc->last->next;
33854ba9607SSascha Wildner 	}
33954ba9607SSascha Wildner 
34054ba9607SSascha Wildner 	/* Finally validate the macro itself. */
34154ba9607SSascha Wildner 
34254ba9607SSascha Wildner 	mdoc->last = n;
34354ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_SIBLING;
344070c62a6SFranco Fichtner 	switch (n->type) {
34554ba9607SSascha Wildner 	case ROFFT_TEXT:
34654ba9607SSascha Wildner 		np = n->parent;
34754ba9607SSascha Wildner 		if (n->sec != SEC_SYNOPSIS ||
34854ba9607SSascha Wildner 		    (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
34954ba9607SSascha Wildner 			check_text(mdoc, n->line, n->pos, n->string);
35054ba9607SSascha Wildner 		if ((n->flags & NODE_NOFILL) == 0 &&
35154ba9607SSascha Wildner 		    (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
35254ba9607SSascha Wildner 		     np->parent->parent->norm->Bl.type != LIST_diag))
35354ba9607SSascha Wildner 			check_text_em(mdoc, n->line, n->pos, n->string);
35454ba9607SSascha Wildner 		if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
35554ba9607SSascha Wildner 		    (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
35654ba9607SSascha Wildner 			check_toptext(mdoc, n->line, n->pos, n->string);
35780387638SSascha Wildner 		break;
35854ba9607SSascha Wildner 	case ROFFT_COMMENT:
35954ba9607SSascha Wildner 	case ROFFT_EQN:
36054ba9607SSascha Wildner 	case ROFFT_TBL:
36180387638SSascha Wildner 		break;
36254ba9607SSascha Wildner 	case ROFFT_ROOT:
36354ba9607SSascha Wildner 		post_root(mdoc);
36480387638SSascha Wildner 		break;
36580387638SSascha Wildner 	default:
36654ba9607SSascha Wildner 		check_args(mdoc, mdoc->last);
36754ba9607SSascha Wildner 
36854ba9607SSascha Wildner 		/*
36954ba9607SSascha Wildner 		 * Closing delimiters are not special at the
37054ba9607SSascha Wildner 		 * beginning of a block, opening delimiters
37154ba9607SSascha Wildner 		 * are not special at the end.
37254ba9607SSascha Wildner 		 */
37354ba9607SSascha Wildner 
37454ba9607SSascha Wildner 		if (n->child != NULL)
37554ba9607SSascha Wildner 			n->child->flags &= ~NODE_DELIMC;
37654ba9607SSascha Wildner 		if (n->last != NULL)
37754ba9607SSascha Wildner 			n->last->flags &= ~NODE_DELIMO;
37854ba9607SSascha Wildner 
37954ba9607SSascha Wildner 		/* Call the macro's postprocessor. */
38054ba9607SSascha Wildner 
38154ba9607SSascha Wildner 		if (n->tok < ROFF_MAX) {
38254ba9607SSascha Wildner 			roff_validate(mdoc);
38354ba9607SSascha Wildner 			break;
38480387638SSascha Wildner 		}
38580387638SSascha Wildner 
38654ba9607SSascha Wildner 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
38754ba9607SSascha Wildner 		p = mdoc_valids + (n->tok - MDOC_Dd);
38854ba9607SSascha Wildner 		if (*p)
38954ba9607SSascha Wildner 			(*p)(mdoc);
39054ba9607SSascha Wildner 		if (mdoc->last == n)
39154ba9607SSascha Wildner 			mdoc_state(mdoc, n);
39254ba9607SSascha Wildner 		break;
39380387638SSascha Wildner 	}
39480387638SSascha Wildner }
39580387638SSascha Wildner 
39680387638SSascha Wildner static void
check_args(struct roff_man * mdoc,struct roff_node * n)39754ba9607SSascha Wildner check_args(struct roff_man *mdoc, struct roff_node *n)
39880387638SSascha Wildner {
39980387638SSascha Wildner 	int		 i;
40080387638SSascha Wildner 
40180387638SSascha Wildner 	if (NULL == n->args)
40280387638SSascha Wildner 		return;
40380387638SSascha Wildner 
40480387638SSascha Wildner 	assert(n->args->argc);
40580387638SSascha Wildner 	for (i = 0; i < (int)n->args->argc; i++)
406f88b6c16SFranco Fichtner 		check_argv(mdoc, n, &n->args->argv[i]);
40780387638SSascha Wildner }
40880387638SSascha Wildner 
40980387638SSascha Wildner static void
check_argv(struct roff_man * mdoc,struct roff_node * n,struct mdoc_argv * v)41054ba9607SSascha Wildner check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
41180387638SSascha Wildner {
41280387638SSascha Wildner 	int		 i;
41380387638SSascha Wildner 
41480387638SSascha Wildner 	for (i = 0; i < (int)v->sz; i++)
415f88b6c16SFranco Fichtner 		check_text(mdoc, v->line, v->pos, v->value[i]);
41680387638SSascha Wildner }
41780387638SSascha Wildner 
41880387638SSascha Wildner static void
check_text(struct roff_man * mdoc,int ln,int pos,char * p)41954ba9607SSascha Wildner check_text(struct roff_man *mdoc, int ln, int pos, char *p)
42080387638SSascha Wildner {
42136342e81SSascha Wildner 	char		*cp;
42280387638SSascha Wildner 
42354ba9607SSascha Wildner 	if (mdoc->last->flags & NODE_NOFILL)
42436342e81SSascha Wildner 		return;
425a4c7eb57SSascha Wildner 
42636342e81SSascha Wildner 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
42754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
42880387638SSascha Wildner }
42980387638SSascha Wildner 
43054ba9607SSascha Wildner static void
check_text_em(struct roff_man * mdoc,int ln,int pos,char * p)43154ba9607SSascha Wildner check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
43280387638SSascha Wildner {
43354ba9607SSascha Wildner 	const struct roff_node	*np, *nn;
43454ba9607SSascha Wildner 	char			*cp;
43580387638SSascha Wildner 
43654ba9607SSascha Wildner 	np = mdoc->last->prev;
43754ba9607SSascha Wildner 	nn = mdoc->last->next;
43880387638SSascha Wildner 
43954ba9607SSascha Wildner 	/* Look for em-dashes wrongly encoded as "--". */
44054ba9607SSascha Wildner 
44154ba9607SSascha Wildner 	for (cp = p; *cp != '\0'; cp++) {
44254ba9607SSascha Wildner 		if (cp[0] != '-' || cp[1] != '-')
44354ba9607SSascha Wildner 			continue;
44454ba9607SSascha Wildner 		cp++;
44554ba9607SSascha Wildner 
44654ba9607SSascha Wildner 		/* Skip input sequences of more than two '-'. */
44754ba9607SSascha Wildner 
44854ba9607SSascha Wildner 		if (cp[1] == '-') {
44954ba9607SSascha Wildner 			while (cp[1] == '-')
45054ba9607SSascha Wildner 				cp++;
45154ba9607SSascha Wildner 			continue;
45254ba9607SSascha Wildner 		}
45354ba9607SSascha Wildner 
45454ba9607SSascha Wildner 		/* Skip "--" directly attached to something else. */
45554ba9607SSascha Wildner 
45654ba9607SSascha Wildner 		if ((cp - p > 1 && cp[-2] != ' ') ||
45754ba9607SSascha Wildner 		    (cp[1] != '\0' && cp[1] != ' '))
45854ba9607SSascha Wildner 			continue;
45954ba9607SSascha Wildner 
46054ba9607SSascha Wildner 		/* Require a letter right before or right afterwards. */
46154ba9607SSascha Wildner 
46254ba9607SSascha Wildner 		if ((cp - p > 2 ?
46354ba9607SSascha Wildner 		     isalpha((unsigned char)cp[-3]) :
46454ba9607SSascha Wildner 		     np != NULL &&
46554ba9607SSascha Wildner 		     np->type == ROFFT_TEXT &&
46654ba9607SSascha Wildner 		     *np->string != '\0' &&
46754ba9607SSascha Wildner 		     isalpha((unsigned char)np->string[
46854ba9607SSascha Wildner 		       strlen(np->string) - 1])) ||
46954ba9607SSascha Wildner 		    (cp[1] != '\0' && cp[2] != '\0' ?
47054ba9607SSascha Wildner 		     isalpha((unsigned char)cp[2]) :
47154ba9607SSascha Wildner 		     nn != NULL &&
47254ba9607SSascha Wildner 		     nn->type == ROFFT_TEXT &&
47354ba9607SSascha Wildner 		     isalpha((unsigned char)*nn->string))) {
47454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_DASHDASH,
47554ba9607SSascha Wildner 			    ln, pos + (int)(cp - p) - 1, NULL);
47680387638SSascha Wildner 			break;
47754ba9607SSascha Wildner 		}
47854ba9607SSascha Wildner 	}
47980387638SSascha Wildner }
48080387638SSascha Wildner 
48154ba9607SSascha Wildner static void
check_toptext(struct roff_man * mdoc,int ln,int pos,const char * p)48254ba9607SSascha Wildner check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
48380387638SSascha Wildner {
48454ba9607SSascha Wildner 	const char	*cp, *cpr;
48554ba9607SSascha Wildner 
48654ba9607SSascha Wildner 	if (*p == '\0')
48754ba9607SSascha Wildner 		return;
48854ba9607SSascha Wildner 
48954ba9607SSascha Wildner 	if ((cp = strstr(p, "OpenBSD")) != NULL)
49054ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
49154ba9607SSascha Wildner 	if ((cp = strstr(p, "NetBSD")) != NULL)
49254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
49354ba9607SSascha Wildner 	if ((cp = strstr(p, "FreeBSD")) != NULL)
49454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
49554ba9607SSascha Wildner 	if ((cp = strstr(p, "DragonFly")) != NULL)
49654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
49754ba9607SSascha Wildner 
49854ba9607SSascha Wildner 	cp = p;
49954ba9607SSascha Wildner 	while ((cp = strstr(cp + 1, "()")) != NULL) {
50054ba9607SSascha Wildner 		for (cpr = cp - 1; cpr >= p; cpr--)
50154ba9607SSascha Wildner 			if (*cpr != '_' && !isalnum((unsigned char)*cpr))
50254ba9607SSascha Wildner 				break;
50354ba9607SSascha Wildner 		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
50454ba9607SSascha Wildner 			cpr++;
50554ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
50654ba9607SSascha Wildner 			    "%.*s()", (int)(cp - cpr), cpr);
50754ba9607SSascha Wildner 		}
50854ba9607SSascha Wildner 	}
50954ba9607SSascha Wildner }
51054ba9607SSascha Wildner 
51154ba9607SSascha Wildner static void
post_abort(POST_ARGS)51254ba9607SSascha Wildner post_abort(POST_ARGS)
51354ba9607SSascha Wildner {
51454ba9607SSascha Wildner 	abort();
51554ba9607SSascha Wildner }
51654ba9607SSascha Wildner 
51754ba9607SSascha Wildner static void
post_delim(POST_ARGS)51854ba9607SSascha Wildner post_delim(POST_ARGS)
51954ba9607SSascha Wildner {
52054ba9607SSascha Wildner 	const struct roff_node	*nch;
52154ba9607SSascha Wildner 	const char		*lc;
52254ba9607SSascha Wildner 	enum mdelim		 delim;
52354ba9607SSascha Wildner 	enum roff_tok		 tok;
52454ba9607SSascha Wildner 
52554ba9607SSascha Wildner 	tok = mdoc->last->tok;
52654ba9607SSascha Wildner 	nch = mdoc->last->last;
52754ba9607SSascha Wildner 	if (nch == NULL || nch->type != ROFFT_TEXT)
52854ba9607SSascha Wildner 		return;
52954ba9607SSascha Wildner 	lc = strchr(nch->string, '\0') - 1;
53054ba9607SSascha Wildner 	if (lc < nch->string)
53154ba9607SSascha Wildner 		return;
53254ba9607SSascha Wildner 	delim = mdoc_isdelim(lc);
53354ba9607SSascha Wildner 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
53454ba9607SSascha Wildner 		return;
53554ba9607SSascha Wildner 	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
53654ba9607SSascha Wildner 	    tok == MDOC_Ss || tok == MDOC_Fo))
53754ba9607SSascha Wildner 		return;
53854ba9607SSascha Wildner 
53954ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_DELIM, nch->line,
54054ba9607SSascha Wildner 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
54154ba9607SSascha Wildner 	    nch == mdoc->last->child ? "" : " ...", nch->string);
54254ba9607SSascha Wildner }
54354ba9607SSascha Wildner 
54454ba9607SSascha Wildner static void
post_delim_nb(POST_ARGS)54554ba9607SSascha Wildner post_delim_nb(POST_ARGS)
54654ba9607SSascha Wildner {
54754ba9607SSascha Wildner 	const struct roff_node	*nch;
54854ba9607SSascha Wildner 	const char		*lc, *cp;
54954ba9607SSascha Wildner 	int			 nw;
55054ba9607SSascha Wildner 	enum mdelim		 delim;
55154ba9607SSascha Wildner 	enum roff_tok		 tok;
55254ba9607SSascha Wildner 
55354ba9607SSascha Wildner 	/*
55454ba9607SSascha Wildner 	 * Find candidates: at least two bytes,
55554ba9607SSascha Wildner 	 * the last one a closing or middle delimiter.
55654ba9607SSascha Wildner 	 */
55754ba9607SSascha Wildner 
55854ba9607SSascha Wildner 	tok = mdoc->last->tok;
55954ba9607SSascha Wildner 	nch = mdoc->last->last;
56054ba9607SSascha Wildner 	if (nch == NULL || nch->type != ROFFT_TEXT)
56154ba9607SSascha Wildner 		return;
56254ba9607SSascha Wildner 	lc = strchr(nch->string, '\0') - 1;
56354ba9607SSascha Wildner 	if (lc <= nch->string)
56454ba9607SSascha Wildner 		return;
56554ba9607SSascha Wildner 	delim = mdoc_isdelim(lc);
56654ba9607SSascha Wildner 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
56754ba9607SSascha Wildner 		return;
56854ba9607SSascha Wildner 
56954ba9607SSascha Wildner 	/*
57054ba9607SSascha Wildner 	 * Reduce false positives by allowing various cases.
57154ba9607SSascha Wildner 	 */
57254ba9607SSascha Wildner 
57354ba9607SSascha Wildner 	/* Escaped delimiters. */
57454ba9607SSascha Wildner 	if (lc > nch->string + 1 && lc[-2] == '\\' &&
57554ba9607SSascha Wildner 	    (lc[-1] == '&' || lc[-1] == 'e'))
57654ba9607SSascha Wildner 		return;
57754ba9607SSascha Wildner 
57854ba9607SSascha Wildner 	/* Specific byte sequences. */
57954ba9607SSascha Wildner 	switch (*lc) {
58054ba9607SSascha Wildner 	case ')':
58154ba9607SSascha Wildner 		for (cp = lc; cp >= nch->string; cp--)
58254ba9607SSascha Wildner 			if (*cp == '(')
58354ba9607SSascha Wildner 				return;
58454ba9607SSascha Wildner 		break;
58554ba9607SSascha Wildner 	case '.':
58654ba9607SSascha Wildner 		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
58754ba9607SSascha Wildner 			return;
58854ba9607SSascha Wildner 		if (lc[-1] == '.')
58954ba9607SSascha Wildner 			return;
59054ba9607SSascha Wildner 		break;
59154ba9607SSascha Wildner 	case ';':
59254ba9607SSascha Wildner 		if (tok == MDOC_Vt)
59354ba9607SSascha Wildner 			return;
59454ba9607SSascha Wildner 		break;
59554ba9607SSascha Wildner 	case '?':
59654ba9607SSascha Wildner 		if (lc[-1] == '?')
59754ba9607SSascha Wildner 			return;
59854ba9607SSascha Wildner 		break;
59954ba9607SSascha Wildner 	case ']':
60054ba9607SSascha Wildner 		for (cp = lc; cp >= nch->string; cp--)
60154ba9607SSascha Wildner 			if (*cp == '[')
60254ba9607SSascha Wildner 				return;
60354ba9607SSascha Wildner 		break;
60454ba9607SSascha Wildner 	case '|':
60554ba9607SSascha Wildner 		if (lc == nch->string + 1 && lc[-1] == '|')
60654ba9607SSascha Wildner 			return;
60754ba9607SSascha Wildner 	default:
60854ba9607SSascha Wildner 		break;
60954ba9607SSascha Wildner 	}
61054ba9607SSascha Wildner 
61154ba9607SSascha Wildner 	/* Exactly two non-alphanumeric bytes. */
61254ba9607SSascha Wildner 	if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
61354ba9607SSascha Wildner 		return;
61454ba9607SSascha Wildner 
61554ba9607SSascha Wildner 	/* At least three alphabetic words with a sentence ending. */
61654ba9607SSascha Wildner 	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
61754ba9607SSascha Wildner 	    tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
61854ba9607SSascha Wildner 		nw = 0;
61954ba9607SSascha Wildner 		for (cp = lc - 1; cp >= nch->string; cp--) {
62054ba9607SSascha Wildner 			if (*cp == ' ') {
62154ba9607SSascha Wildner 				nw++;
62254ba9607SSascha Wildner 				if (cp > nch->string && cp[-1] == ',')
62354ba9607SSascha Wildner 					cp--;
62454ba9607SSascha Wildner 			} else if (isalpha((unsigned int)*cp)) {
62554ba9607SSascha Wildner 				if (nw > 1)
62654ba9607SSascha Wildner 					return;
62754ba9607SSascha Wildner 			} else
62854ba9607SSascha Wildner 				break;
62954ba9607SSascha Wildner 		}
63054ba9607SSascha Wildner 	}
63154ba9607SSascha Wildner 
63254ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
63354ba9607SSascha Wildner 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
63454ba9607SSascha Wildner 	    nch == mdoc->last->child ? "" : " ...", nch->string);
63554ba9607SSascha Wildner }
63654ba9607SSascha Wildner 
63754ba9607SSascha Wildner static void
post_bl_norm(POST_ARGS)63854ba9607SSascha Wildner post_bl_norm(POST_ARGS)
63954ba9607SSascha Wildner {
64054ba9607SSascha Wildner 	struct roff_node *n;
641070c62a6SFranco Fichtner 	struct mdoc_argv *argv, *wa;
642070c62a6SFranco Fichtner 	int		  i;
643070c62a6SFranco Fichtner 	enum mdocargt	  mdoclt;
644070c62a6SFranco Fichtner 	enum mdoc_list	  lt;
64580387638SSascha Wildner 
64654ba9607SSascha Wildner 	n = mdoc->last->parent;
64754ba9607SSascha Wildner 	n->norm->Bl.type = LIST__NONE;
64880387638SSascha Wildner 
64980387638SSascha Wildner 	/*
65080387638SSascha Wildner 	 * First figure out which kind of list to use: bind ourselves to
65180387638SSascha Wildner 	 * the first mentioned list type and warn about any remaining
65280387638SSascha Wildner 	 * ones.  If we find no list type, we default to LIST_item.
65380387638SSascha Wildner 	 */
65480387638SSascha Wildner 
65554ba9607SSascha Wildner 	wa = (n->args == NULL) ? NULL : n->args->argv;
656070c62a6SFranco Fichtner 	mdoclt = MDOC_ARG_MAX;
65780387638SSascha Wildner 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
658070c62a6SFranco Fichtner 		argv = n->args->argv + i;
65980387638SSascha Wildner 		lt = LIST__NONE;
660070c62a6SFranco Fichtner 		switch (argv->arg) {
66180387638SSascha Wildner 		/* Set list types. */
662070c62a6SFranco Fichtner 		case MDOC_Bullet:
66380387638SSascha Wildner 			lt = LIST_bullet;
66480387638SSascha Wildner 			break;
665070c62a6SFranco Fichtner 		case MDOC_Dash:
66680387638SSascha Wildner 			lt = LIST_dash;
66780387638SSascha Wildner 			break;
668070c62a6SFranco Fichtner 		case MDOC_Enum:
66980387638SSascha Wildner 			lt = LIST_enum;
67080387638SSascha Wildner 			break;
671070c62a6SFranco Fichtner 		case MDOC_Hyphen:
67280387638SSascha Wildner 			lt = LIST_hyphen;
67380387638SSascha Wildner 			break;
674070c62a6SFranco Fichtner 		case MDOC_Item:
67580387638SSascha Wildner 			lt = LIST_item;
67680387638SSascha Wildner 			break;
677070c62a6SFranco Fichtner 		case MDOC_Tag:
67880387638SSascha Wildner 			lt = LIST_tag;
67980387638SSascha Wildner 			break;
680070c62a6SFranco Fichtner 		case MDOC_Diag:
68180387638SSascha Wildner 			lt = LIST_diag;
68280387638SSascha Wildner 			break;
683070c62a6SFranco Fichtner 		case MDOC_Hang:
68480387638SSascha Wildner 			lt = LIST_hang;
68580387638SSascha Wildner 			break;
686070c62a6SFranco Fichtner 		case MDOC_Ohang:
68780387638SSascha Wildner 			lt = LIST_ohang;
68880387638SSascha Wildner 			break;
689070c62a6SFranco Fichtner 		case MDOC_Inset:
69080387638SSascha Wildner 			lt = LIST_inset;
69180387638SSascha Wildner 			break;
692070c62a6SFranco Fichtner 		case MDOC_Column:
69380387638SSascha Wildner 			lt = LIST_column;
69480387638SSascha Wildner 			break;
69580387638SSascha Wildner 		/* Set list arguments. */
696070c62a6SFranco Fichtner 		case MDOC_Compact:
697070c62a6SFranco Fichtner 			if (n->norm->Bl.comp)
698070c62a6SFranco Fichtner 				mandoc_msg(MANDOCERR_ARG_REP,
69954ba9607SSascha Wildner 				    argv->line, argv->pos, "Bl -compact");
700070c62a6SFranco Fichtner 			n->norm->Bl.comp = 1;
70180387638SSascha Wildner 			break;
702070c62a6SFranco Fichtner 		case MDOC_Width:
703070c62a6SFranco Fichtner 			wa = argv;
704070c62a6SFranco Fichtner 			if (0 == argv->sz) {
705070c62a6SFranco Fichtner 				mandoc_msg(MANDOCERR_ARG_EMPTY,
70654ba9607SSascha Wildner 				    argv->line, argv->pos, "Bl -width");
707070c62a6SFranco Fichtner 				n->norm->Bl.width = "0n";
70836342e81SSascha Wildner 				break;
70936342e81SSascha Wildner 			}
710070c62a6SFranco Fichtner 			if (NULL != n->norm->Bl.width)
71154ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ARG_REP,
71254ba9607SSascha Wildner 				    argv->line, argv->pos,
71354ba9607SSascha Wildner 				    "Bl -width %s", argv->value[0]);
71454ba9607SSascha Wildner 			rewrite_macro2len(mdoc, argv->value);
715070c62a6SFranco Fichtner 			n->norm->Bl.width = argv->value[0];
71680387638SSascha Wildner 			break;
717070c62a6SFranco Fichtner 		case MDOC_Offset:
718070c62a6SFranco Fichtner 			if (0 == argv->sz) {
719070c62a6SFranco Fichtner 				mandoc_msg(MANDOCERR_ARG_EMPTY,
72054ba9607SSascha Wildner 				    argv->line, argv->pos, "Bl -offset");
72180387638SSascha Wildner 				break;
72280387638SSascha Wildner 			}
723070c62a6SFranco Fichtner 			if (NULL != n->norm->Bl.offs)
72454ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ARG_REP,
72554ba9607SSascha Wildner 				    argv->line, argv->pos,
72654ba9607SSascha Wildner 				    "Bl -offset %s", argv->value[0]);
72754ba9607SSascha Wildner 			rewrite_macro2len(mdoc, argv->value);
728070c62a6SFranco Fichtner 			n->norm->Bl.offs = argv->value[0];
72980387638SSascha Wildner 			break;
73080387638SSascha Wildner 		default:
73180387638SSascha Wildner 			continue;
73280387638SSascha Wildner 		}
733070c62a6SFranco Fichtner 		if (LIST__NONE == lt)
734070c62a6SFranco Fichtner 			continue;
735070c62a6SFranco Fichtner 		mdoclt = argv->arg;
73680387638SSascha Wildner 
73780387638SSascha Wildner 		/* Check: multiple list types. */
73880387638SSascha Wildner 
739070c62a6SFranco Fichtner 		if (LIST__NONE != n->norm->Bl.type) {
74054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
741070c62a6SFranco Fichtner 			    "Bl -%s", mdoc_argnames[argv->arg]);
742070c62a6SFranco Fichtner 			continue;
74380387638SSascha Wildner 		}
74480387638SSascha Wildner 
74580387638SSascha Wildner 		/* The list type should come first. */
74680387638SSascha Wildner 
74780387638SSascha Wildner 		if (n->norm->Bl.width ||
74880387638SSascha Wildner 		    n->norm->Bl.offs ||
74980387638SSascha Wildner 		    n->norm->Bl.comp)
75054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BL_LATETYPE,
75154ba9607SSascha Wildner 			    n->line, n->pos, "Bl -%s",
752070c62a6SFranco Fichtner 			    mdoc_argnames[n->args->argv[0].arg]);
75380387638SSascha Wildner 
754070c62a6SFranco Fichtner 		n->norm->Bl.type = lt;
755070c62a6SFranco Fichtner 		if (LIST_column == lt) {
756070c62a6SFranco Fichtner 			n->norm->Bl.ncols = argv->sz;
757070c62a6SFranco Fichtner 			n->norm->Bl.cols = (void *)argv->value;
758070c62a6SFranco Fichtner 		}
75980387638SSascha Wildner 	}
76080387638SSascha Wildner 
76180387638SSascha Wildner 	/* Allow lists to default to LIST_item. */
76280387638SSascha Wildner 
76380387638SSascha Wildner 	if (LIST__NONE == n->norm->Bl.type) {
76454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
76580387638SSascha Wildner 		n->norm->Bl.type = LIST_item;
76654ba9607SSascha Wildner 		mdoclt = MDOC_Item;
76780387638SSascha Wildner 	}
76880387638SSascha Wildner 
76980387638SSascha Wildner 	/*
77080387638SSascha Wildner 	 * Validate the width field.  Some list types don't need width
77180387638SSascha Wildner 	 * types and should be warned about them.  Others should have it
772f88b6c16SFranco Fichtner 	 * and must also be warned.  Yet others have a default and need
773f88b6c16SFranco Fichtner 	 * no warning.
77480387638SSascha Wildner 	 */
77580387638SSascha Wildner 
77680387638SSascha Wildner 	switch (n->norm->Bl.type) {
777070c62a6SFranco Fichtner 	case LIST_tag:
77854ba9607SSascha Wildner 		if (n->norm->Bl.width == NULL)
77954ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BL_NOWIDTH,
780070c62a6SFranco Fichtner 			    n->line, n->pos, "Bl -tag");
78180387638SSascha Wildner 		break;
782070c62a6SFranco Fichtner 	case LIST_column:
783070c62a6SFranco Fichtner 	case LIST_diag:
784070c62a6SFranco Fichtner 	case LIST_ohang:
785070c62a6SFranco Fichtner 	case LIST_inset:
786070c62a6SFranco Fichtner 	case LIST_item:
78754ba9607SSascha Wildner 		if (n->norm->Bl.width != NULL)
78854ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
78954ba9607SSascha Wildner 			    "Bl -%s", mdoc_argnames[mdoclt]);
79054ba9607SSascha Wildner 		n->norm->Bl.width = NULL;
79180387638SSascha Wildner 		break;
792070c62a6SFranco Fichtner 	case LIST_bullet:
793070c62a6SFranco Fichtner 	case LIST_dash:
794070c62a6SFranco Fichtner 	case LIST_hyphen:
79554ba9607SSascha Wildner 		if (n->norm->Bl.width == NULL)
796f88b6c16SFranco Fichtner 			n->norm->Bl.width = "2n";
797f88b6c16SFranco Fichtner 		break;
798070c62a6SFranco Fichtner 	case LIST_enum:
79954ba9607SSascha Wildner 		if (n->norm->Bl.width == NULL)
800f88b6c16SFranco Fichtner 			n->norm->Bl.width = "3n";
801f88b6c16SFranco Fichtner 		break;
80280387638SSascha Wildner 	default:
80380387638SSascha Wildner 		break;
80480387638SSascha Wildner 	}
80580387638SSascha Wildner }
80680387638SSascha Wildner 
80754ba9607SSascha Wildner static void
post_bd(POST_ARGS)80854ba9607SSascha Wildner post_bd(POST_ARGS)
80980387638SSascha Wildner {
81054ba9607SSascha Wildner 	struct roff_node *n;
811070c62a6SFranco Fichtner 	struct mdoc_argv *argv;
812070c62a6SFranco Fichtner 	int		  i;
813070c62a6SFranco Fichtner 	enum mdoc_disp	  dt;
814070c62a6SFranco Fichtner 
81554ba9607SSascha Wildner 	n = mdoc->last;
81680387638SSascha Wildner 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
817070c62a6SFranco Fichtner 		argv = n->args->argv + i;
81880387638SSascha Wildner 		dt = DISP__NONE;
81980387638SSascha Wildner 
820070c62a6SFranco Fichtner 		switch (argv->arg) {
821070c62a6SFranco Fichtner 		case MDOC_Centred:
822070c62a6SFranco Fichtner 			dt = DISP_centered;
82380387638SSascha Wildner 			break;
824070c62a6SFranco Fichtner 		case MDOC_Ragged:
82580387638SSascha Wildner 			dt = DISP_ragged;
82680387638SSascha Wildner 			break;
827070c62a6SFranco Fichtner 		case MDOC_Unfilled:
82880387638SSascha Wildner 			dt = DISP_unfilled;
82980387638SSascha Wildner 			break;
830070c62a6SFranco Fichtner 		case MDOC_Filled:
83180387638SSascha Wildner 			dt = DISP_filled;
83280387638SSascha Wildner 			break;
833070c62a6SFranco Fichtner 		case MDOC_Literal:
83480387638SSascha Wildner 			dt = DISP_literal;
83580387638SSascha Wildner 			break;
836070c62a6SFranco Fichtner 		case MDOC_File:
83754ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL);
83854ba9607SSascha Wildner 			break;
839070c62a6SFranco Fichtner 		case MDOC_Offset:
840070c62a6SFranco Fichtner 			if (0 == argv->sz) {
841070c62a6SFranco Fichtner 				mandoc_msg(MANDOCERR_ARG_EMPTY,
84254ba9607SSascha Wildner 				    argv->line, argv->pos, "Bd -offset");
84380387638SSascha Wildner 				break;
84480387638SSascha Wildner 			}
845070c62a6SFranco Fichtner 			if (NULL != n->norm->Bd.offs)
84654ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ARG_REP,
84754ba9607SSascha Wildner 				    argv->line, argv->pos,
84854ba9607SSascha Wildner 				    "Bd -offset %s", argv->value[0]);
84954ba9607SSascha Wildner 			rewrite_macro2len(mdoc, argv->value);
850070c62a6SFranco Fichtner 			n->norm->Bd.offs = argv->value[0];
85180387638SSascha Wildner 			break;
852070c62a6SFranco Fichtner 		case MDOC_Compact:
853070c62a6SFranco Fichtner 			if (n->norm->Bd.comp)
854070c62a6SFranco Fichtner 				mandoc_msg(MANDOCERR_ARG_REP,
85554ba9607SSascha Wildner 				    argv->line, argv->pos, "Bd -compact");
856070c62a6SFranco Fichtner 			n->norm->Bd.comp = 1;
85780387638SSascha Wildner 			break;
85880387638SSascha Wildner 		default:
85980387638SSascha Wildner 			abort();
86080387638SSascha Wildner 		}
861070c62a6SFranco Fichtner 		if (DISP__NONE == dt)
862070c62a6SFranco Fichtner 			continue;
86380387638SSascha Wildner 
864070c62a6SFranco Fichtner 		if (DISP__NONE == n->norm->Bd.type)
86580387638SSascha Wildner 			n->norm->Bd.type = dt;
866070c62a6SFranco Fichtner 		else
86754ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
868070c62a6SFranco Fichtner 			    "Bd -%s", mdoc_argnames[argv->arg]);
86980387638SSascha Wildner 	}
87080387638SSascha Wildner 
87180387638SSascha Wildner 	if (DISP__NONE == n->norm->Bd.type) {
87254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
87380387638SSascha Wildner 		n->norm->Bd.type = DISP_ragged;
87480387638SSascha Wildner 	}
87580387638SSascha Wildner }
87680387638SSascha Wildner 
87754ba9607SSascha Wildner /*
87854ba9607SSascha Wildner  * Stand-alone line macros.
87954ba9607SSascha Wildner  */
88054ba9607SSascha Wildner 
88154ba9607SSascha Wildner static void
post_an_norm(POST_ARGS)88254ba9607SSascha Wildner post_an_norm(POST_ARGS)
88380387638SSascha Wildner {
88454ba9607SSascha Wildner 	struct roff_node *n;
885070c62a6SFranco Fichtner 	struct mdoc_argv *argv;
886070c62a6SFranco Fichtner 	size_t	 i;
88780387638SSascha Wildner 
88854ba9607SSascha Wildner 	n = mdoc->last;
889070c62a6SFranco Fichtner 	if (n->args == NULL)
89054ba9607SSascha Wildner 		return;
89180387638SSascha Wildner 
892070c62a6SFranco Fichtner 	for (i = 1; i < n->args->argc; i++) {
893070c62a6SFranco Fichtner 		argv = n->args->argv + i;
89454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
895070c62a6SFranco Fichtner 		    "An -%s", mdoc_argnames[argv->arg]);
896070c62a6SFranco Fichtner 	}
89780387638SSascha Wildner 
898070c62a6SFranco Fichtner 	argv = n->args->argv;
899070c62a6SFranco Fichtner 	if (argv->arg == MDOC_Split)
90080387638SSascha Wildner 		n->norm->An.auth = AUTH_split;
901070c62a6SFranco Fichtner 	else if (argv->arg == MDOC_Nosplit)
90280387638SSascha Wildner 		n->norm->An.auth = AUTH_nosplit;
90380387638SSascha Wildner 	else
90480387638SSascha Wildner 		abort();
90580387638SSascha Wildner }
90680387638SSascha Wildner 
90754ba9607SSascha Wildner static void
post_eoln(POST_ARGS)90854ba9607SSascha Wildner post_eoln(POST_ARGS)
90980387638SSascha Wildner {
91054ba9607SSascha Wildner 	struct roff_node	*n;
91180387638SSascha Wildner 
91254ba9607SSascha Wildner 	post_useless(mdoc);
91354ba9607SSascha Wildner 	n = mdoc->last;
91454ba9607SSascha Wildner 	if (n->child != NULL)
91554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
91654ba9607SSascha Wildner 		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
91780387638SSascha Wildner 
91854ba9607SSascha Wildner 	while (n->child != NULL)
91954ba9607SSascha Wildner 		roff_node_delete(mdoc, n->child);
92054ba9607SSascha Wildner 
92154ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
92254ba9607SSascha Wildner 	    "is currently in beta test." : "currently under development.");
92354ba9607SSascha Wildner 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
92454ba9607SSascha Wildner 	mdoc->last = n;
925070c62a6SFranco Fichtner }
926070c62a6SFranco Fichtner 
927070c62a6SFranco Fichtner static int
build_list(struct roff_man * mdoc,int tok)92854ba9607SSascha Wildner build_list(struct roff_man *mdoc, int tok)
929070c62a6SFranco Fichtner {
93054ba9607SSascha Wildner 	struct roff_node	*n;
93154ba9607SSascha Wildner 	int			 ic;
932070c62a6SFranco Fichtner 
93354ba9607SSascha Wildner 	n = mdoc->last->next;
93454ba9607SSascha Wildner 	for (ic = 1;; ic++) {
93554ba9607SSascha Wildner 		roff_elem_alloc(mdoc, n->line, n->pos, tok);
93654ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
93754ba9607SSascha Wildner 		roff_node_relink(mdoc, n);
93854ba9607SSascha Wildner 		n = mdoc->last = mdoc->last->parent;
93954ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
94054ba9607SSascha Wildner 		if (n->next == NULL)
94154ba9607SSascha Wildner 			return ic;
94254ba9607SSascha Wildner 		if (ic > 1 || n->next->next != NULL) {
94354ba9607SSascha Wildner 			roff_word_alloc(mdoc, n->line, n->pos, ",");
94454ba9607SSascha Wildner 			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
94554ba9607SSascha Wildner 		}
94654ba9607SSascha Wildner 		n = mdoc->last->next;
94754ba9607SSascha Wildner 		if (n->next == NULL) {
94854ba9607SSascha Wildner 			roff_word_alloc(mdoc, n->line, n->pos, "and");
94954ba9607SSascha Wildner 			mdoc->last->flags |= NODE_NOSRC;
95054ba9607SSascha Wildner 		}
95154ba9607SSascha Wildner 	}
95280387638SSascha Wildner }
95380387638SSascha Wildner 
95454ba9607SSascha Wildner static void
post_ex(POST_ARGS)95554ba9607SSascha Wildner post_ex(POST_ARGS)
95680387638SSascha Wildner {
95754ba9607SSascha Wildner 	struct roff_node	*n;
95854ba9607SSascha Wildner 	int			 ic;
95980387638SSascha Wildner 
96054ba9607SSascha Wildner 	post_std(mdoc);
96154ba9607SSascha Wildner 
96254ba9607SSascha Wildner 	n = mdoc->last;
96354ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
96454ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "The");
96554ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
96654ba9607SSascha Wildner 
96754ba9607SSascha Wildner 	if (mdoc->last->next != NULL)
96854ba9607SSascha Wildner 		ic = build_list(mdoc, MDOC_Nm);
96954ba9607SSascha Wildner 	else if (mdoc->meta.name != NULL) {
97054ba9607SSascha Wildner 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
97154ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
97254ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
97354ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
97454ba9607SSascha Wildner 		mdoc->last = mdoc->last->parent;
97554ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
97654ba9607SSascha Wildner 		ic = 1;
97754ba9607SSascha Wildner 	} else {
97854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
97954ba9607SSascha Wildner 		ic = 0;
98080387638SSascha Wildner 	}
98180387638SSascha Wildner 
98254ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos,
98354ba9607SSascha Wildner 	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
98454ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
98554ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos,
98654ba9607SSascha Wildner 	    "on success, and\\~>0 if an error occurs.");
98754ba9607SSascha Wildner 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
98854ba9607SSascha Wildner 	mdoc->last = n;
98954ba9607SSascha Wildner }
99054ba9607SSascha Wildner 
99154ba9607SSascha Wildner static void
post_lb(POST_ARGS)99254ba9607SSascha Wildner post_lb(POST_ARGS)
99380387638SSascha Wildner {
99454ba9607SSascha Wildner 	struct roff_node	*n;
99554ba9607SSascha Wildner 	const char		*p;
99680387638SSascha Wildner 
99754ba9607SSascha Wildner 	post_delim_nb(mdoc);
99854ba9607SSascha Wildner 
99954ba9607SSascha Wildner 	n = mdoc->last;
100054ba9607SSascha Wildner 	assert(n->child->type == ROFFT_TEXT);
100154ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
100254ba9607SSascha Wildner 
100354ba9607SSascha Wildner 	if ((p = mdoc_a2lib(n->child->string)) != NULL) {
100454ba9607SSascha Wildner 		n->child->flags |= NODE_NOPRT;
100554ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, p);
100654ba9607SSascha Wildner 		mdoc->last->flags = NODE_NOSRC;
100754ba9607SSascha Wildner 		mdoc->last = n;
100854ba9607SSascha Wildner 		return;
100980387638SSascha Wildner 	}
101080387638SSascha Wildner 
101154ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_LB_BAD, n->child->line,
101254ba9607SSascha Wildner 	    n->child->pos, "Lb %s", n->child->string);
101354ba9607SSascha Wildner 
101454ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "library");
101554ba9607SSascha Wildner 	mdoc->last->flags = NODE_NOSRC;
101654ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
101754ba9607SSascha Wildner 	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
101854ba9607SSascha Wildner 	mdoc->last = mdoc->last->next;
101954ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
102054ba9607SSascha Wildner 	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
102154ba9607SSascha Wildner 	mdoc->last = n;
102254ba9607SSascha Wildner }
102354ba9607SSascha Wildner 
102454ba9607SSascha Wildner static void
post_rv(POST_ARGS)102554ba9607SSascha Wildner post_rv(POST_ARGS)
102680387638SSascha Wildner {
102754ba9607SSascha Wildner 	struct roff_node	*n;
102854ba9607SSascha Wildner 	int			 ic;
102980387638SSascha Wildner 
103054ba9607SSascha Wildner 	post_std(mdoc);
103154ba9607SSascha Wildner 
103254ba9607SSascha Wildner 	n = mdoc->last;
103354ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
103454ba9607SSascha Wildner 	if (n->child != NULL) {
103554ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "The");
103654ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
103754ba9607SSascha Wildner 		ic = build_list(mdoc, MDOC_Fn);
103854ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos,
103954ba9607SSascha Wildner 		    ic > 1 ? "functions return" : "function returns");
104054ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
104154ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos,
104254ba9607SSascha Wildner 		    "the value\\~0 if successful;");
104354ba9607SSascha Wildner 	} else
104454ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
104554ba9607SSascha Wildner 		    "completion, the value\\~0 is returned;");
104654ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
104754ba9607SSascha Wildner 
104854ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
104954ba9607SSascha Wildner 	    "the value\\~\\-1 is returned and the global variable");
105054ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
105154ba9607SSascha Wildner 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
105254ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
105354ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "errno");
105454ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
105554ba9607SSascha Wildner 	mdoc->last = mdoc->last->parent;
105654ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_SIBLING;
105754ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos,
105854ba9607SSascha Wildner 	    "is set to indicate the error.");
105954ba9607SSascha Wildner 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
106054ba9607SSascha Wildner 	mdoc->last = n;
106180387638SSascha Wildner }
106280387638SSascha Wildner 
106354ba9607SSascha Wildner static void
post_std(POST_ARGS)106454ba9607SSascha Wildner post_std(POST_ARGS)
106554ba9607SSascha Wildner {
106654ba9607SSascha Wildner 	struct roff_node *n;
106754ba9607SSascha Wildner 
106854ba9607SSascha Wildner 	post_delim(mdoc);
106954ba9607SSascha Wildner 
107054ba9607SSascha Wildner 	n = mdoc->last;
107154ba9607SSascha Wildner 	if (n->args && n->args->argc == 1)
107254ba9607SSascha Wildner 		if (n->args->argv[0].arg == MDOC_Std)
107354ba9607SSascha Wildner 			return;
107454ba9607SSascha Wildner 
107554ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
107654ba9607SSascha Wildner 	    "%s", roff_name[n->tok]);
107754ba9607SSascha Wildner }
107854ba9607SSascha Wildner 
107954ba9607SSascha Wildner static void
post_st(POST_ARGS)108054ba9607SSascha Wildner post_st(POST_ARGS)
108154ba9607SSascha Wildner {
108254ba9607SSascha Wildner 	struct roff_node	 *n, *nch;
108354ba9607SSascha Wildner 	const char		 *p;
108454ba9607SSascha Wildner 
108554ba9607SSascha Wildner 	n = mdoc->last;
108654ba9607SSascha Wildner 	nch = n->child;
108754ba9607SSascha Wildner 	assert(nch->type == ROFFT_TEXT);
108854ba9607SSascha Wildner 
108954ba9607SSascha Wildner 	if ((p = mdoc_a2st(nch->string)) == NULL) {
109054ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ST_BAD,
109154ba9607SSascha Wildner 		    nch->line, nch->pos, "St %s", nch->string);
109254ba9607SSascha Wildner 		roff_node_delete(mdoc, n);
109354ba9607SSascha Wildner 		return;
109454ba9607SSascha Wildner 	}
109554ba9607SSascha Wildner 
109654ba9607SSascha Wildner 	nch->flags |= NODE_NOPRT;
109754ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
109854ba9607SSascha Wildner 	roff_word_alloc(mdoc, nch->line, nch->pos, p);
109954ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
110054ba9607SSascha Wildner 	mdoc->last= n;
110154ba9607SSascha Wildner }
110254ba9607SSascha Wildner 
110354ba9607SSascha Wildner static void
post_tg(POST_ARGS)1104*99db7d0eSSascha Wildner post_tg(POST_ARGS)
1105*99db7d0eSSascha Wildner {
1106*99db7d0eSSascha Wildner 	struct roff_node *n;	/* The .Tg node. */
1107*99db7d0eSSascha Wildner 	struct roff_node *nch;	/* The first child of the .Tg node. */
1108*99db7d0eSSascha Wildner 	struct roff_node *nn;   /* The next node after the .Tg node. */
1109*99db7d0eSSascha Wildner 	struct roff_node *np;	/* The parent of the next node. */
1110*99db7d0eSSascha Wildner 	struct roff_node *nt;	/* The TEXT node containing the tag. */
1111*99db7d0eSSascha Wildner 	size_t		  len;	/* The number of bytes in the tag. */
1112*99db7d0eSSascha Wildner 
1113*99db7d0eSSascha Wildner 	/* Find the next node. */
1114*99db7d0eSSascha Wildner 	n = mdoc->last;
1115*99db7d0eSSascha Wildner 	for (nn = n; nn != NULL; nn = nn->parent) {
1116*99db7d0eSSascha Wildner 		if (nn->next != NULL) {
1117*99db7d0eSSascha Wildner 			nn = nn->next;
1118*99db7d0eSSascha Wildner 			break;
1119*99db7d0eSSascha Wildner 		}
1120*99db7d0eSSascha Wildner 	}
1121*99db7d0eSSascha Wildner 
1122*99db7d0eSSascha Wildner 	/* Find the tag. */
1123*99db7d0eSSascha Wildner 	nt = nch = n->child;
1124*99db7d0eSSascha Wildner 	if (nch == NULL && nn != NULL && nn->child != NULL &&
1125*99db7d0eSSascha Wildner 	    nn->child->type == ROFFT_TEXT)
1126*99db7d0eSSascha Wildner 		nt = nn->child;
1127*99db7d0eSSascha Wildner 
1128*99db7d0eSSascha Wildner 	/* Validate the tag. */
1129*99db7d0eSSascha Wildner 	if (nt == NULL || *nt->string == '\0')
1130*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
1131*99db7d0eSSascha Wildner 	if (nt == NULL) {
1132*99db7d0eSSascha Wildner 		roff_node_delete(mdoc, n);
1133*99db7d0eSSascha Wildner 		return;
1134*99db7d0eSSascha Wildner 	}
1135*99db7d0eSSascha Wildner 	len = strcspn(nt->string, " \t\\");
1136*99db7d0eSSascha Wildner 	if (nt->string[len] != '\0')
1137*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_TG_SPC, nt->line,
1138*99db7d0eSSascha Wildner 		    nt->pos + len, "Tg %s", nt->string);
1139*99db7d0eSSascha Wildner 
1140*99db7d0eSSascha Wildner 	/* Keep only the first argument. */
1141*99db7d0eSSascha Wildner 	if (nch != NULL && nch->next != NULL) {
1142*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
1143*99db7d0eSSascha Wildner 		    nch->next->pos, "Tg ... %s", nch->next->string);
1144*99db7d0eSSascha Wildner 		while (nch->next != NULL)
1145*99db7d0eSSascha Wildner 			roff_node_delete(mdoc, nch->next);
1146*99db7d0eSSascha Wildner 	}
1147*99db7d0eSSascha Wildner 
1148*99db7d0eSSascha Wildner 	/* Drop the macro if the first argument is invalid. */
1149*99db7d0eSSascha Wildner 	if (len == 0 || nt->string[len] != '\0') {
1150*99db7d0eSSascha Wildner 		roff_node_delete(mdoc, n);
1151*99db7d0eSSascha Wildner 		return;
1152*99db7d0eSSascha Wildner 	}
1153*99db7d0eSSascha Wildner 
1154*99db7d0eSSascha Wildner 	/* By default, tag the .Tg node itself. */
1155*99db7d0eSSascha Wildner 	if (nn == NULL || nn->flags & NODE_ID)
1156*99db7d0eSSascha Wildner 		nn = n;
1157*99db7d0eSSascha Wildner 
1158*99db7d0eSSascha Wildner 	/* Explicit tagging of specific macros. */
1159*99db7d0eSSascha Wildner 	switch (nn->tok) {
1160*99db7d0eSSascha Wildner 	case MDOC_Sh:
1161*99db7d0eSSascha Wildner 	case MDOC_Ss:
1162*99db7d0eSSascha Wildner 	case MDOC_Fo:
1163*99db7d0eSSascha Wildner 		nn = nn->head->child == NULL ? n : nn->head;
1164*99db7d0eSSascha Wildner 		break;
1165*99db7d0eSSascha Wildner 	case MDOC_It:
1166*99db7d0eSSascha Wildner 		np = nn->parent;
1167*99db7d0eSSascha Wildner 		while (np->tok != MDOC_Bl)
1168*99db7d0eSSascha Wildner 			np = np->parent;
1169*99db7d0eSSascha Wildner 		switch (np->norm->Bl.type) {
1170*99db7d0eSSascha Wildner 		case LIST_column:
1171*99db7d0eSSascha Wildner 			break;
1172*99db7d0eSSascha Wildner 		case LIST_diag:
1173*99db7d0eSSascha Wildner 		case LIST_hang:
1174*99db7d0eSSascha Wildner 		case LIST_inset:
1175*99db7d0eSSascha Wildner 		case LIST_ohang:
1176*99db7d0eSSascha Wildner 		case LIST_tag:
1177*99db7d0eSSascha Wildner 			nn = nn->head;
1178*99db7d0eSSascha Wildner 			break;
1179*99db7d0eSSascha Wildner 		case LIST_bullet:
1180*99db7d0eSSascha Wildner 		case LIST_dash:
1181*99db7d0eSSascha Wildner 		case LIST_enum:
1182*99db7d0eSSascha Wildner 		case LIST_hyphen:
1183*99db7d0eSSascha Wildner 		case LIST_item:
1184*99db7d0eSSascha Wildner 			nn = nn->body->child == NULL ? n : nn->body;
1185*99db7d0eSSascha Wildner 			break;
1186*99db7d0eSSascha Wildner 		default:
1187*99db7d0eSSascha Wildner 			abort();
1188*99db7d0eSSascha Wildner 		}
1189*99db7d0eSSascha Wildner 		break;
1190*99db7d0eSSascha Wildner 	case MDOC_Bd:
1191*99db7d0eSSascha Wildner 	case MDOC_Bl:
1192*99db7d0eSSascha Wildner 	case MDOC_D1:
1193*99db7d0eSSascha Wildner 	case MDOC_Dl:
1194*99db7d0eSSascha Wildner 		nn = nn->body->child == NULL ? n : nn->body;
1195*99db7d0eSSascha Wildner 		break;
1196*99db7d0eSSascha Wildner 	case MDOC_Pp:
1197*99db7d0eSSascha Wildner 		break;
1198*99db7d0eSSascha Wildner 	case MDOC_Cm:
1199*99db7d0eSSascha Wildner 	case MDOC_Dv:
1200*99db7d0eSSascha Wildner 	case MDOC_Em:
1201*99db7d0eSSascha Wildner 	case MDOC_Er:
1202*99db7d0eSSascha Wildner 	case MDOC_Ev:
1203*99db7d0eSSascha Wildner 	case MDOC_Fl:
1204*99db7d0eSSascha Wildner 	case MDOC_Fn:
1205*99db7d0eSSascha Wildner 	case MDOC_Ic:
1206*99db7d0eSSascha Wildner 	case MDOC_Li:
1207*99db7d0eSSascha Wildner 	case MDOC_Ms:
1208*99db7d0eSSascha Wildner 	case MDOC_No:
1209*99db7d0eSSascha Wildner 	case MDOC_Sy:
1210*99db7d0eSSascha Wildner 		if (nn->child == NULL)
1211*99db7d0eSSascha Wildner 			nn = n;
1212*99db7d0eSSascha Wildner 		break;
1213*99db7d0eSSascha Wildner 	default:
1214*99db7d0eSSascha Wildner 		nn = n;
1215*99db7d0eSSascha Wildner 		break;
1216*99db7d0eSSascha Wildner 	}
1217*99db7d0eSSascha Wildner 	tag_put(nt->string, TAG_MANUAL, nn);
1218*99db7d0eSSascha Wildner 	if (nn != n)
1219*99db7d0eSSascha Wildner 		n->flags |= NODE_NOPRT;
1220*99db7d0eSSascha Wildner }
1221*99db7d0eSSascha Wildner 
1222*99db7d0eSSascha Wildner static void
post_obsolete(POST_ARGS)122354ba9607SSascha Wildner post_obsolete(POST_ARGS)
122454ba9607SSascha Wildner {
122554ba9607SSascha Wildner 	struct roff_node *n;
122654ba9607SSascha Wildner 
122754ba9607SSascha Wildner 	n = mdoc->last;
122854ba9607SSascha Wildner 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
122954ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
123054ba9607SSascha Wildner 		    "%s", roff_name[n->tok]);
123154ba9607SSascha Wildner }
123254ba9607SSascha Wildner 
123354ba9607SSascha Wildner static void
post_useless(POST_ARGS)123454ba9607SSascha Wildner post_useless(POST_ARGS)
123554ba9607SSascha Wildner {
123654ba9607SSascha Wildner 	struct roff_node *n;
123754ba9607SSascha Wildner 
123854ba9607SSascha Wildner 	n = mdoc->last;
123954ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
124054ba9607SSascha Wildner 	    "%s", roff_name[n->tok]);
124154ba9607SSascha Wildner }
124254ba9607SSascha Wildner 
124354ba9607SSascha Wildner /*
124454ba9607SSascha Wildner  * Block macros.
124554ba9607SSascha Wildner  */
124654ba9607SSascha Wildner 
124754ba9607SSascha Wildner static void
post_bf(POST_ARGS)124880387638SSascha Wildner post_bf(POST_ARGS)
124980387638SSascha Wildner {
125054ba9607SSascha Wildner 	struct roff_node *np, *nch;
125180387638SSascha Wildner 
125280387638SSascha Wildner 	/*
125380387638SSascha Wildner 	 * Unlike other data pointers, these are "housed" by the HEAD
125480387638SSascha Wildner 	 * element, which contains the goods.
125580387638SSascha Wildner 	 */
125680387638SSascha Wildner 
125780387638SSascha Wildner 	np = mdoc->last;
125854ba9607SSascha Wildner 	if (np->type != ROFFT_HEAD)
125954ba9607SSascha Wildner 		return;
126054ba9607SSascha Wildner 
126154ba9607SSascha Wildner 	assert(np->parent->type == ROFFT_BLOCK);
126254ba9607SSascha Wildner 	assert(np->parent->tok == MDOC_Bf);
126380387638SSascha Wildner 
1264070c62a6SFranco Fichtner 	/* Check the number of arguments. */
126580387638SSascha Wildner 
1266070c62a6SFranco Fichtner 	nch = np->child;
126754ba9607SSascha Wildner 	if (np->parent->args == NULL) {
126854ba9607SSascha Wildner 		if (nch == NULL) {
126954ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BF_NOFONT,
1270070c62a6SFranco Fichtner 			    np->line, np->pos, "Bf");
127154ba9607SSascha Wildner 			return;
127280387638SSascha Wildner 		}
1273070c62a6SFranco Fichtner 		nch = nch->next;
1274070c62a6SFranco Fichtner 	}
127554ba9607SSascha Wildner 	if (nch != NULL)
127654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS,
1277070c62a6SFranco Fichtner 		    nch->line, nch->pos, "Bf ... %s", nch->string);
127880387638SSascha Wildner 
127980387638SSascha Wildner 	/* Extract argument into data. */
128080387638SSascha Wildner 
128154ba9607SSascha Wildner 	if (np->parent->args != NULL) {
128254ba9607SSascha Wildner 		switch (np->parent->args->argv[0].arg) {
128354ba9607SSascha Wildner 		case MDOC_Emphasis:
128480387638SSascha Wildner 			np->norm->Bf.font = FONT_Em;
128554ba9607SSascha Wildner 			break;
128654ba9607SSascha Wildner 		case MDOC_Literal:
128780387638SSascha Wildner 			np->norm->Bf.font = FONT_Li;
128854ba9607SSascha Wildner 			break;
128954ba9607SSascha Wildner 		case MDOC_Symbolic:
129080387638SSascha Wildner 			np->norm->Bf.font = FONT_Sy;
129154ba9607SSascha Wildner 			break;
129254ba9607SSascha Wildner 		default:
129380387638SSascha Wildner 			abort();
129454ba9607SSascha Wildner 		}
129554ba9607SSascha Wildner 		return;
129680387638SSascha Wildner 	}
129780387638SSascha Wildner 
129880387638SSascha Wildner 	/* Extract parameter into data. */
129980387638SSascha Wildner 
130054ba9607SSascha Wildner 	if ( ! strcmp(np->child->string, "Em"))
130180387638SSascha Wildner 		np->norm->Bf.font = FONT_Em;
130254ba9607SSascha Wildner 	else if ( ! strcmp(np->child->string, "Li"))
130380387638SSascha Wildner 		np->norm->Bf.font = FONT_Li;
130454ba9607SSascha Wildner 	else if ( ! strcmp(np->child->string, "Sy"))
130580387638SSascha Wildner 		np->norm->Bf.font = FONT_Sy;
130680387638SSascha Wildner 	else
130754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
130854ba9607SSascha Wildner 		    np->child->pos, "Bf %s", np->child->string);
130980387638SSascha Wildner }
131080387638SSascha Wildner 
131154ba9607SSascha Wildner static void
post_fname(POST_ARGS)131254ba9607SSascha Wildner post_fname(POST_ARGS)
131380387638SSascha Wildner {
1314*99db7d0eSSascha Wildner 	struct roff_node	*n, *nch;
131554ba9607SSascha Wildner 	const char		*cp;
131654ba9607SSascha Wildner 	size_t			 pos;
131780387638SSascha Wildner 
1318*99db7d0eSSascha Wildner 	n = mdoc->last;
1319*99db7d0eSSascha Wildner 	nch = n->child;
1320*99db7d0eSSascha Wildner 	cp = nch->string;
1321*99db7d0eSSascha Wildner 	if (*cp == '(') {
1322*99db7d0eSSascha Wildner 		if (cp[strlen(cp + 1)] == ')')
1323*99db7d0eSSascha Wildner 			return;
1324*99db7d0eSSascha Wildner 		pos = 0;
1325*99db7d0eSSascha Wildner 	} else {
1326*99db7d0eSSascha Wildner 		pos = strcspn(cp, "()");
1327*99db7d0eSSascha Wildner 		if (cp[pos] == '\0') {
1328*99db7d0eSSascha Wildner 			if (n->sec == SEC_DESCRIPTION ||
1329*99db7d0eSSascha Wildner 			    n->sec == SEC_CUSTOM)
1330*99db7d0eSSascha Wildner 				tag_put(NULL, fn_prio++, n);
1331*99db7d0eSSascha Wildner 			return;
1332*99db7d0eSSascha Wildner 		}
1333*99db7d0eSSascha Wildner 	}
1334*99db7d0eSSascha Wildner 	mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
133580387638SSascha Wildner }
133680387638SSascha Wildner 
133754ba9607SSascha Wildner static void
post_fn(POST_ARGS)133854ba9607SSascha Wildner post_fn(POST_ARGS)
133980387638SSascha Wildner {
134054ba9607SSascha Wildner 	post_fname(mdoc);
134154ba9607SSascha Wildner 	post_fa(mdoc);
134280387638SSascha Wildner }
134380387638SSascha Wildner 
134454ba9607SSascha Wildner static void
post_fo(POST_ARGS)1345070c62a6SFranco Fichtner post_fo(POST_ARGS)
1346070c62a6SFranco Fichtner {
134754ba9607SSascha Wildner 	const struct roff_node	*n;
1348070c62a6SFranco Fichtner 
134954ba9607SSascha Wildner 	n = mdoc->last;
135054ba9607SSascha Wildner 
135154ba9607SSascha Wildner 	if (n->type != ROFFT_HEAD)
135254ba9607SSascha Wildner 		return;
135354ba9607SSascha Wildner 
135454ba9607SSascha Wildner 	if (n->child == NULL) {
135554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
135654ba9607SSascha Wildner 		return;
135754ba9607SSascha Wildner 	}
135854ba9607SSascha Wildner 	if (n->child != n->last) {
135954ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS,
136054ba9607SSascha Wildner 		    n->child->next->line, n->child->next->pos,
136154ba9607SSascha Wildner 		    "Fo ... %s", n->child->next->string);
136254ba9607SSascha Wildner 		while (n->child != n->last)
136354ba9607SSascha Wildner 			roff_node_delete(mdoc, n->last);
136454ba9607SSascha Wildner 	} else
136554ba9607SSascha Wildner 		post_delim(mdoc);
136654ba9607SSascha Wildner 
136754ba9607SSascha Wildner 	post_fname(mdoc);
1368070c62a6SFranco Fichtner }
136980387638SSascha Wildner 
137054ba9607SSascha Wildner static void
post_fa(POST_ARGS)137154ba9607SSascha Wildner post_fa(POST_ARGS)
137280387638SSascha Wildner {
137354ba9607SSascha Wildner 	const struct roff_node *n;
137454ba9607SSascha Wildner 	const char *cp;
137580387638SSascha Wildner 
137654ba9607SSascha Wildner 	for (n = mdoc->last->child; n != NULL; n = n->next) {
137754ba9607SSascha Wildner 		for (cp = n->string; *cp != '\0'; cp++) {
137854ba9607SSascha Wildner 			/* Ignore callbacks and alterations. */
137954ba9607SSascha Wildner 			if (*cp == '(' || *cp == '{')
138054ba9607SSascha Wildner 				break;
138154ba9607SSascha Wildner 			if (*cp != ',')
138254ba9607SSascha Wildner 				continue;
138354ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_FA_COMMA, n->line,
138454ba9607SSascha Wildner 			    n->pos + (int)(cp - n->string), "%s", n->string);
138554ba9607SSascha Wildner 			break;
138654ba9607SSascha Wildner 		}
138754ba9607SSascha Wildner 	}
138854ba9607SSascha Wildner 	post_delim_nb(mdoc);
138980387638SSascha Wildner }
139080387638SSascha Wildner 
139154ba9607SSascha Wildner static void
post_nm(POST_ARGS)139280387638SSascha Wildner post_nm(POST_ARGS)
139380387638SSascha Wildner {
139454ba9607SSascha Wildner 	struct roff_node	*n;
139580387638SSascha Wildner 
139654ba9607SSascha Wildner 	n = mdoc->last;
139780387638SSascha Wildner 
139854ba9607SSascha Wildner 	if (n->sec == SEC_NAME && n->child != NULL &&
139954ba9607SSascha Wildner 	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
140054ba9607SSascha Wildner 		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
140180387638SSascha Wildner 
140254ba9607SSascha Wildner 	if (n->last != NULL && n->last->tok == MDOC_Pp)
140354ba9607SSascha Wildner 		roff_node_relink(mdoc, n->last);
140454ba9607SSascha Wildner 
140554ba9607SSascha Wildner 	if (mdoc->meta.name == NULL)
140654ba9607SSascha Wildner 		deroff(&mdoc->meta.name, n);
140754ba9607SSascha Wildner 
140854ba9607SSascha Wildner 	if (mdoc->meta.name == NULL ||
140954ba9607SSascha Wildner 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
141054ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
141154ba9607SSascha Wildner 
141254ba9607SSascha Wildner 	switch (n->type) {
141354ba9607SSascha Wildner 	case ROFFT_ELEM:
141454ba9607SSascha Wildner 		post_delim_nb(mdoc);
141554ba9607SSascha Wildner 		break;
141654ba9607SSascha Wildner 	case ROFFT_HEAD:
141754ba9607SSascha Wildner 		post_delim(mdoc);
141854ba9607SSascha Wildner 		break;
141954ba9607SSascha Wildner 	default:
142054ba9607SSascha Wildner 		return;
142180387638SSascha Wildner 	}
142280387638SSascha Wildner 
142354ba9607SSascha Wildner 	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
142454ba9607SSascha Wildner 	    mdoc->meta.name == NULL)
142554ba9607SSascha Wildner 		return;
142654ba9607SSascha Wildner 
142754ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
142854ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
142954ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
143054ba9607SSascha Wildner 	mdoc->last = n;
143154ba9607SSascha Wildner }
143254ba9607SSascha Wildner 
143354ba9607SSascha Wildner static void
post_nd(POST_ARGS)1434070c62a6SFranco Fichtner post_nd(POST_ARGS)
1435070c62a6SFranco Fichtner {
143654ba9607SSascha Wildner 	struct roff_node	*n;
1437070c62a6SFranco Fichtner 
143854ba9607SSascha Wildner 	n = mdoc->last;
143954ba9607SSascha Wildner 
144054ba9607SSascha Wildner 	if (n->type != ROFFT_BODY)
144154ba9607SSascha Wildner 		return;
144254ba9607SSascha Wildner 
144354ba9607SSascha Wildner 	if (n->sec != SEC_NAME)
144454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
144554ba9607SSascha Wildner 
144654ba9607SSascha Wildner 	if (n->child == NULL)
144754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
144854ba9607SSascha Wildner 	else
144954ba9607SSascha Wildner 		post_delim(mdoc);
145054ba9607SSascha Wildner 
145154ba9607SSascha Wildner 	post_hyph(mdoc);
1452070c62a6SFranco Fichtner }
1453070c62a6SFranco Fichtner 
145454ba9607SSascha Wildner static void
post_display(POST_ARGS)145554ba9607SSascha Wildner post_display(POST_ARGS)
1456070c62a6SFranco Fichtner {
145754ba9607SSascha Wildner 	struct roff_node *n, *np;
1458070c62a6SFranco Fichtner 
145954ba9607SSascha Wildner 	n = mdoc->last;
146054ba9607SSascha Wildner 	switch (n->type) {
146154ba9607SSascha Wildner 	case ROFFT_BODY:
146254ba9607SSascha Wildner 		if (n->end != ENDBODY_NOT) {
146354ba9607SSascha Wildner 			if (n->tok == MDOC_Bd &&
146454ba9607SSascha Wildner 			    n->body->parent->args == NULL)
146554ba9607SSascha Wildner 				roff_node_delete(mdoc, n);
146654ba9607SSascha Wildner 		} else if (n->child == NULL)
146754ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
146854ba9607SSascha Wildner 			    "%s", roff_name[n->tok]);
146954ba9607SSascha Wildner 		else if (n->tok == MDOC_D1)
147054ba9607SSascha Wildner 			post_hyph(mdoc);
147154ba9607SSascha Wildner 		break;
147254ba9607SSascha Wildner 	case ROFFT_BLOCK:
147354ba9607SSascha Wildner 		if (n->tok == MDOC_Bd) {
147454ba9607SSascha Wildner 			if (n->args == NULL) {
147554ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_BD_NOARG,
147654ba9607SSascha Wildner 				    n->line, n->pos, "Bd");
147754ba9607SSascha Wildner 				mdoc->next = ROFF_NEXT_SIBLING;
147854ba9607SSascha Wildner 				while (n->body->child != NULL)
147954ba9607SSascha Wildner 					roff_node_relink(mdoc,
148054ba9607SSascha Wildner 					    n->body->child);
148154ba9607SSascha Wildner 				roff_node_delete(mdoc, n);
148254ba9607SSascha Wildner 				break;
148354ba9607SSascha Wildner 			}
148454ba9607SSascha Wildner 			post_bd(mdoc);
148554ba9607SSascha Wildner 			post_prevpar(mdoc);
148654ba9607SSascha Wildner 		}
148754ba9607SSascha Wildner 		for (np = n->parent; np != NULL; np = np->parent) {
148854ba9607SSascha Wildner 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
148954ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_BD_NEST, n->line,
149054ba9607SSascha Wildner 				    n->pos, "%s in Bd", roff_name[n->tok]);
149154ba9607SSascha Wildner 				break;
149254ba9607SSascha Wildner 			}
149354ba9607SSascha Wildner 		}
149454ba9607SSascha Wildner 		break;
149554ba9607SSascha Wildner 	default:
149654ba9607SSascha Wildner 		break;
149754ba9607SSascha Wildner 	}
1498070c62a6SFranco Fichtner }
1499070c62a6SFranco Fichtner 
150054ba9607SSascha Wildner static void
post_defaults(POST_ARGS)150180387638SSascha Wildner post_defaults(POST_ARGS)
150280387638SSascha Wildner {
1503*99db7d0eSSascha Wildner 	struct roff_node *n;
150454ba9607SSascha Wildner 
1505*99db7d0eSSascha Wildner 	n = mdoc->last;
1506*99db7d0eSSascha Wildner 	if (n->child != NULL) {
150754ba9607SSascha Wildner 		post_delim_nb(mdoc);
150854ba9607SSascha Wildner 		return;
150954ba9607SSascha Wildner 	}
151054ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
1511*99db7d0eSSascha Wildner 	switch (n->tok) {
1512*99db7d0eSSascha Wildner 	case MDOC_Ar:
1513*99db7d0eSSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "file");
151454ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
1515*99db7d0eSSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "...");
151680387638SSascha Wildner 		break;
1517070c62a6SFranco Fichtner 	case MDOC_Pa:
1518070c62a6SFranco Fichtner 	case MDOC_Mt:
1519*99db7d0eSSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "~");
152080387638SSascha Wildner 		break;
152180387638SSascha Wildner 	default:
152280387638SSascha Wildner 		abort();
152380387638SSascha Wildner 	}
1524*99db7d0eSSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
1525*99db7d0eSSascha Wildner 	mdoc->last = n;
152680387638SSascha Wildner }
152780387638SSascha Wildner 
152854ba9607SSascha Wildner static void
post_at(POST_ARGS)152980387638SSascha Wildner post_at(POST_ARGS)
153080387638SSascha Wildner {
153154ba9607SSascha Wildner 	struct roff_node	*n, *nch;
153254ba9607SSascha Wildner 	const char		*att;
1533070c62a6SFranco Fichtner 
1534070c62a6SFranco Fichtner 	n = mdoc->last;
153554ba9607SSascha Wildner 	nch = n->child;
153680387638SSascha Wildner 
153780387638SSascha Wildner 	/*
153880387638SSascha Wildner 	 * If we have a child, look it up in the standard keys.  If a
153980387638SSascha Wildner 	 * key exist, use that instead of the child; if it doesn't,
154080387638SSascha Wildner 	 * prefix "AT&T UNIX " to the existing data.
154180387638SSascha Wildner 	 */
154280387638SSascha Wildner 
154354ba9607SSascha Wildner 	att = NULL;
154454ba9607SSascha Wildner 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
154554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_AT_BAD,
154654ba9607SSascha Wildner 		    nch->line, nch->pos, "At %s", nch->string);
154780387638SSascha Wildner 
154854ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
154954ba9607SSascha Wildner 	if (att != NULL) {
155054ba9607SSascha Wildner 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
155154ba9607SSascha Wildner 		nch->flags |= NODE_NOPRT;
155254ba9607SSascha Wildner 	} else
155354ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
155454ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
155554ba9607SSascha Wildner 	mdoc->last = n;
155680387638SSascha Wildner }
155780387638SSascha Wildner 
155854ba9607SSascha Wildner static void
post_an(POST_ARGS)155980387638SSascha Wildner post_an(POST_ARGS)
156080387638SSascha Wildner {
156154ba9607SSascha Wildner 	struct roff_node *np, *nch;
156254ba9607SSascha Wildner 
156354ba9607SSascha Wildner 	post_an_norm(mdoc);
156480387638SSascha Wildner 
156580387638SSascha Wildner 	np = mdoc->last;
156654ba9607SSascha Wildner 	nch = np->child;
156754ba9607SSascha Wildner 	if (np->norm->An.auth == AUTH__NONE) {
156854ba9607SSascha Wildner 		if (nch == NULL)
156954ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
157054ba9607SSascha Wildner 			    np->line, np->pos, "An");
157154ba9607SSascha Wildner 		else
157254ba9607SSascha Wildner 			post_delim_nb(mdoc);
157354ba9607SSascha Wildner 	} else if (nch != NULL)
157454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS,
157554ba9607SSascha Wildner 		    nch->line, nch->pos, "An ... %s", nch->string);
157680387638SSascha Wildner }
157780387638SSascha Wildner 
157854ba9607SSascha Wildner static void
post_em(POST_ARGS)1579*99db7d0eSSascha Wildner post_em(POST_ARGS)
1580*99db7d0eSSascha Wildner {
1581*99db7d0eSSascha Wildner 	post_tag(mdoc);
1582*99db7d0eSSascha Wildner 	tag_put(NULL, TAG_FALLBACK, mdoc->last);
1583*99db7d0eSSascha Wildner }
1584*99db7d0eSSascha Wildner 
1585*99db7d0eSSascha Wildner static void
post_en(POST_ARGS)1586070c62a6SFranco Fichtner post_en(POST_ARGS)
1587070c62a6SFranco Fichtner {
158854ba9607SSascha Wildner 	post_obsolete(mdoc);
158954ba9607SSascha Wildner 	if (mdoc->last->type == ROFFT_BLOCK)
1590070c62a6SFranco Fichtner 		mdoc->last->norm->Es = mdoc->last_es;
1591070c62a6SFranco Fichtner }
1592070c62a6SFranco Fichtner 
159354ba9607SSascha Wildner static void
post_er(POST_ARGS)1594*99db7d0eSSascha Wildner post_er(POST_ARGS)
1595*99db7d0eSSascha Wildner {
1596*99db7d0eSSascha Wildner 	struct roff_node *n;
1597*99db7d0eSSascha Wildner 
1598*99db7d0eSSascha Wildner 	n = mdoc->last;
1599*99db7d0eSSascha Wildner 	if (n->sec == SEC_ERRORS &&
1600*99db7d0eSSascha Wildner 	    (n->parent->tok == MDOC_It ||
1601*99db7d0eSSascha Wildner 	     (n->parent->tok == MDOC_Bq &&
1602*99db7d0eSSascha Wildner 	      n->parent->parent->parent->tok == MDOC_It)))
1603*99db7d0eSSascha Wildner 		tag_put(NULL, TAG_STRONG, n);
1604*99db7d0eSSascha Wildner 	post_delim_nb(mdoc);
1605*99db7d0eSSascha Wildner }
1606*99db7d0eSSascha Wildner 
1607*99db7d0eSSascha Wildner static void
post_tag(POST_ARGS)1608*99db7d0eSSascha Wildner post_tag(POST_ARGS)
1609*99db7d0eSSascha Wildner {
1610*99db7d0eSSascha Wildner 	struct roff_node *n;
1611*99db7d0eSSascha Wildner 
1612*99db7d0eSSascha Wildner 	n = mdoc->last;
1613*99db7d0eSSascha Wildner 	if ((n->prev == NULL ||
1614*99db7d0eSSascha Wildner 	     (n->prev->type == ROFFT_TEXT &&
1615*99db7d0eSSascha Wildner 	      strcmp(n->prev->string, "|") == 0)) &&
1616*99db7d0eSSascha Wildner 	    (n->parent->tok == MDOC_It ||
1617*99db7d0eSSascha Wildner 	     (n->parent->tok == MDOC_Xo &&
1618*99db7d0eSSascha Wildner 	      n->parent->parent->prev == NULL &&
1619*99db7d0eSSascha Wildner 	      n->parent->parent->parent->tok == MDOC_It)))
1620*99db7d0eSSascha Wildner 		tag_put(NULL, TAG_STRONG, n);
1621*99db7d0eSSascha Wildner 	post_delim_nb(mdoc);
1622*99db7d0eSSascha Wildner }
1623*99db7d0eSSascha Wildner 
1624*99db7d0eSSascha Wildner static void
post_es(POST_ARGS)1625070c62a6SFranco Fichtner post_es(POST_ARGS)
1626070c62a6SFranco Fichtner {
162754ba9607SSascha Wildner 	post_obsolete(mdoc);
1628070c62a6SFranco Fichtner 	mdoc->last_es = mdoc->last;
1629070c62a6SFranco Fichtner }
163080387638SSascha Wildner 
163154ba9607SSascha Wildner static void
post_fl(POST_ARGS)1632*99db7d0eSSascha Wildner post_fl(POST_ARGS)
1633*99db7d0eSSascha Wildner {
1634*99db7d0eSSascha Wildner 	struct roff_node	*n;
1635*99db7d0eSSascha Wildner 	char			*cp;
1636*99db7d0eSSascha Wildner 
1637*99db7d0eSSascha Wildner 	/*
1638*99db7d0eSSascha Wildner 	 * Transform ".Fl Fl long" to ".Fl \-long",
1639*99db7d0eSSascha Wildner 	 * resulting for example in better HTML output.
1640*99db7d0eSSascha Wildner 	 */
1641*99db7d0eSSascha Wildner 
1642*99db7d0eSSascha Wildner 	n = mdoc->last;
1643*99db7d0eSSascha Wildner 	if (n->prev != NULL && n->prev->tok == MDOC_Fl &&
1644*99db7d0eSSascha Wildner 	    n->prev->child == NULL && n->child != NULL &&
1645*99db7d0eSSascha Wildner 	    (n->flags & NODE_LINE) == 0) {
1646*99db7d0eSSascha Wildner 		mandoc_asprintf(&cp, "\\-%s", n->child->string);
1647*99db7d0eSSascha Wildner 		free(n->child->string);
1648*99db7d0eSSascha Wildner 		n->child->string = cp;
1649*99db7d0eSSascha Wildner 		roff_node_delete(mdoc, n->prev);
1650*99db7d0eSSascha Wildner 	}
1651*99db7d0eSSascha Wildner 	post_tag(mdoc);
1652*99db7d0eSSascha Wildner }
1653*99db7d0eSSascha Wildner 
1654*99db7d0eSSascha Wildner static void
post_xx(POST_ARGS)165554ba9607SSascha Wildner post_xx(POST_ARGS)
165654ba9607SSascha Wildner {
165754ba9607SSascha Wildner 	struct roff_node	*n;
165854ba9607SSascha Wildner 	const char		*os;
165954ba9607SSascha Wildner 	char			*v;
166054ba9607SSascha Wildner 
166154ba9607SSascha Wildner 	post_delim_nb(mdoc);
166254ba9607SSascha Wildner 
166354ba9607SSascha Wildner 	n = mdoc->last;
166454ba9607SSascha Wildner 	switch (n->tok) {
166554ba9607SSascha Wildner 	case MDOC_Bsx:
166654ba9607SSascha Wildner 		os = "BSD/OS";
166754ba9607SSascha Wildner 		break;
166854ba9607SSascha Wildner 	case MDOC_Dx:
166954ba9607SSascha Wildner 		os = "DragonFly";
167054ba9607SSascha Wildner 		break;
167154ba9607SSascha Wildner 	case MDOC_Fx:
167254ba9607SSascha Wildner 		os = "FreeBSD";
167354ba9607SSascha Wildner 		break;
167454ba9607SSascha Wildner 	case MDOC_Nx:
167554ba9607SSascha Wildner 		os = "NetBSD";
167654ba9607SSascha Wildner 		if (n->child == NULL)
167754ba9607SSascha Wildner 			break;
167854ba9607SSascha Wildner 		v = n->child->string;
167954ba9607SSascha Wildner 		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
168054ba9607SSascha Wildner 		    v[2] < '0' || v[2] > '9' ||
168154ba9607SSascha Wildner 		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
168254ba9607SSascha Wildner 			break;
168354ba9607SSascha Wildner 		n->child->flags |= NODE_NOPRT;
168454ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_CHILD;
168554ba9607SSascha Wildner 		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
168654ba9607SSascha Wildner 		v = mdoc->last->string;
168754ba9607SSascha Wildner 		v[3] = toupper((unsigned char)v[3]);
168854ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
168954ba9607SSascha Wildner 		mdoc->last = n;
169054ba9607SSascha Wildner 		break;
169154ba9607SSascha Wildner 	case MDOC_Ox:
169254ba9607SSascha Wildner 		os = "OpenBSD";
169354ba9607SSascha Wildner 		break;
169454ba9607SSascha Wildner 	case MDOC_Ux:
169554ba9607SSascha Wildner 		os = "UNIX";
169654ba9607SSascha Wildner 		break;
169754ba9607SSascha Wildner 	default:
169854ba9607SSascha Wildner 		abort();
169954ba9607SSascha Wildner 	}
170054ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_CHILD;
170154ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, os);
170254ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
170354ba9607SSascha Wildner 	mdoc->last = n;
170454ba9607SSascha Wildner }
170554ba9607SSascha Wildner 
170654ba9607SSascha Wildner static void
post_it(POST_ARGS)170780387638SSascha Wildner post_it(POST_ARGS)
170880387638SSascha Wildner {
170954ba9607SSascha Wildner 	struct roff_node *nbl, *nit, *nch;
171060e1e752SSascha Wildner 	int		  i, cols;
171180387638SSascha Wildner 	enum mdoc_list	  lt;
171254ba9607SSascha Wildner 
171354ba9607SSascha Wildner 	post_prevpar(mdoc);
171480387638SSascha Wildner 
1715070c62a6SFranco Fichtner 	nit = mdoc->last;
171654ba9607SSascha Wildner 	if (nit->type != ROFFT_BLOCK)
171754ba9607SSascha Wildner 		return;
171880387638SSascha Wildner 
1719070c62a6SFranco Fichtner 	nbl = nit->parent->parent;
1720070c62a6SFranco Fichtner 	lt = nbl->norm->Bl.type;
172180387638SSascha Wildner 
172280387638SSascha Wildner 	switch (lt) {
1723070c62a6SFranco Fichtner 	case LIST_tag:
1724070c62a6SFranco Fichtner 	case LIST_hang:
1725070c62a6SFranco Fichtner 	case LIST_ohang:
1726070c62a6SFranco Fichtner 	case LIST_inset:
1727070c62a6SFranco Fichtner 	case LIST_diag:
172854ba9607SSascha Wildner 		if (nit->head->child == NULL)
172954ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_IT_NOHEAD,
173054ba9607SSascha Wildner 			    nit->line, nit->pos, "Bl -%s It",
1731070c62a6SFranco Fichtner 			    mdoc_argnames[nbl->args->argv[0].arg]);
173280387638SSascha Wildner 		break;
1733070c62a6SFranco Fichtner 	case LIST_bullet:
1734070c62a6SFranco Fichtner 	case LIST_dash:
1735070c62a6SFranco Fichtner 	case LIST_enum:
1736070c62a6SFranco Fichtner 	case LIST_hyphen:
173754ba9607SSascha Wildner 		if (nit->body == NULL || nit->body->child == NULL)
173854ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_IT_NOBODY,
173954ba9607SSascha Wildner 			    nit->line, nit->pos, "Bl -%s It",
1740070c62a6SFranco Fichtner 			    mdoc_argnames[nbl->args->argv[0].arg]);
1741070c62a6SFranco Fichtner 		/* FALLTHROUGH */
1742070c62a6SFranco Fichtner 	case LIST_item:
174354ba9607SSascha Wildner 		if ((nch = nit->head->child) != NULL)
174454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_ARG_SKIP,
174554ba9607SSascha Wildner 			    nit->line, nit->pos, "It %s",
1746*99db7d0eSSascha Wildner 			    nch->type == ROFFT_TEXT ? nch->string :
1747*99db7d0eSSascha Wildner 			    roff_name[nch->tok]);
174880387638SSascha Wildner 		break;
1749070c62a6SFranco Fichtner 	case LIST_column:
1750070c62a6SFranco Fichtner 		cols = (int)nbl->norm->Bl.ncols;
175180387638SSascha Wildner 
175254ba9607SSascha Wildner 		assert(nit->head->child == NULL);
175380387638SSascha Wildner 
175454ba9607SSascha Wildner 		if (nit->head->next->child == NULL &&
175554ba9607SSascha Wildner 		    nit->head->next->next == NULL) {
175654ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
175754ba9607SSascha Wildner 			    nit->line, nit->pos, "It");
175854ba9607SSascha Wildner 			roff_node_delete(mdoc, nit);
175954ba9607SSascha Wildner 			break;
176054ba9607SSascha Wildner 		}
176180387638SSascha Wildner 
176254ba9607SSascha Wildner 		i = 0;
176354ba9607SSascha Wildner 		for (nch = nit->child; nch != NULL; nch = nch->next) {
176454ba9607SSascha Wildner 			if (nch->type != ROFFT_BODY)
176554ba9607SSascha Wildner 				continue;
176654ba9607SSascha Wildner 			if (i++ && nch->flags & NODE_LINE)
176754ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_TA_LINE,
176854ba9607SSascha Wildner 				    nch->line, nch->pos, "Ta");
176954ba9607SSascha Wildner 		}
1770070c62a6SFranco Fichtner 		if (i < cols || i > cols + 1)
177154ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
177254ba9607SSascha Wildner 			    "%d columns, %d cells", cols, i);
177354ba9607SSascha Wildner 		else if (nit->head->next->child != NULL &&
177454ba9607SSascha Wildner 		    nit->head->next->child->flags & NODE_LINE)
177554ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_IT_NOARG,
177654ba9607SSascha Wildner 			    nit->line, nit->pos, "Bl -column It");
177780387638SSascha Wildner 		break;
1778070c62a6SFranco Fichtner 	default:
1779070c62a6SFranco Fichtner 		abort();
178080387638SSascha Wildner 	}
178180387638SSascha Wildner }
178280387638SSascha Wildner 
178354ba9607SSascha Wildner static void
post_bl_block(POST_ARGS)178480387638SSascha Wildner post_bl_block(POST_ARGS)
178580387638SSascha Wildner {
178654ba9607SSascha Wildner 	struct roff_node *n, *ni, *nc;
178780387638SSascha Wildner 
178854ba9607SSascha Wildner 	post_prevpar(mdoc);
178980387638SSascha Wildner 
179080387638SSascha Wildner 	n = mdoc->last;
179154ba9607SSascha Wildner 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
179254ba9607SSascha Wildner 		if (ni->body == NULL)
1793f88b6c16SFranco Fichtner 			continue;
1794f88b6c16SFranco Fichtner 		nc = ni->body->last;
179554ba9607SSascha Wildner 		while (nc != NULL) {
1796f88b6c16SFranco Fichtner 			switch (nc->tok) {
1797070c62a6SFranco Fichtner 			case MDOC_Pp:
179854ba9607SSascha Wildner 			case ROFF_br:
1799f88b6c16SFranco Fichtner 				break;
1800f88b6c16SFranco Fichtner 			default:
1801f88b6c16SFranco Fichtner 				nc = NULL;
1802f88b6c16SFranco Fichtner 				continue;
1803f88b6c16SFranco Fichtner 			}
180454ba9607SSascha Wildner 			if (ni->next == NULL) {
180554ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
180654ba9607SSascha Wildner 				    nc->pos, "%s", roff_name[nc->tok]);
180754ba9607SSascha Wildner 				roff_node_relink(mdoc, nc);
180854ba9607SSascha Wildner 			} else if (n->norm->Bl.comp == 0 &&
180954ba9607SSascha Wildner 			    n->norm->Bl.type != LIST_column) {
181054ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_PAR_SKIP,
181154ba9607SSascha Wildner 				    nc->line, nc->pos,
181254ba9607SSascha Wildner 				    "%s before It", roff_name[nc->tok]);
181354ba9607SSascha Wildner 				roff_node_delete(mdoc, nc);
1814f88b6c16SFranco Fichtner 			} else
1815f88b6c16SFranco Fichtner 				break;
1816f88b6c16SFranco Fichtner 			nc = ni->body->last;
1817f88b6c16SFranco Fichtner 		}
1818f88b6c16SFranco Fichtner 	}
181980387638SSascha Wildner }
182080387638SSascha Wildner 
182154ba9607SSascha Wildner /*
182254ba9607SSascha Wildner  * If the argument of -offset or -width is a macro,
182354ba9607SSascha Wildner  * replace it with the associated default width.
182454ba9607SSascha Wildner  */
182554ba9607SSascha Wildner static void
rewrite_macro2len(struct roff_man * mdoc,char ** arg)182654ba9607SSascha Wildner rewrite_macro2len(struct roff_man *mdoc, char **arg)
182780387638SSascha Wildner {
182880387638SSascha Wildner 	size_t		  width;
182954ba9607SSascha Wildner 	enum roff_tok	  tok;
183080387638SSascha Wildner 
183154ba9607SSascha Wildner 	if (*arg == NULL)
183254ba9607SSascha Wildner 		return;
183354ba9607SSascha Wildner 	else if ( ! strcmp(*arg, "Ds"))
183480387638SSascha Wildner 		width = 6;
183554ba9607SSascha Wildner 	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
183654ba9607SSascha Wildner 		return;
1837070c62a6SFranco Fichtner 	else
1838070c62a6SFranco Fichtner 		width = macro2len(tok);
183980387638SSascha Wildner 
184054ba9607SSascha Wildner 	free(*arg);
184154ba9607SSascha Wildner 	mandoc_asprintf(arg, "%zun", width);
184280387638SSascha Wildner }
184380387638SSascha Wildner 
184454ba9607SSascha Wildner static void
post_bl_head(POST_ARGS)184580387638SSascha Wildner post_bl_head(POST_ARGS)
184680387638SSascha Wildner {
184754ba9607SSascha Wildner 	struct roff_node *nbl, *nh, *nch, *nnext;
1848070c62a6SFranco Fichtner 	struct mdoc_argv *argv;
184980387638SSascha Wildner 	int		  i, j;
185080387638SSascha Wildner 
185154ba9607SSascha Wildner 	post_bl_norm(mdoc);
185254ba9607SSascha Wildner 
185354ba9607SSascha Wildner 	nh = mdoc->last;
185454ba9607SSascha Wildner 	if (nh->norm->Bl.type != LIST_column) {
185554ba9607SSascha Wildner 		if ((nch = nh->child) == NULL)
185654ba9607SSascha Wildner 			return;
185754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS,
185854ba9607SSascha Wildner 		    nch->line, nch->pos, "Bl ... %s", nch->string);
185954ba9607SSascha Wildner 		while (nch != NULL) {
186054ba9607SSascha Wildner 			roff_node_delete(mdoc, nch);
186154ba9607SSascha Wildner 			nch = nh->child;
186254ba9607SSascha Wildner 		}
186354ba9607SSascha Wildner 		return;
186454ba9607SSascha Wildner 	}
186580387638SSascha Wildner 
186680387638SSascha Wildner 	/*
1867070c62a6SFranco Fichtner 	 * Append old-style lists, where the column width specifiers
186880387638SSascha Wildner 	 * trail as macro parameters, to the new-style ("normal-form")
186980387638SSascha Wildner 	 * lists where they're argument values following -column.
187080387638SSascha Wildner 	 */
187180387638SSascha Wildner 
187254ba9607SSascha Wildner 	if (nh->child == NULL)
187354ba9607SSascha Wildner 		return;
187480387638SSascha Wildner 
187554ba9607SSascha Wildner 	nbl = nh->parent;
187654ba9607SSascha Wildner 	for (j = 0; j < (int)nbl->args->argc; j++)
187754ba9607SSascha Wildner 		if (nbl->args->argv[j].arg == MDOC_Column)
187880387638SSascha Wildner 			break;
187980387638SSascha Wildner 
188054ba9607SSascha Wildner 	assert(j < (int)nbl->args->argc);
188180387638SSascha Wildner 
188280387638SSascha Wildner 	/*
1883a4c7eb57SSascha Wildner 	 * Accommodate for new-style groff column syntax.  Shuffle the
188480387638SSascha Wildner 	 * child nodes, all of which must be TEXT, as arguments for the
188580387638SSascha Wildner 	 * column field.  Then, delete the head children.
188680387638SSascha Wildner 	 */
188780387638SSascha Wildner 
188854ba9607SSascha Wildner 	argv = nbl->args->argv + j;
1889070c62a6SFranco Fichtner 	i = argv->sz;
189054ba9607SSascha Wildner 	for (nch = nh->child; nch != NULL; nch = nch->next)
189154ba9607SSascha Wildner 		argv->sz++;
1892070c62a6SFranco Fichtner 	argv->value = mandoc_reallocarray(argv->value,
1893070c62a6SFranco Fichtner 	    argv->sz, sizeof(char *));
189480387638SSascha Wildner 
189554ba9607SSascha Wildner 	nh->norm->Bl.ncols = argv->sz;
189654ba9607SSascha Wildner 	nh->norm->Bl.cols = (void *)argv->value;
189780387638SSascha Wildner 
189854ba9607SSascha Wildner 	for (nch = nh->child; nch != NULL; nch = nnext) {
189954ba9607SSascha Wildner 		argv->value[i++] = nch->string;
190054ba9607SSascha Wildner 		nch->string = NULL;
190154ba9607SSascha Wildner 		nnext = nch->next;
190254ba9607SSascha Wildner 		roff_node_delete(NULL, nch);
190354ba9607SSascha Wildner 	}
190454ba9607SSascha Wildner 	nh->child = NULL;
190580387638SSascha Wildner }
190680387638SSascha Wildner 
190754ba9607SSascha Wildner static void
post_bl(POST_ARGS)190880387638SSascha Wildner post_bl(POST_ARGS)
190980387638SSascha Wildner {
1910*99db7d0eSSascha Wildner 	struct roff_node	*nbody;           /* of the Bl */
191154ba9607SSascha Wildner 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
191254ba9607SSascha Wildner 	const char		*prev_Er;
191354ba9607SSascha Wildner 	int			 order;
191480387638SSascha Wildner 
19157888c61dSFranco Fichtner 	nbody = mdoc->last;
19167888c61dSFranco Fichtner 	switch (nbody->type) {
191754ba9607SSascha Wildner 	case ROFFT_BLOCK:
191854ba9607SSascha Wildner 		post_bl_block(mdoc);
191954ba9607SSascha Wildner 		return;
192054ba9607SSascha Wildner 	case ROFFT_HEAD:
192154ba9607SSascha Wildner 		post_bl_head(mdoc);
192254ba9607SSascha Wildner 		return;
192354ba9607SSascha Wildner 	case ROFFT_BODY:
192480387638SSascha Wildner 		break;
19257888c61dSFranco Fichtner 	default:
192654ba9607SSascha Wildner 		return;
192780387638SSascha Wildner 	}
192854ba9607SSascha Wildner 	if (nbody->end != ENDBODY_NOT)
192954ba9607SSascha Wildner 		return;
1930070c62a6SFranco Fichtner 
1931*99db7d0eSSascha Wildner 	/*
1932*99db7d0eSSascha Wildner 	 * Up to the first item, move nodes before the list,
1933*99db7d0eSSascha Wildner 	 * but leave transparent nodes where they are
1934*99db7d0eSSascha Wildner 	 * if they precede an item.
1935*99db7d0eSSascha Wildner 	 * The next non-transparent node is kept in nchild.
1936*99db7d0eSSascha Wildner 	 * It only needs to be updated after a non-transparent
1937*99db7d0eSSascha Wildner 	 * node was moved out, and at the very beginning
1938*99db7d0eSSascha Wildner 	 * when no node at all was moved yet.
1939*99db7d0eSSascha Wildner 	 */
1940*99db7d0eSSascha Wildner 
1941*99db7d0eSSascha Wildner 	nchild = mdoc->last;
1942*99db7d0eSSascha Wildner 	for (;;) {
1943*99db7d0eSSascha Wildner 		if (nchild == mdoc->last)
1944*99db7d0eSSascha Wildner 			nchild = roff_node_child(nbody);
194554ba9607SSascha Wildner 		if (nchild == NULL) {
1946*99db7d0eSSascha Wildner 			mdoc->last = nbody;
194754ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BLK_EMPTY,
194854ba9607SSascha Wildner 			    nbody->line, nbody->pos, "Bl");
194954ba9607SSascha Wildner 			return;
195054ba9607SSascha Wildner 		}
1951*99db7d0eSSascha Wildner 		if (nchild->tok == MDOC_It) {
1952*99db7d0eSSascha Wildner 			mdoc->last = nbody;
1953*99db7d0eSSascha Wildner 			break;
1954*99db7d0eSSascha Wildner 		}
1955*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line,
1956*99db7d0eSSascha Wildner 		    nbody->child->pos, "%s", roff_name[nbody->child->tok]);
1957*99db7d0eSSascha Wildner 		if (nbody->parent->prev == NULL) {
1958*99db7d0eSSascha Wildner 			mdoc->last = nbody->parent->parent;
1959*99db7d0eSSascha Wildner 			mdoc->next = ROFF_NEXT_CHILD;
1960*99db7d0eSSascha Wildner 		} else {
1961*99db7d0eSSascha Wildner 			mdoc->last = nbody->parent->prev;
1962*99db7d0eSSascha Wildner 			mdoc->next = ROFF_NEXT_SIBLING;
1963*99db7d0eSSascha Wildner 		}
1964*99db7d0eSSascha Wildner 		roff_node_relink(mdoc, nbody->child);
19657888c61dSFranco Fichtner 	}
19667888c61dSFranco Fichtner 
196754ba9607SSascha Wildner 	/*
1968*99db7d0eSSascha Wildner 	 * We have reached the first item,
1969*99db7d0eSSascha Wildner 	 * so moving nodes out is no longer possible.
1970*99db7d0eSSascha Wildner 	 * But in .Bl -column, the first rows may be implicit,
197154ba9607SSascha Wildner 	 * that is, they may not start with .It macros.
197254ba9607SSascha Wildner 	 * Such rows may be followed by nodes generated on the
1973*99db7d0eSSascha Wildner 	 * roff level, for example .TS.
1974*99db7d0eSSascha Wildner 	 * Wrap such roff nodes into an implicit row.
197554ba9607SSascha Wildner 	 */
197654ba9607SSascha Wildner 
1977*99db7d0eSSascha Wildner 	while (nchild != NULL) {
1978*99db7d0eSSascha Wildner 		if (nchild->tok == MDOC_It) {
1979*99db7d0eSSascha Wildner 			nchild = roff_node_next(nchild);
1980*99db7d0eSSascha Wildner 			continue;
1981*99db7d0eSSascha Wildner 		}
1982*99db7d0eSSascha Wildner 		nnext = nchild->next;
1983*99db7d0eSSascha Wildner 		mdoc->last = nchild->prev;
198454ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
1985*99db7d0eSSascha Wildner 		roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1986*99db7d0eSSascha Wildner 		roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
198754ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
1988*99db7d0eSSascha Wildner 		roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
198954ba9607SSascha Wildner 		while (nchild->tok != MDOC_It) {
199054ba9607SSascha Wildner 			roff_node_relink(mdoc, nchild);
1991*99db7d0eSSascha Wildner 			if (nnext == NULL)
199254ba9607SSascha Wildner 				break;
1993*99db7d0eSSascha Wildner 			nchild = nnext;
199454ba9607SSascha Wildner 			nnext = nchild->next;
199554ba9607SSascha Wildner 			mdoc->next = ROFF_NEXT_SIBLING;
199654ba9607SSascha Wildner 		}
199754ba9607SSascha Wildner 		mdoc->last = nbody;
199880387638SSascha Wildner 	}
199980387638SSascha Wildner 
200054ba9607SSascha Wildner 	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
200154ba9607SSascha Wildner 		return;
200254ba9607SSascha Wildner 
200354ba9607SSascha Wildner 	prev_Er = NULL;
200454ba9607SSascha Wildner 	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
200554ba9607SSascha Wildner 		if (nchild->tok != MDOC_It)
200654ba9607SSascha Wildner 			continue;
200754ba9607SSascha Wildner 		if ((nnext = nchild->head->child) == NULL)
200854ba9607SSascha Wildner 			continue;
200954ba9607SSascha Wildner 		if (nnext->type == ROFFT_BLOCK)
201054ba9607SSascha Wildner 			nnext = nnext->body->child;
201154ba9607SSascha Wildner 		if (nnext == NULL || nnext->tok != MDOC_Er)
201254ba9607SSascha Wildner 			continue;
201354ba9607SSascha Wildner 		nnext = nnext->child;
201454ba9607SSascha Wildner 		if (prev_Er != NULL) {
201554ba9607SSascha Wildner 			order = strcmp(prev_Er, nnext->string);
201654ba9607SSascha Wildner 			if (order > 0)
201754ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ER_ORDER,
201854ba9607SSascha Wildner 				    nnext->line, nnext->pos,
201954ba9607SSascha Wildner 				    "Er %s %s (NetBSD)",
202054ba9607SSascha Wildner 				    prev_Er, nnext->string);
202154ba9607SSascha Wildner 			else if (order == 0)
202254ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ER_REP,
202354ba9607SSascha Wildner 				    nnext->line, nnext->pos,
202454ba9607SSascha Wildner 				    "Er %s (NetBSD)", prev_Er);
202554ba9607SSascha Wildner 		}
202654ba9607SSascha Wildner 		prev_Er = nnext->string;
202754ba9607SSascha Wildner 	}
202880387638SSascha Wildner }
202980387638SSascha Wildner 
203054ba9607SSascha Wildner static void
post_bk(POST_ARGS)2031070c62a6SFranco Fichtner post_bk(POST_ARGS)
203280387638SSascha Wildner {
203354ba9607SSascha Wildner 	struct roff_node	*n;
203480387638SSascha Wildner 
203554ba9607SSascha Wildner 	n = mdoc->last;
203654ba9607SSascha Wildner 
203754ba9607SSascha Wildner 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
203854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
203954ba9607SSascha Wildner 		roff_node_delete(mdoc, n);
204054ba9607SSascha Wildner 	}
204180387638SSascha Wildner }
204280387638SSascha Wildner 
204354ba9607SSascha Wildner static void
post_sm(POST_ARGS)204454ba9607SSascha Wildner post_sm(POST_ARGS)
2045070c62a6SFranco Fichtner {
204654ba9607SSascha Wildner 	struct roff_node	*nch;
204780387638SSascha Wildner 
2048070c62a6SFranco Fichtner 	nch = mdoc->last->child;
2049070c62a6SFranco Fichtner 
205054ba9607SSascha Wildner 	if (nch == NULL) {
2051070c62a6SFranco Fichtner 		mdoc->flags ^= MDOC_SMOFF;
205254ba9607SSascha Wildner 		return;
2053070c62a6SFranco Fichtner 	}
2054070c62a6SFranco Fichtner 
205554ba9607SSascha Wildner 	assert(nch->type == ROFFT_TEXT);
2056070c62a6SFranco Fichtner 
205754ba9607SSascha Wildner 	if ( ! strcmp(nch->string, "on")) {
20587888c61dSFranco Fichtner 		mdoc->flags &= ~MDOC_SMOFF;
205954ba9607SSascha Wildner 		return;
20607888c61dSFranco Fichtner 	}
206154ba9607SSascha Wildner 	if ( ! strcmp(nch->string, "off")) {
20627888c61dSFranco Fichtner 		mdoc->flags |= MDOC_SMOFF;
206354ba9607SSascha Wildner 		return;
20647888c61dSFranco Fichtner 	}
206580387638SSascha Wildner 
206654ba9607SSascha Wildner 	mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
206754ba9607SSascha Wildner 	    "%s %s", roff_name[mdoc->last->tok], nch->string);
206854ba9607SSascha Wildner 	roff_node_relink(mdoc, nch);
206954ba9607SSascha Wildner 	return;
207080387638SSascha Wildner }
207180387638SSascha Wildner 
207254ba9607SSascha Wildner static void
post_root(POST_ARGS)207380387638SSascha Wildner post_root(POST_ARGS)
207480387638SSascha Wildner {
207554ba9607SSascha Wildner 	struct roff_node *n;
207680387638SSascha Wildner 
2077070c62a6SFranco Fichtner 	/* Add missing prologue data. */
207880387638SSascha Wildner 
2079070c62a6SFranco Fichtner 	if (mdoc->meta.date == NULL)
2080*99db7d0eSSascha Wildner 		mdoc->meta.date = mandoc_normdate(NULL, NULL);
208180387638SSascha Wildner 
2082070c62a6SFranco Fichtner 	if (mdoc->meta.title == NULL) {
208354ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
2084070c62a6SFranco Fichtner 		mdoc->meta.title = mandoc_strdup("UNTITLED");
2085070c62a6SFranco Fichtner 	}
2086070c62a6SFranco Fichtner 
2087070c62a6SFranco Fichtner 	if (mdoc->meta.vol == NULL)
2088070c62a6SFranco Fichtner 		mdoc->meta.vol = mandoc_strdup("LOCAL");
2089070c62a6SFranco Fichtner 
2090070c62a6SFranco Fichtner 	if (mdoc->meta.os == NULL) {
209154ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
2092070c62a6SFranco Fichtner 		mdoc->meta.os = mandoc_strdup("");
209354ba9607SSascha Wildner 	} else if (mdoc->meta.os_e &&
209454ba9607SSascha Wildner 	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
209554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
209654ba9607SSascha Wildner 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
209754ba9607SSascha Wildner 		    "(OpenBSD)" : "(NetBSD)");
209880387638SSascha Wildner 
209954ba9607SSascha Wildner 	if (mdoc->meta.arch != NULL &&
210054ba9607SSascha Wildner 	    arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
210154ba9607SSascha Wildner 		n = mdoc->meta.first->child;
210254ba9607SSascha Wildner 		while (n->tok != MDOC_Dt ||
210354ba9607SSascha Wildner 		    n->child == NULL ||
210454ba9607SSascha Wildner 		    n->child->next == NULL ||
210554ba9607SSascha Wildner 		    n->child->next->next == NULL)
210654ba9607SSascha Wildner 			n = n->next;
210754ba9607SSascha Wildner 		n = n->child->next->next;
210854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
210954ba9607SSascha Wildner 		    "Dt ... %s %s", mdoc->meta.arch,
211054ba9607SSascha Wildner 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
211154ba9607SSascha Wildner 		    "(OpenBSD)" : "(NetBSD)");
211254ba9607SSascha Wildner 	}
211380387638SSascha Wildner 
211480387638SSascha Wildner 	/* Check that we begin with a proper `Sh'. */
211580387638SSascha Wildner 
211654ba9607SSascha Wildner 	n = mdoc->meta.first->child;
211754ba9607SSascha Wildner 	while (n != NULL &&
211854ba9607SSascha Wildner 	    (n->type == ROFFT_COMMENT ||
211954ba9607SSascha Wildner 	     (n->tok >= MDOC_Dd &&
212054ba9607SSascha Wildner 	      mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
212154ba9607SSascha Wildner 		n = n->next;
212280387638SSascha Wildner 
212354ba9607SSascha Wildner 	if (n == NULL)
212454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
212554ba9607SSascha Wildner 	else if (n->tok != MDOC_Sh)
212654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
212754ba9607SSascha Wildner 		    "%s", roff_name[n->tok]);
212880387638SSascha Wildner }
212980387638SSascha Wildner 
213054ba9607SSascha Wildner static void
post_rs(POST_ARGS)213180387638SSascha Wildner post_rs(POST_ARGS)
213280387638SSascha Wildner {
213354ba9607SSascha Wildner 	struct roff_node *np, *nch, *next, *prev;
213480387638SSascha Wildner 	int		  i, j;
213580387638SSascha Wildner 
213654ba9607SSascha Wildner 	np = mdoc->last;
213754ba9607SSascha Wildner 
213854ba9607SSascha Wildner 	if (np->type != ROFFT_BODY)
213954ba9607SSascha Wildner 		return;
214054ba9607SSascha Wildner 
214154ba9607SSascha Wildner 	if (np->child == NULL) {
214254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
214354ba9607SSascha Wildner 		return;
214480387638SSascha Wildner 	}
214580387638SSascha Wildner 
214680387638SSascha Wildner 	/*
214780387638SSascha Wildner 	 * The full `Rs' block needs special handling to order the
214880387638SSascha Wildner 	 * sub-elements according to `rsord'.  Pick through each element
2149070c62a6SFranco Fichtner 	 * and correctly order it.  This is an insertion sort.
215080387638SSascha Wildner 	 */
215180387638SSascha Wildner 
215280387638SSascha Wildner 	next = NULL;
215354ba9607SSascha Wildner 	for (nch = np->child->next; nch != NULL; nch = next) {
215454ba9607SSascha Wildner 		/* Determine order number of this child. */
215580387638SSascha Wildner 		for (i = 0; i < RSORD_MAX; i++)
215654ba9607SSascha Wildner 			if (rsord[i] == nch->tok)
215780387638SSascha Wildner 				break;
215880387638SSascha Wildner 
2159070c62a6SFranco Fichtner 		if (i == RSORD_MAX) {
216054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
216154ba9607SSascha Wildner 			    "%s", roff_name[nch->tok]);
2162070c62a6SFranco Fichtner 			i = -1;
216354ba9607SSascha Wildner 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
216454ba9607SSascha Wildner 			np->norm->Rs.quote_T++;
2165070c62a6SFranco Fichtner 
216680387638SSascha Wildner 		/*
216754ba9607SSascha Wildner 		 * Remove this child from the chain.  This somewhat
216854ba9607SSascha Wildner 		 * repeats roff_node_unlink(), but since we're
216980387638SSascha Wildner 		 * just re-ordering, there's no need for the
217080387638SSascha Wildner 		 * full unlink process.
217180387638SSascha Wildner 		 */
217280387638SSascha Wildner 
217354ba9607SSascha Wildner 		if ((next = nch->next) != NULL)
217454ba9607SSascha Wildner 			next->prev = nch->prev;
217580387638SSascha Wildner 
217654ba9607SSascha Wildner 		if ((prev = nch->prev) != NULL)
217754ba9607SSascha Wildner 			prev->next = nch->next;
217880387638SSascha Wildner 
217954ba9607SSascha Wildner 		nch->prev = nch->next = NULL;
218080387638SSascha Wildner 
218180387638SSascha Wildner 		/*
218280387638SSascha Wildner 		 * Scan back until we reach a node that's
218354ba9607SSascha Wildner 		 * to be ordered before this child.
218480387638SSascha Wildner 		 */
218580387638SSascha Wildner 
218680387638SSascha Wildner 		for ( ; prev ; prev = prev->prev) {
218780387638SSascha Wildner 			/* Determine order of `prev'. */
218880387638SSascha Wildner 			for (j = 0; j < RSORD_MAX; j++)
218980387638SSascha Wildner 				if (rsord[j] == prev->tok)
219080387638SSascha Wildner 					break;
2191070c62a6SFranco Fichtner 			if (j == RSORD_MAX)
2192070c62a6SFranco Fichtner 				j = -1;
219380387638SSascha Wildner 
219480387638SSascha Wildner 			if (j <= i)
219580387638SSascha Wildner 				break;
219680387638SSascha Wildner 		}
219780387638SSascha Wildner 
219880387638SSascha Wildner 		/*
219954ba9607SSascha Wildner 		 * Set this child back into its correct place
220054ba9607SSascha Wildner 		 * in front of the `prev' node.
220180387638SSascha Wildner 		 */
220280387638SSascha Wildner 
220354ba9607SSascha Wildner 		nch->prev = prev;
220480387638SSascha Wildner 
220554ba9607SSascha Wildner 		if (prev == NULL) {
220654ba9607SSascha Wildner 			np->child->prev = nch;
220754ba9607SSascha Wildner 			nch->next = np->child;
220854ba9607SSascha Wildner 			np->child = nch;
220980387638SSascha Wildner 		} else {
221054ba9607SSascha Wildner 			if (prev->next)
221154ba9607SSascha Wildner 				prev->next->prev = nch;
221254ba9607SSascha Wildner 			nch->next = prev->next;
221354ba9607SSascha Wildner 			prev->next = nch;
221480387638SSascha Wildner 		}
221580387638SSascha Wildner 	}
221680387638SSascha Wildner }
221780387638SSascha Wildner 
22187888c61dSFranco Fichtner /*
22197888c61dSFranco Fichtner  * For some arguments of some macros,
22207888c61dSFranco Fichtner  * convert all breakable hyphens into ASCII_HYPH.
22217888c61dSFranco Fichtner  */
222254ba9607SSascha Wildner static void
post_hyph(POST_ARGS)22237888c61dSFranco Fichtner post_hyph(POST_ARGS)
22247888c61dSFranco Fichtner {
2225*99db7d0eSSascha Wildner 	struct roff_node	*n, *nch;
22267888c61dSFranco Fichtner 	char			*cp;
22277888c61dSFranco Fichtner 
2228*99db7d0eSSascha Wildner 	n = mdoc->last;
2229*99db7d0eSSascha Wildner 	for (nch = n->child; nch != NULL; nch = nch->next) {
223054ba9607SSascha Wildner 		if (nch->type != ROFFT_TEXT)
22317888c61dSFranco Fichtner 			continue;
22327888c61dSFranco Fichtner 		cp = nch->string;
223354ba9607SSascha Wildner 		if (*cp == '\0')
22347888c61dSFranco Fichtner 			continue;
223554ba9607SSascha Wildner 		while (*(++cp) != '\0')
223654ba9607SSascha Wildner 			if (*cp == '-' &&
22377888c61dSFranco Fichtner 			    isalpha((unsigned char)cp[-1]) &&
2238*99db7d0eSSascha Wildner 			    isalpha((unsigned char)cp[1])) {
2239*99db7d0eSSascha Wildner 				if (n->tag == NULL && n->flags & NODE_ID)
2240*99db7d0eSSascha Wildner 					n->tag = mandoc_strdup(nch->string);
22417888c61dSFranco Fichtner 				*cp = ASCII_HYPH;
22427888c61dSFranco Fichtner 			}
22437888c61dSFranco Fichtner 	}
2244*99db7d0eSSascha Wildner }
22457888c61dSFranco Fichtner 
224654ba9607SSascha Wildner static void
post_ns(POST_ARGS)224760e1e752SSascha Wildner post_ns(POST_ARGS)
224860e1e752SSascha Wildner {
224954ba9607SSascha Wildner 	struct roff_node	*n;
225060e1e752SSascha Wildner 
225154ba9607SSascha Wildner 	n = mdoc->last;
225254ba9607SSascha Wildner 	if (n->flags & NODE_LINE ||
225354ba9607SSascha Wildner 	    (n->next != NULL && n->next->flags & NODE_DELIMC))
225454ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
225560e1e752SSascha Wildner }
225660e1e752SSascha Wildner 
225754ba9607SSascha Wildner static void
post_sx(POST_ARGS)225854ba9607SSascha Wildner post_sx(POST_ARGS)
225954ba9607SSascha Wildner {
226054ba9607SSascha Wildner 	post_delim(mdoc);
226154ba9607SSascha Wildner 	post_hyph(mdoc);
226254ba9607SSascha Wildner }
226354ba9607SSascha Wildner 
226454ba9607SSascha Wildner static void
post_sh(POST_ARGS)226580387638SSascha Wildner post_sh(POST_ARGS)
226680387638SSascha Wildner {
2267*99db7d0eSSascha Wildner 	post_section(mdoc);
2268070c62a6SFranco Fichtner 
226954ba9607SSascha Wildner 	switch (mdoc->last->type) {
227054ba9607SSascha Wildner 	case ROFFT_HEAD:
227154ba9607SSascha Wildner 		post_sh_head(mdoc);
227254ba9607SSascha Wildner 		break;
227354ba9607SSascha Wildner 	case ROFFT_BODY:
227454ba9607SSascha Wildner 		switch (mdoc->lastsec)  {
227554ba9607SSascha Wildner 		case SEC_NAME:
227654ba9607SSascha Wildner 			post_sh_name(mdoc);
227754ba9607SSascha Wildner 			break;
227854ba9607SSascha Wildner 		case SEC_SEE_ALSO:
227954ba9607SSascha Wildner 			post_sh_see_also(mdoc);
228054ba9607SSascha Wildner 			break;
228154ba9607SSascha Wildner 		case SEC_AUTHORS:
228254ba9607SSascha Wildner 			post_sh_authors(mdoc);
228354ba9607SSascha Wildner 			break;
228454ba9607SSascha Wildner 		default:
228554ba9607SSascha Wildner 			break;
228654ba9607SSascha Wildner 		}
228754ba9607SSascha Wildner 		break;
228854ba9607SSascha Wildner 	default:
228954ba9607SSascha Wildner 		break;
229054ba9607SSascha Wildner 	}
229154ba9607SSascha Wildner }
229280387638SSascha Wildner 
229354ba9607SSascha Wildner static void
post_sh_name(POST_ARGS)229454ba9607SSascha Wildner post_sh_name(POST_ARGS)
229554ba9607SSascha Wildner {
229654ba9607SSascha Wildner 	struct roff_node *n;
229754ba9607SSascha Wildner 	int hasnm, hasnd;
229854ba9607SSascha Wildner 
229954ba9607SSascha Wildner 	hasnm = hasnd = 0;
230054ba9607SSascha Wildner 
230154ba9607SSascha Wildner 	for (n = mdoc->last->child; n != NULL; n = n->next) {
230254ba9607SSascha Wildner 		switch (n->tok) {
230354ba9607SSascha Wildner 		case MDOC_Nm:
230454ba9607SSascha Wildner 			if (hasnm && n->child != NULL)
230554ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
230654ba9607SSascha Wildner 				    n->line, n->pos,
230754ba9607SSascha Wildner 				    "Nm %s", n->child->string);
230854ba9607SSascha Wildner 			hasnm = 1;
230954ba9607SSascha Wildner 			continue;
231054ba9607SSascha Wildner 		case MDOC_Nd:
231154ba9607SSascha Wildner 			hasnd = 1;
231254ba9607SSascha Wildner 			if (n->next != NULL)
231354ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_NAMESEC_ND,
231454ba9607SSascha Wildner 				    n->line, n->pos, NULL);
231554ba9607SSascha Wildner 			break;
231654ba9607SSascha Wildner 		case TOKEN_NONE:
231754ba9607SSascha Wildner 			if (n->type == ROFFT_TEXT &&
231854ba9607SSascha Wildner 			    n->string[0] == ',' && n->string[1] == '\0' &&
231954ba9607SSascha Wildner 			    n->next != NULL && n->next->tok == MDOC_Nm) {
232054ba9607SSascha Wildner 				n = n->next;
232154ba9607SSascha Wildner 				continue;
232254ba9607SSascha Wildner 			}
232354ba9607SSascha Wildner 			/* FALLTHROUGH */
232454ba9607SSascha Wildner 		default:
232554ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_NAMESEC_BAD,
232654ba9607SSascha Wildner 			    n->line, n->pos, "%s", roff_name[n->tok]);
232754ba9607SSascha Wildner 			continue;
232854ba9607SSascha Wildner 		}
232954ba9607SSascha Wildner 		break;
233054ba9607SSascha Wildner 	}
233154ba9607SSascha Wildner 
233254ba9607SSascha Wildner 	if ( ! hasnm)
233354ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_NAMESEC_NONM,
233454ba9607SSascha Wildner 		    mdoc->last->line, mdoc->last->pos, NULL);
233554ba9607SSascha Wildner 	if ( ! hasnd)
233654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_NAMESEC_NOND,
233754ba9607SSascha Wildner 		    mdoc->last->line, mdoc->last->pos, NULL);
233854ba9607SSascha Wildner }
233954ba9607SSascha Wildner 
234054ba9607SSascha Wildner static void
post_sh_see_also(POST_ARGS)234154ba9607SSascha Wildner post_sh_see_also(POST_ARGS)
234254ba9607SSascha Wildner {
234354ba9607SSascha Wildner 	const struct roff_node	*n;
234454ba9607SSascha Wildner 	const char		*name, *sec;
234554ba9607SSascha Wildner 	const char		*lastname, *lastsec, *lastpunct;
234654ba9607SSascha Wildner 	int			 cmp;
234754ba9607SSascha Wildner 
234854ba9607SSascha Wildner 	n = mdoc->last->child;
234954ba9607SSascha Wildner 	lastname = lastsec = lastpunct = NULL;
235054ba9607SSascha Wildner 	while (n != NULL) {
235154ba9607SSascha Wildner 		if (n->tok != MDOC_Xr ||
235254ba9607SSascha Wildner 		    n->child == NULL ||
235354ba9607SSascha Wildner 		    n->child->next == NULL)
235454ba9607SSascha Wildner 			break;
235554ba9607SSascha Wildner 
235654ba9607SSascha Wildner 		/* Process one .Xr node. */
235754ba9607SSascha Wildner 
235854ba9607SSascha Wildner 		name = n->child->string;
235954ba9607SSascha Wildner 		sec = n->child->next->string;
236054ba9607SSascha Wildner 		if (lastsec != NULL) {
236154ba9607SSascha Wildner 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
236254ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
236354ba9607SSascha Wildner 				    n->pos, "%s before %s(%s)",
236454ba9607SSascha Wildner 				    lastpunct, name, sec);
236554ba9607SSascha Wildner 			cmp = strcmp(lastsec, sec);
236654ba9607SSascha Wildner 			if (cmp > 0)
236754ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
236854ba9607SSascha Wildner 				    n->pos, "%s(%s) after %s(%s)",
236954ba9607SSascha Wildner 				    name, sec, lastname, lastsec);
237054ba9607SSascha Wildner 			else if (cmp == 0 &&
237154ba9607SSascha Wildner 			    strcasecmp(lastname, name) > 0)
237254ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
237354ba9607SSascha Wildner 				    n->pos, "%s after %s", name, lastname);
237454ba9607SSascha Wildner 		}
237554ba9607SSascha Wildner 		lastname = name;
237654ba9607SSascha Wildner 		lastsec = sec;
237754ba9607SSascha Wildner 
237854ba9607SSascha Wildner 		/* Process the following node. */
237954ba9607SSascha Wildner 
238054ba9607SSascha Wildner 		n = n->next;
238154ba9607SSascha Wildner 		if (n == NULL)
238254ba9607SSascha Wildner 			break;
238354ba9607SSascha Wildner 		if (n->tok == MDOC_Xr) {
238454ba9607SSascha Wildner 			lastpunct = "none";
238554ba9607SSascha Wildner 			continue;
238654ba9607SSascha Wildner 		}
238754ba9607SSascha Wildner 		if (n->type != ROFFT_TEXT)
238854ba9607SSascha Wildner 			break;
238954ba9607SSascha Wildner 		for (name = n->string; *name != '\0'; name++)
239054ba9607SSascha Wildner 			if (isalpha((const unsigned char)*name))
239154ba9607SSascha Wildner 				return;
239254ba9607SSascha Wildner 		lastpunct = n->string;
239354ba9607SSascha Wildner 		if (n->next == NULL || n->next->tok == MDOC_Rs)
239454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
239554ba9607SSascha Wildner 			    n->pos, "%s after %s(%s)",
239654ba9607SSascha Wildner 			    lastpunct, lastname, lastsec);
239754ba9607SSascha Wildner 		n = n->next;
239854ba9607SSascha Wildner 	}
239980387638SSascha Wildner }
240080387638SSascha Wildner 
240180387638SSascha Wildner static int
child_an(const struct roff_node * n)240254ba9607SSascha Wildner child_an(const struct roff_node *n)
240380387638SSascha Wildner {
240480387638SSascha Wildner 
240554ba9607SSascha Wildner 	for (n = n->child; n != NULL; n = n->next)
240654ba9607SSascha Wildner 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
240754ba9607SSascha Wildner 			return 1;
240854ba9607SSascha Wildner 	return 0;
240954ba9607SSascha Wildner }
241054ba9607SSascha Wildner 
241154ba9607SSascha Wildner static void
post_sh_authors(POST_ARGS)241254ba9607SSascha Wildner post_sh_authors(POST_ARGS)
241354ba9607SSascha Wildner {
241454ba9607SSascha Wildner 
241554ba9607SSascha Wildner 	if ( ! child_an(mdoc->last))
241654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_AN_MISSING,
241754ba9607SSascha Wildner 		    mdoc->last->line, mdoc->last->pos, NULL);
241854ba9607SSascha Wildner }
241980387638SSascha Wildner 
242080387638SSascha Wildner /*
242154ba9607SSascha Wildner  * Return an upper bound for the string distance (allowing
242254ba9607SSascha Wildner  * transpositions).  Not a full Levenshtein implementation
242354ba9607SSascha Wildner  * because Levenshtein is quadratic in the string length
242454ba9607SSascha Wildner  * and this function is called for every standard name,
242554ba9607SSascha Wildner  * so the check for each custom name would be cubic.
242654ba9607SSascha Wildner  * The following crude heuristics is linear, resulting
242754ba9607SSascha Wildner  * in quadratic behaviour for checking one custom name,
242854ba9607SSascha Wildner  * which does not cause measurable slowdown.
242980387638SSascha Wildner  */
243080387638SSascha Wildner static int
similar(const char * s1,const char * s2)243154ba9607SSascha Wildner similar(const char *s1, const char *s2)
243254ba9607SSascha Wildner {
243354ba9607SSascha Wildner 	const int	maxdist = 3;
243454ba9607SSascha Wildner 	int		dist = 0;
243554ba9607SSascha Wildner 
243654ba9607SSascha Wildner 	while (s1[0] != '\0' && s2[0] != '\0') {
243754ba9607SSascha Wildner 		if (s1[0] == s2[0]) {
243854ba9607SSascha Wildner 			s1++;
243954ba9607SSascha Wildner 			s2++;
244054ba9607SSascha Wildner 			continue;
244154ba9607SSascha Wildner 		}
244254ba9607SSascha Wildner 		if (++dist > maxdist)
244354ba9607SSascha Wildner 			return INT_MAX;
244454ba9607SSascha Wildner 		if (s1[1] == s2[1]) {  /* replacement */
244554ba9607SSascha Wildner 			s1++;
244654ba9607SSascha Wildner 			s2++;
244754ba9607SSascha Wildner 		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
244854ba9607SSascha Wildner 			s1 += 2;	/* transposition */
244954ba9607SSascha Wildner 			s2 += 2;
245054ba9607SSascha Wildner 		} else if (s1[0] == s2[1])  /* insertion */
245154ba9607SSascha Wildner 			s2++;
245254ba9607SSascha Wildner 		else if (s1[1] == s2[0])  /* deletion */
245354ba9607SSascha Wildner 			s1++;
245454ba9607SSascha Wildner 		else
245554ba9607SSascha Wildner 			return INT_MAX;
245654ba9607SSascha Wildner 	}
245754ba9607SSascha Wildner 	dist += strlen(s1) + strlen(s2);
245854ba9607SSascha Wildner 	return dist > maxdist ? INT_MAX : dist;
245954ba9607SSascha Wildner }
246054ba9607SSascha Wildner 
246154ba9607SSascha Wildner static void
post_sh_head(POST_ARGS)246280387638SSascha Wildner post_sh_head(POST_ARGS)
246380387638SSascha Wildner {
246454ba9607SSascha Wildner 	struct roff_node	*nch;
2465070c62a6SFranco Fichtner 	const char		*goodsec;
246654ba9607SSascha Wildner 	const char *const	*testsec;
246754ba9607SSascha Wildner 	int			 dist, mindist;
246854ba9607SSascha Wildner 	enum roff_sec		 sec;
246980387638SSascha Wildner 
247080387638SSascha Wildner 	/*
247180387638SSascha Wildner 	 * Process a new section.  Sections are either "named" or
247280387638SSascha Wildner 	 * "custom".  Custom sections are user-defined, while named ones
247380387638SSascha Wildner 	 * follow a conventional order and may only appear in certain
247480387638SSascha Wildner 	 * manual sections.
247580387638SSascha Wildner 	 */
247680387638SSascha Wildner 
247754ba9607SSascha Wildner 	sec = mdoc->last->sec;
247880387638SSascha Wildner 
247980387638SSascha Wildner 	/* The NAME should be first. */
248080387638SSascha Wildner 
248154ba9607SSascha Wildner 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
248254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_NAMESEC_FIRST,
248354ba9607SSascha Wildner 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
248454ba9607SSascha Wildner 		    sec != SEC_CUSTOM ? secnames[sec] :
248554ba9607SSascha Wildner 		    (nch = mdoc->last->child) == NULL ? "" :
248654ba9607SSascha Wildner 		    nch->type == ROFFT_TEXT ? nch->string :
248754ba9607SSascha Wildner 		    roff_name[nch->tok]);
248880387638SSascha Wildner 
248980387638SSascha Wildner 	/* The SYNOPSIS gets special attention in other areas. */
249080387638SSascha Wildner 
249154ba9607SSascha Wildner 	if (sec == SEC_SYNOPSIS) {
24927888c61dSFranco Fichtner 		roff_setreg(mdoc->roff, "nS", 1, '=');
249380387638SSascha Wildner 		mdoc->flags |= MDOC_SYNOPSIS;
24947888c61dSFranco Fichtner 	} else {
24957888c61dSFranco Fichtner 		roff_setreg(mdoc->roff, "nS", 0, '=');
249680387638SSascha Wildner 		mdoc->flags &= ~MDOC_SYNOPSIS;
24977888c61dSFranco Fichtner 	}
2498*99db7d0eSSascha Wildner 	if (sec == SEC_DESCRIPTION)
2499*99db7d0eSSascha Wildner 		fn_prio = TAG_STRONG;
250080387638SSascha Wildner 
250180387638SSascha Wildner 	/* Mark our last section. */
250280387638SSascha Wildner 
250380387638SSascha Wildner 	mdoc->lastsec = sec;
250480387638SSascha Wildner 
250580387638SSascha Wildner 	/* We don't care about custom sections after this. */
250680387638SSascha Wildner 
250754ba9607SSascha Wildner 	if (sec == SEC_CUSTOM) {
250854ba9607SSascha Wildner 		if ((nch = mdoc->last->child) == NULL ||
250954ba9607SSascha Wildner 		    nch->type != ROFFT_TEXT || nch->next != NULL)
251054ba9607SSascha Wildner 			return;
251154ba9607SSascha Wildner 		goodsec = NULL;
251254ba9607SSascha Wildner 		mindist = INT_MAX;
251354ba9607SSascha Wildner 		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
251454ba9607SSascha Wildner 			dist = similar(nch->string, *testsec);
251554ba9607SSascha Wildner 			if (dist < mindist) {
251654ba9607SSascha Wildner 				goodsec = *testsec;
251754ba9607SSascha Wildner 				mindist = dist;
251854ba9607SSascha Wildner 			}
251954ba9607SSascha Wildner 		}
252054ba9607SSascha Wildner 		if (goodsec != NULL)
252154ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
252254ba9607SSascha Wildner 			    "Sh %s instead of %s", nch->string, goodsec);
252354ba9607SSascha Wildner 		return;
2524070c62a6SFranco Fichtner 	}
252580387638SSascha Wildner 
252680387638SSascha Wildner 	/*
252780387638SSascha Wildner 	 * Check whether our non-custom section is being repeated or is
252880387638SSascha Wildner 	 * out of order.
252980387638SSascha Wildner 	 */
253080387638SSascha Wildner 
253180387638SSascha Wildner 	if (sec == mdoc->lastnamed)
253254ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
253354ba9607SSascha Wildner 		    mdoc->last->pos, "Sh %s", secnames[sec]);
253480387638SSascha Wildner 
253580387638SSascha Wildner 	if (sec < mdoc->lastnamed)
253654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
253754ba9607SSascha Wildner 		    mdoc->last->pos, "Sh %s", secnames[sec]);
253880387638SSascha Wildner 
253980387638SSascha Wildner 	/* Mark the last named section. */
254080387638SSascha Wildner 
254180387638SSascha Wildner 	mdoc->lastnamed = sec;
254280387638SSascha Wildner 
254380387638SSascha Wildner 	/* Check particular section/manual conventions. */
254480387638SSascha Wildner 
254554ba9607SSascha Wildner 	if (mdoc->meta.msec == NULL)
254654ba9607SSascha Wildner 		return;
254780387638SSascha Wildner 
2548070c62a6SFranco Fichtner 	goodsec = NULL;
254980387638SSascha Wildner 	switch (sec) {
2550070c62a6SFranco Fichtner 	case SEC_ERRORS:
2551070c62a6SFranco Fichtner 		if (*mdoc->meta.msec == '4')
2552070c62a6SFranco Fichtner 			break;
2553070c62a6SFranco Fichtner 		goodsec = "2, 3, 4, 9";
255480387638SSascha Wildner 		/* FALLTHROUGH */
2555070c62a6SFranco Fichtner 	case SEC_RETURN_VALUES:
2556070c62a6SFranco Fichtner 	case SEC_LIBRARY:
255780387638SSascha Wildner 		if (*mdoc->meta.msec == '2')
255880387638SSascha Wildner 			break;
255980387638SSascha Wildner 		if (*mdoc->meta.msec == '3')
256080387638SSascha Wildner 			break;
2561070c62a6SFranco Fichtner 		if (NULL == goodsec)
2562070c62a6SFranco Fichtner 			goodsec = "2, 3, 9";
2563070c62a6SFranco Fichtner 		/* FALLTHROUGH */
2564070c62a6SFranco Fichtner 	case SEC_CONTEXT:
256580387638SSascha Wildner 		if (*mdoc->meta.msec == '9')
256680387638SSascha Wildner 			break;
2567070c62a6SFranco Fichtner 		if (NULL == goodsec)
2568070c62a6SFranco Fichtner 			goodsec = "9";
256954ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_SEC_MSEC,
2570070c62a6SFranco Fichtner 		    mdoc->last->line, mdoc->last->pos,
257154ba9607SSascha Wildner 		    "Sh %s for %s only", secnames[sec], goodsec);
257280387638SSascha Wildner 		break;
257380387638SSascha Wildner 	default:
257480387638SSascha Wildner 		break;
257580387638SSascha Wildner 	}
257680387638SSascha Wildner }
257780387638SSascha Wildner 
257854ba9607SSascha Wildner static void
post_xr(POST_ARGS)257954ba9607SSascha Wildner post_xr(POST_ARGS)
258080387638SSascha Wildner {
258154ba9607SSascha Wildner 	struct roff_node *n, *nch;
258280387638SSascha Wildner 
258360e1e752SSascha Wildner 	n = mdoc->last;
258454ba9607SSascha Wildner 	nch = n->child;
258554ba9607SSascha Wildner 	if (nch->next == NULL) {
258654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_XR_NOSEC,
258754ba9607SSascha Wildner 		    n->line, n->pos, "Xr %s", nch->string);
258854ba9607SSascha Wildner 	} else {
258954ba9607SSascha Wildner 		assert(nch->next == n->last);
259054ba9607SSascha Wildner 		if(mandoc_xr_add(nch->next->string, nch->string,
259154ba9607SSascha Wildner 		    nch->line, nch->pos))
259254ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_XR_SELF,
259354ba9607SSascha Wildner 			    nch->line, nch->pos, "Xr %s %s",
259454ba9607SSascha Wildner 			    nch->string, nch->next->string);
259554ba9607SSascha Wildner 	}
259654ba9607SSascha Wildner 	post_delim_nb(mdoc);
259754ba9607SSascha Wildner }
259854ba9607SSascha Wildner 
259954ba9607SSascha Wildner static void
post_section(POST_ARGS)2600*99db7d0eSSascha Wildner post_section(POST_ARGS)
260154ba9607SSascha Wildner {
2602*99db7d0eSSascha Wildner 	struct roff_node *n, *nch;
2603*99db7d0eSSascha Wildner 	char		 *cp, *tag;
260454ba9607SSascha Wildner 
2605*99db7d0eSSascha Wildner 	n = mdoc->last;
2606*99db7d0eSSascha Wildner 	switch (n->type) {
260754ba9607SSascha Wildner 	case ROFFT_BLOCK:
260854ba9607SSascha Wildner 		post_prevpar(mdoc);
260954ba9607SSascha Wildner 		return;
261054ba9607SSascha Wildner 	case ROFFT_HEAD:
2611*99db7d0eSSascha Wildner 		tag = NULL;
2612*99db7d0eSSascha Wildner 		deroff(&tag, n);
2613*99db7d0eSSascha Wildner 		if (tag != NULL) {
2614*99db7d0eSSascha Wildner 			for (cp = tag; *cp != '\0'; cp++)
2615*99db7d0eSSascha Wildner 				if (*cp == ' ')
2616*99db7d0eSSascha Wildner 					*cp = '_';
2617*99db7d0eSSascha Wildner 			if ((nch = n->child) != NULL &&
2618*99db7d0eSSascha Wildner 			    nch->type == ROFFT_TEXT &&
2619*99db7d0eSSascha Wildner 			    strcmp(nch->string, tag) == 0)
2620*99db7d0eSSascha Wildner 				tag_put(NULL, TAG_STRONG, n);
2621*99db7d0eSSascha Wildner 			else
2622*99db7d0eSSascha Wildner 				tag_put(tag, TAG_FALLBACK, n);
2623*99db7d0eSSascha Wildner 			free(tag);
2624*99db7d0eSSascha Wildner 		}
262554ba9607SSascha Wildner 		post_delim(mdoc);
262654ba9607SSascha Wildner 		post_hyph(mdoc);
262754ba9607SSascha Wildner 		return;
262854ba9607SSascha Wildner 	case ROFFT_BODY:
262954ba9607SSascha Wildner 		break;
263054ba9607SSascha Wildner 	default:
263154ba9607SSascha Wildner 		return;
263254ba9607SSascha Wildner 	}
2633*99db7d0eSSascha Wildner 	if ((nch = n->child) != NULL &&
2634*99db7d0eSSascha Wildner 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br ||
2635*99db7d0eSSascha Wildner 	     nch->tok == ROFF_sp)) {
2636*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2637*99db7d0eSSascha Wildner 		    "%s after %s", roff_name[nch->tok],
2638*99db7d0eSSascha Wildner 		    roff_name[n->tok]);
2639*99db7d0eSSascha Wildner 		roff_node_delete(mdoc, nch);
264054ba9607SSascha Wildner 	}
2641*99db7d0eSSascha Wildner 	if ((nch = n->last) != NULL &&
2642*99db7d0eSSascha Wildner 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) {
2643*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2644*99db7d0eSSascha Wildner 		    "%s at the end of %s", roff_name[nch->tok],
2645*99db7d0eSSascha Wildner 		    roff_name[n->tok]);
2646*99db7d0eSSascha Wildner 		roff_node_delete(mdoc, nch);
264754ba9607SSascha Wildner 	}
264854ba9607SSascha Wildner }
264954ba9607SSascha Wildner 
265054ba9607SSascha Wildner static void
post_prevpar(POST_ARGS)265154ba9607SSascha Wildner post_prevpar(POST_ARGS)
265254ba9607SSascha Wildner {
2653*99db7d0eSSascha Wildner 	struct roff_node *n, *np;
265454ba9607SSascha Wildner 
265554ba9607SSascha Wildner 	n = mdoc->last;
265654ba9607SSascha Wildner 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
265754ba9607SSascha Wildner 		return;
2658*99db7d0eSSascha Wildner 	if ((np = roff_node_prev(n)) == NULL)
2659*99db7d0eSSascha Wildner 		return;
266054ba9607SSascha Wildner 
266154ba9607SSascha Wildner 	/*
266254ba9607SSascha Wildner 	 * Don't allow `Pp' prior to a paragraph-type
266354ba9607SSascha Wildner 	 * block: `Pp' or non-compact `Bd' or `Bl'.
266454ba9607SSascha Wildner 	 */
266554ba9607SSascha Wildner 
2666*99db7d0eSSascha Wildner 	if (np->tok != MDOC_Pp && np->tok != ROFF_br)
266754ba9607SSascha Wildner 		return;
266854ba9607SSascha Wildner 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
266954ba9607SSascha Wildner 		return;
267054ba9607SSascha Wildner 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
267154ba9607SSascha Wildner 		return;
267254ba9607SSascha Wildner 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
267354ba9607SSascha Wildner 		return;
267454ba9607SSascha Wildner 
2675*99db7d0eSSascha Wildner 	mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
2676*99db7d0eSSascha Wildner 	    "%s before %s", roff_name[np->tok], roff_name[n->tok]);
2677*99db7d0eSSascha Wildner 	roff_node_delete(mdoc, np);
267854ba9607SSascha Wildner }
267954ba9607SSascha Wildner 
268054ba9607SSascha Wildner static void
post_par(POST_ARGS)268154ba9607SSascha Wildner post_par(POST_ARGS)
268254ba9607SSascha Wildner {
268354ba9607SSascha Wildner 	struct roff_node *np;
268454ba9607SSascha Wildner 
2685*99db7d0eSSascha Wildner 	fn_prio = TAG_STRONG;
268654ba9607SSascha Wildner 	post_prevpar(mdoc);
268754ba9607SSascha Wildner 
268854ba9607SSascha Wildner 	np = mdoc->last;
268954ba9607SSascha Wildner 	if (np->child != NULL)
269054ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
269154ba9607SSascha Wildner 		    "%s %s", roff_name[np->tok], np->child->string);
269254ba9607SSascha Wildner }
269354ba9607SSascha Wildner 
269454ba9607SSascha Wildner static void
post_dd(POST_ARGS)269554ba9607SSascha Wildner post_dd(POST_ARGS)
269654ba9607SSascha Wildner {
269754ba9607SSascha Wildner 	struct roff_node *n;
269854ba9607SSascha Wildner 
269954ba9607SSascha Wildner 	n = mdoc->last;
270054ba9607SSascha Wildner 	n->flags |= NODE_NOPRT;
270154ba9607SSascha Wildner 
270254ba9607SSascha Wildner 	if (mdoc->meta.date != NULL) {
270354ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
270454ba9607SSascha Wildner 		free(mdoc->meta.date);
270554ba9607SSascha Wildner 	} else if (mdoc->flags & MDOC_PBODY)
270654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
270754ba9607SSascha Wildner 	else if (mdoc->meta.title != NULL)
270854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
270954ba9607SSascha Wildner 		    n->line, n->pos, "Dd after Dt");
271054ba9607SSascha Wildner 	else if (mdoc->meta.os != NULL)
271154ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
271254ba9607SSascha Wildner 		    n->line, n->pos, "Dd after Os");
271354ba9607SSascha Wildner 
2714*99db7d0eSSascha Wildner 	if (mdoc->quick && n != NULL)
2715*99db7d0eSSascha Wildner 		mdoc->meta.date = mandoc_strdup("");
2716*99db7d0eSSascha Wildner 	else
2717*99db7d0eSSascha Wildner 		mdoc->meta.date = mandoc_normdate(n->child, n);
271880387638SSascha Wildner }
271980387638SSascha Wildner 
272054ba9607SSascha Wildner static void
post_dt(POST_ARGS)272180387638SSascha Wildner post_dt(POST_ARGS)
272280387638SSascha Wildner {
272354ba9607SSascha Wildner 	struct roff_node *nn, *n;
272480387638SSascha Wildner 	const char	 *cp;
272580387638SSascha Wildner 	char		 *p;
272680387638SSascha Wildner 
272780387638SSascha Wildner 	n = mdoc->last;
272854ba9607SSascha Wildner 	n->flags |= NODE_NOPRT;
272954ba9607SSascha Wildner 
273054ba9607SSascha Wildner 	if (mdoc->flags & MDOC_PBODY) {
273154ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
273254ba9607SSascha Wildner 		return;
273354ba9607SSascha Wildner 	}
273454ba9607SSascha Wildner 
273554ba9607SSascha Wildner 	if (mdoc->meta.title != NULL)
273654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
273754ba9607SSascha Wildner 	else if (mdoc->meta.os != NULL)
273854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
273954ba9607SSascha Wildner 		    n->line, n->pos, "Dt after Os");
274080387638SSascha Wildner 
274180387638SSascha Wildner 	free(mdoc->meta.title);
2742070c62a6SFranco Fichtner 	free(mdoc->meta.msec);
274380387638SSascha Wildner 	free(mdoc->meta.vol);
274480387638SSascha Wildner 	free(mdoc->meta.arch);
274580387638SSascha Wildner 
2746070c62a6SFranco Fichtner 	mdoc->meta.title = NULL;
2747070c62a6SFranco Fichtner 	mdoc->meta.msec = NULL;
2748070c62a6SFranco Fichtner 	mdoc->meta.vol = NULL;
2749070c62a6SFranco Fichtner 	mdoc->meta.arch = NULL;
275080387638SSascha Wildner 
275154ba9607SSascha Wildner 	/* Mandatory first argument: title. */
275280387638SSascha Wildner 
275354ba9607SSascha Wildner 	nn = n->child;
275454ba9607SSascha Wildner 	if (nn == NULL || *nn->string == '\0') {
275554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
275654ba9607SSascha Wildner 		mdoc->meta.title = mandoc_strdup("UNTITLED");
275754ba9607SSascha Wildner 	} else {
275854ba9607SSascha Wildner 		mdoc->meta.title = mandoc_strdup(nn->string);
275954ba9607SSascha Wildner 
276054ba9607SSascha Wildner 		/* Check that all characters are uppercase. */
276154ba9607SSascha Wildner 
276254ba9607SSascha Wildner 		for (p = nn->string; *p != '\0'; p++)
276354ba9607SSascha Wildner 			if (islower((unsigned char)*p)) {
276454ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
276554ba9607SSascha Wildner 				    nn->pos + (int)(p - nn->string),
2766070c62a6SFranco Fichtner 				    "Dt %s", nn->string);
276780387638SSascha Wildner 				break;
276880387638SSascha Wildner 			}
276980387638SSascha Wildner 	}
277080387638SSascha Wildner 
277154ba9607SSascha Wildner 	/* Mandatory second argument: section. */
277280387638SSascha Wildner 
277354ba9607SSascha Wildner 	if (nn != NULL)
277454ba9607SSascha Wildner 		nn = nn->next;
277580387638SSascha Wildner 
277654ba9607SSascha Wildner 	if (nn == NULL) {
277754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
2778070c62a6SFranco Fichtner 		    "Dt %s", mdoc->meta.title);
277980387638SSascha Wildner 		mdoc->meta.vol = mandoc_strdup("LOCAL");
278054ba9607SSascha Wildner 		return;  /* msec and arch remain NULL. */
278180387638SSascha Wildner 	}
278280387638SSascha Wildner 
278354ba9607SSascha Wildner 	mdoc->meta.msec = mandoc_strdup(nn->string);
278454ba9607SSascha Wildner 
278554ba9607SSascha Wildner 	/* Infer volume title from section number. */
278680387638SSascha Wildner 
278736342e81SSascha Wildner 	cp = mandoc_a2msec(nn->string);
278854ba9607SSascha Wildner 	if (cp == NULL) {
278954ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_MSEC_BAD,
2790070c62a6SFranco Fichtner 		    nn->line, nn->pos, "Dt ... %s", nn->string);
279180387638SSascha Wildner 		mdoc->meta.vol = mandoc_strdup(nn->string);
2792*99db7d0eSSascha Wildner 	} else {
279354ba9607SSascha Wildner 		mdoc->meta.vol = mandoc_strdup(cp);
2794*99db7d0eSSascha Wildner 		if (mdoc->filesec != '\0' &&
2795*99db7d0eSSascha Wildner 		    mdoc->filesec != *nn->string &&
2796*99db7d0eSSascha Wildner 		    *nn->string >= '1' && *nn->string <= '9')
2797*99db7d0eSSascha Wildner 			mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos,
2798*99db7d0eSSascha Wildner 			    "*.%c vs Dt ... %c", mdoc->filesec, *nn->string);
2799*99db7d0eSSascha Wildner 	}
280054ba9607SSascha Wildner 
280154ba9607SSascha Wildner 	/* Optional third argument: architecture. */
280254ba9607SSascha Wildner 
280354ba9607SSascha Wildner 	if ((nn = nn->next) == NULL)
280454ba9607SSascha Wildner 		return;
280554ba9607SSascha Wildner 
280654ba9607SSascha Wildner 	for (p = nn->string; *p != '\0'; p++)
280754ba9607SSascha Wildner 		*p = tolower((unsigned char)*p);
280854ba9607SSascha Wildner 	mdoc->meta.arch = mandoc_strdup(nn->string);
280954ba9607SSascha Wildner 
281054ba9607SSascha Wildner 	/* Ignore fourth and later arguments. */
281154ba9607SSascha Wildner 
281254ba9607SSascha Wildner 	if ((nn = nn->next) != NULL)
281354ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_ARG_EXCESS,
281454ba9607SSascha Wildner 		    nn->line, nn->pos, "Dt ... %s", nn->string);
281580387638SSascha Wildner }
281680387638SSascha Wildner 
281754ba9607SSascha Wildner static void
post_bx(POST_ARGS)281860e1e752SSascha Wildner post_bx(POST_ARGS)
281960e1e752SSascha Wildner {
282054ba9607SSascha Wildner 	struct roff_node	*n, *nch;
282154ba9607SSascha Wildner 	const char		*macro;
282254ba9607SSascha Wildner 
282354ba9607SSascha Wildner 	post_delim_nb(mdoc);
282454ba9607SSascha Wildner 
282554ba9607SSascha Wildner 	n = mdoc->last;
282654ba9607SSascha Wildner 	nch = n->child;
282754ba9607SSascha Wildner 
282854ba9607SSascha Wildner 	if (nch != NULL) {
282954ba9607SSascha Wildner 		macro = !strcmp(nch->string, "Open") ? "Ox" :
283054ba9607SSascha Wildner 		    !strcmp(nch->string, "Net") ? "Nx" :
283154ba9607SSascha Wildner 		    !strcmp(nch->string, "Free") ? "Fx" :
283254ba9607SSascha Wildner 		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
283354ba9607SSascha Wildner 		if (macro != NULL)
283454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BX,
283554ba9607SSascha Wildner 			    n->line, n->pos, "%s", macro);
283654ba9607SSascha Wildner 		mdoc->last = nch;
283754ba9607SSascha Wildner 		nch = nch->next;
283854ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
283954ba9607SSascha Wildner 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
284054ba9607SSascha Wildner 		mdoc->last->flags |= NODE_NOSRC;
284154ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_SIBLING;
284254ba9607SSascha Wildner 	} else
284354ba9607SSascha Wildner 		mdoc->next = ROFF_NEXT_CHILD;
284454ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
284554ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
284654ba9607SSascha Wildner 
284754ba9607SSascha Wildner 	if (nch == NULL) {
284854ba9607SSascha Wildner 		mdoc->last = n;
284954ba9607SSascha Wildner 		return;
285054ba9607SSascha Wildner 	}
285154ba9607SSascha Wildner 
285254ba9607SSascha Wildner 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
285354ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
285454ba9607SSascha Wildner 	mdoc->next = ROFF_NEXT_SIBLING;
285554ba9607SSascha Wildner 	roff_word_alloc(mdoc, n->line, n->pos, "-");
285654ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
285754ba9607SSascha Wildner 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
285854ba9607SSascha Wildner 	mdoc->last->flags |= NODE_NOSRC;
285954ba9607SSascha Wildner 	mdoc->last = n;
286060e1e752SSascha Wildner 
286160e1e752SSascha Wildner 	/*
286260e1e752SSascha Wildner 	 * Make `Bx's second argument always start with an uppercase
286360e1e752SSascha Wildner 	 * letter.  Groff checks if it's an "accepted" term, but we just
286460e1e752SSascha Wildner 	 * uppercase blindly.
286560e1e752SSascha Wildner 	 */
286660e1e752SSascha Wildner 
286754ba9607SSascha Wildner 	*nch->string = (char)toupper((unsigned char)*nch->string);
286860e1e752SSascha Wildner }
286960e1e752SSascha Wildner 
287054ba9607SSascha Wildner static void
post_os(POST_ARGS)287180387638SSascha Wildner post_os(POST_ARGS)
287280387638SSascha Wildner {
287380387638SSascha Wildner #ifndef OSNAME
287480387638SSascha Wildner 	struct utsname	  utsname;
2875070c62a6SFranco Fichtner 	static char	 *defbuf;
287680387638SSascha Wildner #endif
287754ba9607SSascha Wildner 	struct roff_node *n;
287880387638SSascha Wildner 
287980387638SSascha Wildner 	n = mdoc->last;
288054ba9607SSascha Wildner 	n->flags |= NODE_NOPRT;
288154ba9607SSascha Wildner 
288254ba9607SSascha Wildner 	if (mdoc->meta.os != NULL)
288354ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
288454ba9607SSascha Wildner 	else if (mdoc->flags & MDOC_PBODY)
288554ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
288654ba9607SSascha Wildner 
288754ba9607SSascha Wildner 	post_delim(mdoc);
288880387638SSascha Wildner 
288980387638SSascha Wildner 	/*
2890f88b6c16SFranco Fichtner 	 * Set the operating system by way of the `Os' macro.
2891f88b6c16SFranco Fichtner 	 * The order of precedence is:
2892f88b6c16SFranco Fichtner 	 * 1. the argument of the `Os' macro, unless empty
2893f88b6c16SFranco Fichtner 	 * 2. the -Ios=foo command line argument, if provided
2894f88b6c16SFranco Fichtner 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
2895f88b6c16SFranco Fichtner 	 * 4. "sysname release" from uname(3)
289680387638SSascha Wildner 	 */
289780387638SSascha Wildner 
289880387638SSascha Wildner 	free(mdoc->meta.os);
2899070c62a6SFranco Fichtner 	mdoc->meta.os = NULL;
290054ba9607SSascha Wildner 	deroff(&mdoc->meta.os, n);
2901070c62a6SFranco Fichtner 	if (mdoc->meta.os)
2902070c62a6SFranco Fichtner 		goto out;
290380387638SSascha Wildner 
290454ba9607SSascha Wildner 	if (mdoc->os_s != NULL) {
290554ba9607SSascha Wildner 		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2906070c62a6SFranco Fichtner 		goto out;
2907f88b6c16SFranco Fichtner 	}
2908070c62a6SFranco Fichtner 
290980387638SSascha Wildner #ifdef OSNAME
2910070c62a6SFranco Fichtner 	mdoc->meta.os = mandoc_strdup(OSNAME);
291180387638SSascha Wildner #else /*!OSNAME */
291254ba9607SSascha Wildner 	if (defbuf == NULL) {
291354ba9607SSascha Wildner 		if (uname(&utsname) == -1) {
291454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
2915070c62a6SFranco Fichtner 			defbuf = mandoc_strdup("UNKNOWN");
2916070c62a6SFranco Fichtner 		} else
2917070c62a6SFranco Fichtner 			mandoc_asprintf(&defbuf, "%s %s",
2918070c62a6SFranco Fichtner 			    utsname.sysname, utsname.release);
291980387638SSascha Wildner 	}
2920070c62a6SFranco Fichtner 	mdoc->meta.os = mandoc_strdup(defbuf);
292180387638SSascha Wildner #endif /*!OSNAME*/
292280387638SSascha Wildner 
2923070c62a6SFranco Fichtner out:
292454ba9607SSascha Wildner 	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
292554ba9607SSascha Wildner 		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
292654ba9607SSascha Wildner 			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
292754ba9607SSascha Wildner 		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
292854ba9607SSascha Wildner 			mdoc->meta.os_e = MANDOC_OS_NETBSD;
292980387638SSascha Wildner 	}
293080387638SSascha Wildner 
293180387638SSascha Wildner 	/*
293254ba9607SSascha Wildner 	 * This is the earliest point where we can check
293354ba9607SSascha Wildner 	 * Mdocdate conventions because we don't know
293454ba9607SSascha Wildner 	 * the operating system earlier.
293580387638SSascha Wildner 	 */
2936070c62a6SFranco Fichtner 
293754ba9607SSascha Wildner 	if (n->child != NULL)
293854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
293954ba9607SSascha Wildner 		    "Os %s (%s)", n->child->string,
294054ba9607SSascha Wildner 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
294154ba9607SSascha Wildner 		    "OpenBSD" : "NetBSD");
294280387638SSascha Wildner 
294354ba9607SSascha Wildner 	while (n->tok != MDOC_Dd)
294454ba9607SSascha Wildner 		if ((n = n->prev) == NULL)
294554ba9607SSascha Wildner 			return;
294654ba9607SSascha Wildner 	if ((n = n->child) == NULL)
294754ba9607SSascha Wildner 		return;
294854ba9607SSascha Wildner 	if (strncmp(n->string, "$" "Mdocdate", 9)) {
294954ba9607SSascha Wildner 		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
295054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
295154ba9607SSascha Wildner 			    n->pos, "Dd %s (OpenBSD)", n->string);
295254ba9607SSascha Wildner 	} else {
295354ba9607SSascha Wildner 		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
295454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_MDOCDATE, n->line,
295554ba9607SSascha Wildner 			    n->pos, "Dd %s (NetBSD)", n->string);
295654ba9607SSascha Wildner 	}
2957070c62a6SFranco Fichtner }
295880387638SSascha Wildner 
295954ba9607SSascha Wildner enum roff_sec
mdoc_a2sec(const char * p)296054ba9607SSascha Wildner mdoc_a2sec(const char *p)
296160e1e752SSascha Wildner {
296260e1e752SSascha Wildner 	int		 i;
296360e1e752SSascha Wildner 
296460e1e752SSascha Wildner 	for (i = 0; i < (int)SEC__MAX; i++)
296560e1e752SSascha Wildner 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
296654ba9607SSascha Wildner 			return (enum roff_sec)i;
296760e1e752SSascha Wildner 
296854ba9607SSascha Wildner 	return SEC_CUSTOM;
296960e1e752SSascha Wildner }
297060e1e752SSascha Wildner 
297160e1e752SSascha Wildner static size_t
macro2len(enum roff_tok macro)297254ba9607SSascha Wildner macro2len(enum roff_tok macro)
297360e1e752SSascha Wildner {
297460e1e752SSascha Wildner 
297560e1e752SSascha Wildner 	switch (macro) {
2976070c62a6SFranco Fichtner 	case MDOC_Ad:
297754ba9607SSascha Wildner 		return 12;
2978070c62a6SFranco Fichtner 	case MDOC_Ao:
297954ba9607SSascha Wildner 		return 12;
2980070c62a6SFranco Fichtner 	case MDOC_An:
298154ba9607SSascha Wildner 		return 12;
2982070c62a6SFranco Fichtner 	case MDOC_Aq:
298354ba9607SSascha Wildner 		return 12;
2984070c62a6SFranco Fichtner 	case MDOC_Ar:
298554ba9607SSascha Wildner 		return 12;
2986070c62a6SFranco Fichtner 	case MDOC_Bo:
298754ba9607SSascha Wildner 		return 12;
2988070c62a6SFranco Fichtner 	case MDOC_Bq:
298954ba9607SSascha Wildner 		return 12;
2990070c62a6SFranco Fichtner 	case MDOC_Cd:
299154ba9607SSascha Wildner 		return 12;
2992070c62a6SFranco Fichtner 	case MDOC_Cm:
299354ba9607SSascha Wildner 		return 10;
2994070c62a6SFranco Fichtner 	case MDOC_Do:
299554ba9607SSascha Wildner 		return 10;
2996070c62a6SFranco Fichtner 	case MDOC_Dq:
299754ba9607SSascha Wildner 		return 12;
2998070c62a6SFranco Fichtner 	case MDOC_Dv:
299954ba9607SSascha Wildner 		return 12;
3000070c62a6SFranco Fichtner 	case MDOC_Eo:
300154ba9607SSascha Wildner 		return 12;
3002070c62a6SFranco Fichtner 	case MDOC_Em:
300354ba9607SSascha Wildner 		return 10;
3004070c62a6SFranco Fichtner 	case MDOC_Er:
300554ba9607SSascha Wildner 		return 17;
3006070c62a6SFranco Fichtner 	case MDOC_Ev:
300754ba9607SSascha Wildner 		return 15;
3008070c62a6SFranco Fichtner 	case MDOC_Fa:
300954ba9607SSascha Wildner 		return 12;
3010070c62a6SFranco Fichtner 	case MDOC_Fl:
301154ba9607SSascha Wildner 		return 10;
3012070c62a6SFranco Fichtner 	case MDOC_Fo:
301354ba9607SSascha Wildner 		return 16;
3014070c62a6SFranco Fichtner 	case MDOC_Fn:
301554ba9607SSascha Wildner 		return 16;
3016070c62a6SFranco Fichtner 	case MDOC_Ic:
301754ba9607SSascha Wildner 		return 10;
3018070c62a6SFranco Fichtner 	case MDOC_Li:
301954ba9607SSascha Wildner 		return 16;
3020070c62a6SFranco Fichtner 	case MDOC_Ms:
302154ba9607SSascha Wildner 		return 6;
3022070c62a6SFranco Fichtner 	case MDOC_Nm:
302354ba9607SSascha Wildner 		return 10;
3024070c62a6SFranco Fichtner 	case MDOC_No:
302554ba9607SSascha Wildner 		return 12;
3026070c62a6SFranco Fichtner 	case MDOC_Oo:
302754ba9607SSascha Wildner 		return 10;
3028070c62a6SFranco Fichtner 	case MDOC_Op:
302954ba9607SSascha Wildner 		return 14;
3030070c62a6SFranco Fichtner 	case MDOC_Pa:
303154ba9607SSascha Wildner 		return 32;
3032070c62a6SFranco Fichtner 	case MDOC_Pf:
303354ba9607SSascha Wildner 		return 12;
3034070c62a6SFranco Fichtner 	case MDOC_Po:
303554ba9607SSascha Wildner 		return 12;
3036070c62a6SFranco Fichtner 	case MDOC_Pq:
303754ba9607SSascha Wildner 		return 12;
3038070c62a6SFranco Fichtner 	case MDOC_Ql:
303954ba9607SSascha Wildner 		return 16;
3040070c62a6SFranco Fichtner 	case MDOC_Qo:
304154ba9607SSascha Wildner 		return 12;
3042070c62a6SFranco Fichtner 	case MDOC_So:
304354ba9607SSascha Wildner 		return 12;
3044070c62a6SFranco Fichtner 	case MDOC_Sq:
304554ba9607SSascha Wildner 		return 12;
3046070c62a6SFranco Fichtner 	case MDOC_Sy:
304754ba9607SSascha Wildner 		return 6;
3048070c62a6SFranco Fichtner 	case MDOC_Sx:
304954ba9607SSascha Wildner 		return 16;
3050070c62a6SFranco Fichtner 	case MDOC_Tn:
305154ba9607SSascha Wildner 		return 10;
3052070c62a6SFranco Fichtner 	case MDOC_Va:
305354ba9607SSascha Wildner 		return 12;
3054070c62a6SFranco Fichtner 	case MDOC_Vt:
305554ba9607SSascha Wildner 		return 12;
3056070c62a6SFranco Fichtner 	case MDOC_Xr:
305754ba9607SSascha Wildner 		return 10;
305860e1e752SSascha Wildner 	default:
305960e1e752SSascha Wildner 		break;
306060e1e752SSascha Wildner 	};
306154ba9607SSascha Wildner 	return 0;
306260e1e752SSascha Wildner }
3063