xref: /freebsd-src/contrib/mandoc/mdoc_validate.c (revision c1c95add8c80843ba15d784f95c361d795b1f593)
1*c1c95addSBrooks Davis /* $Id: mdoc_validate.c,v 1.391 2022/06/08 16:31:46 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis  * Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
661d06d6bSBaptiste Daroussin  *
761d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin  *
1161d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1261d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1461d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
186d38604fSBaptiste Daroussin  *
196d38604fSBaptiste Daroussin  * Validation module for mdoc(7) syntax trees used by mandoc(1).
2061d06d6bSBaptiste Daroussin  */
2161d06d6bSBaptiste Daroussin #include "config.h"
2261d06d6bSBaptiste Daroussin 
2361d06d6bSBaptiste Daroussin #include <sys/types.h>
2461d06d6bSBaptiste Daroussin #ifndef OSNAME
2561d06d6bSBaptiste Daroussin #include <sys/utsname.h>
2661d06d6bSBaptiste Daroussin #endif
2761d06d6bSBaptiste Daroussin 
2861d06d6bSBaptiste Daroussin #include <assert.h>
2961d06d6bSBaptiste Daroussin #include <ctype.h>
3061d06d6bSBaptiste Daroussin #include <limits.h>
3161d06d6bSBaptiste Daroussin #include <stdio.h>
3261d06d6bSBaptiste Daroussin #include <stdlib.h>
3361d06d6bSBaptiste Daroussin #include <string.h>
3461d06d6bSBaptiste Daroussin #include <time.h>
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3761d06d6bSBaptiste Daroussin #include "mandoc.h"
3861d06d6bSBaptiste Daroussin #include "mandoc_xr.h"
3961d06d6bSBaptiste Daroussin #include "roff.h"
4061d06d6bSBaptiste Daroussin #include "mdoc.h"
4161d06d6bSBaptiste Daroussin #include "libmandoc.h"
4261d06d6bSBaptiste Daroussin #include "roff_int.h"
4361d06d6bSBaptiste Daroussin #include "libmdoc.h"
446d38604fSBaptiste Daroussin #include "tag.h"
4561d06d6bSBaptiste Daroussin 
4661d06d6bSBaptiste Daroussin /* FIXME: .Bl -diag can't have non-text children in HEAD. */
4761d06d6bSBaptiste Daroussin 
4861d06d6bSBaptiste Daroussin #define	POST_ARGS struct roff_man *mdoc
4961d06d6bSBaptiste Daroussin 
5061d06d6bSBaptiste Daroussin enum	check_ineq {
5161d06d6bSBaptiste Daroussin 	CHECK_LT,
5261d06d6bSBaptiste Daroussin 	CHECK_GT,
5361d06d6bSBaptiste Daroussin 	CHECK_EQ
5461d06d6bSBaptiste Daroussin };
5561d06d6bSBaptiste Daroussin 
5661d06d6bSBaptiste Daroussin typedef	void	(*v_post)(POST_ARGS);
5761d06d6bSBaptiste Daroussin 
5861d06d6bSBaptiste Daroussin static	int	 build_list(struct roff_man *, int);
5961d06d6bSBaptiste Daroussin static	void	 check_argv(struct roff_man *,
6061d06d6bSBaptiste Daroussin 			struct roff_node *, struct mdoc_argv *);
6161d06d6bSBaptiste Daroussin static	void	 check_args(struct roff_man *, struct roff_node *);
6261d06d6bSBaptiste Daroussin static	void	 check_text(struct roff_man *, int, int, char *);
6361d06d6bSBaptiste Daroussin static	void	 check_text_em(struct roff_man *, int, int, char *);
6461d06d6bSBaptiste Daroussin static	void	 check_toptext(struct roff_man *, int, int, const char *);
6561d06d6bSBaptiste Daroussin static	int	 child_an(const struct roff_node *);
6661d06d6bSBaptiste Daroussin static	size_t		macro2len(enum roff_tok);
6761d06d6bSBaptiste Daroussin static	void	 rewrite_macro2len(struct roff_man *, char **);
6861d06d6bSBaptiste Daroussin static	int	 similar(const char *, const char *);
6961d06d6bSBaptiste Daroussin 
7045a5aec3SBaptiste Daroussin static	void	 post_abort(POST_ARGS) __attribute__((__noreturn__));
7161d06d6bSBaptiste Daroussin static	void	 post_an(POST_ARGS);
7261d06d6bSBaptiste Daroussin static	void	 post_an_norm(POST_ARGS);
7361d06d6bSBaptiste Daroussin static	void	 post_at(POST_ARGS);
7461d06d6bSBaptiste Daroussin static	void	 post_bd(POST_ARGS);
7561d06d6bSBaptiste Daroussin static	void	 post_bf(POST_ARGS);
7661d06d6bSBaptiste Daroussin static	void	 post_bk(POST_ARGS);
7761d06d6bSBaptiste Daroussin static	void	 post_bl(POST_ARGS);
7861d06d6bSBaptiste Daroussin static	void	 post_bl_block(POST_ARGS);
7961d06d6bSBaptiste Daroussin static	void	 post_bl_head(POST_ARGS);
8061d06d6bSBaptiste Daroussin static	void	 post_bl_norm(POST_ARGS);
8161d06d6bSBaptiste Daroussin static	void	 post_bx(POST_ARGS);
8261d06d6bSBaptiste Daroussin static	void	 post_defaults(POST_ARGS);
8361d06d6bSBaptiste Daroussin static	void	 post_display(POST_ARGS);
8461d06d6bSBaptiste Daroussin static	void	 post_dd(POST_ARGS);
8561d06d6bSBaptiste Daroussin static	void	 post_delim(POST_ARGS);
8661d06d6bSBaptiste Daroussin static	void	 post_delim_nb(POST_ARGS);
8761d06d6bSBaptiste Daroussin static	void	 post_dt(POST_ARGS);
886d38604fSBaptiste Daroussin static	void	 post_em(POST_ARGS);
8961d06d6bSBaptiste Daroussin static	void	 post_en(POST_ARGS);
906d38604fSBaptiste Daroussin static	void	 post_er(POST_ARGS);
9161d06d6bSBaptiste Daroussin static	void	 post_es(POST_ARGS);
9261d06d6bSBaptiste Daroussin static	void	 post_eoln(POST_ARGS);
9361d06d6bSBaptiste Daroussin static	void	 post_ex(POST_ARGS);
9461d06d6bSBaptiste Daroussin static	void	 post_fa(POST_ARGS);
956d38604fSBaptiste Daroussin static	void	 post_fl(POST_ARGS);
9661d06d6bSBaptiste Daroussin static	void	 post_fn(POST_ARGS);
9761d06d6bSBaptiste Daroussin static	void	 post_fname(POST_ARGS);
9861d06d6bSBaptiste Daroussin static	void	 post_fo(POST_ARGS);
9961d06d6bSBaptiste Daroussin static	void	 post_hyph(POST_ARGS);
10061d06d6bSBaptiste Daroussin static	void	 post_it(POST_ARGS);
10161d06d6bSBaptiste Daroussin static	void	 post_lb(POST_ARGS);
10261d06d6bSBaptiste Daroussin static	void	 post_nd(POST_ARGS);
10361d06d6bSBaptiste Daroussin static	void	 post_nm(POST_ARGS);
10461d06d6bSBaptiste Daroussin static	void	 post_ns(POST_ARGS);
10561d06d6bSBaptiste Daroussin static	void	 post_obsolete(POST_ARGS);
10661d06d6bSBaptiste Daroussin static	void	 post_os(POST_ARGS);
10761d06d6bSBaptiste Daroussin static	void	 post_par(POST_ARGS);
10861d06d6bSBaptiste Daroussin static	void	 post_prevpar(POST_ARGS);
10961d06d6bSBaptiste Daroussin static	void	 post_root(POST_ARGS);
11061d06d6bSBaptiste Daroussin static	void	 post_rs(POST_ARGS);
11161d06d6bSBaptiste Daroussin static	void	 post_rv(POST_ARGS);
1126d38604fSBaptiste Daroussin static	void	 post_section(POST_ARGS);
11361d06d6bSBaptiste Daroussin static	void	 post_sh(POST_ARGS);
11461d06d6bSBaptiste Daroussin static	void	 post_sh_head(POST_ARGS);
11561d06d6bSBaptiste Daroussin static	void	 post_sh_name(POST_ARGS);
11661d06d6bSBaptiste Daroussin static	void	 post_sh_see_also(POST_ARGS);
11761d06d6bSBaptiste Daroussin static	void	 post_sh_authors(POST_ARGS);
11861d06d6bSBaptiste Daroussin static	void	 post_sm(POST_ARGS);
11961d06d6bSBaptiste Daroussin static	void	 post_st(POST_ARGS);
12061d06d6bSBaptiste Daroussin static	void	 post_std(POST_ARGS);
12161d06d6bSBaptiste Daroussin static	void	 post_sx(POST_ARGS);
1226d38604fSBaptiste Daroussin static	void	 post_tag(POST_ARGS);
1236d38604fSBaptiste Daroussin static	void	 post_tg(POST_ARGS);
12461d06d6bSBaptiste Daroussin static	void	 post_useless(POST_ARGS);
12561d06d6bSBaptiste Daroussin static	void	 post_xr(POST_ARGS);
12661d06d6bSBaptiste Daroussin static	void	 post_xx(POST_ARGS);
12761d06d6bSBaptiste Daroussin 
1287295610fSBaptiste Daroussin static	const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
12961d06d6bSBaptiste Daroussin 	post_dd,	/* Dd */
13061d06d6bSBaptiste Daroussin 	post_dt,	/* Dt */
13161d06d6bSBaptiste Daroussin 	post_os,	/* Os */
13261d06d6bSBaptiste Daroussin 	post_sh,	/* Sh */
1336d38604fSBaptiste Daroussin 	post_section,	/* Ss */
13461d06d6bSBaptiste Daroussin 	post_par,	/* Pp */
13561d06d6bSBaptiste Daroussin 	post_display,	/* D1 */
13661d06d6bSBaptiste Daroussin 	post_display,	/* Dl */
13761d06d6bSBaptiste Daroussin 	post_display,	/* Bd */
13861d06d6bSBaptiste Daroussin 	NULL,		/* Ed */
13961d06d6bSBaptiste Daroussin 	post_bl,	/* Bl */
14061d06d6bSBaptiste Daroussin 	NULL,		/* El */
14161d06d6bSBaptiste Daroussin 	post_it,	/* It */
14261d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Ad */
14361d06d6bSBaptiste Daroussin 	post_an,	/* An */
14461d06d6bSBaptiste Daroussin 	NULL,		/* Ap */
14561d06d6bSBaptiste Daroussin 	post_defaults,	/* Ar */
14661d06d6bSBaptiste Daroussin 	NULL,		/* Cd */
1476d38604fSBaptiste Daroussin 	post_tag,	/* Cm */
1486d38604fSBaptiste Daroussin 	post_tag,	/* Dv */
1496d38604fSBaptiste Daroussin 	post_er,	/* Er */
1506d38604fSBaptiste Daroussin 	post_tag,	/* Ev */
15161d06d6bSBaptiste Daroussin 	post_ex,	/* Ex */
15261d06d6bSBaptiste Daroussin 	post_fa,	/* Fa */
15361d06d6bSBaptiste Daroussin 	NULL,		/* Fd */
1546d38604fSBaptiste Daroussin 	post_fl,	/* Fl */
15561d06d6bSBaptiste Daroussin 	post_fn,	/* Fn */
15661d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Ft */
1576d38604fSBaptiste Daroussin 	post_tag,	/* Ic */
15861d06d6bSBaptiste Daroussin 	post_delim_nb,	/* In */
1596d38604fSBaptiste Daroussin 	post_tag,	/* Li */
16061d06d6bSBaptiste Daroussin 	post_nd,	/* Nd */
16161d06d6bSBaptiste Daroussin 	post_nm,	/* Nm */
16261d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Op */
1637295610fSBaptiste Daroussin 	post_abort,	/* Ot */
16461d06d6bSBaptiste Daroussin 	post_defaults,	/* Pa */
16561d06d6bSBaptiste Daroussin 	post_rv,	/* Rv */
16661d06d6bSBaptiste Daroussin 	post_st,	/* St */
1676d38604fSBaptiste Daroussin 	post_tag,	/* Va */
16861d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Vt */
16961d06d6bSBaptiste Daroussin 	post_xr,	/* Xr */
17061d06d6bSBaptiste Daroussin 	NULL,		/* %A */
17161d06d6bSBaptiste Daroussin 	post_hyph,	/* %B */ /* FIXME: can be used outside Rs/Re. */
17261d06d6bSBaptiste Daroussin 	NULL,		/* %D */
17361d06d6bSBaptiste Daroussin 	NULL,		/* %I */
17461d06d6bSBaptiste Daroussin 	NULL,		/* %J */
17561d06d6bSBaptiste Daroussin 	post_hyph,	/* %N */
17661d06d6bSBaptiste Daroussin 	post_hyph,	/* %O */
17761d06d6bSBaptiste Daroussin 	NULL,		/* %P */
17861d06d6bSBaptiste Daroussin 	post_hyph,	/* %R */
17961d06d6bSBaptiste Daroussin 	post_hyph,	/* %T */ /* FIXME: can be used outside Rs/Re. */
18061d06d6bSBaptiste Daroussin 	NULL,		/* %V */
18161d06d6bSBaptiste Daroussin 	NULL,		/* Ac */
18261d06d6bSBaptiste Daroussin 	NULL,		/* Ao */
18361d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Aq */
18461d06d6bSBaptiste Daroussin 	post_at,	/* At */
18561d06d6bSBaptiste Daroussin 	NULL,		/* Bc */
18661d06d6bSBaptiste Daroussin 	post_bf,	/* Bf */
18761d06d6bSBaptiste Daroussin 	NULL,		/* Bo */
18861d06d6bSBaptiste Daroussin 	NULL,		/* Bq */
18961d06d6bSBaptiste Daroussin 	post_xx,	/* Bsx */
19061d06d6bSBaptiste Daroussin 	post_bx,	/* Bx */
19161d06d6bSBaptiste Daroussin 	post_obsolete,	/* Db */
19261d06d6bSBaptiste Daroussin 	NULL,		/* Dc */
19361d06d6bSBaptiste Daroussin 	NULL,		/* Do */
19461d06d6bSBaptiste Daroussin 	NULL,		/* Dq */
19561d06d6bSBaptiste Daroussin 	NULL,		/* Ec */
19661d06d6bSBaptiste Daroussin 	NULL,		/* Ef */
1976d38604fSBaptiste Daroussin 	post_em,	/* Em */
19861d06d6bSBaptiste Daroussin 	NULL,		/* Eo */
19961d06d6bSBaptiste Daroussin 	post_xx,	/* Fx */
2006d38604fSBaptiste Daroussin 	post_tag,	/* Ms */
2016d38604fSBaptiste Daroussin 	post_tag,	/* No */
20261d06d6bSBaptiste Daroussin 	post_ns,	/* Ns */
20361d06d6bSBaptiste Daroussin 	post_xx,	/* Nx */
20461d06d6bSBaptiste Daroussin 	post_xx,	/* Ox */
20561d06d6bSBaptiste Daroussin 	NULL,		/* Pc */
20661d06d6bSBaptiste Daroussin 	NULL,		/* Pf */
20761d06d6bSBaptiste Daroussin 	NULL,		/* Po */
20861d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Pq */
20961d06d6bSBaptiste Daroussin 	NULL,		/* Qc */
21061d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Ql */
21161d06d6bSBaptiste Daroussin 	NULL,		/* Qo */
21261d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Qq */
21361d06d6bSBaptiste Daroussin 	NULL,		/* Re */
21461d06d6bSBaptiste Daroussin 	post_rs,	/* Rs */
21561d06d6bSBaptiste Daroussin 	NULL,		/* Sc */
21661d06d6bSBaptiste Daroussin 	NULL,		/* So */
21761d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Sq */
21861d06d6bSBaptiste Daroussin 	post_sm,	/* Sm */
21961d06d6bSBaptiste Daroussin 	post_sx,	/* Sx */
2206d38604fSBaptiste Daroussin 	post_em,	/* Sy */
22161d06d6bSBaptiste Daroussin 	post_useless,	/* Tn */
22261d06d6bSBaptiste Daroussin 	post_xx,	/* Ux */
22361d06d6bSBaptiste Daroussin 	NULL,		/* Xc */
22461d06d6bSBaptiste Daroussin 	NULL,		/* Xo */
22561d06d6bSBaptiste Daroussin 	post_fo,	/* Fo */
22661d06d6bSBaptiste Daroussin 	NULL,		/* Fc */
22761d06d6bSBaptiste Daroussin 	NULL,		/* Oo */
22861d06d6bSBaptiste Daroussin 	NULL,		/* Oc */
22961d06d6bSBaptiste Daroussin 	post_bk,	/* Bk */
23061d06d6bSBaptiste Daroussin 	NULL,		/* Ek */
23161d06d6bSBaptiste Daroussin 	post_eoln,	/* Bt */
23261d06d6bSBaptiste Daroussin 	post_obsolete,	/* Hf */
23361d06d6bSBaptiste Daroussin 	post_obsolete,	/* Fr */
23461d06d6bSBaptiste Daroussin 	post_eoln,	/* Ud */
23561d06d6bSBaptiste Daroussin 	post_lb,	/* Lb */
2367295610fSBaptiste Daroussin 	post_abort,	/* Lp */
23761d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Lk */
23861d06d6bSBaptiste Daroussin 	post_defaults,	/* Mt */
23961d06d6bSBaptiste Daroussin 	post_delim_nb,	/* Brq */
24061d06d6bSBaptiste Daroussin 	NULL,		/* Bro */
24161d06d6bSBaptiste Daroussin 	NULL,		/* Brc */
24261d06d6bSBaptiste Daroussin 	NULL,		/* %C */
24361d06d6bSBaptiste Daroussin 	post_es,	/* Es */
24461d06d6bSBaptiste Daroussin 	post_en,	/* En */
24561d06d6bSBaptiste Daroussin 	post_xx,	/* Dx */
24661d06d6bSBaptiste Daroussin 	NULL,		/* %Q */
24761d06d6bSBaptiste Daroussin 	NULL,		/* %U */
24861d06d6bSBaptiste Daroussin 	NULL,		/* Ta */
2496d38604fSBaptiste Daroussin 	post_tg,	/* Tg */
25061d06d6bSBaptiste Daroussin };
25161d06d6bSBaptiste Daroussin 
25261d06d6bSBaptiste Daroussin #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
25361d06d6bSBaptiste Daroussin 
25461d06d6bSBaptiste Daroussin static	const enum roff_tok rsord[RSORD_MAX] = {
25561d06d6bSBaptiste Daroussin 	MDOC__A,
25661d06d6bSBaptiste Daroussin 	MDOC__T,
25761d06d6bSBaptiste Daroussin 	MDOC__B,
25861d06d6bSBaptiste Daroussin 	MDOC__I,
25961d06d6bSBaptiste Daroussin 	MDOC__J,
26061d06d6bSBaptiste Daroussin 	MDOC__R,
26161d06d6bSBaptiste Daroussin 	MDOC__N,
26261d06d6bSBaptiste Daroussin 	MDOC__V,
26361d06d6bSBaptiste Daroussin 	MDOC__U,
26461d06d6bSBaptiste Daroussin 	MDOC__P,
26561d06d6bSBaptiste Daroussin 	MDOC__Q,
26661d06d6bSBaptiste Daroussin 	MDOC__C,
26761d06d6bSBaptiste Daroussin 	MDOC__D,
26861d06d6bSBaptiste Daroussin 	MDOC__O
26961d06d6bSBaptiste Daroussin };
27061d06d6bSBaptiste Daroussin 
27161d06d6bSBaptiste Daroussin static	const char * const secnames[SEC__MAX] = {
27261d06d6bSBaptiste Daroussin 	NULL,
27361d06d6bSBaptiste Daroussin 	"NAME",
27461d06d6bSBaptiste Daroussin 	"LIBRARY",
27561d06d6bSBaptiste Daroussin 	"SYNOPSIS",
27661d06d6bSBaptiste Daroussin 	"DESCRIPTION",
27761d06d6bSBaptiste Daroussin 	"CONTEXT",
27861d06d6bSBaptiste Daroussin 	"IMPLEMENTATION NOTES",
27961d06d6bSBaptiste Daroussin 	"RETURN VALUES",
28061d06d6bSBaptiste Daroussin 	"ENVIRONMENT",
28161d06d6bSBaptiste Daroussin 	"FILES",
28261d06d6bSBaptiste Daroussin 	"EXIT STATUS",
28361d06d6bSBaptiste Daroussin 	"EXAMPLES",
28461d06d6bSBaptiste Daroussin 	"DIAGNOSTICS",
28561d06d6bSBaptiste Daroussin 	"COMPATIBILITY",
28661d06d6bSBaptiste Daroussin 	"ERRORS",
28761d06d6bSBaptiste Daroussin 	"SEE ALSO",
28861d06d6bSBaptiste Daroussin 	"STANDARDS",
28961d06d6bSBaptiste Daroussin 	"HISTORY",
29061d06d6bSBaptiste Daroussin 	"AUTHORS",
29161d06d6bSBaptiste Daroussin 	"CAVEATS",
29261d06d6bSBaptiste Daroussin 	"BUGS",
29361d06d6bSBaptiste Daroussin 	"SECURITY CONSIDERATIONS",
29461d06d6bSBaptiste Daroussin 	NULL
29561d06d6bSBaptiste Daroussin };
29661d06d6bSBaptiste Daroussin 
2976d38604fSBaptiste Daroussin static	int	  fn_prio = TAG_STRONG;
2986d38604fSBaptiste Daroussin 
29961d06d6bSBaptiste Daroussin 
3007295610fSBaptiste Daroussin /* Validate the subtree rooted at mdoc->last. */
30161d06d6bSBaptiste Daroussin void
3027295610fSBaptiste Daroussin mdoc_validate(struct roff_man *mdoc)
30361d06d6bSBaptiste Daroussin {
30461d06d6bSBaptiste Daroussin 	struct roff_node *n, *np;
30561d06d6bSBaptiste Daroussin 	const v_post *p;
30661d06d6bSBaptiste Daroussin 
3077295610fSBaptiste Daroussin 	/*
3087295610fSBaptiste Daroussin 	 * Translate obsolete macros to modern macros first
3097295610fSBaptiste Daroussin 	 * such that later code does not need to look
3107295610fSBaptiste Daroussin 	 * for the obsolete versions.
3117295610fSBaptiste Daroussin 	 */
3127295610fSBaptiste Daroussin 
31361d06d6bSBaptiste Daroussin 	n = mdoc->last;
3147295610fSBaptiste Daroussin 	switch (n->tok) {
3157295610fSBaptiste Daroussin 	case MDOC_Lp:
3167295610fSBaptiste Daroussin 		n->tok = MDOC_Pp;
3177295610fSBaptiste Daroussin 		break;
3187295610fSBaptiste Daroussin 	case MDOC_Ot:
3197295610fSBaptiste Daroussin 		post_obsolete(mdoc);
3207295610fSBaptiste Daroussin 		n->tok = MDOC_Ft;
3217295610fSBaptiste Daroussin 		break;
3227295610fSBaptiste Daroussin 	default:
3237295610fSBaptiste Daroussin 		break;
3247295610fSBaptiste Daroussin 	}
3257295610fSBaptiste Daroussin 
3267295610fSBaptiste Daroussin 	/*
3277295610fSBaptiste Daroussin 	 * Iterate over all children, recursing into each one
3287295610fSBaptiste Daroussin 	 * in turn, depth-first.
3297295610fSBaptiste Daroussin 	 */
3307295610fSBaptiste Daroussin 
33161d06d6bSBaptiste Daroussin 	mdoc->last = mdoc->last->child;
33261d06d6bSBaptiste Daroussin 	while (mdoc->last != NULL) {
3337295610fSBaptiste Daroussin 		mdoc_validate(mdoc);
33461d06d6bSBaptiste Daroussin 		if (mdoc->last == n)
33561d06d6bSBaptiste Daroussin 			mdoc->last = mdoc->last->child;
33661d06d6bSBaptiste Daroussin 		else
33761d06d6bSBaptiste Daroussin 			mdoc->last = mdoc->last->next;
33861d06d6bSBaptiste Daroussin 	}
33961d06d6bSBaptiste Daroussin 
3407295610fSBaptiste Daroussin 	/* Finally validate the macro itself. */
3417295610fSBaptiste Daroussin 
34261d06d6bSBaptiste Daroussin 	mdoc->last = n;
34361d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_SIBLING;
34461d06d6bSBaptiste Daroussin 	switch (n->type) {
34561d06d6bSBaptiste Daroussin 	case ROFFT_TEXT:
34661d06d6bSBaptiste Daroussin 		np = n->parent;
34761d06d6bSBaptiste Daroussin 		if (n->sec != SEC_SYNOPSIS ||
34861d06d6bSBaptiste Daroussin 		    (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
34961d06d6bSBaptiste Daroussin 			check_text(mdoc, n->line, n->pos, n->string);
3507295610fSBaptiste Daroussin 		if ((n->flags & NODE_NOFILL) == 0 &&
35161d06d6bSBaptiste Daroussin 		    (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
35261d06d6bSBaptiste Daroussin 		     np->parent->parent->norm->Bl.type != LIST_diag))
35361d06d6bSBaptiste Daroussin 			check_text_em(mdoc, n->line, n->pos, n->string);
35461d06d6bSBaptiste Daroussin 		if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
35561d06d6bSBaptiste Daroussin 		    (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
35661d06d6bSBaptiste Daroussin 			check_toptext(mdoc, n->line, n->pos, n->string);
35761d06d6bSBaptiste Daroussin 		break;
35861d06d6bSBaptiste Daroussin 	case ROFFT_COMMENT:
35961d06d6bSBaptiste Daroussin 	case ROFFT_EQN:
36061d06d6bSBaptiste Daroussin 	case ROFFT_TBL:
36161d06d6bSBaptiste Daroussin 		break;
36261d06d6bSBaptiste Daroussin 	case ROFFT_ROOT:
36361d06d6bSBaptiste Daroussin 		post_root(mdoc);
36461d06d6bSBaptiste Daroussin 		break;
36561d06d6bSBaptiste Daroussin 	default:
36661d06d6bSBaptiste Daroussin 		check_args(mdoc, mdoc->last);
36761d06d6bSBaptiste Daroussin 
36861d06d6bSBaptiste Daroussin 		/*
36961d06d6bSBaptiste Daroussin 		 * Closing delimiters are not special at the
37061d06d6bSBaptiste Daroussin 		 * beginning of a block, opening delimiters
37161d06d6bSBaptiste Daroussin 		 * are not special at the end.
37261d06d6bSBaptiste Daroussin 		 */
37361d06d6bSBaptiste Daroussin 
37461d06d6bSBaptiste Daroussin 		if (n->child != NULL)
37561d06d6bSBaptiste Daroussin 			n->child->flags &= ~NODE_DELIMC;
37661d06d6bSBaptiste Daroussin 		if (n->last != NULL)
37761d06d6bSBaptiste Daroussin 			n->last->flags &= ~NODE_DELIMO;
37861d06d6bSBaptiste Daroussin 
37961d06d6bSBaptiste Daroussin 		/* Call the macro's postprocessor. */
38061d06d6bSBaptiste Daroussin 
38161d06d6bSBaptiste Daroussin 		if (n->tok < ROFF_MAX) {
38261d06d6bSBaptiste Daroussin 			roff_validate(mdoc);
38361d06d6bSBaptiste Daroussin 			break;
38461d06d6bSBaptiste Daroussin 		}
38561d06d6bSBaptiste Daroussin 
38661d06d6bSBaptiste Daroussin 		assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
3877295610fSBaptiste Daroussin 		p = mdoc_valids + (n->tok - MDOC_Dd);
38861d06d6bSBaptiste Daroussin 		if (*p)
38961d06d6bSBaptiste Daroussin 			(*p)(mdoc);
39061d06d6bSBaptiste Daroussin 		if (mdoc->last == n)
39161d06d6bSBaptiste Daroussin 			mdoc_state(mdoc, n);
39261d06d6bSBaptiste Daroussin 		break;
39361d06d6bSBaptiste Daroussin 	}
39461d06d6bSBaptiste Daroussin }
39561d06d6bSBaptiste Daroussin 
39661d06d6bSBaptiste Daroussin static void
39761d06d6bSBaptiste Daroussin check_args(struct roff_man *mdoc, struct roff_node *n)
39861d06d6bSBaptiste Daroussin {
39961d06d6bSBaptiste Daroussin 	int		 i;
40061d06d6bSBaptiste Daroussin 
40161d06d6bSBaptiste Daroussin 	if (NULL == n->args)
40261d06d6bSBaptiste Daroussin 		return;
40361d06d6bSBaptiste Daroussin 
40461d06d6bSBaptiste Daroussin 	assert(n->args->argc);
40561d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)n->args->argc; i++)
40661d06d6bSBaptiste Daroussin 		check_argv(mdoc, n, &n->args->argv[i]);
40761d06d6bSBaptiste Daroussin }
40861d06d6bSBaptiste Daroussin 
40961d06d6bSBaptiste Daroussin static void
41061d06d6bSBaptiste Daroussin check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
41161d06d6bSBaptiste Daroussin {
41261d06d6bSBaptiste Daroussin 	int		 i;
41361d06d6bSBaptiste Daroussin 
41461d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)v->sz; i++)
41561d06d6bSBaptiste Daroussin 		check_text(mdoc, v->line, v->pos, v->value[i]);
41661d06d6bSBaptiste Daroussin }
41761d06d6bSBaptiste Daroussin 
41861d06d6bSBaptiste Daroussin static void
41961d06d6bSBaptiste Daroussin check_text(struct roff_man *mdoc, int ln, int pos, char *p)
42061d06d6bSBaptiste Daroussin {
42161d06d6bSBaptiste Daroussin 	char		*cp;
42261d06d6bSBaptiste Daroussin 
4237295610fSBaptiste Daroussin 	if (mdoc->last->flags & NODE_NOFILL)
42461d06d6bSBaptiste Daroussin 		return;
42561d06d6bSBaptiste Daroussin 
42661d06d6bSBaptiste Daroussin 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
4277295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
42861d06d6bSBaptiste Daroussin }
42961d06d6bSBaptiste Daroussin 
43061d06d6bSBaptiste Daroussin static void
43161d06d6bSBaptiste Daroussin check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
43261d06d6bSBaptiste Daroussin {
43361d06d6bSBaptiste Daroussin 	const struct roff_node	*np, *nn;
43461d06d6bSBaptiste Daroussin 	char			*cp;
43561d06d6bSBaptiste Daroussin 
43661d06d6bSBaptiste Daroussin 	np = mdoc->last->prev;
43761d06d6bSBaptiste Daroussin 	nn = mdoc->last->next;
43861d06d6bSBaptiste Daroussin 
43961d06d6bSBaptiste Daroussin 	/* Look for em-dashes wrongly encoded as "--". */
44061d06d6bSBaptiste Daroussin 
44161d06d6bSBaptiste Daroussin 	for (cp = p; *cp != '\0'; cp++) {
44261d06d6bSBaptiste Daroussin 		if (cp[0] != '-' || cp[1] != '-')
44361d06d6bSBaptiste Daroussin 			continue;
44461d06d6bSBaptiste Daroussin 		cp++;
44561d06d6bSBaptiste Daroussin 
44661d06d6bSBaptiste Daroussin 		/* Skip input sequences of more than two '-'. */
44761d06d6bSBaptiste Daroussin 
44861d06d6bSBaptiste Daroussin 		if (cp[1] == '-') {
44961d06d6bSBaptiste Daroussin 			while (cp[1] == '-')
45061d06d6bSBaptiste Daroussin 				cp++;
45161d06d6bSBaptiste Daroussin 			continue;
45261d06d6bSBaptiste Daroussin 		}
45361d06d6bSBaptiste Daroussin 
45461d06d6bSBaptiste Daroussin 		/* Skip "--" directly attached to something else. */
45561d06d6bSBaptiste Daroussin 
45661d06d6bSBaptiste Daroussin 		if ((cp - p > 1 && cp[-2] != ' ') ||
45761d06d6bSBaptiste Daroussin 		    (cp[1] != '\0' && cp[1] != ' '))
45861d06d6bSBaptiste Daroussin 			continue;
45961d06d6bSBaptiste Daroussin 
46061d06d6bSBaptiste Daroussin 		/* Require a letter right before or right afterwards. */
46161d06d6bSBaptiste Daroussin 
46261d06d6bSBaptiste Daroussin 		if ((cp - p > 2 ?
46361d06d6bSBaptiste Daroussin 		     isalpha((unsigned char)cp[-3]) :
46461d06d6bSBaptiste Daroussin 		     np != NULL &&
46561d06d6bSBaptiste Daroussin 		     np->type == ROFFT_TEXT &&
46661d06d6bSBaptiste Daroussin 		     *np->string != '\0' &&
46761d06d6bSBaptiste Daroussin 		     isalpha((unsigned char)np->string[
46861d06d6bSBaptiste Daroussin 		       strlen(np->string) - 1])) ||
46961d06d6bSBaptiste Daroussin 		    (cp[1] != '\0' && cp[2] != '\0' ?
47061d06d6bSBaptiste Daroussin 		     isalpha((unsigned char)cp[2]) :
47161d06d6bSBaptiste Daroussin 		     nn != NULL &&
47261d06d6bSBaptiste Daroussin 		     nn->type == ROFFT_TEXT &&
47361d06d6bSBaptiste Daroussin 		     isalpha((unsigned char)*nn->string))) {
4747295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_DASHDASH,
47561d06d6bSBaptiste Daroussin 			    ln, pos + (int)(cp - p) - 1, NULL);
47661d06d6bSBaptiste Daroussin 			break;
47761d06d6bSBaptiste Daroussin 		}
47861d06d6bSBaptiste Daroussin 	}
47961d06d6bSBaptiste Daroussin }
48061d06d6bSBaptiste Daroussin 
48161d06d6bSBaptiste Daroussin static void
48261d06d6bSBaptiste Daroussin check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
48361d06d6bSBaptiste Daroussin {
48461d06d6bSBaptiste Daroussin 	const char	*cp, *cpr;
48561d06d6bSBaptiste Daroussin 
48661d06d6bSBaptiste Daroussin 	if (*p == '\0')
48761d06d6bSBaptiste Daroussin 		return;
48861d06d6bSBaptiste Daroussin 
48961d06d6bSBaptiste Daroussin 	if ((cp = strstr(p, "OpenBSD")) != NULL)
4907295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
49161d06d6bSBaptiste Daroussin 	if ((cp = strstr(p, "NetBSD")) != NULL)
4927295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
49361d06d6bSBaptiste Daroussin 	if ((cp = strstr(p, "FreeBSD")) != NULL)
4947295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
49561d06d6bSBaptiste Daroussin 	if ((cp = strstr(p, "DragonFly")) != NULL)
4967295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
49761d06d6bSBaptiste Daroussin 
49861d06d6bSBaptiste Daroussin 	cp = p;
49961d06d6bSBaptiste Daroussin 	while ((cp = strstr(cp + 1, "()")) != NULL) {
50061d06d6bSBaptiste Daroussin 		for (cpr = cp - 1; cpr >= p; cpr--)
50161d06d6bSBaptiste Daroussin 			if (*cpr != '_' && !isalnum((unsigned char)*cpr))
50261d06d6bSBaptiste Daroussin 				break;
50361d06d6bSBaptiste Daroussin 		if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
50461d06d6bSBaptiste Daroussin 			cpr++;
5057295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
50661d06d6bSBaptiste Daroussin 			    "%.*s()", (int)(cp - cpr), cpr);
50761d06d6bSBaptiste Daroussin 		}
50861d06d6bSBaptiste Daroussin 	}
50961d06d6bSBaptiste Daroussin }
51061d06d6bSBaptiste Daroussin 
51161d06d6bSBaptiste Daroussin static void
5127295610fSBaptiste Daroussin post_abort(POST_ARGS)
5137295610fSBaptiste Daroussin {
5147295610fSBaptiste Daroussin 	abort();
5157295610fSBaptiste Daroussin }
5167295610fSBaptiste Daroussin 
5177295610fSBaptiste Daroussin static void
51861d06d6bSBaptiste Daroussin post_delim(POST_ARGS)
51961d06d6bSBaptiste Daroussin {
52061d06d6bSBaptiste Daroussin 	const struct roff_node	*nch;
52161d06d6bSBaptiste Daroussin 	const char		*lc;
52261d06d6bSBaptiste Daroussin 	enum mdelim		 delim;
52361d06d6bSBaptiste Daroussin 	enum roff_tok		 tok;
52461d06d6bSBaptiste Daroussin 
52561d06d6bSBaptiste Daroussin 	tok = mdoc->last->tok;
52661d06d6bSBaptiste Daroussin 	nch = mdoc->last->last;
52761d06d6bSBaptiste Daroussin 	if (nch == NULL || nch->type != ROFFT_TEXT)
52861d06d6bSBaptiste Daroussin 		return;
52961d06d6bSBaptiste Daroussin 	lc = strchr(nch->string, '\0') - 1;
53061d06d6bSBaptiste Daroussin 	if (lc < nch->string)
53161d06d6bSBaptiste Daroussin 		return;
53261d06d6bSBaptiste Daroussin 	delim = mdoc_isdelim(lc);
53361d06d6bSBaptiste Daroussin 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
53461d06d6bSBaptiste Daroussin 		return;
53561d06d6bSBaptiste Daroussin 	if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
53661d06d6bSBaptiste Daroussin 	    tok == MDOC_Ss || tok == MDOC_Fo))
53761d06d6bSBaptiste Daroussin 		return;
53861d06d6bSBaptiste Daroussin 
5397295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_DELIM, nch->line,
5407295610fSBaptiste Daroussin 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
54161d06d6bSBaptiste Daroussin 	    nch == mdoc->last->child ? "" : " ...", nch->string);
54261d06d6bSBaptiste Daroussin }
54361d06d6bSBaptiste Daroussin 
54461d06d6bSBaptiste Daroussin static void
54561d06d6bSBaptiste Daroussin post_delim_nb(POST_ARGS)
54661d06d6bSBaptiste Daroussin {
54761d06d6bSBaptiste Daroussin 	const struct roff_node	*nch;
54861d06d6bSBaptiste Daroussin 	const char		*lc, *cp;
54961d06d6bSBaptiste Daroussin 	int			 nw;
55061d06d6bSBaptiste Daroussin 	enum mdelim		 delim;
55161d06d6bSBaptiste Daroussin 	enum roff_tok		 tok;
55261d06d6bSBaptiste Daroussin 
55361d06d6bSBaptiste Daroussin 	/*
55461d06d6bSBaptiste Daroussin 	 * Find candidates: at least two bytes,
55561d06d6bSBaptiste Daroussin 	 * the last one a closing or middle delimiter.
55661d06d6bSBaptiste Daroussin 	 */
55761d06d6bSBaptiste Daroussin 
55861d06d6bSBaptiste Daroussin 	tok = mdoc->last->tok;
55961d06d6bSBaptiste Daroussin 	nch = mdoc->last->last;
56061d06d6bSBaptiste Daroussin 	if (nch == NULL || nch->type != ROFFT_TEXT)
56161d06d6bSBaptiste Daroussin 		return;
56261d06d6bSBaptiste Daroussin 	lc = strchr(nch->string, '\0') - 1;
56361d06d6bSBaptiste Daroussin 	if (lc <= nch->string)
56461d06d6bSBaptiste Daroussin 		return;
56561d06d6bSBaptiste Daroussin 	delim = mdoc_isdelim(lc);
56661d06d6bSBaptiste Daroussin 	if (delim == DELIM_NONE || delim == DELIM_OPEN)
56761d06d6bSBaptiste Daroussin 		return;
56861d06d6bSBaptiste Daroussin 
56961d06d6bSBaptiste Daroussin 	/*
57061d06d6bSBaptiste Daroussin 	 * Reduce false positives by allowing various cases.
57161d06d6bSBaptiste Daroussin 	 */
57261d06d6bSBaptiste Daroussin 
57361d06d6bSBaptiste Daroussin 	/* Escaped delimiters. */
57461d06d6bSBaptiste Daroussin 	if (lc > nch->string + 1 && lc[-2] == '\\' &&
57561d06d6bSBaptiste Daroussin 	    (lc[-1] == '&' || lc[-1] == 'e'))
57661d06d6bSBaptiste Daroussin 		return;
57761d06d6bSBaptiste Daroussin 
57861d06d6bSBaptiste Daroussin 	/* Specific byte sequences. */
57961d06d6bSBaptiste Daroussin 	switch (*lc) {
58061d06d6bSBaptiste Daroussin 	case ')':
58161d06d6bSBaptiste Daroussin 		for (cp = lc; cp >= nch->string; cp--)
58261d06d6bSBaptiste Daroussin 			if (*cp == '(')
58361d06d6bSBaptiste Daroussin 				return;
58461d06d6bSBaptiste Daroussin 		break;
58561d06d6bSBaptiste Daroussin 	case '.':
58661d06d6bSBaptiste Daroussin 		if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
58761d06d6bSBaptiste Daroussin 			return;
58861d06d6bSBaptiste Daroussin 		if (lc[-1] == '.')
58961d06d6bSBaptiste Daroussin 			return;
59061d06d6bSBaptiste Daroussin 		break;
59161d06d6bSBaptiste Daroussin 	case ';':
59261d06d6bSBaptiste Daroussin 		if (tok == MDOC_Vt)
59361d06d6bSBaptiste Daroussin 			return;
59461d06d6bSBaptiste Daroussin 		break;
59561d06d6bSBaptiste Daroussin 	case '?':
59661d06d6bSBaptiste Daroussin 		if (lc[-1] == '?')
59761d06d6bSBaptiste Daroussin 			return;
59861d06d6bSBaptiste Daroussin 		break;
59961d06d6bSBaptiste Daroussin 	case ']':
60061d06d6bSBaptiste Daroussin 		for (cp = lc; cp >= nch->string; cp--)
60161d06d6bSBaptiste Daroussin 			if (*cp == '[')
60261d06d6bSBaptiste Daroussin 				return;
60361d06d6bSBaptiste Daroussin 		break;
60461d06d6bSBaptiste Daroussin 	case '|':
60561d06d6bSBaptiste Daroussin 		if (lc == nch->string + 1 && lc[-1] == '|')
60661d06d6bSBaptiste Daroussin 			return;
60761d06d6bSBaptiste Daroussin 	default:
60861d06d6bSBaptiste Daroussin 		break;
60961d06d6bSBaptiste Daroussin 	}
61061d06d6bSBaptiste Daroussin 
61161d06d6bSBaptiste Daroussin 	/* Exactly two non-alphanumeric bytes. */
61261d06d6bSBaptiste Daroussin 	if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
61361d06d6bSBaptiste Daroussin 		return;
61461d06d6bSBaptiste Daroussin 
61561d06d6bSBaptiste Daroussin 	/* At least three alphabetic words with a sentence ending. */
61661d06d6bSBaptiste Daroussin 	if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
61761d06d6bSBaptiste Daroussin 	    tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
61861d06d6bSBaptiste Daroussin 		nw = 0;
61961d06d6bSBaptiste Daroussin 		for (cp = lc - 1; cp >= nch->string; cp--) {
62061d06d6bSBaptiste Daroussin 			if (*cp == ' ') {
62161d06d6bSBaptiste Daroussin 				nw++;
62261d06d6bSBaptiste Daroussin 				if (cp > nch->string && cp[-1] == ',')
62361d06d6bSBaptiste Daroussin 					cp--;
62461d06d6bSBaptiste Daroussin 			} else if (isalpha((unsigned int)*cp)) {
62561d06d6bSBaptiste Daroussin 				if (nw > 1)
62661d06d6bSBaptiste Daroussin 					return;
62761d06d6bSBaptiste Daroussin 			} else
62861d06d6bSBaptiste Daroussin 				break;
62961d06d6bSBaptiste Daroussin 		}
63061d06d6bSBaptiste Daroussin 	}
63161d06d6bSBaptiste Daroussin 
6327295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
6337295610fSBaptiste Daroussin 	    nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
63461d06d6bSBaptiste Daroussin 	    nch == mdoc->last->child ? "" : " ...", nch->string);
63561d06d6bSBaptiste Daroussin }
63661d06d6bSBaptiste Daroussin 
63761d06d6bSBaptiste Daroussin static void
63861d06d6bSBaptiste Daroussin post_bl_norm(POST_ARGS)
63961d06d6bSBaptiste Daroussin {
64061d06d6bSBaptiste Daroussin 	struct roff_node *n;
64161d06d6bSBaptiste Daroussin 	struct mdoc_argv *argv, *wa;
64261d06d6bSBaptiste Daroussin 	int		  i;
64361d06d6bSBaptiste Daroussin 	enum mdocargt	  mdoclt;
64461d06d6bSBaptiste Daroussin 	enum mdoc_list	  lt;
64561d06d6bSBaptiste Daroussin 
64661d06d6bSBaptiste Daroussin 	n = mdoc->last->parent;
64761d06d6bSBaptiste Daroussin 	n->norm->Bl.type = LIST__NONE;
64861d06d6bSBaptiste Daroussin 
64961d06d6bSBaptiste Daroussin 	/*
65061d06d6bSBaptiste Daroussin 	 * First figure out which kind of list to use: bind ourselves to
65161d06d6bSBaptiste Daroussin 	 * the first mentioned list type and warn about any remaining
65261d06d6bSBaptiste Daroussin 	 * ones.  If we find no list type, we default to LIST_item.
65361d06d6bSBaptiste Daroussin 	 */
65461d06d6bSBaptiste Daroussin 
65561d06d6bSBaptiste Daroussin 	wa = (n->args == NULL) ? NULL : n->args->argv;
65661d06d6bSBaptiste Daroussin 	mdoclt = MDOC_ARG_MAX;
65761d06d6bSBaptiste Daroussin 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
65861d06d6bSBaptiste Daroussin 		argv = n->args->argv + i;
65961d06d6bSBaptiste Daroussin 		lt = LIST__NONE;
66061d06d6bSBaptiste Daroussin 		switch (argv->arg) {
66161d06d6bSBaptiste Daroussin 		/* Set list types. */
66261d06d6bSBaptiste Daroussin 		case MDOC_Bullet:
66361d06d6bSBaptiste Daroussin 			lt = LIST_bullet;
66461d06d6bSBaptiste Daroussin 			break;
66561d06d6bSBaptiste Daroussin 		case MDOC_Dash:
66661d06d6bSBaptiste Daroussin 			lt = LIST_dash;
66761d06d6bSBaptiste Daroussin 			break;
66861d06d6bSBaptiste Daroussin 		case MDOC_Enum:
66961d06d6bSBaptiste Daroussin 			lt = LIST_enum;
67061d06d6bSBaptiste Daroussin 			break;
67161d06d6bSBaptiste Daroussin 		case MDOC_Hyphen:
67261d06d6bSBaptiste Daroussin 			lt = LIST_hyphen;
67361d06d6bSBaptiste Daroussin 			break;
67461d06d6bSBaptiste Daroussin 		case MDOC_Item:
67561d06d6bSBaptiste Daroussin 			lt = LIST_item;
67661d06d6bSBaptiste Daroussin 			break;
67761d06d6bSBaptiste Daroussin 		case MDOC_Tag:
67861d06d6bSBaptiste Daroussin 			lt = LIST_tag;
67961d06d6bSBaptiste Daroussin 			break;
68061d06d6bSBaptiste Daroussin 		case MDOC_Diag:
68161d06d6bSBaptiste Daroussin 			lt = LIST_diag;
68261d06d6bSBaptiste Daroussin 			break;
68361d06d6bSBaptiste Daroussin 		case MDOC_Hang:
68461d06d6bSBaptiste Daroussin 			lt = LIST_hang;
68561d06d6bSBaptiste Daroussin 			break;
68661d06d6bSBaptiste Daroussin 		case MDOC_Ohang:
68761d06d6bSBaptiste Daroussin 			lt = LIST_ohang;
68861d06d6bSBaptiste Daroussin 			break;
68961d06d6bSBaptiste Daroussin 		case MDOC_Inset:
69061d06d6bSBaptiste Daroussin 			lt = LIST_inset;
69161d06d6bSBaptiste Daroussin 			break;
69261d06d6bSBaptiste Daroussin 		case MDOC_Column:
69361d06d6bSBaptiste Daroussin 			lt = LIST_column;
69461d06d6bSBaptiste Daroussin 			break;
69561d06d6bSBaptiste Daroussin 		/* Set list arguments. */
69661d06d6bSBaptiste Daroussin 		case MDOC_Compact:
69761d06d6bSBaptiste Daroussin 			if (n->norm->Bl.comp)
69861d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_REP,
6997295610fSBaptiste Daroussin 				    argv->line, argv->pos, "Bl -compact");
70061d06d6bSBaptiste Daroussin 			n->norm->Bl.comp = 1;
70161d06d6bSBaptiste Daroussin 			break;
70261d06d6bSBaptiste Daroussin 		case MDOC_Width:
70361d06d6bSBaptiste Daroussin 			wa = argv;
70461d06d6bSBaptiste Daroussin 			if (0 == argv->sz) {
70561d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7067295610fSBaptiste Daroussin 				    argv->line, argv->pos, "Bl -width");
70761d06d6bSBaptiste Daroussin 				n->norm->Bl.width = "0n";
70861d06d6bSBaptiste Daroussin 				break;
70961d06d6bSBaptiste Daroussin 			}
71061d06d6bSBaptiste Daroussin 			if (NULL != n->norm->Bl.width)
7117295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_REP,
7127295610fSBaptiste Daroussin 				    argv->line, argv->pos,
7137295610fSBaptiste Daroussin 				    "Bl -width %s", argv->value[0]);
71461d06d6bSBaptiste Daroussin 			rewrite_macro2len(mdoc, argv->value);
71561d06d6bSBaptiste Daroussin 			n->norm->Bl.width = argv->value[0];
71661d06d6bSBaptiste Daroussin 			break;
71761d06d6bSBaptiste Daroussin 		case MDOC_Offset:
71861d06d6bSBaptiste Daroussin 			if (0 == argv->sz) {
71961d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_EMPTY,
7207295610fSBaptiste Daroussin 				    argv->line, argv->pos, "Bl -offset");
72161d06d6bSBaptiste Daroussin 				break;
72261d06d6bSBaptiste Daroussin 			}
72361d06d6bSBaptiste Daroussin 			if (NULL != n->norm->Bl.offs)
7247295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_REP,
7257295610fSBaptiste Daroussin 				    argv->line, argv->pos,
7267295610fSBaptiste Daroussin 				    "Bl -offset %s", argv->value[0]);
72761d06d6bSBaptiste Daroussin 			rewrite_macro2len(mdoc, argv->value);
72861d06d6bSBaptiste Daroussin 			n->norm->Bl.offs = argv->value[0];
72961d06d6bSBaptiste Daroussin 			break;
73061d06d6bSBaptiste Daroussin 		default:
73161d06d6bSBaptiste Daroussin 			continue;
73261d06d6bSBaptiste Daroussin 		}
73361d06d6bSBaptiste Daroussin 		if (LIST__NONE == lt)
73461d06d6bSBaptiste Daroussin 			continue;
73561d06d6bSBaptiste Daroussin 		mdoclt = argv->arg;
73661d06d6bSBaptiste Daroussin 
73761d06d6bSBaptiste Daroussin 		/* Check: multiple list types. */
73861d06d6bSBaptiste Daroussin 
73961d06d6bSBaptiste Daroussin 		if (LIST__NONE != n->norm->Bl.type) {
7407295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
74161d06d6bSBaptiste Daroussin 			    "Bl -%s", mdoc_argnames[argv->arg]);
74261d06d6bSBaptiste Daroussin 			continue;
74361d06d6bSBaptiste Daroussin 		}
74461d06d6bSBaptiste Daroussin 
74561d06d6bSBaptiste Daroussin 		/* The list type should come first. */
74661d06d6bSBaptiste Daroussin 
74761d06d6bSBaptiste Daroussin 		if (n->norm->Bl.width ||
74861d06d6bSBaptiste Daroussin 		    n->norm->Bl.offs ||
74961d06d6bSBaptiste Daroussin 		    n->norm->Bl.comp)
7507295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BL_LATETYPE,
7517295610fSBaptiste Daroussin 			    n->line, n->pos, "Bl -%s",
75261d06d6bSBaptiste Daroussin 			    mdoc_argnames[n->args->argv[0].arg]);
75361d06d6bSBaptiste Daroussin 
75461d06d6bSBaptiste Daroussin 		n->norm->Bl.type = lt;
75561d06d6bSBaptiste Daroussin 		if (LIST_column == lt) {
75661d06d6bSBaptiste Daroussin 			n->norm->Bl.ncols = argv->sz;
75761d06d6bSBaptiste Daroussin 			n->norm->Bl.cols = (void *)argv->value;
75861d06d6bSBaptiste Daroussin 		}
75961d06d6bSBaptiste Daroussin 	}
76061d06d6bSBaptiste Daroussin 
76161d06d6bSBaptiste Daroussin 	/* Allow lists to default to LIST_item. */
76261d06d6bSBaptiste Daroussin 
76361d06d6bSBaptiste Daroussin 	if (LIST__NONE == n->norm->Bl.type) {
7647295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
76561d06d6bSBaptiste Daroussin 		n->norm->Bl.type = LIST_item;
76661d06d6bSBaptiste Daroussin 		mdoclt = MDOC_Item;
76761d06d6bSBaptiste Daroussin 	}
76861d06d6bSBaptiste Daroussin 
76961d06d6bSBaptiste Daroussin 	/*
77061d06d6bSBaptiste Daroussin 	 * Validate the width field.  Some list types don't need width
77161d06d6bSBaptiste Daroussin 	 * types and should be warned about them.  Others should have it
77261d06d6bSBaptiste Daroussin 	 * and must also be warned.  Yet others have a default and need
77361d06d6bSBaptiste Daroussin 	 * no warning.
77461d06d6bSBaptiste Daroussin 	 */
77561d06d6bSBaptiste Daroussin 
77661d06d6bSBaptiste Daroussin 	switch (n->norm->Bl.type) {
77761d06d6bSBaptiste Daroussin 	case LIST_tag:
77861d06d6bSBaptiste Daroussin 		if (n->norm->Bl.width == NULL)
7797295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BL_NOWIDTH,
78061d06d6bSBaptiste Daroussin 			    n->line, n->pos, "Bl -tag");
78161d06d6bSBaptiste Daroussin 		break;
78261d06d6bSBaptiste Daroussin 	case LIST_column:
78361d06d6bSBaptiste Daroussin 	case LIST_diag:
78461d06d6bSBaptiste Daroussin 	case LIST_ohang:
78561d06d6bSBaptiste Daroussin 	case LIST_inset:
78661d06d6bSBaptiste Daroussin 	case LIST_item:
78761d06d6bSBaptiste Daroussin 		if (n->norm->Bl.width != NULL)
7887295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
7897295610fSBaptiste Daroussin 			    "Bl -%s", mdoc_argnames[mdoclt]);
79061d06d6bSBaptiste Daroussin 		n->norm->Bl.width = NULL;
79161d06d6bSBaptiste Daroussin 		break;
79261d06d6bSBaptiste Daroussin 	case LIST_bullet:
79361d06d6bSBaptiste Daroussin 	case LIST_dash:
79461d06d6bSBaptiste Daroussin 	case LIST_hyphen:
79561d06d6bSBaptiste Daroussin 		if (n->norm->Bl.width == NULL)
79661d06d6bSBaptiste Daroussin 			n->norm->Bl.width = "2n";
79761d06d6bSBaptiste Daroussin 		break;
79861d06d6bSBaptiste Daroussin 	case LIST_enum:
79961d06d6bSBaptiste Daroussin 		if (n->norm->Bl.width == NULL)
80061d06d6bSBaptiste Daroussin 			n->norm->Bl.width = "3n";
80161d06d6bSBaptiste Daroussin 		break;
80261d06d6bSBaptiste Daroussin 	default:
80361d06d6bSBaptiste Daroussin 		break;
80461d06d6bSBaptiste Daroussin 	}
80561d06d6bSBaptiste Daroussin }
80661d06d6bSBaptiste Daroussin 
80761d06d6bSBaptiste Daroussin static void
80861d06d6bSBaptiste Daroussin post_bd(POST_ARGS)
80961d06d6bSBaptiste Daroussin {
81061d06d6bSBaptiste Daroussin 	struct roff_node *n;
81161d06d6bSBaptiste Daroussin 	struct mdoc_argv *argv;
81261d06d6bSBaptiste Daroussin 	int		  i;
81361d06d6bSBaptiste Daroussin 	enum mdoc_disp	  dt;
81461d06d6bSBaptiste Daroussin 
81561d06d6bSBaptiste Daroussin 	n = mdoc->last;
81661d06d6bSBaptiste Daroussin 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
81761d06d6bSBaptiste Daroussin 		argv = n->args->argv + i;
81861d06d6bSBaptiste Daroussin 		dt = DISP__NONE;
81961d06d6bSBaptiste Daroussin 
82061d06d6bSBaptiste Daroussin 		switch (argv->arg) {
82161d06d6bSBaptiste Daroussin 		case MDOC_Centred:
82261d06d6bSBaptiste Daroussin 			dt = DISP_centered;
82361d06d6bSBaptiste Daroussin 			break;
82461d06d6bSBaptiste Daroussin 		case MDOC_Ragged:
82561d06d6bSBaptiste Daroussin 			dt = DISP_ragged;
82661d06d6bSBaptiste Daroussin 			break;
82761d06d6bSBaptiste Daroussin 		case MDOC_Unfilled:
82861d06d6bSBaptiste Daroussin 			dt = DISP_unfilled;
82961d06d6bSBaptiste Daroussin 			break;
83061d06d6bSBaptiste Daroussin 		case MDOC_Filled:
83161d06d6bSBaptiste Daroussin 			dt = DISP_filled;
83261d06d6bSBaptiste Daroussin 			break;
83361d06d6bSBaptiste Daroussin 		case MDOC_Literal:
83461d06d6bSBaptiste Daroussin 			dt = DISP_literal;
83561d06d6bSBaptiste Daroussin 			break;
83661d06d6bSBaptiste Daroussin 		case MDOC_File:
8377295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL);
83861d06d6bSBaptiste Daroussin 			break;
83961d06d6bSBaptiste Daroussin 		case MDOC_Offset:
84061d06d6bSBaptiste Daroussin 			if (0 == argv->sz) {
84161d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_EMPTY,
8427295610fSBaptiste Daroussin 				    argv->line, argv->pos, "Bd -offset");
84361d06d6bSBaptiste Daroussin 				break;
84461d06d6bSBaptiste Daroussin 			}
84561d06d6bSBaptiste Daroussin 			if (NULL != n->norm->Bd.offs)
8467295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_REP,
8477295610fSBaptiste Daroussin 				    argv->line, argv->pos,
8487295610fSBaptiste Daroussin 				    "Bd -offset %s", argv->value[0]);
84961d06d6bSBaptiste Daroussin 			rewrite_macro2len(mdoc, argv->value);
85061d06d6bSBaptiste Daroussin 			n->norm->Bd.offs = argv->value[0];
85161d06d6bSBaptiste Daroussin 			break;
85261d06d6bSBaptiste Daroussin 		case MDOC_Compact:
85361d06d6bSBaptiste Daroussin 			if (n->norm->Bd.comp)
85461d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_REP,
8557295610fSBaptiste Daroussin 				    argv->line, argv->pos, "Bd -compact");
85661d06d6bSBaptiste Daroussin 			n->norm->Bd.comp = 1;
85761d06d6bSBaptiste Daroussin 			break;
85861d06d6bSBaptiste Daroussin 		default:
85961d06d6bSBaptiste Daroussin 			abort();
86061d06d6bSBaptiste Daroussin 		}
86161d06d6bSBaptiste Daroussin 		if (DISP__NONE == dt)
86261d06d6bSBaptiste Daroussin 			continue;
86361d06d6bSBaptiste Daroussin 
86461d06d6bSBaptiste Daroussin 		if (DISP__NONE == n->norm->Bd.type)
86561d06d6bSBaptiste Daroussin 			n->norm->Bd.type = dt;
86661d06d6bSBaptiste Daroussin 		else
8677295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
86861d06d6bSBaptiste Daroussin 			    "Bd -%s", mdoc_argnames[argv->arg]);
86961d06d6bSBaptiste Daroussin 	}
87061d06d6bSBaptiste Daroussin 
87161d06d6bSBaptiste Daroussin 	if (DISP__NONE == n->norm->Bd.type) {
8727295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
87361d06d6bSBaptiste Daroussin 		n->norm->Bd.type = DISP_ragged;
87461d06d6bSBaptiste Daroussin 	}
87561d06d6bSBaptiste Daroussin }
87661d06d6bSBaptiste Daroussin 
87761d06d6bSBaptiste Daroussin /*
87861d06d6bSBaptiste Daroussin  * Stand-alone line macros.
87961d06d6bSBaptiste Daroussin  */
88061d06d6bSBaptiste Daroussin 
88161d06d6bSBaptiste Daroussin static void
88261d06d6bSBaptiste Daroussin post_an_norm(POST_ARGS)
88361d06d6bSBaptiste Daroussin {
88461d06d6bSBaptiste Daroussin 	struct roff_node *n;
88561d06d6bSBaptiste Daroussin 	struct mdoc_argv *argv;
88661d06d6bSBaptiste Daroussin 	size_t	 i;
88761d06d6bSBaptiste Daroussin 
88861d06d6bSBaptiste Daroussin 	n = mdoc->last;
88961d06d6bSBaptiste Daroussin 	if (n->args == NULL)
89061d06d6bSBaptiste Daroussin 		return;
89161d06d6bSBaptiste Daroussin 
89261d06d6bSBaptiste Daroussin 	for (i = 1; i < n->args->argc; i++) {
89361d06d6bSBaptiste Daroussin 		argv = n->args->argv + i;
8947295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
89561d06d6bSBaptiste Daroussin 		    "An -%s", mdoc_argnames[argv->arg]);
89661d06d6bSBaptiste Daroussin 	}
89761d06d6bSBaptiste Daroussin 
89861d06d6bSBaptiste Daroussin 	argv = n->args->argv;
89961d06d6bSBaptiste Daroussin 	if (argv->arg == MDOC_Split)
90061d06d6bSBaptiste Daroussin 		n->norm->An.auth = AUTH_split;
90161d06d6bSBaptiste Daroussin 	else if (argv->arg == MDOC_Nosplit)
90261d06d6bSBaptiste Daroussin 		n->norm->An.auth = AUTH_nosplit;
90361d06d6bSBaptiste Daroussin 	else
90461d06d6bSBaptiste Daroussin 		abort();
90561d06d6bSBaptiste Daroussin }
90661d06d6bSBaptiste Daroussin 
90761d06d6bSBaptiste Daroussin static void
90861d06d6bSBaptiste Daroussin post_eoln(POST_ARGS)
90961d06d6bSBaptiste Daroussin {
91061d06d6bSBaptiste Daroussin 	struct roff_node	*n;
91161d06d6bSBaptiste Daroussin 
91261d06d6bSBaptiste Daroussin 	post_useless(mdoc);
91361d06d6bSBaptiste Daroussin 	n = mdoc->last;
91461d06d6bSBaptiste Daroussin 	if (n->child != NULL)
9157295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
91661d06d6bSBaptiste Daroussin 		    n->pos, "%s %s", roff_name[n->tok], n->child->string);
91761d06d6bSBaptiste Daroussin 
91861d06d6bSBaptiste Daroussin 	while (n->child != NULL)
91961d06d6bSBaptiste Daroussin 		roff_node_delete(mdoc, n->child);
92061d06d6bSBaptiste Daroussin 
92161d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
92261d06d6bSBaptiste Daroussin 	    "is currently in beta test." : "currently under development.");
92361d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
92461d06d6bSBaptiste Daroussin 	mdoc->last = n;
92561d06d6bSBaptiste Daroussin }
92661d06d6bSBaptiste Daroussin 
92761d06d6bSBaptiste Daroussin static int
92861d06d6bSBaptiste Daroussin build_list(struct roff_man *mdoc, int tok)
92961d06d6bSBaptiste Daroussin {
93061d06d6bSBaptiste Daroussin 	struct roff_node	*n;
93161d06d6bSBaptiste Daroussin 	int			 ic;
93261d06d6bSBaptiste Daroussin 
93361d06d6bSBaptiste Daroussin 	n = mdoc->last->next;
93461d06d6bSBaptiste Daroussin 	for (ic = 1;; ic++) {
93561d06d6bSBaptiste Daroussin 		roff_elem_alloc(mdoc, n->line, n->pos, tok);
93661d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
9377295610fSBaptiste Daroussin 		roff_node_relink(mdoc, n);
93861d06d6bSBaptiste Daroussin 		n = mdoc->last = mdoc->last->parent;
93961d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
94061d06d6bSBaptiste Daroussin 		if (n->next == NULL)
94161d06d6bSBaptiste Daroussin 			return ic;
94261d06d6bSBaptiste Daroussin 		if (ic > 1 || n->next->next != NULL) {
94361d06d6bSBaptiste Daroussin 			roff_word_alloc(mdoc, n->line, n->pos, ",");
94461d06d6bSBaptiste Daroussin 			mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
94561d06d6bSBaptiste Daroussin 		}
94661d06d6bSBaptiste Daroussin 		n = mdoc->last->next;
94761d06d6bSBaptiste Daroussin 		if (n->next == NULL) {
94861d06d6bSBaptiste Daroussin 			roff_word_alloc(mdoc, n->line, n->pos, "and");
94961d06d6bSBaptiste Daroussin 			mdoc->last->flags |= NODE_NOSRC;
95061d06d6bSBaptiste Daroussin 		}
95161d06d6bSBaptiste Daroussin 	}
95261d06d6bSBaptiste Daroussin }
95361d06d6bSBaptiste Daroussin 
95461d06d6bSBaptiste Daroussin static void
95561d06d6bSBaptiste Daroussin post_ex(POST_ARGS)
95661d06d6bSBaptiste Daroussin {
95761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
95861d06d6bSBaptiste Daroussin 	int			 ic;
95961d06d6bSBaptiste Daroussin 
96061d06d6bSBaptiste Daroussin 	post_std(mdoc);
96161d06d6bSBaptiste Daroussin 
96261d06d6bSBaptiste Daroussin 	n = mdoc->last;
96361d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
96461d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "The");
96561d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
96661d06d6bSBaptiste Daroussin 
96761d06d6bSBaptiste Daroussin 	if (mdoc->last->next != NULL)
96861d06d6bSBaptiste Daroussin 		ic = build_list(mdoc, MDOC_Nm);
96961d06d6bSBaptiste Daroussin 	else if (mdoc->meta.name != NULL) {
97061d06d6bSBaptiste Daroussin 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
97161d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
97261d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
97361d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
97461d06d6bSBaptiste Daroussin 		mdoc->last = mdoc->last->parent;
97561d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
97661d06d6bSBaptiste Daroussin 		ic = 1;
97761d06d6bSBaptiste Daroussin 	} else {
9787295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
97961d06d6bSBaptiste Daroussin 		ic = 0;
98061d06d6bSBaptiste Daroussin 	}
98161d06d6bSBaptiste Daroussin 
98261d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos,
98361d06d6bSBaptiste Daroussin 	    ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
98461d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
98561d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos,
98661d06d6bSBaptiste Daroussin 	    "on success, and\\~>0 if an error occurs.");
98761d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
98861d06d6bSBaptiste Daroussin 	mdoc->last = n;
98961d06d6bSBaptiste Daroussin }
99061d06d6bSBaptiste Daroussin 
99161d06d6bSBaptiste Daroussin static void
99261d06d6bSBaptiste Daroussin post_lb(POST_ARGS)
99361d06d6bSBaptiste Daroussin {
99461d06d6bSBaptiste Daroussin 	struct roff_node	*n;
99561d06d6bSBaptiste Daroussin 	const char		*p;
99661d06d6bSBaptiste Daroussin 
99761d06d6bSBaptiste Daroussin 	post_delim_nb(mdoc);
99861d06d6bSBaptiste Daroussin 
99961d06d6bSBaptiste Daroussin 	n = mdoc->last;
100061d06d6bSBaptiste Daroussin 	assert(n->child->type == ROFFT_TEXT);
100161d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
100261d06d6bSBaptiste Daroussin 
100361d06d6bSBaptiste Daroussin 	if ((p = mdoc_a2lib(n->child->string)) != NULL) {
100461d06d6bSBaptiste Daroussin 		n->child->flags |= NODE_NOPRT;
100561d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, p);
100661d06d6bSBaptiste Daroussin 		mdoc->last->flags = NODE_NOSRC;
100761d06d6bSBaptiste Daroussin 		mdoc->last = n;
100861d06d6bSBaptiste Daroussin 		return;
100961d06d6bSBaptiste Daroussin 	}
101061d06d6bSBaptiste Daroussin 
10117295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_LB_BAD, n->child->line,
101261d06d6bSBaptiste Daroussin 	    n->child->pos, "Lb %s", n->child->string);
101361d06d6bSBaptiste Daroussin 
101461d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "library");
101561d06d6bSBaptiste Daroussin 	mdoc->last->flags = NODE_NOSRC;
101661d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
101761d06d6bSBaptiste Daroussin 	mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
101861d06d6bSBaptiste Daroussin 	mdoc->last = mdoc->last->next;
101961d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
102061d06d6bSBaptiste Daroussin 	mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
102161d06d6bSBaptiste Daroussin 	mdoc->last = n;
102261d06d6bSBaptiste Daroussin }
102361d06d6bSBaptiste Daroussin 
102461d06d6bSBaptiste Daroussin static void
102561d06d6bSBaptiste Daroussin post_rv(POST_ARGS)
102661d06d6bSBaptiste Daroussin {
102761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
102861d06d6bSBaptiste Daroussin 	int			 ic;
102961d06d6bSBaptiste Daroussin 
103061d06d6bSBaptiste Daroussin 	post_std(mdoc);
103161d06d6bSBaptiste Daroussin 
103261d06d6bSBaptiste Daroussin 	n = mdoc->last;
103361d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
103461d06d6bSBaptiste Daroussin 	if (n->child != NULL) {
103561d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "The");
103661d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
103761d06d6bSBaptiste Daroussin 		ic = build_list(mdoc, MDOC_Fn);
103861d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos,
103961d06d6bSBaptiste Daroussin 		    ic > 1 ? "functions return" : "function returns");
104061d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
104161d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos,
104261d06d6bSBaptiste Daroussin 		    "the value\\~0 if successful;");
104361d06d6bSBaptiste Daroussin 	} else
104461d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
104561d06d6bSBaptiste Daroussin 		    "completion, the value\\~0 is returned;");
104661d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
104761d06d6bSBaptiste Daroussin 
104861d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
104961d06d6bSBaptiste Daroussin 	    "the value\\~\\-1 is returned and the global variable");
105061d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
105161d06d6bSBaptiste Daroussin 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
105261d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
105361d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "errno");
105461d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
105561d06d6bSBaptiste Daroussin 	mdoc->last = mdoc->last->parent;
105661d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_SIBLING;
105761d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos,
105861d06d6bSBaptiste Daroussin 	    "is set to indicate the error.");
105961d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
106061d06d6bSBaptiste Daroussin 	mdoc->last = n;
106161d06d6bSBaptiste Daroussin }
106261d06d6bSBaptiste Daroussin 
106361d06d6bSBaptiste Daroussin static void
106461d06d6bSBaptiste Daroussin post_std(POST_ARGS)
106561d06d6bSBaptiste Daroussin {
106661d06d6bSBaptiste Daroussin 	struct roff_node *n;
106761d06d6bSBaptiste Daroussin 
106861d06d6bSBaptiste Daroussin 	post_delim(mdoc);
106961d06d6bSBaptiste Daroussin 
107061d06d6bSBaptiste Daroussin 	n = mdoc->last;
107161d06d6bSBaptiste Daroussin 	if (n->args && n->args->argc == 1)
107261d06d6bSBaptiste Daroussin 		if (n->args->argv[0].arg == MDOC_Std)
107361d06d6bSBaptiste Daroussin 			return;
107461d06d6bSBaptiste Daroussin 
10757295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
10767295610fSBaptiste Daroussin 	    "%s", roff_name[n->tok]);
107761d06d6bSBaptiste Daroussin }
107861d06d6bSBaptiste Daroussin 
107961d06d6bSBaptiste Daroussin static void
108061d06d6bSBaptiste Daroussin post_st(POST_ARGS)
108161d06d6bSBaptiste Daroussin {
108261d06d6bSBaptiste Daroussin 	struct roff_node	 *n, *nch;
108361d06d6bSBaptiste Daroussin 	const char		 *p;
108461d06d6bSBaptiste Daroussin 
108561d06d6bSBaptiste Daroussin 	n = mdoc->last;
108661d06d6bSBaptiste Daroussin 	nch = n->child;
108761d06d6bSBaptiste Daroussin 	assert(nch->type == ROFFT_TEXT);
108861d06d6bSBaptiste Daroussin 
108961d06d6bSBaptiste Daroussin 	if ((p = mdoc_a2st(nch->string)) == NULL) {
10907295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ST_BAD,
109161d06d6bSBaptiste Daroussin 		    nch->line, nch->pos, "St %s", nch->string);
109261d06d6bSBaptiste Daroussin 		roff_node_delete(mdoc, n);
109361d06d6bSBaptiste Daroussin 		return;
109461d06d6bSBaptiste Daroussin 	}
109561d06d6bSBaptiste Daroussin 
109661d06d6bSBaptiste Daroussin 	nch->flags |= NODE_NOPRT;
109761d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
109861d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, nch->line, nch->pos, p);
109961d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
110061d06d6bSBaptiste Daroussin 	mdoc->last= n;
110161d06d6bSBaptiste Daroussin }
110261d06d6bSBaptiste Daroussin 
110361d06d6bSBaptiste Daroussin static void
11046d38604fSBaptiste Daroussin post_tg(POST_ARGS)
11056d38604fSBaptiste Daroussin {
11066d38604fSBaptiste Daroussin 	struct roff_node *n;	/* The .Tg node. */
11076d38604fSBaptiste Daroussin 	struct roff_node *nch;	/* The first child of the .Tg node. */
11086d38604fSBaptiste Daroussin 	struct roff_node *nn;   /* The next node after the .Tg node. */
11096d38604fSBaptiste Daroussin 	struct roff_node *np;	/* The parent of the next node. */
11106d38604fSBaptiste Daroussin 	struct roff_node *nt;	/* The TEXT node containing the tag. */
11116d38604fSBaptiste Daroussin 	size_t		  len;	/* The number of bytes in the tag. */
11126d38604fSBaptiste Daroussin 
11136d38604fSBaptiste Daroussin 	/* Find the next node. */
11146d38604fSBaptiste Daroussin 	n = mdoc->last;
11156d38604fSBaptiste Daroussin 	for (nn = n; nn != NULL; nn = nn->parent) {
1116*c1c95addSBrooks Davis 		if (nn->type != ROFFT_HEAD && nn->type != ROFFT_BODY &&
1117*c1c95addSBrooks Davis 		    nn->type != ROFFT_TAIL && nn->next != NULL) {
11186d38604fSBaptiste Daroussin 			nn = nn->next;
11196d38604fSBaptiste Daroussin 			break;
11206d38604fSBaptiste Daroussin 		}
11216d38604fSBaptiste Daroussin 	}
11226d38604fSBaptiste Daroussin 
11236d38604fSBaptiste Daroussin 	/* Find the tag. */
11246d38604fSBaptiste Daroussin 	nt = nch = n->child;
11256d38604fSBaptiste Daroussin 	if (nch == NULL && nn != NULL && nn->child != NULL &&
11266d38604fSBaptiste Daroussin 	    nn->child->type == ROFFT_TEXT)
11276d38604fSBaptiste Daroussin 		nt = nn->child;
11286d38604fSBaptiste Daroussin 
11296d38604fSBaptiste Daroussin 	/* Validate the tag. */
11306d38604fSBaptiste Daroussin 	if (nt == NULL || *nt->string == '\0')
11316d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
11326d38604fSBaptiste Daroussin 	if (nt == NULL) {
11336d38604fSBaptiste Daroussin 		roff_node_delete(mdoc, n);
11346d38604fSBaptiste Daroussin 		return;
11356d38604fSBaptiste Daroussin 	}
11366d38604fSBaptiste Daroussin 	len = strcspn(nt->string, " \t\\");
11376d38604fSBaptiste Daroussin 	if (nt->string[len] != '\0')
11386d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TG_SPC, nt->line,
11396d38604fSBaptiste Daroussin 		    nt->pos + len, "Tg %s", nt->string);
11406d38604fSBaptiste Daroussin 
11416d38604fSBaptiste Daroussin 	/* Keep only the first argument. */
11426d38604fSBaptiste Daroussin 	if (nch != NULL && nch->next != NULL) {
11436d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
11446d38604fSBaptiste Daroussin 		    nch->next->pos, "Tg ... %s", nch->next->string);
11456d38604fSBaptiste Daroussin 		while (nch->next != NULL)
11466d38604fSBaptiste Daroussin 			roff_node_delete(mdoc, nch->next);
11476d38604fSBaptiste Daroussin 	}
11486d38604fSBaptiste Daroussin 
11496d38604fSBaptiste Daroussin 	/* Drop the macro if the first argument is invalid. */
11506d38604fSBaptiste Daroussin 	if (len == 0 || nt->string[len] != '\0') {
11516d38604fSBaptiste Daroussin 		roff_node_delete(mdoc, n);
11526d38604fSBaptiste Daroussin 		return;
11536d38604fSBaptiste Daroussin 	}
11546d38604fSBaptiste Daroussin 
11556d38604fSBaptiste Daroussin 	/* By default, tag the .Tg node itself. */
11566d38604fSBaptiste Daroussin 	if (nn == NULL || nn->flags & NODE_ID)
11576d38604fSBaptiste Daroussin 		nn = n;
11586d38604fSBaptiste Daroussin 
11596d38604fSBaptiste Daroussin 	/* Explicit tagging of specific macros. */
11606d38604fSBaptiste Daroussin 	switch (nn->tok) {
11616d38604fSBaptiste Daroussin 	case MDOC_Sh:
11626d38604fSBaptiste Daroussin 	case MDOC_Ss:
11636d38604fSBaptiste Daroussin 	case MDOC_Fo:
11646d38604fSBaptiste Daroussin 		nn = nn->head->child == NULL ? n : nn->head;
11656d38604fSBaptiste Daroussin 		break;
11666d38604fSBaptiste Daroussin 	case MDOC_It:
11676d38604fSBaptiste Daroussin 		np = nn->parent;
11686d38604fSBaptiste Daroussin 		while (np->tok != MDOC_Bl)
11696d38604fSBaptiste Daroussin 			np = np->parent;
11706d38604fSBaptiste Daroussin 		switch (np->norm->Bl.type) {
11716d38604fSBaptiste Daroussin 		case LIST_column:
11726d38604fSBaptiste Daroussin 			break;
11736d38604fSBaptiste Daroussin 		case LIST_diag:
11746d38604fSBaptiste Daroussin 		case LIST_hang:
11756d38604fSBaptiste Daroussin 		case LIST_inset:
11766d38604fSBaptiste Daroussin 		case LIST_ohang:
11776d38604fSBaptiste Daroussin 		case LIST_tag:
11786d38604fSBaptiste Daroussin 			nn = nn->head;
11796d38604fSBaptiste Daroussin 			break;
11806d38604fSBaptiste Daroussin 		case LIST_bullet:
11816d38604fSBaptiste Daroussin 		case LIST_dash:
11826d38604fSBaptiste Daroussin 		case LIST_enum:
11836d38604fSBaptiste Daroussin 		case LIST_hyphen:
11846d38604fSBaptiste Daroussin 		case LIST_item:
11856d38604fSBaptiste Daroussin 			nn = nn->body->child == NULL ? n : nn->body;
11866d38604fSBaptiste Daroussin 			break;
11876d38604fSBaptiste Daroussin 		default:
11886d38604fSBaptiste Daroussin 			abort();
11896d38604fSBaptiste Daroussin 		}
11906d38604fSBaptiste Daroussin 		break;
11916d38604fSBaptiste Daroussin 	case MDOC_Bd:
11926d38604fSBaptiste Daroussin 	case MDOC_Bl:
11936d38604fSBaptiste Daroussin 	case MDOC_D1:
11946d38604fSBaptiste Daroussin 	case MDOC_Dl:
11956d38604fSBaptiste Daroussin 		nn = nn->body->child == NULL ? n : nn->body;
11966d38604fSBaptiste Daroussin 		break;
11976d38604fSBaptiste Daroussin 	case MDOC_Pp:
11986d38604fSBaptiste Daroussin 		break;
11996d38604fSBaptiste Daroussin 	case MDOC_Cm:
12006d38604fSBaptiste Daroussin 	case MDOC_Dv:
12016d38604fSBaptiste Daroussin 	case MDOC_Em:
12026d38604fSBaptiste Daroussin 	case MDOC_Er:
12036d38604fSBaptiste Daroussin 	case MDOC_Ev:
12046d38604fSBaptiste Daroussin 	case MDOC_Fl:
12056d38604fSBaptiste Daroussin 	case MDOC_Fn:
12066d38604fSBaptiste Daroussin 	case MDOC_Ic:
12076d38604fSBaptiste Daroussin 	case MDOC_Li:
12086d38604fSBaptiste Daroussin 	case MDOC_Ms:
12096d38604fSBaptiste Daroussin 	case MDOC_No:
12106d38604fSBaptiste Daroussin 	case MDOC_Sy:
12116d38604fSBaptiste Daroussin 		if (nn->child == NULL)
12126d38604fSBaptiste Daroussin 			nn = n;
12136d38604fSBaptiste Daroussin 		break;
12146d38604fSBaptiste Daroussin 	default:
12156d38604fSBaptiste Daroussin 		nn = n;
12166d38604fSBaptiste Daroussin 		break;
12176d38604fSBaptiste Daroussin 	}
12186d38604fSBaptiste Daroussin 	tag_put(nt->string, TAG_MANUAL, nn);
12196d38604fSBaptiste Daroussin 	if (nn != n)
12206d38604fSBaptiste Daroussin 		n->flags |= NODE_NOPRT;
12216d38604fSBaptiste Daroussin }
12226d38604fSBaptiste Daroussin 
12236d38604fSBaptiste Daroussin static void
122461d06d6bSBaptiste Daroussin post_obsolete(POST_ARGS)
122561d06d6bSBaptiste Daroussin {
122661d06d6bSBaptiste Daroussin 	struct roff_node *n;
122761d06d6bSBaptiste Daroussin 
122861d06d6bSBaptiste Daroussin 	n = mdoc->last;
122961d06d6bSBaptiste Daroussin 	if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
12307295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
12317295610fSBaptiste Daroussin 		    "%s", roff_name[n->tok]);
123261d06d6bSBaptiste Daroussin }
123361d06d6bSBaptiste Daroussin 
123461d06d6bSBaptiste Daroussin static void
123561d06d6bSBaptiste Daroussin post_useless(POST_ARGS)
123661d06d6bSBaptiste Daroussin {
123761d06d6bSBaptiste Daroussin 	struct roff_node *n;
123861d06d6bSBaptiste Daroussin 
123961d06d6bSBaptiste Daroussin 	n = mdoc->last;
12407295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
12417295610fSBaptiste Daroussin 	    "%s", roff_name[n->tok]);
124261d06d6bSBaptiste Daroussin }
124361d06d6bSBaptiste Daroussin 
124461d06d6bSBaptiste Daroussin /*
124561d06d6bSBaptiste Daroussin  * Block macros.
124661d06d6bSBaptiste Daroussin  */
124761d06d6bSBaptiste Daroussin 
124861d06d6bSBaptiste Daroussin static void
124961d06d6bSBaptiste Daroussin post_bf(POST_ARGS)
125061d06d6bSBaptiste Daroussin {
125161d06d6bSBaptiste Daroussin 	struct roff_node *np, *nch;
125261d06d6bSBaptiste Daroussin 
125361d06d6bSBaptiste Daroussin 	/*
125461d06d6bSBaptiste Daroussin 	 * Unlike other data pointers, these are "housed" by the HEAD
125561d06d6bSBaptiste Daroussin 	 * element, which contains the goods.
125661d06d6bSBaptiste Daroussin 	 */
125761d06d6bSBaptiste Daroussin 
125861d06d6bSBaptiste Daroussin 	np = mdoc->last;
125961d06d6bSBaptiste Daroussin 	if (np->type != ROFFT_HEAD)
126061d06d6bSBaptiste Daroussin 		return;
126161d06d6bSBaptiste Daroussin 
126261d06d6bSBaptiste Daroussin 	assert(np->parent->type == ROFFT_BLOCK);
126361d06d6bSBaptiste Daroussin 	assert(np->parent->tok == MDOC_Bf);
126461d06d6bSBaptiste Daroussin 
126561d06d6bSBaptiste Daroussin 	/* Check the number of arguments. */
126661d06d6bSBaptiste Daroussin 
126761d06d6bSBaptiste Daroussin 	nch = np->child;
126861d06d6bSBaptiste Daroussin 	if (np->parent->args == NULL) {
126961d06d6bSBaptiste Daroussin 		if (nch == NULL) {
12707295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BF_NOFONT,
127161d06d6bSBaptiste Daroussin 			    np->line, np->pos, "Bf");
127261d06d6bSBaptiste Daroussin 			return;
127361d06d6bSBaptiste Daroussin 		}
127461d06d6bSBaptiste Daroussin 		nch = nch->next;
127561d06d6bSBaptiste Daroussin 	}
127661d06d6bSBaptiste Daroussin 	if (nch != NULL)
12777295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
127861d06d6bSBaptiste Daroussin 		    nch->line, nch->pos, "Bf ... %s", nch->string);
127961d06d6bSBaptiste Daroussin 
128061d06d6bSBaptiste Daroussin 	/* Extract argument into data. */
128161d06d6bSBaptiste Daroussin 
128261d06d6bSBaptiste Daroussin 	if (np->parent->args != NULL) {
128361d06d6bSBaptiste Daroussin 		switch (np->parent->args->argv[0].arg) {
128461d06d6bSBaptiste Daroussin 		case MDOC_Emphasis:
128561d06d6bSBaptiste Daroussin 			np->norm->Bf.font = FONT_Em;
128661d06d6bSBaptiste Daroussin 			break;
128761d06d6bSBaptiste Daroussin 		case MDOC_Literal:
128861d06d6bSBaptiste Daroussin 			np->norm->Bf.font = FONT_Li;
128961d06d6bSBaptiste Daroussin 			break;
129061d06d6bSBaptiste Daroussin 		case MDOC_Symbolic:
129161d06d6bSBaptiste Daroussin 			np->norm->Bf.font = FONT_Sy;
129261d06d6bSBaptiste Daroussin 			break;
129361d06d6bSBaptiste Daroussin 		default:
129461d06d6bSBaptiste Daroussin 			abort();
129561d06d6bSBaptiste Daroussin 		}
129661d06d6bSBaptiste Daroussin 		return;
129761d06d6bSBaptiste Daroussin 	}
129861d06d6bSBaptiste Daroussin 
129961d06d6bSBaptiste Daroussin 	/* Extract parameter into data. */
130061d06d6bSBaptiste Daroussin 
130161d06d6bSBaptiste Daroussin 	if ( ! strcmp(np->child->string, "Em"))
130261d06d6bSBaptiste Daroussin 		np->norm->Bf.font = FONT_Em;
130361d06d6bSBaptiste Daroussin 	else if ( ! strcmp(np->child->string, "Li"))
130461d06d6bSBaptiste Daroussin 		np->norm->Bf.font = FONT_Li;
130561d06d6bSBaptiste Daroussin 	else if ( ! strcmp(np->child->string, "Sy"))
130661d06d6bSBaptiste Daroussin 		np->norm->Bf.font = FONT_Sy;
130761d06d6bSBaptiste Daroussin 	else
13087295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
13097295610fSBaptiste Daroussin 		    np->child->pos, "Bf %s", np->child->string);
131061d06d6bSBaptiste Daroussin }
131161d06d6bSBaptiste Daroussin 
131261d06d6bSBaptiste Daroussin static void
131361d06d6bSBaptiste Daroussin post_fname(POST_ARGS)
131461d06d6bSBaptiste Daroussin {
13156d38604fSBaptiste Daroussin 	struct roff_node	*n, *nch;
131661d06d6bSBaptiste Daroussin 	const char		*cp;
131761d06d6bSBaptiste Daroussin 	size_t			 pos;
131861d06d6bSBaptiste Daroussin 
13196d38604fSBaptiste Daroussin 	n = mdoc->last;
13206d38604fSBaptiste Daroussin 	nch = n->child;
13216d38604fSBaptiste Daroussin 	cp = nch->string;
13226d38604fSBaptiste Daroussin 	if (*cp == '(') {
13236d38604fSBaptiste Daroussin 		if (cp[strlen(cp + 1)] == ')')
13246d38604fSBaptiste Daroussin 			return;
13256d38604fSBaptiste Daroussin 		pos = 0;
13266d38604fSBaptiste Daroussin 	} else {
13276d38604fSBaptiste Daroussin 		pos = strcspn(cp, "()");
13286d38604fSBaptiste Daroussin 		if (cp[pos] == '\0') {
13296d38604fSBaptiste Daroussin 			if (n->sec == SEC_DESCRIPTION ||
13306d38604fSBaptiste Daroussin 			    n->sec == SEC_CUSTOM)
13316d38604fSBaptiste Daroussin 				tag_put(NULL, fn_prio++, n);
13326d38604fSBaptiste Daroussin 			return;
13336d38604fSBaptiste Daroussin 		}
13346d38604fSBaptiste Daroussin 	}
13356d38604fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
133661d06d6bSBaptiste Daroussin }
133761d06d6bSBaptiste Daroussin 
133861d06d6bSBaptiste Daroussin static void
133961d06d6bSBaptiste Daroussin post_fn(POST_ARGS)
134061d06d6bSBaptiste Daroussin {
134161d06d6bSBaptiste Daroussin 	post_fname(mdoc);
134261d06d6bSBaptiste Daroussin 	post_fa(mdoc);
134361d06d6bSBaptiste Daroussin }
134461d06d6bSBaptiste Daroussin 
134561d06d6bSBaptiste Daroussin static void
134661d06d6bSBaptiste Daroussin post_fo(POST_ARGS)
134761d06d6bSBaptiste Daroussin {
134861d06d6bSBaptiste Daroussin 	const struct roff_node	*n;
134961d06d6bSBaptiste Daroussin 
135061d06d6bSBaptiste Daroussin 	n = mdoc->last;
135161d06d6bSBaptiste Daroussin 
135261d06d6bSBaptiste Daroussin 	if (n->type != ROFFT_HEAD)
135361d06d6bSBaptiste Daroussin 		return;
135461d06d6bSBaptiste Daroussin 
135561d06d6bSBaptiste Daroussin 	if (n->child == NULL) {
13567295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
135761d06d6bSBaptiste Daroussin 		return;
135861d06d6bSBaptiste Daroussin 	}
135961d06d6bSBaptiste Daroussin 	if (n->child != n->last) {
13607295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
136161d06d6bSBaptiste Daroussin 		    n->child->next->line, n->child->next->pos,
136261d06d6bSBaptiste Daroussin 		    "Fo ... %s", n->child->next->string);
136361d06d6bSBaptiste Daroussin 		while (n->child != n->last)
136461d06d6bSBaptiste Daroussin 			roff_node_delete(mdoc, n->last);
136561d06d6bSBaptiste Daroussin 	} else
136661d06d6bSBaptiste Daroussin 		post_delim(mdoc);
136761d06d6bSBaptiste Daroussin 
136861d06d6bSBaptiste Daroussin 	post_fname(mdoc);
136961d06d6bSBaptiste Daroussin }
137061d06d6bSBaptiste Daroussin 
137161d06d6bSBaptiste Daroussin static void
137261d06d6bSBaptiste Daroussin post_fa(POST_ARGS)
137361d06d6bSBaptiste Daroussin {
137461d06d6bSBaptiste Daroussin 	const struct roff_node *n;
137561d06d6bSBaptiste Daroussin 	const char *cp;
137661d06d6bSBaptiste Daroussin 
137761d06d6bSBaptiste Daroussin 	for (n = mdoc->last->child; n != NULL; n = n->next) {
137861d06d6bSBaptiste Daroussin 		for (cp = n->string; *cp != '\0'; cp++) {
137961d06d6bSBaptiste Daroussin 			/* Ignore callbacks and alterations. */
138061d06d6bSBaptiste Daroussin 			if (*cp == '(' || *cp == '{')
138161d06d6bSBaptiste Daroussin 				break;
138261d06d6bSBaptiste Daroussin 			if (*cp != ',')
138361d06d6bSBaptiste Daroussin 				continue;
13847295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_FA_COMMA, n->line,
13857295610fSBaptiste Daroussin 			    n->pos + (int)(cp - n->string), "%s", n->string);
138661d06d6bSBaptiste Daroussin 			break;
138761d06d6bSBaptiste Daroussin 		}
138861d06d6bSBaptiste Daroussin 	}
138961d06d6bSBaptiste Daroussin 	post_delim_nb(mdoc);
139061d06d6bSBaptiste Daroussin }
139161d06d6bSBaptiste Daroussin 
139261d06d6bSBaptiste Daroussin static void
139361d06d6bSBaptiste Daroussin post_nm(POST_ARGS)
139461d06d6bSBaptiste Daroussin {
139561d06d6bSBaptiste Daroussin 	struct roff_node	*n;
139661d06d6bSBaptiste Daroussin 
139761d06d6bSBaptiste Daroussin 	n = mdoc->last;
139861d06d6bSBaptiste Daroussin 
139961d06d6bSBaptiste Daroussin 	if (n->sec == SEC_NAME && n->child != NULL &&
140061d06d6bSBaptiste Daroussin 	    n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
140161d06d6bSBaptiste Daroussin 		mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
140261d06d6bSBaptiste Daroussin 
14037295610fSBaptiste Daroussin 	if (n->last != NULL && n->last->tok == MDOC_Pp)
14047295610fSBaptiste Daroussin 		roff_node_relink(mdoc, n->last);
140561d06d6bSBaptiste Daroussin 
140661d06d6bSBaptiste Daroussin 	if (mdoc->meta.name == NULL)
140761d06d6bSBaptiste Daroussin 		deroff(&mdoc->meta.name, n);
140861d06d6bSBaptiste Daroussin 
140961d06d6bSBaptiste Daroussin 	if (mdoc->meta.name == NULL ||
141061d06d6bSBaptiste Daroussin 	    (mdoc->lastsec == SEC_NAME && n->child == NULL))
14117295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
141261d06d6bSBaptiste Daroussin 
141361d06d6bSBaptiste Daroussin 	switch (n->type) {
141461d06d6bSBaptiste Daroussin 	case ROFFT_ELEM:
141561d06d6bSBaptiste Daroussin 		post_delim_nb(mdoc);
141661d06d6bSBaptiste Daroussin 		break;
141761d06d6bSBaptiste Daroussin 	case ROFFT_HEAD:
141861d06d6bSBaptiste Daroussin 		post_delim(mdoc);
141961d06d6bSBaptiste Daroussin 		break;
142061d06d6bSBaptiste Daroussin 	default:
142161d06d6bSBaptiste Daroussin 		return;
142261d06d6bSBaptiste Daroussin 	}
142361d06d6bSBaptiste Daroussin 
142461d06d6bSBaptiste Daroussin 	if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
142561d06d6bSBaptiste Daroussin 	    mdoc->meta.name == NULL)
142661d06d6bSBaptiste Daroussin 		return;
142761d06d6bSBaptiste Daroussin 
142861d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
142961d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
143061d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
143161d06d6bSBaptiste Daroussin 	mdoc->last = n;
143261d06d6bSBaptiste Daroussin }
143361d06d6bSBaptiste Daroussin 
143461d06d6bSBaptiste Daroussin static void
143561d06d6bSBaptiste Daroussin post_nd(POST_ARGS)
143661d06d6bSBaptiste Daroussin {
143761d06d6bSBaptiste Daroussin 	struct roff_node	*n;
143861d06d6bSBaptiste Daroussin 
143961d06d6bSBaptiste Daroussin 	n = mdoc->last;
144061d06d6bSBaptiste Daroussin 
144161d06d6bSBaptiste Daroussin 	if (n->type != ROFFT_BODY)
144261d06d6bSBaptiste Daroussin 		return;
144361d06d6bSBaptiste Daroussin 
144461d06d6bSBaptiste Daroussin 	if (n->sec != SEC_NAME)
14457295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
144661d06d6bSBaptiste Daroussin 
144761d06d6bSBaptiste Daroussin 	if (n->child == NULL)
14487295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
144961d06d6bSBaptiste Daroussin 	else
145061d06d6bSBaptiste Daroussin 		post_delim(mdoc);
145161d06d6bSBaptiste Daroussin 
145261d06d6bSBaptiste Daroussin 	post_hyph(mdoc);
145361d06d6bSBaptiste Daroussin }
145461d06d6bSBaptiste Daroussin 
145561d06d6bSBaptiste Daroussin static void
145661d06d6bSBaptiste Daroussin post_display(POST_ARGS)
145761d06d6bSBaptiste Daroussin {
145861d06d6bSBaptiste Daroussin 	struct roff_node *n, *np;
145961d06d6bSBaptiste Daroussin 
146061d06d6bSBaptiste Daroussin 	n = mdoc->last;
146161d06d6bSBaptiste Daroussin 	switch (n->type) {
146261d06d6bSBaptiste Daroussin 	case ROFFT_BODY:
146361d06d6bSBaptiste Daroussin 		if (n->end != ENDBODY_NOT) {
146461d06d6bSBaptiste Daroussin 			if (n->tok == MDOC_Bd &&
146561d06d6bSBaptiste Daroussin 			    n->body->parent->args == NULL)
146661d06d6bSBaptiste Daroussin 				roff_node_delete(mdoc, n);
146761d06d6bSBaptiste Daroussin 		} else if (n->child == NULL)
14687295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
14697295610fSBaptiste Daroussin 			    "%s", roff_name[n->tok]);
147061d06d6bSBaptiste Daroussin 		else if (n->tok == MDOC_D1)
147161d06d6bSBaptiste Daroussin 			post_hyph(mdoc);
147261d06d6bSBaptiste Daroussin 		break;
147361d06d6bSBaptiste Daroussin 	case ROFFT_BLOCK:
147461d06d6bSBaptiste Daroussin 		if (n->tok == MDOC_Bd) {
147561d06d6bSBaptiste Daroussin 			if (n->args == NULL) {
147661d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_BD_NOARG,
14777295610fSBaptiste Daroussin 				    n->line, n->pos, "Bd");
147861d06d6bSBaptiste Daroussin 				mdoc->next = ROFF_NEXT_SIBLING;
147961d06d6bSBaptiste Daroussin 				while (n->body->child != NULL)
14807295610fSBaptiste Daroussin 					roff_node_relink(mdoc,
148161d06d6bSBaptiste Daroussin 					    n->body->child);
148261d06d6bSBaptiste Daroussin 				roff_node_delete(mdoc, n);
148361d06d6bSBaptiste Daroussin 				break;
148461d06d6bSBaptiste Daroussin 			}
148561d06d6bSBaptiste Daroussin 			post_bd(mdoc);
148661d06d6bSBaptiste Daroussin 			post_prevpar(mdoc);
148761d06d6bSBaptiste Daroussin 		}
148861d06d6bSBaptiste Daroussin 		for (np = n->parent; np != NULL; np = np->parent) {
148961d06d6bSBaptiste Daroussin 			if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
14907295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_BD_NEST, n->line,
14917295610fSBaptiste Daroussin 				    n->pos, "%s in Bd", roff_name[n->tok]);
149261d06d6bSBaptiste Daroussin 				break;
149361d06d6bSBaptiste Daroussin 			}
149461d06d6bSBaptiste Daroussin 		}
149561d06d6bSBaptiste Daroussin 		break;
149661d06d6bSBaptiste Daroussin 	default:
149761d06d6bSBaptiste Daroussin 		break;
149861d06d6bSBaptiste Daroussin 	}
149961d06d6bSBaptiste Daroussin }
150061d06d6bSBaptiste Daroussin 
150161d06d6bSBaptiste Daroussin static void
150261d06d6bSBaptiste Daroussin post_defaults(POST_ARGS)
150361d06d6bSBaptiste Daroussin {
15046d38604fSBaptiste Daroussin 	struct roff_node *n;
150561d06d6bSBaptiste Daroussin 
15066d38604fSBaptiste Daroussin 	n = mdoc->last;
15076d38604fSBaptiste Daroussin 	if (n->child != NULL) {
150861d06d6bSBaptiste Daroussin 		post_delim_nb(mdoc);
150961d06d6bSBaptiste Daroussin 		return;
151061d06d6bSBaptiste Daroussin 	}
151161d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
15126d38604fSBaptiste Daroussin 	switch (n->tok) {
15136d38604fSBaptiste Daroussin 	case MDOC_Ar:
15146d38604fSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "file");
151561d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
15166d38604fSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "...");
151761d06d6bSBaptiste Daroussin 		break;
151861d06d6bSBaptiste Daroussin 	case MDOC_Pa:
151961d06d6bSBaptiste Daroussin 	case MDOC_Mt:
15206d38604fSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "~");
152161d06d6bSBaptiste Daroussin 		break;
152261d06d6bSBaptiste Daroussin 	default:
152361d06d6bSBaptiste Daroussin 		abort();
152461d06d6bSBaptiste Daroussin 	}
15256d38604fSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
15266d38604fSBaptiste Daroussin 	mdoc->last = n;
152761d06d6bSBaptiste Daroussin }
152861d06d6bSBaptiste Daroussin 
152961d06d6bSBaptiste Daroussin static void
153061d06d6bSBaptiste Daroussin post_at(POST_ARGS)
153161d06d6bSBaptiste Daroussin {
153261d06d6bSBaptiste Daroussin 	struct roff_node	*n, *nch;
153361d06d6bSBaptiste Daroussin 	const char		*att;
153461d06d6bSBaptiste Daroussin 
153561d06d6bSBaptiste Daroussin 	n = mdoc->last;
153661d06d6bSBaptiste Daroussin 	nch = n->child;
153761d06d6bSBaptiste Daroussin 
153861d06d6bSBaptiste Daroussin 	/*
153961d06d6bSBaptiste Daroussin 	 * If we have a child, look it up in the standard keys.  If a
154061d06d6bSBaptiste Daroussin 	 * key exist, use that instead of the child; if it doesn't,
154161d06d6bSBaptiste Daroussin 	 * prefix "AT&T UNIX " to the existing data.
154261d06d6bSBaptiste Daroussin 	 */
154361d06d6bSBaptiste Daroussin 
154461d06d6bSBaptiste Daroussin 	att = NULL;
154561d06d6bSBaptiste Daroussin 	if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
15467295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_AT_BAD,
154761d06d6bSBaptiste Daroussin 		    nch->line, nch->pos, "At %s", nch->string);
154861d06d6bSBaptiste Daroussin 
154961d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
155061d06d6bSBaptiste Daroussin 	if (att != NULL) {
155161d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, nch->line, nch->pos, att);
155261d06d6bSBaptiste Daroussin 		nch->flags |= NODE_NOPRT;
155361d06d6bSBaptiste Daroussin 	} else
155461d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
155561d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
155661d06d6bSBaptiste Daroussin 	mdoc->last = n;
155761d06d6bSBaptiste Daroussin }
155861d06d6bSBaptiste Daroussin 
155961d06d6bSBaptiste Daroussin static void
156061d06d6bSBaptiste Daroussin post_an(POST_ARGS)
156161d06d6bSBaptiste Daroussin {
156261d06d6bSBaptiste Daroussin 	struct roff_node *np, *nch;
156361d06d6bSBaptiste Daroussin 
156461d06d6bSBaptiste Daroussin 	post_an_norm(mdoc);
156561d06d6bSBaptiste Daroussin 
156661d06d6bSBaptiste Daroussin 	np = mdoc->last;
156761d06d6bSBaptiste Daroussin 	nch = np->child;
156861d06d6bSBaptiste Daroussin 	if (np->norm->An.auth == AUTH__NONE) {
156961d06d6bSBaptiste Daroussin 		if (nch == NULL)
15707295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
157161d06d6bSBaptiste Daroussin 			    np->line, np->pos, "An");
157261d06d6bSBaptiste Daroussin 		else
157361d06d6bSBaptiste Daroussin 			post_delim_nb(mdoc);
157461d06d6bSBaptiste Daroussin 	} else if (nch != NULL)
15757295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
157661d06d6bSBaptiste Daroussin 		    nch->line, nch->pos, "An ... %s", nch->string);
157761d06d6bSBaptiste Daroussin }
157861d06d6bSBaptiste Daroussin 
157961d06d6bSBaptiste Daroussin static void
15806d38604fSBaptiste Daroussin post_em(POST_ARGS)
15816d38604fSBaptiste Daroussin {
15826d38604fSBaptiste Daroussin 	post_tag(mdoc);
15836d38604fSBaptiste Daroussin 	tag_put(NULL, TAG_FALLBACK, mdoc->last);
15846d38604fSBaptiste Daroussin }
15856d38604fSBaptiste Daroussin 
15866d38604fSBaptiste Daroussin static void
158761d06d6bSBaptiste Daroussin post_en(POST_ARGS)
158861d06d6bSBaptiste Daroussin {
158961d06d6bSBaptiste Daroussin 	post_obsolete(mdoc);
159061d06d6bSBaptiste Daroussin 	if (mdoc->last->type == ROFFT_BLOCK)
159161d06d6bSBaptiste Daroussin 		mdoc->last->norm->Es = mdoc->last_es;
159261d06d6bSBaptiste Daroussin }
159361d06d6bSBaptiste Daroussin 
159461d06d6bSBaptiste Daroussin static void
15956d38604fSBaptiste Daroussin post_er(POST_ARGS)
15966d38604fSBaptiste Daroussin {
15976d38604fSBaptiste Daroussin 	struct roff_node *n;
15986d38604fSBaptiste Daroussin 
15996d38604fSBaptiste Daroussin 	n = mdoc->last;
16006d38604fSBaptiste Daroussin 	if (n->sec == SEC_ERRORS &&
16016d38604fSBaptiste Daroussin 	    (n->parent->tok == MDOC_It ||
16026d38604fSBaptiste Daroussin 	     (n->parent->tok == MDOC_Bq &&
16036d38604fSBaptiste Daroussin 	      n->parent->parent->parent->tok == MDOC_It)))
16046d38604fSBaptiste Daroussin 		tag_put(NULL, TAG_STRONG, n);
16056d38604fSBaptiste Daroussin 	post_delim_nb(mdoc);
16066d38604fSBaptiste Daroussin }
16076d38604fSBaptiste Daroussin 
16086d38604fSBaptiste Daroussin static void
16096d38604fSBaptiste Daroussin post_tag(POST_ARGS)
16106d38604fSBaptiste Daroussin {
16116d38604fSBaptiste Daroussin 	struct roff_node *n;
16126d38604fSBaptiste Daroussin 
16136d38604fSBaptiste Daroussin 	n = mdoc->last;
16146d38604fSBaptiste Daroussin 	if ((n->prev == NULL ||
16156d38604fSBaptiste Daroussin 	     (n->prev->type == ROFFT_TEXT &&
16166d38604fSBaptiste Daroussin 	      strcmp(n->prev->string, "|") == 0)) &&
16176d38604fSBaptiste Daroussin 	    (n->parent->tok == MDOC_It ||
16186d38604fSBaptiste Daroussin 	     (n->parent->tok == MDOC_Xo &&
16196d38604fSBaptiste Daroussin 	      n->parent->parent->prev == NULL &&
16206d38604fSBaptiste Daroussin 	      n->parent->parent->parent->tok == MDOC_It)))
16216d38604fSBaptiste Daroussin 		tag_put(NULL, TAG_STRONG, n);
16226d38604fSBaptiste Daroussin 	post_delim_nb(mdoc);
16236d38604fSBaptiste Daroussin }
16246d38604fSBaptiste Daroussin 
16256d38604fSBaptiste Daroussin static void
162661d06d6bSBaptiste Daroussin post_es(POST_ARGS)
162761d06d6bSBaptiste Daroussin {
162861d06d6bSBaptiste Daroussin 	post_obsolete(mdoc);
162961d06d6bSBaptiste Daroussin 	mdoc->last_es = mdoc->last;
163061d06d6bSBaptiste Daroussin }
163161d06d6bSBaptiste Daroussin 
163261d06d6bSBaptiste Daroussin static void
16336d38604fSBaptiste Daroussin post_fl(POST_ARGS)
16346d38604fSBaptiste Daroussin {
16356d38604fSBaptiste Daroussin 	struct roff_node	*n;
16366d38604fSBaptiste Daroussin 	char			*cp;
16376d38604fSBaptiste Daroussin 
16386d38604fSBaptiste Daroussin 	/*
16396d38604fSBaptiste Daroussin 	 * Transform ".Fl Fl long" to ".Fl \-long",
16406d38604fSBaptiste Daroussin 	 * resulting for example in better HTML output.
16416d38604fSBaptiste Daroussin 	 */
16426d38604fSBaptiste Daroussin 
16436d38604fSBaptiste Daroussin 	n = mdoc->last;
16446d38604fSBaptiste Daroussin 	if (n->prev != NULL && n->prev->tok == MDOC_Fl &&
16456d38604fSBaptiste Daroussin 	    n->prev->child == NULL && n->child != NULL &&
16466d38604fSBaptiste Daroussin 	    (n->flags & NODE_LINE) == 0) {
16476d38604fSBaptiste Daroussin 		mandoc_asprintf(&cp, "\\-%s", n->child->string);
16486d38604fSBaptiste Daroussin 		free(n->child->string);
16496d38604fSBaptiste Daroussin 		n->child->string = cp;
16506d38604fSBaptiste Daroussin 		roff_node_delete(mdoc, n->prev);
16516d38604fSBaptiste Daroussin 	}
16526d38604fSBaptiste Daroussin 	post_tag(mdoc);
16536d38604fSBaptiste Daroussin }
16546d38604fSBaptiste Daroussin 
16556d38604fSBaptiste Daroussin static void
165661d06d6bSBaptiste Daroussin post_xx(POST_ARGS)
165761d06d6bSBaptiste Daroussin {
165861d06d6bSBaptiste Daroussin 	struct roff_node	*n;
165961d06d6bSBaptiste Daroussin 	const char		*os;
166061d06d6bSBaptiste Daroussin 	char			*v;
166161d06d6bSBaptiste Daroussin 
166261d06d6bSBaptiste Daroussin 	post_delim_nb(mdoc);
166361d06d6bSBaptiste Daroussin 
166461d06d6bSBaptiste Daroussin 	n = mdoc->last;
166561d06d6bSBaptiste Daroussin 	switch (n->tok) {
166661d06d6bSBaptiste Daroussin 	case MDOC_Bsx:
166761d06d6bSBaptiste Daroussin 		os = "BSD/OS";
166861d06d6bSBaptiste Daroussin 		break;
166961d06d6bSBaptiste Daroussin 	case MDOC_Dx:
167061d06d6bSBaptiste Daroussin 		os = "DragonFly";
167161d06d6bSBaptiste Daroussin 		break;
167261d06d6bSBaptiste Daroussin 	case MDOC_Fx:
167361d06d6bSBaptiste Daroussin 		os = "FreeBSD";
167461d06d6bSBaptiste Daroussin 		break;
167561d06d6bSBaptiste Daroussin 	case MDOC_Nx:
167661d06d6bSBaptiste Daroussin 		os = "NetBSD";
167761d06d6bSBaptiste Daroussin 		if (n->child == NULL)
167861d06d6bSBaptiste Daroussin 			break;
167961d06d6bSBaptiste Daroussin 		v = n->child->string;
168061d06d6bSBaptiste Daroussin 		if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
168161d06d6bSBaptiste Daroussin 		    v[2] < '0' || v[2] > '9' ||
168261d06d6bSBaptiste Daroussin 		    v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
168361d06d6bSBaptiste Daroussin 			break;
168461d06d6bSBaptiste Daroussin 		n->child->flags |= NODE_NOPRT;
168561d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_CHILD;
168661d06d6bSBaptiste Daroussin 		roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
168761d06d6bSBaptiste Daroussin 		v = mdoc->last->string;
168861d06d6bSBaptiste Daroussin 		v[3] = toupper((unsigned char)v[3]);
168961d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
169061d06d6bSBaptiste Daroussin 		mdoc->last = n;
169161d06d6bSBaptiste Daroussin 		break;
169261d06d6bSBaptiste Daroussin 	case MDOC_Ox:
169361d06d6bSBaptiste Daroussin 		os = "OpenBSD";
169461d06d6bSBaptiste Daroussin 		break;
169561d06d6bSBaptiste Daroussin 	case MDOC_Ux:
169661d06d6bSBaptiste Daroussin 		os = "UNIX";
169761d06d6bSBaptiste Daroussin 		break;
169861d06d6bSBaptiste Daroussin 	default:
169961d06d6bSBaptiste Daroussin 		abort();
170061d06d6bSBaptiste Daroussin 	}
170161d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_CHILD;
170261d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, os);
170361d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
170461d06d6bSBaptiste Daroussin 	mdoc->last = n;
170561d06d6bSBaptiste Daroussin }
170661d06d6bSBaptiste Daroussin 
170761d06d6bSBaptiste Daroussin static void
170861d06d6bSBaptiste Daroussin post_it(POST_ARGS)
170961d06d6bSBaptiste Daroussin {
171061d06d6bSBaptiste Daroussin 	struct roff_node *nbl, *nit, *nch;
171161d06d6bSBaptiste Daroussin 	int		  i, cols;
171261d06d6bSBaptiste Daroussin 	enum mdoc_list	  lt;
171361d06d6bSBaptiste Daroussin 
171461d06d6bSBaptiste Daroussin 	post_prevpar(mdoc);
171561d06d6bSBaptiste Daroussin 
171661d06d6bSBaptiste Daroussin 	nit = mdoc->last;
171761d06d6bSBaptiste Daroussin 	if (nit->type != ROFFT_BLOCK)
171861d06d6bSBaptiste Daroussin 		return;
171961d06d6bSBaptiste Daroussin 
172061d06d6bSBaptiste Daroussin 	nbl = nit->parent->parent;
172161d06d6bSBaptiste Daroussin 	lt = nbl->norm->Bl.type;
172261d06d6bSBaptiste Daroussin 
172361d06d6bSBaptiste Daroussin 	switch (lt) {
172461d06d6bSBaptiste Daroussin 	case LIST_tag:
172561d06d6bSBaptiste Daroussin 	case LIST_hang:
172661d06d6bSBaptiste Daroussin 	case LIST_ohang:
172761d06d6bSBaptiste Daroussin 	case LIST_inset:
172861d06d6bSBaptiste Daroussin 	case LIST_diag:
172961d06d6bSBaptiste Daroussin 		if (nit->head->child == NULL)
17307295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_IT_NOHEAD,
17317295610fSBaptiste Daroussin 			    nit->line, nit->pos, "Bl -%s It",
173261d06d6bSBaptiste Daroussin 			    mdoc_argnames[nbl->args->argv[0].arg]);
173361d06d6bSBaptiste Daroussin 		break;
173461d06d6bSBaptiste Daroussin 	case LIST_bullet:
173561d06d6bSBaptiste Daroussin 	case LIST_dash:
173661d06d6bSBaptiste Daroussin 	case LIST_enum:
173761d06d6bSBaptiste Daroussin 	case LIST_hyphen:
173861d06d6bSBaptiste Daroussin 		if (nit->body == NULL || nit->body->child == NULL)
17397295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_IT_NOBODY,
17407295610fSBaptiste Daroussin 			    nit->line, nit->pos, "Bl -%s It",
174161d06d6bSBaptiste Daroussin 			    mdoc_argnames[nbl->args->argv[0].arg]);
174261d06d6bSBaptiste Daroussin 		/* FALLTHROUGH */
174361d06d6bSBaptiste Daroussin 	case LIST_item:
174461d06d6bSBaptiste Daroussin 		if ((nch = nit->head->child) != NULL)
17457295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ARG_SKIP,
174661d06d6bSBaptiste Daroussin 			    nit->line, nit->pos, "It %s",
17476d38604fSBaptiste Daroussin 			    nch->type == ROFFT_TEXT ? nch->string :
17486d38604fSBaptiste Daroussin 			    roff_name[nch->tok]);
174961d06d6bSBaptiste Daroussin 		break;
175061d06d6bSBaptiste Daroussin 	case LIST_column:
175161d06d6bSBaptiste Daroussin 		cols = (int)nbl->norm->Bl.ncols;
175261d06d6bSBaptiste Daroussin 
175361d06d6bSBaptiste Daroussin 		assert(nit->head->child == NULL);
175461d06d6bSBaptiste Daroussin 
175561d06d6bSBaptiste Daroussin 		if (nit->head->next->child == NULL &&
175661d06d6bSBaptiste Daroussin 		    nit->head->next->next == NULL) {
17577295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_MACRO_EMPTY,
175861d06d6bSBaptiste Daroussin 			    nit->line, nit->pos, "It");
175961d06d6bSBaptiste Daroussin 			roff_node_delete(mdoc, nit);
176061d06d6bSBaptiste Daroussin 			break;
176161d06d6bSBaptiste Daroussin 		}
176261d06d6bSBaptiste Daroussin 
176361d06d6bSBaptiste Daroussin 		i = 0;
176461d06d6bSBaptiste Daroussin 		for (nch = nit->child; nch != NULL; nch = nch->next) {
176561d06d6bSBaptiste Daroussin 			if (nch->type != ROFFT_BODY)
176661d06d6bSBaptiste Daroussin 				continue;
176761d06d6bSBaptiste Daroussin 			if (i++ && nch->flags & NODE_LINE)
17687295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TA_LINE,
176961d06d6bSBaptiste Daroussin 				    nch->line, nch->pos, "Ta");
177061d06d6bSBaptiste Daroussin 		}
177161d06d6bSBaptiste Daroussin 		if (i < cols || i > cols + 1)
17727295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
177361d06d6bSBaptiste Daroussin 			    "%d columns, %d cells", cols, i);
177461d06d6bSBaptiste Daroussin 		else if (nit->head->next->child != NULL &&
17757295610fSBaptiste Daroussin 		    nit->head->next->child->flags & NODE_LINE)
17767295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_IT_NOARG,
177761d06d6bSBaptiste Daroussin 			    nit->line, nit->pos, "Bl -column It");
177861d06d6bSBaptiste Daroussin 		break;
177961d06d6bSBaptiste Daroussin 	default:
178061d06d6bSBaptiste Daroussin 		abort();
178161d06d6bSBaptiste Daroussin 	}
178261d06d6bSBaptiste Daroussin }
178361d06d6bSBaptiste Daroussin 
178461d06d6bSBaptiste Daroussin static void
178561d06d6bSBaptiste Daroussin post_bl_block(POST_ARGS)
178661d06d6bSBaptiste Daroussin {
178761d06d6bSBaptiste Daroussin 	struct roff_node *n, *ni, *nc;
178861d06d6bSBaptiste Daroussin 
178961d06d6bSBaptiste Daroussin 	post_prevpar(mdoc);
179061d06d6bSBaptiste Daroussin 
179161d06d6bSBaptiste Daroussin 	n = mdoc->last;
179261d06d6bSBaptiste Daroussin 	for (ni = n->body->child; ni != NULL; ni = ni->next) {
179361d06d6bSBaptiste Daroussin 		if (ni->body == NULL)
179461d06d6bSBaptiste Daroussin 			continue;
179561d06d6bSBaptiste Daroussin 		nc = ni->body->last;
179661d06d6bSBaptiste Daroussin 		while (nc != NULL) {
179761d06d6bSBaptiste Daroussin 			switch (nc->tok) {
179861d06d6bSBaptiste Daroussin 			case MDOC_Pp:
179961d06d6bSBaptiste Daroussin 			case ROFF_br:
180061d06d6bSBaptiste Daroussin 				break;
180161d06d6bSBaptiste Daroussin 			default:
180261d06d6bSBaptiste Daroussin 				nc = NULL;
180361d06d6bSBaptiste Daroussin 				continue;
180461d06d6bSBaptiste Daroussin 			}
180561d06d6bSBaptiste Daroussin 			if (ni->next == NULL) {
18067295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
18077295610fSBaptiste Daroussin 				    nc->pos, "%s", roff_name[nc->tok]);
18087295610fSBaptiste Daroussin 				roff_node_relink(mdoc, nc);
180961d06d6bSBaptiste Daroussin 			} else if (n->norm->Bl.comp == 0 &&
181061d06d6bSBaptiste Daroussin 			    n->norm->Bl.type != LIST_column) {
18117295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_PAR_SKIP,
18127295610fSBaptiste Daroussin 				    nc->line, nc->pos,
181361d06d6bSBaptiste Daroussin 				    "%s before It", roff_name[nc->tok]);
181461d06d6bSBaptiste Daroussin 				roff_node_delete(mdoc, nc);
181561d06d6bSBaptiste Daroussin 			} else
181661d06d6bSBaptiste Daroussin 				break;
181761d06d6bSBaptiste Daroussin 			nc = ni->body->last;
181861d06d6bSBaptiste Daroussin 		}
181961d06d6bSBaptiste Daroussin 	}
182061d06d6bSBaptiste Daroussin }
182161d06d6bSBaptiste Daroussin 
182261d06d6bSBaptiste Daroussin /*
18239f6a619aSEric van Gyzen  * If "in" begins with a dot, a word, and whitespace, return a dynamically
18249f6a619aSEric van Gyzen  * allocated copy of "in" that skips all of those.  Otherwise, return NULL.
18259f6a619aSEric van Gyzen  *
18269f6a619aSEric van Gyzen  * This is a partial workaround for the TODO list item beginning with:
18279f6a619aSEric van Gyzen  * - When the -width string contains macros, the macros must be rendered
18289f6a619aSEric van Gyzen  */
18299f6a619aSEric van Gyzen static char *
18309f6a619aSEric van Gyzen skip_leading_dot_word(const char *in)
18319f6a619aSEric van Gyzen {
18329f6a619aSEric van Gyzen 	const char *iter = in;
18339f6a619aSEric van Gyzen 	const char *space;
18349f6a619aSEric van Gyzen 
18359f6a619aSEric van Gyzen 	if (*iter != '.')
18369f6a619aSEric van Gyzen 		return NULL;
18379f6a619aSEric van Gyzen 	iter++;
18389f6a619aSEric van Gyzen 
18399f6a619aSEric van Gyzen 	while (*iter != '\0' && !isspace(*iter))
18409f6a619aSEric van Gyzen 		iter++;
18419f6a619aSEric van Gyzen 	/*
18429f6a619aSEric van Gyzen 	 * If the dot was followed by space or NUL,
18439f6a619aSEric van Gyzen 	 * do not skip anything.
18449f6a619aSEric van Gyzen 	 */
18459f6a619aSEric van Gyzen 	if (iter == in + 1)
18469f6a619aSEric van Gyzen 		return NULL;
18479f6a619aSEric van Gyzen 
18489f6a619aSEric van Gyzen 	space = iter;
18499f6a619aSEric van Gyzen 	while (isspace(*iter))
18509f6a619aSEric van Gyzen 		iter++;
18519f6a619aSEric van Gyzen 	/*
18529f6a619aSEric van Gyzen 	 * If the word was not followed by space,
18539f6a619aSEric van Gyzen 	 * do not skip anything.
18549f6a619aSEric van Gyzen 	 */
18559f6a619aSEric van Gyzen 	if (iter == space)
18569f6a619aSEric van Gyzen 		return NULL;
18579f6a619aSEric van Gyzen 
18589f6a619aSEric van Gyzen 	return strdup(iter);
18599f6a619aSEric van Gyzen }
18609f6a619aSEric van Gyzen 
18619f6a619aSEric van Gyzen /*
186261d06d6bSBaptiste Daroussin  * If the argument of -offset or -width is a macro,
186361d06d6bSBaptiste Daroussin  * replace it with the associated default width.
186461d06d6bSBaptiste Daroussin  */
186561d06d6bSBaptiste Daroussin static void
186661d06d6bSBaptiste Daroussin rewrite_macro2len(struct roff_man *mdoc, char **arg)
186761d06d6bSBaptiste Daroussin {
186861d06d6bSBaptiste Daroussin 	size_t		  width;
186961d06d6bSBaptiste Daroussin 	enum roff_tok	  tok;
18709f6a619aSEric van Gyzen 	char		 *newarg;
187161d06d6bSBaptiste Daroussin 
18729f6a619aSEric van Gyzen 	newarg = NULL;
187361d06d6bSBaptiste Daroussin 	if (*arg == NULL)
187461d06d6bSBaptiste Daroussin 		return;
187561d06d6bSBaptiste Daroussin 	else if ( ! strcmp(*arg, "Ds"))
187661d06d6bSBaptiste Daroussin 		width = 6;
18779f6a619aSEric van Gyzen 	else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) != TOKEN_NONE)
187861d06d6bSBaptiste Daroussin 		width = macro2len(tok);
18799f6a619aSEric van Gyzen 	else if ((newarg = skip_leading_dot_word(*arg)) == NULL)
18809f6a619aSEric van Gyzen 		return;
188161d06d6bSBaptiste Daroussin 
188261d06d6bSBaptiste Daroussin 	free(*arg);
18839f6a619aSEric van Gyzen 	if (newarg != NULL)
18849f6a619aSEric van Gyzen 		*arg = newarg;
18859f6a619aSEric van Gyzen 	else
188661d06d6bSBaptiste Daroussin 		mandoc_asprintf(arg, "%zun", width);
188761d06d6bSBaptiste Daroussin }
188861d06d6bSBaptiste Daroussin 
188961d06d6bSBaptiste Daroussin static void
189061d06d6bSBaptiste Daroussin post_bl_head(POST_ARGS)
189161d06d6bSBaptiste Daroussin {
189261d06d6bSBaptiste Daroussin 	struct roff_node *nbl, *nh, *nch, *nnext;
189361d06d6bSBaptiste Daroussin 	struct mdoc_argv *argv;
189461d06d6bSBaptiste Daroussin 	int		  i, j;
189561d06d6bSBaptiste Daroussin 
189661d06d6bSBaptiste Daroussin 	post_bl_norm(mdoc);
189761d06d6bSBaptiste Daroussin 
189861d06d6bSBaptiste Daroussin 	nh = mdoc->last;
189961d06d6bSBaptiste Daroussin 	if (nh->norm->Bl.type != LIST_column) {
190061d06d6bSBaptiste Daroussin 		if ((nch = nh->child) == NULL)
190161d06d6bSBaptiste Daroussin 			return;
19027295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
190361d06d6bSBaptiste Daroussin 		    nch->line, nch->pos, "Bl ... %s", nch->string);
190461d06d6bSBaptiste Daroussin 		while (nch != NULL) {
190561d06d6bSBaptiste Daroussin 			roff_node_delete(mdoc, nch);
190661d06d6bSBaptiste Daroussin 			nch = nh->child;
190761d06d6bSBaptiste Daroussin 		}
190861d06d6bSBaptiste Daroussin 		return;
190961d06d6bSBaptiste Daroussin 	}
191061d06d6bSBaptiste Daroussin 
191161d06d6bSBaptiste Daroussin 	/*
191261d06d6bSBaptiste Daroussin 	 * Append old-style lists, where the column width specifiers
191361d06d6bSBaptiste Daroussin 	 * trail as macro parameters, to the new-style ("normal-form")
191461d06d6bSBaptiste Daroussin 	 * lists where they're argument values following -column.
191561d06d6bSBaptiste Daroussin 	 */
191661d06d6bSBaptiste Daroussin 
191761d06d6bSBaptiste Daroussin 	if (nh->child == NULL)
191861d06d6bSBaptiste Daroussin 		return;
191961d06d6bSBaptiste Daroussin 
192061d06d6bSBaptiste Daroussin 	nbl = nh->parent;
192161d06d6bSBaptiste Daroussin 	for (j = 0; j < (int)nbl->args->argc; j++)
192261d06d6bSBaptiste Daroussin 		if (nbl->args->argv[j].arg == MDOC_Column)
192361d06d6bSBaptiste Daroussin 			break;
192461d06d6bSBaptiste Daroussin 
192561d06d6bSBaptiste Daroussin 	assert(j < (int)nbl->args->argc);
192661d06d6bSBaptiste Daroussin 
192761d06d6bSBaptiste Daroussin 	/*
192861d06d6bSBaptiste Daroussin 	 * Accommodate for new-style groff column syntax.  Shuffle the
192961d06d6bSBaptiste Daroussin 	 * child nodes, all of which must be TEXT, as arguments for the
193061d06d6bSBaptiste Daroussin 	 * column field.  Then, delete the head children.
193161d06d6bSBaptiste Daroussin 	 */
193261d06d6bSBaptiste Daroussin 
193361d06d6bSBaptiste Daroussin 	argv = nbl->args->argv + j;
193461d06d6bSBaptiste Daroussin 	i = argv->sz;
193561d06d6bSBaptiste Daroussin 	for (nch = nh->child; nch != NULL; nch = nch->next)
193661d06d6bSBaptiste Daroussin 		argv->sz++;
193761d06d6bSBaptiste Daroussin 	argv->value = mandoc_reallocarray(argv->value,
193861d06d6bSBaptiste Daroussin 	    argv->sz, sizeof(char *));
193961d06d6bSBaptiste Daroussin 
194061d06d6bSBaptiste Daroussin 	nh->norm->Bl.ncols = argv->sz;
194161d06d6bSBaptiste Daroussin 	nh->norm->Bl.cols = (void *)argv->value;
194261d06d6bSBaptiste Daroussin 
194361d06d6bSBaptiste Daroussin 	for (nch = nh->child; nch != NULL; nch = nnext) {
194461d06d6bSBaptiste Daroussin 		argv->value[i++] = nch->string;
194561d06d6bSBaptiste Daroussin 		nch->string = NULL;
194661d06d6bSBaptiste Daroussin 		nnext = nch->next;
194761d06d6bSBaptiste Daroussin 		roff_node_delete(NULL, nch);
194861d06d6bSBaptiste Daroussin 	}
194961d06d6bSBaptiste Daroussin 	nh->child = NULL;
195061d06d6bSBaptiste Daroussin }
195161d06d6bSBaptiste Daroussin 
195261d06d6bSBaptiste Daroussin static void
195361d06d6bSBaptiste Daroussin post_bl(POST_ARGS)
195461d06d6bSBaptiste Daroussin {
19556d38604fSBaptiste Daroussin 	struct roff_node	*nbody;           /* of the Bl */
195661d06d6bSBaptiste Daroussin 	struct roff_node	*nchild, *nnext;  /* of the Bl body */
195761d06d6bSBaptiste Daroussin 	const char		*prev_Er;
195861d06d6bSBaptiste Daroussin 	int			 order;
195961d06d6bSBaptiste Daroussin 
196061d06d6bSBaptiste Daroussin 	nbody = mdoc->last;
196161d06d6bSBaptiste Daroussin 	switch (nbody->type) {
196261d06d6bSBaptiste Daroussin 	case ROFFT_BLOCK:
196361d06d6bSBaptiste Daroussin 		post_bl_block(mdoc);
196461d06d6bSBaptiste Daroussin 		return;
196561d06d6bSBaptiste Daroussin 	case ROFFT_HEAD:
196661d06d6bSBaptiste Daroussin 		post_bl_head(mdoc);
196761d06d6bSBaptiste Daroussin 		return;
196861d06d6bSBaptiste Daroussin 	case ROFFT_BODY:
196961d06d6bSBaptiste Daroussin 		break;
197061d06d6bSBaptiste Daroussin 	default:
197161d06d6bSBaptiste Daroussin 		return;
197261d06d6bSBaptiste Daroussin 	}
197361d06d6bSBaptiste Daroussin 	if (nbody->end != ENDBODY_NOT)
197461d06d6bSBaptiste Daroussin 		return;
197561d06d6bSBaptiste Daroussin 
19766d38604fSBaptiste Daroussin 	/*
19776d38604fSBaptiste Daroussin 	 * Up to the first item, move nodes before the list,
19786d38604fSBaptiste Daroussin 	 * but leave transparent nodes where they are
19796d38604fSBaptiste Daroussin 	 * if they precede an item.
19806d38604fSBaptiste Daroussin 	 * The next non-transparent node is kept in nchild.
19816d38604fSBaptiste Daroussin 	 * It only needs to be updated after a non-transparent
19826d38604fSBaptiste Daroussin 	 * node was moved out, and at the very beginning
19836d38604fSBaptiste Daroussin 	 * when no node at all was moved yet.
19846d38604fSBaptiste Daroussin 	 */
19856d38604fSBaptiste Daroussin 
19866d38604fSBaptiste Daroussin 	nchild = mdoc->last;
19876d38604fSBaptiste Daroussin 	for (;;) {
19886d38604fSBaptiste Daroussin 		if (nchild == mdoc->last)
19896d38604fSBaptiste Daroussin 			nchild = roff_node_child(nbody);
199061d06d6bSBaptiste Daroussin 		if (nchild == NULL) {
19916d38604fSBaptiste Daroussin 			mdoc->last = nbody;
19927295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BLK_EMPTY,
199361d06d6bSBaptiste Daroussin 			    nbody->line, nbody->pos, "Bl");
199461d06d6bSBaptiste Daroussin 			return;
199561d06d6bSBaptiste Daroussin 		}
19966d38604fSBaptiste Daroussin 		if (nchild->tok == MDOC_It) {
19976d38604fSBaptiste Daroussin 			mdoc->last = nbody;
19986d38604fSBaptiste Daroussin 			break;
19996d38604fSBaptiste Daroussin 		}
20006d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line,
20016d38604fSBaptiste Daroussin 		    nbody->child->pos, "%s", roff_name[nbody->child->tok]);
20026d38604fSBaptiste Daroussin 		if (nbody->parent->prev == NULL) {
20036d38604fSBaptiste Daroussin 			mdoc->last = nbody->parent->parent;
20046d38604fSBaptiste Daroussin 			mdoc->next = ROFF_NEXT_CHILD;
20056d38604fSBaptiste Daroussin 		} else {
20066d38604fSBaptiste Daroussin 			mdoc->last = nbody->parent->prev;
20076d38604fSBaptiste Daroussin 			mdoc->next = ROFF_NEXT_SIBLING;
20086d38604fSBaptiste Daroussin 		}
20096d38604fSBaptiste Daroussin 		roff_node_relink(mdoc, nbody->child);
201061d06d6bSBaptiste Daroussin 	}
201161d06d6bSBaptiste Daroussin 
201261d06d6bSBaptiste Daroussin 	/*
20136d38604fSBaptiste Daroussin 	 * We have reached the first item,
20146d38604fSBaptiste Daroussin 	 * so moving nodes out is no longer possible.
20156d38604fSBaptiste Daroussin 	 * But in .Bl -column, the first rows may be implicit,
201661d06d6bSBaptiste Daroussin 	 * that is, they may not start with .It macros.
201761d06d6bSBaptiste Daroussin 	 * Such rows may be followed by nodes generated on the
20186d38604fSBaptiste Daroussin 	 * roff level, for example .TS.
20196d38604fSBaptiste Daroussin 	 * Wrap such roff nodes into an implicit row.
202061d06d6bSBaptiste Daroussin 	 */
202161d06d6bSBaptiste Daroussin 
20226d38604fSBaptiste Daroussin 	while (nchild != NULL) {
20236d38604fSBaptiste Daroussin 		if (nchild->tok == MDOC_It) {
20246d38604fSBaptiste Daroussin 			nchild = roff_node_next(nchild);
20256d38604fSBaptiste Daroussin 			continue;
20266d38604fSBaptiste Daroussin 		}
20276d38604fSBaptiste Daroussin 		nnext = nchild->next;
20286d38604fSBaptiste Daroussin 		mdoc->last = nchild->prev;
202961d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
20306d38604fSBaptiste Daroussin 		roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
20316d38604fSBaptiste Daroussin 		roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
203261d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
20336d38604fSBaptiste Daroussin 		roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
203461d06d6bSBaptiste Daroussin 		while (nchild->tok != MDOC_It) {
20357295610fSBaptiste Daroussin 			roff_node_relink(mdoc, nchild);
20366d38604fSBaptiste Daroussin 			if (nnext == NULL)
203761d06d6bSBaptiste Daroussin 				break;
20386d38604fSBaptiste Daroussin 			nchild = nnext;
203961d06d6bSBaptiste Daroussin 			nnext = nchild->next;
204061d06d6bSBaptiste Daroussin 			mdoc->next = ROFF_NEXT_SIBLING;
204161d06d6bSBaptiste Daroussin 		}
204261d06d6bSBaptiste Daroussin 		mdoc->last = nbody;
204361d06d6bSBaptiste Daroussin 	}
204461d06d6bSBaptiste Daroussin 
204561d06d6bSBaptiste Daroussin 	if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
204661d06d6bSBaptiste Daroussin 		return;
204761d06d6bSBaptiste Daroussin 
204861d06d6bSBaptiste Daroussin 	prev_Er = NULL;
204961d06d6bSBaptiste Daroussin 	for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
205061d06d6bSBaptiste Daroussin 		if (nchild->tok != MDOC_It)
205161d06d6bSBaptiste Daroussin 			continue;
205261d06d6bSBaptiste Daroussin 		if ((nnext = nchild->head->child) == NULL)
205361d06d6bSBaptiste Daroussin 			continue;
205461d06d6bSBaptiste Daroussin 		if (nnext->type == ROFFT_BLOCK)
205561d06d6bSBaptiste Daroussin 			nnext = nnext->body->child;
205661d06d6bSBaptiste Daroussin 		if (nnext == NULL || nnext->tok != MDOC_Er)
205761d06d6bSBaptiste Daroussin 			continue;
205861d06d6bSBaptiste Daroussin 		nnext = nnext->child;
205961d06d6bSBaptiste Daroussin 		if (prev_Er != NULL) {
206061d06d6bSBaptiste Daroussin 			order = strcmp(prev_Er, nnext->string);
206161d06d6bSBaptiste Daroussin 			if (order > 0)
20627295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ER_ORDER,
20637295610fSBaptiste Daroussin 				    nnext->line, nnext->pos,
206461d06d6bSBaptiste Daroussin 				    "Er %s %s (NetBSD)",
206561d06d6bSBaptiste Daroussin 				    prev_Er, nnext->string);
206661d06d6bSBaptiste Daroussin 			else if (order == 0)
20677295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ER_REP,
20687295610fSBaptiste Daroussin 				    nnext->line, nnext->pos,
206961d06d6bSBaptiste Daroussin 				    "Er %s (NetBSD)", prev_Er);
207061d06d6bSBaptiste Daroussin 		}
207161d06d6bSBaptiste Daroussin 		prev_Er = nnext->string;
207261d06d6bSBaptiste Daroussin 	}
207361d06d6bSBaptiste Daroussin }
207461d06d6bSBaptiste Daroussin 
207561d06d6bSBaptiste Daroussin static void
207661d06d6bSBaptiste Daroussin post_bk(POST_ARGS)
207761d06d6bSBaptiste Daroussin {
207861d06d6bSBaptiste Daroussin 	struct roff_node	*n;
207961d06d6bSBaptiste Daroussin 
208061d06d6bSBaptiste Daroussin 	n = mdoc->last;
208161d06d6bSBaptiste Daroussin 
208261d06d6bSBaptiste Daroussin 	if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
20837295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
208461d06d6bSBaptiste Daroussin 		roff_node_delete(mdoc, n);
208561d06d6bSBaptiste Daroussin 	}
208661d06d6bSBaptiste Daroussin }
208761d06d6bSBaptiste Daroussin 
208861d06d6bSBaptiste Daroussin static void
208961d06d6bSBaptiste Daroussin post_sm(POST_ARGS)
209061d06d6bSBaptiste Daroussin {
209161d06d6bSBaptiste Daroussin 	struct roff_node	*nch;
209261d06d6bSBaptiste Daroussin 
209361d06d6bSBaptiste Daroussin 	nch = mdoc->last->child;
209461d06d6bSBaptiste Daroussin 
209561d06d6bSBaptiste Daroussin 	if (nch == NULL) {
209661d06d6bSBaptiste Daroussin 		mdoc->flags ^= MDOC_SMOFF;
209761d06d6bSBaptiste Daroussin 		return;
209861d06d6bSBaptiste Daroussin 	}
209961d06d6bSBaptiste Daroussin 
210061d06d6bSBaptiste Daroussin 	assert(nch->type == ROFFT_TEXT);
210161d06d6bSBaptiste Daroussin 
210261d06d6bSBaptiste Daroussin 	if ( ! strcmp(nch->string, "on")) {
210361d06d6bSBaptiste Daroussin 		mdoc->flags &= ~MDOC_SMOFF;
210461d06d6bSBaptiste Daroussin 		return;
210561d06d6bSBaptiste Daroussin 	}
210661d06d6bSBaptiste Daroussin 	if ( ! strcmp(nch->string, "off")) {
210761d06d6bSBaptiste Daroussin 		mdoc->flags |= MDOC_SMOFF;
210861d06d6bSBaptiste Daroussin 		return;
210961d06d6bSBaptiste Daroussin 	}
211061d06d6bSBaptiste Daroussin 
21117295610fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
211261d06d6bSBaptiste Daroussin 	    "%s %s", roff_name[mdoc->last->tok], nch->string);
21137295610fSBaptiste Daroussin 	roff_node_relink(mdoc, nch);
211461d06d6bSBaptiste Daroussin 	return;
211561d06d6bSBaptiste Daroussin }
211661d06d6bSBaptiste Daroussin 
211761d06d6bSBaptiste Daroussin static void
211861d06d6bSBaptiste Daroussin post_root(POST_ARGS)
211961d06d6bSBaptiste Daroussin {
212061d06d6bSBaptiste Daroussin 	struct roff_node *n;
212161d06d6bSBaptiste Daroussin 
212261d06d6bSBaptiste Daroussin 	/* Add missing prologue data. */
212361d06d6bSBaptiste Daroussin 
212461d06d6bSBaptiste Daroussin 	if (mdoc->meta.date == NULL)
21256d38604fSBaptiste Daroussin 		mdoc->meta.date = mandoc_normdate(NULL, NULL);
212661d06d6bSBaptiste Daroussin 
212761d06d6bSBaptiste Daroussin 	if (mdoc->meta.title == NULL) {
21287295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
212961d06d6bSBaptiste Daroussin 		mdoc->meta.title = mandoc_strdup("UNTITLED");
213061d06d6bSBaptiste Daroussin 	}
213161d06d6bSBaptiste Daroussin 
213261d06d6bSBaptiste Daroussin 	if (mdoc->meta.vol == NULL)
213361d06d6bSBaptiste Daroussin 		mdoc->meta.vol = mandoc_strdup("LOCAL");
213461d06d6bSBaptiste Daroussin 
213561d06d6bSBaptiste Daroussin 	if (mdoc->meta.os == NULL) {
21367295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
213761d06d6bSBaptiste Daroussin 		mdoc->meta.os = mandoc_strdup("");
213861d06d6bSBaptiste Daroussin 	} else if (mdoc->meta.os_e &&
213961d06d6bSBaptiste Daroussin 	    (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
21407295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
214161d06d6bSBaptiste Daroussin 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
214261d06d6bSBaptiste Daroussin 		    "(OpenBSD)" : "(NetBSD)");
214361d06d6bSBaptiste Daroussin 
214461d06d6bSBaptiste Daroussin 	if (mdoc->meta.arch != NULL &&
21457295610fSBaptiste Daroussin 	    arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
21467295610fSBaptiste Daroussin 		n = mdoc->meta.first->child;
214761d06d6bSBaptiste Daroussin 		while (n->tok != MDOC_Dt ||
214861d06d6bSBaptiste Daroussin 		    n->child == NULL ||
214961d06d6bSBaptiste Daroussin 		    n->child->next == NULL ||
215061d06d6bSBaptiste Daroussin 		    n->child->next->next == NULL)
215161d06d6bSBaptiste Daroussin 			n = n->next;
215261d06d6bSBaptiste Daroussin 		n = n->child->next->next;
21537295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
215461d06d6bSBaptiste Daroussin 		    "Dt ... %s %s", mdoc->meta.arch,
215561d06d6bSBaptiste Daroussin 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
215661d06d6bSBaptiste Daroussin 		    "(OpenBSD)" : "(NetBSD)");
215761d06d6bSBaptiste Daroussin 	}
215861d06d6bSBaptiste Daroussin 
215961d06d6bSBaptiste Daroussin 	/* Check that we begin with a proper `Sh'. */
216061d06d6bSBaptiste Daroussin 
21617295610fSBaptiste Daroussin 	n = mdoc->meta.first->child;
216261d06d6bSBaptiste Daroussin 	while (n != NULL &&
216361d06d6bSBaptiste Daroussin 	    (n->type == ROFFT_COMMENT ||
216461d06d6bSBaptiste Daroussin 	     (n->tok >= MDOC_Dd &&
21657295610fSBaptiste Daroussin 	      mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
216661d06d6bSBaptiste Daroussin 		n = n->next;
216761d06d6bSBaptiste Daroussin 
216861d06d6bSBaptiste Daroussin 	if (n == NULL)
21697295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
217061d06d6bSBaptiste Daroussin 	else if (n->tok != MDOC_Sh)
21717295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
21727295610fSBaptiste Daroussin 		    "%s", roff_name[n->tok]);
217361d06d6bSBaptiste Daroussin }
217461d06d6bSBaptiste Daroussin 
217561d06d6bSBaptiste Daroussin static void
217661d06d6bSBaptiste Daroussin post_rs(POST_ARGS)
217761d06d6bSBaptiste Daroussin {
217861d06d6bSBaptiste Daroussin 	struct roff_node *np, *nch, *next, *prev;
217961d06d6bSBaptiste Daroussin 	int		  i, j;
218061d06d6bSBaptiste Daroussin 
218161d06d6bSBaptiste Daroussin 	np = mdoc->last;
218261d06d6bSBaptiste Daroussin 
218361d06d6bSBaptiste Daroussin 	if (np->type != ROFFT_BODY)
218461d06d6bSBaptiste Daroussin 		return;
218561d06d6bSBaptiste Daroussin 
218661d06d6bSBaptiste Daroussin 	if (np->child == NULL) {
21877295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
218861d06d6bSBaptiste Daroussin 		return;
218961d06d6bSBaptiste Daroussin 	}
219061d06d6bSBaptiste Daroussin 
219161d06d6bSBaptiste Daroussin 	/*
219261d06d6bSBaptiste Daroussin 	 * The full `Rs' block needs special handling to order the
219361d06d6bSBaptiste Daroussin 	 * sub-elements according to `rsord'.  Pick through each element
219461d06d6bSBaptiste Daroussin 	 * and correctly order it.  This is an insertion sort.
219561d06d6bSBaptiste Daroussin 	 */
219661d06d6bSBaptiste Daroussin 
219761d06d6bSBaptiste Daroussin 	next = NULL;
219861d06d6bSBaptiste Daroussin 	for (nch = np->child->next; nch != NULL; nch = next) {
219961d06d6bSBaptiste Daroussin 		/* Determine order number of this child. */
220061d06d6bSBaptiste Daroussin 		for (i = 0; i < RSORD_MAX; i++)
220161d06d6bSBaptiste Daroussin 			if (rsord[i] == nch->tok)
220261d06d6bSBaptiste Daroussin 				break;
220361d06d6bSBaptiste Daroussin 
220461d06d6bSBaptiste Daroussin 		if (i == RSORD_MAX) {
22057295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
22067295610fSBaptiste Daroussin 			    "%s", roff_name[nch->tok]);
220761d06d6bSBaptiste Daroussin 			i = -1;
220861d06d6bSBaptiste Daroussin 		} else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
220961d06d6bSBaptiste Daroussin 			np->norm->Rs.quote_T++;
221061d06d6bSBaptiste Daroussin 
221161d06d6bSBaptiste Daroussin 		/*
221261d06d6bSBaptiste Daroussin 		 * Remove this child from the chain.  This somewhat
221361d06d6bSBaptiste Daroussin 		 * repeats roff_node_unlink(), but since we're
221461d06d6bSBaptiste Daroussin 		 * just re-ordering, there's no need for the
221561d06d6bSBaptiste Daroussin 		 * full unlink process.
221661d06d6bSBaptiste Daroussin 		 */
221761d06d6bSBaptiste Daroussin 
221861d06d6bSBaptiste Daroussin 		if ((next = nch->next) != NULL)
221961d06d6bSBaptiste Daroussin 			next->prev = nch->prev;
222061d06d6bSBaptiste Daroussin 
222161d06d6bSBaptiste Daroussin 		if ((prev = nch->prev) != NULL)
222261d06d6bSBaptiste Daroussin 			prev->next = nch->next;
222361d06d6bSBaptiste Daroussin 
222461d06d6bSBaptiste Daroussin 		nch->prev = nch->next = NULL;
222561d06d6bSBaptiste Daroussin 
222661d06d6bSBaptiste Daroussin 		/*
222761d06d6bSBaptiste Daroussin 		 * Scan back until we reach a node that's
222861d06d6bSBaptiste Daroussin 		 * to be ordered before this child.
222961d06d6bSBaptiste Daroussin 		 */
223061d06d6bSBaptiste Daroussin 
223161d06d6bSBaptiste Daroussin 		for ( ; prev ; prev = prev->prev) {
223261d06d6bSBaptiste Daroussin 			/* Determine order of `prev'. */
223361d06d6bSBaptiste Daroussin 			for (j = 0; j < RSORD_MAX; j++)
223461d06d6bSBaptiste Daroussin 				if (rsord[j] == prev->tok)
223561d06d6bSBaptiste Daroussin 					break;
223661d06d6bSBaptiste Daroussin 			if (j == RSORD_MAX)
223761d06d6bSBaptiste Daroussin 				j = -1;
223861d06d6bSBaptiste Daroussin 
223961d06d6bSBaptiste Daroussin 			if (j <= i)
224061d06d6bSBaptiste Daroussin 				break;
224161d06d6bSBaptiste Daroussin 		}
224261d06d6bSBaptiste Daroussin 
224361d06d6bSBaptiste Daroussin 		/*
224461d06d6bSBaptiste Daroussin 		 * Set this child back into its correct place
224561d06d6bSBaptiste Daroussin 		 * in front of the `prev' node.
224661d06d6bSBaptiste Daroussin 		 */
224761d06d6bSBaptiste Daroussin 
224861d06d6bSBaptiste Daroussin 		nch->prev = prev;
224961d06d6bSBaptiste Daroussin 
225061d06d6bSBaptiste Daroussin 		if (prev == NULL) {
225161d06d6bSBaptiste Daroussin 			np->child->prev = nch;
225261d06d6bSBaptiste Daroussin 			nch->next = np->child;
225361d06d6bSBaptiste Daroussin 			np->child = nch;
225461d06d6bSBaptiste Daroussin 		} else {
225561d06d6bSBaptiste Daroussin 			if (prev->next)
225661d06d6bSBaptiste Daroussin 				prev->next->prev = nch;
225761d06d6bSBaptiste Daroussin 			nch->next = prev->next;
225861d06d6bSBaptiste Daroussin 			prev->next = nch;
225961d06d6bSBaptiste Daroussin 		}
226061d06d6bSBaptiste Daroussin 	}
226161d06d6bSBaptiste Daroussin }
226261d06d6bSBaptiste Daroussin 
226361d06d6bSBaptiste Daroussin /*
226461d06d6bSBaptiste Daroussin  * For some arguments of some macros,
226561d06d6bSBaptiste Daroussin  * convert all breakable hyphens into ASCII_HYPH.
226661d06d6bSBaptiste Daroussin  */
226761d06d6bSBaptiste Daroussin static void
226861d06d6bSBaptiste Daroussin post_hyph(POST_ARGS)
226961d06d6bSBaptiste Daroussin {
22706d38604fSBaptiste Daroussin 	struct roff_node	*n, *nch;
227161d06d6bSBaptiste Daroussin 	char			*cp;
227261d06d6bSBaptiste Daroussin 
22736d38604fSBaptiste Daroussin 	n = mdoc->last;
22746d38604fSBaptiste Daroussin 	for (nch = n->child; nch != NULL; nch = nch->next) {
227561d06d6bSBaptiste Daroussin 		if (nch->type != ROFFT_TEXT)
227661d06d6bSBaptiste Daroussin 			continue;
227761d06d6bSBaptiste Daroussin 		cp = nch->string;
227861d06d6bSBaptiste Daroussin 		if (*cp == '\0')
227961d06d6bSBaptiste Daroussin 			continue;
228061d06d6bSBaptiste Daroussin 		while (*(++cp) != '\0')
228161d06d6bSBaptiste Daroussin 			if (*cp == '-' &&
228261d06d6bSBaptiste Daroussin 			    isalpha((unsigned char)cp[-1]) &&
22836d38604fSBaptiste Daroussin 			    isalpha((unsigned char)cp[1])) {
22846d38604fSBaptiste Daroussin 				if (n->tag == NULL && n->flags & NODE_ID)
22856d38604fSBaptiste Daroussin 					n->tag = mandoc_strdup(nch->string);
228661d06d6bSBaptiste Daroussin 				*cp = ASCII_HYPH;
228761d06d6bSBaptiste Daroussin 			}
228861d06d6bSBaptiste Daroussin 	}
22896d38604fSBaptiste Daroussin }
229061d06d6bSBaptiste Daroussin 
229161d06d6bSBaptiste Daroussin static void
229261d06d6bSBaptiste Daroussin post_ns(POST_ARGS)
229361d06d6bSBaptiste Daroussin {
229461d06d6bSBaptiste Daroussin 	struct roff_node	*n;
229561d06d6bSBaptiste Daroussin 
229661d06d6bSBaptiste Daroussin 	n = mdoc->last;
229761d06d6bSBaptiste Daroussin 	if (n->flags & NODE_LINE ||
229861d06d6bSBaptiste Daroussin 	    (n->next != NULL && n->next->flags & NODE_DELIMC))
22997295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
230061d06d6bSBaptiste Daroussin }
230161d06d6bSBaptiste Daroussin 
230261d06d6bSBaptiste Daroussin static void
230361d06d6bSBaptiste Daroussin post_sx(POST_ARGS)
230461d06d6bSBaptiste Daroussin {
230561d06d6bSBaptiste Daroussin 	post_delim(mdoc);
230661d06d6bSBaptiste Daroussin 	post_hyph(mdoc);
230761d06d6bSBaptiste Daroussin }
230861d06d6bSBaptiste Daroussin 
230961d06d6bSBaptiste Daroussin static void
231061d06d6bSBaptiste Daroussin post_sh(POST_ARGS)
231161d06d6bSBaptiste Daroussin {
23126d38604fSBaptiste Daroussin 	post_section(mdoc);
231361d06d6bSBaptiste Daroussin 
231461d06d6bSBaptiste Daroussin 	switch (mdoc->last->type) {
231561d06d6bSBaptiste Daroussin 	case ROFFT_HEAD:
231661d06d6bSBaptiste Daroussin 		post_sh_head(mdoc);
231761d06d6bSBaptiste Daroussin 		break;
231861d06d6bSBaptiste Daroussin 	case ROFFT_BODY:
231961d06d6bSBaptiste Daroussin 		switch (mdoc->lastsec)  {
232061d06d6bSBaptiste Daroussin 		case SEC_NAME:
232161d06d6bSBaptiste Daroussin 			post_sh_name(mdoc);
232261d06d6bSBaptiste Daroussin 			break;
232361d06d6bSBaptiste Daroussin 		case SEC_SEE_ALSO:
232461d06d6bSBaptiste Daroussin 			post_sh_see_also(mdoc);
232561d06d6bSBaptiste Daroussin 			break;
232661d06d6bSBaptiste Daroussin 		case SEC_AUTHORS:
232761d06d6bSBaptiste Daroussin 			post_sh_authors(mdoc);
232861d06d6bSBaptiste Daroussin 			break;
232961d06d6bSBaptiste Daroussin 		default:
233061d06d6bSBaptiste Daroussin 			break;
233161d06d6bSBaptiste Daroussin 		}
233261d06d6bSBaptiste Daroussin 		break;
233361d06d6bSBaptiste Daroussin 	default:
233461d06d6bSBaptiste Daroussin 		break;
233561d06d6bSBaptiste Daroussin 	}
233661d06d6bSBaptiste Daroussin }
233761d06d6bSBaptiste Daroussin 
233861d06d6bSBaptiste Daroussin static void
233961d06d6bSBaptiste Daroussin post_sh_name(POST_ARGS)
234061d06d6bSBaptiste Daroussin {
234161d06d6bSBaptiste Daroussin 	struct roff_node *n;
234261d06d6bSBaptiste Daroussin 	int hasnm, hasnd;
234361d06d6bSBaptiste Daroussin 
234461d06d6bSBaptiste Daroussin 	hasnm = hasnd = 0;
234561d06d6bSBaptiste Daroussin 
234661d06d6bSBaptiste Daroussin 	for (n = mdoc->last->child; n != NULL; n = n->next) {
234761d06d6bSBaptiste Daroussin 		switch (n->tok) {
234861d06d6bSBaptiste Daroussin 		case MDOC_Nm:
234961d06d6bSBaptiste Daroussin 			if (hasnm && n->child != NULL)
23507295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
23517295610fSBaptiste Daroussin 				    n->line, n->pos,
235261d06d6bSBaptiste Daroussin 				    "Nm %s", n->child->string);
235361d06d6bSBaptiste Daroussin 			hasnm = 1;
235461d06d6bSBaptiste Daroussin 			continue;
235561d06d6bSBaptiste Daroussin 		case MDOC_Nd:
235661d06d6bSBaptiste Daroussin 			hasnd = 1;
235761d06d6bSBaptiste Daroussin 			if (n->next != NULL)
235861d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_NAMESEC_ND,
23597295610fSBaptiste Daroussin 				    n->line, n->pos, NULL);
236061d06d6bSBaptiste Daroussin 			break;
236161d06d6bSBaptiste Daroussin 		case TOKEN_NONE:
236261d06d6bSBaptiste Daroussin 			if (n->type == ROFFT_TEXT &&
236361d06d6bSBaptiste Daroussin 			    n->string[0] == ',' && n->string[1] == '\0' &&
236461d06d6bSBaptiste Daroussin 			    n->next != NULL && n->next->tok == MDOC_Nm) {
236561d06d6bSBaptiste Daroussin 				n = n->next;
236661d06d6bSBaptiste Daroussin 				continue;
236761d06d6bSBaptiste Daroussin 			}
236861d06d6bSBaptiste Daroussin 			/* FALLTHROUGH */
236961d06d6bSBaptiste Daroussin 		default:
23707295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_NAMESEC_BAD,
23717295610fSBaptiste Daroussin 			    n->line, n->pos, "%s", roff_name[n->tok]);
237261d06d6bSBaptiste Daroussin 			continue;
237361d06d6bSBaptiste Daroussin 		}
237461d06d6bSBaptiste Daroussin 		break;
237561d06d6bSBaptiste Daroussin 	}
237661d06d6bSBaptiste Daroussin 
237761d06d6bSBaptiste Daroussin 	if ( ! hasnm)
23787295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NAMESEC_NONM,
237961d06d6bSBaptiste Daroussin 		    mdoc->last->line, mdoc->last->pos, NULL);
238061d06d6bSBaptiste Daroussin 	if ( ! hasnd)
23817295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NAMESEC_NOND,
238261d06d6bSBaptiste Daroussin 		    mdoc->last->line, mdoc->last->pos, NULL);
238361d06d6bSBaptiste Daroussin }
238461d06d6bSBaptiste Daroussin 
238561d06d6bSBaptiste Daroussin static void
238661d06d6bSBaptiste Daroussin post_sh_see_also(POST_ARGS)
238761d06d6bSBaptiste Daroussin {
238861d06d6bSBaptiste Daroussin 	const struct roff_node	*n;
238961d06d6bSBaptiste Daroussin 	const char		*name, *sec;
239061d06d6bSBaptiste Daroussin 	const char		*lastname, *lastsec, *lastpunct;
239161d06d6bSBaptiste Daroussin 	int			 cmp;
239261d06d6bSBaptiste Daroussin 
239361d06d6bSBaptiste Daroussin 	n = mdoc->last->child;
239461d06d6bSBaptiste Daroussin 	lastname = lastsec = lastpunct = NULL;
239561d06d6bSBaptiste Daroussin 	while (n != NULL) {
239661d06d6bSBaptiste Daroussin 		if (n->tok != MDOC_Xr ||
239761d06d6bSBaptiste Daroussin 		    n->child == NULL ||
239861d06d6bSBaptiste Daroussin 		    n->child->next == NULL)
239961d06d6bSBaptiste Daroussin 			break;
240061d06d6bSBaptiste Daroussin 
240161d06d6bSBaptiste Daroussin 		/* Process one .Xr node. */
240261d06d6bSBaptiste Daroussin 
240361d06d6bSBaptiste Daroussin 		name = n->child->string;
240461d06d6bSBaptiste Daroussin 		sec = n->child->next->string;
240561d06d6bSBaptiste Daroussin 		if (lastsec != NULL) {
240661d06d6bSBaptiste Daroussin 			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
24077295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
24087295610fSBaptiste Daroussin 				    n->pos, "%s before %s(%s)",
24097295610fSBaptiste Daroussin 				    lastpunct, name, sec);
241061d06d6bSBaptiste Daroussin 			cmp = strcmp(lastsec, sec);
241161d06d6bSBaptiste Daroussin 			if (cmp > 0)
24127295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
24137295610fSBaptiste Daroussin 				    n->pos, "%s(%s) after %s(%s)",
24147295610fSBaptiste Daroussin 				    name, sec, lastname, lastsec);
241561d06d6bSBaptiste Daroussin 			else if (cmp == 0 &&
241661d06d6bSBaptiste Daroussin 			    strcasecmp(lastname, name) > 0)
24177295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_XR_ORDER, n->line,
24187295610fSBaptiste Daroussin 				    n->pos, "%s after %s", name, lastname);
241961d06d6bSBaptiste Daroussin 		}
242061d06d6bSBaptiste Daroussin 		lastname = name;
242161d06d6bSBaptiste Daroussin 		lastsec = sec;
242261d06d6bSBaptiste Daroussin 
242361d06d6bSBaptiste Daroussin 		/* Process the following node. */
242461d06d6bSBaptiste Daroussin 
242561d06d6bSBaptiste Daroussin 		n = n->next;
242661d06d6bSBaptiste Daroussin 		if (n == NULL)
242761d06d6bSBaptiste Daroussin 			break;
242861d06d6bSBaptiste Daroussin 		if (n->tok == MDOC_Xr) {
242961d06d6bSBaptiste Daroussin 			lastpunct = "none";
243061d06d6bSBaptiste Daroussin 			continue;
243161d06d6bSBaptiste Daroussin 		}
243261d06d6bSBaptiste Daroussin 		if (n->type != ROFFT_TEXT)
243361d06d6bSBaptiste Daroussin 			break;
243461d06d6bSBaptiste Daroussin 		for (name = n->string; *name != '\0'; name++)
243561d06d6bSBaptiste Daroussin 			if (isalpha((const unsigned char)*name))
243661d06d6bSBaptiste Daroussin 				return;
243761d06d6bSBaptiste Daroussin 		lastpunct = n->string;
243861d06d6bSBaptiste Daroussin 		if (n->next == NULL || n->next->tok == MDOC_Rs)
24397295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
24407295610fSBaptiste Daroussin 			    n->pos, "%s after %s(%s)",
244161d06d6bSBaptiste Daroussin 			    lastpunct, lastname, lastsec);
244261d06d6bSBaptiste Daroussin 		n = n->next;
244361d06d6bSBaptiste Daroussin 	}
244461d06d6bSBaptiste Daroussin }
244561d06d6bSBaptiste Daroussin 
244661d06d6bSBaptiste Daroussin static int
244761d06d6bSBaptiste Daroussin child_an(const struct roff_node *n)
244861d06d6bSBaptiste Daroussin {
244961d06d6bSBaptiste Daroussin 
245061d06d6bSBaptiste Daroussin 	for (n = n->child; n != NULL; n = n->next)
245161d06d6bSBaptiste Daroussin 		if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
245261d06d6bSBaptiste Daroussin 			return 1;
245361d06d6bSBaptiste Daroussin 	return 0;
245461d06d6bSBaptiste Daroussin }
245561d06d6bSBaptiste Daroussin 
245661d06d6bSBaptiste Daroussin static void
245761d06d6bSBaptiste Daroussin post_sh_authors(POST_ARGS)
245861d06d6bSBaptiste Daroussin {
245961d06d6bSBaptiste Daroussin 
246061d06d6bSBaptiste Daroussin 	if ( ! child_an(mdoc->last))
24617295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_AN_MISSING,
246261d06d6bSBaptiste Daroussin 		    mdoc->last->line, mdoc->last->pos, NULL);
246361d06d6bSBaptiste Daroussin }
246461d06d6bSBaptiste Daroussin 
246561d06d6bSBaptiste Daroussin /*
246661d06d6bSBaptiste Daroussin  * Return an upper bound for the string distance (allowing
246761d06d6bSBaptiste Daroussin  * transpositions).  Not a full Levenshtein implementation
246861d06d6bSBaptiste Daroussin  * because Levenshtein is quadratic in the string length
246961d06d6bSBaptiste Daroussin  * and this function is called for every standard name,
247061d06d6bSBaptiste Daroussin  * so the check for each custom name would be cubic.
247161d06d6bSBaptiste Daroussin  * The following crude heuristics is linear, resulting
247261d06d6bSBaptiste Daroussin  * in quadratic behaviour for checking one custom name,
247361d06d6bSBaptiste Daroussin  * which does not cause measurable slowdown.
247461d06d6bSBaptiste Daroussin  */
247561d06d6bSBaptiste Daroussin static int
247661d06d6bSBaptiste Daroussin similar(const char *s1, const char *s2)
247761d06d6bSBaptiste Daroussin {
247861d06d6bSBaptiste Daroussin 	const int	maxdist = 3;
247961d06d6bSBaptiste Daroussin 	int		dist = 0;
248061d06d6bSBaptiste Daroussin 
248161d06d6bSBaptiste Daroussin 	while (s1[0] != '\0' && s2[0] != '\0') {
248261d06d6bSBaptiste Daroussin 		if (s1[0] == s2[0]) {
248361d06d6bSBaptiste Daroussin 			s1++;
248461d06d6bSBaptiste Daroussin 			s2++;
248561d06d6bSBaptiste Daroussin 			continue;
248661d06d6bSBaptiste Daroussin 		}
248761d06d6bSBaptiste Daroussin 		if (++dist > maxdist)
248861d06d6bSBaptiste Daroussin 			return INT_MAX;
248961d06d6bSBaptiste Daroussin 		if (s1[1] == s2[1]) {  /* replacement */
249061d06d6bSBaptiste Daroussin 			s1++;
249161d06d6bSBaptiste Daroussin 			s2++;
249261d06d6bSBaptiste Daroussin 		} else if (s1[0] == s2[1] && s1[1] == s2[0]) {
249361d06d6bSBaptiste Daroussin 			s1 += 2;	/* transposition */
249461d06d6bSBaptiste Daroussin 			s2 += 2;
249561d06d6bSBaptiste Daroussin 		} else if (s1[0] == s2[1])  /* insertion */
249661d06d6bSBaptiste Daroussin 			s2++;
249761d06d6bSBaptiste Daroussin 		else if (s1[1] == s2[0])  /* deletion */
249861d06d6bSBaptiste Daroussin 			s1++;
249961d06d6bSBaptiste Daroussin 		else
250061d06d6bSBaptiste Daroussin 			return INT_MAX;
250161d06d6bSBaptiste Daroussin 	}
250261d06d6bSBaptiste Daroussin 	dist += strlen(s1) + strlen(s2);
250361d06d6bSBaptiste Daroussin 	return dist > maxdist ? INT_MAX : dist;
250461d06d6bSBaptiste Daroussin }
250561d06d6bSBaptiste Daroussin 
250661d06d6bSBaptiste Daroussin static void
250761d06d6bSBaptiste Daroussin post_sh_head(POST_ARGS)
250861d06d6bSBaptiste Daroussin {
250961d06d6bSBaptiste Daroussin 	struct roff_node	*nch;
251061d06d6bSBaptiste Daroussin 	const char		*goodsec;
251161d06d6bSBaptiste Daroussin 	const char *const	*testsec;
251261d06d6bSBaptiste Daroussin 	int			 dist, mindist;
251361d06d6bSBaptiste Daroussin 	enum roff_sec		 sec;
251461d06d6bSBaptiste Daroussin 
251561d06d6bSBaptiste Daroussin 	/*
251661d06d6bSBaptiste Daroussin 	 * Process a new section.  Sections are either "named" or
251761d06d6bSBaptiste Daroussin 	 * "custom".  Custom sections are user-defined, while named ones
251861d06d6bSBaptiste Daroussin 	 * follow a conventional order and may only appear in certain
251961d06d6bSBaptiste Daroussin 	 * manual sections.
252061d06d6bSBaptiste Daroussin 	 */
252161d06d6bSBaptiste Daroussin 
252261d06d6bSBaptiste Daroussin 	sec = mdoc->last->sec;
252361d06d6bSBaptiste Daroussin 
252461d06d6bSBaptiste Daroussin 	/* The NAME should be first. */
252561d06d6bSBaptiste Daroussin 
252661d06d6bSBaptiste Daroussin 	if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
25277295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_NAMESEC_FIRST,
252861d06d6bSBaptiste Daroussin 		    mdoc->last->line, mdoc->last->pos, "Sh %s",
252961d06d6bSBaptiste Daroussin 		    sec != SEC_CUSTOM ? secnames[sec] :
253061d06d6bSBaptiste Daroussin 		    (nch = mdoc->last->child) == NULL ? "" :
253161d06d6bSBaptiste Daroussin 		    nch->type == ROFFT_TEXT ? nch->string :
253261d06d6bSBaptiste Daroussin 		    roff_name[nch->tok]);
253361d06d6bSBaptiste Daroussin 
253461d06d6bSBaptiste Daroussin 	/* The SYNOPSIS gets special attention in other areas. */
253561d06d6bSBaptiste Daroussin 
253661d06d6bSBaptiste Daroussin 	if (sec == SEC_SYNOPSIS) {
253761d06d6bSBaptiste Daroussin 		roff_setreg(mdoc->roff, "nS", 1, '=');
253861d06d6bSBaptiste Daroussin 		mdoc->flags |= MDOC_SYNOPSIS;
253961d06d6bSBaptiste Daroussin 	} else {
254061d06d6bSBaptiste Daroussin 		roff_setreg(mdoc->roff, "nS", 0, '=');
254161d06d6bSBaptiste Daroussin 		mdoc->flags &= ~MDOC_SYNOPSIS;
254261d06d6bSBaptiste Daroussin 	}
25436d38604fSBaptiste Daroussin 	if (sec == SEC_DESCRIPTION)
25446d38604fSBaptiste Daroussin 		fn_prio = TAG_STRONG;
254561d06d6bSBaptiste Daroussin 
254661d06d6bSBaptiste Daroussin 	/* Mark our last section. */
254761d06d6bSBaptiste Daroussin 
254861d06d6bSBaptiste Daroussin 	mdoc->lastsec = sec;
254961d06d6bSBaptiste Daroussin 
255061d06d6bSBaptiste Daroussin 	/* We don't care about custom sections after this. */
255161d06d6bSBaptiste Daroussin 
255261d06d6bSBaptiste Daroussin 	if (sec == SEC_CUSTOM) {
255361d06d6bSBaptiste Daroussin 		if ((nch = mdoc->last->child) == NULL ||
255461d06d6bSBaptiste Daroussin 		    nch->type != ROFFT_TEXT || nch->next != NULL)
255561d06d6bSBaptiste Daroussin 			return;
255661d06d6bSBaptiste Daroussin 		goodsec = NULL;
255761d06d6bSBaptiste Daroussin 		mindist = INT_MAX;
255861d06d6bSBaptiste Daroussin 		for (testsec = secnames + 1; *testsec != NULL; testsec++) {
255961d06d6bSBaptiste Daroussin 			dist = similar(nch->string, *testsec);
256061d06d6bSBaptiste Daroussin 			if (dist < mindist) {
256161d06d6bSBaptiste Daroussin 				goodsec = *testsec;
256261d06d6bSBaptiste Daroussin 				mindist = dist;
256361d06d6bSBaptiste Daroussin 			}
256461d06d6bSBaptiste Daroussin 		}
256561d06d6bSBaptiste Daroussin 		if (goodsec != NULL)
25667295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
25677295610fSBaptiste Daroussin 			    "Sh %s instead of %s", nch->string, goodsec);
256861d06d6bSBaptiste Daroussin 		return;
256961d06d6bSBaptiste Daroussin 	}
257061d06d6bSBaptiste Daroussin 
257161d06d6bSBaptiste Daroussin 	/*
257261d06d6bSBaptiste Daroussin 	 * Check whether our non-custom section is being repeated or is
257361d06d6bSBaptiste Daroussin 	 * out of order.
257461d06d6bSBaptiste Daroussin 	 */
257561d06d6bSBaptiste Daroussin 
257661d06d6bSBaptiste Daroussin 	if (sec == mdoc->lastnamed)
25777295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
25787295610fSBaptiste Daroussin 		    mdoc->last->pos, "Sh %s", secnames[sec]);
257961d06d6bSBaptiste Daroussin 
258061d06d6bSBaptiste Daroussin 	if (sec < mdoc->lastnamed)
25817295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
25827295610fSBaptiste Daroussin 		    mdoc->last->pos, "Sh %s", secnames[sec]);
258361d06d6bSBaptiste Daroussin 
258461d06d6bSBaptiste Daroussin 	/* Mark the last named section. */
258561d06d6bSBaptiste Daroussin 
258661d06d6bSBaptiste Daroussin 	mdoc->lastnamed = sec;
258761d06d6bSBaptiste Daroussin 
258861d06d6bSBaptiste Daroussin 	/* Check particular section/manual conventions. */
258961d06d6bSBaptiste Daroussin 
259061d06d6bSBaptiste Daroussin 	if (mdoc->meta.msec == NULL)
259161d06d6bSBaptiste Daroussin 		return;
259261d06d6bSBaptiste Daroussin 
259361d06d6bSBaptiste Daroussin 	goodsec = NULL;
259461d06d6bSBaptiste Daroussin 	switch (sec) {
259561d06d6bSBaptiste Daroussin 	case SEC_ERRORS:
259661d06d6bSBaptiste Daroussin 		if (*mdoc->meta.msec == '4')
259761d06d6bSBaptiste Daroussin 			break;
259861d06d6bSBaptiste Daroussin 		goodsec = "2, 3, 4, 9";
259961d06d6bSBaptiste Daroussin 		/* FALLTHROUGH */
260061d06d6bSBaptiste Daroussin 	case SEC_RETURN_VALUES:
260161d06d6bSBaptiste Daroussin 	case SEC_LIBRARY:
260261d06d6bSBaptiste Daroussin 		if (*mdoc->meta.msec == '2')
260361d06d6bSBaptiste Daroussin 			break;
260461d06d6bSBaptiste Daroussin 		if (*mdoc->meta.msec == '3')
260561d06d6bSBaptiste Daroussin 			break;
260661d06d6bSBaptiste Daroussin 		if (NULL == goodsec)
260761d06d6bSBaptiste Daroussin 			goodsec = "2, 3, 9";
260861d06d6bSBaptiste Daroussin 		/* FALLTHROUGH */
260961d06d6bSBaptiste Daroussin 	case SEC_CONTEXT:
261061d06d6bSBaptiste Daroussin 		if (*mdoc->meta.msec == '9')
261161d06d6bSBaptiste Daroussin 			break;
261261d06d6bSBaptiste Daroussin 		if (NULL == goodsec)
261361d06d6bSBaptiste Daroussin 			goodsec = "9";
26147295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_SEC_MSEC,
261561d06d6bSBaptiste Daroussin 		    mdoc->last->line, mdoc->last->pos,
261661d06d6bSBaptiste Daroussin 		    "Sh %s for %s only", secnames[sec], goodsec);
261761d06d6bSBaptiste Daroussin 		break;
261861d06d6bSBaptiste Daroussin 	default:
261961d06d6bSBaptiste Daroussin 		break;
262061d06d6bSBaptiste Daroussin 	}
262161d06d6bSBaptiste Daroussin }
262261d06d6bSBaptiste Daroussin 
262361d06d6bSBaptiste Daroussin static void
262461d06d6bSBaptiste Daroussin post_xr(POST_ARGS)
262561d06d6bSBaptiste Daroussin {
262661d06d6bSBaptiste Daroussin 	struct roff_node *n, *nch;
262761d06d6bSBaptiste Daroussin 
262861d06d6bSBaptiste Daroussin 	n = mdoc->last;
262961d06d6bSBaptiste Daroussin 	nch = n->child;
263061d06d6bSBaptiste Daroussin 	if (nch->next == NULL) {
26317295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_XR_NOSEC,
263261d06d6bSBaptiste Daroussin 		    n->line, n->pos, "Xr %s", nch->string);
263361d06d6bSBaptiste Daroussin 	} else {
263461d06d6bSBaptiste Daroussin 		assert(nch->next == n->last);
263561d06d6bSBaptiste Daroussin 		if(mandoc_xr_add(nch->next->string, nch->string,
263661d06d6bSBaptiste Daroussin 		    nch->line, nch->pos))
26377295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_XR_SELF,
263861d06d6bSBaptiste Daroussin 			    nch->line, nch->pos, "Xr %s %s",
263961d06d6bSBaptiste Daroussin 			    nch->string, nch->next->string);
264061d06d6bSBaptiste Daroussin 	}
264161d06d6bSBaptiste Daroussin 	post_delim_nb(mdoc);
264261d06d6bSBaptiste Daroussin }
264361d06d6bSBaptiste Daroussin 
264461d06d6bSBaptiste Daroussin static void
26456d38604fSBaptiste Daroussin post_section(POST_ARGS)
264661d06d6bSBaptiste Daroussin {
26476d38604fSBaptiste Daroussin 	struct roff_node *n, *nch;
26486d38604fSBaptiste Daroussin 	char		 *cp, *tag;
264961d06d6bSBaptiste Daroussin 
26506d38604fSBaptiste Daroussin 	n = mdoc->last;
26516d38604fSBaptiste Daroussin 	switch (n->type) {
265261d06d6bSBaptiste Daroussin 	case ROFFT_BLOCK:
265361d06d6bSBaptiste Daroussin 		post_prevpar(mdoc);
265461d06d6bSBaptiste Daroussin 		return;
265561d06d6bSBaptiste Daroussin 	case ROFFT_HEAD:
26566d38604fSBaptiste Daroussin 		tag = NULL;
26576d38604fSBaptiste Daroussin 		deroff(&tag, n);
26586d38604fSBaptiste Daroussin 		if (tag != NULL) {
26596d38604fSBaptiste Daroussin 			for (cp = tag; *cp != '\0'; cp++)
26606d38604fSBaptiste Daroussin 				if (*cp == ' ')
26616d38604fSBaptiste Daroussin 					*cp = '_';
26626d38604fSBaptiste Daroussin 			if ((nch = n->child) != NULL &&
26636d38604fSBaptiste Daroussin 			    nch->type == ROFFT_TEXT &&
26646d38604fSBaptiste Daroussin 			    strcmp(nch->string, tag) == 0)
26656d38604fSBaptiste Daroussin 				tag_put(NULL, TAG_STRONG, n);
26666d38604fSBaptiste Daroussin 			else
26676d38604fSBaptiste Daroussin 				tag_put(tag, TAG_FALLBACK, n);
26686d38604fSBaptiste Daroussin 			free(tag);
26696d38604fSBaptiste Daroussin 		}
267061d06d6bSBaptiste Daroussin 		post_delim(mdoc);
267161d06d6bSBaptiste Daroussin 		post_hyph(mdoc);
267261d06d6bSBaptiste Daroussin 		return;
267361d06d6bSBaptiste Daroussin 	case ROFFT_BODY:
267461d06d6bSBaptiste Daroussin 		break;
267561d06d6bSBaptiste Daroussin 	default:
267661d06d6bSBaptiste Daroussin 		return;
267761d06d6bSBaptiste Daroussin 	}
26786d38604fSBaptiste Daroussin 	if ((nch = n->child) != NULL &&
26796d38604fSBaptiste Daroussin 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br ||
26806d38604fSBaptiste Daroussin 	     nch->tok == ROFF_sp)) {
26816d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
26826d38604fSBaptiste Daroussin 		    "%s after %s", roff_name[nch->tok],
26836d38604fSBaptiste Daroussin 		    roff_name[n->tok]);
26846d38604fSBaptiste Daroussin 		roff_node_delete(mdoc, nch);
268561d06d6bSBaptiste Daroussin 	}
26866d38604fSBaptiste Daroussin 	if ((nch = n->last) != NULL &&
26876d38604fSBaptiste Daroussin 	    (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) {
26886d38604fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
26896d38604fSBaptiste Daroussin 		    "%s at the end of %s", roff_name[nch->tok],
26906d38604fSBaptiste Daroussin 		    roff_name[n->tok]);
26916d38604fSBaptiste Daroussin 		roff_node_delete(mdoc, nch);
269261d06d6bSBaptiste Daroussin 	}
269361d06d6bSBaptiste Daroussin }
269461d06d6bSBaptiste Daroussin 
269561d06d6bSBaptiste Daroussin static void
269661d06d6bSBaptiste Daroussin post_prevpar(POST_ARGS)
269761d06d6bSBaptiste Daroussin {
26986d38604fSBaptiste Daroussin 	struct roff_node *n, *np;
269961d06d6bSBaptiste Daroussin 
270061d06d6bSBaptiste Daroussin 	n = mdoc->last;
270161d06d6bSBaptiste Daroussin 	if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
270261d06d6bSBaptiste Daroussin 		return;
27036d38604fSBaptiste Daroussin 	if ((np = roff_node_prev(n)) == NULL)
27046d38604fSBaptiste Daroussin 		return;
270561d06d6bSBaptiste Daroussin 
270661d06d6bSBaptiste Daroussin 	/*
27077295610fSBaptiste Daroussin 	 * Don't allow `Pp' prior to a paragraph-type
27087295610fSBaptiste Daroussin 	 * block: `Pp' or non-compact `Bd' or `Bl'.
270961d06d6bSBaptiste Daroussin 	 */
271061d06d6bSBaptiste Daroussin 
27116d38604fSBaptiste Daroussin 	if (np->tok != MDOC_Pp && np->tok != ROFF_br)
271261d06d6bSBaptiste Daroussin 		return;
271361d06d6bSBaptiste Daroussin 	if (n->tok == MDOC_Bl && n->norm->Bl.comp)
271461d06d6bSBaptiste Daroussin 		return;
271561d06d6bSBaptiste Daroussin 	if (n->tok == MDOC_Bd && n->norm->Bd.comp)
271661d06d6bSBaptiste Daroussin 		return;
271761d06d6bSBaptiste Daroussin 	if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
271861d06d6bSBaptiste Daroussin 		return;
271961d06d6bSBaptiste Daroussin 
27206d38604fSBaptiste Daroussin 	mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
27216d38604fSBaptiste Daroussin 	    "%s before %s", roff_name[np->tok], roff_name[n->tok]);
27226d38604fSBaptiste Daroussin 	roff_node_delete(mdoc, np);
272361d06d6bSBaptiste Daroussin }
272461d06d6bSBaptiste Daroussin 
272561d06d6bSBaptiste Daroussin static void
272661d06d6bSBaptiste Daroussin post_par(POST_ARGS)
272761d06d6bSBaptiste Daroussin {
272861d06d6bSBaptiste Daroussin 	struct roff_node *np;
272961d06d6bSBaptiste Daroussin 
27306d38604fSBaptiste Daroussin 	fn_prio = TAG_STRONG;
273161d06d6bSBaptiste Daroussin 	post_prevpar(mdoc);
273261d06d6bSBaptiste Daroussin 
27337295610fSBaptiste Daroussin 	np = mdoc->last;
27347295610fSBaptiste Daroussin 	if (np->child != NULL)
27357295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
27367295610fSBaptiste Daroussin 		    "%s %s", roff_name[np->tok], np->child->string);
273761d06d6bSBaptiste Daroussin }
273861d06d6bSBaptiste Daroussin 
273961d06d6bSBaptiste Daroussin static void
274061d06d6bSBaptiste Daroussin post_dd(POST_ARGS)
274161d06d6bSBaptiste Daroussin {
274261d06d6bSBaptiste Daroussin 	struct roff_node *n;
274361d06d6bSBaptiste Daroussin 
274461d06d6bSBaptiste Daroussin 	n = mdoc->last;
274561d06d6bSBaptiste Daroussin 	n->flags |= NODE_NOPRT;
274661d06d6bSBaptiste Daroussin 
274761d06d6bSBaptiste Daroussin 	if (mdoc->meta.date != NULL) {
27487295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
274961d06d6bSBaptiste Daroussin 		free(mdoc->meta.date);
275061d06d6bSBaptiste Daroussin 	} else if (mdoc->flags & MDOC_PBODY)
27517295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
275261d06d6bSBaptiste Daroussin 	else if (mdoc->meta.title != NULL)
27537295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
275461d06d6bSBaptiste Daroussin 		    n->line, n->pos, "Dd after Dt");
275561d06d6bSBaptiste Daroussin 	else if (mdoc->meta.os != NULL)
27567295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
275761d06d6bSBaptiste Daroussin 		    n->line, n->pos, "Dd after Os");
275861d06d6bSBaptiste Daroussin 
27596d38604fSBaptiste Daroussin 	if (mdoc->quick && n != NULL)
27606d38604fSBaptiste Daroussin 		mdoc->meta.date = mandoc_strdup("");
27616d38604fSBaptiste Daroussin 	else
27626d38604fSBaptiste Daroussin 		mdoc->meta.date = mandoc_normdate(n->child, n);
276361d06d6bSBaptiste Daroussin }
276461d06d6bSBaptiste Daroussin 
276561d06d6bSBaptiste Daroussin static void
276661d06d6bSBaptiste Daroussin post_dt(POST_ARGS)
276761d06d6bSBaptiste Daroussin {
276861d06d6bSBaptiste Daroussin 	struct roff_node *nn, *n;
276961d06d6bSBaptiste Daroussin 	const char	 *cp;
277061d06d6bSBaptiste Daroussin 	char		 *p;
277161d06d6bSBaptiste Daroussin 
277261d06d6bSBaptiste Daroussin 	n = mdoc->last;
277361d06d6bSBaptiste Daroussin 	n->flags |= NODE_NOPRT;
277461d06d6bSBaptiste Daroussin 
277561d06d6bSBaptiste Daroussin 	if (mdoc->flags & MDOC_PBODY) {
27767295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
277761d06d6bSBaptiste Daroussin 		return;
277861d06d6bSBaptiste Daroussin 	}
277961d06d6bSBaptiste Daroussin 
278061d06d6bSBaptiste Daroussin 	if (mdoc->meta.title != NULL)
27817295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
278261d06d6bSBaptiste Daroussin 	else if (mdoc->meta.os != NULL)
27837295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
278461d06d6bSBaptiste Daroussin 		    n->line, n->pos, "Dt after Os");
278561d06d6bSBaptiste Daroussin 
278661d06d6bSBaptiste Daroussin 	free(mdoc->meta.title);
278761d06d6bSBaptiste Daroussin 	free(mdoc->meta.msec);
278861d06d6bSBaptiste Daroussin 	free(mdoc->meta.vol);
278961d06d6bSBaptiste Daroussin 	free(mdoc->meta.arch);
279061d06d6bSBaptiste Daroussin 
279161d06d6bSBaptiste Daroussin 	mdoc->meta.title = NULL;
279261d06d6bSBaptiste Daroussin 	mdoc->meta.msec = NULL;
279361d06d6bSBaptiste Daroussin 	mdoc->meta.vol = NULL;
279461d06d6bSBaptiste Daroussin 	mdoc->meta.arch = NULL;
279561d06d6bSBaptiste Daroussin 
279661d06d6bSBaptiste Daroussin 	/* Mandatory first argument: title. */
279761d06d6bSBaptiste Daroussin 
279861d06d6bSBaptiste Daroussin 	nn = n->child;
279961d06d6bSBaptiste Daroussin 	if (nn == NULL || *nn->string == '\0') {
28007295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
280161d06d6bSBaptiste Daroussin 		mdoc->meta.title = mandoc_strdup("UNTITLED");
280261d06d6bSBaptiste Daroussin 	} else {
280361d06d6bSBaptiste Daroussin 		mdoc->meta.title = mandoc_strdup(nn->string);
280461d06d6bSBaptiste Daroussin 
280561d06d6bSBaptiste Daroussin 		/* Check that all characters are uppercase. */
280661d06d6bSBaptiste Daroussin 
280761d06d6bSBaptiste Daroussin 		for (p = nn->string; *p != '\0'; p++)
280861d06d6bSBaptiste Daroussin 			if (islower((unsigned char)*p)) {
28097295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
28107295610fSBaptiste Daroussin 				    nn->pos + (int)(p - nn->string),
281161d06d6bSBaptiste Daroussin 				    "Dt %s", nn->string);
281261d06d6bSBaptiste Daroussin 				break;
281361d06d6bSBaptiste Daroussin 			}
281461d06d6bSBaptiste Daroussin 	}
281561d06d6bSBaptiste Daroussin 
281661d06d6bSBaptiste Daroussin 	/* Mandatory second argument: section. */
281761d06d6bSBaptiste Daroussin 
281861d06d6bSBaptiste Daroussin 	if (nn != NULL)
281961d06d6bSBaptiste Daroussin 		nn = nn->next;
282061d06d6bSBaptiste Daroussin 
282161d06d6bSBaptiste Daroussin 	if (nn == NULL) {
28227295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
282361d06d6bSBaptiste Daroussin 		    "Dt %s", mdoc->meta.title);
282461d06d6bSBaptiste Daroussin 		mdoc->meta.vol = mandoc_strdup("LOCAL");
282561d06d6bSBaptiste Daroussin 		return;  /* msec and arch remain NULL. */
282661d06d6bSBaptiste Daroussin 	}
282761d06d6bSBaptiste Daroussin 
282861d06d6bSBaptiste Daroussin 	mdoc->meta.msec = mandoc_strdup(nn->string);
282961d06d6bSBaptiste Daroussin 
283061d06d6bSBaptiste Daroussin 	/* Infer volume title from section number. */
283161d06d6bSBaptiste Daroussin 
283261d06d6bSBaptiste Daroussin 	cp = mandoc_a2msec(nn->string);
283361d06d6bSBaptiste Daroussin 	if (cp == NULL) {
28347295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_MSEC_BAD,
283561d06d6bSBaptiste Daroussin 		    nn->line, nn->pos, "Dt ... %s", nn->string);
283661d06d6bSBaptiste Daroussin 		mdoc->meta.vol = mandoc_strdup(nn->string);
28376d38604fSBaptiste Daroussin 	} else {
283861d06d6bSBaptiste Daroussin 		mdoc->meta.vol = mandoc_strdup(cp);
28396d38604fSBaptiste Daroussin 		if (mdoc->filesec != '\0' &&
28406d38604fSBaptiste Daroussin 		    mdoc->filesec != *nn->string &&
28416d38604fSBaptiste Daroussin 		    *nn->string >= '1' && *nn->string <= '9')
28426d38604fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos,
28436d38604fSBaptiste Daroussin 			    "*.%c vs Dt ... %c", mdoc->filesec, *nn->string);
28446d38604fSBaptiste Daroussin 	}
284561d06d6bSBaptiste Daroussin 
284661d06d6bSBaptiste Daroussin 	/* Optional third argument: architecture. */
284761d06d6bSBaptiste Daroussin 
284861d06d6bSBaptiste Daroussin 	if ((nn = nn->next) == NULL)
284961d06d6bSBaptiste Daroussin 		return;
285061d06d6bSBaptiste Daroussin 
285161d06d6bSBaptiste Daroussin 	for (p = nn->string; *p != '\0'; p++)
285261d06d6bSBaptiste Daroussin 		*p = tolower((unsigned char)*p);
285361d06d6bSBaptiste Daroussin 	mdoc->meta.arch = mandoc_strdup(nn->string);
285461d06d6bSBaptiste Daroussin 
285561d06d6bSBaptiste Daroussin 	/* Ignore fourth and later arguments. */
285661d06d6bSBaptiste Daroussin 
285761d06d6bSBaptiste Daroussin 	if ((nn = nn->next) != NULL)
28587295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ARG_EXCESS,
285961d06d6bSBaptiste Daroussin 		    nn->line, nn->pos, "Dt ... %s", nn->string);
286061d06d6bSBaptiste Daroussin }
286161d06d6bSBaptiste Daroussin 
286261d06d6bSBaptiste Daroussin static void
286361d06d6bSBaptiste Daroussin post_bx(POST_ARGS)
286461d06d6bSBaptiste Daroussin {
286561d06d6bSBaptiste Daroussin 	struct roff_node	*n, *nch;
286661d06d6bSBaptiste Daroussin 	const char		*macro;
286761d06d6bSBaptiste Daroussin 
286861d06d6bSBaptiste Daroussin 	post_delim_nb(mdoc);
286961d06d6bSBaptiste Daroussin 
287061d06d6bSBaptiste Daroussin 	n = mdoc->last;
287161d06d6bSBaptiste Daroussin 	nch = n->child;
287261d06d6bSBaptiste Daroussin 
287361d06d6bSBaptiste Daroussin 	if (nch != NULL) {
287461d06d6bSBaptiste Daroussin 		macro = !strcmp(nch->string, "Open") ? "Ox" :
287561d06d6bSBaptiste Daroussin 		    !strcmp(nch->string, "Net") ? "Nx" :
287661d06d6bSBaptiste Daroussin 		    !strcmp(nch->string, "Free") ? "Fx" :
287761d06d6bSBaptiste Daroussin 		    !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
287861d06d6bSBaptiste Daroussin 		if (macro != NULL)
28797295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BX,
28807295610fSBaptiste Daroussin 			    n->line, n->pos, "%s", macro);
288161d06d6bSBaptiste Daroussin 		mdoc->last = nch;
288261d06d6bSBaptiste Daroussin 		nch = nch->next;
288361d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
288461d06d6bSBaptiste Daroussin 		roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
288561d06d6bSBaptiste Daroussin 		mdoc->last->flags |= NODE_NOSRC;
288661d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_SIBLING;
288761d06d6bSBaptiste Daroussin 	} else
288861d06d6bSBaptiste Daroussin 		mdoc->next = ROFF_NEXT_CHILD;
288961d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "BSD");
289061d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
289161d06d6bSBaptiste Daroussin 
289261d06d6bSBaptiste Daroussin 	if (nch == NULL) {
289361d06d6bSBaptiste Daroussin 		mdoc->last = n;
289461d06d6bSBaptiste Daroussin 		return;
289561d06d6bSBaptiste Daroussin 	}
289661d06d6bSBaptiste Daroussin 
289761d06d6bSBaptiste Daroussin 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
289861d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
289961d06d6bSBaptiste Daroussin 	mdoc->next = ROFF_NEXT_SIBLING;
290061d06d6bSBaptiste Daroussin 	roff_word_alloc(mdoc, n->line, n->pos, "-");
290161d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
290261d06d6bSBaptiste Daroussin 	roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
290361d06d6bSBaptiste Daroussin 	mdoc->last->flags |= NODE_NOSRC;
290461d06d6bSBaptiste Daroussin 	mdoc->last = n;
290561d06d6bSBaptiste Daroussin 
290661d06d6bSBaptiste Daroussin 	/*
290761d06d6bSBaptiste Daroussin 	 * Make `Bx's second argument always start with an uppercase
290861d06d6bSBaptiste Daroussin 	 * letter.  Groff checks if it's an "accepted" term, but we just
290961d06d6bSBaptiste Daroussin 	 * uppercase blindly.
291061d06d6bSBaptiste Daroussin 	 */
291161d06d6bSBaptiste Daroussin 
291261d06d6bSBaptiste Daroussin 	*nch->string = (char)toupper((unsigned char)*nch->string);
291361d06d6bSBaptiste Daroussin }
291461d06d6bSBaptiste Daroussin 
291561d06d6bSBaptiste Daroussin static void
291661d06d6bSBaptiste Daroussin post_os(POST_ARGS)
291761d06d6bSBaptiste Daroussin {
291861d06d6bSBaptiste Daroussin #ifndef OSNAME
291961d06d6bSBaptiste Daroussin 	struct utsname	  utsname;
292061d06d6bSBaptiste Daroussin #endif
292161d06d6bSBaptiste Daroussin 	struct roff_node *n;
292261d06d6bSBaptiste Daroussin 
292361d06d6bSBaptiste Daroussin 	n = mdoc->last;
292461d06d6bSBaptiste Daroussin 	n->flags |= NODE_NOPRT;
292561d06d6bSBaptiste Daroussin 
292661d06d6bSBaptiste Daroussin 	if (mdoc->meta.os != NULL)
29277295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
292861d06d6bSBaptiste Daroussin 	else if (mdoc->flags & MDOC_PBODY)
29297295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
293061d06d6bSBaptiste Daroussin 
293161d06d6bSBaptiste Daroussin 	post_delim(mdoc);
293261d06d6bSBaptiste Daroussin 
293361d06d6bSBaptiste Daroussin 	/*
293461d06d6bSBaptiste Daroussin 	 * Set the operating system by way of the `Os' macro.
293561d06d6bSBaptiste Daroussin 	 * The order of precedence is:
293661d06d6bSBaptiste Daroussin 	 * 1. the argument of the `Os' macro, unless empty
293761d06d6bSBaptiste Daroussin 	 * 2. the -Ios=foo command line argument, if provided
293861d06d6bSBaptiste Daroussin 	 * 3. -DOSNAME="\"foo\"", if provided during compilation
293961d06d6bSBaptiste Daroussin 	 * 4. "sysname release" from uname(3)
294061d06d6bSBaptiste Daroussin 	 */
294161d06d6bSBaptiste Daroussin 
294261d06d6bSBaptiste Daroussin 	free(mdoc->meta.os);
294361d06d6bSBaptiste Daroussin 	mdoc->meta.os = NULL;
294461d06d6bSBaptiste Daroussin 	deroff(&mdoc->meta.os, n);
294561d06d6bSBaptiste Daroussin 	if (mdoc->meta.os)
294661d06d6bSBaptiste Daroussin 		goto out;
294761d06d6bSBaptiste Daroussin 
294861d06d6bSBaptiste Daroussin 	if (mdoc->os_s != NULL) {
294961d06d6bSBaptiste Daroussin 		mdoc->meta.os = mandoc_strdup(mdoc->os_s);
295061d06d6bSBaptiste Daroussin 		goto out;
295161d06d6bSBaptiste Daroussin 	}
295261d06d6bSBaptiste Daroussin 
295361d06d6bSBaptiste Daroussin #ifdef OSNAME
295461d06d6bSBaptiste Daroussin 	mdoc->meta.os = mandoc_strdup(OSNAME);
295561d06d6bSBaptiste Daroussin #else /*!OSNAME */
2956*c1c95addSBrooks Davis 	if (mdoc->os_r == NULL) {
295761d06d6bSBaptiste Daroussin 		if (uname(&utsname) == -1) {
29587295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
2959*c1c95addSBrooks Davis 			mdoc->os_r = mandoc_strdup("UNKNOWN");
296061d06d6bSBaptiste Daroussin 		} else
2961*c1c95addSBrooks Davis 			mandoc_asprintf(&mdoc->os_r, "%s %s",
296261d06d6bSBaptiste Daroussin 			    utsname.sysname, utsname.release);
296361d06d6bSBaptiste Daroussin 	}
2964*c1c95addSBrooks Davis 	mdoc->meta.os = mandoc_strdup(mdoc->os_r);
296561d06d6bSBaptiste Daroussin #endif /*!OSNAME*/
296661d06d6bSBaptiste Daroussin 
296761d06d6bSBaptiste Daroussin out:
296861d06d6bSBaptiste Daroussin 	if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
296961d06d6bSBaptiste Daroussin 		if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
297061d06d6bSBaptiste Daroussin 			mdoc->meta.os_e = MANDOC_OS_OPENBSD;
297161d06d6bSBaptiste Daroussin 		else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
297261d06d6bSBaptiste Daroussin 			mdoc->meta.os_e = MANDOC_OS_NETBSD;
297361d06d6bSBaptiste Daroussin 	}
297461d06d6bSBaptiste Daroussin 
297561d06d6bSBaptiste Daroussin 	/*
297661d06d6bSBaptiste Daroussin 	 * This is the earliest point where we can check
297761d06d6bSBaptiste Daroussin 	 * Mdocdate conventions because we don't know
297861d06d6bSBaptiste Daroussin 	 * the operating system earlier.
297961d06d6bSBaptiste Daroussin 	 */
298061d06d6bSBaptiste Daroussin 
298161d06d6bSBaptiste Daroussin 	if (n->child != NULL)
29827295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
298361d06d6bSBaptiste Daroussin 		    "Os %s (%s)", n->child->string,
298461d06d6bSBaptiste Daroussin 		    mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
298561d06d6bSBaptiste Daroussin 		    "OpenBSD" : "NetBSD");
298661d06d6bSBaptiste Daroussin 
298761d06d6bSBaptiste Daroussin 	while (n->tok != MDOC_Dd)
298861d06d6bSBaptiste Daroussin 		if ((n = n->prev) == NULL)
298961d06d6bSBaptiste Daroussin 			return;
299061d06d6bSBaptiste Daroussin 	if ((n = n->child) == NULL)
299161d06d6bSBaptiste Daroussin 		return;
299261d06d6bSBaptiste Daroussin 	if (strncmp(n->string, "$" "Mdocdate", 9)) {
299361d06d6bSBaptiste Daroussin 		if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
29947295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
29957295610fSBaptiste Daroussin 			    n->pos, "Dd %s (OpenBSD)", n->string);
299661d06d6bSBaptiste Daroussin 	} else {
299761d06d6bSBaptiste Daroussin 		if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
29987295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_MDOCDATE, n->line,
29997295610fSBaptiste Daroussin 			    n->pos, "Dd %s (NetBSD)", n->string);
300061d06d6bSBaptiste Daroussin 	}
300161d06d6bSBaptiste Daroussin }
300261d06d6bSBaptiste Daroussin 
300361d06d6bSBaptiste Daroussin enum roff_sec
300461d06d6bSBaptiste Daroussin mdoc_a2sec(const char *p)
300561d06d6bSBaptiste Daroussin {
300661d06d6bSBaptiste Daroussin 	int		 i;
300761d06d6bSBaptiste Daroussin 
300861d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)SEC__MAX; i++)
300961d06d6bSBaptiste Daroussin 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
301061d06d6bSBaptiste Daroussin 			return (enum roff_sec)i;
301161d06d6bSBaptiste Daroussin 
301261d06d6bSBaptiste Daroussin 	return SEC_CUSTOM;
301361d06d6bSBaptiste Daroussin }
301461d06d6bSBaptiste Daroussin 
301561d06d6bSBaptiste Daroussin static size_t
301661d06d6bSBaptiste Daroussin macro2len(enum roff_tok macro)
301761d06d6bSBaptiste Daroussin {
301861d06d6bSBaptiste Daroussin 
301961d06d6bSBaptiste Daroussin 	switch (macro) {
302061d06d6bSBaptiste Daroussin 	case MDOC_Ad:
302161d06d6bSBaptiste Daroussin 		return 12;
302261d06d6bSBaptiste Daroussin 	case MDOC_Ao:
302361d06d6bSBaptiste Daroussin 		return 12;
302461d06d6bSBaptiste Daroussin 	case MDOC_An:
302561d06d6bSBaptiste Daroussin 		return 12;
302661d06d6bSBaptiste Daroussin 	case MDOC_Aq:
302761d06d6bSBaptiste Daroussin 		return 12;
302861d06d6bSBaptiste Daroussin 	case MDOC_Ar:
302961d06d6bSBaptiste Daroussin 		return 12;
303061d06d6bSBaptiste Daroussin 	case MDOC_Bo:
303161d06d6bSBaptiste Daroussin 		return 12;
303261d06d6bSBaptiste Daroussin 	case MDOC_Bq:
303361d06d6bSBaptiste Daroussin 		return 12;
303461d06d6bSBaptiste Daroussin 	case MDOC_Cd:
303561d06d6bSBaptiste Daroussin 		return 12;
303661d06d6bSBaptiste Daroussin 	case MDOC_Cm:
303761d06d6bSBaptiste Daroussin 		return 10;
303861d06d6bSBaptiste Daroussin 	case MDOC_Do:
303961d06d6bSBaptiste Daroussin 		return 10;
304061d06d6bSBaptiste Daroussin 	case MDOC_Dq:
304161d06d6bSBaptiste Daroussin 		return 12;
304261d06d6bSBaptiste Daroussin 	case MDOC_Dv:
304361d06d6bSBaptiste Daroussin 		return 12;
304461d06d6bSBaptiste Daroussin 	case MDOC_Eo:
304561d06d6bSBaptiste Daroussin 		return 12;
304661d06d6bSBaptiste Daroussin 	case MDOC_Em:
304761d06d6bSBaptiste Daroussin 		return 10;
304861d06d6bSBaptiste Daroussin 	case MDOC_Er:
304961d06d6bSBaptiste Daroussin 		return 17;
305061d06d6bSBaptiste Daroussin 	case MDOC_Ev:
305161d06d6bSBaptiste Daroussin 		return 15;
305261d06d6bSBaptiste Daroussin 	case MDOC_Fa:
305361d06d6bSBaptiste Daroussin 		return 12;
305461d06d6bSBaptiste Daroussin 	case MDOC_Fl:
305561d06d6bSBaptiste Daroussin 		return 10;
305661d06d6bSBaptiste Daroussin 	case MDOC_Fo:
305761d06d6bSBaptiste Daroussin 		return 16;
305861d06d6bSBaptiste Daroussin 	case MDOC_Fn:
305961d06d6bSBaptiste Daroussin 		return 16;
306061d06d6bSBaptiste Daroussin 	case MDOC_Ic:
306161d06d6bSBaptiste Daroussin 		return 10;
306261d06d6bSBaptiste Daroussin 	case MDOC_Li:
306361d06d6bSBaptiste Daroussin 		return 16;
306461d06d6bSBaptiste Daroussin 	case MDOC_Ms:
306561d06d6bSBaptiste Daroussin 		return 6;
306661d06d6bSBaptiste Daroussin 	case MDOC_Nm:
306761d06d6bSBaptiste Daroussin 		return 10;
306861d06d6bSBaptiste Daroussin 	case MDOC_No:
306961d06d6bSBaptiste Daroussin 		return 12;
307061d06d6bSBaptiste Daroussin 	case MDOC_Oo:
307161d06d6bSBaptiste Daroussin 		return 10;
307261d06d6bSBaptiste Daroussin 	case MDOC_Op:
307361d06d6bSBaptiste Daroussin 		return 14;
307461d06d6bSBaptiste Daroussin 	case MDOC_Pa:
307561d06d6bSBaptiste Daroussin 		return 32;
307661d06d6bSBaptiste Daroussin 	case MDOC_Pf:
307761d06d6bSBaptiste Daroussin 		return 12;
307861d06d6bSBaptiste Daroussin 	case MDOC_Po:
307961d06d6bSBaptiste Daroussin 		return 12;
308061d06d6bSBaptiste Daroussin 	case MDOC_Pq:
308161d06d6bSBaptiste Daroussin 		return 12;
308261d06d6bSBaptiste Daroussin 	case MDOC_Ql:
308361d06d6bSBaptiste Daroussin 		return 16;
308461d06d6bSBaptiste Daroussin 	case MDOC_Qo:
308561d06d6bSBaptiste Daroussin 		return 12;
308661d06d6bSBaptiste Daroussin 	case MDOC_So:
308761d06d6bSBaptiste Daroussin 		return 12;
308861d06d6bSBaptiste Daroussin 	case MDOC_Sq:
308961d06d6bSBaptiste Daroussin 		return 12;
309061d06d6bSBaptiste Daroussin 	case MDOC_Sy:
309161d06d6bSBaptiste Daroussin 		return 6;
309261d06d6bSBaptiste Daroussin 	case MDOC_Sx:
309361d06d6bSBaptiste Daroussin 		return 16;
309461d06d6bSBaptiste Daroussin 	case MDOC_Tn:
309561d06d6bSBaptiste Daroussin 		return 10;
309661d06d6bSBaptiste Daroussin 	case MDOC_Va:
309761d06d6bSBaptiste Daroussin 		return 12;
309861d06d6bSBaptiste Daroussin 	case MDOC_Vt:
309961d06d6bSBaptiste Daroussin 		return 12;
310061d06d6bSBaptiste Daroussin 	case MDOC_Xr:
310161d06d6bSBaptiste Daroussin 		return 10;
310261d06d6bSBaptiste Daroussin 	default:
310361d06d6bSBaptiste Daroussin 		break;
310461d06d6bSBaptiste Daroussin 	};
310561d06d6bSBaptiste Daroussin 	return 0;
310661d06d6bSBaptiste Daroussin }
3107