16167eca2Schristos /* Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp */
24154958bSjoerg /*
3603fc4ebSjoerg * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
46167eca2Schristos * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
55c413d0cSchristos * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
64154958bSjoerg *
74154958bSjoerg * Permission to use, copy, modify, and distribute this software for any
84154958bSjoerg * purpose with or without fee is hereby granted, provided that the above
94154958bSjoerg * copyright notice and this permission notice appear in all copies.
104154958bSjoerg *
11f47368cfSchristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
124154958bSjoerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13f47368cfSchristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
144154958bSjoerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
154154958bSjoerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
164154958bSjoerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
174154958bSjoerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
184154958bSjoerg */
19d5e63c8dSjoerg #include "config.h"
20d5e63c8dSjoerg
215c413d0cSchristos #include <sys/types.h>
22c0d9444aSjoerg #ifndef OSNAME
23c0d9444aSjoerg #include <sys/utsname.h>
24c0d9444aSjoerg #endif
25c0d9444aSjoerg
264154958bSjoerg #include <assert.h>
274154958bSjoerg #include <ctype.h>
284154958bSjoerg #include <limits.h>
290a84adc5Sjoerg #include <stdio.h>
304154958bSjoerg #include <stdlib.h>
314154958bSjoerg #include <string.h>
32c0d9444aSjoerg #include <time.h>
334154958bSjoerg
345c413d0cSchristos #include "mandoc_aux.h"
35f47368cfSchristos #include "mandoc.h"
3614e7489eSchristos #include "mandoc_xr.h"
37f47368cfSchristos #include "roff.h"
38f47368cfSchristos #include "mdoc.h"
394154958bSjoerg #include "libmandoc.h"
40f47368cfSchristos #include "roff_int.h"
41f47368cfSchristos #include "libmdoc.h"
424154958bSjoerg
434154958bSjoerg /* FIXME: .Bl -diag can't have non-text children in HEAD. */
444154958bSjoerg
45f47368cfSchristos #define POST_ARGS struct roff_man *mdoc
464154958bSjoerg
47c0d9444aSjoerg enum check_ineq {
48c0d9444aSjoerg CHECK_LT,
49c0d9444aSjoerg CHECK_GT,
50c0d9444aSjoerg CHECK_EQ
51c0d9444aSjoerg };
52c0d9444aSjoerg
535c413d0cSchristos typedef void (*v_post)(POST_ARGS);
544154958bSjoerg
5537ef69edSchristos static int build_list(struct roff_man *, int);
56f47368cfSchristos static void check_argv(struct roff_man *,
57f47368cfSchristos struct roff_node *, struct mdoc_argv *);
58f47368cfSchristos static void check_args(struct roff_man *, struct roff_node *);
5914e7489eSchristos static void check_text(struct roff_man *, int, int, char *);
6014e7489eSchristos static void check_text_em(struct roff_man *, int, int, char *);
6114e7489eSchristos static void check_toptext(struct roff_man *, int, int, const char *);
62f47368cfSchristos static int child_an(const struct roff_node *);
6314e7489eSchristos static size_t macro2len(enum roff_tok);
6414e7489eSchristos static void rewrite_macro2len(struct roff_man *, char **);
6514e7489eSchristos static int similar(const char *, const char *);
66c0d9444aSjoerg
67*d90f6d4dSchristos static void post_abort(POST_ARGS) __dead;
685c413d0cSchristos static void post_an(POST_ARGS);
69f47368cfSchristos static void post_an_norm(POST_ARGS);
705c413d0cSchristos static void post_at(POST_ARGS);
71f47368cfSchristos static void post_bd(POST_ARGS);
725c413d0cSchristos static void post_bf(POST_ARGS);
735c413d0cSchristos static void post_bk(POST_ARGS);
745c413d0cSchristos static void post_bl(POST_ARGS);
755c413d0cSchristos static void post_bl_block(POST_ARGS);
765c413d0cSchristos static void post_bl_head(POST_ARGS);
77f47368cfSchristos static void post_bl_norm(POST_ARGS);
785c413d0cSchristos static void post_bx(POST_ARGS);
795c413d0cSchristos static void post_defaults(POST_ARGS);
80f47368cfSchristos static void post_display(POST_ARGS);
815c413d0cSchristos static void post_dd(POST_ARGS);
8214e7489eSchristos static void post_delim(POST_ARGS);
8314e7489eSchristos static void post_delim_nb(POST_ARGS);
845c413d0cSchristos static void post_dt(POST_ARGS);
855c413d0cSchristos static void post_en(POST_ARGS);
865c413d0cSchristos static void post_es(POST_ARGS);
875c413d0cSchristos static void post_eoln(POST_ARGS);
885c413d0cSchristos static void post_ex(POST_ARGS);
895c413d0cSchristos static void post_fa(POST_ARGS);
905c413d0cSchristos static void post_fn(POST_ARGS);
915c413d0cSchristos static void post_fname(POST_ARGS);
925c413d0cSchristos static void post_fo(POST_ARGS);
935c413d0cSchristos static void post_hyph(POST_ARGS);
945c413d0cSchristos static void post_ignpar(POST_ARGS);
955c413d0cSchristos static void post_it(POST_ARGS);
965c413d0cSchristos static void post_lb(POST_ARGS);
975c413d0cSchristos static void post_nd(POST_ARGS);
985c413d0cSchristos static void post_nm(POST_ARGS);
995c413d0cSchristos static void post_ns(POST_ARGS);
100f47368cfSchristos static void post_obsolete(POST_ARGS);
1015c413d0cSchristos static void post_os(POST_ARGS);
1025c413d0cSchristos static void post_par(POST_ARGS);
103f47368cfSchristos static void post_prevpar(POST_ARGS);
1045c413d0cSchristos static void post_root(POST_ARGS);
1055c413d0cSchristos static void post_rs(POST_ARGS);
10637ef69edSchristos static void post_rv(POST_ARGS);
1075c413d0cSchristos static void post_sh(POST_ARGS);
1085c413d0cSchristos static void post_sh_head(POST_ARGS);
1095c413d0cSchristos static void post_sh_name(POST_ARGS);
1105c413d0cSchristos static void post_sh_see_also(POST_ARGS);
1115c413d0cSchristos static void post_sh_authors(POST_ARGS);
1125c413d0cSchristos static void post_sm(POST_ARGS);
1135c413d0cSchristos static void post_st(POST_ARGS);
114f47368cfSchristos static void post_std(POST_ARGS);
11514e7489eSchristos static void post_sx(POST_ARGS);
11614e7489eSchristos static void post_useless(POST_ARGS);
11737ef69edSchristos static void post_xr(POST_ARGS);
11837ef69edSchristos static void post_xx(POST_ARGS);
1194154958bSjoerg
1206167eca2Schristos static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
121f47368cfSchristos post_dd, /* Dd */
122f47368cfSchristos post_dt, /* Dt */
123f47368cfSchristos post_os, /* Os */
124f47368cfSchristos post_sh, /* Sh */
125f47368cfSchristos post_ignpar, /* Ss */
126f47368cfSchristos post_par, /* Pp */
127f47368cfSchristos post_display, /* D1 */
128f47368cfSchristos post_display, /* Dl */
129f47368cfSchristos post_display, /* Bd */
130f47368cfSchristos NULL, /* Ed */
131f47368cfSchristos post_bl, /* Bl */
132f47368cfSchristos NULL, /* El */
133f47368cfSchristos post_it, /* It */
13414e7489eSchristos post_delim_nb, /* Ad */
135f47368cfSchristos post_an, /* An */
13614e7489eSchristos NULL, /* Ap */
137f47368cfSchristos post_defaults, /* Ar */
138f47368cfSchristos NULL, /* Cd */
13914e7489eSchristos post_delim_nb, /* Cm */
14014e7489eSchristos post_delim_nb, /* Dv */
14114e7489eSchristos post_delim_nb, /* Er */
14214e7489eSchristos post_delim_nb, /* Ev */
143f47368cfSchristos post_ex, /* Ex */
144f47368cfSchristos post_fa, /* Fa */
145f47368cfSchristos NULL, /* Fd */
14614e7489eSchristos post_delim_nb, /* Fl */
147f47368cfSchristos post_fn, /* Fn */
14814e7489eSchristos post_delim_nb, /* Ft */
14914e7489eSchristos post_delim_nb, /* Ic */
15014e7489eSchristos post_delim_nb, /* In */
151f47368cfSchristos post_defaults, /* Li */
152f47368cfSchristos post_nd, /* Nd */
153f47368cfSchristos post_nm, /* Nm */
15414e7489eSchristos post_delim_nb, /* Op */
1556167eca2Schristos post_abort, /* Ot */
156f47368cfSchristos post_defaults, /* Pa */
15737ef69edSchristos post_rv, /* Rv */
158f47368cfSchristos post_st, /* St */
15914e7489eSchristos post_delim_nb, /* Va */
16014e7489eSchristos post_delim_nb, /* Vt */
16137ef69edSchristos post_xr, /* Xr */
162f47368cfSchristos NULL, /* %A */
163f47368cfSchristos post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
164f47368cfSchristos NULL, /* %D */
165f47368cfSchristos NULL, /* %I */
166f47368cfSchristos NULL, /* %J */
167f47368cfSchristos post_hyph, /* %N */
168f47368cfSchristos post_hyph, /* %O */
169f47368cfSchristos NULL, /* %P */
170f47368cfSchristos post_hyph, /* %R */
171f47368cfSchristos post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
172f47368cfSchristos NULL, /* %V */
173f47368cfSchristos NULL, /* Ac */
174f47368cfSchristos NULL, /* Ao */
17514e7489eSchristos post_delim_nb, /* Aq */
176f47368cfSchristos post_at, /* At */
177f47368cfSchristos NULL, /* Bc */
178f47368cfSchristos post_bf, /* Bf */
179f47368cfSchristos NULL, /* Bo */
180f47368cfSchristos NULL, /* Bq */
18137ef69edSchristos post_xx, /* Bsx */
182f47368cfSchristos post_bx, /* Bx */
183f47368cfSchristos post_obsolete, /* Db */
184f47368cfSchristos NULL, /* Dc */
185f47368cfSchristos NULL, /* Do */
186f47368cfSchristos NULL, /* Dq */
187f47368cfSchristos NULL, /* Ec */
188f47368cfSchristos NULL, /* Ef */
18914e7489eSchristos post_delim_nb, /* Em */
190f47368cfSchristos NULL, /* Eo */
19137ef69edSchristos post_xx, /* Fx */
19214e7489eSchristos post_delim_nb, /* Ms */
193f47368cfSchristos NULL, /* No */
194f47368cfSchristos post_ns, /* Ns */
19537ef69edSchristos post_xx, /* Nx */
19637ef69edSchristos post_xx, /* Ox */
197f47368cfSchristos NULL, /* Pc */
198f47368cfSchristos NULL, /* Pf */
199f47368cfSchristos NULL, /* Po */
20014e7489eSchristos post_delim_nb, /* Pq */
201f47368cfSchristos NULL, /* Qc */
20214e7489eSchristos post_delim_nb, /* Ql */
203f47368cfSchristos NULL, /* Qo */
20414e7489eSchristos post_delim_nb, /* Qq */
205f47368cfSchristos NULL, /* Re */
206f47368cfSchristos post_rs, /* Rs */
207f47368cfSchristos NULL, /* Sc */
208f47368cfSchristos NULL, /* So */
20914e7489eSchristos post_delim_nb, /* Sq */
210f47368cfSchristos post_sm, /* Sm */
21114e7489eSchristos post_sx, /* Sx */
21214e7489eSchristos post_delim_nb, /* Sy */
21314e7489eSchristos post_useless, /* Tn */
21437ef69edSchristos post_xx, /* Ux */
215f47368cfSchristos NULL, /* Xc */
216f47368cfSchristos NULL, /* Xo */
217f47368cfSchristos post_fo, /* Fo */
218f47368cfSchristos NULL, /* Fc */
219f47368cfSchristos NULL, /* Oo */
220f47368cfSchristos NULL, /* Oc */
221f47368cfSchristos post_bk, /* Bk */
222f47368cfSchristos NULL, /* Ek */
223f47368cfSchristos post_eoln, /* Bt */
22414e7489eSchristos post_obsolete, /* Hf */
225f47368cfSchristos post_obsolete, /* Fr */
226f47368cfSchristos post_eoln, /* Ud */
227f47368cfSchristos post_lb, /* Lb */
2286167eca2Schristos post_abort, /* Lp */
22914e7489eSchristos post_delim_nb, /* Lk */
230f47368cfSchristos post_defaults, /* Mt */
23114e7489eSchristos post_delim_nb, /* Brq */
232f47368cfSchristos NULL, /* Bro */
233f47368cfSchristos NULL, /* Brc */
234f47368cfSchristos NULL, /* %C */
235f47368cfSchristos post_es, /* Es */
236f47368cfSchristos post_en, /* En */
23737ef69edSchristos post_xx, /* Dx */
238f47368cfSchristos NULL, /* %Q */
239f47368cfSchristos NULL, /* %U */
240f47368cfSchristos NULL, /* Ta */
2414154958bSjoerg };
2424154958bSjoerg
243c0d9444aSjoerg #define RSORD_MAX 14 /* Number of `Rs' blocks. */
244c0d9444aSjoerg
24514e7489eSchristos static const enum roff_tok rsord[RSORD_MAX] = {
246c0d9444aSjoerg MDOC__A,
247c0d9444aSjoerg MDOC__T,
248c0d9444aSjoerg MDOC__B,
249c0d9444aSjoerg MDOC__I,
250c0d9444aSjoerg MDOC__J,
251c0d9444aSjoerg MDOC__R,
252c0d9444aSjoerg MDOC__N,
253c0d9444aSjoerg MDOC__V,
254603fc4ebSjoerg MDOC__U,
255c0d9444aSjoerg MDOC__P,
256c0d9444aSjoerg MDOC__Q,
257c0d9444aSjoerg MDOC__C,
258603fc4ebSjoerg MDOC__D,
259603fc4ebSjoerg MDOC__O
260c0d9444aSjoerg };
261c0d9444aSjoerg
26248741257Sjoerg static const char * const secnames[SEC__MAX] = {
26348741257Sjoerg NULL,
26448741257Sjoerg "NAME",
26548741257Sjoerg "LIBRARY",
26648741257Sjoerg "SYNOPSIS",
26748741257Sjoerg "DESCRIPTION",
2685c413d0cSchristos "CONTEXT",
26948741257Sjoerg "IMPLEMENTATION NOTES",
27048741257Sjoerg "RETURN VALUES",
27148741257Sjoerg "ENVIRONMENT",
27248741257Sjoerg "FILES",
27348741257Sjoerg "EXIT STATUS",
27448741257Sjoerg "EXAMPLES",
27548741257Sjoerg "DIAGNOSTICS",
27648741257Sjoerg "COMPATIBILITY",
27748741257Sjoerg "ERRORS",
27848741257Sjoerg "SEE ALSO",
27948741257Sjoerg "STANDARDS",
28048741257Sjoerg "HISTORY",
28148741257Sjoerg "AUTHORS",
28248741257Sjoerg "CAVEATS",
28348741257Sjoerg "BUGS",
28448741257Sjoerg "SECURITY CONSIDERATIONS",
28548741257Sjoerg NULL
28648741257Sjoerg };
2874154958bSjoerg
2885c413d0cSchristos
2896167eca2Schristos /* Validate the subtree rooted at mdoc->last. */
2905c413d0cSchristos void
mdoc_validate(struct roff_man * mdoc)2916167eca2Schristos mdoc_validate(struct roff_man *mdoc)
2924154958bSjoerg {
29314e7489eSchristos struct roff_node *n, *np;
29414e7489eSchristos const v_post *p;
2954154958bSjoerg
2966167eca2Schristos /*
2976167eca2Schristos * Translate obsolete macros to modern macros first
2986167eca2Schristos * such that later code does not need to look
2996167eca2Schristos * for the obsolete versions.
3006167eca2Schristos */
3016167eca2Schristos
3025c413d0cSchristos n = mdoc->last;
3036167eca2Schristos switch (n->tok) {
3046167eca2Schristos case MDOC_Lp:
3056167eca2Schristos n->tok = MDOC_Pp;
3066167eca2Schristos break;
3076167eca2Schristos case MDOC_Ot:
3086167eca2Schristos post_obsolete(mdoc);
3096167eca2Schristos n->tok = MDOC_Ft;
3106167eca2Schristos break;
3116167eca2Schristos default:
3126167eca2Schristos break;
3136167eca2Schristos }
3146167eca2Schristos
3156167eca2Schristos /*
3166167eca2Schristos * Iterate over all children, recursing into each one
3176167eca2Schristos * in turn, depth-first.
3186167eca2Schristos */
3196167eca2Schristos
320f47368cfSchristos mdoc->last = mdoc->last->child;
321f47368cfSchristos while (mdoc->last != NULL) {
3226167eca2Schristos mdoc_validate(mdoc);
323f47368cfSchristos if (mdoc->last == n)
324f47368cfSchristos mdoc->last = mdoc->last->child;
325f47368cfSchristos else
326f47368cfSchristos mdoc->last = mdoc->last->next;
327f47368cfSchristos }
3284154958bSjoerg
3296167eca2Schristos /* Finally validate the macro itself. */
3306167eca2Schristos
331f47368cfSchristos mdoc->last = n;
332f47368cfSchristos mdoc->next = ROFF_NEXT_SIBLING;
3335c413d0cSchristos switch (n->type) {
334f47368cfSchristos case ROFFT_TEXT:
33514e7489eSchristos np = n->parent;
33637ef69edSchristos if (n->sec != SEC_SYNOPSIS ||
33714e7489eSchristos (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
338f47368cfSchristos check_text(mdoc, n->line, n->pos, n->string);
3396167eca2Schristos if ((n->flags & NODE_NOFILL) == 0 &&
34014e7489eSchristos (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
34114e7489eSchristos np->parent->parent->norm->Bl.type != LIST_diag))
34214e7489eSchristos check_text_em(mdoc, n->line, n->pos, n->string);
34314e7489eSchristos if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
34414e7489eSchristos (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
34514e7489eSchristos check_toptext(mdoc, n->line, n->pos, n->string);
346c0d9444aSjoerg break;
34714e7489eSchristos case ROFFT_COMMENT:
348f47368cfSchristos case ROFFT_EQN:
349f47368cfSchristos case ROFFT_TBL:
350f47368cfSchristos break;
351f47368cfSchristos case ROFFT_ROOT:
3525c413d0cSchristos post_root(mdoc);
353c0d9444aSjoerg break;
354c0d9444aSjoerg default:
355f47368cfSchristos check_args(mdoc, mdoc->last);
3565c413d0cSchristos
3575c413d0cSchristos /*
3585c413d0cSchristos * Closing delimiters are not special at the
3595c413d0cSchristos * beginning of a block, opening delimiters
3605c413d0cSchristos * are not special at the end.
3615c413d0cSchristos */
3625c413d0cSchristos
3635c413d0cSchristos if (n->child != NULL)
36437ef69edSchristos n->child->flags &= ~NODE_DELIMC;
3655c413d0cSchristos if (n->last != NULL)
36637ef69edSchristos n->last->flags &= ~NODE_DELIMO;
3675c413d0cSchristos
3685c413d0cSchristos /* Call the macro's postprocessor. */
3695c413d0cSchristos
37014e7489eSchristos if (n->tok < ROFF_MAX) {
37114e7489eSchristos roff_validate(mdoc);
37214e7489eSchristos break;
37314e7489eSchristos }
37414e7489eSchristos
37514e7489eSchristos assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
3766167eca2Schristos p = mdoc_valids + (n->tok - MDOC_Dd);
3775c413d0cSchristos if (*p)
3785c413d0cSchristos (*p)(mdoc);
379f47368cfSchristos if (mdoc->last == n)
380f47368cfSchristos mdoc_state(mdoc, n);
3815c413d0cSchristos break;
3824154958bSjoerg }
383c0d9444aSjoerg }
384c0d9444aSjoerg
385c0d9444aSjoerg static void
check_args(struct roff_man * mdoc,struct roff_node * n)386f47368cfSchristos check_args(struct roff_man *mdoc, struct roff_node *n)
3874154958bSjoerg {
3884154958bSjoerg int i;
3894154958bSjoerg
3904154958bSjoerg if (NULL == n->args)
391c0d9444aSjoerg return;
3924154958bSjoerg
3934154958bSjoerg assert(n->args->argc);
3944154958bSjoerg for (i = 0; i < (int)n->args->argc; i++)
395603fc4ebSjoerg check_argv(mdoc, n, &n->args->argv[i]);
3964154958bSjoerg }
3974154958bSjoerg
398c0d9444aSjoerg static void
check_argv(struct roff_man * mdoc,struct roff_node * n,struct mdoc_argv * v)399f47368cfSchristos check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
4004154958bSjoerg {
4014154958bSjoerg int i;
4024154958bSjoerg
4034154958bSjoerg for (i = 0; i < (int)v->sz; i++)
404603fc4ebSjoerg check_text(mdoc, v->line, v->pos, v->value[i]);
4054154958bSjoerg }
4064154958bSjoerg
407c0d9444aSjoerg static void
check_text(struct roff_man * mdoc,int ln,int pos,char * p)408f47368cfSchristos check_text(struct roff_man *mdoc, int ln, int pos, char *p)
4094154958bSjoerg {
410b1e8115bSjoerg char *cp;
41182361f10Sjoerg
4126167eca2Schristos if (mdoc->last->flags & NODE_NOFILL)
4131350fe09Sjoerg return;
4141350fe09Sjoerg
4151350fe09Sjoerg for (cp = p; NULL != (p = strchr(p, '\t')); p++)
4166167eca2Schristos mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
4174154958bSjoerg }
4184154958bSjoerg
4195c413d0cSchristos static void
check_text_em(struct roff_man * mdoc,int ln,int pos,char * p)42014e7489eSchristos check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
42114e7489eSchristos {
42214e7489eSchristos const struct roff_node *np, *nn;
42314e7489eSchristos char *cp;
42414e7489eSchristos
42514e7489eSchristos np = mdoc->last->prev;
42614e7489eSchristos nn = mdoc->last->next;
42714e7489eSchristos
42814e7489eSchristos /* Look for em-dashes wrongly encoded as "--". */
42914e7489eSchristos
43014e7489eSchristos for (cp = p; *cp != '\0'; cp++) {
43114e7489eSchristos if (cp[0] != '-' || cp[1] != '-')
43214e7489eSchristos continue;
43314e7489eSchristos cp++;
43414e7489eSchristos
43514e7489eSchristos /* Skip input sequences of more than two '-'. */
43614e7489eSchristos
43714e7489eSchristos if (cp[1] == '-') {
43814e7489eSchristos while (cp[1] == '-')
43914e7489eSchristos cp++;
44014e7489eSchristos continue;
44114e7489eSchristos }
44214e7489eSchristos
44314e7489eSchristos /* Skip "--" directly attached to something else. */
44414e7489eSchristos
44514e7489eSchristos if ((cp - p > 1 && cp[-2] != ' ') ||
44614e7489eSchristos (cp[1] != '\0' && cp[1] != ' '))
44714e7489eSchristos continue;
44814e7489eSchristos
44914e7489eSchristos /* Require a letter right before or right afterwards. */
45014e7489eSchristos
45114e7489eSchristos if ((cp - p > 2 ?
45214e7489eSchristos isalpha((unsigned char)cp[-3]) :
45314e7489eSchristos np != NULL &&
45414e7489eSchristos np->type == ROFFT_TEXT &&
45514e7489eSchristos *np->string != '\0' &&
45614e7489eSchristos isalpha((unsigned char)np->string[
45714e7489eSchristos strlen(np->string) - 1])) ||
45814e7489eSchristos (cp[1] != '\0' && cp[2] != '\0' ?
45914e7489eSchristos isalpha((unsigned char)cp[2]) :
46014e7489eSchristos nn != NULL &&
46114e7489eSchristos nn->type == ROFFT_TEXT &&
46214e7489eSchristos isalpha((unsigned char)*nn->string))) {
4636167eca2Schristos mandoc_msg(MANDOCERR_DASHDASH,
46414e7489eSchristos ln, pos + (int)(cp - p) - 1, NULL);
46514e7489eSchristos break;
46614e7489eSchristos }
46714e7489eSchristos }
46814e7489eSchristos }
46914e7489eSchristos
47014e7489eSchristos static void
check_toptext(struct roff_man * mdoc,int ln,int pos,const char * p)47114e7489eSchristos check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
47214e7489eSchristos {
47314e7489eSchristos const char *cp, *cpr;
47414e7489eSchristos
47514e7489eSchristos if (*p == '\0')
47614e7489eSchristos return;
47714e7489eSchristos
47814e7489eSchristos if ((cp = strstr(p, "OpenBSD")) != NULL)
4796167eca2Schristos mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
48014e7489eSchristos if ((cp = strstr(p, "NetBSD")) != NULL)
4816167eca2Schristos mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
48214e7489eSchristos if ((cp = strstr(p, "FreeBSD")) != NULL)
4836167eca2Schristos mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
48414e7489eSchristos if ((cp = strstr(p, "DragonFly")) != NULL)
4856167eca2Schristos mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
48614e7489eSchristos
48714e7489eSchristos cp = p;
48814e7489eSchristos while ((cp = strstr(cp + 1, "()")) != NULL) {
48914e7489eSchristos for (cpr = cp - 1; cpr >= p; cpr--)
49014e7489eSchristos if (*cpr != '_' && !isalnum((unsigned char)*cpr))
49114e7489eSchristos break;
49214e7489eSchristos if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
49314e7489eSchristos cpr++;
4946167eca2Schristos mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
49514e7489eSchristos "%.*s()", (int)(cp - cpr), cpr);
49614e7489eSchristos }
49714e7489eSchristos }
49814e7489eSchristos }
49914e7489eSchristos
50014e7489eSchristos static void
post_abort(POST_ARGS)5016167eca2Schristos post_abort(POST_ARGS)
5026167eca2Schristos {
5036167eca2Schristos abort();
5046167eca2Schristos }
5056167eca2Schristos
5066167eca2Schristos static void
post_delim(POST_ARGS)50714e7489eSchristos post_delim(POST_ARGS)
50814e7489eSchristos {
50914e7489eSchristos const struct roff_node *nch;
51014e7489eSchristos const char *lc;
51114e7489eSchristos enum mdelim delim;
51214e7489eSchristos enum roff_tok tok;
51314e7489eSchristos
51414e7489eSchristos tok = mdoc->last->tok;
51514e7489eSchristos nch = mdoc->last->last;
51614e7489eSchristos if (nch == NULL || nch->type != ROFFT_TEXT)
51714e7489eSchristos return;
51814e7489eSchristos lc = strchr(nch->string, '\0') - 1;
51914e7489eSchristos if (lc < nch->string)
52014e7489eSchristos return;
52114e7489eSchristos delim = mdoc_isdelim(lc);
52214e7489eSchristos if (delim == DELIM_NONE || delim == DELIM_OPEN)
52314e7489eSchristos return;
52414e7489eSchristos if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
52514e7489eSchristos tok == MDOC_Ss || tok == MDOC_Fo))
52614e7489eSchristos return;
52714e7489eSchristos
5286167eca2Schristos mandoc_msg(MANDOCERR_DELIM, nch->line,
5296167eca2Schristos nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
53014e7489eSchristos nch == mdoc->last->child ? "" : " ...", nch->string);
53114e7489eSchristos }
53214e7489eSchristos
53314e7489eSchristos static void
post_delim_nb(POST_ARGS)53414e7489eSchristos post_delim_nb(POST_ARGS)
53514e7489eSchristos {
53614e7489eSchristos const struct roff_node *nch;
53714e7489eSchristos const char *lc, *cp;
53814e7489eSchristos int nw;
53914e7489eSchristos enum mdelim delim;
54014e7489eSchristos enum roff_tok tok;
54114e7489eSchristos
54214e7489eSchristos /*
54314e7489eSchristos * Find candidates: at least two bytes,
54414e7489eSchristos * the last one a closing or middle delimiter.
54514e7489eSchristos */
54614e7489eSchristos
54714e7489eSchristos tok = mdoc->last->tok;
54814e7489eSchristos nch = mdoc->last->last;
54914e7489eSchristos if (nch == NULL || nch->type != ROFFT_TEXT)
55014e7489eSchristos return;
55114e7489eSchristos lc = strchr(nch->string, '\0') - 1;
55214e7489eSchristos if (lc <= nch->string)
55314e7489eSchristos return;
55414e7489eSchristos delim = mdoc_isdelim(lc);
55514e7489eSchristos if (delim == DELIM_NONE || delim == DELIM_OPEN)
55614e7489eSchristos return;
55714e7489eSchristos
55814e7489eSchristos /*
55914e7489eSchristos * Reduce false positives by allowing various cases.
56014e7489eSchristos */
56114e7489eSchristos
56214e7489eSchristos /* Escaped delimiters. */
56314e7489eSchristos if (lc > nch->string + 1 && lc[-2] == '\\' &&
56414e7489eSchristos (lc[-1] == '&' || lc[-1] == 'e'))
56514e7489eSchristos return;
56614e7489eSchristos
56714e7489eSchristos /* Specific byte sequences. */
56814e7489eSchristos switch (*lc) {
56914e7489eSchristos case ')':
57014e7489eSchristos for (cp = lc; cp >= nch->string; cp--)
57114e7489eSchristos if (*cp == '(')
57214e7489eSchristos return;
57314e7489eSchristos break;
57414e7489eSchristos case '.':
57514e7489eSchristos if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
57614e7489eSchristos return;
57714e7489eSchristos if (lc[-1] == '.')
57814e7489eSchristos return;
57914e7489eSchristos break;
58014e7489eSchristos case ';':
58114e7489eSchristos if (tok == MDOC_Vt)
58214e7489eSchristos return;
58314e7489eSchristos break;
58414e7489eSchristos case '?':
58514e7489eSchristos if (lc[-1] == '?')
58614e7489eSchristos return;
58714e7489eSchristos break;
58814e7489eSchristos case ']':
58914e7489eSchristos for (cp = lc; cp >= nch->string; cp--)
59014e7489eSchristos if (*cp == '[')
59114e7489eSchristos return;
59214e7489eSchristos break;
59314e7489eSchristos case '|':
59414e7489eSchristos if (lc == nch->string + 1 && lc[-1] == '|')
59514e7489eSchristos return;
59614e7489eSchristos default:
59714e7489eSchristos break;
59814e7489eSchristos }
59914e7489eSchristos
60014e7489eSchristos /* Exactly two non-alphanumeric bytes. */
60114e7489eSchristos if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
60214e7489eSchristos return;
60314e7489eSchristos
60414e7489eSchristos /* At least three alphabetic words with a sentence ending. */
60514e7489eSchristos if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
60614e7489eSchristos tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
60714e7489eSchristos nw = 0;
60814e7489eSchristos for (cp = lc - 1; cp >= nch->string; cp--) {
60914e7489eSchristos if (*cp == ' ') {
61014e7489eSchristos nw++;
61114e7489eSchristos if (cp > nch->string && cp[-1] == ',')
61214e7489eSchristos cp--;
61314e7489eSchristos } else if (isalpha((unsigned int)*cp)) {
61414e7489eSchristos if (nw > 1)
61514e7489eSchristos return;
61614e7489eSchristos } else
61714e7489eSchristos break;
61814e7489eSchristos }
61914e7489eSchristos }
62014e7489eSchristos
6216167eca2Schristos mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
6226167eca2Schristos nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
62314e7489eSchristos nch == mdoc->last->child ? "" : " ...", nch->string);
62414e7489eSchristos }
62514e7489eSchristos
62614e7489eSchristos static void
post_bl_norm(POST_ARGS)627f47368cfSchristos post_bl_norm(POST_ARGS)
6284154958bSjoerg {
629f47368cfSchristos struct roff_node *n;
6305c413d0cSchristos struct mdoc_argv *argv, *wa;
6315c413d0cSchristos int i;
6325c413d0cSchristos enum mdocargt mdoclt;
6337574e07eSjoerg enum mdoc_list lt;
6344154958bSjoerg
635f47368cfSchristos n = mdoc->last->parent;
636f47368cfSchristos n->norm->Bl.type = LIST__NONE;
6374154958bSjoerg
6387574e07eSjoerg /*
6397574e07eSjoerg * First figure out which kind of list to use: bind ourselves to
6407574e07eSjoerg * the first mentioned list type and warn about any remaining
6417574e07eSjoerg * ones. If we find no list type, we default to LIST_item.
6427574e07eSjoerg */
6434154958bSjoerg
6445c413d0cSchristos wa = (n->args == NULL) ? NULL : n->args->argv;
6455c413d0cSchristos mdoclt = MDOC_ARG_MAX;
6467574e07eSjoerg for (i = 0; n->args && i < (int)n->args->argc; i++) {
6475c413d0cSchristos argv = n->args->argv + i;
6487574e07eSjoerg lt = LIST__NONE;
6495c413d0cSchristos switch (argv->arg) {
6507574e07eSjoerg /* Set list types. */
6515c413d0cSchristos case MDOC_Bullet:
6527574e07eSjoerg lt = LIST_bullet;
6537574e07eSjoerg break;
6545c413d0cSchristos case MDOC_Dash:
6557574e07eSjoerg lt = LIST_dash;
6567574e07eSjoerg break;
6575c413d0cSchristos case MDOC_Enum:
6587574e07eSjoerg lt = LIST_enum;
6597574e07eSjoerg break;
6605c413d0cSchristos case MDOC_Hyphen:
6617574e07eSjoerg lt = LIST_hyphen;
6627574e07eSjoerg break;
6635c413d0cSchristos case MDOC_Item:
6647574e07eSjoerg lt = LIST_item;
6657574e07eSjoerg break;
6665c413d0cSchristos case MDOC_Tag:
6677574e07eSjoerg lt = LIST_tag;
6687574e07eSjoerg break;
6695c413d0cSchristos case MDOC_Diag:
6707574e07eSjoerg lt = LIST_diag;
6717574e07eSjoerg break;
6725c413d0cSchristos case MDOC_Hang:
6737574e07eSjoerg lt = LIST_hang;
6747574e07eSjoerg break;
6755c413d0cSchristos case MDOC_Ohang:
6767574e07eSjoerg lt = LIST_ohang;
6777574e07eSjoerg break;
6785c413d0cSchristos case MDOC_Inset:
6797574e07eSjoerg lt = LIST_inset;
6807574e07eSjoerg break;
6815c413d0cSchristos case MDOC_Column:
6827574e07eSjoerg lt = LIST_column;
6834154958bSjoerg break;
6847574e07eSjoerg /* Set list arguments. */
6855c413d0cSchristos case MDOC_Compact:
6865c413d0cSchristos if (n->norm->Bl.comp)
6875c413d0cSchristos mandoc_msg(MANDOCERR_ARG_REP,
6886167eca2Schristos argv->line, argv->pos, "Bl -compact");
6895c413d0cSchristos n->norm->Bl.comp = 1;
6900a84adc5Sjoerg break;
6915c413d0cSchristos case MDOC_Width:
6925c413d0cSchristos wa = argv;
6935c413d0cSchristos if (0 == argv->sz) {
6945c413d0cSchristos mandoc_msg(MANDOCERR_ARG_EMPTY,
6956167eca2Schristos argv->line, argv->pos, "Bl -width");
6965c413d0cSchristos n->norm->Bl.width = "0n";
69743ed5f5eSchristos break;
69843ed5f5eSchristos }
6995c413d0cSchristos if (NULL != n->norm->Bl.width)
7006167eca2Schristos mandoc_msg(MANDOCERR_ARG_REP,
7016167eca2Schristos argv->line, argv->pos,
7026167eca2Schristos "Bl -width %s", argv->value[0]);
70314e7489eSchristos rewrite_macro2len(mdoc, argv->value);
7045c413d0cSchristos n->norm->Bl.width = argv->value[0];
7054154958bSjoerg break;
7065c413d0cSchristos case MDOC_Offset:
7075c413d0cSchristos if (0 == argv->sz) {
7085c413d0cSchristos mandoc_msg(MANDOCERR_ARG_EMPTY,
7096167eca2Schristos argv->line, argv->pos, "Bl -offset");
7106c26a9aaSjoerg break;
7116c26a9aaSjoerg }
7125c413d0cSchristos if (NULL != n->norm->Bl.offs)
7136167eca2Schristos mandoc_msg(MANDOCERR_ARG_REP,
7146167eca2Schristos argv->line, argv->pos,
7156167eca2Schristos "Bl -offset %s", argv->value[0]);
71614e7489eSchristos rewrite_macro2len(mdoc, argv->value);
7175c413d0cSchristos n->norm->Bl.offs = argv->value[0];
7184154958bSjoerg break;
7197da9b934Sjoerg default:
7207da9b934Sjoerg continue;
7214154958bSjoerg }
7225c413d0cSchristos if (LIST__NONE == lt)
7235c413d0cSchristos continue;
7245c413d0cSchristos mdoclt = argv->arg;
7256c26a9aaSjoerg
7267574e07eSjoerg /* Check: multiple list types. */
7277574e07eSjoerg
7285c413d0cSchristos if (LIST__NONE != n->norm->Bl.type) {
7296167eca2Schristos mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
7305c413d0cSchristos "Bl -%s", mdoc_argnames[argv->arg]);
7315c413d0cSchristos continue;
73282361f10Sjoerg }
7337574e07eSjoerg
7347574e07eSjoerg /* The list type should come first. */
7357574e07eSjoerg
736c0d9444aSjoerg if (n->norm->Bl.width ||
737c0d9444aSjoerg n->norm->Bl.offs ||
738c0d9444aSjoerg n->norm->Bl.comp)
7396167eca2Schristos mandoc_msg(MANDOCERR_BL_LATETYPE,
7406167eca2Schristos n->line, n->pos, "Bl -%s",
7415c413d0cSchristos mdoc_argnames[n->args->argv[0].arg]);
7427574e07eSjoerg
7435c413d0cSchristos n->norm->Bl.type = lt;
7445c413d0cSchristos if (LIST_column == lt) {
7455c413d0cSchristos n->norm->Bl.ncols = argv->sz;
7465c413d0cSchristos n->norm->Bl.cols = (void *)argv->value;
7475c413d0cSchristos }
7487574e07eSjoerg }
7497574e07eSjoerg
7507574e07eSjoerg /* Allow lists to default to LIST_item. */
7517574e07eSjoerg
752c0d9444aSjoerg if (LIST__NONE == n->norm->Bl.type) {
7536167eca2Schristos mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
754c0d9444aSjoerg n->norm->Bl.type = LIST_item;
75537ef69edSchristos mdoclt = MDOC_Item;
7560a84adc5Sjoerg }
7574154958bSjoerg
7584154958bSjoerg /*
7594154958bSjoerg * Validate the width field. Some list types don't need width
7604154958bSjoerg * types and should be warned about them. Others should have it
761603fc4ebSjoerg * and must also be warned. Yet others have a default and need
762603fc4ebSjoerg * no warning.
7634154958bSjoerg */
7644154958bSjoerg
765c0d9444aSjoerg switch (n->norm->Bl.type) {
7665c413d0cSchristos case LIST_tag:
76714e7489eSchristos if (n->norm->Bl.width == NULL)
7686167eca2Schristos mandoc_msg(MANDOCERR_BL_NOWIDTH,
7695c413d0cSchristos n->line, n->pos, "Bl -tag");
7704154958bSjoerg break;
7715c413d0cSchristos case LIST_column:
7725c413d0cSchristos case LIST_diag:
7735c413d0cSchristos case LIST_ohang:
7745c413d0cSchristos case LIST_inset:
7755c413d0cSchristos case LIST_item:
77614e7489eSchristos if (n->norm->Bl.width != NULL)
7776167eca2Schristos mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
7786167eca2Schristos "Bl -%s", mdoc_argnames[mdoclt]);
77914e7489eSchristos n->norm->Bl.width = NULL;
7807574e07eSjoerg break;
7815c413d0cSchristos case LIST_bullet:
7825c413d0cSchristos case LIST_dash:
7835c413d0cSchristos case LIST_hyphen:
78414e7489eSchristos if (n->norm->Bl.width == NULL)
785603fc4ebSjoerg n->norm->Bl.width = "2n";
786603fc4ebSjoerg break;
7875c413d0cSchristos case LIST_enum:
78814e7489eSchristos if (n->norm->Bl.width == NULL)
789603fc4ebSjoerg n->norm->Bl.width = "3n";
790603fc4ebSjoerg break;
7914154958bSjoerg default:
7924154958bSjoerg break;
7934154958bSjoerg }
7944154958bSjoerg }
7954154958bSjoerg
7965c413d0cSchristos static void
post_bd(POST_ARGS)797f47368cfSchristos post_bd(POST_ARGS)
7984154958bSjoerg {
799f47368cfSchristos struct roff_node *n;
8005c413d0cSchristos struct mdoc_argv *argv;
8015c413d0cSchristos int i;
8026c26a9aaSjoerg enum mdoc_disp dt;
8034154958bSjoerg
804f47368cfSchristos n = mdoc->last;
8056c26a9aaSjoerg for (i = 0; n->args && i < (int)n->args->argc; i++) {
8065c413d0cSchristos argv = n->args->argv + i;
8076c26a9aaSjoerg dt = DISP__NONE;
8086c26a9aaSjoerg
8095c413d0cSchristos switch (argv->arg) {
8105c413d0cSchristos case MDOC_Centred:
8115c413d0cSchristos dt = DISP_centered;
8126c26a9aaSjoerg break;
8135c413d0cSchristos case MDOC_Ragged:
8146c26a9aaSjoerg dt = DISP_ragged;
8156c26a9aaSjoerg break;
8165c413d0cSchristos case MDOC_Unfilled:
8176c26a9aaSjoerg dt = DISP_unfilled;
8186c26a9aaSjoerg break;
8195c413d0cSchristos case MDOC_Filled:
8206c26a9aaSjoerg dt = DISP_filled;
8216c26a9aaSjoerg break;
8225c413d0cSchristos case MDOC_Literal:
8236c26a9aaSjoerg dt = DISP_literal;
8244154958bSjoerg break;
8255c413d0cSchristos case MDOC_File:
8266167eca2Schristos mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL);
8275c413d0cSchristos break;
8285c413d0cSchristos case MDOC_Offset:
8295c413d0cSchristos if (0 == argv->sz) {
8305c413d0cSchristos mandoc_msg(MANDOCERR_ARG_EMPTY,
8316167eca2Schristos argv->line, argv->pos, "Bd -offset");
8324154958bSjoerg break;
8334154958bSjoerg }
8345c413d0cSchristos if (NULL != n->norm->Bd.offs)
8356167eca2Schristos mandoc_msg(MANDOCERR_ARG_REP,
8366167eca2Schristos argv->line, argv->pos,
8376167eca2Schristos "Bd -offset %s", argv->value[0]);
83814e7489eSchristos rewrite_macro2len(mdoc, argv->value);
8395c413d0cSchristos n->norm->Bd.offs = argv->value[0];
8406c26a9aaSjoerg break;
8415c413d0cSchristos case MDOC_Compact:
8425c413d0cSchristos if (n->norm->Bd.comp)
8435c413d0cSchristos mandoc_msg(MANDOCERR_ARG_REP,
8446167eca2Schristos argv->line, argv->pos, "Bd -compact");
8455c413d0cSchristos n->norm->Bd.comp = 1;
8466c26a9aaSjoerg break;
8476c26a9aaSjoerg default:
8486c26a9aaSjoerg abort();
8496c26a9aaSjoerg }
8505c413d0cSchristos if (DISP__NONE == dt)
8515c413d0cSchristos continue;
8526c26a9aaSjoerg
8535c413d0cSchristos if (DISP__NONE == n->norm->Bd.type)
854c0d9444aSjoerg n->norm->Bd.type = dt;
8555c413d0cSchristos else
8566167eca2Schristos mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
8575c413d0cSchristos "Bd -%s", mdoc_argnames[argv->arg]);
8586c26a9aaSjoerg }
8596c26a9aaSjoerg
860c0d9444aSjoerg if (DISP__NONE == n->norm->Bd.type) {
8616167eca2Schristos mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
862c0d9444aSjoerg n->norm->Bd.type = DISP_ragged;
8636c26a9aaSjoerg }
8644154958bSjoerg }
8654154958bSjoerg
86637ef69edSchristos /*
86737ef69edSchristos * Stand-alone line macros.
86837ef69edSchristos */
86937ef69edSchristos
8705c413d0cSchristos static void
post_an_norm(POST_ARGS)871f47368cfSchristos post_an_norm(POST_ARGS)
8724154958bSjoerg {
873f47368cfSchristos struct roff_node *n;
8745c413d0cSchristos struct mdoc_argv *argv;
8755c413d0cSchristos size_t i;
8764154958bSjoerg
877f47368cfSchristos n = mdoc->last;
8785c413d0cSchristos if (n->args == NULL)
8795c413d0cSchristos return;
880c0d9444aSjoerg
8815c413d0cSchristos for (i = 1; i < n->args->argc; i++) {
8825c413d0cSchristos argv = n->args->argv + i;
8836167eca2Schristos mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
8845c413d0cSchristos "An -%s", mdoc_argnames[argv->arg]);
8855c413d0cSchristos }
88682361f10Sjoerg
8875c413d0cSchristos argv = n->args->argv;
8885c413d0cSchristos if (argv->arg == MDOC_Split)
889c0d9444aSjoerg n->norm->An.auth = AUTH_split;
8905c413d0cSchristos else if (argv->arg == MDOC_Nosplit)
891c0d9444aSjoerg n->norm->An.auth = AUTH_nosplit;
89282361f10Sjoerg else
89382361f10Sjoerg abort();
8944154958bSjoerg }
8954154958bSjoerg
8965c413d0cSchristos static void
post_eoln(POST_ARGS)89737ef69edSchristos post_eoln(POST_ARGS)
89837ef69edSchristos {
89937ef69edSchristos struct roff_node *n;
90037ef69edSchristos
90114e7489eSchristos post_useless(mdoc);
90237ef69edSchristos n = mdoc->last;
90337ef69edSchristos if (n->child != NULL)
9046167eca2Schristos mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
90514e7489eSchristos n->pos, "%s %s", roff_name[n->tok], n->child->string);
90637ef69edSchristos
90737ef69edSchristos while (n->child != NULL)
90837ef69edSchristos roff_node_delete(mdoc, n->child);
90937ef69edSchristos
91037ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
91137ef69edSchristos "is currently in beta test." : "currently under development.");
91237ef69edSchristos mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
91337ef69edSchristos mdoc->last = n;
91437ef69edSchristos }
91537ef69edSchristos
91637ef69edSchristos static int
build_list(struct roff_man * mdoc,int tok)91737ef69edSchristos build_list(struct roff_man *mdoc, int tok)
91837ef69edSchristos {
91937ef69edSchristos struct roff_node *n;
92037ef69edSchristos int ic;
92137ef69edSchristos
92237ef69edSchristos n = mdoc->last->next;
92337ef69edSchristos for (ic = 1;; ic++) {
92437ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, tok);
92537ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
9266167eca2Schristos roff_node_relink(mdoc, n);
92737ef69edSchristos n = mdoc->last = mdoc->last->parent;
92837ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
92937ef69edSchristos if (n->next == NULL)
93037ef69edSchristos return ic;
93137ef69edSchristos if (ic > 1 || n->next->next != NULL) {
93237ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, ",");
93337ef69edSchristos mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
93437ef69edSchristos }
93537ef69edSchristos n = mdoc->last->next;
93637ef69edSchristos if (n->next == NULL) {
93737ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "and");
93837ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
93937ef69edSchristos }
94037ef69edSchristos }
94137ef69edSchristos }
94237ef69edSchristos
94337ef69edSchristos static void
post_ex(POST_ARGS)94437ef69edSchristos post_ex(POST_ARGS)
94537ef69edSchristos {
94637ef69edSchristos struct roff_node *n;
94737ef69edSchristos int ic;
94837ef69edSchristos
94937ef69edSchristos post_std(mdoc);
95037ef69edSchristos
95137ef69edSchristos n = mdoc->last;
95237ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
95337ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "The");
95437ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
95537ef69edSchristos
95637ef69edSchristos if (mdoc->last->next != NULL)
95737ef69edSchristos ic = build_list(mdoc, MDOC_Nm);
95837ef69edSchristos else if (mdoc->meta.name != NULL) {
95937ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
96037ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
96137ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
96237ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
96337ef69edSchristos mdoc->last = mdoc->last->parent;
96437ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
96537ef69edSchristos ic = 1;
96637ef69edSchristos } else {
9676167eca2Schristos mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
96837ef69edSchristos ic = 0;
96937ef69edSchristos }
97037ef69edSchristos
97137ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos,
97237ef69edSchristos ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
97337ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
97437ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos,
97537ef69edSchristos "on success, and\\~>0 if an error occurs.");
97637ef69edSchristos mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
97737ef69edSchristos mdoc->last = n;
97837ef69edSchristos }
97937ef69edSchristos
98037ef69edSchristos static void
post_lb(POST_ARGS)98137ef69edSchristos post_lb(POST_ARGS)
98237ef69edSchristos {
98337ef69edSchristos struct roff_node *n;
98437ef69edSchristos const char *p;
98537ef69edSchristos
98614e7489eSchristos post_delim_nb(mdoc);
98714e7489eSchristos
98837ef69edSchristos n = mdoc->last;
98937ef69edSchristos assert(n->child->type == ROFFT_TEXT);
99037ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
99137ef69edSchristos
99237ef69edSchristos if ((p = mdoc_a2lib(n->child->string)) != NULL) {
99337ef69edSchristos n->child->flags |= NODE_NOPRT;
99437ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, p);
99537ef69edSchristos mdoc->last->flags = NODE_NOSRC;
99637ef69edSchristos mdoc->last = n;
99737ef69edSchristos return;
99837ef69edSchristos }
99937ef69edSchristos
10006167eca2Schristos mandoc_msg(MANDOCERR_LB_BAD, n->child->line,
100114e7489eSchristos n->child->pos, "Lb %s", n->child->string);
100214e7489eSchristos
100337ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "library");
100437ef69edSchristos mdoc->last->flags = NODE_NOSRC;
100514e7489eSchristos roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
100637ef69edSchristos mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
100737ef69edSchristos mdoc->last = mdoc->last->next;
100814e7489eSchristos roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
100937ef69edSchristos mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
101037ef69edSchristos mdoc->last = n;
101137ef69edSchristos }
101237ef69edSchristos
101337ef69edSchristos static void
post_rv(POST_ARGS)101437ef69edSchristos post_rv(POST_ARGS)
101537ef69edSchristos {
101637ef69edSchristos struct roff_node *n;
101737ef69edSchristos int ic;
101837ef69edSchristos
101937ef69edSchristos post_std(mdoc);
102037ef69edSchristos
102137ef69edSchristos n = mdoc->last;
102237ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
102337ef69edSchristos if (n->child != NULL) {
102437ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "The");
102537ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
102637ef69edSchristos ic = build_list(mdoc, MDOC_Fn);
102737ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos,
102837ef69edSchristos ic > 1 ? "functions return" : "function returns");
102937ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
103037ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos,
103137ef69edSchristos "the value\\~0 if successful;");
103237ef69edSchristos } else
103337ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
103437ef69edSchristos "completion, the value\\~0 is returned;");
103537ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
103637ef69edSchristos
103737ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
103837ef69edSchristos "the value\\~\\-1 is returned and the global variable");
103937ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
104037ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
104137ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
104237ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "errno");
104337ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
104437ef69edSchristos mdoc->last = mdoc->last->parent;
104537ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
104637ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos,
104737ef69edSchristos "is set to indicate the error.");
104837ef69edSchristos mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
104937ef69edSchristos mdoc->last = n;
105037ef69edSchristos }
105137ef69edSchristos
105237ef69edSchristos static void
post_std(POST_ARGS)1053f47368cfSchristos post_std(POST_ARGS)
10544154958bSjoerg {
1055f47368cfSchristos struct roff_node *n;
10564154958bSjoerg
105714e7489eSchristos post_delim(mdoc);
105814e7489eSchristos
1059f47368cfSchristos n = mdoc->last;
1060f47368cfSchristos if (n->args && n->args->argc == 1)
1061f47368cfSchristos if (n->args->argv[0].arg == MDOC_Std)
10625c413d0cSchristos return;
10634154958bSjoerg
10646167eca2Schristos mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
10656167eca2Schristos "%s", roff_name[n->tok]);
10667574e07eSjoerg }
10677574e07eSjoerg
10685c413d0cSchristos static void
post_st(POST_ARGS)106937ef69edSchristos post_st(POST_ARGS)
107037ef69edSchristos {
107137ef69edSchristos struct roff_node *n, *nch;
107237ef69edSchristos const char *p;
107337ef69edSchristos
107437ef69edSchristos n = mdoc->last;
107537ef69edSchristos nch = n->child;
107637ef69edSchristos assert(nch->type == ROFFT_TEXT);
107737ef69edSchristos
107837ef69edSchristos if ((p = mdoc_a2st(nch->string)) == NULL) {
10796167eca2Schristos mandoc_msg(MANDOCERR_ST_BAD,
108037ef69edSchristos nch->line, nch->pos, "St %s", nch->string);
108137ef69edSchristos roff_node_delete(mdoc, n);
108237ef69edSchristos return;
108337ef69edSchristos }
108437ef69edSchristos
108537ef69edSchristos nch->flags |= NODE_NOPRT;
108637ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
108737ef69edSchristos roff_word_alloc(mdoc, nch->line, nch->pos, p);
108837ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
108937ef69edSchristos mdoc->last= n;
109037ef69edSchristos }
109137ef69edSchristos
109237ef69edSchristos static void
post_obsolete(POST_ARGS)1093f47368cfSchristos post_obsolete(POST_ARGS)
10945c413d0cSchristos {
1095f47368cfSchristos struct roff_node *n;
10965c413d0cSchristos
1097f47368cfSchristos n = mdoc->last;
1098f47368cfSchristos if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
10996167eca2Schristos mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
11006167eca2Schristos "%s", roff_name[n->tok]);
110114e7489eSchristos }
110214e7489eSchristos
110314e7489eSchristos static void
post_useless(POST_ARGS)110414e7489eSchristos post_useless(POST_ARGS)
110514e7489eSchristos {
110614e7489eSchristos struct roff_node *n;
110714e7489eSchristos
110814e7489eSchristos n = mdoc->last;
11096167eca2Schristos mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
11106167eca2Schristos "%s", roff_name[n->tok]);
11115c413d0cSchristos }
11125c413d0cSchristos
111337ef69edSchristos /*
111437ef69edSchristos * Block macros.
111537ef69edSchristos */
111637ef69edSchristos
11175c413d0cSchristos static void
post_bf(POST_ARGS)11184154958bSjoerg post_bf(POST_ARGS)
11194154958bSjoerg {
1120f47368cfSchristos struct roff_node *np, *nch;
11214154958bSjoerg
112282361f10Sjoerg /*
112382361f10Sjoerg * Unlike other data pointers, these are "housed" by the HEAD
112482361f10Sjoerg * element, which contains the goods.
112582361f10Sjoerg */
112682361f10Sjoerg
112782361f10Sjoerg np = mdoc->last;
1128f47368cfSchristos if (np->type != ROFFT_HEAD)
11295c413d0cSchristos return;
11305c413d0cSchristos
1131f47368cfSchristos assert(np->parent->type == ROFFT_BLOCK);
1132f47368cfSchristos assert(np->parent->tok == MDOC_Bf);
11334154958bSjoerg
11345c413d0cSchristos /* Check the number of arguments. */
11354154958bSjoerg
11365c413d0cSchristos nch = np->child;
1137f47368cfSchristos if (np->parent->args == NULL) {
1138f47368cfSchristos if (nch == NULL) {
11396167eca2Schristos mandoc_msg(MANDOCERR_BF_NOFONT,
11405c413d0cSchristos np->line, np->pos, "Bf");
11415c413d0cSchristos return;
1142c0d9444aSjoerg }
11435c413d0cSchristos nch = nch->next;
11445c413d0cSchristos }
1145f47368cfSchristos if (nch != NULL)
11466167eca2Schristos mandoc_msg(MANDOCERR_ARG_EXCESS,
11475c413d0cSchristos nch->line, nch->pos, "Bf ... %s", nch->string);
114882361f10Sjoerg
114982361f10Sjoerg /* Extract argument into data. */
115082361f10Sjoerg
1151f47368cfSchristos if (np->parent->args != NULL) {
1152f47368cfSchristos switch (np->parent->args->argv[0].arg) {
1153f47368cfSchristos case MDOC_Emphasis:
1154c0d9444aSjoerg np->norm->Bf.font = FONT_Em;
1155f47368cfSchristos break;
1156f47368cfSchristos case MDOC_Literal:
1157c0d9444aSjoerg np->norm->Bf.font = FONT_Li;
1158f47368cfSchristos break;
1159f47368cfSchristos case MDOC_Symbolic:
1160c0d9444aSjoerg np->norm->Bf.font = FONT_Sy;
1161f47368cfSchristos break;
1162f47368cfSchristos default:
116382361f10Sjoerg abort();
1164f47368cfSchristos }
11655c413d0cSchristos return;
116682361f10Sjoerg }
116782361f10Sjoerg
116882361f10Sjoerg /* Extract parameter into data. */
116982361f10Sjoerg
1170f47368cfSchristos if ( ! strcmp(np->child->string, "Em"))
1171c0d9444aSjoerg np->norm->Bf.font = FONT_Em;
1172f47368cfSchristos else if ( ! strcmp(np->child->string, "Li"))
1173c0d9444aSjoerg np->norm->Bf.font = FONT_Li;
1174f47368cfSchristos else if ( ! strcmp(np->child->string, "Sy"))
1175c0d9444aSjoerg np->norm->Bf.font = FONT_Sy;
1176c0d9444aSjoerg else
11776167eca2Schristos mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
11786167eca2Schristos np->child->pos, "Bf %s", np->child->string);
11794154958bSjoerg }
11804154958bSjoerg
11815c413d0cSchristos static void
post_fname(POST_ARGS)11825c413d0cSchristos post_fname(POST_ARGS)
11835c413d0cSchristos {
1184f47368cfSchristos const struct roff_node *n;
11855c413d0cSchristos const char *cp;
11865c413d0cSchristos size_t pos;
11874154958bSjoerg
11885c413d0cSchristos n = mdoc->last->child;
11895c413d0cSchristos pos = strcspn(n->string, "()");
11905c413d0cSchristos cp = n->string + pos;
11915c413d0cSchristos if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
11926167eca2Schristos mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos,
11936167eca2Schristos "%s", n->string);
11945c413d0cSchristos }
11955c413d0cSchristos
11965c413d0cSchristos static void
post_fn(POST_ARGS)11975c413d0cSchristos post_fn(POST_ARGS)
11985c413d0cSchristos {
11995c413d0cSchristos
12005c413d0cSchristos post_fname(mdoc);
12015c413d0cSchristos post_fa(mdoc);
12025c413d0cSchristos }
12035c413d0cSchristos
12045c413d0cSchristos static void
post_fo(POST_ARGS)12055c413d0cSchristos post_fo(POST_ARGS)
12065c413d0cSchristos {
1207f47368cfSchristos const struct roff_node *n;
12085c413d0cSchristos
12095c413d0cSchristos n = mdoc->last;
12105c413d0cSchristos
1211f47368cfSchristos if (n->type != ROFFT_HEAD)
12125c413d0cSchristos return;
12135c413d0cSchristos
12145c413d0cSchristos if (n->child == NULL) {
12156167eca2Schristos mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
12165c413d0cSchristos return;
12175c413d0cSchristos }
12185c413d0cSchristos if (n->child != n->last) {
12196167eca2Schristos mandoc_msg(MANDOCERR_ARG_EXCESS,
12205c413d0cSchristos n->child->next->line, n->child->next->pos,
12215c413d0cSchristos "Fo ... %s", n->child->next->string);
1222cde7fc85Schristos while (n->child != n->last) {
12239b35adc8Schristos struct roff_node *p = n->last;
12249b35adc8Schristos roff_node_delete(mdoc, p);
1225cde7fc85Schristos }
1226cde7fc85Schristos
122714e7489eSchristos } else
122814e7489eSchristos post_delim(mdoc);
12295c413d0cSchristos
12305c413d0cSchristos post_fname(mdoc);
12315c413d0cSchristos }
12325c413d0cSchristos
12335c413d0cSchristos static void
post_fa(POST_ARGS)12345c413d0cSchristos post_fa(POST_ARGS)
12355c413d0cSchristos {
1236f47368cfSchristos const struct roff_node *n;
12375c413d0cSchristos const char *cp;
12385c413d0cSchristos
12395c413d0cSchristos for (n = mdoc->last->child; n != NULL; n = n->next) {
12405c413d0cSchristos for (cp = n->string; *cp != '\0'; cp++) {
12415c413d0cSchristos /* Ignore callbacks and alterations. */
12425c413d0cSchristos if (*cp == '(' || *cp == '{')
12435c413d0cSchristos break;
12445c413d0cSchristos if (*cp != ',')
12455c413d0cSchristos continue;
12466167eca2Schristos mandoc_msg(MANDOCERR_FA_COMMA, n->line,
12476167eca2Schristos n->pos + (int)(cp - n->string), "%s", n->string);
12485c413d0cSchristos break;
12495c413d0cSchristos }
12505c413d0cSchristos }
125114e7489eSchristos post_delim_nb(mdoc);
12525c413d0cSchristos }
12535c413d0cSchristos
12545c413d0cSchristos static void
post_nm(POST_ARGS)12554154958bSjoerg post_nm(POST_ARGS)
12564154958bSjoerg {
1257f47368cfSchristos struct roff_node *n;
12585c413d0cSchristos
12595c413d0cSchristos n = mdoc->last;
12605c413d0cSchristos
126114e7489eSchristos if (n->sec == SEC_NAME && n->child != NULL &&
126214e7489eSchristos n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
126314e7489eSchristos mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
126414e7489eSchristos
12656167eca2Schristos if (n->last != NULL && n->last->tok == MDOC_Pp)
12666167eca2Schristos roff_node_relink(mdoc, n->last);
1267c0d9444aSjoerg
126837ef69edSchristos if (mdoc->meta.name == NULL)
1269f47368cfSchristos deroff(&mdoc->meta.name, n);
1270c0d9444aSjoerg
127137ef69edSchristos if (mdoc->meta.name == NULL ||
127237ef69edSchristos (mdoc->lastsec == SEC_NAME && n->child == NULL))
12736167eca2Schristos mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
127437ef69edSchristos
127514e7489eSchristos switch (n->type) {
127614e7489eSchristos case ROFFT_ELEM:
127714e7489eSchristos post_delim_nb(mdoc);
127814e7489eSchristos break;
127914e7489eSchristos case ROFFT_HEAD:
128014e7489eSchristos post_delim(mdoc);
128114e7489eSchristos break;
128214e7489eSchristos default:
128314e7489eSchristos return;
128414e7489eSchristos }
128514e7489eSchristos
128614e7489eSchristos if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
128737ef69edSchristos mdoc->meta.name == NULL)
128837ef69edSchristos return;
128937ef69edSchristos
129037ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
129137ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
129237ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
129337ef69edSchristos mdoc->last = n;
1294c0d9444aSjoerg }
1295c0d9444aSjoerg
12965c413d0cSchristos static void
post_nd(POST_ARGS)12975c413d0cSchristos post_nd(POST_ARGS)
12985c413d0cSchristos {
1299f47368cfSchristos struct roff_node *n;
13005c413d0cSchristos
13015c413d0cSchristos n = mdoc->last;
13025c413d0cSchristos
1303f47368cfSchristos if (n->type != ROFFT_BODY)
13045c413d0cSchristos return;
13055c413d0cSchristos
130614e7489eSchristos if (n->sec != SEC_NAME)
13076167eca2Schristos mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
130814e7489eSchristos
13095c413d0cSchristos if (n->child == NULL)
13106167eca2Schristos mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
131114e7489eSchristos else
131214e7489eSchristos post_delim(mdoc);
13135c413d0cSchristos
13145c413d0cSchristos post_hyph(mdoc);
13155c413d0cSchristos }
13165c413d0cSchristos
13175c413d0cSchristos static void
post_display(POST_ARGS)1318f47368cfSchristos post_display(POST_ARGS)
13195c413d0cSchristos {
1320f47368cfSchristos struct roff_node *n, *np;
13215c413d0cSchristos
13225c413d0cSchristos n = mdoc->last;
1323f47368cfSchristos switch (n->type) {
1324f47368cfSchristos case ROFFT_BODY:
132537ef69edSchristos if (n->end != ENDBODY_NOT) {
132637ef69edSchristos if (n->tok == MDOC_Bd &&
132737ef69edSchristos n->body->parent->args == NULL)
132837ef69edSchristos roff_node_delete(mdoc, n);
132937ef69edSchristos } else if (n->child == NULL)
13306167eca2Schristos mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
13316167eca2Schristos "%s", roff_name[n->tok]);
1332f47368cfSchristos else if (n->tok == MDOC_D1)
1333f47368cfSchristos post_hyph(mdoc);
1334f47368cfSchristos break;
1335f47368cfSchristos case ROFFT_BLOCK:
1336f47368cfSchristos if (n->tok == MDOC_Bd) {
1337f47368cfSchristos if (n->args == NULL) {
1338f47368cfSchristos mandoc_msg(MANDOCERR_BD_NOARG,
13396167eca2Schristos n->line, n->pos, "Bd");
1340f47368cfSchristos mdoc->next = ROFF_NEXT_SIBLING;
1341f47368cfSchristos while (n->body->child != NULL)
13426167eca2Schristos roff_node_relink(mdoc,
1343f47368cfSchristos n->body->child);
1344f47368cfSchristos roff_node_delete(mdoc, n);
1345f47368cfSchristos break;
1346f47368cfSchristos }
1347f47368cfSchristos post_bd(mdoc);
1348f47368cfSchristos post_prevpar(mdoc);
1349f47368cfSchristos }
1350f47368cfSchristos for (np = n->parent; np != NULL; np = np->parent) {
1351f47368cfSchristos if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
13526167eca2Schristos mandoc_msg(MANDOCERR_BD_NEST, n->line,
13536167eca2Schristos n->pos, "%s in Bd", roff_name[n->tok]);
1354f47368cfSchristos break;
1355f47368cfSchristos }
1356f47368cfSchristos }
1357f47368cfSchristos break;
1358f47368cfSchristos default:
1359f47368cfSchristos break;
1360f47368cfSchristos }
1361c0d9444aSjoerg }
1362c0d9444aSjoerg
13635c413d0cSchristos static void
post_defaults(POST_ARGS)1364c0d9444aSjoerg post_defaults(POST_ARGS)
1365c0d9444aSjoerg {
1366f47368cfSchristos struct roff_node *nn;
1367c0d9444aSjoerg
136814e7489eSchristos if (mdoc->last->child != NULL) {
136914e7489eSchristos post_delim_nb(mdoc);
137014e7489eSchristos return;
137114e7489eSchristos }
137214e7489eSchristos
1373c0d9444aSjoerg /*
1374c0d9444aSjoerg * The `Ar' defaults to "file ..." if no value is provided as an
1375c0d9444aSjoerg * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1376c0d9444aSjoerg * gets an empty string.
1377c0d9444aSjoerg */
13784154958bSjoerg
1379c0d9444aSjoerg nn = mdoc->last;
1380c0d9444aSjoerg switch (nn->tok) {
13815c413d0cSchristos case MDOC_Ar:
1382f47368cfSchristos mdoc->next = ROFF_NEXT_CHILD;
1383f47368cfSchristos roff_word_alloc(mdoc, nn->line, nn->pos, "file");
138437ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
1385f47368cfSchristos roff_word_alloc(mdoc, nn->line, nn->pos, "...");
138637ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
1387c0d9444aSjoerg break;
13885c413d0cSchristos case MDOC_Pa:
13895c413d0cSchristos case MDOC_Mt:
1390f47368cfSchristos mdoc->next = ROFF_NEXT_CHILD;
1391f47368cfSchristos roff_word_alloc(mdoc, nn->line, nn->pos, "~");
139237ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
1393c0d9444aSjoerg break;
1394c0d9444aSjoerg default:
1395c0d9444aSjoerg abort();
13964154958bSjoerg }
1397c0d9444aSjoerg mdoc->last = nn;
1398c0d9444aSjoerg }
13994154958bSjoerg
14005c413d0cSchristos static void
post_at(POST_ARGS)14014154958bSjoerg post_at(POST_ARGS)
14024154958bSjoerg {
140337ef69edSchristos struct roff_node *n, *nch;
140437ef69edSchristos const char *att;
14055c413d0cSchristos
14065c413d0cSchristos n = mdoc->last;
140737ef69edSchristos nch = n->child;
1408c0d9444aSjoerg
1409c0d9444aSjoerg /*
1410c0d9444aSjoerg * If we have a child, look it up in the standard keys. If a
1411c0d9444aSjoerg * key exist, use that instead of the child; if it doesn't,
1412c0d9444aSjoerg * prefix "AT&T UNIX " to the existing data.
1413c0d9444aSjoerg */
14144154958bSjoerg
141537ef69edSchristos att = NULL;
141637ef69edSchristos if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
14176167eca2Schristos mandoc_msg(MANDOCERR_AT_BAD,
141837ef69edSchristos nch->line, nch->pos, "At %s", nch->string);
1419c0d9444aSjoerg
142037ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
142137ef69edSchristos if (att != NULL) {
142237ef69edSchristos roff_word_alloc(mdoc, nch->line, nch->pos, att);
142337ef69edSchristos nch->flags |= NODE_NOPRT;
142437ef69edSchristos } else
142537ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
142637ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
142737ef69edSchristos mdoc->last = n;
14284154958bSjoerg }
14294154958bSjoerg
14305c413d0cSchristos static void
post_an(POST_ARGS)14314154958bSjoerg post_an(POST_ARGS)
14324154958bSjoerg {
1433f47368cfSchristos struct roff_node *np, *nch;
1434f47368cfSchristos
1435f47368cfSchristos post_an_norm(mdoc);
14364154958bSjoerg
143782361f10Sjoerg np = mdoc->last;
14385c413d0cSchristos nch = np->child;
14395c413d0cSchristos if (np->norm->An.auth == AUTH__NONE) {
14405c413d0cSchristos if (nch == NULL)
14416167eca2Schristos mandoc_msg(MANDOCERR_MACRO_EMPTY,
14425c413d0cSchristos np->line, np->pos, "An");
144314e7489eSchristos else
144414e7489eSchristos post_delim_nb(mdoc);
14455c413d0cSchristos } else if (nch != NULL)
14466167eca2Schristos mandoc_msg(MANDOCERR_ARG_EXCESS,
14475c413d0cSchristos nch->line, nch->pos, "An ... %s", nch->string);
14484154958bSjoerg }
14494154958bSjoerg
14505c413d0cSchristos static void
post_en(POST_ARGS)14515c413d0cSchristos post_en(POST_ARGS)
14525c413d0cSchristos {
14534154958bSjoerg
1454f47368cfSchristos post_obsolete(mdoc);
1455f47368cfSchristos if (mdoc->last->type == ROFFT_BLOCK)
14565c413d0cSchristos mdoc->last->norm->Es = mdoc->last_es;
14575c413d0cSchristos }
14585c413d0cSchristos
14595c413d0cSchristos static void
post_es(POST_ARGS)14605c413d0cSchristos post_es(POST_ARGS)
14615c413d0cSchristos {
14625c413d0cSchristos
1463f47368cfSchristos post_obsolete(mdoc);
14645c413d0cSchristos mdoc->last_es = mdoc->last;
14655c413d0cSchristos }
14665c413d0cSchristos
14675c413d0cSchristos static void
post_xx(POST_ARGS)146837ef69edSchristos post_xx(POST_ARGS)
146937ef69edSchristos {
147037ef69edSchristos struct roff_node *n;
147137ef69edSchristos const char *os;
147214e7489eSchristos char *v;
147314e7489eSchristos
147414e7489eSchristos post_delim_nb(mdoc);
147537ef69edSchristos
147637ef69edSchristos n = mdoc->last;
147737ef69edSchristos switch (n->tok) {
147837ef69edSchristos case MDOC_Bsx:
147937ef69edSchristos os = "BSD/OS";
148037ef69edSchristos break;
148137ef69edSchristos case MDOC_Dx:
148237ef69edSchristos os = "DragonFly";
148337ef69edSchristos break;
148437ef69edSchristos case MDOC_Fx:
148537ef69edSchristos os = "FreeBSD";
148637ef69edSchristos break;
148737ef69edSchristos case MDOC_Nx:
148837ef69edSchristos os = "NetBSD";
148914e7489eSchristos if (n->child == NULL)
149014e7489eSchristos break;
149114e7489eSchristos v = n->child->string;
149214e7489eSchristos if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
149314e7489eSchristos v[2] < '0' || v[2] > '9' ||
149414e7489eSchristos v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
149514e7489eSchristos break;
149614e7489eSchristos n->child->flags |= NODE_NOPRT;
149714e7489eSchristos mdoc->next = ROFF_NEXT_CHILD;
149814e7489eSchristos roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
149914e7489eSchristos v = mdoc->last->string;
150014e7489eSchristos v[3] = toupper((unsigned char)v[3]);
150114e7489eSchristos mdoc->last->flags |= NODE_NOSRC;
150214e7489eSchristos mdoc->last = n;
150337ef69edSchristos break;
150437ef69edSchristos case MDOC_Ox:
150537ef69edSchristos os = "OpenBSD";
150637ef69edSchristos break;
150737ef69edSchristos case MDOC_Ux:
150837ef69edSchristos os = "UNIX";
150937ef69edSchristos break;
151037ef69edSchristos default:
151137ef69edSchristos abort();
151237ef69edSchristos }
151337ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
151437ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, os);
151537ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
151637ef69edSchristos mdoc->last = n;
151737ef69edSchristos }
151837ef69edSchristos
151937ef69edSchristos static void
post_it(POST_ARGS)15204154958bSjoerg post_it(POST_ARGS)
15214154958bSjoerg {
1522f47368cfSchristos struct roff_node *nbl, *nit, *nch;
152348741257Sjoerg int i, cols;
15247574e07eSjoerg enum mdoc_list lt;
1525f47368cfSchristos
1526f47368cfSchristos post_prevpar(mdoc);
15274154958bSjoerg
15285c413d0cSchristos nit = mdoc->last;
1529f47368cfSchristos if (nit->type != ROFFT_BLOCK)
15305c413d0cSchristos return;
15314154958bSjoerg
15325c413d0cSchristos nbl = nit->parent->parent;
15335c413d0cSchristos lt = nbl->norm->Bl.type;
15344154958bSjoerg
15357574e07eSjoerg switch (lt) {
15365c413d0cSchristos case LIST_tag:
15375c413d0cSchristos case LIST_hang:
15385c413d0cSchristos case LIST_ohang:
15395c413d0cSchristos case LIST_inset:
15405c413d0cSchristos case LIST_diag:
15415c413d0cSchristos if (nit->head->child == NULL)
15426167eca2Schristos mandoc_msg(MANDOCERR_IT_NOHEAD,
15436167eca2Schristos nit->line, nit->pos, "Bl -%s It",
15445c413d0cSchristos mdoc_argnames[nbl->args->argv[0].arg]);
15454154958bSjoerg break;
15465c413d0cSchristos case LIST_bullet:
15475c413d0cSchristos case LIST_dash:
15485c413d0cSchristos case LIST_enum:
15495c413d0cSchristos case LIST_hyphen:
15505c413d0cSchristos if (nit->body == NULL || nit->body->child == NULL)
15516167eca2Schristos mandoc_msg(MANDOCERR_IT_NOBODY,
15526167eca2Schristos nit->line, nit->pos, "Bl -%s It",
15535c413d0cSchristos mdoc_argnames[nbl->args->argv[0].arg]);
15545c413d0cSchristos /* FALLTHROUGH */
15555c413d0cSchristos case LIST_item:
155637ef69edSchristos if ((nch = nit->head->child) != NULL)
15576167eca2Schristos mandoc_msg(MANDOCERR_ARG_SKIP,
155814e7489eSchristos nit->line, nit->pos, "It %s",
155914e7489eSchristos nch->string == NULL ? roff_name[nch->tok] :
156014e7489eSchristos nch->string);
15614154958bSjoerg break;
15625c413d0cSchristos case LIST_column:
15635c413d0cSchristos cols = (int)nbl->norm->Bl.ncols;
15647574e07eSjoerg
15655c413d0cSchristos assert(nit->head->child == NULL);
15667574e07eSjoerg
156714e7489eSchristos if (nit->head->next->child == NULL &&
156814e7489eSchristos nit->head->next->next == NULL) {
15696167eca2Schristos mandoc_msg(MANDOCERR_MACRO_EMPTY,
157014e7489eSchristos nit->line, nit->pos, "It");
157114e7489eSchristos roff_node_delete(mdoc, nit);
157214e7489eSchristos break;
157314e7489eSchristos }
15744154958bSjoerg
157514e7489eSchristos i = 0;
157614e7489eSchristos for (nch = nit->child; nch != NULL; nch = nch->next) {
157714e7489eSchristos if (nch->type != ROFFT_BODY)
157814e7489eSchristos continue;
157914e7489eSchristos if (i++ && nch->flags & NODE_LINE)
15806167eca2Schristos mandoc_msg(MANDOCERR_TA_LINE,
158114e7489eSchristos nch->line, nch->pos, "Ta");
158214e7489eSchristos }
15835c413d0cSchristos if (i < cols || i > cols + 1)
15846167eca2Schristos mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
15855c413d0cSchristos "%d columns, %d cells", cols, i);
158614e7489eSchristos else if (nit->head->next->child != NULL &&
15876167eca2Schristos nit->head->next->child->flags & NODE_LINE)
15886167eca2Schristos mandoc_msg(MANDOCERR_IT_NOARG,
158914e7489eSchristos nit->line, nit->pos, "Bl -column It");
15904154958bSjoerg break;
15914154958bSjoerg default:
15925c413d0cSchristos abort();
15935c413d0cSchristos }
15944154958bSjoerg }
15954154958bSjoerg
15965c413d0cSchristos static void
post_bl_block(POST_ARGS)1597c0d9444aSjoerg post_bl_block(POST_ARGS)
1598c0d9444aSjoerg {
1599f47368cfSchristos struct roff_node *n, *ni, *nc;
1600f47368cfSchristos
1601f47368cfSchristos post_prevpar(mdoc);
1602c0d9444aSjoerg
1603c0d9444aSjoerg n = mdoc->last;
1604f47368cfSchristos for (ni = n->body->child; ni != NULL; ni = ni->next) {
1605f47368cfSchristos if (ni->body == NULL)
1606603fc4ebSjoerg continue;
1607603fc4ebSjoerg nc = ni->body->last;
1608f47368cfSchristos while (nc != NULL) {
1609603fc4ebSjoerg switch (nc->tok) {
16105c413d0cSchristos case MDOC_Pp:
161114e7489eSchristos case ROFF_br:
1612603fc4ebSjoerg break;
1613603fc4ebSjoerg default:
1614603fc4ebSjoerg nc = NULL;
1615603fc4ebSjoerg continue;
1616603fc4ebSjoerg }
1617f47368cfSchristos if (ni->next == NULL) {
16186167eca2Schristos mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
16196167eca2Schristos nc->pos, "%s", roff_name[nc->tok]);
16206167eca2Schristos roff_node_relink(mdoc, nc);
1621f47368cfSchristos } else if (n->norm->Bl.comp == 0 &&
1622f47368cfSchristos n->norm->Bl.type != LIST_column) {
16236167eca2Schristos mandoc_msg(MANDOCERR_PAR_SKIP,
16246167eca2Schristos nc->line, nc->pos,
162514e7489eSchristos "%s before It", roff_name[nc->tok]);
1626f47368cfSchristos roff_node_delete(mdoc, nc);
1627603fc4ebSjoerg } else
1628603fc4ebSjoerg break;
1629603fc4ebSjoerg nc = ni->body->last;
1630603fc4ebSjoerg }
1631603fc4ebSjoerg }
1632c0d9444aSjoerg }
1633c0d9444aSjoerg
1634c0d9444aSjoerg /*
16355c413d0cSchristos * If the argument of -offset or -width is a macro,
16365c413d0cSchristos * replace it with the associated default width.
1637c0d9444aSjoerg */
163814e7489eSchristos static void
rewrite_macro2len(struct roff_man * mdoc,char ** arg)163914e7489eSchristos rewrite_macro2len(struct roff_man *mdoc, char **arg)
16405c413d0cSchristos {
16415c413d0cSchristos size_t width;
164214e7489eSchristos enum roff_tok tok;
1643c0d9444aSjoerg
16445c413d0cSchristos if (*arg == NULL)
16455c413d0cSchristos return;
16465c413d0cSchristos else if ( ! strcmp(*arg, "Ds"))
1647c0d9444aSjoerg width = 6;
164814e7489eSchristos else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
16495c413d0cSchristos return;
16505c413d0cSchristos else
16515c413d0cSchristos width = macro2len(tok);
16525c413d0cSchristos
16535c413d0cSchristos free(*arg);
16545c413d0cSchristos mandoc_asprintf(arg, "%zun", width);
1655c0d9444aSjoerg }
1656c0d9444aSjoerg
16575c413d0cSchristos static void
post_bl_head(POST_ARGS)16584154958bSjoerg post_bl_head(POST_ARGS)
16594154958bSjoerg {
1660f47368cfSchristos struct roff_node *nbl, *nh, *nch, *nnext;
16615c413d0cSchristos struct mdoc_argv *argv;
1662c0d9444aSjoerg int i, j;
16634154958bSjoerg
1664f47368cfSchristos post_bl_norm(mdoc);
16655c413d0cSchristos
1666f47368cfSchristos nh = mdoc->last;
16675c413d0cSchristos if (nh->norm->Bl.type != LIST_column) {
16685c413d0cSchristos if ((nch = nh->child) == NULL)
16695c413d0cSchristos return;
16706167eca2Schristos mandoc_msg(MANDOCERR_ARG_EXCESS,
16715c413d0cSchristos nch->line, nch->pos, "Bl ... %s", nch->string);
16725c413d0cSchristos while (nch != NULL) {
1673f47368cfSchristos roff_node_delete(mdoc, nch);
16745c413d0cSchristos nch = nh->child;
16755c413d0cSchristos }
16765c413d0cSchristos return;
16775c413d0cSchristos }
16784154958bSjoerg
1679c0d9444aSjoerg /*
16805c413d0cSchristos * Append old-style lists, where the column width specifiers
1681c0d9444aSjoerg * trail as macro parameters, to the new-style ("normal-form")
1682c0d9444aSjoerg * lists where they're argument values following -column.
1683c0d9444aSjoerg */
1684c0d9444aSjoerg
16855c413d0cSchristos if (nh->child == NULL)
16865c413d0cSchristos return;
1687c0d9444aSjoerg
16885c413d0cSchristos nbl = nh->parent;
16895c413d0cSchristos for (j = 0; j < (int)nbl->args->argc; j++)
16905c413d0cSchristos if (nbl->args->argv[j].arg == MDOC_Column)
1691c0d9444aSjoerg break;
1692c0d9444aSjoerg
16935c413d0cSchristos assert(j < (int)nbl->args->argc);
1694c0d9444aSjoerg
1695c0d9444aSjoerg /*
1696b1e8115bSjoerg * Accommodate for new-style groff column syntax. Shuffle the
1697c0d9444aSjoerg * child nodes, all of which must be TEXT, as arguments for the
1698c0d9444aSjoerg * column field. Then, delete the head children.
1699c0d9444aSjoerg */
1700c0d9444aSjoerg
17015c413d0cSchristos argv = nbl->args->argv + j;
17025c413d0cSchristos i = argv->sz;
1703f47368cfSchristos for (nch = nh->child; nch != NULL; nch = nch->next)
1704f47368cfSchristos argv->sz++;
17055c413d0cSchristos argv->value = mandoc_reallocarray(argv->value,
17065c413d0cSchristos argv->sz, sizeof(char *));
1707c0d9444aSjoerg
17085c413d0cSchristos nh->norm->Bl.ncols = argv->sz;
17095c413d0cSchristos nh->norm->Bl.cols = (void *)argv->value;
1710c0d9444aSjoerg
17115c413d0cSchristos for (nch = nh->child; nch != NULL; nch = nnext) {
17125c413d0cSchristos argv->value[i++] = nch->string;
17135c413d0cSchristos nch->string = NULL;
17145c413d0cSchristos nnext = nch->next;
1715f47368cfSchristos roff_node_delete(NULL, nch);
17165c413d0cSchristos }
17175c413d0cSchristos nh->child = NULL;
17187bcc2a5fSjoerg }
17197bcc2a5fSjoerg
17205c413d0cSchristos static void
post_bl(POST_ARGS)17214154958bSjoerg post_bl(POST_ARGS)
17224154958bSjoerg {
1723f47368cfSchristos struct roff_node *nparent, *nprev; /* of the Bl block */
1724f47368cfSchristos struct roff_node *nblock, *nbody; /* of the Bl */
1725f47368cfSchristos struct roff_node *nchild, *nnext; /* of the Bl body */
172614e7489eSchristos const char *prev_Er;
172714e7489eSchristos int order;
17284154958bSjoerg
1729603fc4ebSjoerg nbody = mdoc->last;
1730603fc4ebSjoerg switch (nbody->type) {
1731f47368cfSchristos case ROFFT_BLOCK:
17325c413d0cSchristos post_bl_block(mdoc);
17335c413d0cSchristos return;
1734f47368cfSchristos case ROFFT_HEAD:
17355c413d0cSchristos post_bl_head(mdoc);
17365c413d0cSchristos return;
1737f47368cfSchristos case ROFFT_BODY:
1738c0d9444aSjoerg break;
1739603fc4ebSjoerg default:
17405c413d0cSchristos return;
1741c0d9444aSjoerg }
1742f47368cfSchristos if (nbody->end != ENDBODY_NOT)
1743f47368cfSchristos return;
1744c0d9444aSjoerg
1745603fc4ebSjoerg nchild = nbody->child;
17465c413d0cSchristos if (nchild == NULL) {
17476167eca2Schristos mandoc_msg(MANDOCERR_BLK_EMPTY,
17485c413d0cSchristos nbody->line, nbody->pos, "Bl");
17495c413d0cSchristos return;
17505c413d0cSchristos }
17515c413d0cSchristos while (nchild != NULL) {
175237ef69edSchristos nnext = nchild->next;
17535c413d0cSchristos if (nchild->tok == MDOC_It ||
17545c413d0cSchristos (nchild->tok == MDOC_Sm &&
175537ef69edSchristos nnext != NULL && nnext->tok == MDOC_It)) {
175637ef69edSchristos nchild = nnext;
175737ef69edSchristos continue;
175837ef69edSchristos }
175937ef69edSchristos
176037ef69edSchristos /*
176137ef69edSchristos * In .Bl -column, the first rows may be implicit,
176237ef69edSchristos * that is, they may not start with .It macros.
176337ef69edSchristos * Such rows may be followed by nodes generated on the
176437ef69edSchristos * roff level, for example .TS, which cannot be moved
176537ef69edSchristos * out of the list. In that case, wrap such roff nodes
176637ef69edSchristos * into an implicit row.
176737ef69edSchristos */
176837ef69edSchristos
176937ef69edSchristos if (nchild->prev != NULL) {
177037ef69edSchristos mdoc->last = nchild;
177137ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
177237ef69edSchristos roff_block_alloc(mdoc, nchild->line,
177337ef69edSchristos nchild->pos, MDOC_It);
177437ef69edSchristos roff_head_alloc(mdoc, nchild->line,
177537ef69edSchristos nchild->pos, MDOC_It);
177637ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
177737ef69edSchristos roff_body_alloc(mdoc, nchild->line,
177837ef69edSchristos nchild->pos, MDOC_It);
177937ef69edSchristos while (nchild->tok != MDOC_It) {
17806167eca2Schristos roff_node_relink(mdoc, nchild);
178137ef69edSchristos if ((nchild = nnext) == NULL)
178237ef69edSchristos break;
178337ef69edSchristos nnext = nchild->next;
178437ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
178537ef69edSchristos }
178637ef69edSchristos mdoc->last = nbody;
1787603fc4ebSjoerg continue;
1788603fc4ebSjoerg }
1789603fc4ebSjoerg
17906167eca2Schristos mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos,
17916167eca2Schristos "%s", roff_name[nchild->tok]);
1792603fc4ebSjoerg
1793603fc4ebSjoerg /*
1794603fc4ebSjoerg * Move the node out of the Bl block.
1795603fc4ebSjoerg * First, collect all required node pointers.
1796603fc4ebSjoerg */
1797603fc4ebSjoerg
1798603fc4ebSjoerg nblock = nbody->parent;
1799603fc4ebSjoerg nprev = nblock->prev;
1800603fc4ebSjoerg nparent = nblock->parent;
1801603fc4ebSjoerg
1802603fc4ebSjoerg /*
1803603fc4ebSjoerg * Unlink this child.
1804603fc4ebSjoerg */
1805603fc4ebSjoerg
1806603fc4ebSjoerg nbody->child = nnext;
1807f47368cfSchristos if (nnext == NULL)
1808f47368cfSchristos nbody->last = NULL;
1809f47368cfSchristos else
1810603fc4ebSjoerg nnext->prev = NULL;
1811603fc4ebSjoerg
1812603fc4ebSjoerg /*
1813603fc4ebSjoerg * Relink this child.
1814603fc4ebSjoerg */
1815603fc4ebSjoerg
1816603fc4ebSjoerg nchild->parent = nparent;
1817603fc4ebSjoerg nchild->prev = nprev;
1818603fc4ebSjoerg nchild->next = nblock;
1819603fc4ebSjoerg
1820603fc4ebSjoerg nblock->prev = nchild;
1821f47368cfSchristos if (nprev == NULL)
1822603fc4ebSjoerg nparent->child = nchild;
1823603fc4ebSjoerg else
1824603fc4ebSjoerg nprev->next = nchild;
1825603fc4ebSjoerg
1826603fc4ebSjoerg nchild = nnext;
18274154958bSjoerg }
182814e7489eSchristos
182914e7489eSchristos if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
183014e7489eSchristos return;
183114e7489eSchristos
183214e7489eSchristos prev_Er = NULL;
183314e7489eSchristos for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
183414e7489eSchristos if (nchild->tok != MDOC_It)
183514e7489eSchristos continue;
183614e7489eSchristos if ((nnext = nchild->head->child) == NULL)
183714e7489eSchristos continue;
183814e7489eSchristos if (nnext->type == ROFFT_BLOCK)
183914e7489eSchristos nnext = nnext->body->child;
184014e7489eSchristos if (nnext == NULL || nnext->tok != MDOC_Er)
184114e7489eSchristos continue;
184214e7489eSchristos nnext = nnext->child;
184314e7489eSchristos if (prev_Er != NULL) {
184414e7489eSchristos order = strcmp(prev_Er, nnext->string);
184514e7489eSchristos if (order > 0)
18466167eca2Schristos mandoc_msg(MANDOCERR_ER_ORDER,
18476167eca2Schristos nnext->line, nnext->pos,
184814e7489eSchristos "Er %s %s (NetBSD)",
184914e7489eSchristos prev_Er, nnext->string);
185014e7489eSchristos else if (order == 0)
18516167eca2Schristos mandoc_msg(MANDOCERR_ER_REP,
18526167eca2Schristos nnext->line, nnext->pos,
185314e7489eSchristos "Er %s (NetBSD)", prev_Er);
185414e7489eSchristos }
185514e7489eSchristos prev_Er = nnext->string;
185614e7489eSchristos }
18574154958bSjoerg }
18584154958bSjoerg
18595c413d0cSchristos static void
post_bk(POST_ARGS)18605c413d0cSchristos post_bk(POST_ARGS)
18614154958bSjoerg {
1862f47368cfSchristos struct roff_node *n;
18634154958bSjoerg
18645c413d0cSchristos n = mdoc->last;
1865c0d9444aSjoerg
1866f47368cfSchristos if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
18676167eca2Schristos mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
1868f47368cfSchristos roff_node_delete(mdoc, n);
18695c413d0cSchristos }
18704154958bSjoerg }
18714154958bSjoerg
18725c413d0cSchristos static void
post_sm(POST_ARGS)1873f47368cfSchristos post_sm(POST_ARGS)
18745c413d0cSchristos {
1875f47368cfSchristos struct roff_node *nch;
18765c413d0cSchristos
18775c413d0cSchristos nch = mdoc->last->child;
18785c413d0cSchristos
18795c413d0cSchristos if (nch == NULL) {
18805c413d0cSchristos mdoc->flags ^= MDOC_SMOFF;
18815c413d0cSchristos return;
18825c413d0cSchristos }
18835c413d0cSchristos
1884f47368cfSchristos assert(nch->type == ROFFT_TEXT);
18855c413d0cSchristos
18865c413d0cSchristos if ( ! strcmp(nch->string, "on")) {
18875c413d0cSchristos mdoc->flags &= ~MDOC_SMOFF;
18885c413d0cSchristos return;
18895c413d0cSchristos }
18905c413d0cSchristos if ( ! strcmp(nch->string, "off")) {
18915c413d0cSchristos mdoc->flags |= MDOC_SMOFF;
18925c413d0cSchristos return;
18935c413d0cSchristos }
18945c413d0cSchristos
18956167eca2Schristos mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
189614e7489eSchristos "%s %s", roff_name[mdoc->last->tok], nch->string);
18976167eca2Schristos roff_node_relink(mdoc, nch);
18985c413d0cSchristos return;
18995c413d0cSchristos }
19005c413d0cSchristos
19015c413d0cSchristos static void
post_root(POST_ARGS)19025c413d0cSchristos post_root(POST_ARGS)
19035c413d0cSchristos {
1904f47368cfSchristos struct roff_node *n;
19055c413d0cSchristos
19065c413d0cSchristos /* Add missing prologue data. */
19075c413d0cSchristos
19085c413d0cSchristos if (mdoc->meta.date == NULL)
190914e7489eSchristos mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
191014e7489eSchristos mandoc_normdate(mdoc, NULL, 0, 0);
19115c413d0cSchristos
19125c413d0cSchristos if (mdoc->meta.title == NULL) {
19136167eca2Schristos mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
19145c413d0cSchristos mdoc->meta.title = mandoc_strdup("UNTITLED");
19155c413d0cSchristos }
19165c413d0cSchristos
19175c413d0cSchristos if (mdoc->meta.vol == NULL)
19185c413d0cSchristos mdoc->meta.vol = mandoc_strdup("LOCAL");
19195c413d0cSchristos
19205c413d0cSchristos if (mdoc->meta.os == NULL) {
19216167eca2Schristos mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
19225c413d0cSchristos mdoc->meta.os = mandoc_strdup("");
192314e7489eSchristos } else if (mdoc->meta.os_e &&
192414e7489eSchristos (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
19256167eca2Schristos mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
192614e7489eSchristos mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
192714e7489eSchristos "(OpenBSD)" : "(NetBSD)");
192814e7489eSchristos
192914e7489eSchristos if (mdoc->meta.arch != NULL &&
19306167eca2Schristos arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
19316167eca2Schristos n = mdoc->meta.first->child;
193214e7489eSchristos while (n->tok != MDOC_Dt ||
193314e7489eSchristos n->child == NULL ||
193414e7489eSchristos n->child->next == NULL ||
193514e7489eSchristos n->child->next->next == NULL)
193614e7489eSchristos n = n->next;
193714e7489eSchristos n = n->child->next->next;
19386167eca2Schristos mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
193914e7489eSchristos "Dt ... %s %s", mdoc->meta.arch,
194014e7489eSchristos mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
194114e7489eSchristos "(OpenBSD)" : "(NetBSD)");
194214e7489eSchristos }
1943c0d9444aSjoerg
1944c0d9444aSjoerg /* Check that we begin with a proper `Sh'. */
1945c0d9444aSjoerg
19466167eca2Schristos n = mdoc->meta.first->child;
194714e7489eSchristos while (n != NULL &&
194814e7489eSchristos (n->type == ROFFT_COMMENT ||
194914e7489eSchristos (n->tok >= MDOC_Dd &&
19506167eca2Schristos mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
19515c413d0cSchristos n = n->next;
19525c413d0cSchristos
19535c413d0cSchristos if (n == NULL)
19546167eca2Schristos mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
19555c413d0cSchristos else if (n->tok != MDOC_Sh)
19566167eca2Schristos mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
19576167eca2Schristos "%s", roff_name[n->tok]);
1958c0d9444aSjoerg }
1959c0d9444aSjoerg
19605c413d0cSchristos static void
post_rs(POST_ARGS)19614154958bSjoerg post_rs(POST_ARGS)
19624154958bSjoerg {
1963f47368cfSchristos struct roff_node *np, *nch, *next, *prev;
1964c0d9444aSjoerg int i, j;
19654154958bSjoerg
19665c413d0cSchristos np = mdoc->last;
19675c413d0cSchristos
1968f47368cfSchristos if (np->type != ROFFT_BODY)
19695c413d0cSchristos return;
19705c413d0cSchristos
19715c413d0cSchristos if (np->child == NULL) {
19726167eca2Schristos mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
19735c413d0cSchristos return;
1974c0d9444aSjoerg }
1975c0d9444aSjoerg
1976c0d9444aSjoerg /*
1977c0d9444aSjoerg * The full `Rs' block needs special handling to order the
1978c0d9444aSjoerg * sub-elements according to `rsord'. Pick through each element
19795c413d0cSchristos * and correctly order it. This is an insertion sort.
1980c0d9444aSjoerg */
1981c0d9444aSjoerg
1982c0d9444aSjoerg next = NULL;
19835c413d0cSchristos for (nch = np->child->next; nch != NULL; nch = next) {
19845c413d0cSchristos /* Determine order number of this child. */
1985c0d9444aSjoerg for (i = 0; i < RSORD_MAX; i++)
19865c413d0cSchristos if (rsord[i] == nch->tok)
1987c0d9444aSjoerg break;
1988c0d9444aSjoerg
19895c413d0cSchristos if (i == RSORD_MAX) {
19906167eca2Schristos mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
19916167eca2Schristos "%s", roff_name[nch->tok]);
19925c413d0cSchristos i = -1;
19935c413d0cSchristos } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
19945c413d0cSchristos np->norm->Rs.quote_T++;
19955c413d0cSchristos
1996c0d9444aSjoerg /*
19975c413d0cSchristos * Remove this child from the chain. This somewhat
1998f47368cfSchristos * repeats roff_node_unlink(), but since we're
1999c0d9444aSjoerg * just re-ordering, there's no need for the
2000c0d9444aSjoerg * full unlink process.
2001c0d9444aSjoerg */
2002c0d9444aSjoerg
20035c413d0cSchristos if ((next = nch->next) != NULL)
20045c413d0cSchristos next->prev = nch->prev;
2005c0d9444aSjoerg
20065c413d0cSchristos if ((prev = nch->prev) != NULL)
20075c413d0cSchristos prev->next = nch->next;
2008c0d9444aSjoerg
20095c413d0cSchristos nch->prev = nch->next = NULL;
2010c0d9444aSjoerg
2011c0d9444aSjoerg /*
2012c0d9444aSjoerg * Scan back until we reach a node that's
20135c413d0cSchristos * to be ordered before this child.
2014c0d9444aSjoerg */
2015c0d9444aSjoerg
2016c0d9444aSjoerg for ( ; prev ; prev = prev->prev) {
2017c0d9444aSjoerg /* Determine order of `prev'. */
2018c0d9444aSjoerg for (j = 0; j < RSORD_MAX; j++)
2019c0d9444aSjoerg if (rsord[j] == prev->tok)
2020c0d9444aSjoerg break;
20215c413d0cSchristos if (j == RSORD_MAX)
20225c413d0cSchristos j = -1;
2023c0d9444aSjoerg
2024c0d9444aSjoerg if (j <= i)
2025c0d9444aSjoerg break;
2026c0d9444aSjoerg }
2027c0d9444aSjoerg
2028c0d9444aSjoerg /*
20295c413d0cSchristos * Set this child back into its correct place
20305c413d0cSchristos * in front of the `prev' node.
2031c0d9444aSjoerg */
2032c0d9444aSjoerg
20335c413d0cSchristos nch->prev = prev;
2034c0d9444aSjoerg
20355c413d0cSchristos if (prev == NULL) {
20365c413d0cSchristos np->child->prev = nch;
20375c413d0cSchristos nch->next = np->child;
20385c413d0cSchristos np->child = nch;
2039c0d9444aSjoerg } else {
20405c413d0cSchristos if (prev->next)
20415c413d0cSchristos prev->next->prev = nch;
20425c413d0cSchristos nch->next = prev->next;
20435c413d0cSchristos prev->next = nch;
2044c0d9444aSjoerg }
20454154958bSjoerg }
20464154958bSjoerg }
20474154958bSjoerg
2048603fc4ebSjoerg /*
2049603fc4ebSjoerg * For some arguments of some macros,
2050603fc4ebSjoerg * convert all breakable hyphens into ASCII_HYPH.
2051603fc4ebSjoerg */
20525c413d0cSchristos static void
post_hyph(POST_ARGS)2053603fc4ebSjoerg post_hyph(POST_ARGS)
2054603fc4ebSjoerg {
2055f47368cfSchristos struct roff_node *nch;
2056603fc4ebSjoerg char *cp;
2057603fc4ebSjoerg
20585c413d0cSchristos for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
2059f47368cfSchristos if (nch->type != ROFFT_TEXT)
2060603fc4ebSjoerg continue;
2061603fc4ebSjoerg cp = nch->string;
20625c413d0cSchristos if (*cp == '\0')
2063603fc4ebSjoerg continue;
20645c413d0cSchristos while (*(++cp) != '\0')
20655c413d0cSchristos if (*cp == '-' &&
2066603fc4ebSjoerg isalpha((unsigned char)cp[-1]) &&
2067603fc4ebSjoerg isalpha((unsigned char)cp[1]))
2068603fc4ebSjoerg *cp = ASCII_HYPH;
2069603fc4ebSjoerg }
2070603fc4ebSjoerg }
2071603fc4ebSjoerg
20725c413d0cSchristos static void
post_ns(POST_ARGS)207348741257Sjoerg post_ns(POST_ARGS)
207448741257Sjoerg {
207514e7489eSchristos struct roff_node *n;
207648741257Sjoerg
207714e7489eSchristos n = mdoc->last;
207814e7489eSchristos if (n->flags & NODE_LINE ||
207914e7489eSchristos (n->next != NULL && n->next->flags & NODE_DELIMC))
20806167eca2Schristos mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
208114e7489eSchristos }
208214e7489eSchristos
208314e7489eSchristos static void
post_sx(POST_ARGS)208414e7489eSchristos post_sx(POST_ARGS)
208514e7489eSchristos {
208614e7489eSchristos post_delim(mdoc);
208714e7489eSchristos post_hyph(mdoc);
208848741257Sjoerg }
208948741257Sjoerg
20905c413d0cSchristos static void
post_sh(POST_ARGS)20914154958bSjoerg post_sh(POST_ARGS)
20924154958bSjoerg {
20934154958bSjoerg
20945c413d0cSchristos post_ignpar(mdoc);
20954154958bSjoerg
20965c413d0cSchristos switch (mdoc->last->type) {
2097f47368cfSchristos case ROFFT_HEAD:
20985c413d0cSchristos post_sh_head(mdoc);
20995c413d0cSchristos break;
2100f47368cfSchristos case ROFFT_BODY:
21015c413d0cSchristos switch (mdoc->lastsec) {
21025c413d0cSchristos case SEC_NAME:
21035c413d0cSchristos post_sh_name(mdoc);
21045c413d0cSchristos break;
21055c413d0cSchristos case SEC_SEE_ALSO:
21065c413d0cSchristos post_sh_see_also(mdoc);
21075c413d0cSchristos break;
21085c413d0cSchristos case SEC_AUTHORS:
21095c413d0cSchristos post_sh_authors(mdoc);
21105c413d0cSchristos break;
21115c413d0cSchristos default:
21125c413d0cSchristos break;
21135c413d0cSchristos }
21145c413d0cSchristos break;
21155c413d0cSchristos default:
21165c413d0cSchristos break;
21175c413d0cSchristos }
21184154958bSjoerg }
21194154958bSjoerg
21205c413d0cSchristos static void
post_sh_name(POST_ARGS)21215c413d0cSchristos post_sh_name(POST_ARGS)
21224154958bSjoerg {
2123f47368cfSchristos struct roff_node *n;
21245c413d0cSchristos int hasnm, hasnd;
21254154958bSjoerg
21265c413d0cSchristos hasnm = hasnd = 0;
21274154958bSjoerg
21285c413d0cSchristos for (n = mdoc->last->child; n != NULL; n = n->next) {
21295c413d0cSchristos switch (n->tok) {
21305c413d0cSchristos case MDOC_Nm:
213137ef69edSchristos if (hasnm && n->child != NULL)
21326167eca2Schristos mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
21336167eca2Schristos n->line, n->pos,
213437ef69edSchristos "Nm %s", n->child->string);
21355c413d0cSchristos hasnm = 1;
213637ef69edSchristos continue;
21375c413d0cSchristos case MDOC_Nd:
21385c413d0cSchristos hasnd = 1;
21395c413d0cSchristos if (n->next != NULL)
21405c413d0cSchristos mandoc_msg(MANDOCERR_NAMESEC_ND,
21416167eca2Schristos n->line, n->pos, NULL);
21425c413d0cSchristos break;
2143f47368cfSchristos case TOKEN_NONE:
214437ef69edSchristos if (n->type == ROFFT_TEXT &&
214537ef69edSchristos n->string[0] == ',' && n->string[1] == '\0' &&
214637ef69edSchristos n->next != NULL && n->next->tok == MDOC_Nm) {
214737ef69edSchristos n = n->next;
214837ef69edSchristos continue;
214937ef69edSchristos }
21505c413d0cSchristos /* FALLTHROUGH */
21515c413d0cSchristos default:
21526167eca2Schristos mandoc_msg(MANDOCERR_NAMESEC_BAD,
21536167eca2Schristos n->line, n->pos, "%s", roff_name[n->tok]);
215437ef69edSchristos continue;
21555c413d0cSchristos }
215637ef69edSchristos break;
2157c0d9444aSjoerg }
21584154958bSjoerg
21595c413d0cSchristos if ( ! hasnm)
21606167eca2Schristos mandoc_msg(MANDOCERR_NAMESEC_NONM,
21615c413d0cSchristos mdoc->last->line, mdoc->last->pos, NULL);
21625c413d0cSchristos if ( ! hasnd)
21636167eca2Schristos mandoc_msg(MANDOCERR_NAMESEC_NOND,
21645c413d0cSchristos mdoc->last->line, mdoc->last->pos, NULL);
21654154958bSjoerg }
21664154958bSjoerg
21675c413d0cSchristos static void
post_sh_see_also(POST_ARGS)21685c413d0cSchristos post_sh_see_also(POST_ARGS)
21695c413d0cSchristos {
2170f47368cfSchristos const struct roff_node *n;
21715c413d0cSchristos const char *name, *sec;
21725c413d0cSchristos const char *lastname, *lastsec, *lastpunct;
21735c413d0cSchristos int cmp;
21744154958bSjoerg
21755c413d0cSchristos n = mdoc->last->child;
21765c413d0cSchristos lastname = lastsec = lastpunct = NULL;
21775c413d0cSchristos while (n != NULL) {
2178f47368cfSchristos if (n->tok != MDOC_Xr ||
2179f47368cfSchristos n->child == NULL ||
2180f47368cfSchristos n->child->next == NULL)
21815c413d0cSchristos break;
21825c413d0cSchristos
21835c413d0cSchristos /* Process one .Xr node. */
21845c413d0cSchristos
21855c413d0cSchristos name = n->child->string;
21865c413d0cSchristos sec = n->child->next->string;
21875c413d0cSchristos if (lastsec != NULL) {
21885c413d0cSchristos if (lastpunct[0] != ',' || lastpunct[1] != '\0')
21896167eca2Schristos mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
21906167eca2Schristos n->pos, "%s before %s(%s)",
21916167eca2Schristos lastpunct, name, sec);
21925c413d0cSchristos cmp = strcmp(lastsec, sec);
21935c413d0cSchristos if (cmp > 0)
21946167eca2Schristos mandoc_msg(MANDOCERR_XR_ORDER, n->line,
21956167eca2Schristos n->pos, "%s(%s) after %s(%s)",
21966167eca2Schristos name, sec, lastname, lastsec);
21975c413d0cSchristos else if (cmp == 0 &&
21985c413d0cSchristos strcasecmp(lastname, name) > 0)
21996167eca2Schristos mandoc_msg(MANDOCERR_XR_ORDER, n->line,
22006167eca2Schristos n->pos, "%s after %s", name, lastname);
22015c413d0cSchristos }
22025c413d0cSchristos lastname = name;
22035c413d0cSchristos lastsec = sec;
22045c413d0cSchristos
22055c413d0cSchristos /* Process the following node. */
22065c413d0cSchristos
22075c413d0cSchristos n = n->next;
22085c413d0cSchristos if (n == NULL)
22095c413d0cSchristos break;
22105c413d0cSchristos if (n->tok == MDOC_Xr) {
22115c413d0cSchristos lastpunct = "none";
22125c413d0cSchristos continue;
22135c413d0cSchristos }
2214f47368cfSchristos if (n->type != ROFFT_TEXT)
22155c413d0cSchristos break;
22165c413d0cSchristos for (name = n->string; *name != '\0'; name++)
22175c413d0cSchristos if (isalpha((const unsigned char)*name))
22185c413d0cSchristos return;
22195c413d0cSchristos lastpunct = n->string;
222014e7489eSchristos if (n->next == NULL || n->next->tok == MDOC_Rs)
22216167eca2Schristos mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
22226167eca2Schristos n->pos, "%s after %s(%s)",
22235c413d0cSchristos lastpunct, lastname, lastsec);
22245c413d0cSchristos n = n->next;
22255c413d0cSchristos }
2226c0d9444aSjoerg }
22274154958bSjoerg
22284154958bSjoerg static int
child_an(const struct roff_node * n)2229f47368cfSchristos child_an(const struct roff_node *n)
22305c413d0cSchristos {
22315c413d0cSchristos
22325c413d0cSchristos for (n = n->child; n != NULL; n = n->next)
2233f47368cfSchristos if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2234f47368cfSchristos return 1;
2235f47368cfSchristos return 0;
22365c413d0cSchristos }
22375c413d0cSchristos
22385c413d0cSchristos static void
post_sh_authors(POST_ARGS)22395c413d0cSchristos post_sh_authors(POST_ARGS)
22405c413d0cSchristos {
22415c413d0cSchristos
22425c413d0cSchristos if ( ! child_an(mdoc->last))
22436167eca2Schristos mandoc_msg(MANDOCERR_AN_MISSING,
22445c413d0cSchristos mdoc->last->line, mdoc->last->pos, NULL);
22455c413d0cSchristos }
22465c413d0cSchristos
224714e7489eSchristos /*
224814e7489eSchristos * Return an upper bound for the string distance (allowing
224914e7489eSchristos * transpositions). Not a full Levenshtein implementation
225014e7489eSchristos * because Levenshtein is quadratic in the string length
225114e7489eSchristos * and this function is called for every standard name,
225214e7489eSchristos * so the check for each custom name would be cubic.
225314e7489eSchristos * The following crude heuristics is linear, resulting
225414e7489eSchristos * in quadratic behaviour for checking one custom name,
225514e7489eSchristos * which does not cause measurable slowdown.
225614e7489eSchristos */
225714e7489eSchristos static int
similar(const char * s1,const char * s2)225814e7489eSchristos similar(const char *s1, const char *s2)
225914e7489eSchristos {
226014e7489eSchristos const int maxdist = 3;
226114e7489eSchristos int dist = 0;
226214e7489eSchristos
226314e7489eSchristos while (s1[0] != '\0' && s2[0] != '\0') {
226414e7489eSchristos if (s1[0] == s2[0]) {
226514e7489eSchristos s1++;
226614e7489eSchristos s2++;
226714e7489eSchristos continue;
226814e7489eSchristos }
226914e7489eSchristos if (++dist > maxdist)
227014e7489eSchristos return INT_MAX;
227114e7489eSchristos if (s1[1] == s2[1]) { /* replacement */
227214e7489eSchristos s1++;
227314e7489eSchristos s2++;
227414e7489eSchristos } else if (s1[0] == s2[1] && s1[1] == s2[0]) {
227514e7489eSchristos s1 += 2; /* transposition */
227614e7489eSchristos s2 += 2;
227714e7489eSchristos } else if (s1[0] == s2[1]) /* insertion */
227814e7489eSchristos s2++;
227914e7489eSchristos else if (s1[1] == s2[0]) /* deletion */
228014e7489eSchristos s1++;
228114e7489eSchristos else
228214e7489eSchristos return INT_MAX;
228314e7489eSchristos }
228414e7489eSchristos dist += strlen(s1) + strlen(s2);
228514e7489eSchristos return dist > maxdist ? INT_MAX : dist;
228614e7489eSchristos }
228714e7489eSchristos
22885c413d0cSchristos static void
post_sh_head(POST_ARGS)22894154958bSjoerg post_sh_head(POST_ARGS)
22904154958bSjoerg {
229137ef69edSchristos struct roff_node *nch;
22925c413d0cSchristos const char *goodsec;
229314e7489eSchristos const char *const *testsec;
229414e7489eSchristos int dist, mindist;
2295f47368cfSchristos enum roff_sec sec;
22964154958bSjoerg
22974154958bSjoerg /*
22984154958bSjoerg * Process a new section. Sections are either "named" or
2299c0d9444aSjoerg * "custom". Custom sections are user-defined, while named ones
2300c0d9444aSjoerg * follow a conventional order and may only appear in certain
2301c0d9444aSjoerg * manual sections.
23024154958bSjoerg */
23034154958bSjoerg
2304f47368cfSchristos sec = mdoc->last->sec;
23054154958bSjoerg
2306c0d9444aSjoerg /* The NAME should be first. */
23074154958bSjoerg
230837ef69edSchristos if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
23096167eca2Schristos mandoc_msg(MANDOCERR_NAMESEC_FIRST,
231037ef69edSchristos mdoc->last->line, mdoc->last->pos, "Sh %s",
231137ef69edSchristos sec != SEC_CUSTOM ? secnames[sec] :
231237ef69edSchristos (nch = mdoc->last->child) == NULL ? "" :
231337ef69edSchristos nch->type == ROFFT_TEXT ? nch->string :
231414e7489eSchristos roff_name[nch->tok]);
2315c0d9444aSjoerg
2316c0d9444aSjoerg /* The SYNOPSIS gets special attention in other areas. */
2317c0d9444aSjoerg
2318f47368cfSchristos if (sec == SEC_SYNOPSIS) {
2319603fc4ebSjoerg roff_setreg(mdoc->roff, "nS", 1, '=');
2320c0d9444aSjoerg mdoc->flags |= MDOC_SYNOPSIS;
2321603fc4ebSjoerg } else {
2322603fc4ebSjoerg roff_setreg(mdoc->roff, "nS", 0, '=');
2323c0d9444aSjoerg mdoc->flags &= ~MDOC_SYNOPSIS;
2324603fc4ebSjoerg }
2325c0d9444aSjoerg
2326c0d9444aSjoerg /* Mark our last section. */
2327c0d9444aSjoerg
2328c0d9444aSjoerg mdoc->lastsec = sec;
2329c0d9444aSjoerg
2330c0d9444aSjoerg /* We don't care about custom sections after this. */
23310a84adc5Sjoerg
233214e7489eSchristos if (sec == SEC_CUSTOM) {
233314e7489eSchristos if ((nch = mdoc->last->child) == NULL ||
233414e7489eSchristos nch->type != ROFFT_TEXT || nch->next != NULL)
23355c413d0cSchristos return;
233614e7489eSchristos goodsec = NULL;
233714e7489eSchristos mindist = INT_MAX;
233814e7489eSchristos for (testsec = secnames + 1; *testsec != NULL; testsec++) {
233914e7489eSchristos dist = similar(nch->string, *testsec);
234014e7489eSchristos if (dist < mindist) {
234114e7489eSchristos goodsec = *testsec;
234214e7489eSchristos mindist = dist;
234314e7489eSchristos }
234414e7489eSchristos }
234514e7489eSchristos if (goodsec != NULL)
23466167eca2Schristos mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
23476167eca2Schristos "Sh %s instead of %s", nch->string, goodsec);
234814e7489eSchristos return;
234914e7489eSchristos }
23500a84adc5Sjoerg
23514154958bSjoerg /*
2352c0d9444aSjoerg * Check whether our non-custom section is being repeated or is
2353c0d9444aSjoerg * out of order.
23544154958bSjoerg */
23554154958bSjoerg
2356c0d9444aSjoerg if (sec == mdoc->lastnamed)
23576167eca2Schristos mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
23586167eca2Schristos mdoc->last->pos, "Sh %s", secnames[sec]);
2359c0d9444aSjoerg
2360c0d9444aSjoerg if (sec < mdoc->lastnamed)
23616167eca2Schristos mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
23626167eca2Schristos mdoc->last->pos, "Sh %s", secnames[sec]);
2363c0d9444aSjoerg
2364c0d9444aSjoerg /* Mark the last named section. */
2365c0d9444aSjoerg
2366c0d9444aSjoerg mdoc->lastnamed = sec;
2367c0d9444aSjoerg
2368c0d9444aSjoerg /* Check particular section/manual conventions. */
2369c0d9444aSjoerg
2370f47368cfSchristos if (mdoc->meta.msec == NULL)
23715c413d0cSchristos return;
2372c0d9444aSjoerg
23735c413d0cSchristos goodsec = NULL;
2374c0d9444aSjoerg switch (sec) {
23755c413d0cSchristos case SEC_ERRORS:
23765c413d0cSchristos if (*mdoc->meta.msec == '4')
23775c413d0cSchristos break;
23785c413d0cSchristos goodsec = "2, 3, 4, 9";
2379c0d9444aSjoerg /* FALLTHROUGH */
23805c413d0cSchristos case SEC_RETURN_VALUES:
23815c413d0cSchristos case SEC_LIBRARY:
23820a84adc5Sjoerg if (*mdoc->meta.msec == '2')
23834154958bSjoerg break;
23840a84adc5Sjoerg if (*mdoc->meta.msec == '3')
23854154958bSjoerg break;
23865c413d0cSchristos if (NULL == goodsec)
23875c413d0cSchristos goodsec = "2, 3, 9";
23885c413d0cSchristos /* FALLTHROUGH */
23895c413d0cSchristos case SEC_CONTEXT:
23900a84adc5Sjoerg if (*mdoc->meta.msec == '9')
23910a84adc5Sjoerg break;
23925c413d0cSchristos if (NULL == goodsec)
23935c413d0cSchristos goodsec = "9";
23946167eca2Schristos mandoc_msg(MANDOCERR_SEC_MSEC,
23955c413d0cSchristos mdoc->last->line, mdoc->last->pos,
2396f47368cfSchristos "Sh %s for %s only", secnames[sec], goodsec);
2397c0d9444aSjoerg break;
23984154958bSjoerg default:
23994154958bSjoerg break;
24004154958bSjoerg }
24014154958bSjoerg }
2402c0d9444aSjoerg
24035c413d0cSchristos static void
post_xr(POST_ARGS)240437ef69edSchristos post_xr(POST_ARGS)
240537ef69edSchristos {
240637ef69edSchristos struct roff_node *n, *nch;
240737ef69edSchristos
240837ef69edSchristos n = mdoc->last;
240937ef69edSchristos nch = n->child;
241037ef69edSchristos if (nch->next == NULL) {
24116167eca2Schristos mandoc_msg(MANDOCERR_XR_NOSEC,
241237ef69edSchristos n->line, n->pos, "Xr %s", nch->string);
241314e7489eSchristos } else {
241437ef69edSchristos assert(nch->next == n->last);
241514e7489eSchristos if(mandoc_xr_add(nch->next->string, nch->string,
241614e7489eSchristos nch->line, nch->pos))
24176167eca2Schristos mandoc_msg(MANDOCERR_XR_SELF,
241814e7489eSchristos nch->line, nch->pos, "Xr %s %s",
241914e7489eSchristos nch->string, nch->next->string);
242014e7489eSchristos }
242114e7489eSchristos post_delim_nb(mdoc);
242237ef69edSchristos }
242337ef69edSchristos
242437ef69edSchristos static void
post_ignpar(POST_ARGS)2425c0d9444aSjoerg post_ignpar(POST_ARGS)
2426c0d9444aSjoerg {
2427f47368cfSchristos struct roff_node *np;
2428c0d9444aSjoerg
24295c413d0cSchristos switch (mdoc->last->type) {
243014e7489eSchristos case ROFFT_BLOCK:
243114e7489eSchristos post_prevpar(mdoc);
243214e7489eSchristos return;
2433f47368cfSchristos case ROFFT_HEAD:
243414e7489eSchristos post_delim(mdoc);
24355c413d0cSchristos post_hyph(mdoc);
24365c413d0cSchristos return;
2437f47368cfSchristos case ROFFT_BODY:
24385c413d0cSchristos break;
24395c413d0cSchristos default:
24405c413d0cSchristos return;
24415c413d0cSchristos }
2442c0d9444aSjoerg
2443f47368cfSchristos if ((np = mdoc->last->child) != NULL)
24446167eca2Schristos if (np->tok == MDOC_Pp ||
24456167eca2Schristos np->tok == ROFF_br || np->tok == ROFF_sp) {
24466167eca2Schristos mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
244714e7489eSchristos "%s after %s", roff_name[np->tok],
244814e7489eSchristos roff_name[mdoc->last->tok]);
2449f47368cfSchristos roff_node_delete(mdoc, np);
2450c0d9444aSjoerg }
2451c0d9444aSjoerg
2452f47368cfSchristos if ((np = mdoc->last->last) != NULL)
24536167eca2Schristos if (np->tok == MDOC_Pp || np->tok == ROFF_br) {
24546167eca2Schristos mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
24556167eca2Schristos "%s at the end of %s", roff_name[np->tok],
245614e7489eSchristos roff_name[mdoc->last->tok]);
2457f47368cfSchristos roff_node_delete(mdoc, np);
2458c0d9444aSjoerg }
2459c0d9444aSjoerg }
2460c0d9444aSjoerg
24615c413d0cSchristos static void
post_prevpar(POST_ARGS)2462f47368cfSchristos post_prevpar(POST_ARGS)
2463c0d9444aSjoerg {
2464f47368cfSchristos struct roff_node *n;
2465c0d9444aSjoerg
2466f47368cfSchristos n = mdoc->last;
2467f47368cfSchristos if (NULL == n->prev)
24685c413d0cSchristos return;
2469f47368cfSchristos if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
24705c413d0cSchristos return;
2471c0d9444aSjoerg
2472c0d9444aSjoerg /*
24736167eca2Schristos * Don't allow `Pp' prior to a paragraph-type
24746167eca2Schristos * block: `Pp' or non-compact `Bd' or `Bl'.
2475c0d9444aSjoerg */
2476c0d9444aSjoerg
24776167eca2Schristos if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br)
24785c413d0cSchristos return;
2479f47368cfSchristos if (n->tok == MDOC_Bl && n->norm->Bl.comp)
24805c413d0cSchristos return;
2481f47368cfSchristos if (n->tok == MDOC_Bd && n->norm->Bd.comp)
24825c413d0cSchristos return;
2483f47368cfSchristos if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
24845c413d0cSchristos return;
2485c0d9444aSjoerg
24866167eca2Schristos mandoc_msg(MANDOCERR_PAR_SKIP, n->prev->line, n->prev->pos,
24876167eca2Schristos "%s before %s", roff_name[n->prev->tok], roff_name[n->tok]);
2488f47368cfSchristos roff_node_delete(mdoc, n->prev);
2489c0d9444aSjoerg }
2490c0d9444aSjoerg
24915c413d0cSchristos static void
post_par(POST_ARGS)2492603fc4ebSjoerg post_par(POST_ARGS)
2493603fc4ebSjoerg {
2494f47368cfSchristos struct roff_node *np;
2495603fc4ebSjoerg
2496f47368cfSchristos post_prevpar(mdoc);
2497603fc4ebSjoerg
24986167eca2Schristos np = mdoc->last;
24996167eca2Schristos if (np->child != NULL)
25006167eca2Schristos mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
25016167eca2Schristos "%s %s", roff_name[np->tok], np->child->string);
2502c0d9444aSjoerg }
2503c0d9444aSjoerg
25045c413d0cSchristos static void
post_dd(POST_ARGS)2505c0d9444aSjoerg post_dd(POST_ARGS)
2506c0d9444aSjoerg {
2507f47368cfSchristos struct roff_node *n;
25085c413d0cSchristos char *datestr;
2509c0d9444aSjoerg
251048741257Sjoerg n = mdoc->last;
251137ef69edSchristos n->flags |= NODE_NOPRT;
251237ef69edSchristos
2513f47368cfSchristos if (mdoc->meta.date != NULL) {
25146167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
2515f47368cfSchristos free(mdoc->meta.date);
2516f47368cfSchristos } else if (mdoc->flags & MDOC_PBODY)
25176167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
2518f47368cfSchristos else if (mdoc->meta.title != NULL)
25196167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_ORDER,
2520f47368cfSchristos n->line, n->pos, "Dd after Dt");
2521f47368cfSchristos else if (mdoc->meta.os != NULL)
25226167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_ORDER,
2523f47368cfSchristos n->line, n->pos, "Dd after Os");
2524f47368cfSchristos
2525f47368cfSchristos if (n->child == NULL || n->child->string[0] == '\0') {
25265c413d0cSchristos mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
252714e7489eSchristos mandoc_normdate(mdoc, NULL, n->line, n->pos);
252837ef69edSchristos return;
2529c0d9444aSjoerg }
2530c0d9444aSjoerg
25315c413d0cSchristos datestr = NULL;
2532f47368cfSchristos deroff(&datestr, n);
25335c413d0cSchristos if (mdoc->quick)
25345c413d0cSchristos mdoc->meta.date = datestr;
25355c413d0cSchristos else {
253614e7489eSchristos mdoc->meta.date = mandoc_normdate(mdoc,
25375c413d0cSchristos datestr, n->line, n->pos);
25385c413d0cSchristos free(datestr);
25395c413d0cSchristos }
2540b1e8115bSjoerg }
2541c0d9444aSjoerg
25425c413d0cSchristos static void
post_dt(POST_ARGS)2543c0d9444aSjoerg post_dt(POST_ARGS)
2544c0d9444aSjoerg {
2545f47368cfSchristos struct roff_node *nn, *n;
2546c0d9444aSjoerg const char *cp;
2547c0d9444aSjoerg char *p;
2548c0d9444aSjoerg
2549c0d9444aSjoerg n = mdoc->last;
255037ef69edSchristos n->flags |= NODE_NOPRT;
255137ef69edSchristos
2552f47368cfSchristos if (mdoc->flags & MDOC_PBODY) {
25536167eca2Schristos mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
255437ef69edSchristos return;
2555f47368cfSchristos }
2556f47368cfSchristos
2557f47368cfSchristos if (mdoc->meta.title != NULL)
25586167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
2559f47368cfSchristos else if (mdoc->meta.os != NULL)
25606167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_ORDER,
2561f47368cfSchristos n->line, n->pos, "Dt after Os");
2562c0d9444aSjoerg
2563c0d9444aSjoerg free(mdoc->meta.title);
25645c413d0cSchristos free(mdoc->meta.msec);
2565c0d9444aSjoerg free(mdoc->meta.vol);
2566c0d9444aSjoerg free(mdoc->meta.arch);
2567c0d9444aSjoerg
25685c413d0cSchristos mdoc->meta.title = NULL;
25695c413d0cSchristos mdoc->meta.msec = NULL;
25705c413d0cSchristos mdoc->meta.vol = NULL;
25715c413d0cSchristos mdoc->meta.arch = NULL;
2572c0d9444aSjoerg
25735c413d0cSchristos /* Mandatory first argument: title. */
2574c0d9444aSjoerg
25755c413d0cSchristos nn = n->child;
25765c413d0cSchristos if (nn == NULL || *nn->string == '\0') {
25776167eca2Schristos mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
25785c413d0cSchristos mdoc->meta.title = mandoc_strdup("UNTITLED");
25795c413d0cSchristos } else {
25805c413d0cSchristos mdoc->meta.title = mandoc_strdup(nn->string);
2581c0d9444aSjoerg
25825c413d0cSchristos /* Check that all characters are uppercase. */
25835c413d0cSchristos
25845c413d0cSchristos for (p = nn->string; *p != '\0'; p++)
25855c413d0cSchristos if (islower((unsigned char)*p)) {
25866167eca2Schristos mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
25876167eca2Schristos nn->pos + (int)(p - nn->string),
25885c413d0cSchristos "Dt %s", nn->string);
2589c0d9444aSjoerg break;
2590c0d9444aSjoerg }
2591c0d9444aSjoerg }
2592c0d9444aSjoerg
259337ef69edSchristos /* Mandatory second argument: section. */
2594c0d9444aSjoerg
25955c413d0cSchristos if (nn != NULL)
25965c413d0cSchristos nn = nn->next;
2597c0d9444aSjoerg
25985c413d0cSchristos if (nn == NULL) {
25996167eca2Schristos mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
26005c413d0cSchristos "Dt %s", mdoc->meta.title);
2601c0d9444aSjoerg mdoc->meta.vol = mandoc_strdup("LOCAL");
260237ef69edSchristos return; /* msec and arch remain NULL. */
2603c0d9444aSjoerg }
2604c0d9444aSjoerg
26055c413d0cSchristos mdoc->meta.msec = mandoc_strdup(nn->string);
26065c413d0cSchristos
26075c413d0cSchristos /* Infer volume title from section number. */
2608c0d9444aSjoerg
26091350fe09Sjoerg cp = mandoc_a2msec(nn->string);
26105c413d0cSchristos if (cp == NULL) {
26116167eca2Schristos mandoc_msg(MANDOCERR_MSEC_BAD,
26125c413d0cSchristos nn->line, nn->pos, "Dt ... %s", nn->string);
2613c0d9444aSjoerg mdoc->meta.vol = mandoc_strdup(nn->string);
2614c0d9444aSjoerg } else
26155c413d0cSchristos mdoc->meta.vol = mandoc_strdup(cp);
26165c413d0cSchristos
26175c413d0cSchristos /* Optional third argument: architecture. */
26185c413d0cSchristos
26195c413d0cSchristos if ((nn = nn->next) == NULL)
262037ef69edSchristos return;
26215c413d0cSchristos
26225c413d0cSchristos for (p = nn->string; *p != '\0'; p++)
26235c413d0cSchristos *p = tolower((unsigned char)*p);
26245c413d0cSchristos mdoc->meta.arch = mandoc_strdup(nn->string);
26255c413d0cSchristos
26265c413d0cSchristos /* Ignore fourth and later arguments. */
26275c413d0cSchristos
26285c413d0cSchristos if ((nn = nn->next) != NULL)
26296167eca2Schristos mandoc_msg(MANDOCERR_ARG_EXCESS,
26305c413d0cSchristos nn->line, nn->pos, "Dt ... %s", nn->string);
2631c0d9444aSjoerg }
2632c0d9444aSjoerg
26335c413d0cSchristos static void
post_bx(POST_ARGS)263448741257Sjoerg post_bx(POST_ARGS)
263548741257Sjoerg {
263637ef69edSchristos struct roff_node *n, *nch;
263714e7489eSchristos const char *macro;
263814e7489eSchristos
263914e7489eSchristos post_delim_nb(mdoc);
264037ef69edSchristos
264137ef69edSchristos n = mdoc->last;
264237ef69edSchristos nch = n->child;
264337ef69edSchristos
264437ef69edSchristos if (nch != NULL) {
264514e7489eSchristos macro = !strcmp(nch->string, "Open") ? "Ox" :
264614e7489eSchristos !strcmp(nch->string, "Net") ? "Nx" :
264714e7489eSchristos !strcmp(nch->string, "Free") ? "Fx" :
264814e7489eSchristos !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
264914e7489eSchristos if (macro != NULL)
26506167eca2Schristos mandoc_msg(MANDOCERR_BX,
26516167eca2Schristos n->line, n->pos, "%s", macro);
265237ef69edSchristos mdoc->last = nch;
265337ef69edSchristos nch = nch->next;
265437ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
265537ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
265637ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
265737ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
265837ef69edSchristos } else
265937ef69edSchristos mdoc->next = ROFF_NEXT_CHILD;
266037ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "BSD");
266137ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
266237ef69edSchristos
266337ef69edSchristos if (nch == NULL) {
266437ef69edSchristos mdoc->last = n;
266537ef69edSchristos return;
266637ef69edSchristos }
266737ef69edSchristos
266837ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
266937ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
267037ef69edSchristos mdoc->next = ROFF_NEXT_SIBLING;
267137ef69edSchristos roff_word_alloc(mdoc, n->line, n->pos, "-");
267237ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
267337ef69edSchristos roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
267437ef69edSchristos mdoc->last->flags |= NODE_NOSRC;
267537ef69edSchristos mdoc->last = n;
267648741257Sjoerg
267748741257Sjoerg /*
267848741257Sjoerg * Make `Bx's second argument always start with an uppercase
267948741257Sjoerg * letter. Groff checks if it's an "accepted" term, but we just
268048741257Sjoerg * uppercase blindly.
268148741257Sjoerg */
268248741257Sjoerg
268337ef69edSchristos *nch->string = (char)toupper((unsigned char)*nch->string);
268448741257Sjoerg }
268548741257Sjoerg
26865c413d0cSchristos static void
post_os(POST_ARGS)2687c0d9444aSjoerg post_os(POST_ARGS)
2688c0d9444aSjoerg {
2689c0d9444aSjoerg #ifndef OSNAME
2690c0d9444aSjoerg struct utsname utsname;
26915c413d0cSchristos static char *defbuf;
2692c0d9444aSjoerg #endif
2693f47368cfSchristos struct roff_node *n;
2694c0d9444aSjoerg
2695c0d9444aSjoerg n = mdoc->last;
269637ef69edSchristos n->flags |= NODE_NOPRT;
269737ef69edSchristos
2698f47368cfSchristos if (mdoc->meta.os != NULL)
26996167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
2700f47368cfSchristos else if (mdoc->flags & MDOC_PBODY)
27016167eca2Schristos mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
2702c0d9444aSjoerg
270314e7489eSchristos post_delim(mdoc);
270414e7489eSchristos
2705c0d9444aSjoerg /*
2706603fc4ebSjoerg * Set the operating system by way of the `Os' macro.
2707603fc4ebSjoerg * The order of precedence is:
2708603fc4ebSjoerg * 1. the argument of the `Os' macro, unless empty
2709603fc4ebSjoerg * 2. the -Ios=foo command line argument, if provided
2710603fc4ebSjoerg * 3. -DOSNAME="\"foo\"", if provided during compilation
2711603fc4ebSjoerg * 4. "sysname release" from uname(3)
2712c0d9444aSjoerg */
2713c0d9444aSjoerg
2714c0d9444aSjoerg free(mdoc->meta.os);
27155c413d0cSchristos mdoc->meta.os = NULL;
2716f47368cfSchristos deroff(&mdoc->meta.os, n);
27175c413d0cSchristos if (mdoc->meta.os)
271814e7489eSchristos goto out;
2719c0d9444aSjoerg
272014e7489eSchristos if (mdoc->os_s != NULL) {
272114e7489eSchristos mdoc->meta.os = mandoc_strdup(mdoc->os_s);
272214e7489eSchristos goto out;
2723603fc4ebSjoerg }
27245c413d0cSchristos
2725c0d9444aSjoerg #ifdef OSNAME
27265c413d0cSchristos mdoc->meta.os = mandoc_strdup(OSNAME);
2727c0d9444aSjoerg #else /*!OSNAME */
2728f47368cfSchristos if (defbuf == NULL) {
2729f47368cfSchristos if (uname(&utsname) == -1) {
27306167eca2Schristos mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
27315c413d0cSchristos defbuf = mandoc_strdup("UNKNOWN");
27325c413d0cSchristos } else
27335c413d0cSchristos mandoc_asprintf(&defbuf, "%s %s",
27345c413d0cSchristos utsname.sysname, utsname.release);
2735c0d9444aSjoerg }
27365c413d0cSchristos mdoc->meta.os = mandoc_strdup(defbuf);
2737c0d9444aSjoerg #endif /*!OSNAME*/
273814e7489eSchristos
273914e7489eSchristos out:
274014e7489eSchristos if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
274114e7489eSchristos if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
274214e7489eSchristos mdoc->meta.os_e = MANDOC_OS_OPENBSD;
274314e7489eSchristos else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
274414e7489eSchristos mdoc->meta.os_e = MANDOC_OS_NETBSD;
274514e7489eSchristos }
274614e7489eSchristos
274714e7489eSchristos /*
274814e7489eSchristos * This is the earliest point where we can check
274914e7489eSchristos * Mdocdate conventions because we don't know
275014e7489eSchristos * the operating system earlier.
275114e7489eSchristos */
275214e7489eSchristos
275314e7489eSchristos if (n->child != NULL)
27546167eca2Schristos mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
275514e7489eSchristos "Os %s (%s)", n->child->string,
275614e7489eSchristos mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
275714e7489eSchristos "OpenBSD" : "NetBSD");
275814e7489eSchristos
275914e7489eSchristos while (n->tok != MDOC_Dd)
276014e7489eSchristos if ((n = n->prev) == NULL)
276114e7489eSchristos return;
276214e7489eSchristos if ((n = n->child) == NULL)
276314e7489eSchristos return;
276414e7489eSchristos if (strncmp(n->string, "$" "Mdocdate", 9)) {
276514e7489eSchristos if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
27666167eca2Schristos mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
27676167eca2Schristos n->pos, "Dd %s (OpenBSD)", n->string);
276814e7489eSchristos } else {
276914e7489eSchristos if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
27706167eca2Schristos mandoc_msg(MANDOCERR_MDOCDATE, n->line,
27716167eca2Schristos n->pos, "Dd %s (NetBSD)", n->string);
277214e7489eSchristos }
2773c0d9444aSjoerg }
2774c0d9444aSjoerg
2775f47368cfSchristos enum roff_sec
mdoc_a2sec(const char * p)2776f47368cfSchristos mdoc_a2sec(const char *p)
277748741257Sjoerg {
277848741257Sjoerg int i;
277948741257Sjoerg
278048741257Sjoerg for (i = 0; i < (int)SEC__MAX; i++)
278148741257Sjoerg if (secnames[i] && 0 == strcmp(p, secnames[i]))
2782f47368cfSchristos return (enum roff_sec)i;
278348741257Sjoerg
2784f47368cfSchristos return SEC_CUSTOM;
278548741257Sjoerg }
278648741257Sjoerg
278748741257Sjoerg static size_t
macro2len(enum roff_tok macro)278814e7489eSchristos macro2len(enum roff_tok macro)
278948741257Sjoerg {
279048741257Sjoerg
279148741257Sjoerg switch (macro) {
27925c413d0cSchristos case MDOC_Ad:
2793f47368cfSchristos return 12;
27945c413d0cSchristos case MDOC_Ao:
2795f47368cfSchristos return 12;
27965c413d0cSchristos case MDOC_An:
2797f47368cfSchristos return 12;
27985c413d0cSchristos case MDOC_Aq:
2799f47368cfSchristos return 12;
28005c413d0cSchristos case MDOC_Ar:
2801f47368cfSchristos return 12;
28025c413d0cSchristos case MDOC_Bo:
2803f47368cfSchristos return 12;
28045c413d0cSchristos case MDOC_Bq:
2805f47368cfSchristos return 12;
28065c413d0cSchristos case MDOC_Cd:
2807f47368cfSchristos return 12;
28085c413d0cSchristos case MDOC_Cm:
2809f47368cfSchristos return 10;
28105c413d0cSchristos case MDOC_Do:
2811f47368cfSchristos return 10;
28125c413d0cSchristos case MDOC_Dq:
2813f47368cfSchristos return 12;
28145c413d0cSchristos case MDOC_Dv:
2815f47368cfSchristos return 12;
28165c413d0cSchristos case MDOC_Eo:
2817f47368cfSchristos return 12;
28185c413d0cSchristos case MDOC_Em:
2819f47368cfSchristos return 10;
28205c413d0cSchristos case MDOC_Er:
2821f47368cfSchristos return 17;
28225c413d0cSchristos case MDOC_Ev:
2823f47368cfSchristos return 15;
28245c413d0cSchristos case MDOC_Fa:
2825f47368cfSchristos return 12;
28265c413d0cSchristos case MDOC_Fl:
2827f47368cfSchristos return 10;
28285c413d0cSchristos case MDOC_Fo:
2829f47368cfSchristos return 16;
28305c413d0cSchristos case MDOC_Fn:
2831f47368cfSchristos return 16;
28325c413d0cSchristos case MDOC_Ic:
2833f47368cfSchristos return 10;
28345c413d0cSchristos case MDOC_Li:
2835f47368cfSchristos return 16;
28365c413d0cSchristos case MDOC_Ms:
2837f47368cfSchristos return 6;
28385c413d0cSchristos case MDOC_Nm:
2839f47368cfSchristos return 10;
28405c413d0cSchristos case MDOC_No:
2841f47368cfSchristos return 12;
28425c413d0cSchristos case MDOC_Oo:
2843f47368cfSchristos return 10;
28445c413d0cSchristos case MDOC_Op:
2845f47368cfSchristos return 14;
28465c413d0cSchristos case MDOC_Pa:
2847f47368cfSchristos return 32;
28485c413d0cSchristos case MDOC_Pf:
2849f47368cfSchristos return 12;
28505c413d0cSchristos case MDOC_Po:
2851f47368cfSchristos return 12;
28525c413d0cSchristos case MDOC_Pq:
2853f47368cfSchristos return 12;
28545c413d0cSchristos case MDOC_Ql:
2855f47368cfSchristos return 16;
28565c413d0cSchristos case MDOC_Qo:
2857f47368cfSchristos return 12;
28585c413d0cSchristos case MDOC_So:
2859f47368cfSchristos return 12;
28605c413d0cSchristos case MDOC_Sq:
2861f47368cfSchristos return 12;
28625c413d0cSchristos case MDOC_Sy:
2863f47368cfSchristos return 6;
28645c413d0cSchristos case MDOC_Sx:
2865f47368cfSchristos return 16;
28665c413d0cSchristos case MDOC_Tn:
2867f47368cfSchristos return 10;
28685c413d0cSchristos case MDOC_Va:
2869f47368cfSchristos return 12;
28705c413d0cSchristos case MDOC_Vt:
2871f47368cfSchristos return 12;
28725c413d0cSchristos case MDOC_Xr:
2873f47368cfSchristos return 10;
287448741257Sjoerg default:
287548741257Sjoerg break;
287648741257Sjoerg };
2877f47368cfSchristos return 0;
287848741257Sjoerg }
2879