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