1*479c151dSjsg /* $OpenBSD: mdoc_validate.c,v 1.307 2024/09/20 02:00:46 jsg Exp $ */
2f73abda9Skristaps /*
38055da74Sschwarze * Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org>
40ac7e6ecSschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
539c2a57eSschwarze * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6f73abda9Skristaps *
7f73abda9Skristaps * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps *
11d1982c71Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12a6464425Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d1982c71Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14a6464425Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180ac7e6ecSschwarze *
190ac7e6ecSschwarze * Validation module for mdoc(7) syntax trees used by mandoc(1).
20f73abda9Skristaps */
21dadc3a61Sschwarze #include <sys/types.h>
2220fa2881Sschwarze #ifndef OSNAME
2320fa2881Sschwarze #include <sys/utsname.h>
2420fa2881Sschwarze #endif
2520fa2881Sschwarze
26f73abda9Skristaps #include <assert.h>
27f73abda9Skristaps #include <ctype.h>
28d92dc4efSschwarze #include <limits.h>
293216dddfSschwarze #include <stdio.h>
30f73abda9Skristaps #include <stdlib.h>
31f73abda9Skristaps #include <string.h>
3220fa2881Sschwarze #include <time.h>
33f73abda9Skristaps
344f4f7972Sschwarze #include "mandoc_aux.h"
35d1982c71Sschwarze #include "mandoc.h"
3619b6bef7Sschwarze #include "mandoc_xr.h"
37d1982c71Sschwarze #include "roff.h"
38d1982c71Sschwarze #include "mdoc.h"
39f6854d5cSschwarze #include "libmandoc.h"
40fa2127f9Sschwarze #include "roff_int.h"
41d1982c71Sschwarze #include "libmdoc.h"
426e2a0df9Sschwarze #include "tag.h"
43f73abda9Skristaps
44f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */
45f73abda9Skristaps
46ede1b9d0Sschwarze #define POST_ARGS struct roff_man *mdoc
47f73abda9Skristaps
487c2be9f8Sschwarze enum check_ineq {
497c2be9f8Sschwarze CHECK_LT,
507c2be9f8Sschwarze CHECK_GT,
517c2be9f8Sschwarze CHECK_EQ
527c2be9f8Sschwarze };
537c2be9f8Sschwarze
5498b8f00aSschwarze typedef void (*v_post)(POST_ARGS);
55f73abda9Skristaps
568ccddcd3Sschwarze static int build_list(struct roff_man *, int);
57ede1b9d0Sschwarze static void check_argv(struct roff_man *,
583a0d07afSschwarze struct roff_node *, struct mdoc_argv *);
59ede1b9d0Sschwarze static void check_args(struct roff_man *, struct roff_node *);
606dc98fe5Sschwarze static void check_text(struct roff_man *, int, int, char *);
616dc98fe5Sschwarze static void check_text_em(struct roff_man *, int, int, char *);
6248497dd5Sschwarze static void check_toptext(struct roff_man *, int, int, const char *);
633a0d07afSschwarze static int child_an(const struct roff_node *);
6414a309e3Sschwarze static size_t macro2len(enum roff_tok);
656050a3daSschwarze static void rewrite_macro2len(struct roff_man *, char **);
664482121fSschwarze static int similar(const char *, const char *);
6767c719adSschwarze
6836dc2246Sschwarze static void post_abort(POST_ARGS) __attribute__((__noreturn__));
6998b8f00aSschwarze static void post_an(POST_ARGS);
703e642ba0Sschwarze static void post_an_norm(POST_ARGS);
7198b8f00aSschwarze static void post_at(POST_ARGS);
723e642ba0Sschwarze static void post_bd(POST_ARGS);
7398b8f00aSschwarze static void post_bf(POST_ARGS);
7498b8f00aSschwarze static void post_bk(POST_ARGS);
7598b8f00aSschwarze static void post_bl(POST_ARGS);
7698b8f00aSschwarze static void post_bl_block(POST_ARGS);
7798b8f00aSschwarze static void post_bl_head(POST_ARGS);
783e642ba0Sschwarze static void post_bl_norm(POST_ARGS);
7998b8f00aSschwarze static void post_bx(POST_ARGS);
8098b8f00aSschwarze static void post_defaults(POST_ARGS);
813e642ba0Sschwarze static void post_display(POST_ARGS);
8298b8f00aSschwarze static void post_dd(POST_ARGS);
8304fbb99fSschwarze static void post_delim(POST_ARGS);
84fe8e59edSschwarze static void post_delim_nb(POST_ARGS);
8598b8f00aSschwarze static void post_dt(POST_ARGS);
860ac7e6ecSschwarze static void post_em(POST_ARGS);
8798b8f00aSschwarze static void post_en(POST_ARGS);
880ac7e6ecSschwarze static void post_er(POST_ARGS);
8998b8f00aSschwarze static void post_es(POST_ARGS);
9098b8f00aSschwarze static void post_eoln(POST_ARGS);
9198b8f00aSschwarze static void post_ex(POST_ARGS);
9298b8f00aSschwarze static void post_fa(POST_ARGS);
93b952e091Sschwarze static void post_fl(POST_ARGS);
9498b8f00aSschwarze static void post_fn(POST_ARGS);
9598b8f00aSschwarze static void post_fname(POST_ARGS);
9698b8f00aSschwarze static void post_fo(POST_ARGS);
9798b8f00aSschwarze static void post_hyph(POST_ARGS);
9898b8f00aSschwarze static void post_it(POST_ARGS);
9998b8f00aSschwarze static void post_lb(POST_ARGS);
10098b8f00aSschwarze static void post_nd(POST_ARGS);
10198b8f00aSschwarze static void post_nm(POST_ARGS);
10298b8f00aSschwarze static void post_ns(POST_ARGS);
1033e642ba0Sschwarze static void post_obsolete(POST_ARGS);
10498b8f00aSschwarze static void post_os(POST_ARGS);
10598b8f00aSschwarze static void post_par(POST_ARGS);
1063e642ba0Sschwarze static void post_prevpar(POST_ARGS);
10798b8f00aSschwarze static void post_root(POST_ARGS);
10898b8f00aSschwarze static void post_rs(POST_ARGS);
1098ccddcd3Sschwarze static void post_rv(POST_ARGS);
110ba1a6076Sschwarze static void post_section(POST_ARGS);
11198b8f00aSschwarze static void post_sh(POST_ARGS);
11298b8f00aSschwarze static void post_sh_head(POST_ARGS);
11398b8f00aSschwarze static void post_sh_name(POST_ARGS);
11498b8f00aSschwarze static void post_sh_see_also(POST_ARGS);
11598b8f00aSschwarze static void post_sh_authors(POST_ARGS);
11698b8f00aSschwarze static void post_sm(POST_ARGS);
11798b8f00aSschwarze static void post_st(POST_ARGS);
1183e642ba0Sschwarze static void post_std(POST_ARGS);
119fe8e59edSschwarze static void post_sx(POST_ARGS);
1200ac7e6ecSschwarze static void post_tag(POST_ARGS);
12192929bf6Sschwarze static void post_tg(POST_ARGS);
122bc205043Sschwarze static void post_useless(POST_ARGS);
1235ae08040Sschwarze static void post_xr(POST_ARGS);
124816c3c54Sschwarze static void post_xx(POST_ARGS);
12598b8f00aSschwarze
126b3f54129Sschwarze static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
1273e642ba0Sschwarze post_dd, /* Dd */
1283e642ba0Sschwarze post_dt, /* Dt */
1293e642ba0Sschwarze post_os, /* Os */
1303e642ba0Sschwarze post_sh, /* Sh */
131ba1a6076Sschwarze post_section, /* Ss */
1323e642ba0Sschwarze post_par, /* Pp */
1333e642ba0Sschwarze post_display, /* D1 */
1343e642ba0Sschwarze post_display, /* Dl */
1353e642ba0Sschwarze post_display, /* Bd */
1363e642ba0Sschwarze NULL, /* Ed */
1373e642ba0Sschwarze post_bl, /* Bl */
1383e642ba0Sschwarze NULL, /* El */
1393e642ba0Sschwarze post_it, /* It */
140fe8e59edSschwarze post_delim_nb, /* Ad */
1413e642ba0Sschwarze post_an, /* An */
14214a309e3Sschwarze NULL, /* Ap */
1433e642ba0Sschwarze post_defaults, /* Ar */
1443e642ba0Sschwarze NULL, /* Cd */
1450ac7e6ecSschwarze post_tag, /* Cm */
1460ac7e6ecSschwarze post_tag, /* Dv */
1470ac7e6ecSschwarze post_er, /* Er */
1480ac7e6ecSschwarze post_tag, /* Ev */
1493e642ba0Sschwarze post_ex, /* Ex */
1503e642ba0Sschwarze post_fa, /* Fa */
1513e642ba0Sschwarze NULL, /* Fd */
152b952e091Sschwarze post_fl, /* Fl */
1533e642ba0Sschwarze post_fn, /* Fn */
154fe8e59edSschwarze post_delim_nb, /* Ft */
1550ac7e6ecSschwarze post_tag, /* Ic */
156fe8e59edSschwarze post_delim_nb, /* In */
1570ac7e6ecSschwarze post_tag, /* Li */
1583e642ba0Sschwarze post_nd, /* Nd */
1593e642ba0Sschwarze post_nm, /* Nm */
160fe8e59edSschwarze post_delim_nb, /* Op */
1617c539ecbSschwarze post_abort, /* Ot */
1623e642ba0Sschwarze post_defaults, /* Pa */
1638ccddcd3Sschwarze post_rv, /* Rv */
1643e642ba0Sschwarze post_st, /* St */
165e0c064b2Sschwarze post_tag, /* Va */
166fe8e59edSschwarze post_delim_nb, /* Vt */
1675ae08040Sschwarze post_xr, /* Xr */
1683e642ba0Sschwarze NULL, /* %A */
1693e642ba0Sschwarze post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
1703e642ba0Sschwarze NULL, /* %D */
1713e642ba0Sschwarze NULL, /* %I */
1723e642ba0Sschwarze NULL, /* %J */
1733e642ba0Sschwarze post_hyph, /* %N */
1743e642ba0Sschwarze post_hyph, /* %O */
1753e642ba0Sschwarze NULL, /* %P */
1763e642ba0Sschwarze post_hyph, /* %R */
1773e642ba0Sschwarze post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
1783e642ba0Sschwarze NULL, /* %V */
1793e642ba0Sschwarze NULL, /* Ac */
1801abead14Sschwarze NULL, /* Ao */
181fe8e59edSschwarze post_delim_nb, /* Aq */
1823e642ba0Sschwarze post_at, /* At */
1833e642ba0Sschwarze NULL, /* Bc */
1843e642ba0Sschwarze post_bf, /* Bf */
1851abead14Sschwarze NULL, /* Bo */
1863e642ba0Sschwarze NULL, /* Bq */
187816c3c54Sschwarze post_xx, /* Bsx */
1883e642ba0Sschwarze post_bx, /* Bx */
1893e642ba0Sschwarze post_obsolete, /* Db */
1903e642ba0Sschwarze NULL, /* Dc */
1913e642ba0Sschwarze NULL, /* Do */
1923e642ba0Sschwarze NULL, /* Dq */
1933e642ba0Sschwarze NULL, /* Ec */
1943e642ba0Sschwarze NULL, /* Ef */
1950ac7e6ecSschwarze post_em, /* Em */
1963e642ba0Sschwarze NULL, /* Eo */
197816c3c54Sschwarze post_xx, /* Fx */
1980ac7e6ecSschwarze post_tag, /* Ms */
1990ac7e6ecSschwarze post_tag, /* No */
2003e642ba0Sschwarze post_ns, /* Ns */
201816c3c54Sschwarze post_xx, /* Nx */
202816c3c54Sschwarze post_xx, /* Ox */
2033e642ba0Sschwarze NULL, /* Pc */
2043e642ba0Sschwarze NULL, /* Pf */
2051abead14Sschwarze NULL, /* Po */
206fe8e59edSschwarze post_delim_nb, /* Pq */
2073e642ba0Sschwarze NULL, /* Qc */
208fe8e59edSschwarze post_delim_nb, /* Ql */
2091abead14Sschwarze NULL, /* Qo */
210fe8e59edSschwarze post_delim_nb, /* Qq */
2113e642ba0Sschwarze NULL, /* Re */
2123e642ba0Sschwarze post_rs, /* Rs */
2133e642ba0Sschwarze NULL, /* Sc */
2141abead14Sschwarze NULL, /* So */
215fe8e59edSschwarze post_delim_nb, /* Sq */
2163e642ba0Sschwarze post_sm, /* Sm */
217fe8e59edSschwarze post_sx, /* Sx */
2180ac7e6ecSschwarze post_em, /* Sy */
219bc205043Sschwarze post_useless, /* Tn */
220816c3c54Sschwarze post_xx, /* Ux */
2213e642ba0Sschwarze NULL, /* Xc */
2223e642ba0Sschwarze NULL, /* Xo */
2233e642ba0Sschwarze post_fo, /* Fo */
2243e642ba0Sschwarze NULL, /* Fc */
2251abead14Sschwarze NULL, /* Oo */
2263e642ba0Sschwarze NULL, /* Oc */
2273e642ba0Sschwarze post_bk, /* Bk */
2283e642ba0Sschwarze NULL, /* Ek */
2293e642ba0Sschwarze post_eoln, /* Bt */
23004fbb99fSschwarze post_obsolete, /* Hf */
2313e642ba0Sschwarze post_obsolete, /* Fr */
2323e642ba0Sschwarze post_eoln, /* Ud */
2333e642ba0Sschwarze post_lb, /* Lb */
2347c539ecbSschwarze post_abort, /* Lp */
235fe8e59edSschwarze post_delim_nb, /* Lk */
2363e642ba0Sschwarze post_defaults, /* Mt */
237fe8e59edSschwarze post_delim_nb, /* Brq */
2381abead14Sschwarze NULL, /* Bro */
2393e642ba0Sschwarze NULL, /* Brc */
2403e642ba0Sschwarze NULL, /* %C */
2413e642ba0Sschwarze post_es, /* Es */
2423e642ba0Sschwarze post_en, /* En */
243816c3c54Sschwarze post_xx, /* Dx */
2443e642ba0Sschwarze NULL, /* %Q */
2453e642ba0Sschwarze NULL, /* %U */
2463e642ba0Sschwarze NULL, /* Ta */
24792929bf6Sschwarze post_tg, /* Tg */
248f73abda9Skristaps };
249f73abda9Skristaps
25020fa2881Sschwarze #define RSORD_MAX 14 /* Number of `Rs' blocks. */
25120fa2881Sschwarze
25214a309e3Sschwarze static const enum roff_tok rsord[RSORD_MAX] = {
25320fa2881Sschwarze MDOC__A,
25420fa2881Sschwarze MDOC__T,
25520fa2881Sschwarze MDOC__B,
25620fa2881Sschwarze MDOC__I,
25720fa2881Sschwarze MDOC__J,
25820fa2881Sschwarze MDOC__R,
25920fa2881Sschwarze MDOC__N,
26020fa2881Sschwarze MDOC__V,
2610397c682Sschwarze MDOC__U,
26220fa2881Sschwarze MDOC__P,
26320fa2881Sschwarze MDOC__Q,
2644e32ec8fSschwarze MDOC__C,
26520fa2881Sschwarze MDOC__D,
2664e32ec8fSschwarze MDOC__O
26720fa2881Sschwarze };
26820fa2881Sschwarze
26919a69263Sschwarze static const char * const secnames[SEC__MAX] = {
27019a69263Sschwarze NULL,
27119a69263Sschwarze "NAME",
27219a69263Sschwarze "LIBRARY",
27319a69263Sschwarze "SYNOPSIS",
27419a69263Sschwarze "DESCRIPTION",
27503ab2f23Sdlg "CONTEXT",
27619a69263Sschwarze "IMPLEMENTATION NOTES",
27719a69263Sschwarze "RETURN VALUES",
27819a69263Sschwarze "ENVIRONMENT",
27919a69263Sschwarze "FILES",
28019a69263Sschwarze "EXIT STATUS",
28119a69263Sschwarze "EXAMPLES",
28219a69263Sschwarze "DIAGNOSTICS",
28319a69263Sschwarze "COMPATIBILITY",
28419a69263Sschwarze "ERRORS",
28519a69263Sschwarze "SEE ALSO",
28619a69263Sschwarze "STANDARDS",
28719a69263Sschwarze "HISTORY",
28819a69263Sschwarze "AUTHORS",
28919a69263Sschwarze "CAVEATS",
29019a69263Sschwarze "BUGS",
29119a69263Sschwarze "SECURITY CONSIDERATIONS",
29219a69263Sschwarze NULL
29319a69263Sschwarze };
294f73abda9Skristaps
2950ac7e6ecSschwarze static int fn_prio = TAG_STRONG;
2960ac7e6ecSschwarze
29749aff9f8Sschwarze
2987c539ecbSschwarze /* Validate the subtree rooted at mdoc->last. */
29998b8f00aSschwarze void
mdoc_validate(struct roff_man * mdoc)30083d65a5aSschwarze mdoc_validate(struct roff_man *mdoc)
301f73abda9Skristaps {
3026dc98fe5Sschwarze struct roff_node *n, *np;
30314a309e3Sschwarze const v_post *p;
304f73abda9Skristaps
3057c539ecbSschwarze /*
3067c539ecbSschwarze * Translate obsolete macros to modern macros first
3077c539ecbSschwarze * such that later code does not need to look
3087c539ecbSschwarze * for the obsolete versions.
3097c539ecbSschwarze */
3107c539ecbSschwarze
311753701eeSschwarze n = mdoc->last;
3127c539ecbSschwarze switch (n->tok) {
3137c539ecbSschwarze case MDOC_Lp:
3147c539ecbSschwarze n->tok = MDOC_Pp;
3157c539ecbSschwarze break;
3167c539ecbSschwarze case MDOC_Ot:
3177c539ecbSschwarze post_obsolete(mdoc);
3187c539ecbSschwarze n->tok = MDOC_Ft;
3197c539ecbSschwarze break;
3207c539ecbSschwarze default:
3217c539ecbSschwarze break;
3227c539ecbSschwarze }
3237c539ecbSschwarze
3247c539ecbSschwarze /*
3257c539ecbSschwarze * Iterate over all children, recursing into each one
3267c539ecbSschwarze * in turn, depth-first.
3277c539ecbSschwarze */
3287c539ecbSschwarze
329396853b5Sschwarze mdoc->last = mdoc->last->child;
330396853b5Sschwarze while (mdoc->last != NULL) {
33183d65a5aSschwarze mdoc_validate(mdoc);
332396853b5Sschwarze if (mdoc->last == n)
333396853b5Sschwarze mdoc->last = mdoc->last->child;
334396853b5Sschwarze else
335396853b5Sschwarze mdoc->last = mdoc->last->next;
336396853b5Sschwarze }
337f73abda9Skristaps
3387c539ecbSschwarze /* Finally validate the macro itself. */
3397c539ecbSschwarze
340396853b5Sschwarze mdoc->last = n;
341396853b5Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
342753701eeSschwarze switch (n->type) {
343d1982c71Sschwarze case ROFFT_TEXT:
3446dc98fe5Sschwarze np = n->parent;
34579ca1811Sschwarze if (n->sec != SEC_SYNOPSIS ||
3466dc98fe5Sschwarze (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
3473e642ba0Sschwarze check_text(mdoc, n->line, n->pos, n->string);
3487faedc4aSschwarze if ((n->flags & NODE_NOFILL) == 0 &&
3496dc98fe5Sschwarze (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
3506dc98fe5Sschwarze np->parent->parent->norm->Bl.type != LIST_diag))
3516dc98fe5Sschwarze check_text_em(mdoc, n->line, n->pos, n->string);
3526dc98fe5Sschwarze if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
3536dc98fe5Sschwarze (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
35448497dd5Sschwarze check_toptext(mdoc, n->line, n->pos, n->string);
3553e642ba0Sschwarze break;
3564c293873Sschwarze case ROFFT_COMMENT:
357d1982c71Sschwarze case ROFFT_EQN:
358d1982c71Sschwarze case ROFFT_TBL:
35998b8f00aSschwarze break;
360d1982c71Sschwarze case ROFFT_ROOT:
36198b8f00aSschwarze post_root(mdoc);
36298b8f00aSschwarze break;
3632791bd1cSschwarze default:
3643e642ba0Sschwarze check_args(mdoc, mdoc->last);
3656f9818f6Sschwarze
3666f9818f6Sschwarze /*
3676f9818f6Sschwarze * Closing delimiters are not special at the
3686f9818f6Sschwarze * beginning of a block, opening delimiters
3696f9818f6Sschwarze * are not special at the end.
3706f9818f6Sschwarze */
3716f9818f6Sschwarze
3726f9818f6Sschwarze if (n->child != NULL)
373c4b0939cSschwarze n->child->flags &= ~NODE_DELIMC;
3746f9818f6Sschwarze if (n->last != NULL)
375c4b0939cSschwarze n->last->flags &= ~NODE_DELIMO;
3766f9818f6Sschwarze
3776f9818f6Sschwarze /* Call the macro's postprocessor. */
3786f9818f6Sschwarze
37929478532Sschwarze if (n->tok < ROFF_MAX) {
380c4d3fa85Sschwarze roff_validate(mdoc);
381c4d3fa85Sschwarze break;
38229478532Sschwarze }
38329478532Sschwarze
38429478532Sschwarze assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
385b3f54129Sschwarze p = mdoc_valids + (n->tok - MDOC_Dd);
38698b8f00aSschwarze if (*p)
38798b8f00aSschwarze (*p)(mdoc);
388396853b5Sschwarze if (mdoc->last == n)
389396853b5Sschwarze mdoc_state(mdoc, n);
39098b8f00aSschwarze break;
3912791bd1cSschwarze }
392f73abda9Skristaps }
393f73abda9Skristaps
39498b8f00aSschwarze static void
check_args(struct roff_man * mdoc,struct roff_node * n)395ede1b9d0Sschwarze check_args(struct roff_man *mdoc, struct roff_node *n)
396f73abda9Skristaps {
397f73abda9Skristaps int i;
398f73abda9Skristaps
399f73abda9Skristaps if (NULL == n->args)
40020fa2881Sschwarze return;
401f73abda9Skristaps
402f73abda9Skristaps assert(n->args->argc);
403f73abda9Skristaps for (i = 0; i < (int)n->args->argc; i++)
4047ead8a4eSschwarze check_argv(mdoc, n, &n->args->argv[i]);
405f73abda9Skristaps }
406f73abda9Skristaps
40720fa2881Sschwarze static void
check_argv(struct roff_man * mdoc,struct roff_node * n,struct mdoc_argv * v)408ede1b9d0Sschwarze check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
409f73abda9Skristaps {
410f73abda9Skristaps int i;
411f73abda9Skristaps
412f73abda9Skristaps for (i = 0; i < (int)v->sz; i++)
4137ead8a4eSschwarze check_text(mdoc, v->line, v->pos, v->value[i]);
414f73abda9Skristaps }
415f73abda9Skristaps
41620fa2881Sschwarze static void
check_text(struct roff_man * mdoc,int ln,int pos,char * p)417ede1b9d0Sschwarze check_text(struct roff_man *mdoc, int ln, int pos, char *p)
418f73abda9Skristaps {
41904e980cbSschwarze char *cp;
420769ee804Sschwarze
4217faedc4aSschwarze if (mdoc->last->flags & NODE_NOFILL)
4221cdbf331Sschwarze return;
4231cdbf331Sschwarze
4241cdbf331Sschwarze for (cp = p; NULL != (p = strchr(p, '\t')); p++)
425a5a5f808Sschwarze mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL);
426f73abda9Skristaps }
427f73abda9Skristaps
42898b8f00aSschwarze static void
check_text_em(struct roff_man * mdoc,int ln,int pos,char * p)4296dc98fe5Sschwarze check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
4306dc98fe5Sschwarze {
4316dc98fe5Sschwarze const struct roff_node *np, *nn;
4326dc98fe5Sschwarze char *cp;
4336dc98fe5Sschwarze
4346dc98fe5Sschwarze np = mdoc->last->prev;
4356dc98fe5Sschwarze nn = mdoc->last->next;
4366dc98fe5Sschwarze
4376dc98fe5Sschwarze /* Look for em-dashes wrongly encoded as "--". */
4386dc98fe5Sschwarze
4396dc98fe5Sschwarze for (cp = p; *cp != '\0'; cp++) {
440dfdd4af9Sschwarze if (cp[0] != '-' || cp[1] != '-')
4416dc98fe5Sschwarze continue;
442dfdd4af9Sschwarze cp++;
4436dc98fe5Sschwarze
4446dc98fe5Sschwarze /* Skip input sequences of more than two '-'. */
4456dc98fe5Sschwarze
4466dc98fe5Sschwarze if (cp[1] == '-') {
4476dc98fe5Sschwarze while (cp[1] == '-')
4486dc98fe5Sschwarze cp++;
4496dc98fe5Sschwarze continue;
4506dc98fe5Sschwarze }
4516dc98fe5Sschwarze
4526dc98fe5Sschwarze /* Skip "--" directly attached to something else. */
4536dc98fe5Sschwarze
4546dc98fe5Sschwarze if ((cp - p > 1 && cp[-2] != ' ') ||
4556dc98fe5Sschwarze (cp[1] != '\0' && cp[1] != ' '))
4566dc98fe5Sschwarze continue;
4576dc98fe5Sschwarze
4586dc98fe5Sschwarze /* Require a letter right before or right afterwards. */
4596dc98fe5Sschwarze
4606dc98fe5Sschwarze if ((cp - p > 2 ?
4616dc98fe5Sschwarze isalpha((unsigned char)cp[-3]) :
4626dc98fe5Sschwarze np != NULL &&
4636dc98fe5Sschwarze np->type == ROFFT_TEXT &&
46489db6ba1Sschwarze *np->string != '\0' &&
4656dc98fe5Sschwarze isalpha((unsigned char)np->string[
4666dc98fe5Sschwarze strlen(np->string) - 1])) ||
46700e80a2aSschwarze (cp[1] != '\0' && cp[2] != '\0' ?
4686dc98fe5Sschwarze isalpha((unsigned char)cp[2]) :
4696dc98fe5Sschwarze nn != NULL &&
4706dc98fe5Sschwarze nn->type == ROFFT_TEXT &&
4716dc98fe5Sschwarze isalpha((unsigned char)*nn->string))) {
472a5a5f808Sschwarze mandoc_msg(MANDOCERR_DASHDASH,
4736dc98fe5Sschwarze ln, pos + (int)(cp - p) - 1, NULL);
4746dc98fe5Sschwarze break;
4756dc98fe5Sschwarze }
4766dc98fe5Sschwarze }
4776dc98fe5Sschwarze }
4786dc98fe5Sschwarze
4796dc98fe5Sschwarze static void
check_toptext(struct roff_man * mdoc,int ln,int pos,const char * p)48048497dd5Sschwarze check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
481f0c18971Sschwarze {
48248497dd5Sschwarze const char *cp, *cpr;
48348497dd5Sschwarze
48448497dd5Sschwarze if (*p == '\0')
48548497dd5Sschwarze return;
486f0c18971Sschwarze
487f0c18971Sschwarze if ((cp = strstr(p, "OpenBSD")) != NULL)
488a5a5f808Sschwarze mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox");
489f0c18971Sschwarze if ((cp = strstr(p, "NetBSD")) != NULL)
490a5a5f808Sschwarze mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx");
491f0c18971Sschwarze if ((cp = strstr(p, "FreeBSD")) != NULL)
492a5a5f808Sschwarze mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx");
493f0c18971Sschwarze if ((cp = strstr(p, "DragonFly")) != NULL)
494a5a5f808Sschwarze mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx");
49548497dd5Sschwarze
49648497dd5Sschwarze cp = p;
49748497dd5Sschwarze while ((cp = strstr(cp + 1, "()")) != NULL) {
49848497dd5Sschwarze for (cpr = cp - 1; cpr >= p; cpr--)
49948497dd5Sschwarze if (*cpr != '_' && !isalnum((unsigned char)*cpr))
50048497dd5Sschwarze break;
50148497dd5Sschwarze if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
50248497dd5Sschwarze cpr++;
503a5a5f808Sschwarze mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p),
50448497dd5Sschwarze "%.*s()", (int)(cp - cpr), cpr);
50548497dd5Sschwarze }
50648497dd5Sschwarze }
507f0c18971Sschwarze }
508f0c18971Sschwarze
50936dc2246Sschwarze static void
post_abort(POST_ARGS)5107c539ecbSschwarze post_abort(POST_ARGS)
5117c539ecbSschwarze {
5127c539ecbSschwarze abort();
5137c539ecbSschwarze }
5147c539ecbSschwarze
5157c539ecbSschwarze static void
post_delim(POST_ARGS)51604fbb99fSschwarze post_delim(POST_ARGS)
51704fbb99fSschwarze {
51804fbb99fSschwarze const struct roff_node *nch;
519fe8e59edSschwarze const char *lc;
520fe8e59edSschwarze enum mdelim delim;
521fe8e59edSschwarze enum roff_tok tok;
522fe8e59edSschwarze
523fe8e59edSschwarze tok = mdoc->last->tok;
524fe8e59edSschwarze nch = mdoc->last->last;
525fe8e59edSschwarze if (nch == NULL || nch->type != ROFFT_TEXT)
526fe8e59edSschwarze return;
527fe8e59edSschwarze lc = strchr(nch->string, '\0') - 1;
528fe8e59edSschwarze if (lc < nch->string)
529fe8e59edSschwarze return;
530fe8e59edSschwarze delim = mdoc_isdelim(lc);
531fe8e59edSschwarze if (delim == DELIM_NONE || delim == DELIM_OPEN)
532fe8e59edSschwarze return;
533fe8e59edSschwarze if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
534fe8e59edSschwarze tok == MDOC_Ss || tok == MDOC_Fo))
535fe8e59edSschwarze return;
536fe8e59edSschwarze
537a5a5f808Sschwarze mandoc_msg(MANDOCERR_DELIM, nch->line,
538a5a5f808Sschwarze nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
539fe8e59edSschwarze nch == mdoc->last->child ? "" : " ...", nch->string);
540fe8e59edSschwarze }
541fe8e59edSschwarze
542fe8e59edSschwarze static void
post_delim_nb(POST_ARGS)543fe8e59edSschwarze post_delim_nb(POST_ARGS)
544fe8e59edSschwarze {
545fe8e59edSschwarze const struct roff_node *nch;
546b7c50e87Sschwarze const char *lc, *cp;
547b7c50e87Sschwarze int nw;
54804fbb99fSschwarze enum mdelim delim;
549b7c50e87Sschwarze enum roff_tok tok;
55004fbb99fSschwarze
551b7c50e87Sschwarze /*
552b7c50e87Sschwarze * Find candidates: at least two bytes,
553b7c50e87Sschwarze * the last one a closing or middle delimiter.
554b7c50e87Sschwarze */
555b7c50e87Sschwarze
556b7c50e87Sschwarze tok = mdoc->last->tok;
55704fbb99fSschwarze nch = mdoc->last->last;
55804fbb99fSschwarze if (nch == NULL || nch->type != ROFFT_TEXT)
55904fbb99fSschwarze return;
56004fbb99fSschwarze lc = strchr(nch->string, '\0') - 1;
56104fbb99fSschwarze if (lc <= nch->string)
56204fbb99fSschwarze return;
56304fbb99fSschwarze delim = mdoc_isdelim(lc);
56404fbb99fSschwarze if (delim == DELIM_NONE || delim == DELIM_OPEN)
56504fbb99fSschwarze return;
566b7c50e87Sschwarze
567b7c50e87Sschwarze /*
568b7c50e87Sschwarze * Reduce false positives by allowing various cases.
569b7c50e87Sschwarze */
570b7c50e87Sschwarze
571b7c50e87Sschwarze /* Escaped delimiters. */
572b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '\\' &&
573b7c50e87Sschwarze (lc[-1] == '&' || lc[-1] == 'e'))
574b7c50e87Sschwarze return;
575b7c50e87Sschwarze
576b7c50e87Sschwarze /* Specific byte sequences. */
577b7c50e87Sschwarze switch (*lc) {
578b7c50e87Sschwarze case ')':
579b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--)
580b7c50e87Sschwarze if (*cp == '(')
581b7c50e87Sschwarze return;
582b7c50e87Sschwarze break;
583b7c50e87Sschwarze case '.':
584b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
585b7c50e87Sschwarze return;
586b7c50e87Sschwarze if (lc[-1] == '.')
587b7c50e87Sschwarze return;
588b7c50e87Sschwarze break;
589b7c50e87Sschwarze case ';':
590b7c50e87Sschwarze if (tok == MDOC_Vt)
591b7c50e87Sschwarze return;
592b7c50e87Sschwarze break;
593b7c50e87Sschwarze case '?':
594b7c50e87Sschwarze if (lc[-1] == '?')
595b7c50e87Sschwarze return;
596b7c50e87Sschwarze break;
597b7c50e87Sschwarze case ']':
598b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--)
599b7c50e87Sschwarze if (*cp == '[')
600b7c50e87Sschwarze return;
601b7c50e87Sschwarze break;
602b7c50e87Sschwarze case '|':
603b7c50e87Sschwarze if (lc == nch->string + 1 && lc[-1] == '|')
604b7c50e87Sschwarze return;
605b7c50e87Sschwarze default:
606b7c50e87Sschwarze break;
607b7c50e87Sschwarze }
608b7c50e87Sschwarze
609b7c50e87Sschwarze /* Exactly two non-alphanumeric bytes. */
610b7c50e87Sschwarze if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
611b7c50e87Sschwarze return;
612b7c50e87Sschwarze
613b7c50e87Sschwarze /* At least three alphabetic words with a sentence ending. */
614b7c50e87Sschwarze if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
6151abead14Sschwarze tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
616b7c50e87Sschwarze nw = 0;
617b7c50e87Sschwarze for (cp = lc - 1; cp >= nch->string; cp--) {
618b7c50e87Sschwarze if (*cp == ' ') {
619b7c50e87Sschwarze nw++;
620b7c50e87Sschwarze if (cp > nch->string && cp[-1] == ',')
621b7c50e87Sschwarze cp--;
622b7c50e87Sschwarze } else if (isalpha((unsigned int)*cp)) {
623b7c50e87Sschwarze if (nw > 1)
624b7c50e87Sschwarze return;
625b7c50e87Sschwarze } else
626b7c50e87Sschwarze break;
627b7c50e87Sschwarze }
628b7c50e87Sschwarze }
629b7c50e87Sschwarze
630a5a5f808Sschwarze mandoc_msg(MANDOCERR_DELIM_NB, nch->line,
631a5a5f808Sschwarze nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok],
63204fbb99fSschwarze nch == mdoc->last->child ? "" : " ...", nch->string);
63304fbb99fSschwarze }
63404fbb99fSschwarze
63504fbb99fSschwarze static void
post_bl_norm(POST_ARGS)6363e642ba0Sschwarze post_bl_norm(POST_ARGS)
637f73abda9Skristaps {
6383e642ba0Sschwarze struct roff_node *n;
639aa99c14fSschwarze struct mdoc_argv *argv, *wa;
6404a9f685fSschwarze int i;
641aa99c14fSschwarze enum mdocargt mdoclt;
6424a9f685fSschwarze enum mdoc_list lt;
643f73abda9Skristaps
6443e642ba0Sschwarze n = mdoc->last->parent;
6453e642ba0Sschwarze n->norm->Bl.type = LIST__NONE;
646f73abda9Skristaps
6476093755cSschwarze /*
6486093755cSschwarze * First figure out which kind of list to use: bind ourselves to
6496093755cSschwarze * the first mentioned list type and warn about any remaining
6506093755cSschwarze * ones. If we find no list type, we default to LIST_item.
6516093755cSschwarze */
652f73abda9Skristaps
653dadc3a61Sschwarze wa = (n->args == NULL) ? NULL : n->args->argv;
654aa99c14fSschwarze mdoclt = MDOC_ARG_MAX;
6556093755cSschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) {
6564a9f685fSschwarze argv = n->args->argv + i;
6576093755cSschwarze lt = LIST__NONE;
6584a9f685fSschwarze switch (argv->arg) {
6596093755cSschwarze /* Set list types. */
66049aff9f8Sschwarze case MDOC_Bullet:
6616093755cSschwarze lt = LIST_bullet;
6626093755cSschwarze break;
66349aff9f8Sschwarze case MDOC_Dash:
6646093755cSschwarze lt = LIST_dash;
6656093755cSschwarze break;
66649aff9f8Sschwarze case MDOC_Enum:
6676093755cSschwarze lt = LIST_enum;
6686093755cSschwarze break;
66949aff9f8Sschwarze case MDOC_Hyphen:
6706093755cSschwarze lt = LIST_hyphen;
6716093755cSschwarze break;
67249aff9f8Sschwarze case MDOC_Item:
6736093755cSschwarze lt = LIST_item;
6746093755cSschwarze break;
67549aff9f8Sschwarze case MDOC_Tag:
6766093755cSschwarze lt = LIST_tag;
6776093755cSschwarze break;
67849aff9f8Sschwarze case MDOC_Diag:
6796093755cSschwarze lt = LIST_diag;
6806093755cSschwarze break;
68149aff9f8Sschwarze case MDOC_Hang:
6826093755cSschwarze lt = LIST_hang;
6836093755cSschwarze break;
68449aff9f8Sschwarze case MDOC_Ohang:
6856093755cSschwarze lt = LIST_ohang;
6866093755cSschwarze break;
68749aff9f8Sschwarze case MDOC_Inset:
6886093755cSschwarze lt = LIST_inset;
6896093755cSschwarze break;
69049aff9f8Sschwarze case MDOC_Column:
6916093755cSschwarze lt = LIST_column;
69264d728e4Sschwarze break;
6936093755cSschwarze /* Set list arguments. */
69449aff9f8Sschwarze case MDOC_Compact:
6954a9f685fSschwarze if (n->norm->Bl.comp)
6964a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP,
697a5a5f808Sschwarze argv->line, argv->pos, "Bl -compact");
6984a9f685fSschwarze n->norm->Bl.comp = 1;
69950e63e03Sschwarze break;
70049aff9f8Sschwarze case MDOC_Width:
701aa99c14fSschwarze wa = argv;
7024a9f685fSschwarze if (0 == argv->sz) {
7034a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY,
704a5a5f808Sschwarze argv->line, argv->pos, "Bl -width");
7054a9f685fSschwarze n->norm->Bl.width = "0n";
70622972b14Sschwarze break;
70722972b14Sschwarze }
7084a9f685fSschwarze if (NULL != n->norm->Bl.width)
709a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_REP,
710a5a5f808Sschwarze argv->line, argv->pos,
711a5a5f808Sschwarze "Bl -width %s", argv->value[0]);
7126050a3daSschwarze rewrite_macro2len(mdoc, argv->value);
7134a9f685fSschwarze n->norm->Bl.width = argv->value[0];
71464d728e4Sschwarze break;
71549aff9f8Sschwarze case MDOC_Offset:
7164a9f685fSschwarze if (0 == argv->sz) {
7174a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY,
718a5a5f808Sschwarze argv->line, argv->pos, "Bl -offset");
71931e23753Sschwarze break;
72031e23753Sschwarze }
7214a9f685fSschwarze if (NULL != n->norm->Bl.offs)
722a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_REP,
723a5a5f808Sschwarze argv->line, argv->pos,
724a5a5f808Sschwarze "Bl -offset %s", argv->value[0]);
7256050a3daSschwarze rewrite_macro2len(mdoc, argv->value);
7264a9f685fSschwarze n->norm->Bl.offs = argv->value[0];
727f73abda9Skristaps break;
728ddce0b0cSschwarze default:
729ddce0b0cSschwarze continue;
730f73abda9Skristaps }
731dc0d8bb2Sschwarze if (LIST__NONE == lt)
732dc0d8bb2Sschwarze continue;
733aa99c14fSschwarze mdoclt = argv->arg;
734f73abda9Skristaps
7356093755cSschwarze /* Check: multiple list types. */
7366093755cSschwarze
737dc0d8bb2Sschwarze if (LIST__NONE != n->norm->Bl.type) {
738a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos,
739bd594191Sschwarze "Bl -%s", mdoc_argnames[argv->arg]);
740dc0d8bb2Sschwarze continue;
741769ee804Sschwarze }
7426093755cSschwarze
7436093755cSschwarze /* The list type should come first. */
7446093755cSschwarze
7458c62fbf5Sschwarze if (n->norm->Bl.width ||
7468c62fbf5Sschwarze n->norm->Bl.offs ||
7478c62fbf5Sschwarze n->norm->Bl.comp)
748a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_LATETYPE,
749a5a5f808Sschwarze n->line, n->pos, "Bl -%s",
75066788495Sschwarze mdoc_argnames[n->args->argv[0].arg]);
751dc0d8bb2Sschwarze
752dc0d8bb2Sschwarze n->norm->Bl.type = lt;
753dc0d8bb2Sschwarze if (LIST_column == lt) {
754dc0d8bb2Sschwarze n->norm->Bl.ncols = argv->sz;
755dc0d8bb2Sschwarze n->norm->Bl.cols = (void *)argv->value;
756dc0d8bb2Sschwarze }
7576093755cSschwarze }
7586093755cSschwarze
7596093755cSschwarze /* Allow lists to default to LIST_item. */
7606093755cSschwarze
7618c62fbf5Sschwarze if (LIST__NONE == n->norm->Bl.type) {
762a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl");
7638c62fbf5Sschwarze n->norm->Bl.type = LIST_item;
7642c7eb483Sschwarze mdoclt = MDOC_Item;
7656e03d529Sschwarze }
766f73abda9Skristaps
76764d728e4Sschwarze /*
76864d728e4Sschwarze * Validate the width field. Some list types don't need width
76964d728e4Sschwarze * types and should be warned about them. Others should have it
7705eced068Sschwarze * and must also be warned. Yet others have a default and need
7715eced068Sschwarze * no warning.
77264d728e4Sschwarze */
77364d728e4Sschwarze
7748c62fbf5Sschwarze switch (n->norm->Bl.type) {
77549aff9f8Sschwarze case LIST_tag:
776162c3bafSschwarze if (n->norm->Bl.width == NULL)
777a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_NOWIDTH,
778bd594191Sschwarze n->line, n->pos, "Bl -tag");
779f73abda9Skristaps break;
78049aff9f8Sschwarze case LIST_column:
78149aff9f8Sschwarze case LIST_diag:
78249aff9f8Sschwarze case LIST_ohang:
78349aff9f8Sschwarze case LIST_inset:
78449aff9f8Sschwarze case LIST_item:
785162c3bafSschwarze if (n->norm->Bl.width != NULL)
786a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos,
787a5a5f808Sschwarze "Bl -%s", mdoc_argnames[mdoclt]);
788162c3bafSschwarze n->norm->Bl.width = NULL;
7896093755cSschwarze break;
79049aff9f8Sschwarze case LIST_bullet:
79149aff9f8Sschwarze case LIST_dash:
79249aff9f8Sschwarze case LIST_hyphen:
793162c3bafSschwarze if (n->norm->Bl.width == NULL)
7945eced068Sschwarze n->norm->Bl.width = "2n";
7955eced068Sschwarze break;
79649aff9f8Sschwarze case LIST_enum:
797162c3bafSschwarze if (n->norm->Bl.width == NULL)
7985eced068Sschwarze n->norm->Bl.width = "3n";
7995eced068Sschwarze break;
80064d728e4Sschwarze default:
801f73abda9Skristaps break;
80264d728e4Sschwarze }
803f73abda9Skristaps }
804f73abda9Skristaps
80598b8f00aSschwarze static void
post_bd(POST_ARGS)8063e642ba0Sschwarze post_bd(POST_ARGS)
807f73abda9Skristaps {
8083e642ba0Sschwarze struct roff_node *n;
8094a9f685fSschwarze struct mdoc_argv *argv;
8104a9f685fSschwarze int i;
8114a9f685fSschwarze enum mdoc_disp dt;
812f73abda9Skristaps
8133e642ba0Sschwarze n = mdoc->last;
81431e23753Sschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) {
8154a9f685fSschwarze argv = n->args->argv + i;
81631e23753Sschwarze dt = DISP__NONE;
81731e23753Sschwarze
8184a9f685fSschwarze switch (argv->arg) {
81949aff9f8Sschwarze case MDOC_Centred:
8202065e47aSschwarze dt = DISP_centered;
82131e23753Sschwarze break;
82249aff9f8Sschwarze case MDOC_Ragged:
82331e23753Sschwarze dt = DISP_ragged;
82431e23753Sschwarze break;
82549aff9f8Sschwarze case MDOC_Unfilled:
82631e23753Sschwarze dt = DISP_unfilled;
82731e23753Sschwarze break;
82849aff9f8Sschwarze case MDOC_Filled:
82931e23753Sschwarze dt = DISP_filled;
83031e23753Sschwarze break;
83149aff9f8Sschwarze case MDOC_Literal:
83231e23753Sschwarze dt = DISP_literal;
833f73abda9Skristaps break;
83449aff9f8Sschwarze case MDOC_File:
835a5a5f808Sschwarze mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL);
8361164a325Sschwarze break;
83749aff9f8Sschwarze case MDOC_Offset:
8384a9f685fSschwarze if (0 == argv->sz) {
8394a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY,
840a5a5f808Sschwarze argv->line, argv->pos, "Bd -offset");
841f73abda9Skristaps break;
842f73abda9Skristaps }
8434a9f685fSschwarze if (NULL != n->norm->Bd.offs)
844a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_REP,
845a5a5f808Sschwarze argv->line, argv->pos,
846a5a5f808Sschwarze "Bd -offset %s", argv->value[0]);
8476050a3daSschwarze rewrite_macro2len(mdoc, argv->value);
8484a9f685fSschwarze n->norm->Bd.offs = argv->value[0];
84931e23753Sschwarze break;
85049aff9f8Sschwarze case MDOC_Compact:
8514a9f685fSschwarze if (n->norm->Bd.comp)
8524a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP,
853a5a5f808Sschwarze argv->line, argv->pos, "Bd -compact");
8544a9f685fSschwarze n->norm->Bd.comp = 1;
85531e23753Sschwarze break;
85631e23753Sschwarze default:
85731e23753Sschwarze abort();
85831e23753Sschwarze }
859dc0d8bb2Sschwarze if (DISP__NONE == dt)
860dc0d8bb2Sschwarze continue;
86131e23753Sschwarze
862dc0d8bb2Sschwarze if (DISP__NONE == n->norm->Bd.type)
8638c62fbf5Sschwarze n->norm->Bd.type = dt;
864dc0d8bb2Sschwarze else
865a5a5f808Sschwarze mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos,
866bd594191Sschwarze "Bd -%s", mdoc_argnames[argv->arg]);
86731e23753Sschwarze }
86831e23753Sschwarze
8698c62fbf5Sschwarze if (DISP__NONE == n->norm->Bd.type) {
870a5a5f808Sschwarze mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd");
8718c62fbf5Sschwarze n->norm->Bd.type = DISP_ragged;
87231e23753Sschwarze }
873f73abda9Skristaps }
874f73abda9Skristaps
8758ccddcd3Sschwarze /*
8768ccddcd3Sschwarze * Stand-alone line macros.
8778ccddcd3Sschwarze */
8788ccddcd3Sschwarze
87998b8f00aSschwarze static void
post_an_norm(POST_ARGS)8803e642ba0Sschwarze post_an_norm(POST_ARGS)
881f73abda9Skristaps {
8823e642ba0Sschwarze struct roff_node *n;
883aa99c14fSschwarze struct mdoc_argv *argv;
884aa99c14fSschwarze size_t i;
885f73abda9Skristaps
8863e642ba0Sschwarze n = mdoc->last;
887aa99c14fSschwarze if (n->args == NULL)
88898b8f00aSschwarze return;
889769ee804Sschwarze
890aa99c14fSschwarze for (i = 1; i < n->args->argc; i++) {
891aa99c14fSschwarze argv = n->args->argv + i;
892a5a5f808Sschwarze mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos,
893aa99c14fSschwarze "An -%s", mdoc_argnames[argv->arg]);
894aa99c14fSschwarze }
8957c2be9f8Sschwarze
896aa99c14fSschwarze argv = n->args->argv;
897aa99c14fSschwarze if (argv->arg == MDOC_Split)
8988c62fbf5Sschwarze n->norm->An.auth = AUTH_split;
899aa99c14fSschwarze else if (argv->arg == MDOC_Nosplit)
9008c62fbf5Sschwarze n->norm->An.auth = AUTH_nosplit;
901769ee804Sschwarze else
902769ee804Sschwarze abort();
903f73abda9Skristaps }
904f73abda9Skristaps
90598b8f00aSschwarze static void
post_eoln(POST_ARGS)9068ccddcd3Sschwarze post_eoln(POST_ARGS)
9078ccddcd3Sschwarze {
9088ccddcd3Sschwarze struct roff_node *n;
9098ccddcd3Sschwarze
910bc205043Sschwarze post_useless(mdoc);
9118ccddcd3Sschwarze n = mdoc->last;
9128ccddcd3Sschwarze if (n->child != NULL)
913a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, n->line,
91414a309e3Sschwarze n->pos, "%s %s", roff_name[n->tok], n->child->string);
9158ccddcd3Sschwarze
9168ccddcd3Sschwarze while (n->child != NULL)
9178ccddcd3Sschwarze roff_node_delete(mdoc, n->child);
9188ccddcd3Sschwarze
9198ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
9208ccddcd3Sschwarze "is currently in beta test." : "currently under development.");
9218ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
9228ccddcd3Sschwarze mdoc->last = n;
9238ccddcd3Sschwarze }
9248ccddcd3Sschwarze
9258ccddcd3Sschwarze static int
build_list(struct roff_man * mdoc,int tok)9268ccddcd3Sschwarze build_list(struct roff_man *mdoc, int tok)
9278ccddcd3Sschwarze {
9288ccddcd3Sschwarze struct roff_node *n;
9298ccddcd3Sschwarze int ic;
9308ccddcd3Sschwarze
9318ccddcd3Sschwarze n = mdoc->last->next;
9328ccddcd3Sschwarze for (ic = 1;; ic++) {
9338ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, tok);
9348ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9358251afdeSschwarze roff_node_relink(mdoc, n);
9368ccddcd3Sschwarze n = mdoc->last = mdoc->last->parent;
9378ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
9388ccddcd3Sschwarze if (n->next == NULL)
9398ccddcd3Sschwarze return ic;
9408ccddcd3Sschwarze if (ic > 1 || n->next->next != NULL) {
9418ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, ",");
9428ccddcd3Sschwarze mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
9438ccddcd3Sschwarze }
9448ccddcd3Sschwarze n = mdoc->last->next;
9458ccddcd3Sschwarze if (n->next == NULL) {
9468ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "and");
9478ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9488ccddcd3Sschwarze }
9498ccddcd3Sschwarze }
9508ccddcd3Sschwarze }
9518ccddcd3Sschwarze
9528ccddcd3Sschwarze static void
post_ex(POST_ARGS)9538ccddcd3Sschwarze post_ex(POST_ARGS)
9548ccddcd3Sschwarze {
9558ccddcd3Sschwarze struct roff_node *n;
9568ccddcd3Sschwarze int ic;
9578ccddcd3Sschwarze
9588ccddcd3Sschwarze post_std(mdoc);
9598ccddcd3Sschwarze
9608ccddcd3Sschwarze n = mdoc->last;
9618ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD;
9628ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The");
9638ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9648ccddcd3Sschwarze
9658ccddcd3Sschwarze if (mdoc->last->next != NULL)
9668ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Nm);
9678ccddcd3Sschwarze else if (mdoc->meta.name != NULL) {
9688ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
9698ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9708ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
9718ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9728ccddcd3Sschwarze mdoc->last = mdoc->last->parent;
9738ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
9748ccddcd3Sschwarze ic = 1;
9758ccddcd3Sschwarze } else {
976a5a5f808Sschwarze mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex");
9778ccddcd3Sschwarze ic = 0;
9788ccddcd3Sschwarze }
9798ccddcd3Sschwarze
9808ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos,
9818ccddcd3Sschwarze ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
9828ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
9838ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos,
9848ccddcd3Sschwarze "on success, and\\~>0 if an error occurs.");
9858ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
9868ccddcd3Sschwarze mdoc->last = n;
9878ccddcd3Sschwarze }
9888ccddcd3Sschwarze
9898ccddcd3Sschwarze static void
post_lb(POST_ARGS)9908ccddcd3Sschwarze post_lb(POST_ARGS)
9918ccddcd3Sschwarze {
9928ccddcd3Sschwarze struct roff_node *n;
9938ccddcd3Sschwarze
994fe8e59edSschwarze post_delim_nb(mdoc);
99504fbb99fSschwarze
9968ccddcd3Sschwarze n = mdoc->last;
9978ccddcd3Sschwarze assert(n->child->type == ROFFT_TEXT);
9988ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD;
9998ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "library");
10008ccddcd3Sschwarze mdoc->last->flags = NODE_NOSRC;
1001965f5c87Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
10028ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
10038ccddcd3Sschwarze mdoc->last = mdoc->last->next;
1004965f5c87Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
10058ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
10068ccddcd3Sschwarze mdoc->last = n;
10078ccddcd3Sschwarze }
10088ccddcd3Sschwarze
10098ccddcd3Sschwarze static void
post_rv(POST_ARGS)10108ccddcd3Sschwarze post_rv(POST_ARGS)
10118ccddcd3Sschwarze {
10128ccddcd3Sschwarze struct roff_node *n;
10138ccddcd3Sschwarze int ic;
10148ccddcd3Sschwarze
10158ccddcd3Sschwarze post_std(mdoc);
10168ccddcd3Sschwarze
10178ccddcd3Sschwarze n = mdoc->last;
10188ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD;
10198ccddcd3Sschwarze if (n->child != NULL) {
10208ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The");
10218ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10228ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Fn);
10238ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos,
10248ccddcd3Sschwarze ic > 1 ? "functions return" : "function returns");
10258ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10268ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos,
10278ccddcd3Sschwarze "the value\\~0 if successful;");
10288ccddcd3Sschwarze } else
10298ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
10308ccddcd3Sschwarze "completion, the value\\~0 is returned;");
10318ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10328ccddcd3Sschwarze
10338ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
10348ccddcd3Sschwarze "the value\\~\\-1 is returned and the global variable");
10358ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10368ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
10378ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10388ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "errno");
10398ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10408ccddcd3Sschwarze mdoc->last = mdoc->last->parent;
10418ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
10428ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos,
10438ccddcd3Sschwarze "is set to indicate the error.");
10448ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
10458ccddcd3Sschwarze mdoc->last = n;
10468ccddcd3Sschwarze }
10478ccddcd3Sschwarze
10488ccddcd3Sschwarze static void
post_std(POST_ARGS)10493e642ba0Sschwarze post_std(POST_ARGS)
1050f73abda9Skristaps {
10513e642ba0Sschwarze struct roff_node *n;
1052f73abda9Skristaps
1053fe8e59edSschwarze post_delim(mdoc);
1054fe8e59edSschwarze
10553e642ba0Sschwarze n = mdoc->last;
10563e642ba0Sschwarze if (n->args && n->args->argc == 1)
10573e642ba0Sschwarze if (n->args->argv[0].arg == MDOC_Std)
105898b8f00aSschwarze return;
1059f73abda9Skristaps
1060a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos,
1061a5a5f808Sschwarze "%s", roff_name[n->tok]);
10626093755cSschwarze }
10636093755cSschwarze
106498b8f00aSschwarze static void
post_st(POST_ARGS)10658ccddcd3Sschwarze post_st(POST_ARGS)
10668ccddcd3Sschwarze {
10678ccddcd3Sschwarze struct roff_node *n, *nch;
10688ccddcd3Sschwarze const char *p;
10698ccddcd3Sschwarze
10708ccddcd3Sschwarze n = mdoc->last;
10718ccddcd3Sschwarze nch = n->child;
10728ccddcd3Sschwarze assert(nch->type == ROFFT_TEXT);
10738ccddcd3Sschwarze
10748ccddcd3Sschwarze if ((p = mdoc_a2st(nch->string)) == NULL) {
1075a5a5f808Sschwarze mandoc_msg(MANDOCERR_ST_BAD,
10768ccddcd3Sschwarze nch->line, nch->pos, "St %s", nch->string);
10778ccddcd3Sschwarze roff_node_delete(mdoc, n);
10788ccddcd3Sschwarze return;
10798ccddcd3Sschwarze }
10808ccddcd3Sschwarze
10818ccddcd3Sschwarze nch->flags |= NODE_NOPRT;
10828ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD;
10838ccddcd3Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, p);
10848ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC;
10858ccddcd3Sschwarze mdoc->last= n;
10868ccddcd3Sschwarze }
10878ccddcd3Sschwarze
10888ccddcd3Sschwarze static void
post_tg(POST_ARGS)108992929bf6Sschwarze post_tg(POST_ARGS)
109092929bf6Sschwarze {
10910ac7e6ecSschwarze struct roff_node *n; /* The .Tg node. */
10920ac7e6ecSschwarze struct roff_node *nch; /* The first child of the .Tg node. */
10930ac7e6ecSschwarze struct roff_node *nn; /* The next node after the .Tg node. */
1094e053e0fdSschwarze struct roff_node *np; /* The parent of the next node. */
10950ac7e6ecSschwarze struct roff_node *nt; /* The TEXT node containing the tag. */
10960ac7e6ecSschwarze size_t len; /* The number of bytes in the tag. */
109792929bf6Sschwarze
10989a542ed3Sschwarze /* Find the next node. */
109992929bf6Sschwarze n = mdoc->last;
11009a542ed3Sschwarze for (nn = n; nn != NULL; nn = nn->parent) {
1101176a26abSschwarze if (nn->type != ROFFT_HEAD && nn->type != ROFFT_BODY &&
1102176a26abSschwarze nn->type != ROFFT_TAIL && nn->next != NULL) {
11039a542ed3Sschwarze nn = nn->next;
11049a542ed3Sschwarze break;
11059a542ed3Sschwarze }
11069a542ed3Sschwarze }
11079a542ed3Sschwarze
11080ac7e6ecSschwarze /* Find the tag. */
11090ac7e6ecSschwarze nt = nch = n->child;
11100ac7e6ecSschwarze if (nch == NULL && nn != NULL && nn->child != NULL &&
11110ac7e6ecSschwarze nn->child->type == ROFFT_TEXT)
11120ac7e6ecSschwarze nt = nn->child;
11139a542ed3Sschwarze
11140ac7e6ecSschwarze /* Validate the tag. */
11150ac7e6ecSschwarze if (nt == NULL || *nt->string == '\0')
111692929bf6Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
11170ac7e6ecSschwarze if (nt == NULL) {
111892929bf6Sschwarze roff_node_delete(mdoc, n);
111992929bf6Sschwarze return;
112092929bf6Sschwarze }
11210ac7e6ecSschwarze len = strcspn(nt->string, " \t\\");
11220ac7e6ecSschwarze if (nt->string[len] != '\0')
11230ac7e6ecSschwarze mandoc_msg(MANDOCERR_TG_SPC, nt->line,
11240ac7e6ecSschwarze nt->pos + len, "Tg %s", nt->string);
11259a542ed3Sschwarze
11269a542ed3Sschwarze /* Keep only the first argument. */
11270ac7e6ecSschwarze if (nch != NULL && nch->next != NULL) {
112892929bf6Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
112992929bf6Sschwarze nch->next->pos, "Tg ... %s", nch->next->string);
113092929bf6Sschwarze while (nch->next != NULL)
113192929bf6Sschwarze roff_node_delete(mdoc, nch->next);
113292929bf6Sschwarze }
11339a542ed3Sschwarze
11349a542ed3Sschwarze /* Drop the macro if the first argument is invalid. */
11350ac7e6ecSschwarze if (len == 0 || nt->string[len] != '\0') {
113692929bf6Sschwarze roff_node_delete(mdoc, n);
11379a542ed3Sschwarze return;
11389a542ed3Sschwarze }
11399a542ed3Sschwarze
11400ac7e6ecSschwarze /* By default, tag the .Tg node itself. */
1141e053e0fdSschwarze if (nn == NULL || nn->flags & NODE_ID)
11420ac7e6ecSschwarze nn = n;
11439a542ed3Sschwarze
11449a542ed3Sschwarze /* Explicit tagging of specific macros. */
11459a542ed3Sschwarze switch (nn->tok) {
11469a542ed3Sschwarze case MDOC_Sh:
11479a542ed3Sschwarze case MDOC_Ss:
11480ac7e6ecSschwarze case MDOC_Fo:
1149e053e0fdSschwarze nn = nn->head->child == NULL ? n : nn->head;
1150e053e0fdSschwarze break;
1151e053e0fdSschwarze case MDOC_It:
1152e053e0fdSschwarze np = nn->parent;
1153e053e0fdSschwarze while (np->tok != MDOC_Bl)
1154e053e0fdSschwarze np = np->parent;
1155e053e0fdSschwarze switch (np->norm->Bl.type) {
1156e053e0fdSschwarze case LIST_column:
1157e053e0fdSschwarze break;
1158e053e0fdSschwarze case LIST_diag:
1159e053e0fdSschwarze case LIST_hang:
1160e053e0fdSschwarze case LIST_inset:
1161e053e0fdSschwarze case LIST_ohang:
1162e053e0fdSschwarze case LIST_tag:
11630ac7e6ecSschwarze nn = nn->head;
1164e053e0fdSschwarze break;
1165e053e0fdSschwarze case LIST_bullet:
1166e053e0fdSschwarze case LIST_dash:
1167e053e0fdSschwarze case LIST_enum:
1168e053e0fdSschwarze case LIST_hyphen:
1169e053e0fdSschwarze case LIST_item:
1170e053e0fdSschwarze nn = nn->body->child == NULL ? n : nn->body;
1171e053e0fdSschwarze break;
1172e053e0fdSschwarze default:
1173e053e0fdSschwarze abort();
1174e053e0fdSschwarze }
1175e053e0fdSschwarze break;
1176e053e0fdSschwarze case MDOC_Bd:
1177e053e0fdSschwarze case MDOC_Bl:
1178e053e0fdSschwarze case MDOC_D1:
1179e053e0fdSschwarze case MDOC_Dl:
1180e053e0fdSschwarze nn = nn->body->child == NULL ? n : nn->body;
1181e053e0fdSschwarze break;
1182e053e0fdSschwarze case MDOC_Pp:
1183e053e0fdSschwarze break;
11840ac7e6ecSschwarze case MDOC_Cm:
11850ac7e6ecSschwarze case MDOC_Dv:
11860ac7e6ecSschwarze case MDOC_Em:
11870ac7e6ecSschwarze case MDOC_Er:
11880ac7e6ecSschwarze case MDOC_Ev:
11890ac7e6ecSschwarze case MDOC_Fl:
11900ac7e6ecSschwarze case MDOC_Fn:
11910ac7e6ecSschwarze case MDOC_Ic:
11920ac7e6ecSschwarze case MDOC_Li:
11930ac7e6ecSschwarze case MDOC_Ms:
11940ac7e6ecSschwarze case MDOC_No:
11950ac7e6ecSschwarze case MDOC_Sy:
1196e053e0fdSschwarze if (nn->child == NULL)
1197e053e0fdSschwarze nn = n;
11989a542ed3Sschwarze break;
11999a542ed3Sschwarze default:
12000ac7e6ecSschwarze nn = n;
12019a542ed3Sschwarze break;
12029a542ed3Sschwarze }
12030ac7e6ecSschwarze tag_put(nt->string, TAG_MANUAL, nn);
12040ac7e6ecSschwarze if (nn != n)
12050ac7e6ecSschwarze n->flags |= NODE_NOPRT;
120692929bf6Sschwarze }
120792929bf6Sschwarze
120892929bf6Sschwarze static void
post_obsolete(POST_ARGS)12093e642ba0Sschwarze post_obsolete(POST_ARGS)
1210551cd4a8Sschwarze {
12113e642ba0Sschwarze struct roff_node *n;
1212551cd4a8Sschwarze
12133e642ba0Sschwarze n = mdoc->last;
1214d1982c71Sschwarze if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1215a5a5f808Sschwarze mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos,
1216a5a5f808Sschwarze "%s", roff_name[n->tok]);
1217551cd4a8Sschwarze }
1218551cd4a8Sschwarze
1219bc205043Sschwarze static void
post_useless(POST_ARGS)1220bc205043Sschwarze post_useless(POST_ARGS)
1221bc205043Sschwarze {
1222bc205043Sschwarze struct roff_node *n;
1223bc205043Sschwarze
1224bc205043Sschwarze n = mdoc->last;
1225a5a5f808Sschwarze mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos,
1226a5a5f808Sschwarze "%s", roff_name[n->tok]);
1227bc205043Sschwarze }
1228bc205043Sschwarze
12298ccddcd3Sschwarze /*
12308ccddcd3Sschwarze * Block macros.
12318ccddcd3Sschwarze */
12328ccddcd3Sschwarze
123398b8f00aSschwarze static void
post_bf(POST_ARGS)1234f73abda9Skristaps post_bf(POST_ARGS)
1235f73abda9Skristaps {
12363a0d07afSschwarze struct roff_node *np, *nch;
1237f73abda9Skristaps
1238769ee804Sschwarze /*
1239769ee804Sschwarze * Unlike other data pointers, these are "housed" by the HEAD
1240769ee804Sschwarze * element, which contains the goods.
1241769ee804Sschwarze */
1242769ee804Sschwarze
1243769ee804Sschwarze np = mdoc->last;
1244d1982c71Sschwarze if (np->type != ROFFT_HEAD)
1245ae2efdd8Sschwarze return;
1246ae2efdd8Sschwarze
1247d1982c71Sschwarze assert(np->parent->type == ROFFT_BLOCK);
1248f051602aSschwarze assert(np->parent->tok == MDOC_Bf);
124950d41253Sschwarze
1250ecb10c32Sschwarze /* Check the number of arguments. */
1251f73abda9Skristaps
1252ecb10c32Sschwarze nch = np->child;
1253f051602aSschwarze if (np->parent->args == NULL) {
1254f051602aSschwarze if (nch == NULL) {
1255a5a5f808Sschwarze mandoc_msg(MANDOCERR_BF_NOFONT,
1256bd594191Sschwarze np->line, np->pos, "Bf");
125798b8f00aSschwarze return;
125820fa2881Sschwarze }
1259ecb10c32Sschwarze nch = nch->next;
1260ecb10c32Sschwarze }
1261f051602aSschwarze if (nch != NULL)
1262a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS,
1263ecb10c32Sschwarze nch->line, nch->pos, "Bf ... %s", nch->string);
1264769ee804Sschwarze
1265769ee804Sschwarze /* Extract argument into data. */
1266769ee804Sschwarze
1267f051602aSschwarze if (np->parent->args != NULL) {
1268f051602aSschwarze switch (np->parent->args->argv[0].arg) {
1269f051602aSschwarze case MDOC_Emphasis:
12708c62fbf5Sschwarze np->norm->Bf.font = FONT_Em;
1271f051602aSschwarze break;
1272f051602aSschwarze case MDOC_Literal:
12738c62fbf5Sschwarze np->norm->Bf.font = FONT_Li;
1274f051602aSschwarze break;
1275f051602aSschwarze case MDOC_Symbolic:
12768c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy;
1277f051602aSschwarze break;
1278f051602aSschwarze default:
1279769ee804Sschwarze abort();
1280f051602aSschwarze }
128198b8f00aSschwarze return;
1282769ee804Sschwarze }
1283769ee804Sschwarze
1284769ee804Sschwarze /* Extract parameter into data. */
1285769ee804Sschwarze
1286f051602aSschwarze if ( ! strcmp(np->child->string, "Em"))
12878c62fbf5Sschwarze np->norm->Bf.font = FONT_Em;
1288f051602aSschwarze else if ( ! strcmp(np->child->string, "Li"))
12898c62fbf5Sschwarze np->norm->Bf.font = FONT_Li;
1290f051602aSschwarze else if ( ! strcmp(np->child->string, "Sy"))
12918c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy;
129220fa2881Sschwarze else
1293a5a5f808Sschwarze mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line,
1294a5a5f808Sschwarze np->child->pos, "Bf %s", np->child->string);
1295f73abda9Skristaps }
1296f73abda9Skristaps
129798b8f00aSschwarze static void
post_fname(POST_ARGS)12980c5064e3Sschwarze post_fname(POST_ARGS)
12990c5064e3Sschwarze {
13000ac7e6ecSschwarze struct roff_node *n, *nch;
13010ff14c71Sschwarze const char *cp;
13020c5064e3Sschwarze size_t pos;
13030c5064e3Sschwarze
13040ac7e6ecSschwarze n = mdoc->last;
13050ac7e6ecSschwarze nch = n->child;
13060ac7e6ecSschwarze cp = nch->string;
13078dbab69bSschwarze if (*cp == '(') {
13088dbab69bSschwarze if (cp[strlen(cp + 1)] == ')')
13098dbab69bSschwarze return;
13108dbab69bSschwarze pos = 0;
13118dbab69bSschwarze } else {
13128dbab69bSschwarze pos = strcspn(cp, "()");
13130ac7e6ecSschwarze if (cp[pos] == '\0') {
13140ac7e6ecSschwarze if (n->sec == SEC_DESCRIPTION ||
13150ac7e6ecSschwarze n->sec == SEC_CUSTOM)
13160ac7e6ecSschwarze tag_put(NULL, fn_prio++, n);
13178dbab69bSschwarze return;
13188dbab69bSschwarze }
13190ac7e6ecSschwarze }
13200ac7e6ecSschwarze mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp);
13210c5064e3Sschwarze }
13220c5064e3Sschwarze
132398b8f00aSschwarze static void
post_fn(POST_ARGS)13240c5064e3Sschwarze post_fn(POST_ARGS)
13250c5064e3Sschwarze {
13260c5064e3Sschwarze post_fname(mdoc);
13270c5064e3Sschwarze post_fa(mdoc);
13280c5064e3Sschwarze }
13290c5064e3Sschwarze
133098b8f00aSschwarze static void
post_fo(POST_ARGS)1331753701eeSschwarze post_fo(POST_ARGS)
1332753701eeSschwarze {
13333a0d07afSschwarze const struct roff_node *n;
1334753701eeSschwarze
1335afcd1f03Sschwarze n = mdoc->last;
1336afcd1f03Sschwarze
1337d1982c71Sschwarze if (n->type != ROFFT_HEAD)
1338afcd1f03Sschwarze return;
1339afcd1f03Sschwarze
1340afcd1f03Sschwarze if (n->child == NULL) {
1341a5a5f808Sschwarze mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo");
1342afcd1f03Sschwarze return;
1343afcd1f03Sschwarze }
1344afcd1f03Sschwarze if (n->child != n->last) {
1345a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS,
1346afcd1f03Sschwarze n->child->next->line, n->child->next->pos,
1347afcd1f03Sschwarze "Fo ... %s", n->child->next->string);
1348afcd1f03Sschwarze while (n->child != n->last)
1349fa2127f9Sschwarze roff_node_delete(mdoc, n->last);
1350fe8e59edSschwarze } else
1351fe8e59edSschwarze post_delim(mdoc);
1352afcd1f03Sschwarze
13530c5064e3Sschwarze post_fname(mdoc);
1354753701eeSschwarze }
1355753701eeSschwarze
135698b8f00aSschwarze static void
post_fa(POST_ARGS)13577e92c062Sschwarze post_fa(POST_ARGS)
13587e92c062Sschwarze {
13593a0d07afSschwarze const struct roff_node *n;
13607e92c062Sschwarze const char *cp;
13617e92c062Sschwarze
13627e92c062Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) {
13637e92c062Sschwarze for (cp = n->string; *cp != '\0'; cp++) {
13647e92c062Sschwarze /* Ignore callbacks and alterations. */
13657e92c062Sschwarze if (*cp == '(' || *cp == '{')
13667e92c062Sschwarze break;
13677e92c062Sschwarze if (*cp != ',')
13687e92c062Sschwarze continue;
1369a5a5f808Sschwarze mandoc_msg(MANDOCERR_FA_COMMA, n->line,
1370a5a5f808Sschwarze n->pos + (int)(cp - n->string), "%s", n->string);
13717e92c062Sschwarze break;
13727e92c062Sschwarze }
13737e92c062Sschwarze }
1374fe8e59edSschwarze post_delim_nb(mdoc);
13757e92c062Sschwarze }
13767e92c062Sschwarze
137798b8f00aSschwarze static void
post_nm(POST_ARGS)1378f73abda9Skristaps post_nm(POST_ARGS)
1379f73abda9Skristaps {
13803a0d07afSschwarze struct roff_node *n;
13812d266539Sschwarze
13822d266539Sschwarze n = mdoc->last;
13832d266539Sschwarze
13842ab19127Sschwarze if (n->sec == SEC_NAME && n->child != NULL &&
13852ab19127Sschwarze n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
138652d11c96Sschwarze mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
138752d11c96Sschwarze
13887c539ecbSschwarze if (n->last != NULL && n->last->tok == MDOC_Pp)
13898251afdeSschwarze roff_node_relink(mdoc, n->last);
139020fa2881Sschwarze
1391f27faaccSschwarze if (mdoc->meta.name == NULL)
1392423631c9Sschwarze deroff(&mdoc->meta.name, n);
139320fa2881Sschwarze
1394f27faaccSschwarze if (mdoc->meta.name == NULL ||
1395f27faaccSschwarze (mdoc->lastsec == SEC_NAME && n->child == NULL))
1396a5a5f808Sschwarze mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm");
139790957cf5Sschwarze
1398fe8e59edSschwarze switch (n->type) {
1399fe8e59edSschwarze case ROFFT_ELEM:
1400fe8e59edSschwarze post_delim_nb(mdoc);
1401fe8e59edSschwarze break;
1402fe8e59edSschwarze case ROFFT_HEAD:
140304fbb99fSschwarze post_delim(mdoc);
1404fe8e59edSschwarze break;
1405fe8e59edSschwarze default:
1406fe8e59edSschwarze return;
1407fe8e59edSschwarze }
140804fbb99fSschwarze
1409fe8e59edSschwarze if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
141090957cf5Sschwarze mdoc->meta.name == NULL)
141190957cf5Sschwarze return;
141290957cf5Sschwarze
141390957cf5Sschwarze mdoc->next = ROFF_NEXT_CHILD;
141490957cf5Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
141590957cf5Sschwarze mdoc->last->flags |= NODE_NOSRC;
141690957cf5Sschwarze mdoc->last = n;
141720fa2881Sschwarze }
141820fa2881Sschwarze
141998b8f00aSschwarze static void
post_nd(POST_ARGS)1420753701eeSschwarze post_nd(POST_ARGS)
1421753701eeSschwarze {
14223a0d07afSschwarze struct roff_node *n;
1423753701eeSschwarze
14241570daf1Sschwarze n = mdoc->last;
14251570daf1Sschwarze
1426d1982c71Sschwarze if (n->type != ROFFT_BODY)
14271570daf1Sschwarze return;
14281570daf1Sschwarze
142956e9e976Sschwarze if (n->sec != SEC_NAME)
1430a5a5f808Sschwarze mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd");
143156e9e976Sschwarze
14321570daf1Sschwarze if (n->child == NULL)
1433a5a5f808Sschwarze mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd");
1434fe8e59edSschwarze else
1435fe8e59edSschwarze post_delim(mdoc);
14361570daf1Sschwarze
143798b8f00aSschwarze post_hyph(mdoc);
1438753701eeSschwarze }
1439753701eeSschwarze
144098b8f00aSschwarze static void
post_display(POST_ARGS)14413e642ba0Sschwarze post_display(POST_ARGS)
1442753701eeSschwarze {
14433e642ba0Sschwarze struct roff_node *n, *np;
1444753701eeSschwarze
1445b7530f2fSschwarze n = mdoc->last;
14463e642ba0Sschwarze switch (n->type) {
14473e642ba0Sschwarze case ROFFT_BODY:
14484b5c4138Sschwarze if (n->end != ENDBODY_NOT) {
1449611d9138Sschwarze if (n->tok == MDOC_Bd &&
1450611d9138Sschwarze n->body->parent->args == NULL)
14514b5c4138Sschwarze roff_node_delete(mdoc, n);
14524b5c4138Sschwarze } else if (n->child == NULL)
1453a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos,
1454a5a5f808Sschwarze "%s", roff_name[n->tok]);
14553e642ba0Sschwarze else if (n->tok == MDOC_D1)
14563e642ba0Sschwarze post_hyph(mdoc);
14573e642ba0Sschwarze break;
14583e642ba0Sschwarze case ROFFT_BLOCK:
14593e642ba0Sschwarze if (n->tok == MDOC_Bd) {
1460a43e24e2Sschwarze if (n->args == NULL) {
1461a43e24e2Sschwarze mandoc_msg(MANDOCERR_BD_NOARG,
1462a5a5f808Sschwarze n->line, n->pos, "Bd");
1463a43e24e2Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
1464a43e24e2Sschwarze while (n->body->child != NULL)
14658251afdeSschwarze roff_node_relink(mdoc,
1466a43e24e2Sschwarze n->body->child);
1467a43e24e2Sschwarze roff_node_delete(mdoc, n);
1468a43e24e2Sschwarze break;
1469a43e24e2Sschwarze }
14703e642ba0Sschwarze post_bd(mdoc);
14713e642ba0Sschwarze post_prevpar(mdoc);
14723e642ba0Sschwarze }
14733e642ba0Sschwarze for (np = n->parent; np != NULL; np = np->parent) {
14743e642ba0Sschwarze if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1475a5a5f808Sschwarze mandoc_msg(MANDOCERR_BD_NEST, n->line,
1476a5a5f808Sschwarze n->pos, "%s in Bd", roff_name[n->tok]);
14773e642ba0Sschwarze break;
14783e642ba0Sschwarze }
14793e642ba0Sschwarze }
14803e642ba0Sschwarze break;
14813e642ba0Sschwarze default:
14823e642ba0Sschwarze break;
14833e642ba0Sschwarze }
148420fa2881Sschwarze }
148520fa2881Sschwarze
148698b8f00aSschwarze static void
post_defaults(POST_ARGS)148720fa2881Sschwarze post_defaults(POST_ARGS)
148820fa2881Sschwarze {
14890ac7e6ecSschwarze struct roff_node *n;
149020fa2881Sschwarze
14910ac7e6ecSschwarze n = mdoc->last;
14920ac7e6ecSschwarze if (n->child != NULL) {
1493fe8e59edSschwarze post_delim_nb(mdoc);
149404fbb99fSschwarze return;
149504fbb99fSschwarze }
1496396853b5Sschwarze mdoc->next = ROFF_NEXT_CHILD;
14970ac7e6ecSschwarze switch (n->tok) {
14980ac7e6ecSschwarze case MDOC_Ar:
14990ac7e6ecSschwarze roff_word_alloc(mdoc, n->line, n->pos, "file");
150043808411Sschwarze mdoc->last->flags |= NODE_NOSRC;
15010ac7e6ecSschwarze roff_word_alloc(mdoc, n->line, n->pos, "...");
150220fa2881Sschwarze break;
150349aff9f8Sschwarze case MDOC_Pa:
150449aff9f8Sschwarze case MDOC_Mt:
15050ac7e6ecSschwarze roff_word_alloc(mdoc, n->line, n->pos, "~");
150620fa2881Sschwarze break;
150720fa2881Sschwarze default:
150820fa2881Sschwarze abort();
1509f73abda9Skristaps }
15100ac7e6ecSschwarze mdoc->last->flags |= NODE_NOSRC;
15110ac7e6ecSschwarze mdoc->last = n;
151220fa2881Sschwarze }
1513f73abda9Skristaps
151498b8f00aSschwarze static void
post_at(POST_ARGS)1515f73abda9Skristaps post_at(POST_ARGS)
1516f73abda9Skristaps {
15173af8e8d7Sschwarze struct roff_node *n, *nch;
15183af8e8d7Sschwarze const char *att;
151920fa2881Sschwarze
1520753701eeSschwarze n = mdoc->last;
15213af8e8d7Sschwarze nch = n->child;
1522753701eeSschwarze
152320fa2881Sschwarze /*
152420fa2881Sschwarze * If we have a child, look it up in the standard keys. If a
152520fa2881Sschwarze * key exist, use that instead of the child; if it doesn't,
152620fa2881Sschwarze * prefix "AT&T UNIX " to the existing data.
152720fa2881Sschwarze */
1528f73abda9Skristaps
15293af8e8d7Sschwarze att = NULL;
15303af8e8d7Sschwarze if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1531a5a5f808Sschwarze mandoc_msg(MANDOCERR_AT_BAD,
15323af8e8d7Sschwarze nch->line, nch->pos, "At %s", nch->string);
1533f73abda9Skristaps
15343af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD;
15353af8e8d7Sschwarze if (att != NULL) {
15363af8e8d7Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, att);
15373af8e8d7Sschwarze nch->flags |= NODE_NOPRT;
15383af8e8d7Sschwarze } else
15393af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
15403af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
15413af8e8d7Sschwarze mdoc->last = n;
154220fa2881Sschwarze }
1543f73abda9Skristaps
154498b8f00aSschwarze static void
post_an(POST_ARGS)1545f73abda9Skristaps post_an(POST_ARGS)
1546f73abda9Skristaps {
15473a0d07afSschwarze struct roff_node *np, *nch;
1548f73abda9Skristaps
15493e642ba0Sschwarze post_an_norm(mdoc);
15503e642ba0Sschwarze
1551769ee804Sschwarze np = mdoc->last;
1552cba50636Sschwarze nch = np->child;
1553cba50636Sschwarze if (np->norm->An.auth == AUTH__NONE) {
1554cba50636Sschwarze if (nch == NULL)
1555a5a5f808Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY,
1556cba50636Sschwarze np->line, np->pos, "An");
155704fbb99fSschwarze else
1558fe8e59edSschwarze post_delim_nb(mdoc);
1559cba50636Sschwarze } else if (nch != NULL)
1560a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS,
15613798fb25Sschwarze nch->line, nch->pos, "An ... %s", nch->string);
1562f73abda9Skristaps }
1563f73abda9Skristaps
156498b8f00aSschwarze static void
post_em(POST_ARGS)15650ac7e6ecSschwarze post_em(POST_ARGS)
15660ac7e6ecSschwarze {
15670ac7e6ecSschwarze post_tag(mdoc);
15680ac7e6ecSschwarze tag_put(NULL, TAG_FALLBACK, mdoc->last);
15690ac7e6ecSschwarze }
15700ac7e6ecSschwarze
15710ac7e6ecSschwarze static void
post_en(POST_ARGS)1572551cd4a8Sschwarze post_en(POST_ARGS)
1573551cd4a8Sschwarze {
15743e642ba0Sschwarze post_obsolete(mdoc);
1575d1982c71Sschwarze if (mdoc->last->type == ROFFT_BLOCK)
1576551cd4a8Sschwarze mdoc->last->norm->Es = mdoc->last_es;
1577551cd4a8Sschwarze }
1578551cd4a8Sschwarze
157998b8f00aSschwarze static void
post_er(POST_ARGS)15800ac7e6ecSschwarze post_er(POST_ARGS)
15810ac7e6ecSschwarze {
15820ac7e6ecSschwarze struct roff_node *n;
15830ac7e6ecSschwarze
15840ac7e6ecSschwarze n = mdoc->last;
15850ac7e6ecSschwarze if (n->sec == SEC_ERRORS &&
15860ac7e6ecSschwarze (n->parent->tok == MDOC_It ||
15870ac7e6ecSschwarze (n->parent->tok == MDOC_Bq &&
15880ac7e6ecSschwarze n->parent->parent->parent->tok == MDOC_It)))
15890ac7e6ecSschwarze tag_put(NULL, TAG_STRONG, n);
15900ac7e6ecSschwarze post_delim_nb(mdoc);
15910ac7e6ecSschwarze }
15920ac7e6ecSschwarze
15930ac7e6ecSschwarze static void
post_tag(POST_ARGS)15940ac7e6ecSschwarze post_tag(POST_ARGS)
15950ac7e6ecSschwarze {
15960ac7e6ecSschwarze struct roff_node *n;
15970ac7e6ecSschwarze
15980ac7e6ecSschwarze n = mdoc->last;
15990ac7e6ecSschwarze if ((n->prev == NULL ||
16000ac7e6ecSschwarze (n->prev->type == ROFFT_TEXT &&
16010ac7e6ecSschwarze strcmp(n->prev->string, "|") == 0)) &&
16020ac7e6ecSschwarze (n->parent->tok == MDOC_It ||
16030ac7e6ecSschwarze (n->parent->tok == MDOC_Xo &&
16040ac7e6ecSschwarze n->parent->parent->prev == NULL &&
16050ac7e6ecSschwarze n->parent->parent->parent->tok == MDOC_It)))
16060ac7e6ecSschwarze tag_put(NULL, TAG_STRONG, n);
16070ac7e6ecSschwarze post_delim_nb(mdoc);
16080ac7e6ecSschwarze }
16090ac7e6ecSschwarze
16100ac7e6ecSschwarze static void
post_es(POST_ARGS)1611551cd4a8Sschwarze post_es(POST_ARGS)
1612551cd4a8Sschwarze {
16133e642ba0Sschwarze post_obsolete(mdoc);
1614551cd4a8Sschwarze mdoc->last_es = mdoc->last;
1615551cd4a8Sschwarze }
1616551cd4a8Sschwarze
161798b8f00aSschwarze static void
post_fl(POST_ARGS)1618b952e091Sschwarze post_fl(POST_ARGS)
1619b952e091Sschwarze {
1620b952e091Sschwarze struct roff_node *n;
1621b952e091Sschwarze char *cp;
1622b952e091Sschwarze
1623b952e091Sschwarze /*
1624b952e091Sschwarze * Transform ".Fl Fl long" to ".Fl \-long",
1625b952e091Sschwarze * resulting for example in better HTML output.
1626b952e091Sschwarze */
1627b952e091Sschwarze
1628b952e091Sschwarze n = mdoc->last;
1629b952e091Sschwarze if (n->prev != NULL && n->prev->tok == MDOC_Fl &&
1630b952e091Sschwarze n->prev->child == NULL && n->child != NULL &&
1631b952e091Sschwarze (n->flags & NODE_LINE) == 0) {
1632b952e091Sschwarze mandoc_asprintf(&cp, "\\-%s", n->child->string);
1633b952e091Sschwarze free(n->child->string);
1634b952e091Sschwarze n->child->string = cp;
1635b952e091Sschwarze roff_node_delete(mdoc, n->prev);
1636b952e091Sschwarze }
1637b952e091Sschwarze post_tag(mdoc);
1638b952e091Sschwarze }
1639b952e091Sschwarze
1640b952e091Sschwarze static void
post_xx(POST_ARGS)1641816c3c54Sschwarze post_xx(POST_ARGS)
1642816c3c54Sschwarze {
1643816c3c54Sschwarze struct roff_node *n;
1644816c3c54Sschwarze const char *os;
164507ff2819Sschwarze char *v;
1646816c3c54Sschwarze
1647fe8e59edSschwarze post_delim_nb(mdoc);
164804fbb99fSschwarze
1649816c3c54Sschwarze n = mdoc->last;
1650816c3c54Sschwarze switch (n->tok) {
1651816c3c54Sschwarze case MDOC_Bsx:
1652816c3c54Sschwarze os = "BSD/OS";
1653816c3c54Sschwarze break;
1654816c3c54Sschwarze case MDOC_Dx:
1655816c3c54Sschwarze os = "DragonFly";
1656816c3c54Sschwarze break;
1657816c3c54Sschwarze case MDOC_Fx:
1658816c3c54Sschwarze os = "FreeBSD";
1659816c3c54Sschwarze break;
1660816c3c54Sschwarze case MDOC_Nx:
1661816c3c54Sschwarze os = "NetBSD";
166207ff2819Sschwarze if (n->child == NULL)
166307ff2819Sschwarze break;
166407ff2819Sschwarze v = n->child->string;
166507ff2819Sschwarze if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
166607ff2819Sschwarze v[2] < '0' || v[2] > '9' ||
166707ff2819Sschwarze v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
166807ff2819Sschwarze break;
166907ff2819Sschwarze n->child->flags |= NODE_NOPRT;
167007ff2819Sschwarze mdoc->next = ROFF_NEXT_CHILD;
167107ff2819Sschwarze roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
167207ff2819Sschwarze v = mdoc->last->string;
167307ff2819Sschwarze v[3] = toupper((unsigned char)v[3]);
167407ff2819Sschwarze mdoc->last->flags |= NODE_NOSRC;
167507ff2819Sschwarze mdoc->last = n;
1676816c3c54Sschwarze break;
1677816c3c54Sschwarze case MDOC_Ox:
1678816c3c54Sschwarze os = "OpenBSD";
1679816c3c54Sschwarze break;
1680816c3c54Sschwarze case MDOC_Ux:
1681816c3c54Sschwarze os = "UNIX";
1682816c3c54Sschwarze break;
1683816c3c54Sschwarze default:
1684816c3c54Sschwarze abort();
1685816c3c54Sschwarze }
1686816c3c54Sschwarze mdoc->next = ROFF_NEXT_CHILD;
1687816c3c54Sschwarze roff_word_alloc(mdoc, n->line, n->pos, os);
1688816c3c54Sschwarze mdoc->last->flags |= NODE_NOSRC;
1689816c3c54Sschwarze mdoc->last = n;
1690816c3c54Sschwarze }
1691816c3c54Sschwarze
1692816c3c54Sschwarze static void
post_it(POST_ARGS)1693f73abda9Skristaps post_it(POST_ARGS)
1694f73abda9Skristaps {
16953a0d07afSschwarze struct roff_node *nbl, *nit, *nch;
169619a69263Sschwarze int i, cols;
16976093755cSschwarze enum mdoc_list lt;
1698f73abda9Skristaps
16993e642ba0Sschwarze post_prevpar(mdoc);
17003e642ba0Sschwarze
17019530682eSschwarze nit = mdoc->last;
1702d1982c71Sschwarze if (nit->type != ROFFT_BLOCK)
170398b8f00aSschwarze return;
1704f73abda9Skristaps
17059530682eSschwarze nbl = nit->parent->parent;
17069530682eSschwarze lt = nbl->norm->Bl.type;
17076093755cSschwarze
17086093755cSschwarze switch (lt) {
170949aff9f8Sschwarze case LIST_tag:
171049aff9f8Sschwarze case LIST_hang:
171149aff9f8Sschwarze case LIST_ohang:
171249aff9f8Sschwarze case LIST_inset:
171349aff9f8Sschwarze case LIST_diag:
1714d26e35c2Sschwarze if (nit->head->child == NULL)
1715a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_NOHEAD,
1716a5a5f808Sschwarze nit->line, nit->pos, "Bl -%s It",
17179530682eSschwarze mdoc_argnames[nbl->args->argv[0].arg]);
1718f73abda9Skristaps break;
171949aff9f8Sschwarze case LIST_bullet:
172049aff9f8Sschwarze case LIST_dash:
172149aff9f8Sschwarze case LIST_enum:
172249aff9f8Sschwarze case LIST_hyphen:
1723d26e35c2Sschwarze if (nit->body == NULL || nit->body->child == NULL)
1724a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_NOBODY,
1725a5a5f808Sschwarze nit->line, nit->pos, "Bl -%s It",
172666788495Sschwarze mdoc_argnames[nbl->args->argv[0].arg]);
1727f73abda9Skristaps /* FALLTHROUGH */
172849aff9f8Sschwarze case LIST_item:
172925f8eeb1Sschwarze if ((nch = nit->head->child) != NULL)
1730a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP,
173114a309e3Sschwarze nit->line, nit->pos, "It %s",
17320ac7e6ecSschwarze nch->type == ROFFT_TEXT ? nch->string :
17330ac7e6ecSschwarze roff_name[nch->tok]);
1734f73abda9Skristaps break;
173549aff9f8Sschwarze case LIST_column:
17369530682eSschwarze cols = (int)nbl->norm->Bl.ncols;
17376093755cSschwarze
1738d26e35c2Sschwarze assert(nit->head->child == NULL);
17396093755cSschwarze
174080f58981Sschwarze if (nit->head->next->child == NULL &&
174180f58981Sschwarze nit->head->next->next == NULL) {
1742a5a5f808Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY,
174380f58981Sschwarze nit->line, nit->pos, "It");
174480f58981Sschwarze roff_node_delete(mdoc, nit);
174580f58981Sschwarze break;
174680f58981Sschwarze }
174753292e81Sschwarze
174880f58981Sschwarze i = 0;
174980f58981Sschwarze for (nch = nit->child; nch != NULL; nch = nch->next) {
175080f58981Sschwarze if (nch->type != ROFFT_BODY)
175180f58981Sschwarze continue;
175280f58981Sschwarze if (i++ && nch->flags & NODE_LINE)
1753a5a5f808Sschwarze mandoc_msg(MANDOCERR_TA_LINE,
175480f58981Sschwarze nch->line, nch->pos, "Ta");
175580f58981Sschwarze }
1756e14c4c11Sschwarze if (i < cols || i > cols + 1)
1757a5a5f808Sschwarze mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos,
1758bce599dfSschwarze "%d columns, %d cells", cols, i);
175980f58981Sschwarze else if (nit->head->next->child != NULL &&
176085490b9fSschwarze nit->head->next->child->flags & NODE_LINE)
1761a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_NOARG,
176280f58981Sschwarze nit->line, nit->pos, "Bl -column It");
1763e14c4c11Sschwarze break;
1764f73abda9Skristaps default:
176566788495Sschwarze abort();
1766f73abda9Skristaps }
1767f73abda9Skristaps }
1768f73abda9Skristaps
176998b8f00aSschwarze static void
post_bl_block(POST_ARGS)177020fa2881Sschwarze post_bl_block(POST_ARGS)
177120fa2881Sschwarze {
17723a0d07afSschwarze struct roff_node *n, *ni, *nc;
177320fa2881Sschwarze
17743e642ba0Sschwarze post_prevpar(mdoc);
17753e642ba0Sschwarze
177620fa2881Sschwarze n = mdoc->last;
1777f051602aSschwarze for (ni = n->body->child; ni != NULL; ni = ni->next) {
1778f051602aSschwarze if (ni->body == NULL)
1779bb99f0faSschwarze continue;
1780bb99f0faSschwarze nc = ni->body->last;
1781f051602aSschwarze while (nc != NULL) {
1782bb99f0faSschwarze switch (nc->tok) {
178349aff9f8Sschwarze case MDOC_Pp:
178429478532Sschwarze case ROFF_br:
1785bb99f0faSschwarze break;
1786bb99f0faSschwarze default:
1787bb99f0faSschwarze nc = NULL;
1788bb99f0faSschwarze continue;
1789bb99f0faSschwarze }
1790f051602aSschwarze if (ni->next == NULL) {
1791a5a5f808Sschwarze mandoc_msg(MANDOCERR_PAR_MOVE, nc->line,
1792a5a5f808Sschwarze nc->pos, "%s", roff_name[nc->tok]);
17938251afdeSschwarze roff_node_relink(mdoc, nc);
1794f051602aSschwarze } else if (n->norm->Bl.comp == 0 &&
1795f051602aSschwarze n->norm->Bl.type != LIST_column) {
1796a5a5f808Sschwarze mandoc_msg(MANDOCERR_PAR_SKIP,
1797a5a5f808Sschwarze nc->line, nc->pos,
179814a309e3Sschwarze "%s before It", roff_name[nc->tok]);
1799fa2127f9Sschwarze roff_node_delete(mdoc, nc);
1800bb99f0faSschwarze } else
1801bb99f0faSschwarze break;
1802bb99f0faSschwarze nc = ni->body->last;
1803bb99f0faSschwarze }
1804bb99f0faSschwarze }
180520fa2881Sschwarze }
180620fa2881Sschwarze
180790d52a15Sschwarze /*
180890d52a15Sschwarze * If the argument of -offset or -width is a macro,
180990d52a15Sschwarze * replace it with the associated default width.
181090d52a15Sschwarze */
18116050a3daSschwarze static void
rewrite_macro2len(struct roff_man * mdoc,char ** arg)18126050a3daSschwarze rewrite_macro2len(struct roff_man *mdoc, char **arg)
181320fa2881Sschwarze {
181420fa2881Sschwarze size_t width;
181514a309e3Sschwarze enum roff_tok tok;
181620fa2881Sschwarze
181790d52a15Sschwarze if (*arg == NULL)
181890d52a15Sschwarze return;
181990d52a15Sschwarze else if ( ! strcmp(*arg, "Ds"))
182020fa2881Sschwarze width = 6;
18216050a3daSschwarze else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
182290d52a15Sschwarze return;
1823dc0d8bb2Sschwarze else
1824dc0d8bb2Sschwarze width = macro2len(tok);
182520fa2881Sschwarze
182690d52a15Sschwarze free(*arg);
182790d52a15Sschwarze mandoc_asprintf(arg, "%zun", width);
182820fa2881Sschwarze }
182920fa2881Sschwarze
183098b8f00aSschwarze static void
post_bl_head(POST_ARGS)1831395185ccSschwarze post_bl_head(POST_ARGS)
1832395185ccSschwarze {
18333a0d07afSschwarze struct roff_node *nbl, *nh, *nch, *nnext;
1834f5174743Sschwarze struct mdoc_argv *argv;
183520fa2881Sschwarze int i, j;
1836395185ccSschwarze
18373e642ba0Sschwarze post_bl_norm(mdoc);
18382588c917Sschwarze
18393e642ba0Sschwarze nh = mdoc->last;
18402588c917Sschwarze if (nh->norm->Bl.type != LIST_column) {
18412588c917Sschwarze if ((nch = nh->child) == NULL)
18422588c917Sschwarze return;
1843a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS,
18442588c917Sschwarze nch->line, nch->pos, "Bl ... %s", nch->string);
18452588c917Sschwarze while (nch != NULL) {
1846fa2127f9Sschwarze roff_node_delete(mdoc, nch);
18472588c917Sschwarze nch = nh->child;
18482588c917Sschwarze }
184998b8f00aSschwarze return;
185098b8f00aSschwarze }
1851395185ccSschwarze
185220fa2881Sschwarze /*
1853f5174743Sschwarze * Append old-style lists, where the column width specifiers
185420fa2881Sschwarze * trail as macro parameters, to the new-style ("normal-form")
185520fa2881Sschwarze * lists where they're argument values following -column.
185620fa2881Sschwarze */
185720fa2881Sschwarze
18582588c917Sschwarze if (nh->child == NULL)
185998b8f00aSschwarze return;
186020fa2881Sschwarze
18612588c917Sschwarze nbl = nh->parent;
18622588c917Sschwarze for (j = 0; j < (int)nbl->args->argc; j++)
18632588c917Sschwarze if (nbl->args->argv[j].arg == MDOC_Column)
186420fa2881Sschwarze break;
186520fa2881Sschwarze
18662588c917Sschwarze assert(j < (int)nbl->args->argc);
186720fa2881Sschwarze
186820fa2881Sschwarze /*
1869a5e11edeSschwarze * Accommodate for new-style groff column syntax. Shuffle the
187020fa2881Sschwarze * child nodes, all of which must be TEXT, as arguments for the
187120fa2881Sschwarze * column field. Then, delete the head children.
187220fa2881Sschwarze */
187320fa2881Sschwarze
18742588c917Sschwarze argv = nbl->args->argv + j;
1875f5174743Sschwarze i = argv->sz;
187630e5ee06Sschwarze for (nch = nh->child; nch != NULL; nch = nch->next)
187730e5ee06Sschwarze argv->sz++;
1878f5174743Sschwarze argv->value = mandoc_reallocarray(argv->value,
1879f5174743Sschwarze argv->sz, sizeof(char *));
188020fa2881Sschwarze
18812588c917Sschwarze nh->norm->Bl.ncols = argv->sz;
18822588c917Sschwarze nh->norm->Bl.cols = (void *)argv->value;
188320fa2881Sschwarze
18842588c917Sschwarze for (nch = nh->child; nch != NULL; nch = nnext) {
18852588c917Sschwarze argv->value[i++] = nch->string;
18862588c917Sschwarze nch->string = NULL;
18872588c917Sschwarze nnext = nch->next;
1888fa2127f9Sschwarze roff_node_delete(NULL, nch);
1889395185ccSschwarze }
18902588c917Sschwarze nh->child = NULL;
1891b16e7ddfSschwarze }
1892b16e7ddfSschwarze
189398b8f00aSschwarze static void
post_bl(POST_ARGS)1894f73abda9Skristaps post_bl(POST_ARGS)
1895f73abda9Skristaps {
18967ebbefbeSschwarze struct roff_node *nbody; /* of the Bl */
18973a0d07afSschwarze struct roff_node *nchild, *nnext; /* of the Bl body */
1898ce0ef847Sschwarze const char *prev_Er;
1899ce0ef847Sschwarze int order;
1900f73abda9Skristaps
19012a427d60Sschwarze nbody = mdoc->last;
19022a427d60Sschwarze switch (nbody->type) {
1903d1982c71Sschwarze case ROFFT_BLOCK:
190498b8f00aSschwarze post_bl_block(mdoc);
190598b8f00aSschwarze return;
1906d1982c71Sschwarze case ROFFT_HEAD:
190798b8f00aSschwarze post_bl_head(mdoc);
190898b8f00aSschwarze return;
1909d1982c71Sschwarze case ROFFT_BODY:
1910f6127a73Sschwarze break;
19112a427d60Sschwarze default:
191298b8f00aSschwarze return;
1913f6127a73Sschwarze }
1914396853b5Sschwarze if (nbody->end != ENDBODY_NOT)
1915396853b5Sschwarze return;
1916f6127a73Sschwarze
19177ebbefbeSschwarze /*
19187ebbefbeSschwarze * Up to the first item, move nodes before the list,
19197ebbefbeSschwarze * but leave transparent nodes where they are
19207ebbefbeSschwarze * if they precede an item.
19217ebbefbeSschwarze * The next non-transparent node is kept in nchild.
19227ebbefbeSschwarze * It only needs to be updated after a non-transparent
19237ebbefbeSschwarze * node was moved out, and at the very beginning
19247ebbefbeSschwarze * when no node at all was moved yet.
19257ebbefbeSschwarze */
19267ebbefbeSschwarze
19277ebbefbeSschwarze nchild = mdoc->last;
19287ebbefbeSschwarze for (;;) {
19297ebbefbeSschwarze if (nchild == mdoc->last)
19307ebbefbeSschwarze nchild = roff_node_child(nbody);
1931b7530f2fSschwarze if (nchild == NULL) {
19327ebbefbeSschwarze mdoc->last = nbody;
1933a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_EMPTY,
1934b7530f2fSschwarze nbody->line, nbody->pos, "Bl");
1935b7530f2fSschwarze return;
1936b7530f2fSschwarze }
19377ebbefbeSschwarze if (nchild->tok == MDOC_It) {
19387ebbefbeSschwarze mdoc->last = nbody;
19397ebbefbeSschwarze break;
19407ebbefbeSschwarze }
19417ebbefbeSschwarze mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line,
19427ebbefbeSschwarze nbody->child->pos, "%s", roff_name[nbody->child->tok]);
19437ebbefbeSschwarze if (nbody->parent->prev == NULL) {
19447ebbefbeSschwarze mdoc->last = nbody->parent->parent;
19457ebbefbeSschwarze mdoc->next = ROFF_NEXT_CHILD;
19467ebbefbeSschwarze } else {
19477ebbefbeSschwarze mdoc->last = nbody->parent->prev;
19487ebbefbeSschwarze mdoc->next = ROFF_NEXT_SIBLING;
19497ebbefbeSschwarze }
19507ebbefbeSschwarze roff_node_relink(mdoc, nbody->child);
1951ad7fa6e5Sschwarze }
1952ad7fa6e5Sschwarze
1953ad7fa6e5Sschwarze /*
19547ebbefbeSschwarze * We have reached the first item,
19557ebbefbeSschwarze * so moving nodes out is no longer possible.
19567ebbefbeSschwarze * But in .Bl -column, the first rows may be implicit,
1957ad7fa6e5Sschwarze * that is, they may not start with .It macros.
1958ad7fa6e5Sschwarze * Such rows may be followed by nodes generated on the
19597ebbefbeSschwarze * roff level, for example .TS.
19607ebbefbeSschwarze * Wrap such roff nodes into an implicit row.
1961ad7fa6e5Sschwarze */
1962ad7fa6e5Sschwarze
19637ebbefbeSschwarze while (nchild != NULL) {
19647ebbefbeSschwarze if (nchild->tok == MDOC_It) {
19657ebbefbeSschwarze nchild = roff_node_next(nchild);
19667ebbefbeSschwarze continue;
19677ebbefbeSschwarze }
19687ebbefbeSschwarze nnext = nchild->next;
19697ebbefbeSschwarze mdoc->last = nchild->prev;
1970ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
19717ebbefbeSschwarze roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
19727ebbefbeSschwarze roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1973ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
19747ebbefbeSschwarze roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It);
1975ad7fa6e5Sschwarze while (nchild->tok != MDOC_It) {
19768251afdeSschwarze roff_node_relink(mdoc, nchild);
19777ebbefbeSschwarze if (nnext == NULL)
1978ad7fa6e5Sschwarze break;
19797ebbefbeSschwarze nchild = nnext;
1980ad7fa6e5Sschwarze nnext = nchild->next;
1981ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
1982ad7fa6e5Sschwarze }
1983ad7fa6e5Sschwarze mdoc->last = nbody;
1984f73abda9Skristaps }
1985ce0ef847Sschwarze
1986f3476b07Sschwarze if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1987ce0ef847Sschwarze return;
1988ce0ef847Sschwarze
1989ce0ef847Sschwarze prev_Er = NULL;
1990ce0ef847Sschwarze for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1991ce0ef847Sschwarze if (nchild->tok != MDOC_It)
1992ce0ef847Sschwarze continue;
1993ce0ef847Sschwarze if ((nnext = nchild->head->child) == NULL)
1994ce0ef847Sschwarze continue;
1995ce0ef847Sschwarze if (nnext->type == ROFFT_BLOCK)
1996ce0ef847Sschwarze nnext = nnext->body->child;
1997ce0ef847Sschwarze if (nnext == NULL || nnext->tok != MDOC_Er)
1998ce0ef847Sschwarze continue;
1999ce0ef847Sschwarze nnext = nnext->child;
2000ce0ef847Sschwarze if (prev_Er != NULL) {
2001ce0ef847Sschwarze order = strcmp(prev_Er, nnext->string);
2002ce0ef847Sschwarze if (order > 0)
2003a5a5f808Sschwarze mandoc_msg(MANDOCERR_ER_ORDER,
2004a5a5f808Sschwarze nnext->line, nnext->pos,
2005f3476b07Sschwarze "Er %s %s (NetBSD)",
2006f3476b07Sschwarze prev_Er, nnext->string);
2007ce0ef847Sschwarze else if (order == 0)
2008a5a5f808Sschwarze mandoc_msg(MANDOCERR_ER_REP,
2009a5a5f808Sschwarze nnext->line, nnext->pos,
2010f3476b07Sschwarze "Er %s (NetBSD)", prev_Er);
2011ce0ef847Sschwarze }
2012ce0ef847Sschwarze prev_Er = nnext->string;
2013ce0ef847Sschwarze }
2014f73abda9Skristaps }
2015f73abda9Skristaps
201698b8f00aSschwarze static void
post_bk(POST_ARGS)2017753701eeSschwarze post_bk(POST_ARGS)
2018753701eeSschwarze {
20193a0d07afSschwarze struct roff_node *n;
2020753701eeSschwarze
20212588c917Sschwarze n = mdoc->last;
20222588c917Sschwarze
2023d1982c71Sschwarze if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
2024a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk");
2025fa2127f9Sschwarze roff_node_delete(mdoc, n);
20262588c917Sschwarze }
2027753701eeSschwarze }
2028753701eeSschwarze
202998b8f00aSschwarze static void
post_sm(POST_ARGS)2030f051602aSschwarze post_sm(POST_ARGS)
2031f73abda9Skristaps {
20323a0d07afSschwarze struct roff_node *nch;
2033f73abda9Skristaps
2034dc0d8bb2Sschwarze nch = mdoc->last->child;
2035dc0d8bb2Sschwarze
203678bbbab4Sschwarze if (nch == NULL) {
2037f9e7bf99Sschwarze mdoc->flags ^= MDOC_SMOFF;
203898b8f00aSschwarze return;
2039bb648afaSschwarze }
2040f9e7bf99Sschwarze
2041d1982c71Sschwarze assert(nch->type == ROFFT_TEXT);
204220fa2881Sschwarze
204378bbbab4Sschwarze if ( ! strcmp(nch->string, "on")) {
2044ec2beb53Sschwarze mdoc->flags &= ~MDOC_SMOFF;
204598b8f00aSschwarze return;
2046ec2beb53Sschwarze }
204778bbbab4Sschwarze if ( ! strcmp(nch->string, "off")) {
2048ec2beb53Sschwarze mdoc->flags |= MDOC_SMOFF;
204998b8f00aSschwarze return;
2050ec2beb53Sschwarze }
205120fa2881Sschwarze
2052a5a5f808Sschwarze mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos,
205314a309e3Sschwarze "%s %s", roff_name[mdoc->last->tok], nch->string);
20548251afdeSschwarze roff_node_relink(mdoc, nch);
205598b8f00aSschwarze return;
205620fa2881Sschwarze }
2057f73abda9Skristaps
205898b8f00aSschwarze static void
post_root(POST_ARGS)2059f73abda9Skristaps post_root(POST_ARGS)
2060f73abda9Skristaps {
20613a0d07afSschwarze struct roff_node *n;
2062f73abda9Skristaps
2063ac1f49d0Sschwarze /* Add missing prologue data. */
206420fa2881Sschwarze
2065ac1f49d0Sschwarze if (mdoc->meta.date == NULL)
2066ea5923abSschwarze mdoc->meta.date = mandoc_normdate(NULL, NULL);
20673fdead0cSschwarze
20683fdead0cSschwarze if (mdoc->meta.title == NULL) {
2069a5a5f808Sschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
20703fdead0cSschwarze mdoc->meta.title = mandoc_strdup("UNTITLED");
20713fdead0cSschwarze }
20723fdead0cSschwarze
2073ac1f49d0Sschwarze if (mdoc->meta.vol == NULL)
2074ac1f49d0Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL");
20753fdead0cSschwarze
20763fdead0cSschwarze if (mdoc->meta.os == NULL) {
2077a5a5f808Sschwarze mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL);
20783fdead0cSschwarze mdoc->meta.os = mandoc_strdup("");
2079172864f7Sschwarze } else if (mdoc->meta.os_e &&
2080172864f7Sschwarze (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
2081a5a5f808Sschwarze mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0,
2082f3476b07Sschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2083f3476b07Sschwarze "(OpenBSD)" : "(NetBSD)");
2084f73abda9Skristaps
20858fd2959dSschwarze if (mdoc->meta.arch != NULL &&
2086f0fa0445Sschwarze arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) {
20876b86842eSschwarze n = mdoc->meta.first->child;
20883f450c8cSschwarze while (n->tok != MDOC_Dt ||
20893f450c8cSschwarze n->child == NULL ||
20903f450c8cSschwarze n->child->next == NULL ||
20913f450c8cSschwarze n->child->next->next == NULL)
20928fd2959dSschwarze n = n->next;
20938fd2959dSschwarze n = n->child->next->next;
2094a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos,
20958fd2959dSschwarze "Dt ... %s %s", mdoc->meta.arch,
20968fd2959dSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
20978fd2959dSschwarze "(OpenBSD)" : "(NetBSD)");
20988fd2959dSschwarze }
20998fd2959dSschwarze
210020fa2881Sschwarze /* Check that we begin with a proper `Sh'. */
210120fa2881Sschwarze
21026b86842eSschwarze n = mdoc->meta.first->child;
21034c293873Sschwarze while (n != NULL &&
21044c293873Sschwarze (n->type == ROFFT_COMMENT ||
21054c293873Sschwarze (n->tok >= MDOC_Dd &&
210616fe0cfcSschwarze mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
2107e20417bdSschwarze n = n->next;
2108e20417bdSschwarze
2109e20417bdSschwarze if (n == NULL)
2110a5a5f808Sschwarze mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL);
2111e20417bdSschwarze else if (n->tok != MDOC_Sh)
2112a5a5f808Sschwarze mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos,
2113a5a5f808Sschwarze "%s", roff_name[n->tok]);
211420fa2881Sschwarze }
2115f73abda9Skristaps
211698b8f00aSschwarze static void
post_rs(POST_ARGS)2117011fe33bSschwarze post_rs(POST_ARGS)
2118011fe33bSschwarze {
21193a0d07afSschwarze struct roff_node *np, *nch, *next, *prev;
212020fa2881Sschwarze int i, j;
2121011fe33bSschwarze
21226e96429aSschwarze np = mdoc->last;
21236e96429aSschwarze
2124d1982c71Sschwarze if (np->type != ROFFT_BODY)
212598b8f00aSschwarze return;
21266e96429aSschwarze
21276e96429aSschwarze if (np->child == NULL) {
2128a5a5f808Sschwarze mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs");
212998b8f00aSschwarze return;
2130bb648afaSschwarze }
2131011fe33bSschwarze
213220fa2881Sschwarze /*
213320fa2881Sschwarze * The full `Rs' block needs special handling to order the
213420fa2881Sschwarze * sub-elements according to `rsord'. Pick through each element
2135b538baa5Sschwarze * and correctly order it. This is an insertion sort.
213620fa2881Sschwarze */
213720fa2881Sschwarze
213820fa2881Sschwarze next = NULL;
21396e96429aSschwarze for (nch = np->child->next; nch != NULL; nch = next) {
21406e96429aSschwarze /* Determine order number of this child. */
214120fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++)
21426e96429aSschwarze if (rsord[i] == nch->tok)
214320fa2881Sschwarze break;
214420fa2881Sschwarze
2145b538baa5Sschwarze if (i == RSORD_MAX) {
2146a5a5f808Sschwarze mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos,
2147a5a5f808Sschwarze "%s", roff_name[nch->tok]);
2148b538baa5Sschwarze i = -1;
21496e96429aSschwarze } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
21506e96429aSschwarze np->norm->Rs.quote_T++;
2151b538baa5Sschwarze
215220fa2881Sschwarze /*
21536e96429aSschwarze * Remove this child from the chain. This somewhat
2154fa2127f9Sschwarze * repeats roff_node_unlink(), but since we're
215520fa2881Sschwarze * just re-ordering, there's no need for the
215620fa2881Sschwarze * full unlink process.
215720fa2881Sschwarze */
215820fa2881Sschwarze
21596e96429aSschwarze if ((next = nch->next) != NULL)
21606e96429aSschwarze next->prev = nch->prev;
216120fa2881Sschwarze
21626e96429aSschwarze if ((prev = nch->prev) != NULL)
21636e96429aSschwarze prev->next = nch->next;
216420fa2881Sschwarze
21656e96429aSschwarze nch->prev = nch->next = NULL;
216620fa2881Sschwarze
216720fa2881Sschwarze /*
216820fa2881Sschwarze * Scan back until we reach a node that's
21696e96429aSschwarze * to be ordered before this child.
217020fa2881Sschwarze */
217120fa2881Sschwarze
217220fa2881Sschwarze for ( ; prev ; prev = prev->prev) {
217320fa2881Sschwarze /* Determine order of `prev'. */
217420fa2881Sschwarze for (j = 0; j < RSORD_MAX; j++)
217520fa2881Sschwarze if (rsord[j] == prev->tok)
217620fa2881Sschwarze break;
2177b538baa5Sschwarze if (j == RSORD_MAX)
2178b538baa5Sschwarze j = -1;
217920fa2881Sschwarze
218020fa2881Sschwarze if (j <= i)
218120fa2881Sschwarze break;
218220fa2881Sschwarze }
218320fa2881Sschwarze
218420fa2881Sschwarze /*
21856e96429aSschwarze * Set this child back into its correct place
21866e96429aSschwarze * in front of the `prev' node.
218720fa2881Sschwarze */
218820fa2881Sschwarze
21896e96429aSschwarze nch->prev = prev;
219020fa2881Sschwarze
21916e96429aSschwarze if (prev == NULL) {
21926e96429aSschwarze np->child->prev = nch;
21936e96429aSschwarze nch->next = np->child;
21946e96429aSschwarze np->child = nch;
219520fa2881Sschwarze } else {
21966e96429aSschwarze if (prev->next)
21976e96429aSschwarze prev->next->prev = nch;
21986e96429aSschwarze nch->next = prev->next;
21996e96429aSschwarze prev->next = nch;
220020fa2881Sschwarze }
2201011fe33bSschwarze }
2202011fe33bSschwarze }
2203011fe33bSschwarze
22044039b21cSschwarze /*
22054039b21cSschwarze * For some arguments of some macros,
22064039b21cSschwarze * convert all breakable hyphens into ASCII_HYPH.
22074039b21cSschwarze */
220898b8f00aSschwarze static void
post_hyph(POST_ARGS)22094039b21cSschwarze post_hyph(POST_ARGS)
22104039b21cSschwarze {
22115ff59bf7Sschwarze struct roff_node *n, *nch;
22124039b21cSschwarze char *cp;
22134039b21cSschwarze
22145ff59bf7Sschwarze n = mdoc->last;
22155ff59bf7Sschwarze for (nch = n->child; nch != NULL; nch = nch->next) {
2216d1982c71Sschwarze if (nch->type != ROFFT_TEXT)
22174039b21cSschwarze continue;
22184039b21cSschwarze cp = nch->string;
2219b7530f2fSschwarze if (*cp == '\0')
22204039b21cSschwarze continue;
2221b7530f2fSschwarze while (*(++cp) != '\0')
2222b7530f2fSschwarze if (*cp == '-' &&
22234039b21cSschwarze isalpha((unsigned char)cp[-1]) &&
22245ff59bf7Sschwarze isalpha((unsigned char)cp[1])) {
2225c220f9cfSschwarze if (n->tag == NULL && n->flags & NODE_ID)
2226c220f9cfSschwarze n->tag = mandoc_strdup(nch->string);
22274039b21cSschwarze *cp = ASCII_HYPH;
22284039b21cSschwarze }
22294039b21cSschwarze }
22305ff59bf7Sschwarze }
22314039b21cSschwarze
223298b8f00aSschwarze static void
post_ns(POST_ARGS)2233af216717Sschwarze post_ns(POST_ARGS)
2234af216717Sschwarze {
2235676013e4Sschwarze struct roff_node *n;
2236af216717Sschwarze
2237676013e4Sschwarze n = mdoc->last;
2238676013e4Sschwarze if (n->flags & NODE_LINE ||
2239676013e4Sschwarze (n->next != NULL && n->next->flags & NODE_DELIMC))
2240a5a5f808Sschwarze mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL);
2241af216717Sschwarze }
2242af216717Sschwarze
224398b8f00aSschwarze static void
post_sx(POST_ARGS)2244fe8e59edSschwarze post_sx(POST_ARGS)
2245fe8e59edSschwarze {
2246fe8e59edSschwarze post_delim(mdoc);
2247fe8e59edSschwarze post_hyph(mdoc);
2248fe8e59edSschwarze }
2249fe8e59edSschwarze
2250fe8e59edSschwarze static void
post_sh(POST_ARGS)2251f73abda9Skristaps post_sh(POST_ARGS)
2252f73abda9Skristaps {
2253ba1a6076Sschwarze post_section(mdoc);
2254753701eeSschwarze
2255cd6c268fSschwarze switch (mdoc->last->type) {
2256d1982c71Sschwarze case ROFFT_HEAD:
225798b8f00aSschwarze post_sh_head(mdoc);
225898b8f00aSschwarze break;
2259d1982c71Sschwarze case ROFFT_BODY:
2260cd6c268fSschwarze switch (mdoc->lastsec) {
2261cd6c268fSschwarze case SEC_NAME:
226298b8f00aSschwarze post_sh_name(mdoc);
226398b8f00aSschwarze break;
22647c384856Sschwarze case SEC_SEE_ALSO:
226598b8f00aSschwarze post_sh_see_also(mdoc);
226698b8f00aSschwarze break;
2267cd6c268fSschwarze case SEC_AUTHORS:
226898b8f00aSschwarze post_sh_authors(mdoc);
226998b8f00aSschwarze break;
2270cd6c268fSschwarze default:
2271cd6c268fSschwarze break;
2272cd6c268fSschwarze }
2273cd6c268fSschwarze break;
2274cd6c268fSschwarze default:
2275cd6c268fSschwarze break;
2276cd6c268fSschwarze }
2277f73abda9Skristaps }
2278f73abda9Skristaps
227998b8f00aSschwarze static void
post_sh_name(POST_ARGS)2280cd6c268fSschwarze post_sh_name(POST_ARGS)
2281f73abda9Skristaps {
22823a0d07afSschwarze struct roff_node *n;
228320e2cf25Sschwarze int hasnm, hasnd;
2284f73abda9Skristaps
228520e2cf25Sschwarze hasnm = hasnd = 0;
2286f73abda9Skristaps
228720e2cf25Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) {
228820e2cf25Sschwarze switch (n->tok) {
228920e2cf25Sschwarze case MDOC_Nm:
2290f27faaccSschwarze if (hasnm && n->child != NULL)
2291a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESEC_PUNCT,
2292a5a5f808Sschwarze n->line, n->pos,
2293f27faaccSschwarze "Nm %s", n->child->string);
229420e2cf25Sschwarze hasnm = 1;
2295f27faaccSschwarze continue;
229620e2cf25Sschwarze case MDOC_Nd:
229720e2cf25Sschwarze hasnd = 1;
229820e2cf25Sschwarze if (n->next != NULL)
229920e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_ND,
2300a5a5f808Sschwarze n->line, n->pos, NULL);
230120e2cf25Sschwarze break;
23022d6f95d3Sschwarze case TOKEN_NONE:
2303f27faaccSschwarze if (n->type == ROFFT_TEXT &&
2304f27faaccSschwarze n->string[0] == ',' && n->string[1] == '\0' &&
2305f27faaccSschwarze n->next != NULL && n->next->tok == MDOC_Nm) {
2306f27faaccSschwarze n = n->next;
2307f27faaccSschwarze continue;
2308f27faaccSschwarze }
2309fa072f7fSschwarze /* FALLTHROUGH */
231020e2cf25Sschwarze default:
2311a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD,
2312a5a5f808Sschwarze n->line, n->pos, "%s", roff_name[n->tok]);
2313f27faaccSschwarze continue;
231420e2cf25Sschwarze }
2315f27faaccSschwarze break;
2316f73abda9Skristaps }
2317f73abda9Skristaps
231820e2cf25Sschwarze if ( ! hasnm)
2319a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NONM,
232020e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL);
232120e2cf25Sschwarze if ( ! hasnd)
2322a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NOND,
232320e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL);
232420fa2881Sschwarze }
2325f73abda9Skristaps
232698b8f00aSschwarze static void
post_sh_see_also(POST_ARGS)23277c384856Sschwarze post_sh_see_also(POST_ARGS)
23287c384856Sschwarze {
23293a0d07afSschwarze const struct roff_node *n;
23307c384856Sschwarze const char *name, *sec;
23317c384856Sschwarze const char *lastname, *lastsec, *lastpunct;
23327c384856Sschwarze int cmp;
23337c384856Sschwarze
23347c384856Sschwarze n = mdoc->last->child;
23357c384856Sschwarze lastname = lastsec = lastpunct = NULL;
23367c384856Sschwarze while (n != NULL) {
233730e5ee06Sschwarze if (n->tok != MDOC_Xr ||
233830e5ee06Sschwarze n->child == NULL ||
233930e5ee06Sschwarze n->child->next == NULL)
23407c384856Sschwarze break;
23417c384856Sschwarze
23427c384856Sschwarze /* Process one .Xr node. */
23437c384856Sschwarze
23447c384856Sschwarze name = n->child->string;
23457c384856Sschwarze sec = n->child->next->string;
23467c384856Sschwarze if (lastsec != NULL) {
23477c384856Sschwarze if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2348a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2349a5a5f808Sschwarze n->pos, "%s before %s(%s)",
2350a5a5f808Sschwarze lastpunct, name, sec);
23517c384856Sschwarze cmp = strcmp(lastsec, sec);
23527c384856Sschwarze if (cmp > 0)
2353a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2354a5a5f808Sschwarze n->pos, "%s(%s) after %s(%s)",
2355a5a5f808Sschwarze name, sec, lastname, lastsec);
23567c384856Sschwarze else if (cmp == 0 &&
23577c384856Sschwarze strcasecmp(lastname, name) > 0)
2358a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_ORDER, n->line,
2359a5a5f808Sschwarze n->pos, "%s after %s", name, lastname);
23607c384856Sschwarze }
23617c384856Sschwarze lastname = name;
23627c384856Sschwarze lastsec = sec;
23637c384856Sschwarze
23647c384856Sschwarze /* Process the following node. */
23657c384856Sschwarze
23667c384856Sschwarze n = n->next;
23677c384856Sschwarze if (n == NULL)
23687c384856Sschwarze break;
23697c384856Sschwarze if (n->tok == MDOC_Xr) {
23707c384856Sschwarze lastpunct = "none";
23717c384856Sschwarze continue;
23727c384856Sschwarze }
2373d1982c71Sschwarze if (n->type != ROFFT_TEXT)
23747c384856Sschwarze break;
23757c384856Sschwarze for (name = n->string; *name != '\0'; name++)
23767c384856Sschwarze if (isalpha((const unsigned char)*name))
237798b8f00aSschwarze return;
23787c384856Sschwarze lastpunct = n->string;
2379c7098240Sschwarze if (n->next == NULL || n->next->tok == MDOC_Rs)
2380a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_PUNCT, n->line,
2381a5a5f808Sschwarze n->pos, "%s after %s(%s)",
23827c384856Sschwarze lastpunct, lastname, lastsec);
23837c384856Sschwarze n = n->next;
23847c384856Sschwarze }
23857c384856Sschwarze }
23867c384856Sschwarze
23877c384856Sschwarze static int
child_an(const struct roff_node * n)23883a0d07afSschwarze child_an(const struct roff_node *n)
2389cd6c268fSschwarze {
2390cd6c268fSschwarze
2391cd6c268fSschwarze for (n = n->child; n != NULL; n = n->next)
239230e5ee06Sschwarze if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
2393526e306bSschwarze return 1;
2394526e306bSschwarze return 0;
2395cd6c268fSschwarze }
2396cd6c268fSschwarze
239798b8f00aSschwarze static void
post_sh_authors(POST_ARGS)2398cd6c268fSschwarze post_sh_authors(POST_ARGS)
2399cd6c268fSschwarze {
2400cd6c268fSschwarze
2401cd6c268fSschwarze if ( ! child_an(mdoc->last))
2402a5a5f808Sschwarze mandoc_msg(MANDOCERR_AN_MISSING,
2403cd6c268fSschwarze mdoc->last->line, mdoc->last->pos, NULL);
2404cd6c268fSschwarze }
2405cd6c268fSschwarze
24064482121fSschwarze /*
24074482121fSschwarze * Return an upper bound for the string distance (allowing
24084482121fSschwarze * transpositions). Not a full Levenshtein implementation
24094482121fSschwarze * because Levenshtein is quadratic in the string length
24104482121fSschwarze * and this function is called for every standard name,
24114482121fSschwarze * so the check for each custom name would be cubic.
24124482121fSschwarze * The following crude heuristics is linear, resulting
24134482121fSschwarze * in quadratic behaviour for checking one custom name,
24144482121fSschwarze * which does not cause measurable slowdown.
24154482121fSschwarze */
24164482121fSschwarze static int
similar(const char * s1,const char * s2)24174482121fSschwarze similar(const char *s1, const char *s2)
24184482121fSschwarze {
24194482121fSschwarze const int maxdist = 3;
24204482121fSschwarze int dist = 0;
24214482121fSschwarze
24224482121fSschwarze while (s1[0] != '\0' && s2[0] != '\0') {
24234482121fSschwarze if (s1[0] == s2[0]) {
24244482121fSschwarze s1++;
24254482121fSschwarze s2++;
24264482121fSschwarze continue;
24274482121fSschwarze }
24284482121fSschwarze if (++dist > maxdist)
24294482121fSschwarze return INT_MAX;
24304482121fSschwarze if (s1[1] == s2[1]) { /* replacement */
24314482121fSschwarze s1++;
24324482121fSschwarze s2++;
24334482121fSschwarze } else if (s1[0] == s2[1] && s1[1] == s2[0]) {
24344482121fSschwarze s1 += 2; /* transposition */
24354482121fSschwarze s2 += 2;
24364482121fSschwarze } else if (s1[0] == s2[1]) /* insertion */
24374482121fSschwarze s2++;
24384482121fSschwarze else if (s1[1] == s2[0]) /* deletion */
24394482121fSschwarze s1++;
24404482121fSschwarze else
24414482121fSschwarze return INT_MAX;
24424482121fSschwarze }
24434482121fSschwarze dist += strlen(s1) + strlen(s2);
24444482121fSschwarze return dist > maxdist ? INT_MAX : dist;
24454482121fSschwarze }
24464482121fSschwarze
244798b8f00aSschwarze static void
post_sh_head(POST_ARGS)2448f73abda9Skristaps post_sh_head(POST_ARGS)
2449f73abda9Skristaps {
2450cf0e2075Sschwarze struct roff_node *nch;
245151fcab2fSschwarze const char *goodsec;
24524482121fSschwarze const char *const *testsec;
24534482121fSschwarze int dist, mindist;
24543a0d07afSschwarze enum roff_sec sec;
2455f73abda9Skristaps
2456f73abda9Skristaps /*
2457f73abda9Skristaps * Process a new section. Sections are either "named" or
245820fa2881Sschwarze * "custom". Custom sections are user-defined, while named ones
245920fa2881Sschwarze * follow a conventional order and may only appear in certain
246020fa2881Sschwarze * manual sections.
2461f73abda9Skristaps */
2462f73abda9Skristaps
2463396853b5Sschwarze sec = mdoc->last->sec;
2464f73abda9Skristaps
246520fa2881Sschwarze /* The NAME should be first. */
2466f73abda9Skristaps
2467d37754b9Sschwarze if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
2468a5a5f808Sschwarze mandoc_msg(MANDOCERR_NAMESEC_FIRST,
2469d37754b9Sschwarze mdoc->last->line, mdoc->last->pos, "Sh %s",
2470cf0e2075Sschwarze sec != SEC_CUSTOM ? secnames[sec] :
2471cf0e2075Sschwarze (nch = mdoc->last->child) == NULL ? "" :
2472cf0e2075Sschwarze nch->type == ROFFT_TEXT ? nch->string :
247314a309e3Sschwarze roff_name[nch->tok]);
247420fa2881Sschwarze
247520fa2881Sschwarze /* The SYNOPSIS gets special attention in other areas. */
247620fa2881Sschwarze
2477396853b5Sschwarze if (sec == SEC_SYNOPSIS) {
247875088a49Sschwarze roff_setreg(mdoc->roff, "nS", 1, '=');
247920fa2881Sschwarze mdoc->flags |= MDOC_SYNOPSIS;
248022881299Sschwarze } else {
248175088a49Sschwarze roff_setreg(mdoc->roff, "nS", 0, '=');
248220fa2881Sschwarze mdoc->flags &= ~MDOC_SYNOPSIS;
248322881299Sschwarze }
24840ac7e6ecSschwarze if (sec == SEC_DESCRIPTION)
24850ac7e6ecSschwarze fn_prio = TAG_STRONG;
248620fa2881Sschwarze
248720fa2881Sschwarze /* Mark our last section. */
248820fa2881Sschwarze
248920fa2881Sschwarze mdoc->lastsec = sec;
24901eccdf28Sschwarze
249120fa2881Sschwarze /* We don't care about custom sections after this. */
2492fccfce9dSschwarze
24934482121fSschwarze if (sec == SEC_CUSTOM) {
24944482121fSschwarze if ((nch = mdoc->last->child) == NULL ||
24954482121fSschwarze nch->type != ROFFT_TEXT || nch->next != NULL)
249698b8f00aSschwarze return;
24974482121fSschwarze goodsec = NULL;
24984482121fSschwarze mindist = INT_MAX;
24994482121fSschwarze for (testsec = secnames + 1; *testsec != NULL; testsec++) {
25004482121fSschwarze dist = similar(nch->string, *testsec);
25014482121fSschwarze if (dist < mindist) {
25024482121fSschwarze goodsec = *testsec;
25034482121fSschwarze mindist = dist;
25044482121fSschwarze }
25054482121fSschwarze }
25064482121fSschwarze if (goodsec != NULL)
2507a5a5f808Sschwarze mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos,
2508a5a5f808Sschwarze "Sh %s instead of %s", nch->string, goodsec);
25094482121fSschwarze return;
25104482121fSschwarze }
2511fccfce9dSschwarze
25126be99f77Sschwarze /*
251320fa2881Sschwarze * Check whether our non-custom section is being repeated or is
251420fa2881Sschwarze * out of order.
25156be99f77Sschwarze */
2516f73abda9Skristaps
251720fa2881Sschwarze if (sec == mdoc->lastnamed)
2518a5a5f808Sschwarze mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line,
2519a5a5f808Sschwarze mdoc->last->pos, "Sh %s", secnames[sec]);
252020fa2881Sschwarze
252120fa2881Sschwarze if (sec < mdoc->lastnamed)
2522a5a5f808Sschwarze mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line,
2523a5a5f808Sschwarze mdoc->last->pos, "Sh %s", secnames[sec]);
252420fa2881Sschwarze
252520fa2881Sschwarze /* Mark the last named section. */
252620fa2881Sschwarze
252720fa2881Sschwarze mdoc->lastnamed = sec;
252820fa2881Sschwarze
252920fa2881Sschwarze /* Check particular section/manual conventions. */
253020fa2881Sschwarze
2531396853b5Sschwarze if (mdoc->meta.msec == NULL)
253298b8f00aSschwarze return;
253320fa2881Sschwarze
253451fcab2fSschwarze goodsec = NULL;
253520fa2881Sschwarze switch (sec) {
253649aff9f8Sschwarze case SEC_ERRORS:
2537be89e780Sschwarze if (*mdoc->meta.msec == '4')
2538be89e780Sschwarze break;
253951fcab2fSschwarze goodsec = "2, 3, 4, 9";
2540be89e780Sschwarze /* FALLTHROUGH */
254149aff9f8Sschwarze case SEC_RETURN_VALUES:
254249aff9f8Sschwarze case SEC_LIBRARY:
254392c0ca7fSschwarze if (*mdoc->meta.msec == '2')
2544f73abda9Skristaps break;
254592c0ca7fSschwarze if (*mdoc->meta.msec == '3')
254692c0ca7fSschwarze break;
254751fcab2fSschwarze if (NULL == goodsec)
254851fcab2fSschwarze goodsec = "2, 3, 9";
254903ab2f23Sdlg /* FALLTHROUGH */
255049aff9f8Sschwarze case SEC_CONTEXT:
255192c0ca7fSschwarze if (*mdoc->meta.msec == '9')
255292c0ca7fSschwarze break;
255351fcab2fSschwarze if (NULL == goodsec)
255451fcab2fSschwarze goodsec = "9";
2555a5a5f808Sschwarze mandoc_msg(MANDOCERR_SEC_MSEC,
255651fcab2fSschwarze mdoc->last->line, mdoc->last->pos,
2557396853b5Sschwarze "Sh %s for %s only", secnames[sec], goodsec);
255820fa2881Sschwarze break;
2559f73abda9Skristaps default:
2560f73abda9Skristaps break;
2561f73abda9Skristaps }
2562f73abda9Skristaps }
2563d39b9a9cSschwarze
256498b8f00aSschwarze static void
post_xr(POST_ARGS)25655ae08040Sschwarze post_xr(POST_ARGS)
25665ae08040Sschwarze {
25675ae08040Sschwarze struct roff_node *n, *nch;
25685ae08040Sschwarze
25695ae08040Sschwarze n = mdoc->last;
25705ae08040Sschwarze nch = n->child;
25715ae08040Sschwarze if (nch->next == NULL) {
2572a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_NOSEC,
25735ae08040Sschwarze n->line, n->pos, "Xr %s", nch->string);
257419b6bef7Sschwarze } else {
25755ae08040Sschwarze assert(nch->next == n->last);
257652d11c96Sschwarze if(mandoc_xr_add(nch->next->string, nch->string,
257752d11c96Sschwarze nch->line, nch->pos))
2578a5a5f808Sschwarze mandoc_msg(MANDOCERR_XR_SELF,
257952d11c96Sschwarze nch->line, nch->pos, "Xr %s %s",
258052d11c96Sschwarze nch->string, nch->next->string);
258119b6bef7Sschwarze }
2582fe8e59edSschwarze post_delim_nb(mdoc);
25835ae08040Sschwarze }
25845ae08040Sschwarze
25855ae08040Sschwarze static void
post_section(POST_ARGS)2586ba1a6076Sschwarze post_section(POST_ARGS)
2587f6127a73Sschwarze {
2588ba1a6076Sschwarze struct roff_node *n, *nch;
2589ba1a6076Sschwarze char *cp, *tag;
2590f6127a73Sschwarze
2591ba1a6076Sschwarze n = mdoc->last;
2592ba1a6076Sschwarze switch (n->type) {
25939c7c6a1fSschwarze case ROFFT_BLOCK:
25949c7c6a1fSschwarze post_prevpar(mdoc);
25959c7c6a1fSschwarze return;
2596d1982c71Sschwarze case ROFFT_HEAD:
2597ba1a6076Sschwarze tag = NULL;
2598ba1a6076Sschwarze deroff(&tag, n);
2599ba1a6076Sschwarze if (tag != NULL) {
2600ba1a6076Sschwarze for (cp = tag; *cp != '\0'; cp++)
2601ba1a6076Sschwarze if (*cp == ' ')
2602ba1a6076Sschwarze *cp = '_';
2603ba1a6076Sschwarze if ((nch = n->child) != NULL &&
2604ba1a6076Sschwarze nch->type == ROFFT_TEXT &&
2605ba1a6076Sschwarze strcmp(nch->string, tag) == 0)
2606e79243feSschwarze tag_put(NULL, TAG_STRONG, n);
2607ba1a6076Sschwarze else
2608ba1a6076Sschwarze tag_put(tag, TAG_FALLBACK, n);
2609ba1a6076Sschwarze free(tag);
2610ba1a6076Sschwarze }
2611fe8e59edSschwarze post_delim(mdoc);
2612753701eeSschwarze post_hyph(mdoc);
261398b8f00aSschwarze return;
2614d1982c71Sschwarze case ROFFT_BODY:
2615b7530f2fSschwarze break;
2616b7530f2fSschwarze default:
2617b7530f2fSschwarze return;
2618b7530f2fSschwarze }
2619ba1a6076Sschwarze if ((nch = n->child) != NULL &&
2620ba1a6076Sschwarze (nch->tok == MDOC_Pp || nch->tok == ROFF_br ||
2621ba1a6076Sschwarze nch->tok == ROFF_sp)) {
2622ba1a6076Sschwarze mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2623ba1a6076Sschwarze "%s after %s", roff_name[nch->tok],
2624ba1a6076Sschwarze roff_name[n->tok]);
2625ba1a6076Sschwarze roff_node_delete(mdoc, nch);
2626f6127a73Sschwarze }
2627ba1a6076Sschwarze if ((nch = n->last) != NULL &&
2628ba1a6076Sschwarze (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) {
2629ba1a6076Sschwarze mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos,
2630ba1a6076Sschwarze "%s at the end of %s", roff_name[nch->tok],
2631ba1a6076Sschwarze roff_name[n->tok]);
2632ba1a6076Sschwarze roff_node_delete(mdoc, nch);
2633f6127a73Sschwarze }
2634f6127a73Sschwarze }
2635f6127a73Sschwarze
263698b8f00aSschwarze static void
post_prevpar(POST_ARGS)26373e642ba0Sschwarze post_prevpar(POST_ARGS)
2638d39b9a9cSschwarze {
26397ebbefbeSschwarze struct roff_node *n, *np;
2640d39b9a9cSschwarze
26413e642ba0Sschwarze n = mdoc->last;
2642d1982c71Sschwarze if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
264398b8f00aSschwarze return;
26447ebbefbeSschwarze if ((np = roff_node_prev(n)) == NULL)
26457ebbefbeSschwarze return;
2646d39b9a9cSschwarze
264720fa2881Sschwarze /*
26487c539ecbSschwarze * Don't allow `Pp' prior to a paragraph-type
26497c539ecbSschwarze * block: `Pp' or non-compact `Bd' or `Bl'.
265020fa2881Sschwarze */
2651d39b9a9cSschwarze
26527ebbefbeSschwarze if (np->tok != MDOC_Pp && np->tok != ROFF_br)
265398b8f00aSschwarze return;
26543e642ba0Sschwarze if (n->tok == MDOC_Bl && n->norm->Bl.comp)
265598b8f00aSschwarze return;
26563e642ba0Sschwarze if (n->tok == MDOC_Bd && n->norm->Bd.comp)
265798b8f00aSschwarze return;
26583e642ba0Sschwarze if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
265998b8f00aSschwarze return;
2660d39b9a9cSschwarze
26617ebbefbeSschwarze mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos,
26627ebbefbeSschwarze "%s before %s", roff_name[np->tok], roff_name[n->tok]);
26637ebbefbeSschwarze roff_node_delete(mdoc, np);
2664d39b9a9cSschwarze }
266520fa2881Sschwarze
266698b8f00aSschwarze static void
post_par(POST_ARGS)2667e0dd4c9cSschwarze post_par(POST_ARGS)
2668e0dd4c9cSschwarze {
26693a0d07afSschwarze struct roff_node *np;
2670e0dd4c9cSschwarze
26710ac7e6ecSschwarze fn_prio = TAG_STRONG;
26723e642ba0Sschwarze post_prevpar(mdoc);
2673753701eeSschwarze
26748251afdeSschwarze np = mdoc->last;
26758251afdeSschwarze if (np->child != NULL)
2676a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos,
2677a5a5f808Sschwarze "%s %s", roff_name[np->tok], np->child->string);
2678e0dd4c9cSschwarze }
2679e0dd4c9cSschwarze
268098b8f00aSschwarze static void
post_dd(POST_ARGS)268120fa2881Sschwarze post_dd(POST_ARGS)
268220fa2881Sschwarze {
26833a0d07afSschwarze struct roff_node *n;
268420fa2881Sschwarze
2685b058e777Sschwarze n = mdoc->last;
268643808411Sschwarze n->flags |= NODE_NOPRT;
268743808411Sschwarze
2688396853b5Sschwarze if (mdoc->meta.date != NULL) {
2689a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd");
2690396853b5Sschwarze free(mdoc->meta.date);
2691396853b5Sschwarze } else if (mdoc->flags & MDOC_PBODY)
2692a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd");
2693396853b5Sschwarze else if (mdoc->meta.title != NULL)
2694a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER,
2695396853b5Sschwarze n->line, n->pos, "Dd after Dt");
2696396853b5Sschwarze else if (mdoc->meta.os != NULL)
2697a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER,
2698396853b5Sschwarze n->line, n->pos, "Dd after Os");
2699396853b5Sschwarze
2700ea5923abSschwarze if (mdoc->quick && n != NULL)
2701ea5923abSschwarze mdoc->meta.date = mandoc_strdup("");
2702ea5923abSschwarze else
2703ea5923abSschwarze mdoc->meta.date = mandoc_normdate(n->child, n);
270404e980cbSschwarze }
270520fa2881Sschwarze
270698b8f00aSschwarze static void
post_dt(POST_ARGS)270720fa2881Sschwarze post_dt(POST_ARGS)
270820fa2881Sschwarze {
27093a0d07afSschwarze struct roff_node *nn, *n;
271020fa2881Sschwarze const char *cp;
271120fa2881Sschwarze char *p;
271220fa2881Sschwarze
271320fa2881Sschwarze n = mdoc->last;
271443808411Sschwarze n->flags |= NODE_NOPRT;
271543808411Sschwarze
2716396853b5Sschwarze if (mdoc->flags & MDOC_PBODY) {
2717a5a5f808Sschwarze mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt");
271843808411Sschwarze return;
2719396853b5Sschwarze }
2720396853b5Sschwarze
2721396853b5Sschwarze if (mdoc->meta.title != NULL)
2722a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt");
2723396853b5Sschwarze else if (mdoc->meta.os != NULL)
2724a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER,
2725396853b5Sschwarze n->line, n->pos, "Dt after Os");
272620fa2881Sschwarze
272720fa2881Sschwarze free(mdoc->meta.title);
27283fdead0cSschwarze free(mdoc->meta.msec);
272920fa2881Sschwarze free(mdoc->meta.vol);
273020fa2881Sschwarze free(mdoc->meta.arch);
273120fa2881Sschwarze
27323fdead0cSschwarze mdoc->meta.title = NULL;
27333fdead0cSschwarze mdoc->meta.msec = NULL;
27343fdead0cSschwarze mdoc->meta.vol = NULL;
27353fdead0cSschwarze mdoc->meta.arch = NULL;
273620fa2881Sschwarze
27379a928a59Sschwarze /* Mandatory first argument: title. */
273820fa2881Sschwarze
27399a928a59Sschwarze nn = n->child;
27409a928a59Sschwarze if (nn == NULL || *nn->string == '\0') {
2741a5a5f808Sschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt");
27429a928a59Sschwarze mdoc->meta.title = mandoc_strdup("UNTITLED");
27439a928a59Sschwarze } else {
27449a928a59Sschwarze mdoc->meta.title = mandoc_strdup(nn->string);
27459a928a59Sschwarze
27469a928a59Sschwarze /* Check that all characters are uppercase. */
27479a928a59Sschwarze
27489a928a59Sschwarze for (p = nn->string; *p != '\0'; p++)
27499a928a59Sschwarze if (islower((unsigned char)*p)) {
2750a5a5f808Sschwarze mandoc_msg(MANDOCERR_TITLE_CASE, nn->line,
2751a5a5f808Sschwarze nn->pos + (int)(p - nn->string),
2752bd594191Sschwarze "Dt %s", nn->string);
275320fa2881Sschwarze break;
275420fa2881Sschwarze }
275520fa2881Sschwarze }
275620fa2881Sschwarze
27575ae08040Sschwarze /* Mandatory second argument: section. */
275820fa2881Sschwarze
27599a928a59Sschwarze if (nn != NULL)
27609a928a59Sschwarze nn = nn->next;
276120fa2881Sschwarze
27629a928a59Sschwarze if (nn == NULL) {
2763a5a5f808Sschwarze mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
27643fdead0cSschwarze "Dt %s", mdoc->meta.title);
276520fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL");
276643808411Sschwarze return; /* msec and arch remain NULL. */
276720fa2881Sschwarze }
276820fa2881Sschwarze
27699a928a59Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string);
27709a928a59Sschwarze
27719a928a59Sschwarze /* Infer volume title from section number. */
277220fa2881Sschwarze
277388ec69e3Sschwarze cp = mandoc_a2msec(nn->string);
27749a928a59Sschwarze if (cp == NULL) {
2775a5a5f808Sschwarze mandoc_msg(MANDOCERR_MSEC_BAD,
2776bd594191Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string);
277720fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string);
2778ee646987Sschwarze } else {
27799a928a59Sschwarze mdoc->meta.vol = mandoc_strdup(cp);
2780ee646987Sschwarze if (mdoc->filesec != '\0' &&
2781ee646987Sschwarze mdoc->filesec != *nn->string &&
2782ee646987Sschwarze *nn->string >= '1' && *nn->string <= '9')
2783ee646987Sschwarze mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos,
2784ee646987Sschwarze "*.%c vs Dt ... %c", mdoc->filesec, *nn->string);
2785ee646987Sschwarze }
278620fa2881Sschwarze
27879a928a59Sschwarze /* Optional third argument: architecture. */
278820fa2881Sschwarze
27899a928a59Sschwarze if ((nn = nn->next) == NULL)
279043808411Sschwarze return;
27919a928a59Sschwarze
27929a928a59Sschwarze for (p = nn->string; *p != '\0'; p++)
2793b94f27c5Sschwarze *p = tolower((unsigned char)*p);
2794b94f27c5Sschwarze mdoc->meta.arch = mandoc_strdup(nn->string);
279520fa2881Sschwarze
27969a928a59Sschwarze /* Ignore fourth and later arguments. */
27979a928a59Sschwarze
27989a928a59Sschwarze if ((nn = nn->next) != NULL)
2799a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_EXCESS,
28009a928a59Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string);
280120fa2881Sschwarze }
280220fa2881Sschwarze
280398b8f00aSschwarze static void
post_bx(POST_ARGS)2804992063deSschwarze post_bx(POST_ARGS)
2805992063deSschwarze {
28063af8e8d7Sschwarze struct roff_node *n, *nch;
2807f0c18971Sschwarze const char *macro;
28083af8e8d7Sschwarze
2809fe8e59edSschwarze post_delim_nb(mdoc);
281004fbb99fSschwarze
28113af8e8d7Sschwarze n = mdoc->last;
28123af8e8d7Sschwarze nch = n->child;
28133af8e8d7Sschwarze
28143af8e8d7Sschwarze if (nch != NULL) {
2815f0c18971Sschwarze macro = !strcmp(nch->string, "Open") ? "Ox" :
2816f0c18971Sschwarze !strcmp(nch->string, "Net") ? "Nx" :
2817f0c18971Sschwarze !strcmp(nch->string, "Free") ? "Fx" :
2818f0c18971Sschwarze !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2819f0c18971Sschwarze if (macro != NULL)
2820a5a5f808Sschwarze mandoc_msg(MANDOCERR_BX,
2821a5a5f808Sschwarze n->line, n->pos, "%s", macro);
28223af8e8d7Sschwarze mdoc->last = nch;
28233af8e8d7Sschwarze nch = nch->next;
28243af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
28253af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
28263af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
28273af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
28283af8e8d7Sschwarze } else
28293af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD;
28303af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "BSD");
28313af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
28323af8e8d7Sschwarze
28333af8e8d7Sschwarze if (nch == NULL) {
28343af8e8d7Sschwarze mdoc->last = n;
28353af8e8d7Sschwarze return;
28363af8e8d7Sschwarze }
28373af8e8d7Sschwarze
28383af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
28393af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
28403af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING;
28413af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "-");
28423af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
28433af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
28443af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC;
28453af8e8d7Sschwarze mdoc->last = n;
2846992063deSschwarze
2847992063deSschwarze /*
2848992063deSschwarze * Make `Bx's second argument always start with an uppercase
2849992063deSschwarze * letter. Groff checks if it's an "accepted" term, but we just
2850992063deSschwarze * uppercase blindly.
2851992063deSschwarze */
2852992063deSschwarze
28533af8e8d7Sschwarze *nch->string = (char)toupper((unsigned char)*nch->string);
2854992063deSschwarze }
2855992063deSschwarze
285698b8f00aSschwarze static void
post_os(POST_ARGS)285720fa2881Sschwarze post_os(POST_ARGS)
285820fa2881Sschwarze {
285920fa2881Sschwarze #ifndef OSNAME
286020fa2881Sschwarze struct utsname utsname;
286120fa2881Sschwarze #endif
28623a0d07afSschwarze struct roff_node *n;
286320fa2881Sschwarze
286420fa2881Sschwarze n = mdoc->last;
286543808411Sschwarze n->flags |= NODE_NOPRT;
286643808411Sschwarze
2867396853b5Sschwarze if (mdoc->meta.os != NULL)
2868a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os");
2869396853b5Sschwarze else if (mdoc->flags & MDOC_PBODY)
2870a5a5f808Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os");
287120fa2881Sschwarze
2872fe8e59edSschwarze post_delim(mdoc);
2873fe8e59edSschwarze
287420fa2881Sschwarze /*
2875353fa9ecSschwarze * Set the operating system by way of the `Os' macro.
2876353fa9ecSschwarze * The order of precedence is:
2877353fa9ecSschwarze * 1. the argument of the `Os' macro, unless empty
2878353fa9ecSschwarze * 2. the -Ios=foo command line argument, if provided
2879353fa9ecSschwarze * 3. -DOSNAME="\"foo\"", if provided during compilation
2880353fa9ecSschwarze * 4. "sysname release" from uname(3)
288120fa2881Sschwarze */
288220fa2881Sschwarze
288320fa2881Sschwarze free(mdoc->meta.os);
288483af2bccSschwarze mdoc->meta.os = NULL;
2885423631c9Sschwarze deroff(&mdoc->meta.os, n);
288683af2bccSschwarze if (mdoc->meta.os)
2887ce0ef847Sschwarze goto out;
28884c468128Sschwarze
2889f3476b07Sschwarze if (mdoc->os_s != NULL) {
2890f3476b07Sschwarze mdoc->meta.os = mandoc_strdup(mdoc->os_s);
2891ce0ef847Sschwarze goto out;
2892353fa9ecSschwarze }
28934c468128Sschwarze
289420fa2881Sschwarze #ifdef OSNAME
28954c468128Sschwarze mdoc->meta.os = mandoc_strdup(OSNAME);
289620fa2881Sschwarze #else /*!OSNAME */
28978055da74Sschwarze if (mdoc->os_r == NULL) {
2898f051602aSschwarze if (uname(&utsname) == -1) {
2899a5a5f808Sschwarze mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os");
29008055da74Sschwarze mdoc->os_r = mandoc_strdup("UNKNOWN");
2901a450f7c4Sschwarze } else
29028055da74Sschwarze mandoc_asprintf(&mdoc->os_r, "%s %s",
2903a450f7c4Sschwarze utsname.sysname, utsname.release);
290420fa2881Sschwarze }
29058055da74Sschwarze mdoc->meta.os = mandoc_strdup(mdoc->os_r);
290620fa2881Sschwarze #endif /*!OSNAME*/
2907ce0ef847Sschwarze
2908f3476b07Sschwarze out:
2909f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2910f3476b07Sschwarze if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2911f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2912f3476b07Sschwarze else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2913f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_NETBSD;
2914f3476b07Sschwarze }
29153427e516Sschwarze
29163427e516Sschwarze /*
29173427e516Sschwarze * This is the earliest point where we can check
29183427e516Sschwarze * Mdocdate conventions because we don't know
29193427e516Sschwarze * the operating system earlier.
29203427e516Sschwarze */
29213427e516Sschwarze
29222f84042eSschwarze if (n->child != NULL)
2923a5a5f808Sschwarze mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos,
29242f84042eSschwarze "Os %s (%s)", n->child->string,
29252f84042eSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
29262f84042eSschwarze "OpenBSD" : "NetBSD");
29272f84042eSschwarze
29283427e516Sschwarze while (n->tok != MDOC_Dd)
29293427e516Sschwarze if ((n = n->prev) == NULL)
29303427e516Sschwarze return;
29313427e516Sschwarze if ((n = n->child) == NULL)
29323427e516Sschwarze return;
293307ec32d0Sschwarze if (strncmp(n->string, "$" "Mdocdate", 9)) {
2934f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
2935a5a5f808Sschwarze mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line,
2936a5a5f808Sschwarze n->pos, "Dd %s (OpenBSD)", n->string);
29373427e516Sschwarze } else {
2938f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
2939a5a5f808Sschwarze mandoc_msg(MANDOCERR_MDOCDATE, n->line,
2940a5a5f808Sschwarze n->pos, "Dd %s (NetBSD)", n->string);
29413427e516Sschwarze }
294220fa2881Sschwarze }
294320fa2881Sschwarze
2944396853b5Sschwarze enum roff_sec
mdoc_a2sec(const char * p)2945396853b5Sschwarze mdoc_a2sec(const char *p)
294619a69263Sschwarze {
294719a69263Sschwarze int i;
294819a69263Sschwarze
294919a69263Sschwarze for (i = 0; i < (int)SEC__MAX; i++)
295019a69263Sschwarze if (secnames[i] && 0 == strcmp(p, secnames[i]))
2951526e306bSschwarze return (enum roff_sec)i;
295219a69263Sschwarze
2953526e306bSschwarze return SEC_CUSTOM;
295419a69263Sschwarze }
295519a69263Sschwarze
295619a69263Sschwarze static size_t
macro2len(enum roff_tok macro)295714a309e3Sschwarze macro2len(enum roff_tok macro)
295819a69263Sschwarze {
295919a69263Sschwarze
296019a69263Sschwarze switch (macro) {
296149aff9f8Sschwarze case MDOC_Ad:
2962526e306bSschwarze return 12;
296349aff9f8Sschwarze case MDOC_Ao:
2964526e306bSschwarze return 12;
296549aff9f8Sschwarze case MDOC_An:
2966526e306bSschwarze return 12;
296749aff9f8Sschwarze case MDOC_Aq:
2968526e306bSschwarze return 12;
296949aff9f8Sschwarze case MDOC_Ar:
2970526e306bSschwarze return 12;
297149aff9f8Sschwarze case MDOC_Bo:
2972526e306bSschwarze return 12;
297349aff9f8Sschwarze case MDOC_Bq:
2974526e306bSschwarze return 12;
297549aff9f8Sschwarze case MDOC_Cd:
2976526e306bSschwarze return 12;
297749aff9f8Sschwarze case MDOC_Cm:
2978526e306bSschwarze return 10;
297949aff9f8Sschwarze case MDOC_Do:
2980526e306bSschwarze return 10;
298149aff9f8Sschwarze case MDOC_Dq:
2982526e306bSschwarze return 12;
298349aff9f8Sschwarze case MDOC_Dv:
2984526e306bSschwarze return 12;
298549aff9f8Sschwarze case MDOC_Eo:
2986526e306bSschwarze return 12;
298749aff9f8Sschwarze case MDOC_Em:
2988526e306bSschwarze return 10;
298949aff9f8Sschwarze case MDOC_Er:
2990526e306bSschwarze return 17;
299149aff9f8Sschwarze case MDOC_Ev:
2992526e306bSschwarze return 15;
299349aff9f8Sschwarze case MDOC_Fa:
2994526e306bSschwarze return 12;
299549aff9f8Sschwarze case MDOC_Fl:
2996526e306bSschwarze return 10;
299749aff9f8Sschwarze case MDOC_Fo:
2998526e306bSschwarze return 16;
299949aff9f8Sschwarze case MDOC_Fn:
3000526e306bSschwarze return 16;
300149aff9f8Sschwarze case MDOC_Ic:
3002526e306bSschwarze return 10;
300349aff9f8Sschwarze case MDOC_Li:
3004526e306bSschwarze return 16;
300549aff9f8Sschwarze case MDOC_Ms:
3006526e306bSschwarze return 6;
300749aff9f8Sschwarze case MDOC_Nm:
3008526e306bSschwarze return 10;
300949aff9f8Sschwarze case MDOC_No:
3010526e306bSschwarze return 12;
301149aff9f8Sschwarze case MDOC_Oo:
3012526e306bSschwarze return 10;
301349aff9f8Sschwarze case MDOC_Op:
3014526e306bSschwarze return 14;
301549aff9f8Sschwarze case MDOC_Pa:
3016526e306bSschwarze return 32;
301749aff9f8Sschwarze case MDOC_Pf:
3018526e306bSschwarze return 12;
301949aff9f8Sschwarze case MDOC_Po:
3020526e306bSschwarze return 12;
302149aff9f8Sschwarze case MDOC_Pq:
3022526e306bSschwarze return 12;
302349aff9f8Sschwarze case MDOC_Ql:
3024526e306bSschwarze return 16;
302549aff9f8Sschwarze case MDOC_Qo:
3026526e306bSschwarze return 12;
302749aff9f8Sschwarze case MDOC_So:
3028526e306bSschwarze return 12;
302949aff9f8Sschwarze case MDOC_Sq:
3030526e306bSschwarze return 12;
303149aff9f8Sschwarze case MDOC_Sy:
3032526e306bSschwarze return 6;
303349aff9f8Sschwarze case MDOC_Sx:
3034526e306bSschwarze return 16;
303549aff9f8Sschwarze case MDOC_Tn:
3036526e306bSschwarze return 10;
303749aff9f8Sschwarze case MDOC_Va:
3038526e306bSschwarze return 12;
303949aff9f8Sschwarze case MDOC_Vt:
3040526e306bSschwarze return 12;
304149aff9f8Sschwarze case MDOC_Xr:
3042526e306bSschwarze return 10;
304319a69263Sschwarze default:
304419a69263Sschwarze break;
3045*479c151dSjsg }
3046526e306bSschwarze return 0;
304719a69263Sschwarze }
3048