1*7c539ecbSschwarze /* $OpenBSD: mdoc_validate.c,v 1.278 2018/12/03 21:00:06 schwarze Exp $ */ 2f73abda9Skristaps /* 322972b14Sschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 46dc98fe5Sschwarze * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> 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. 18f73abda9Skristaps */ 19dadc3a61Sschwarze #include <sys/types.h> 2020fa2881Sschwarze #ifndef OSNAME 2120fa2881Sschwarze #include <sys/utsname.h> 2220fa2881Sschwarze #endif 2320fa2881Sschwarze 24f73abda9Skristaps #include <assert.h> 25f73abda9Skristaps #include <ctype.h> 26d92dc4efSschwarze #include <limits.h> 273216dddfSschwarze #include <stdio.h> 28f73abda9Skristaps #include <stdlib.h> 29f73abda9Skristaps #include <string.h> 3020fa2881Sschwarze #include <time.h> 31f73abda9Skristaps 324f4f7972Sschwarze #include "mandoc_aux.h" 33d1982c71Sschwarze #include "mandoc.h" 3419b6bef7Sschwarze #include "mandoc_xr.h" 35d1982c71Sschwarze #include "roff.h" 36d1982c71Sschwarze #include "mdoc.h" 37f6854d5cSschwarze #include "libmandoc.h" 38fa2127f9Sschwarze #include "roff_int.h" 39d1982c71Sschwarze #include "libmdoc.h" 40f73abda9Skristaps 41f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */ 42f73abda9Skristaps 43ede1b9d0Sschwarze #define POST_ARGS struct roff_man *mdoc 44f73abda9Skristaps 457c2be9f8Sschwarze enum check_ineq { 467c2be9f8Sschwarze CHECK_LT, 477c2be9f8Sschwarze CHECK_GT, 487c2be9f8Sschwarze CHECK_EQ 497c2be9f8Sschwarze }; 507c2be9f8Sschwarze 5198b8f00aSschwarze typedef void (*v_post)(POST_ARGS); 52f73abda9Skristaps 538ccddcd3Sschwarze static int build_list(struct roff_man *, int); 54ede1b9d0Sschwarze static void check_argv(struct roff_man *, 553a0d07afSschwarze struct roff_node *, struct mdoc_argv *); 56ede1b9d0Sschwarze static void check_args(struct roff_man *, struct roff_node *); 576dc98fe5Sschwarze static void check_text(struct roff_man *, int, int, char *); 586dc98fe5Sschwarze static void check_text_em(struct roff_man *, int, int, char *); 5948497dd5Sschwarze static void check_toptext(struct roff_man *, int, int, const char *); 603a0d07afSschwarze static int child_an(const struct roff_node *); 6114a309e3Sschwarze static size_t macro2len(enum roff_tok); 626050a3daSschwarze static void rewrite_macro2len(struct roff_man *, char **); 634482121fSschwarze static int similar(const char *, const char *); 6467c719adSschwarze 65*7c539ecbSschwarze static void post_abort(POST_ARGS); 6698b8f00aSschwarze static void post_an(POST_ARGS); 673e642ba0Sschwarze static void post_an_norm(POST_ARGS); 6898b8f00aSschwarze static void post_at(POST_ARGS); 693e642ba0Sschwarze static void post_bd(POST_ARGS); 7098b8f00aSschwarze static void post_bf(POST_ARGS); 7198b8f00aSschwarze static void post_bk(POST_ARGS); 7298b8f00aSschwarze static void post_bl(POST_ARGS); 7398b8f00aSschwarze static void post_bl_block(POST_ARGS); 7498b8f00aSschwarze static void post_bl_head(POST_ARGS); 753e642ba0Sschwarze static void post_bl_norm(POST_ARGS); 7698b8f00aSschwarze static void post_bx(POST_ARGS); 7798b8f00aSschwarze static void post_defaults(POST_ARGS); 783e642ba0Sschwarze static void post_display(POST_ARGS); 7998b8f00aSschwarze static void post_dd(POST_ARGS); 8004fbb99fSschwarze static void post_delim(POST_ARGS); 81fe8e59edSschwarze static void post_delim_nb(POST_ARGS); 8298b8f00aSschwarze static void post_dt(POST_ARGS); 8398b8f00aSschwarze static void post_en(POST_ARGS); 8498b8f00aSschwarze static void post_es(POST_ARGS); 8598b8f00aSschwarze static void post_eoln(POST_ARGS); 8698b8f00aSschwarze static void post_ex(POST_ARGS); 8798b8f00aSschwarze static void post_fa(POST_ARGS); 8898b8f00aSschwarze static void post_fn(POST_ARGS); 8998b8f00aSschwarze static void post_fname(POST_ARGS); 9098b8f00aSschwarze static void post_fo(POST_ARGS); 9198b8f00aSschwarze static void post_hyph(POST_ARGS); 9298b8f00aSschwarze static void post_ignpar(POST_ARGS); 9398b8f00aSschwarze static void post_it(POST_ARGS); 9498b8f00aSschwarze static void post_lb(POST_ARGS); 9598b8f00aSschwarze static void post_nd(POST_ARGS); 9698b8f00aSschwarze static void post_nm(POST_ARGS); 9798b8f00aSschwarze static void post_ns(POST_ARGS); 983e642ba0Sschwarze static void post_obsolete(POST_ARGS); 9998b8f00aSschwarze static void post_os(POST_ARGS); 10098b8f00aSschwarze static void post_par(POST_ARGS); 1013e642ba0Sschwarze static void post_prevpar(POST_ARGS); 10298b8f00aSschwarze static void post_root(POST_ARGS); 10398b8f00aSschwarze static void post_rs(POST_ARGS); 1048ccddcd3Sschwarze static void post_rv(POST_ARGS); 10598b8f00aSschwarze static void post_sh(POST_ARGS); 10698b8f00aSschwarze static void post_sh_head(POST_ARGS); 10798b8f00aSschwarze static void post_sh_name(POST_ARGS); 10898b8f00aSschwarze static void post_sh_see_also(POST_ARGS); 10998b8f00aSschwarze static void post_sh_authors(POST_ARGS); 11098b8f00aSschwarze static void post_sm(POST_ARGS); 11198b8f00aSschwarze static void post_st(POST_ARGS); 1123e642ba0Sschwarze static void post_std(POST_ARGS); 113fe8e59edSschwarze static void post_sx(POST_ARGS); 114bc205043Sschwarze static void post_useless(POST_ARGS); 1155ae08040Sschwarze static void post_xr(POST_ARGS); 116816c3c54Sschwarze static void post_xx(POST_ARGS); 11798b8f00aSschwarze 118b3f54129Sschwarze static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { 1193e642ba0Sschwarze post_dd, /* Dd */ 1203e642ba0Sschwarze post_dt, /* Dt */ 1213e642ba0Sschwarze post_os, /* Os */ 1223e642ba0Sschwarze post_sh, /* Sh */ 1233e642ba0Sschwarze post_ignpar, /* Ss */ 1243e642ba0Sschwarze post_par, /* Pp */ 1253e642ba0Sschwarze post_display, /* D1 */ 1263e642ba0Sschwarze post_display, /* Dl */ 1273e642ba0Sschwarze post_display, /* Bd */ 1283e642ba0Sschwarze NULL, /* Ed */ 1293e642ba0Sschwarze post_bl, /* Bl */ 1303e642ba0Sschwarze NULL, /* El */ 1313e642ba0Sschwarze post_it, /* It */ 132fe8e59edSschwarze post_delim_nb, /* Ad */ 1333e642ba0Sschwarze post_an, /* An */ 13414a309e3Sschwarze NULL, /* Ap */ 1353e642ba0Sschwarze post_defaults, /* Ar */ 1363e642ba0Sschwarze NULL, /* Cd */ 137fe8e59edSschwarze post_delim_nb, /* Cm */ 138fe8e59edSschwarze post_delim_nb, /* Dv */ 139fe8e59edSschwarze post_delim_nb, /* Er */ 140fe8e59edSschwarze post_delim_nb, /* Ev */ 1413e642ba0Sschwarze post_ex, /* Ex */ 1423e642ba0Sschwarze post_fa, /* Fa */ 1433e642ba0Sschwarze NULL, /* Fd */ 144fe8e59edSschwarze post_delim_nb, /* Fl */ 1453e642ba0Sschwarze post_fn, /* Fn */ 146fe8e59edSschwarze post_delim_nb, /* Ft */ 147fe8e59edSschwarze post_delim_nb, /* Ic */ 148fe8e59edSschwarze post_delim_nb, /* In */ 1493e642ba0Sschwarze post_defaults, /* Li */ 1503e642ba0Sschwarze post_nd, /* Nd */ 1513e642ba0Sschwarze post_nm, /* Nm */ 152fe8e59edSschwarze post_delim_nb, /* Op */ 153*7c539ecbSschwarze post_abort, /* Ot */ 1543e642ba0Sschwarze post_defaults, /* Pa */ 1558ccddcd3Sschwarze post_rv, /* Rv */ 1563e642ba0Sschwarze post_st, /* St */ 157fe8e59edSschwarze post_delim_nb, /* Va */ 158fe8e59edSschwarze post_delim_nb, /* Vt */ 1595ae08040Sschwarze post_xr, /* Xr */ 1603e642ba0Sschwarze NULL, /* %A */ 1613e642ba0Sschwarze post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ 1623e642ba0Sschwarze NULL, /* %D */ 1633e642ba0Sschwarze NULL, /* %I */ 1643e642ba0Sschwarze NULL, /* %J */ 1653e642ba0Sschwarze post_hyph, /* %N */ 1663e642ba0Sschwarze post_hyph, /* %O */ 1673e642ba0Sschwarze NULL, /* %P */ 1683e642ba0Sschwarze post_hyph, /* %R */ 1693e642ba0Sschwarze post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ 1703e642ba0Sschwarze NULL, /* %V */ 1713e642ba0Sschwarze NULL, /* Ac */ 1721abead14Sschwarze NULL, /* Ao */ 173fe8e59edSschwarze post_delim_nb, /* Aq */ 1743e642ba0Sschwarze post_at, /* At */ 1753e642ba0Sschwarze NULL, /* Bc */ 1763e642ba0Sschwarze post_bf, /* Bf */ 1771abead14Sschwarze NULL, /* Bo */ 1783e642ba0Sschwarze NULL, /* Bq */ 179816c3c54Sschwarze post_xx, /* Bsx */ 1803e642ba0Sschwarze post_bx, /* Bx */ 1813e642ba0Sschwarze post_obsolete, /* Db */ 1823e642ba0Sschwarze NULL, /* Dc */ 1833e642ba0Sschwarze NULL, /* Do */ 1843e642ba0Sschwarze NULL, /* Dq */ 1853e642ba0Sschwarze NULL, /* Ec */ 1863e642ba0Sschwarze NULL, /* Ef */ 187fe8e59edSschwarze post_delim_nb, /* Em */ 1883e642ba0Sschwarze NULL, /* Eo */ 189816c3c54Sschwarze post_xx, /* Fx */ 190fe8e59edSschwarze post_delim_nb, /* Ms */ 191abcca917Sschwarze NULL, /* No */ 1923e642ba0Sschwarze post_ns, /* Ns */ 193816c3c54Sschwarze post_xx, /* Nx */ 194816c3c54Sschwarze post_xx, /* Ox */ 1953e642ba0Sschwarze NULL, /* Pc */ 1963e642ba0Sschwarze NULL, /* Pf */ 1971abead14Sschwarze NULL, /* Po */ 198fe8e59edSschwarze post_delim_nb, /* Pq */ 1993e642ba0Sschwarze NULL, /* Qc */ 200fe8e59edSschwarze post_delim_nb, /* Ql */ 2011abead14Sschwarze NULL, /* Qo */ 202fe8e59edSschwarze post_delim_nb, /* Qq */ 2033e642ba0Sschwarze NULL, /* Re */ 2043e642ba0Sschwarze post_rs, /* Rs */ 2053e642ba0Sschwarze NULL, /* Sc */ 2061abead14Sschwarze NULL, /* So */ 207fe8e59edSschwarze post_delim_nb, /* Sq */ 2083e642ba0Sschwarze post_sm, /* Sm */ 209fe8e59edSschwarze post_sx, /* Sx */ 210fe8e59edSschwarze post_delim_nb, /* Sy */ 211bc205043Sschwarze post_useless, /* Tn */ 212816c3c54Sschwarze post_xx, /* Ux */ 2133e642ba0Sschwarze NULL, /* Xc */ 2143e642ba0Sschwarze NULL, /* Xo */ 2153e642ba0Sschwarze post_fo, /* Fo */ 2163e642ba0Sschwarze NULL, /* Fc */ 2171abead14Sschwarze NULL, /* Oo */ 2183e642ba0Sschwarze NULL, /* Oc */ 2193e642ba0Sschwarze post_bk, /* Bk */ 2203e642ba0Sschwarze NULL, /* Ek */ 2213e642ba0Sschwarze post_eoln, /* Bt */ 22204fbb99fSschwarze post_obsolete, /* Hf */ 2233e642ba0Sschwarze post_obsolete, /* Fr */ 2243e642ba0Sschwarze post_eoln, /* Ud */ 2253e642ba0Sschwarze post_lb, /* Lb */ 226*7c539ecbSschwarze post_abort, /* Lp */ 227fe8e59edSschwarze post_delim_nb, /* Lk */ 2283e642ba0Sschwarze post_defaults, /* Mt */ 229fe8e59edSschwarze post_delim_nb, /* Brq */ 2301abead14Sschwarze NULL, /* Bro */ 2313e642ba0Sschwarze NULL, /* Brc */ 2323e642ba0Sschwarze NULL, /* %C */ 2333e642ba0Sschwarze post_es, /* Es */ 2343e642ba0Sschwarze post_en, /* En */ 235816c3c54Sschwarze post_xx, /* Dx */ 2363e642ba0Sschwarze NULL, /* %Q */ 2373e642ba0Sschwarze NULL, /* %U */ 2383e642ba0Sschwarze NULL, /* Ta */ 239f73abda9Skristaps }; 240f73abda9Skristaps 24120fa2881Sschwarze #define RSORD_MAX 14 /* Number of `Rs' blocks. */ 24220fa2881Sschwarze 24314a309e3Sschwarze static const enum roff_tok rsord[RSORD_MAX] = { 24420fa2881Sschwarze MDOC__A, 24520fa2881Sschwarze MDOC__T, 24620fa2881Sschwarze MDOC__B, 24720fa2881Sschwarze MDOC__I, 24820fa2881Sschwarze MDOC__J, 24920fa2881Sschwarze MDOC__R, 25020fa2881Sschwarze MDOC__N, 25120fa2881Sschwarze MDOC__V, 2520397c682Sschwarze MDOC__U, 25320fa2881Sschwarze MDOC__P, 25420fa2881Sschwarze MDOC__Q, 2554e32ec8fSschwarze MDOC__C, 25620fa2881Sschwarze MDOC__D, 2574e32ec8fSschwarze MDOC__O 25820fa2881Sschwarze }; 25920fa2881Sschwarze 26019a69263Sschwarze static const char * const secnames[SEC__MAX] = { 26119a69263Sschwarze NULL, 26219a69263Sschwarze "NAME", 26319a69263Sschwarze "LIBRARY", 26419a69263Sschwarze "SYNOPSIS", 26519a69263Sschwarze "DESCRIPTION", 26603ab2f23Sdlg "CONTEXT", 26719a69263Sschwarze "IMPLEMENTATION NOTES", 26819a69263Sschwarze "RETURN VALUES", 26919a69263Sschwarze "ENVIRONMENT", 27019a69263Sschwarze "FILES", 27119a69263Sschwarze "EXIT STATUS", 27219a69263Sschwarze "EXAMPLES", 27319a69263Sschwarze "DIAGNOSTICS", 27419a69263Sschwarze "COMPATIBILITY", 27519a69263Sschwarze "ERRORS", 27619a69263Sschwarze "SEE ALSO", 27719a69263Sschwarze "STANDARDS", 27819a69263Sschwarze "HISTORY", 27919a69263Sschwarze "AUTHORS", 28019a69263Sschwarze "CAVEATS", 28119a69263Sschwarze "BUGS", 28219a69263Sschwarze "SECURITY CONSIDERATIONS", 28319a69263Sschwarze NULL 28419a69263Sschwarze }; 285f73abda9Skristaps 28649aff9f8Sschwarze 287*7c539ecbSschwarze /* Validate the subtree rooted at mdoc->last. */ 28898b8f00aSschwarze void 289396853b5Sschwarze mdoc_node_validate(struct roff_man *mdoc) 290f73abda9Skristaps { 2916dc98fe5Sschwarze struct roff_node *n, *np; 29214a309e3Sschwarze const v_post *p; 293f73abda9Skristaps 294*7c539ecbSschwarze /* 295*7c539ecbSschwarze * Translate obsolete macros to modern macros first 296*7c539ecbSschwarze * such that later code does not need to look 297*7c539ecbSschwarze * for the obsolete versions. 298*7c539ecbSschwarze */ 299*7c539ecbSschwarze 300753701eeSschwarze n = mdoc->last; 301*7c539ecbSschwarze switch (n->tok) { 302*7c539ecbSschwarze case MDOC_Lp: 303*7c539ecbSschwarze n->tok = MDOC_Pp; 304*7c539ecbSschwarze break; 305*7c539ecbSschwarze case MDOC_Ot: 306*7c539ecbSschwarze post_obsolete(mdoc); 307*7c539ecbSschwarze n->tok = MDOC_Ft; 308*7c539ecbSschwarze break; 309*7c539ecbSschwarze default: 310*7c539ecbSschwarze break; 311*7c539ecbSschwarze } 312*7c539ecbSschwarze 313*7c539ecbSschwarze /* 314*7c539ecbSschwarze * Iterate over all children, recursing into each one 315*7c539ecbSschwarze * in turn, depth-first. 316*7c539ecbSschwarze */ 317*7c539ecbSschwarze 318396853b5Sschwarze mdoc->last = mdoc->last->child; 319396853b5Sschwarze while (mdoc->last != NULL) { 320396853b5Sschwarze mdoc_node_validate(mdoc); 321396853b5Sschwarze if (mdoc->last == n) 322396853b5Sschwarze mdoc->last = mdoc->last->child; 323396853b5Sschwarze else 324396853b5Sschwarze mdoc->last = mdoc->last->next; 325396853b5Sschwarze } 326f73abda9Skristaps 327*7c539ecbSschwarze /* Finally validate the macro itself. */ 328*7c539ecbSschwarze 329396853b5Sschwarze mdoc->last = n; 330396853b5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 331753701eeSschwarze switch (n->type) { 332d1982c71Sschwarze case ROFFT_TEXT: 3336dc98fe5Sschwarze np = n->parent; 33479ca1811Sschwarze if (n->sec != SEC_SYNOPSIS || 3356dc98fe5Sschwarze (np->tok != MDOC_Cd && np->tok != MDOC_Fd)) 3363e642ba0Sschwarze check_text(mdoc, n->line, n->pos, n->string); 3376dc98fe5Sschwarze if (np->tok != MDOC_Ql && np->tok != MDOC_Dl && 3386dc98fe5Sschwarze (np->tok != MDOC_Bd || 3396dc98fe5Sschwarze (mdoc->flags & MDOC_LITERAL) == 0) && 3406dc98fe5Sschwarze (np->tok != MDOC_It || np->type != ROFFT_HEAD || 3416dc98fe5Sschwarze np->parent->parent->norm->Bl.type != LIST_diag)) 3426dc98fe5Sschwarze check_text_em(mdoc, n->line, n->pos, n->string); 3436dc98fe5Sschwarze if (np->tok == MDOC_It || (np->type == ROFFT_BODY && 3446dc98fe5Sschwarze (np->tok == MDOC_Sh || np->tok == MDOC_Ss))) 34548497dd5Sschwarze check_toptext(mdoc, n->line, n->pos, n->string); 3463e642ba0Sschwarze break; 3474c293873Sschwarze case ROFFT_COMMENT: 348d1982c71Sschwarze case ROFFT_EQN: 349d1982c71Sschwarze case ROFFT_TBL: 35098b8f00aSschwarze break; 351d1982c71Sschwarze case ROFFT_ROOT: 35298b8f00aSschwarze post_root(mdoc); 35398b8f00aSschwarze break; 3542791bd1cSschwarze default: 3553e642ba0Sschwarze check_args(mdoc, mdoc->last); 3566f9818f6Sschwarze 3576f9818f6Sschwarze /* 3586f9818f6Sschwarze * Closing delimiters are not special at the 3596f9818f6Sschwarze * beginning of a block, opening delimiters 3606f9818f6Sschwarze * are not special at the end. 3616f9818f6Sschwarze */ 3626f9818f6Sschwarze 3636f9818f6Sschwarze if (n->child != NULL) 364c4b0939cSschwarze n->child->flags &= ~NODE_DELIMC; 3656f9818f6Sschwarze if (n->last != NULL) 366c4b0939cSschwarze n->last->flags &= ~NODE_DELIMO; 3676f9818f6Sschwarze 3686f9818f6Sschwarze /* Call the macro's postprocessor. */ 3696f9818f6Sschwarze 37029478532Sschwarze if (n->tok < ROFF_MAX) { 37129478532Sschwarze switch(n->tok) { 37229478532Sschwarze case ROFF_br: 3736561cb23Sschwarze case ROFF_sp: 37429478532Sschwarze post_par(mdoc); 37529478532Sschwarze break; 37629478532Sschwarze default: 377c4d3fa85Sschwarze roff_validate(mdoc); 378c4d3fa85Sschwarze break; 37929478532Sschwarze } 38029478532Sschwarze break; 38129478532Sschwarze } 38229478532Sschwarze 38329478532Sschwarze assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 384b3f54129Sschwarze p = mdoc_valids + (n->tok - MDOC_Dd); 38598b8f00aSschwarze if (*p) 38698b8f00aSschwarze (*p)(mdoc); 387396853b5Sschwarze if (mdoc->last == n) 388396853b5Sschwarze mdoc_state(mdoc, n); 38998b8f00aSschwarze break; 3902791bd1cSschwarze } 391f73abda9Skristaps } 392f73abda9Skristaps 39398b8f00aSschwarze static void 394ede1b9d0Sschwarze check_args(struct roff_man *mdoc, struct roff_node *n) 395f73abda9Skristaps { 396f73abda9Skristaps int i; 397f73abda9Skristaps 398f73abda9Skristaps if (NULL == n->args) 39920fa2881Sschwarze return; 400f73abda9Skristaps 401f73abda9Skristaps assert(n->args->argc); 402f73abda9Skristaps for (i = 0; i < (int)n->args->argc; i++) 4037ead8a4eSschwarze check_argv(mdoc, n, &n->args->argv[i]); 404f73abda9Skristaps } 405f73abda9Skristaps 40620fa2881Sschwarze static void 407ede1b9d0Sschwarze check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) 408f73abda9Skristaps { 409f73abda9Skristaps int i; 410f73abda9Skristaps 411f73abda9Skristaps for (i = 0; i < (int)v->sz; i++) 4127ead8a4eSschwarze check_text(mdoc, v->line, v->pos, v->value[i]); 413f73abda9Skristaps } 414f73abda9Skristaps 41520fa2881Sschwarze static void 416ede1b9d0Sschwarze check_text(struct roff_man *mdoc, int ln, int pos, char *p) 417f73abda9Skristaps { 41804e980cbSschwarze char *cp; 419769ee804Sschwarze 4207ead8a4eSschwarze if (MDOC_LITERAL & mdoc->flags) 4211cdbf331Sschwarze return; 4221cdbf331Sschwarze 4231cdbf331Sschwarze for (cp = p; NULL != (p = strchr(p, '\t')); p++) 424dd5b31c3Sschwarze mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse, 425dd5b31c3Sschwarze ln, pos + (int)(p - cp), NULL); 426f73abda9Skristaps } 427f73abda9Skristaps 42898b8f00aSschwarze static void 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))) { 4726dc98fe5Sschwarze mandoc_msg(MANDOCERR_DASHDASH, mdoc->parse, 4736dc98fe5Sschwarze ln, pos + (int)(cp - p) - 1, NULL); 4746dc98fe5Sschwarze break; 4756dc98fe5Sschwarze } 4766dc98fe5Sschwarze } 4776dc98fe5Sschwarze } 4786dc98fe5Sschwarze 4796dc98fe5Sschwarze static void 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) 488f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 489f0c18971Sschwarze ln, pos + (cp - p), "Ox"); 490f0c18971Sschwarze if ((cp = strstr(p, "NetBSD")) != NULL) 491f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 492f0c18971Sschwarze ln, pos + (cp - p), "Nx"); 493f0c18971Sschwarze if ((cp = strstr(p, "FreeBSD")) != NULL) 494f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 495f0c18971Sschwarze ln, pos + (cp - p), "Fx"); 496f0c18971Sschwarze if ((cp = strstr(p, "DragonFly")) != NULL) 497f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 498f0c18971Sschwarze ln, pos + (cp - p), "Dx"); 49948497dd5Sschwarze 50048497dd5Sschwarze cp = p; 50148497dd5Sschwarze while ((cp = strstr(cp + 1, "()")) != NULL) { 50248497dd5Sschwarze for (cpr = cp - 1; cpr >= p; cpr--) 50348497dd5Sschwarze if (*cpr != '_' && !isalnum((unsigned char)*cpr)) 50448497dd5Sschwarze break; 50548497dd5Sschwarze if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { 50648497dd5Sschwarze cpr++; 50748497dd5Sschwarze mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse, 50848497dd5Sschwarze ln, pos + (cpr - p), 50948497dd5Sschwarze "%.*s()", (int)(cp - cpr), cpr); 51048497dd5Sschwarze } 51148497dd5Sschwarze } 512f0c18971Sschwarze } 513f0c18971Sschwarze 514f0c18971Sschwarze static void 515*7c539ecbSschwarze post_abort(POST_ARGS) 516*7c539ecbSschwarze { 517*7c539ecbSschwarze abort(); 518*7c539ecbSschwarze } 519*7c539ecbSschwarze 520*7c539ecbSschwarze static void 52104fbb99fSschwarze post_delim(POST_ARGS) 52204fbb99fSschwarze { 52304fbb99fSschwarze const struct roff_node *nch; 524fe8e59edSschwarze const char *lc; 525fe8e59edSschwarze enum mdelim delim; 526fe8e59edSschwarze enum roff_tok tok; 527fe8e59edSschwarze 528fe8e59edSschwarze tok = mdoc->last->tok; 529fe8e59edSschwarze nch = mdoc->last->last; 530fe8e59edSschwarze if (nch == NULL || nch->type != ROFFT_TEXT) 531fe8e59edSschwarze return; 532fe8e59edSschwarze lc = strchr(nch->string, '\0') - 1; 533fe8e59edSschwarze if (lc < nch->string) 534fe8e59edSschwarze return; 535fe8e59edSschwarze delim = mdoc_isdelim(lc); 536fe8e59edSschwarze if (delim == DELIM_NONE || delim == DELIM_OPEN) 537fe8e59edSschwarze return; 538fe8e59edSschwarze if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh || 539fe8e59edSschwarze tok == MDOC_Ss || tok == MDOC_Fo)) 540fe8e59edSschwarze return; 541fe8e59edSschwarze 542fe8e59edSschwarze mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse, 543fe8e59edSschwarze nch->line, nch->pos + (lc - nch->string), 544fe8e59edSschwarze "%s%s %s", roff_name[tok], 545fe8e59edSschwarze nch == mdoc->last->child ? "" : " ...", nch->string); 546fe8e59edSschwarze } 547fe8e59edSschwarze 548fe8e59edSschwarze static void 549fe8e59edSschwarze post_delim_nb(POST_ARGS) 550fe8e59edSschwarze { 551fe8e59edSschwarze const struct roff_node *nch; 552b7c50e87Sschwarze const char *lc, *cp; 553b7c50e87Sschwarze int nw; 55404fbb99fSschwarze enum mdelim delim; 555b7c50e87Sschwarze enum roff_tok tok; 55604fbb99fSschwarze 557b7c50e87Sschwarze /* 558b7c50e87Sschwarze * Find candidates: at least two bytes, 559b7c50e87Sschwarze * the last one a closing or middle delimiter. 560b7c50e87Sschwarze */ 561b7c50e87Sschwarze 562b7c50e87Sschwarze tok = mdoc->last->tok; 56304fbb99fSschwarze nch = mdoc->last->last; 56404fbb99fSschwarze if (nch == NULL || nch->type != ROFFT_TEXT) 56504fbb99fSschwarze return; 56604fbb99fSschwarze lc = strchr(nch->string, '\0') - 1; 56704fbb99fSschwarze if (lc <= nch->string) 56804fbb99fSschwarze return; 56904fbb99fSschwarze delim = mdoc_isdelim(lc); 57004fbb99fSschwarze if (delim == DELIM_NONE || delim == DELIM_OPEN) 57104fbb99fSschwarze return; 572b7c50e87Sschwarze 573b7c50e87Sschwarze /* 574b7c50e87Sschwarze * Reduce false positives by allowing various cases. 575b7c50e87Sschwarze */ 576b7c50e87Sschwarze 577b7c50e87Sschwarze /* Escaped delimiters. */ 578b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '\\' && 579b7c50e87Sschwarze (lc[-1] == '&' || lc[-1] == 'e')) 580b7c50e87Sschwarze return; 581b7c50e87Sschwarze 582b7c50e87Sschwarze /* Specific byte sequences. */ 583b7c50e87Sschwarze switch (*lc) { 584b7c50e87Sschwarze case ')': 585b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--) 586b7c50e87Sschwarze if (*cp == '(') 587b7c50e87Sschwarze return; 588b7c50e87Sschwarze break; 589b7c50e87Sschwarze case '.': 590b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') 591b7c50e87Sschwarze return; 592b7c50e87Sschwarze if (lc[-1] == '.') 593b7c50e87Sschwarze return; 594b7c50e87Sschwarze break; 595b7c50e87Sschwarze case ';': 596b7c50e87Sschwarze if (tok == MDOC_Vt) 597b7c50e87Sschwarze return; 598b7c50e87Sschwarze break; 599b7c50e87Sschwarze case '?': 600b7c50e87Sschwarze if (lc[-1] == '?') 601b7c50e87Sschwarze return; 602b7c50e87Sschwarze break; 603b7c50e87Sschwarze case ']': 604b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--) 605b7c50e87Sschwarze if (*cp == '[') 606b7c50e87Sschwarze return; 607b7c50e87Sschwarze break; 608b7c50e87Sschwarze case '|': 609b7c50e87Sschwarze if (lc == nch->string + 1 && lc[-1] == '|') 610b7c50e87Sschwarze return; 611b7c50e87Sschwarze default: 612b7c50e87Sschwarze break; 613b7c50e87Sschwarze } 614b7c50e87Sschwarze 615b7c50e87Sschwarze /* Exactly two non-alphanumeric bytes. */ 616b7c50e87Sschwarze if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) 617b7c50e87Sschwarze return; 618b7c50e87Sschwarze 619b7c50e87Sschwarze /* At least three alphabetic words with a sentence ending. */ 620b7c50e87Sschwarze if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || 6211abead14Sschwarze tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) { 622b7c50e87Sschwarze nw = 0; 623b7c50e87Sschwarze for (cp = lc - 1; cp >= nch->string; cp--) { 624b7c50e87Sschwarze if (*cp == ' ') { 625b7c50e87Sschwarze nw++; 626b7c50e87Sschwarze if (cp > nch->string && cp[-1] == ',') 627b7c50e87Sschwarze cp--; 628b7c50e87Sschwarze } else if (isalpha((unsigned int)*cp)) { 629b7c50e87Sschwarze if (nw > 1) 630b7c50e87Sschwarze return; 631b7c50e87Sschwarze } else 632b7c50e87Sschwarze break; 633b7c50e87Sschwarze } 634b7c50e87Sschwarze } 635b7c50e87Sschwarze 636fe8e59edSschwarze mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse, 63704fbb99fSschwarze nch->line, nch->pos + (lc - nch->string), 638b7c50e87Sschwarze "%s%s %s", roff_name[tok], 63904fbb99fSschwarze nch == mdoc->last->child ? "" : " ...", nch->string); 64004fbb99fSschwarze } 64104fbb99fSschwarze 64204fbb99fSschwarze static void 6433e642ba0Sschwarze post_bl_norm(POST_ARGS) 644f73abda9Skristaps { 6453e642ba0Sschwarze struct roff_node *n; 646aa99c14fSschwarze struct mdoc_argv *argv, *wa; 6474a9f685fSschwarze int i; 648aa99c14fSschwarze enum mdocargt mdoclt; 6494a9f685fSschwarze enum mdoc_list lt; 650f73abda9Skristaps 6513e642ba0Sschwarze n = mdoc->last->parent; 6523e642ba0Sschwarze n->norm->Bl.type = LIST__NONE; 653f73abda9Skristaps 6546093755cSschwarze /* 6556093755cSschwarze * First figure out which kind of list to use: bind ourselves to 6566093755cSschwarze * the first mentioned list type and warn about any remaining 6576093755cSschwarze * ones. If we find no list type, we default to LIST_item. 6586093755cSschwarze */ 659f73abda9Skristaps 660dadc3a61Sschwarze wa = (n->args == NULL) ? NULL : n->args->argv; 661aa99c14fSschwarze mdoclt = MDOC_ARG_MAX; 6626093755cSschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 6634a9f685fSschwarze argv = n->args->argv + i; 6646093755cSschwarze lt = LIST__NONE; 6654a9f685fSschwarze switch (argv->arg) { 6666093755cSschwarze /* Set list types. */ 66749aff9f8Sschwarze case MDOC_Bullet: 6686093755cSschwarze lt = LIST_bullet; 6696093755cSschwarze break; 67049aff9f8Sschwarze case MDOC_Dash: 6716093755cSschwarze lt = LIST_dash; 6726093755cSschwarze break; 67349aff9f8Sschwarze case MDOC_Enum: 6746093755cSschwarze lt = LIST_enum; 6756093755cSschwarze break; 67649aff9f8Sschwarze case MDOC_Hyphen: 6776093755cSschwarze lt = LIST_hyphen; 6786093755cSschwarze break; 67949aff9f8Sschwarze case MDOC_Item: 6806093755cSschwarze lt = LIST_item; 6816093755cSschwarze break; 68249aff9f8Sschwarze case MDOC_Tag: 6836093755cSschwarze lt = LIST_tag; 6846093755cSschwarze break; 68549aff9f8Sschwarze case MDOC_Diag: 6866093755cSschwarze lt = LIST_diag; 6876093755cSschwarze break; 68849aff9f8Sschwarze case MDOC_Hang: 6896093755cSschwarze lt = LIST_hang; 6906093755cSschwarze break; 69149aff9f8Sschwarze case MDOC_Ohang: 6926093755cSschwarze lt = LIST_ohang; 6936093755cSschwarze break; 69449aff9f8Sschwarze case MDOC_Inset: 6956093755cSschwarze lt = LIST_inset; 6966093755cSschwarze break; 69749aff9f8Sschwarze case MDOC_Column: 6986093755cSschwarze lt = LIST_column; 69964d728e4Sschwarze break; 7006093755cSschwarze /* Set list arguments. */ 70149aff9f8Sschwarze case MDOC_Compact: 7024a9f685fSschwarze if (n->norm->Bl.comp) 7034a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP, 7044a9f685fSschwarze mdoc->parse, argv->line, 7054a9f685fSschwarze argv->pos, "Bl -compact"); 7064a9f685fSschwarze n->norm->Bl.comp = 1; 70750e63e03Sschwarze break; 70849aff9f8Sschwarze case MDOC_Width: 709aa99c14fSschwarze wa = argv; 7104a9f685fSschwarze if (0 == argv->sz) { 7114a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 7124a9f685fSschwarze mdoc->parse, argv->line, 7134a9f685fSschwarze argv->pos, "Bl -width"); 7144a9f685fSschwarze n->norm->Bl.width = "0n"; 71522972b14Sschwarze break; 71622972b14Sschwarze } 7174a9f685fSschwarze if (NULL != n->norm->Bl.width) 7184a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 7194a9f685fSschwarze mdoc->parse, argv->line, 7204a9f685fSschwarze argv->pos, "Bl -width %s", 7214a9f685fSschwarze argv->value[0]); 7226050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 7234a9f685fSschwarze n->norm->Bl.width = argv->value[0]; 72464d728e4Sschwarze break; 72549aff9f8Sschwarze case MDOC_Offset: 7264a9f685fSschwarze if (0 == argv->sz) { 7274a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 7284a9f685fSschwarze mdoc->parse, argv->line, 7294a9f685fSschwarze argv->pos, "Bl -offset"); 73031e23753Sschwarze break; 73131e23753Sschwarze } 7324a9f685fSschwarze if (NULL != n->norm->Bl.offs) 7334a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 7344a9f685fSschwarze mdoc->parse, argv->line, 7354a9f685fSschwarze argv->pos, "Bl -offset %s", 7364a9f685fSschwarze argv->value[0]); 7376050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 7384a9f685fSschwarze n->norm->Bl.offs = argv->value[0]; 739f73abda9Skristaps break; 740ddce0b0cSschwarze default: 741ddce0b0cSschwarze continue; 742f73abda9Skristaps } 743dc0d8bb2Sschwarze if (LIST__NONE == lt) 744dc0d8bb2Sschwarze continue; 745aa99c14fSschwarze mdoclt = argv->arg; 746f73abda9Skristaps 7476093755cSschwarze /* Check: multiple list types. */ 7486093755cSschwarze 749dc0d8bb2Sschwarze if (LIST__NONE != n->norm->Bl.type) { 750bd594191Sschwarze mandoc_vmsg(MANDOCERR_BL_REP, 751dc0d8bb2Sschwarze mdoc->parse, n->line, n->pos, 752bd594191Sschwarze "Bl -%s", mdoc_argnames[argv->arg]); 753dc0d8bb2Sschwarze continue; 754769ee804Sschwarze } 7556093755cSschwarze 7566093755cSschwarze /* The list type should come first. */ 7576093755cSschwarze 7588c62fbf5Sschwarze if (n->norm->Bl.width || 7598c62fbf5Sschwarze n->norm->Bl.offs || 7608c62fbf5Sschwarze n->norm->Bl.comp) 761bd594191Sschwarze mandoc_vmsg(MANDOCERR_BL_LATETYPE, 762bd594191Sschwarze mdoc->parse, n->line, n->pos, "Bl -%s", 76366788495Sschwarze mdoc_argnames[n->args->argv[0].arg]); 764dc0d8bb2Sschwarze 765dc0d8bb2Sschwarze n->norm->Bl.type = lt; 766dc0d8bb2Sschwarze if (LIST_column == lt) { 767dc0d8bb2Sschwarze n->norm->Bl.ncols = argv->sz; 768dc0d8bb2Sschwarze n->norm->Bl.cols = (void *)argv->value; 769dc0d8bb2Sschwarze } 7706093755cSschwarze } 7716093755cSschwarze 7726093755cSschwarze /* Allow lists to default to LIST_item. */ 7736093755cSschwarze 7748c62fbf5Sschwarze if (LIST__NONE == n->norm->Bl.type) { 775bd594191Sschwarze mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, 776bd594191Sschwarze n->line, n->pos, "Bl"); 7778c62fbf5Sschwarze n->norm->Bl.type = LIST_item; 7782c7eb483Sschwarze mdoclt = MDOC_Item; 7796e03d529Sschwarze } 780f73abda9Skristaps 78164d728e4Sschwarze /* 78264d728e4Sschwarze * Validate the width field. Some list types don't need width 78364d728e4Sschwarze * types and should be warned about them. Others should have it 7845eced068Sschwarze * and must also be warned. Yet others have a default and need 7855eced068Sschwarze * no warning. 78664d728e4Sschwarze */ 78764d728e4Sschwarze 7888c62fbf5Sschwarze switch (n->norm->Bl.type) { 78949aff9f8Sschwarze case LIST_tag: 790162c3bafSschwarze if (n->norm->Bl.width == NULL) 791bd594191Sschwarze mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse, 792bd594191Sschwarze n->line, n->pos, "Bl -tag"); 793f73abda9Skristaps break; 79449aff9f8Sschwarze case LIST_column: 79549aff9f8Sschwarze case LIST_diag: 79649aff9f8Sschwarze case LIST_ohang: 79749aff9f8Sschwarze case LIST_inset: 79849aff9f8Sschwarze case LIST_item: 799162c3bafSschwarze if (n->norm->Bl.width != NULL) 800aa99c14fSschwarze mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, 801aa99c14fSschwarze wa->line, wa->pos, "Bl -%s", 802aa99c14fSschwarze mdoc_argnames[mdoclt]); 803162c3bafSschwarze n->norm->Bl.width = NULL; 8046093755cSschwarze break; 80549aff9f8Sschwarze case LIST_bullet: 80649aff9f8Sschwarze case LIST_dash: 80749aff9f8Sschwarze case LIST_hyphen: 808162c3bafSschwarze if (n->norm->Bl.width == NULL) 8095eced068Sschwarze n->norm->Bl.width = "2n"; 8105eced068Sschwarze break; 81149aff9f8Sschwarze case LIST_enum: 812162c3bafSschwarze if (n->norm->Bl.width == NULL) 8135eced068Sschwarze n->norm->Bl.width = "3n"; 8145eced068Sschwarze break; 81564d728e4Sschwarze default: 816f73abda9Skristaps break; 81764d728e4Sschwarze } 818f73abda9Skristaps } 819f73abda9Skristaps 82098b8f00aSschwarze static void 8213e642ba0Sschwarze post_bd(POST_ARGS) 822f73abda9Skristaps { 8233e642ba0Sschwarze struct roff_node *n; 8244a9f685fSschwarze struct mdoc_argv *argv; 8254a9f685fSschwarze int i; 8264a9f685fSschwarze enum mdoc_disp dt; 827f73abda9Skristaps 8283e642ba0Sschwarze n = mdoc->last; 82931e23753Sschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 8304a9f685fSschwarze argv = n->args->argv + i; 83131e23753Sschwarze dt = DISP__NONE; 83231e23753Sschwarze 8334a9f685fSschwarze switch (argv->arg) { 83449aff9f8Sschwarze case MDOC_Centred: 8352065e47aSschwarze dt = DISP_centered; 83631e23753Sschwarze break; 83749aff9f8Sschwarze case MDOC_Ragged: 83831e23753Sschwarze dt = DISP_ragged; 83931e23753Sschwarze break; 84049aff9f8Sschwarze case MDOC_Unfilled: 84131e23753Sschwarze dt = DISP_unfilled; 84231e23753Sschwarze break; 84349aff9f8Sschwarze case MDOC_Filled: 84431e23753Sschwarze dt = DISP_filled; 84531e23753Sschwarze break; 84649aff9f8Sschwarze case MDOC_Literal: 84731e23753Sschwarze dt = DISP_literal; 848f73abda9Skristaps break; 84949aff9f8Sschwarze case MDOC_File: 850bd594191Sschwarze mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse, 851bd594191Sschwarze n->line, n->pos, NULL); 8521164a325Sschwarze break; 85349aff9f8Sschwarze case MDOC_Offset: 8544a9f685fSschwarze if (0 == argv->sz) { 8554a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 8564a9f685fSschwarze mdoc->parse, argv->line, 8574a9f685fSschwarze argv->pos, "Bd -offset"); 858f73abda9Skristaps break; 859f73abda9Skristaps } 8604a9f685fSschwarze if (NULL != n->norm->Bd.offs) 8614a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 8624a9f685fSschwarze mdoc->parse, argv->line, 8634a9f685fSschwarze argv->pos, "Bd -offset %s", 8644a9f685fSschwarze argv->value[0]); 8656050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 8664a9f685fSschwarze n->norm->Bd.offs = argv->value[0]; 86731e23753Sschwarze break; 86849aff9f8Sschwarze case MDOC_Compact: 8694a9f685fSschwarze if (n->norm->Bd.comp) 8704a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP, 8714a9f685fSschwarze mdoc->parse, argv->line, 8724a9f685fSschwarze argv->pos, "Bd -compact"); 8734a9f685fSschwarze n->norm->Bd.comp = 1; 87431e23753Sschwarze break; 87531e23753Sschwarze default: 87631e23753Sschwarze abort(); 87731e23753Sschwarze } 878dc0d8bb2Sschwarze if (DISP__NONE == dt) 879dc0d8bb2Sschwarze continue; 88031e23753Sschwarze 881dc0d8bb2Sschwarze if (DISP__NONE == n->norm->Bd.type) 8828c62fbf5Sschwarze n->norm->Bd.type = dt; 883dc0d8bb2Sschwarze else 884bd594191Sschwarze mandoc_vmsg(MANDOCERR_BD_REP, 885dc0d8bb2Sschwarze mdoc->parse, n->line, n->pos, 886bd594191Sschwarze "Bd -%s", mdoc_argnames[argv->arg]); 88731e23753Sschwarze } 88831e23753Sschwarze 8898c62fbf5Sschwarze if (DISP__NONE == n->norm->Bd.type) { 890bd594191Sschwarze mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse, 891bd594191Sschwarze n->line, n->pos, "Bd"); 8928c62fbf5Sschwarze n->norm->Bd.type = DISP_ragged; 89331e23753Sschwarze } 894f73abda9Skristaps } 895f73abda9Skristaps 8968ccddcd3Sschwarze /* 8978ccddcd3Sschwarze * Stand-alone line macros. 8988ccddcd3Sschwarze */ 8998ccddcd3Sschwarze 90098b8f00aSschwarze static void 9013e642ba0Sschwarze post_an_norm(POST_ARGS) 902f73abda9Skristaps { 9033e642ba0Sschwarze struct roff_node *n; 904aa99c14fSschwarze struct mdoc_argv *argv; 905aa99c14fSschwarze size_t i; 906f73abda9Skristaps 9073e642ba0Sschwarze n = mdoc->last; 908aa99c14fSschwarze if (n->args == NULL) 90998b8f00aSschwarze return; 910769ee804Sschwarze 911aa99c14fSschwarze for (i = 1; i < n->args->argc; i++) { 912aa99c14fSschwarze argv = n->args->argv + i; 913aa99c14fSschwarze mandoc_vmsg(MANDOCERR_AN_REP, 914aa99c14fSschwarze mdoc->parse, argv->line, argv->pos, 915aa99c14fSschwarze "An -%s", mdoc_argnames[argv->arg]); 916aa99c14fSschwarze } 9177c2be9f8Sschwarze 918aa99c14fSschwarze argv = n->args->argv; 919aa99c14fSschwarze if (argv->arg == MDOC_Split) 9208c62fbf5Sschwarze n->norm->An.auth = AUTH_split; 921aa99c14fSschwarze else if (argv->arg == MDOC_Nosplit) 9228c62fbf5Sschwarze n->norm->An.auth = AUTH_nosplit; 923769ee804Sschwarze else 924769ee804Sschwarze abort(); 925f73abda9Skristaps } 926f73abda9Skristaps 92798b8f00aSschwarze static void 9288ccddcd3Sschwarze post_eoln(POST_ARGS) 9298ccddcd3Sschwarze { 9308ccddcd3Sschwarze struct roff_node *n; 9318ccddcd3Sschwarze 932bc205043Sschwarze post_useless(mdoc); 9338ccddcd3Sschwarze n = mdoc->last; 9348ccddcd3Sschwarze if (n->child != NULL) 93514a309e3Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line, 93614a309e3Sschwarze n->pos, "%s %s", roff_name[n->tok], n->child->string); 9378ccddcd3Sschwarze 9388ccddcd3Sschwarze while (n->child != NULL) 9398ccddcd3Sschwarze roff_node_delete(mdoc, n->child); 9408ccddcd3Sschwarze 9418ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? 9428ccddcd3Sschwarze "is currently in beta test." : "currently under development."); 9438ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 9448ccddcd3Sschwarze mdoc->last = n; 9458ccddcd3Sschwarze } 9468ccddcd3Sschwarze 9478ccddcd3Sschwarze static int 9488ccddcd3Sschwarze build_list(struct roff_man *mdoc, int tok) 9498ccddcd3Sschwarze { 9508ccddcd3Sschwarze struct roff_node *n; 9518ccddcd3Sschwarze int ic; 9528ccddcd3Sschwarze 9538ccddcd3Sschwarze n = mdoc->last->next; 9548ccddcd3Sschwarze for (ic = 1;; ic++) { 9558ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, tok); 9568ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9578ccddcd3Sschwarze mdoc_node_relink(mdoc, n); 9588ccddcd3Sschwarze n = mdoc->last = mdoc->last->parent; 9598ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 9608ccddcd3Sschwarze if (n->next == NULL) 9618ccddcd3Sschwarze return ic; 9628ccddcd3Sschwarze if (ic > 1 || n->next->next != NULL) { 9638ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, ","); 9648ccddcd3Sschwarze mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC; 9658ccddcd3Sschwarze } 9668ccddcd3Sschwarze n = mdoc->last->next; 9678ccddcd3Sschwarze if (n->next == NULL) { 9688ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "and"); 9698ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9708ccddcd3Sschwarze } 9718ccddcd3Sschwarze } 9728ccddcd3Sschwarze } 9738ccddcd3Sschwarze 9748ccddcd3Sschwarze static void 9758ccddcd3Sschwarze post_ex(POST_ARGS) 9768ccddcd3Sschwarze { 9778ccddcd3Sschwarze struct roff_node *n; 9788ccddcd3Sschwarze int ic; 9798ccddcd3Sschwarze 9808ccddcd3Sschwarze post_std(mdoc); 9818ccddcd3Sschwarze 9828ccddcd3Sschwarze n = mdoc->last; 9838ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 9848ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The"); 9858ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9868ccddcd3Sschwarze 9878ccddcd3Sschwarze if (mdoc->last->next != NULL) 9888ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Nm); 9898ccddcd3Sschwarze else if (mdoc->meta.name != NULL) { 9908ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); 9918ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9928ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 9938ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9948ccddcd3Sschwarze mdoc->last = mdoc->last->parent; 9958ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 9968ccddcd3Sschwarze ic = 1; 9978ccddcd3Sschwarze } else { 9988ccddcd3Sschwarze mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, 9998ccddcd3Sschwarze n->line, n->pos, "Ex"); 10008ccddcd3Sschwarze ic = 0; 10018ccddcd3Sschwarze } 10028ccddcd3Sschwarze 10038ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 10048ccddcd3Sschwarze ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); 10058ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10068ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 10078ccddcd3Sschwarze "on success, and\\~>0 if an error occurs."); 10088ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 10098ccddcd3Sschwarze mdoc->last = n; 10108ccddcd3Sschwarze } 10118ccddcd3Sschwarze 10128ccddcd3Sschwarze static void 10138ccddcd3Sschwarze post_lb(POST_ARGS) 10148ccddcd3Sschwarze { 10158ccddcd3Sschwarze struct roff_node *n; 10168ccddcd3Sschwarze 1017fe8e59edSschwarze post_delim_nb(mdoc); 101804fbb99fSschwarze 10198ccddcd3Sschwarze n = mdoc->last; 10208ccddcd3Sschwarze assert(n->child->type == ROFFT_TEXT); 10218ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 10228ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "library"); 10238ccddcd3Sschwarze mdoc->last->flags = NODE_NOSRC; 1024965f5c87Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(lq"); 10258ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 10268ccddcd3Sschwarze mdoc->last = mdoc->last->next; 1027965f5c87Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(rq"); 10288ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; 10298ccddcd3Sschwarze mdoc->last = n; 10308ccddcd3Sschwarze } 10318ccddcd3Sschwarze 10328ccddcd3Sschwarze static void 10338ccddcd3Sschwarze post_rv(POST_ARGS) 10348ccddcd3Sschwarze { 10358ccddcd3Sschwarze struct roff_node *n; 10368ccddcd3Sschwarze int ic; 10378ccddcd3Sschwarze 10388ccddcd3Sschwarze post_std(mdoc); 10398ccddcd3Sschwarze 10408ccddcd3Sschwarze n = mdoc->last; 10418ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 10428ccddcd3Sschwarze if (n->child != NULL) { 10438ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The"); 10448ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10458ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Fn); 10468ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 10478ccddcd3Sschwarze ic > 1 ? "functions return" : "function returns"); 10488ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10498ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 10508ccddcd3Sschwarze "the value\\~0 if successful;"); 10518ccddcd3Sschwarze } else 10528ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " 10538ccddcd3Sschwarze "completion, the value\\~0 is returned;"); 10548ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10558ccddcd3Sschwarze 10568ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "otherwise " 10578ccddcd3Sschwarze "the value\\~\\-1 is returned and the global variable"); 10588ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10598ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); 10608ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10618ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "errno"); 10628ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 10638ccddcd3Sschwarze mdoc->last = mdoc->last->parent; 10648ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 10658ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 10668ccddcd3Sschwarze "is set to indicate the error."); 10678ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 10688ccddcd3Sschwarze mdoc->last = n; 10698ccddcd3Sschwarze } 10708ccddcd3Sschwarze 10718ccddcd3Sschwarze static void 10723e642ba0Sschwarze post_std(POST_ARGS) 1073f73abda9Skristaps { 10743e642ba0Sschwarze struct roff_node *n; 1075f73abda9Skristaps 1076fe8e59edSschwarze post_delim(mdoc); 1077fe8e59edSschwarze 10783e642ba0Sschwarze n = mdoc->last; 10793e642ba0Sschwarze if (n->args && n->args->argc == 1) 10803e642ba0Sschwarze if (n->args->argv[0].arg == MDOC_Std) 108198b8f00aSschwarze return; 1082f73abda9Skristaps 108366788495Sschwarze mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, 108414a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 10856093755cSschwarze } 10866093755cSschwarze 108798b8f00aSschwarze static void 10888ccddcd3Sschwarze post_st(POST_ARGS) 10898ccddcd3Sschwarze { 10908ccddcd3Sschwarze struct roff_node *n, *nch; 10918ccddcd3Sschwarze const char *p; 10928ccddcd3Sschwarze 10938ccddcd3Sschwarze n = mdoc->last; 10948ccddcd3Sschwarze nch = n->child; 10958ccddcd3Sschwarze assert(nch->type == ROFFT_TEXT); 10968ccddcd3Sschwarze 10978ccddcd3Sschwarze if ((p = mdoc_a2st(nch->string)) == NULL) { 10988ccddcd3Sschwarze mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, 10998ccddcd3Sschwarze nch->line, nch->pos, "St %s", nch->string); 11008ccddcd3Sschwarze roff_node_delete(mdoc, n); 11018ccddcd3Sschwarze return; 11028ccddcd3Sschwarze } 11038ccddcd3Sschwarze 11048ccddcd3Sschwarze nch->flags |= NODE_NOPRT; 11058ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 11068ccddcd3Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, p); 11078ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 11088ccddcd3Sschwarze mdoc->last= n; 11098ccddcd3Sschwarze } 11108ccddcd3Sschwarze 11118ccddcd3Sschwarze static void 11123e642ba0Sschwarze post_obsolete(POST_ARGS) 1113551cd4a8Sschwarze { 11143e642ba0Sschwarze struct roff_node *n; 1115551cd4a8Sschwarze 11163e642ba0Sschwarze n = mdoc->last; 1117d1982c71Sschwarze if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 1118551cd4a8Sschwarze mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, 111914a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 1120551cd4a8Sschwarze } 1121551cd4a8Sschwarze 1122bc205043Sschwarze static void 1123bc205043Sschwarze post_useless(POST_ARGS) 1124bc205043Sschwarze { 1125bc205043Sschwarze struct roff_node *n; 1126bc205043Sschwarze 1127bc205043Sschwarze n = mdoc->last; 1128bc205043Sschwarze mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse, 1129bc205043Sschwarze n->line, n->pos, roff_name[n->tok]); 1130bc205043Sschwarze } 1131bc205043Sschwarze 11328ccddcd3Sschwarze /* 11338ccddcd3Sschwarze * Block macros. 11348ccddcd3Sschwarze */ 11358ccddcd3Sschwarze 113698b8f00aSschwarze static void 1137f73abda9Skristaps post_bf(POST_ARGS) 1138f73abda9Skristaps { 11393a0d07afSschwarze struct roff_node *np, *nch; 1140f73abda9Skristaps 1141769ee804Sschwarze /* 1142769ee804Sschwarze * Unlike other data pointers, these are "housed" by the HEAD 1143769ee804Sschwarze * element, which contains the goods. 1144769ee804Sschwarze */ 1145769ee804Sschwarze 1146769ee804Sschwarze np = mdoc->last; 1147d1982c71Sschwarze if (np->type != ROFFT_HEAD) 1148ae2efdd8Sschwarze return; 1149ae2efdd8Sschwarze 1150d1982c71Sschwarze assert(np->parent->type == ROFFT_BLOCK); 1151f051602aSschwarze assert(np->parent->tok == MDOC_Bf); 115250d41253Sschwarze 1153ecb10c32Sschwarze /* Check the number of arguments. */ 1154f73abda9Skristaps 1155ecb10c32Sschwarze nch = np->child; 1156f051602aSschwarze if (np->parent->args == NULL) { 1157f051602aSschwarze if (nch == NULL) { 1158bd594191Sschwarze mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, 1159bd594191Sschwarze np->line, np->pos, "Bf"); 116098b8f00aSschwarze return; 116120fa2881Sschwarze } 1162ecb10c32Sschwarze nch = nch->next; 1163ecb10c32Sschwarze } 1164f051602aSschwarze if (nch != NULL) 1165ecb10c32Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1166ecb10c32Sschwarze nch->line, nch->pos, "Bf ... %s", nch->string); 1167769ee804Sschwarze 1168769ee804Sschwarze /* Extract argument into data. */ 1169769ee804Sschwarze 1170f051602aSschwarze if (np->parent->args != NULL) { 1171f051602aSschwarze switch (np->parent->args->argv[0].arg) { 1172f051602aSschwarze case MDOC_Emphasis: 11738c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1174f051602aSschwarze break; 1175f051602aSschwarze case MDOC_Literal: 11768c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1177f051602aSschwarze break; 1178f051602aSschwarze case MDOC_Symbolic: 11798c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 1180f051602aSschwarze break; 1181f051602aSschwarze default: 1182769ee804Sschwarze abort(); 1183f051602aSschwarze } 118498b8f00aSschwarze return; 1185769ee804Sschwarze } 1186769ee804Sschwarze 1187769ee804Sschwarze /* Extract parameter into data. */ 1188769ee804Sschwarze 1189f051602aSschwarze if ( ! strcmp(np->child->string, "Em")) 11908c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1191f051602aSschwarze else if ( ! strcmp(np->child->string, "Li")) 11928c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1193f051602aSschwarze else if ( ! strcmp(np->child->string, "Sy")) 11948c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 119520fa2881Sschwarze else 1196ecb10c32Sschwarze mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, 1197ecb10c32Sschwarze np->child->line, np->child->pos, 1198ecb10c32Sschwarze "Bf %s", np->child->string); 1199f73abda9Skristaps } 1200f73abda9Skristaps 120198b8f00aSschwarze static void 12020c5064e3Sschwarze post_fname(POST_ARGS) 12030c5064e3Sschwarze { 12043a0d07afSschwarze const struct roff_node *n; 12050ff14c71Sschwarze const char *cp; 12060c5064e3Sschwarze size_t pos; 12070c5064e3Sschwarze 12080c5064e3Sschwarze n = mdoc->last->child; 12090c5064e3Sschwarze pos = strcspn(n->string, "()"); 12100ff14c71Sschwarze cp = n->string + pos; 12110ff14c71Sschwarze if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) 12120c5064e3Sschwarze mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse, 12130c5064e3Sschwarze n->line, n->pos + pos, n->string); 12140c5064e3Sschwarze } 12150c5064e3Sschwarze 121698b8f00aSschwarze static void 12170c5064e3Sschwarze post_fn(POST_ARGS) 12180c5064e3Sschwarze { 12190c5064e3Sschwarze 12200c5064e3Sschwarze post_fname(mdoc); 12210c5064e3Sschwarze post_fa(mdoc); 12220c5064e3Sschwarze } 12230c5064e3Sschwarze 122498b8f00aSschwarze static void 1225753701eeSschwarze post_fo(POST_ARGS) 1226753701eeSschwarze { 12273a0d07afSschwarze const struct roff_node *n; 1228753701eeSschwarze 1229afcd1f03Sschwarze n = mdoc->last; 1230afcd1f03Sschwarze 1231d1982c71Sschwarze if (n->type != ROFFT_HEAD) 1232afcd1f03Sschwarze return; 1233afcd1f03Sschwarze 1234afcd1f03Sschwarze if (n->child == NULL) { 1235afcd1f03Sschwarze mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse, 1236afcd1f03Sschwarze n->line, n->pos, "Fo"); 1237afcd1f03Sschwarze return; 1238afcd1f03Sschwarze } 1239afcd1f03Sschwarze if (n->child != n->last) { 1240afcd1f03Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1241afcd1f03Sschwarze n->child->next->line, n->child->next->pos, 1242afcd1f03Sschwarze "Fo ... %s", n->child->next->string); 1243afcd1f03Sschwarze while (n->child != n->last) 1244fa2127f9Sschwarze roff_node_delete(mdoc, n->last); 1245fe8e59edSschwarze } else 1246fe8e59edSschwarze post_delim(mdoc); 1247afcd1f03Sschwarze 12480c5064e3Sschwarze post_fname(mdoc); 1249753701eeSschwarze } 1250753701eeSschwarze 125198b8f00aSschwarze static void 12527e92c062Sschwarze post_fa(POST_ARGS) 12537e92c062Sschwarze { 12543a0d07afSschwarze const struct roff_node *n; 12557e92c062Sschwarze const char *cp; 12567e92c062Sschwarze 12577e92c062Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) { 12587e92c062Sschwarze for (cp = n->string; *cp != '\0'; cp++) { 12597e92c062Sschwarze /* Ignore callbacks and alterations. */ 12607e92c062Sschwarze if (*cp == '(' || *cp == '{') 12617e92c062Sschwarze break; 12627e92c062Sschwarze if (*cp != ',') 12637e92c062Sschwarze continue; 12647e92c062Sschwarze mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse, 12657e92c062Sschwarze n->line, n->pos + (cp - n->string), 12667e92c062Sschwarze n->string); 12677e92c062Sschwarze break; 12687e92c062Sschwarze } 12697e92c062Sschwarze } 1270fe8e59edSschwarze post_delim_nb(mdoc); 12717e92c062Sschwarze } 12727e92c062Sschwarze 127398b8f00aSschwarze static void 1274f73abda9Skristaps post_nm(POST_ARGS) 1275f73abda9Skristaps { 12763a0d07afSschwarze struct roff_node *n; 12772d266539Sschwarze 12782d266539Sschwarze n = mdoc->last; 12792d266539Sschwarze 12802ab19127Sschwarze if (n->sec == SEC_NAME && n->child != NULL && 12812ab19127Sschwarze n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL) 128252d11c96Sschwarze mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); 128352d11c96Sschwarze 1284*7c539ecbSschwarze if (n->last != NULL && n->last->tok == MDOC_Pp) 12852d266539Sschwarze mdoc_node_relink(mdoc, n->last); 128620fa2881Sschwarze 1287f27faaccSschwarze if (mdoc->meta.name == NULL) 1288423631c9Sschwarze deroff(&mdoc->meta.name, n); 128920fa2881Sschwarze 1290f27faaccSschwarze if (mdoc->meta.name == NULL || 1291f27faaccSschwarze (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1292bd594191Sschwarze mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, 12932d266539Sschwarze n->line, n->pos, "Nm"); 129490957cf5Sschwarze 1295fe8e59edSschwarze switch (n->type) { 1296fe8e59edSschwarze case ROFFT_ELEM: 1297fe8e59edSschwarze post_delim_nb(mdoc); 1298fe8e59edSschwarze break; 1299fe8e59edSschwarze case ROFFT_HEAD: 130004fbb99fSschwarze post_delim(mdoc); 1301fe8e59edSschwarze break; 1302fe8e59edSschwarze default: 1303fe8e59edSschwarze return; 1304fe8e59edSschwarze } 130504fbb99fSschwarze 1306fe8e59edSschwarze if ((n->child != NULL && n->child->type == ROFFT_TEXT) || 130790957cf5Sschwarze mdoc->meta.name == NULL) 130890957cf5Sschwarze return; 130990957cf5Sschwarze 131090957cf5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 131190957cf5Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 131290957cf5Sschwarze mdoc->last->flags |= NODE_NOSRC; 131390957cf5Sschwarze mdoc->last = n; 131420fa2881Sschwarze } 131520fa2881Sschwarze 131698b8f00aSschwarze static void 1317753701eeSschwarze post_nd(POST_ARGS) 1318753701eeSschwarze { 13193a0d07afSschwarze struct roff_node *n; 1320753701eeSschwarze 13211570daf1Sschwarze n = mdoc->last; 13221570daf1Sschwarze 1323d1982c71Sschwarze if (n->type != ROFFT_BODY) 13241570daf1Sschwarze return; 13251570daf1Sschwarze 132656e9e976Sschwarze if (n->sec != SEC_NAME) 132756e9e976Sschwarze mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse, 132856e9e976Sschwarze n->line, n->pos, "Nd"); 132956e9e976Sschwarze 13301570daf1Sschwarze if (n->child == NULL) 13311570daf1Sschwarze mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, 13321570daf1Sschwarze n->line, n->pos, "Nd"); 1333fe8e59edSschwarze else 1334fe8e59edSschwarze post_delim(mdoc); 13351570daf1Sschwarze 133698b8f00aSschwarze post_hyph(mdoc); 1337753701eeSschwarze } 1338753701eeSschwarze 133998b8f00aSschwarze static void 13403e642ba0Sschwarze post_display(POST_ARGS) 1341753701eeSschwarze { 13423e642ba0Sschwarze struct roff_node *n, *np; 1343753701eeSschwarze 1344b7530f2fSschwarze n = mdoc->last; 13453e642ba0Sschwarze switch (n->type) { 13463e642ba0Sschwarze case ROFFT_BODY: 13474b5c4138Sschwarze if (n->end != ENDBODY_NOT) { 1348611d9138Sschwarze if (n->tok == MDOC_Bd && 1349611d9138Sschwarze n->body->parent->args == NULL) 13504b5c4138Sschwarze roff_node_delete(mdoc, n); 13514b5c4138Sschwarze } else if (n->child == NULL) 13521d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 135314a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 13543e642ba0Sschwarze else if (n->tok == MDOC_D1) 13553e642ba0Sschwarze post_hyph(mdoc); 13563e642ba0Sschwarze break; 13573e642ba0Sschwarze case ROFFT_BLOCK: 13583e642ba0Sschwarze if (n->tok == MDOC_Bd) { 1359a43e24e2Sschwarze if (n->args == NULL) { 1360a43e24e2Sschwarze mandoc_msg(MANDOCERR_BD_NOARG, 1361a43e24e2Sschwarze mdoc->parse, n->line, n->pos, "Bd"); 1362a43e24e2Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1363a43e24e2Sschwarze while (n->body->child != NULL) 1364a43e24e2Sschwarze mdoc_node_relink(mdoc, 1365a43e24e2Sschwarze n->body->child); 1366a43e24e2Sschwarze roff_node_delete(mdoc, n); 1367a43e24e2Sschwarze break; 1368a43e24e2Sschwarze } 13693e642ba0Sschwarze post_bd(mdoc); 13703e642ba0Sschwarze post_prevpar(mdoc); 13713e642ba0Sschwarze } 13723e642ba0Sschwarze for (np = n->parent; np != NULL; np = np->parent) { 13733e642ba0Sschwarze if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 13743e642ba0Sschwarze mandoc_vmsg(MANDOCERR_BD_NEST, 13753e642ba0Sschwarze mdoc->parse, n->line, n->pos, 137614a309e3Sschwarze "%s in Bd", roff_name[n->tok]); 13773e642ba0Sschwarze break; 13783e642ba0Sschwarze } 13793e642ba0Sschwarze } 13803e642ba0Sschwarze break; 13813e642ba0Sschwarze default: 13823e642ba0Sschwarze break; 13833e642ba0Sschwarze } 138420fa2881Sschwarze } 138520fa2881Sschwarze 138698b8f00aSschwarze static void 138720fa2881Sschwarze post_defaults(POST_ARGS) 138820fa2881Sschwarze { 13893a0d07afSschwarze struct roff_node *nn; 139020fa2881Sschwarze 139104fbb99fSschwarze if (mdoc->last->child != NULL) { 1392fe8e59edSschwarze post_delim_nb(mdoc); 139304fbb99fSschwarze return; 139404fbb99fSschwarze } 139504fbb99fSschwarze 139620fa2881Sschwarze /* 139720fa2881Sschwarze * The `Ar' defaults to "file ..." if no value is provided as an 139820fa2881Sschwarze * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 139920fa2881Sschwarze * gets an empty string. 140020fa2881Sschwarze */ 1401f73abda9Skristaps 140220fa2881Sschwarze nn = mdoc->last; 140320fa2881Sschwarze switch (nn->tok) { 140449aff9f8Sschwarze case MDOC_Ar: 1405396853b5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 140669c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "file"); 140743808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 140869c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "..."); 140943808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 141020fa2881Sschwarze break; 141149aff9f8Sschwarze case MDOC_Pa: 141249aff9f8Sschwarze case MDOC_Mt: 1413396853b5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 141469c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "~"); 141543808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 141620fa2881Sschwarze break; 141720fa2881Sschwarze default: 141820fa2881Sschwarze abort(); 1419f73abda9Skristaps } 142020fa2881Sschwarze mdoc->last = nn; 142120fa2881Sschwarze } 1422f73abda9Skristaps 142398b8f00aSschwarze static void 1424f73abda9Skristaps post_at(POST_ARGS) 1425f73abda9Skristaps { 14263af8e8d7Sschwarze struct roff_node *n, *nch; 14273af8e8d7Sschwarze const char *att; 142820fa2881Sschwarze 1429753701eeSschwarze n = mdoc->last; 14303af8e8d7Sschwarze nch = n->child; 1431753701eeSschwarze 143220fa2881Sschwarze /* 143320fa2881Sschwarze * If we have a child, look it up in the standard keys. If a 143420fa2881Sschwarze * key exist, use that instead of the child; if it doesn't, 143520fa2881Sschwarze * prefix "AT&T UNIX " to the existing data. 143620fa2881Sschwarze */ 1437f73abda9Skristaps 14383af8e8d7Sschwarze att = NULL; 14393af8e8d7Sschwarze if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1440bd594191Sschwarze mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, 14413af8e8d7Sschwarze nch->line, nch->pos, "At %s", nch->string); 1442f73abda9Skristaps 14433af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD; 14443af8e8d7Sschwarze if (att != NULL) { 14453af8e8d7Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, att); 14463af8e8d7Sschwarze nch->flags |= NODE_NOPRT; 14473af8e8d7Sschwarze } else 14483af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 14493af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 14503af8e8d7Sschwarze mdoc->last = n; 145120fa2881Sschwarze } 1452f73abda9Skristaps 145398b8f00aSschwarze static void 1454f73abda9Skristaps post_an(POST_ARGS) 1455f73abda9Skristaps { 14563a0d07afSschwarze struct roff_node *np, *nch; 1457f73abda9Skristaps 14583e642ba0Sschwarze post_an_norm(mdoc); 14593e642ba0Sschwarze 1460769ee804Sschwarze np = mdoc->last; 1461cba50636Sschwarze nch = np->child; 1462cba50636Sschwarze if (np->norm->An.auth == AUTH__NONE) { 1463cba50636Sschwarze if (nch == NULL) 1464cba50636Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 1465cba50636Sschwarze np->line, np->pos, "An"); 146604fbb99fSschwarze else 1467fe8e59edSschwarze post_delim_nb(mdoc); 1468cba50636Sschwarze } else if (nch != NULL) 14693798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 14703798fb25Sschwarze nch->line, nch->pos, "An ... %s", nch->string); 1471f73abda9Skristaps } 1472f73abda9Skristaps 147398b8f00aSschwarze static void 1474551cd4a8Sschwarze post_en(POST_ARGS) 1475551cd4a8Sschwarze { 1476551cd4a8Sschwarze 14773e642ba0Sschwarze post_obsolete(mdoc); 1478d1982c71Sschwarze if (mdoc->last->type == ROFFT_BLOCK) 1479551cd4a8Sschwarze mdoc->last->norm->Es = mdoc->last_es; 1480551cd4a8Sschwarze } 1481551cd4a8Sschwarze 148298b8f00aSschwarze static void 1483551cd4a8Sschwarze post_es(POST_ARGS) 1484551cd4a8Sschwarze { 1485551cd4a8Sschwarze 14863e642ba0Sschwarze post_obsolete(mdoc); 1487551cd4a8Sschwarze mdoc->last_es = mdoc->last; 1488551cd4a8Sschwarze } 1489551cd4a8Sschwarze 149098b8f00aSschwarze static void 1491816c3c54Sschwarze post_xx(POST_ARGS) 1492816c3c54Sschwarze { 1493816c3c54Sschwarze struct roff_node *n; 1494816c3c54Sschwarze const char *os; 149507ff2819Sschwarze char *v; 1496816c3c54Sschwarze 1497fe8e59edSschwarze post_delim_nb(mdoc); 149804fbb99fSschwarze 1499816c3c54Sschwarze n = mdoc->last; 1500816c3c54Sschwarze switch (n->tok) { 1501816c3c54Sschwarze case MDOC_Bsx: 1502816c3c54Sschwarze os = "BSD/OS"; 1503816c3c54Sschwarze break; 1504816c3c54Sschwarze case MDOC_Dx: 1505816c3c54Sschwarze os = "DragonFly"; 1506816c3c54Sschwarze break; 1507816c3c54Sschwarze case MDOC_Fx: 1508816c3c54Sschwarze os = "FreeBSD"; 1509816c3c54Sschwarze break; 1510816c3c54Sschwarze case MDOC_Nx: 1511816c3c54Sschwarze os = "NetBSD"; 151207ff2819Sschwarze if (n->child == NULL) 151307ff2819Sschwarze break; 151407ff2819Sschwarze v = n->child->string; 151507ff2819Sschwarze if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || 151607ff2819Sschwarze v[2] < '0' || v[2] > '9' || 151707ff2819Sschwarze v[3] < 'a' || v[3] > 'z' || v[4] != '\0') 151807ff2819Sschwarze break; 151907ff2819Sschwarze n->child->flags |= NODE_NOPRT; 152007ff2819Sschwarze mdoc->next = ROFF_NEXT_CHILD; 152107ff2819Sschwarze roff_word_alloc(mdoc, n->child->line, n->child->pos, v); 152207ff2819Sschwarze v = mdoc->last->string; 152307ff2819Sschwarze v[3] = toupper((unsigned char)v[3]); 152407ff2819Sschwarze mdoc->last->flags |= NODE_NOSRC; 152507ff2819Sschwarze mdoc->last = n; 1526816c3c54Sschwarze break; 1527816c3c54Sschwarze case MDOC_Ox: 1528816c3c54Sschwarze os = "OpenBSD"; 1529816c3c54Sschwarze break; 1530816c3c54Sschwarze case MDOC_Ux: 1531816c3c54Sschwarze os = "UNIX"; 1532816c3c54Sschwarze break; 1533816c3c54Sschwarze default: 1534816c3c54Sschwarze abort(); 1535816c3c54Sschwarze } 1536816c3c54Sschwarze mdoc->next = ROFF_NEXT_CHILD; 1537816c3c54Sschwarze roff_word_alloc(mdoc, n->line, n->pos, os); 1538816c3c54Sschwarze mdoc->last->flags |= NODE_NOSRC; 1539816c3c54Sschwarze mdoc->last = n; 1540816c3c54Sschwarze } 1541816c3c54Sschwarze 1542816c3c54Sschwarze static void 1543f73abda9Skristaps post_it(POST_ARGS) 1544f73abda9Skristaps { 15453a0d07afSschwarze struct roff_node *nbl, *nit, *nch; 154619a69263Sschwarze int i, cols; 15476093755cSschwarze enum mdoc_list lt; 1548f73abda9Skristaps 15493e642ba0Sschwarze post_prevpar(mdoc); 15503e642ba0Sschwarze 15519530682eSschwarze nit = mdoc->last; 1552d1982c71Sschwarze if (nit->type != ROFFT_BLOCK) 155398b8f00aSschwarze return; 1554f73abda9Skristaps 15559530682eSschwarze nbl = nit->parent->parent; 15569530682eSschwarze lt = nbl->norm->Bl.type; 15576093755cSschwarze 15586093755cSschwarze switch (lt) { 155949aff9f8Sschwarze case LIST_tag: 156049aff9f8Sschwarze case LIST_hang: 156149aff9f8Sschwarze case LIST_ohang: 156249aff9f8Sschwarze case LIST_inset: 156349aff9f8Sschwarze case LIST_diag: 1564d26e35c2Sschwarze if (nit->head->child == NULL) 1565bd594191Sschwarze mandoc_vmsg(MANDOCERR_IT_NOHEAD, 15669530682eSschwarze mdoc->parse, nit->line, nit->pos, 1567bd594191Sschwarze "Bl -%s It", 15689530682eSschwarze mdoc_argnames[nbl->args->argv[0].arg]); 1569f73abda9Skristaps break; 157049aff9f8Sschwarze case LIST_bullet: 157149aff9f8Sschwarze case LIST_dash: 157249aff9f8Sschwarze case LIST_enum: 157349aff9f8Sschwarze case LIST_hyphen: 1574d26e35c2Sschwarze if (nit->body == NULL || nit->body->child == NULL) 1575bd594191Sschwarze mandoc_vmsg(MANDOCERR_IT_NOBODY, 157666788495Sschwarze mdoc->parse, nit->line, nit->pos, 1577bd594191Sschwarze "Bl -%s It", 157866788495Sschwarze mdoc_argnames[nbl->args->argv[0].arg]); 1579f73abda9Skristaps /* FALLTHROUGH */ 158049aff9f8Sschwarze case LIST_item: 158125f8eeb1Sschwarze if ((nch = nit->head->child) != NULL) 158214a309e3Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, 158314a309e3Sschwarze nit->line, nit->pos, "It %s", 158414a309e3Sschwarze nch->string == NULL ? roff_name[nch->tok] : 158514a309e3Sschwarze nch->string); 1586f73abda9Skristaps break; 158749aff9f8Sschwarze case LIST_column: 15889530682eSschwarze cols = (int)nbl->norm->Bl.ncols; 15896093755cSschwarze 1590d26e35c2Sschwarze assert(nit->head->child == NULL); 15916093755cSschwarze 159280f58981Sschwarze if (nit->head->next->child == NULL && 159380f58981Sschwarze nit->head->next->next == NULL) { 159480f58981Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 159580f58981Sschwarze nit->line, nit->pos, "It"); 159680f58981Sschwarze roff_node_delete(mdoc, nit); 159780f58981Sschwarze break; 159880f58981Sschwarze } 159953292e81Sschwarze 160080f58981Sschwarze i = 0; 160180f58981Sschwarze for (nch = nit->child; nch != NULL; nch = nch->next) { 160280f58981Sschwarze if (nch->type != ROFFT_BODY) 160380f58981Sschwarze continue; 160480f58981Sschwarze if (i++ && nch->flags & NODE_LINE) 160580f58981Sschwarze mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse, 160680f58981Sschwarze nch->line, nch->pos, "Ta"); 160780f58981Sschwarze } 1608e14c4c11Sschwarze if (i < cols || i > cols + 1) 1609bce599dfSschwarze mandoc_vmsg(MANDOCERR_BL_COL, 1610e14c4c11Sschwarze mdoc->parse, nit->line, nit->pos, 1611bce599dfSschwarze "%d columns, %d cells", cols, i); 161280f58981Sschwarze else if (nit->head->next->child != NULL && 161380f58981Sschwarze nit->head->next->child->line > nit->line) 161480f58981Sschwarze mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse, 161580f58981Sschwarze nit->line, nit->pos, "Bl -column It"); 1616e14c4c11Sschwarze break; 1617f73abda9Skristaps default: 161866788495Sschwarze abort(); 1619f73abda9Skristaps } 1620f73abda9Skristaps } 1621f73abda9Skristaps 162298b8f00aSschwarze static void 162320fa2881Sschwarze post_bl_block(POST_ARGS) 162420fa2881Sschwarze { 16253a0d07afSschwarze struct roff_node *n, *ni, *nc; 162620fa2881Sschwarze 16273e642ba0Sschwarze post_prevpar(mdoc); 16283e642ba0Sschwarze 162920fa2881Sschwarze n = mdoc->last; 1630f051602aSschwarze for (ni = n->body->child; ni != NULL; ni = ni->next) { 1631f051602aSschwarze if (ni->body == NULL) 1632bb99f0faSschwarze continue; 1633bb99f0faSschwarze nc = ni->body->last; 1634f051602aSschwarze while (nc != NULL) { 1635bb99f0faSschwarze switch (nc->tok) { 163649aff9f8Sschwarze case MDOC_Pp: 163729478532Sschwarze case ROFF_br: 1638bb99f0faSschwarze break; 1639bb99f0faSschwarze default: 1640bb99f0faSschwarze nc = NULL; 1641bb99f0faSschwarze continue; 1642bb99f0faSschwarze } 1643f051602aSschwarze if (ni->next == NULL) { 164420369664Sschwarze mandoc_msg(MANDOCERR_PAR_MOVE, 164520369664Sschwarze mdoc->parse, nc->line, nc->pos, 164614a309e3Sschwarze roff_name[nc->tok]); 164798b8f00aSschwarze mdoc_node_relink(mdoc, nc); 1648f051602aSschwarze } else if (n->norm->Bl.comp == 0 && 1649f051602aSschwarze n->norm->Bl.type != LIST_column) { 165020369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 165120369664Sschwarze mdoc->parse, nc->line, nc->pos, 165214a309e3Sschwarze "%s before It", roff_name[nc->tok]); 1653fa2127f9Sschwarze roff_node_delete(mdoc, nc); 1654bb99f0faSschwarze } else 1655bb99f0faSschwarze break; 1656bb99f0faSschwarze nc = ni->body->last; 1657bb99f0faSschwarze } 1658bb99f0faSschwarze } 165920fa2881Sschwarze } 166020fa2881Sschwarze 166190d52a15Sschwarze /* 166290d52a15Sschwarze * If the argument of -offset or -width is a macro, 166390d52a15Sschwarze * replace it with the associated default width. 166490d52a15Sschwarze */ 16656050a3daSschwarze static void 16666050a3daSschwarze rewrite_macro2len(struct roff_man *mdoc, char **arg) 166720fa2881Sschwarze { 166820fa2881Sschwarze size_t width; 166914a309e3Sschwarze enum roff_tok tok; 167020fa2881Sschwarze 167190d52a15Sschwarze if (*arg == NULL) 167290d52a15Sschwarze return; 167390d52a15Sschwarze else if ( ! strcmp(*arg, "Ds")) 167420fa2881Sschwarze width = 6; 16756050a3daSschwarze else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) 167690d52a15Sschwarze return; 1677dc0d8bb2Sschwarze else 1678dc0d8bb2Sschwarze width = macro2len(tok); 167920fa2881Sschwarze 168090d52a15Sschwarze free(*arg); 168190d52a15Sschwarze mandoc_asprintf(arg, "%zun", width); 168220fa2881Sschwarze } 168320fa2881Sschwarze 168498b8f00aSschwarze static void 1685395185ccSschwarze post_bl_head(POST_ARGS) 1686395185ccSschwarze { 16873a0d07afSschwarze struct roff_node *nbl, *nh, *nch, *nnext; 1688f5174743Sschwarze struct mdoc_argv *argv; 168920fa2881Sschwarze int i, j; 1690395185ccSschwarze 16913e642ba0Sschwarze post_bl_norm(mdoc); 16922588c917Sschwarze 16933e642ba0Sschwarze nh = mdoc->last; 16942588c917Sschwarze if (nh->norm->Bl.type != LIST_column) { 16952588c917Sschwarze if ((nch = nh->child) == NULL) 16962588c917Sschwarze return; 16972588c917Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 16982588c917Sschwarze nch->line, nch->pos, "Bl ... %s", nch->string); 16992588c917Sschwarze while (nch != NULL) { 1700fa2127f9Sschwarze roff_node_delete(mdoc, nch); 17012588c917Sschwarze nch = nh->child; 17022588c917Sschwarze } 170398b8f00aSschwarze return; 170498b8f00aSschwarze } 1705395185ccSschwarze 170620fa2881Sschwarze /* 1707f5174743Sschwarze * Append old-style lists, where the column width specifiers 170820fa2881Sschwarze * trail as macro parameters, to the new-style ("normal-form") 170920fa2881Sschwarze * lists where they're argument values following -column. 171020fa2881Sschwarze */ 171120fa2881Sschwarze 17122588c917Sschwarze if (nh->child == NULL) 171398b8f00aSschwarze return; 171420fa2881Sschwarze 17152588c917Sschwarze nbl = nh->parent; 17162588c917Sschwarze for (j = 0; j < (int)nbl->args->argc; j++) 17172588c917Sschwarze if (nbl->args->argv[j].arg == MDOC_Column) 171820fa2881Sschwarze break; 171920fa2881Sschwarze 17202588c917Sschwarze assert(j < (int)nbl->args->argc); 172120fa2881Sschwarze 172220fa2881Sschwarze /* 1723a5e11edeSschwarze * Accommodate for new-style groff column syntax. Shuffle the 172420fa2881Sschwarze * child nodes, all of which must be TEXT, as arguments for the 172520fa2881Sschwarze * column field. Then, delete the head children. 172620fa2881Sschwarze */ 172720fa2881Sschwarze 17282588c917Sschwarze argv = nbl->args->argv + j; 1729f5174743Sschwarze i = argv->sz; 173030e5ee06Sschwarze for (nch = nh->child; nch != NULL; nch = nch->next) 173130e5ee06Sschwarze argv->sz++; 1732f5174743Sschwarze argv->value = mandoc_reallocarray(argv->value, 1733f5174743Sschwarze argv->sz, sizeof(char *)); 173420fa2881Sschwarze 17352588c917Sschwarze nh->norm->Bl.ncols = argv->sz; 17362588c917Sschwarze nh->norm->Bl.cols = (void *)argv->value; 173720fa2881Sschwarze 17382588c917Sschwarze for (nch = nh->child; nch != NULL; nch = nnext) { 17392588c917Sschwarze argv->value[i++] = nch->string; 17402588c917Sschwarze nch->string = NULL; 17412588c917Sschwarze nnext = nch->next; 1742fa2127f9Sschwarze roff_node_delete(NULL, nch); 1743395185ccSschwarze } 17442588c917Sschwarze nh->child = NULL; 1745b16e7ddfSschwarze } 1746b16e7ddfSschwarze 174798b8f00aSschwarze static void 1748f73abda9Skristaps post_bl(POST_ARGS) 1749f73abda9Skristaps { 17503a0d07afSschwarze struct roff_node *nparent, *nprev; /* of the Bl block */ 17513a0d07afSschwarze struct roff_node *nblock, *nbody; /* of the Bl */ 17523a0d07afSschwarze struct roff_node *nchild, *nnext; /* of the Bl body */ 1753ce0ef847Sschwarze const char *prev_Er; 1754ce0ef847Sschwarze int order; 1755f73abda9Skristaps 17562a427d60Sschwarze nbody = mdoc->last; 17572a427d60Sschwarze switch (nbody->type) { 1758d1982c71Sschwarze case ROFFT_BLOCK: 175998b8f00aSschwarze post_bl_block(mdoc); 176098b8f00aSschwarze return; 1761d1982c71Sschwarze case ROFFT_HEAD: 176298b8f00aSschwarze post_bl_head(mdoc); 176398b8f00aSschwarze return; 1764d1982c71Sschwarze case ROFFT_BODY: 1765f6127a73Sschwarze break; 17662a427d60Sschwarze default: 176798b8f00aSschwarze return; 1768f6127a73Sschwarze } 1769396853b5Sschwarze if (nbody->end != ENDBODY_NOT) 1770396853b5Sschwarze return; 1771f6127a73Sschwarze 17722a427d60Sschwarze nchild = nbody->child; 1773b7530f2fSschwarze if (nchild == NULL) { 17741d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 1775b7530f2fSschwarze nbody->line, nbody->pos, "Bl"); 1776b7530f2fSschwarze return; 1777b7530f2fSschwarze } 1778b7530f2fSschwarze while (nchild != NULL) { 1779ad7fa6e5Sschwarze nnext = nchild->next; 1780204684a7Sschwarze if (nchild->tok == MDOC_It || 1781204684a7Sschwarze (nchild->tok == MDOC_Sm && 1782ad7fa6e5Sschwarze nnext != NULL && nnext->tok == MDOC_It)) { 1783ad7fa6e5Sschwarze nchild = nnext; 1784ad7fa6e5Sschwarze continue; 1785ad7fa6e5Sschwarze } 1786ad7fa6e5Sschwarze 1787ad7fa6e5Sschwarze /* 1788ad7fa6e5Sschwarze * In .Bl -column, the first rows may be implicit, 1789ad7fa6e5Sschwarze * that is, they may not start with .It macros. 1790ad7fa6e5Sschwarze * Such rows may be followed by nodes generated on the 1791ad7fa6e5Sschwarze * roff level, for example .TS, which cannot be moved 1792ad7fa6e5Sschwarze * out of the list. In that case, wrap such roff nodes 1793ad7fa6e5Sschwarze * into an implicit row. 1794ad7fa6e5Sschwarze */ 1795ad7fa6e5Sschwarze 1796ad7fa6e5Sschwarze if (nchild->prev != NULL) { 1797ad7fa6e5Sschwarze mdoc->last = nchild; 1798ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1799ad7fa6e5Sschwarze roff_block_alloc(mdoc, nchild->line, 1800ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1801ad7fa6e5Sschwarze roff_head_alloc(mdoc, nchild->line, 1802ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1803ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1804ad7fa6e5Sschwarze roff_body_alloc(mdoc, nchild->line, 1805ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1806ad7fa6e5Sschwarze while (nchild->tok != MDOC_It) { 1807ad7fa6e5Sschwarze mdoc_node_relink(mdoc, nchild); 1808ad7fa6e5Sschwarze if ((nchild = nnext) == NULL) 1809ad7fa6e5Sschwarze break; 1810ad7fa6e5Sschwarze nnext = nchild->next; 1811ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1812ad7fa6e5Sschwarze } 1813ad7fa6e5Sschwarze mdoc->last = nbody; 18142a427d60Sschwarze continue; 18152a427d60Sschwarze } 18162a427d60Sschwarze 1817dd25b57cSschwarze mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, 181814a309e3Sschwarze nchild->line, nchild->pos, roff_name[nchild->tok]); 18192a427d60Sschwarze 18202a427d60Sschwarze /* 18212a427d60Sschwarze * Move the node out of the Bl block. 18222a427d60Sschwarze * First, collect all required node pointers. 18232a427d60Sschwarze */ 18242a427d60Sschwarze 18252a427d60Sschwarze nblock = nbody->parent; 18262a427d60Sschwarze nprev = nblock->prev; 18272a427d60Sschwarze nparent = nblock->parent; 18282a427d60Sschwarze 18292a427d60Sschwarze /* 18302a427d60Sschwarze * Unlink this child. 18312a427d60Sschwarze */ 18322a427d60Sschwarze 18332a427d60Sschwarze nbody->child = nnext; 183430e5ee06Sschwarze if (nnext == NULL) 183530e5ee06Sschwarze nbody->last = NULL; 183630e5ee06Sschwarze else 18372a427d60Sschwarze nnext->prev = NULL; 18382a427d60Sschwarze 18392a427d60Sschwarze /* 18402a427d60Sschwarze * Relink this child. 18412a427d60Sschwarze */ 18422a427d60Sschwarze 18432a427d60Sschwarze nchild->parent = nparent; 18442a427d60Sschwarze nchild->prev = nprev; 18452a427d60Sschwarze nchild->next = nblock; 18462a427d60Sschwarze 18472a427d60Sschwarze nblock->prev = nchild; 1848f051602aSschwarze if (nprev == NULL) 18492a427d60Sschwarze nparent->child = nchild; 18502a427d60Sschwarze else 18512a427d60Sschwarze nprev->next = nchild; 18522a427d60Sschwarze 18532a427d60Sschwarze nchild = nnext; 1854f73abda9Skristaps } 1855ce0ef847Sschwarze 1856f3476b07Sschwarze if (mdoc->meta.os_e != MANDOC_OS_NETBSD) 1857ce0ef847Sschwarze return; 1858ce0ef847Sschwarze 1859ce0ef847Sschwarze prev_Er = NULL; 1860ce0ef847Sschwarze for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { 1861ce0ef847Sschwarze if (nchild->tok != MDOC_It) 1862ce0ef847Sschwarze continue; 1863ce0ef847Sschwarze if ((nnext = nchild->head->child) == NULL) 1864ce0ef847Sschwarze continue; 1865ce0ef847Sschwarze if (nnext->type == ROFFT_BLOCK) 1866ce0ef847Sschwarze nnext = nnext->body->child; 1867ce0ef847Sschwarze if (nnext == NULL || nnext->tok != MDOC_Er) 1868ce0ef847Sschwarze continue; 1869ce0ef847Sschwarze nnext = nnext->child; 1870ce0ef847Sschwarze if (prev_Er != NULL) { 1871ce0ef847Sschwarze order = strcmp(prev_Er, nnext->string); 1872ce0ef847Sschwarze if (order > 0) 1873ce0ef847Sschwarze mandoc_vmsg(MANDOCERR_ER_ORDER, 1874ce0ef847Sschwarze mdoc->parse, nnext->line, nnext->pos, 1875f3476b07Sschwarze "Er %s %s (NetBSD)", 1876f3476b07Sschwarze prev_Er, nnext->string); 1877ce0ef847Sschwarze else if (order == 0) 1878ce0ef847Sschwarze mandoc_vmsg(MANDOCERR_ER_REP, 1879ce0ef847Sschwarze mdoc->parse, nnext->line, nnext->pos, 1880f3476b07Sschwarze "Er %s (NetBSD)", prev_Er); 1881ce0ef847Sschwarze } 1882ce0ef847Sschwarze prev_Er = nnext->string; 1883ce0ef847Sschwarze } 1884f73abda9Skristaps } 1885f73abda9Skristaps 188698b8f00aSschwarze static void 1887753701eeSschwarze post_bk(POST_ARGS) 1888753701eeSschwarze { 18893a0d07afSschwarze struct roff_node *n; 1890753701eeSschwarze 18912588c917Sschwarze n = mdoc->last; 18922588c917Sschwarze 1893d1982c71Sschwarze if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 18941d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, 18952588c917Sschwarze mdoc->parse, n->line, n->pos, "Bk"); 1896fa2127f9Sschwarze roff_node_delete(mdoc, n); 18972588c917Sschwarze } 1898753701eeSschwarze } 1899753701eeSschwarze 190098b8f00aSschwarze static void 1901f051602aSschwarze post_sm(POST_ARGS) 1902f73abda9Skristaps { 19033a0d07afSschwarze struct roff_node *nch; 1904f73abda9Skristaps 1905dc0d8bb2Sschwarze nch = mdoc->last->child; 1906dc0d8bb2Sschwarze 190778bbbab4Sschwarze if (nch == NULL) { 1908f9e7bf99Sschwarze mdoc->flags ^= MDOC_SMOFF; 190998b8f00aSschwarze return; 1910bb648afaSschwarze } 1911f9e7bf99Sschwarze 1912d1982c71Sschwarze assert(nch->type == ROFFT_TEXT); 191320fa2881Sschwarze 191478bbbab4Sschwarze if ( ! strcmp(nch->string, "on")) { 1915ec2beb53Sschwarze mdoc->flags &= ~MDOC_SMOFF; 191698b8f00aSschwarze return; 1917ec2beb53Sschwarze } 191878bbbab4Sschwarze if ( ! strcmp(nch->string, "off")) { 1919ec2beb53Sschwarze mdoc->flags |= MDOC_SMOFF; 192098b8f00aSschwarze return; 1921ec2beb53Sschwarze } 192220fa2881Sschwarze 1923dc0d8bb2Sschwarze mandoc_vmsg(MANDOCERR_SM_BAD, 1924dc0d8bb2Sschwarze mdoc->parse, nch->line, nch->pos, 192514a309e3Sschwarze "%s %s", roff_name[mdoc->last->tok], nch->string); 192698b8f00aSschwarze mdoc_node_relink(mdoc, nch); 192798b8f00aSschwarze return; 192820fa2881Sschwarze } 1929f73abda9Skristaps 193098b8f00aSschwarze static void 1931f73abda9Skristaps post_root(POST_ARGS) 1932f73abda9Skristaps { 19338fd2959dSschwarze const char *openbsd_arch[] = { 19348fd2959dSschwarze "alpha", "amd64", "arm64", "armv7", "hppa", "i386", 19358fd2959dSschwarze "landisk", "loongson", "luna88k", "macppc", "mips64", 19368fd2959dSschwarze "octeon", "sgi", "socppc", "sparc64", NULL 19378fd2959dSschwarze }; 19388fd2959dSschwarze const char *netbsd_arch[] = { 19398fd2959dSschwarze "acorn26", "acorn32", "algor", "alpha", "amiga", 19408fd2959dSschwarze "arc", "atari", 19418fd2959dSschwarze "bebox", "cats", "cesfic", "cobalt", "dreamcast", 19428fd2959dSschwarze "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5", 19438fd2959dSschwarze "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa", 19448fd2959dSschwarze "i386", "ibmnws", "luna68k", 19458fd2959dSschwarze "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc", 19468fd2959dSschwarze "netwinder", "news68k", "newsmips", "next68k", 19478fd2959dSschwarze "pc532", "playstation2", "pmax", "pmppc", "prep", 19488fd2959dSschwarze "sandpoint", "sbmips", "sgimips", "shark", 19498fd2959dSschwarze "sparc", "sparc64", "sun2", "sun3", 19508fd2959dSschwarze "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL 19518fd2959dSschwarze }; 19528fd2959dSschwarze const char **arches[] = { NULL, netbsd_arch, openbsd_arch }; 19538fd2959dSschwarze 19543a0d07afSschwarze struct roff_node *n; 19558fd2959dSschwarze const char **arch; 1956f73abda9Skristaps 1957ac1f49d0Sschwarze /* Add missing prologue data. */ 195820fa2881Sschwarze 1959ac1f49d0Sschwarze if (mdoc->meta.date == NULL) 19603427e516Sschwarze mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 19613427e516Sschwarze mandoc_normdate(mdoc, NULL, 0, 0); 19623fdead0cSschwarze 19633fdead0cSschwarze if (mdoc->meta.title == NULL) { 19643fdead0cSschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, 19653fdead0cSschwarze mdoc->parse, 0, 0, "EOF"); 19663fdead0cSschwarze mdoc->meta.title = mandoc_strdup("UNTITLED"); 19673fdead0cSschwarze } 19683fdead0cSschwarze 1969ac1f49d0Sschwarze if (mdoc->meta.vol == NULL) 1970ac1f49d0Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 19713fdead0cSschwarze 19723fdead0cSschwarze if (mdoc->meta.os == NULL) { 19733fdead0cSschwarze mandoc_msg(MANDOCERR_OS_MISSING, 19743fdead0cSschwarze mdoc->parse, 0, 0, NULL); 19753fdead0cSschwarze mdoc->meta.os = mandoc_strdup(""); 1976172864f7Sschwarze } else if (mdoc->meta.os_e && 1977172864f7Sschwarze (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) 1978f3476b07Sschwarze mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0, 1979f3476b07Sschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 1980f3476b07Sschwarze "(OpenBSD)" : "(NetBSD)"); 1981f73abda9Skristaps 19828fd2959dSschwarze if (mdoc->meta.arch != NULL && 19838fd2959dSschwarze (arch = arches[mdoc->meta.os_e]) != NULL) { 19848fd2959dSschwarze while (*arch != NULL && strcmp(*arch, mdoc->meta.arch)) 19858fd2959dSschwarze arch++; 19868fd2959dSschwarze if (*arch == NULL) { 19878fd2959dSschwarze n = mdoc->first->child; 19883f450c8cSschwarze while (n->tok != MDOC_Dt || 19893f450c8cSschwarze n->child == NULL || 19903f450c8cSschwarze n->child->next == NULL || 19913f450c8cSschwarze n->child->next->next == NULL) 19928fd2959dSschwarze n = n->next; 19938fd2959dSschwarze n = n->child->next->next; 19948fd2959dSschwarze mandoc_vmsg(MANDOCERR_ARCH_BAD, 19958fd2959dSschwarze mdoc->parse, n->line, n->pos, 19968fd2959dSschwarze "Dt ... %s %s", mdoc->meta.arch, 19978fd2959dSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 19988fd2959dSschwarze "(OpenBSD)" : "(NetBSD)"); 19998fd2959dSschwarze } 20008fd2959dSschwarze } 20018fd2959dSschwarze 200220fa2881Sschwarze /* Check that we begin with a proper `Sh'. */ 200320fa2881Sschwarze 2004e20417bdSschwarze n = mdoc->first->child; 20054c293873Sschwarze while (n != NULL && 20064c293873Sschwarze (n->type == ROFFT_COMMENT || 20074c293873Sschwarze (n->tok >= MDOC_Dd && 200816fe0cfcSschwarze mdoc_macro(n->tok)->flags & MDOC_PROLOGUE))) 2009e20417bdSschwarze n = n->next; 2010e20417bdSschwarze 2011e20417bdSschwarze if (n == NULL) 2012e20417bdSschwarze mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); 2013e20417bdSschwarze else if (n->tok != MDOC_Sh) 201451fcab2fSschwarze mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, 201514a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 201620fa2881Sschwarze } 2017f73abda9Skristaps 201898b8f00aSschwarze static void 2019011fe33bSschwarze post_rs(POST_ARGS) 2020011fe33bSschwarze { 20213a0d07afSschwarze struct roff_node *np, *nch, *next, *prev; 202220fa2881Sschwarze int i, j; 2023011fe33bSschwarze 20246e96429aSschwarze np = mdoc->last; 20256e96429aSschwarze 2026d1982c71Sschwarze if (np->type != ROFFT_BODY) 202798b8f00aSschwarze return; 20286e96429aSschwarze 20296e96429aSschwarze if (np->child == NULL) { 20306e96429aSschwarze mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse, 20316e96429aSschwarze np->line, np->pos, "Rs"); 203298b8f00aSschwarze return; 2033bb648afaSschwarze } 2034011fe33bSschwarze 203520fa2881Sschwarze /* 203620fa2881Sschwarze * The full `Rs' block needs special handling to order the 203720fa2881Sschwarze * sub-elements according to `rsord'. Pick through each element 2038b538baa5Sschwarze * and correctly order it. This is an insertion sort. 203920fa2881Sschwarze */ 204020fa2881Sschwarze 204120fa2881Sschwarze next = NULL; 20426e96429aSschwarze for (nch = np->child->next; nch != NULL; nch = next) { 20436e96429aSschwarze /* Determine order number of this child. */ 204420fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 20456e96429aSschwarze if (rsord[i] == nch->tok) 204620fa2881Sschwarze break; 204720fa2881Sschwarze 2048b538baa5Sschwarze if (i == RSORD_MAX) { 204914a309e3Sschwarze mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse, 205014a309e3Sschwarze nch->line, nch->pos, roff_name[nch->tok]); 2051b538baa5Sschwarze i = -1; 20526e96429aSschwarze } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 20536e96429aSschwarze np->norm->Rs.quote_T++; 2054b538baa5Sschwarze 205520fa2881Sschwarze /* 20566e96429aSschwarze * Remove this child from the chain. This somewhat 2057fa2127f9Sschwarze * repeats roff_node_unlink(), but since we're 205820fa2881Sschwarze * just re-ordering, there's no need for the 205920fa2881Sschwarze * full unlink process. 206020fa2881Sschwarze */ 206120fa2881Sschwarze 20626e96429aSschwarze if ((next = nch->next) != NULL) 20636e96429aSschwarze next->prev = nch->prev; 206420fa2881Sschwarze 20656e96429aSschwarze if ((prev = nch->prev) != NULL) 20666e96429aSschwarze prev->next = nch->next; 206720fa2881Sschwarze 20686e96429aSschwarze nch->prev = nch->next = NULL; 206920fa2881Sschwarze 207020fa2881Sschwarze /* 207120fa2881Sschwarze * Scan back until we reach a node that's 20726e96429aSschwarze * to be ordered before this child. 207320fa2881Sschwarze */ 207420fa2881Sschwarze 207520fa2881Sschwarze for ( ; prev ; prev = prev->prev) { 207620fa2881Sschwarze /* Determine order of `prev'. */ 207720fa2881Sschwarze for (j = 0; j < RSORD_MAX; j++) 207820fa2881Sschwarze if (rsord[j] == prev->tok) 207920fa2881Sschwarze break; 2080b538baa5Sschwarze if (j == RSORD_MAX) 2081b538baa5Sschwarze j = -1; 208220fa2881Sschwarze 208320fa2881Sschwarze if (j <= i) 208420fa2881Sschwarze break; 208520fa2881Sschwarze } 208620fa2881Sschwarze 208720fa2881Sschwarze /* 20886e96429aSschwarze * Set this child back into its correct place 20896e96429aSschwarze * in front of the `prev' node. 209020fa2881Sschwarze */ 209120fa2881Sschwarze 20926e96429aSschwarze nch->prev = prev; 209320fa2881Sschwarze 20946e96429aSschwarze if (prev == NULL) { 20956e96429aSschwarze np->child->prev = nch; 20966e96429aSschwarze nch->next = np->child; 20976e96429aSschwarze np->child = nch; 209820fa2881Sschwarze } else { 20996e96429aSschwarze if (prev->next) 21006e96429aSschwarze prev->next->prev = nch; 21016e96429aSschwarze nch->next = prev->next; 21026e96429aSschwarze prev->next = nch; 210320fa2881Sschwarze } 2104011fe33bSschwarze } 2105011fe33bSschwarze } 2106011fe33bSschwarze 21074039b21cSschwarze /* 21084039b21cSschwarze * For some arguments of some macros, 21094039b21cSschwarze * convert all breakable hyphens into ASCII_HYPH. 21104039b21cSschwarze */ 211198b8f00aSschwarze static void 21124039b21cSschwarze post_hyph(POST_ARGS) 21134039b21cSschwarze { 21143a0d07afSschwarze struct roff_node *nch; 21154039b21cSschwarze char *cp; 21164039b21cSschwarze 2117b7530f2fSschwarze for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { 2118d1982c71Sschwarze if (nch->type != ROFFT_TEXT) 21194039b21cSschwarze continue; 21204039b21cSschwarze cp = nch->string; 2121b7530f2fSschwarze if (*cp == '\0') 21224039b21cSschwarze continue; 2123b7530f2fSschwarze while (*(++cp) != '\0') 2124b7530f2fSschwarze if (*cp == '-' && 21254039b21cSschwarze isalpha((unsigned char)cp[-1]) && 21264039b21cSschwarze isalpha((unsigned char)cp[1])) 21274039b21cSschwarze *cp = ASCII_HYPH; 21284039b21cSschwarze } 21294039b21cSschwarze } 21304039b21cSschwarze 213198b8f00aSschwarze static void 2132af216717Sschwarze post_ns(POST_ARGS) 2133af216717Sschwarze { 2134676013e4Sschwarze struct roff_node *n; 2135af216717Sschwarze 2136676013e4Sschwarze n = mdoc->last; 2137676013e4Sschwarze if (n->flags & NODE_LINE || 2138676013e4Sschwarze (n->next != NULL && n->next->flags & NODE_DELIMC)) 213928153913Sschwarze mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, 2140676013e4Sschwarze n->line, n->pos, NULL); 2141af216717Sschwarze } 2142af216717Sschwarze 214398b8f00aSschwarze static void 2144fe8e59edSschwarze post_sx(POST_ARGS) 2145fe8e59edSschwarze { 2146fe8e59edSschwarze post_delim(mdoc); 2147fe8e59edSschwarze post_hyph(mdoc); 2148fe8e59edSschwarze } 2149fe8e59edSschwarze 2150fe8e59edSschwarze static void 2151f73abda9Skristaps post_sh(POST_ARGS) 2152f73abda9Skristaps { 2153f73abda9Skristaps 2154753701eeSschwarze post_ignpar(mdoc); 2155753701eeSschwarze 2156cd6c268fSschwarze switch (mdoc->last->type) { 2157d1982c71Sschwarze case ROFFT_HEAD: 215898b8f00aSschwarze post_sh_head(mdoc); 215998b8f00aSschwarze break; 2160d1982c71Sschwarze case ROFFT_BODY: 2161cd6c268fSschwarze switch (mdoc->lastsec) { 2162cd6c268fSschwarze case SEC_NAME: 216398b8f00aSschwarze post_sh_name(mdoc); 216498b8f00aSschwarze break; 21657c384856Sschwarze case SEC_SEE_ALSO: 216698b8f00aSschwarze post_sh_see_also(mdoc); 216798b8f00aSschwarze break; 2168cd6c268fSschwarze case SEC_AUTHORS: 216998b8f00aSschwarze post_sh_authors(mdoc); 217098b8f00aSschwarze break; 2171cd6c268fSschwarze default: 2172cd6c268fSschwarze break; 2173cd6c268fSschwarze } 2174cd6c268fSschwarze break; 2175cd6c268fSschwarze default: 2176cd6c268fSschwarze break; 2177cd6c268fSschwarze } 2178f73abda9Skristaps } 2179f73abda9Skristaps 218098b8f00aSschwarze static void 2181cd6c268fSschwarze post_sh_name(POST_ARGS) 2182f73abda9Skristaps { 21833a0d07afSschwarze struct roff_node *n; 218420e2cf25Sschwarze int hasnm, hasnd; 2185f73abda9Skristaps 218620e2cf25Sschwarze hasnm = hasnd = 0; 2187f73abda9Skristaps 218820e2cf25Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) { 218920e2cf25Sschwarze switch (n->tok) { 219020e2cf25Sschwarze case MDOC_Nm: 2191f27faaccSschwarze if (hasnm && n->child != NULL) 2192f27faaccSschwarze mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT, 2193f27faaccSschwarze mdoc->parse, n->line, n->pos, 2194f27faaccSschwarze "Nm %s", n->child->string); 219520e2cf25Sschwarze hasnm = 1; 2196f27faaccSschwarze continue; 219720e2cf25Sschwarze case MDOC_Nd: 219820e2cf25Sschwarze hasnd = 1; 219920e2cf25Sschwarze if (n->next != NULL) 220020e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_ND, 220120e2cf25Sschwarze mdoc->parse, n->line, n->pos, NULL); 220220e2cf25Sschwarze break; 22032d6f95d3Sschwarze case TOKEN_NONE: 2204f27faaccSschwarze if (n->type == ROFFT_TEXT && 2205f27faaccSschwarze n->string[0] == ',' && n->string[1] == '\0' && 2206f27faaccSschwarze n->next != NULL && n->next->tok == MDOC_Nm) { 2207f27faaccSschwarze n = n->next; 2208f27faaccSschwarze continue; 2209f27faaccSschwarze } 2210fa072f7fSschwarze /* FALLTHROUGH */ 221120e2cf25Sschwarze default: 221251fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 221314a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 2214f27faaccSschwarze continue; 221520e2cf25Sschwarze } 2216f27faaccSschwarze break; 2217f73abda9Skristaps } 2218f73abda9Skristaps 221920e2cf25Sschwarze if ( ! hasnm) 222020e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, 222120e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL); 222220e2cf25Sschwarze if ( ! hasnd) 222320e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, 222420e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL); 222520fa2881Sschwarze } 2226f73abda9Skristaps 222798b8f00aSschwarze static void 22287c384856Sschwarze post_sh_see_also(POST_ARGS) 22297c384856Sschwarze { 22303a0d07afSschwarze const struct roff_node *n; 22317c384856Sschwarze const char *name, *sec; 22327c384856Sschwarze const char *lastname, *lastsec, *lastpunct; 22337c384856Sschwarze int cmp; 22347c384856Sschwarze 22357c384856Sschwarze n = mdoc->last->child; 22367c384856Sschwarze lastname = lastsec = lastpunct = NULL; 22377c384856Sschwarze while (n != NULL) { 223830e5ee06Sschwarze if (n->tok != MDOC_Xr || 223930e5ee06Sschwarze n->child == NULL || 224030e5ee06Sschwarze n->child->next == NULL) 22417c384856Sschwarze break; 22427c384856Sschwarze 22437c384856Sschwarze /* Process one .Xr node. */ 22447c384856Sschwarze 22457c384856Sschwarze name = n->child->string; 22467c384856Sschwarze sec = n->child->next->string; 22477c384856Sschwarze if (lastsec != NULL) { 22487c384856Sschwarze if (lastpunct[0] != ',' || lastpunct[1] != '\0') 22497c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_PUNCT, 22507c384856Sschwarze mdoc->parse, n->line, n->pos, 22517c384856Sschwarze "%s before %s(%s)", lastpunct, 22527c384856Sschwarze name, sec); 22537c384856Sschwarze cmp = strcmp(lastsec, sec); 22547c384856Sschwarze if (cmp > 0) 22557c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_ORDER, 22567c384856Sschwarze mdoc->parse, n->line, n->pos, 22577c384856Sschwarze "%s(%s) after %s(%s)", name, 22587c384856Sschwarze sec, lastname, lastsec); 22597c384856Sschwarze else if (cmp == 0 && 22607c384856Sschwarze strcasecmp(lastname, name) > 0) 22617c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_ORDER, 22627c384856Sschwarze mdoc->parse, n->line, n->pos, 22637c384856Sschwarze "%s after %s", name, lastname); 22647c384856Sschwarze } 22657c384856Sschwarze lastname = name; 22667c384856Sschwarze lastsec = sec; 22677c384856Sschwarze 22687c384856Sschwarze /* Process the following node. */ 22697c384856Sschwarze 22707c384856Sschwarze n = n->next; 22717c384856Sschwarze if (n == NULL) 22727c384856Sschwarze break; 22737c384856Sschwarze if (n->tok == MDOC_Xr) { 22747c384856Sschwarze lastpunct = "none"; 22757c384856Sschwarze continue; 22767c384856Sschwarze } 2277d1982c71Sschwarze if (n->type != ROFFT_TEXT) 22787c384856Sschwarze break; 22797c384856Sschwarze for (name = n->string; *name != '\0'; name++) 22807c384856Sschwarze if (isalpha((const unsigned char)*name)) 228198b8f00aSschwarze return; 22827c384856Sschwarze lastpunct = n->string; 2283c7098240Sschwarze if (n->next == NULL || n->next->tok == MDOC_Rs) 22847c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, 22857c384856Sschwarze n->line, n->pos, "%s after %s(%s)", 22867c384856Sschwarze lastpunct, lastname, lastsec); 22877c384856Sschwarze n = n->next; 22887c384856Sschwarze } 22897c384856Sschwarze } 22907c384856Sschwarze 22917c384856Sschwarze static int 22923a0d07afSschwarze child_an(const struct roff_node *n) 2293cd6c268fSschwarze { 2294cd6c268fSschwarze 2295cd6c268fSschwarze for (n = n->child; n != NULL; n = n->next) 229630e5ee06Sschwarze if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 2297526e306bSschwarze return 1; 2298526e306bSschwarze return 0; 2299cd6c268fSschwarze } 2300cd6c268fSschwarze 230198b8f00aSschwarze static void 2302cd6c268fSschwarze post_sh_authors(POST_ARGS) 2303cd6c268fSschwarze { 2304cd6c268fSschwarze 2305cd6c268fSschwarze if ( ! child_an(mdoc->last)) 2306cd6c268fSschwarze mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse, 2307cd6c268fSschwarze mdoc->last->line, mdoc->last->pos, NULL); 2308cd6c268fSschwarze } 2309cd6c268fSschwarze 23104482121fSschwarze /* 23114482121fSschwarze * Return an upper bound for the string distance (allowing 23124482121fSschwarze * transpositions). Not a full Levenshtein implementation 23134482121fSschwarze * because Levenshtein is quadratic in the string length 23144482121fSschwarze * and this function is called for every standard name, 23154482121fSschwarze * so the check for each custom name would be cubic. 23164482121fSschwarze * The following crude heuristics is linear, resulting 23174482121fSschwarze * in quadratic behaviour for checking one custom name, 23184482121fSschwarze * which does not cause measurable slowdown. 23194482121fSschwarze */ 23204482121fSschwarze static int 23214482121fSschwarze similar(const char *s1, const char *s2) 23224482121fSschwarze { 23234482121fSschwarze const int maxdist = 3; 23244482121fSschwarze int dist = 0; 23254482121fSschwarze 23264482121fSschwarze while (s1[0] != '\0' && s2[0] != '\0') { 23274482121fSschwarze if (s1[0] == s2[0]) { 23284482121fSschwarze s1++; 23294482121fSschwarze s2++; 23304482121fSschwarze continue; 23314482121fSschwarze } 23324482121fSschwarze if (++dist > maxdist) 23334482121fSschwarze return INT_MAX; 23344482121fSschwarze if (s1[1] == s2[1]) { /* replacement */ 23354482121fSschwarze s1++; 23364482121fSschwarze s2++; 23374482121fSschwarze } else if (s1[0] == s2[1] && s1[1] == s2[0]) { 23384482121fSschwarze s1 += 2; /* transposition */ 23394482121fSschwarze s2 += 2; 23404482121fSschwarze } else if (s1[0] == s2[1]) /* insertion */ 23414482121fSschwarze s2++; 23424482121fSschwarze else if (s1[1] == s2[0]) /* deletion */ 23434482121fSschwarze s1++; 23444482121fSschwarze else 23454482121fSschwarze return INT_MAX; 23464482121fSschwarze } 23474482121fSschwarze dist += strlen(s1) + strlen(s2); 23484482121fSschwarze return dist > maxdist ? INT_MAX : dist; 23494482121fSschwarze } 23504482121fSschwarze 235198b8f00aSschwarze static void 2352f73abda9Skristaps post_sh_head(POST_ARGS) 2353f73abda9Skristaps { 2354cf0e2075Sschwarze struct roff_node *nch; 235551fcab2fSschwarze const char *goodsec; 23564482121fSschwarze const char *const *testsec; 23574482121fSschwarze int dist, mindist; 23583a0d07afSschwarze enum roff_sec sec; 2359f73abda9Skristaps 2360f73abda9Skristaps /* 2361f73abda9Skristaps * Process a new section. Sections are either "named" or 236220fa2881Sschwarze * "custom". Custom sections are user-defined, while named ones 236320fa2881Sschwarze * follow a conventional order and may only appear in certain 236420fa2881Sschwarze * manual sections. 2365f73abda9Skristaps */ 2366f73abda9Skristaps 2367396853b5Sschwarze sec = mdoc->last->sec; 2368f73abda9Skristaps 236920fa2881Sschwarze /* The NAME should be first. */ 2370f73abda9Skristaps 2371d37754b9Sschwarze if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 2372bd594191Sschwarze mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, 2373d37754b9Sschwarze mdoc->last->line, mdoc->last->pos, "Sh %s", 2374cf0e2075Sschwarze sec != SEC_CUSTOM ? secnames[sec] : 2375cf0e2075Sschwarze (nch = mdoc->last->child) == NULL ? "" : 2376cf0e2075Sschwarze nch->type == ROFFT_TEXT ? nch->string : 237714a309e3Sschwarze roff_name[nch->tok]); 237820fa2881Sschwarze 237920fa2881Sschwarze /* The SYNOPSIS gets special attention in other areas. */ 238020fa2881Sschwarze 2381396853b5Sschwarze if (sec == SEC_SYNOPSIS) { 238275088a49Sschwarze roff_setreg(mdoc->roff, "nS", 1, '='); 238320fa2881Sschwarze mdoc->flags |= MDOC_SYNOPSIS; 238422881299Sschwarze } else { 238575088a49Sschwarze roff_setreg(mdoc->roff, "nS", 0, '='); 238620fa2881Sschwarze mdoc->flags &= ~MDOC_SYNOPSIS; 238722881299Sschwarze } 238820fa2881Sschwarze 238920fa2881Sschwarze /* Mark our last section. */ 239020fa2881Sschwarze 239120fa2881Sschwarze mdoc->lastsec = sec; 23921eccdf28Sschwarze 239320fa2881Sschwarze /* We don't care about custom sections after this. */ 2394fccfce9dSschwarze 23954482121fSschwarze if (sec == SEC_CUSTOM) { 23964482121fSschwarze if ((nch = mdoc->last->child) == NULL || 23974482121fSschwarze nch->type != ROFFT_TEXT || nch->next != NULL) 239898b8f00aSschwarze return; 23994482121fSschwarze goodsec = NULL; 24004482121fSschwarze mindist = INT_MAX; 24014482121fSschwarze for (testsec = secnames + 1; *testsec != NULL; testsec++) { 24024482121fSschwarze dist = similar(nch->string, *testsec); 24034482121fSschwarze if (dist < mindist) { 24044482121fSschwarze goodsec = *testsec; 24054482121fSschwarze mindist = dist; 24064482121fSschwarze } 24074482121fSschwarze } 24084482121fSschwarze if (goodsec != NULL) 24094482121fSschwarze mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse, 24104482121fSschwarze nch->line, nch->pos, "Sh %s instead of %s", 24114482121fSschwarze nch->string, goodsec); 24124482121fSschwarze return; 24134482121fSschwarze } 2414fccfce9dSschwarze 24156be99f77Sschwarze /* 241620fa2881Sschwarze * Check whether our non-custom section is being repeated or is 241720fa2881Sschwarze * out of order. 24186be99f77Sschwarze */ 2419f73abda9Skristaps 242020fa2881Sschwarze if (sec == mdoc->lastnamed) 2421bd594191Sschwarze mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, 2422bd594191Sschwarze mdoc->last->line, mdoc->last->pos, 2423396853b5Sschwarze "Sh %s", secnames[sec]); 242420fa2881Sschwarze 242520fa2881Sschwarze if (sec < mdoc->lastnamed) 2426bd594191Sschwarze mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, 2427bd594191Sschwarze mdoc->last->line, mdoc->last->pos, 2428396853b5Sschwarze "Sh %s", secnames[sec]); 242920fa2881Sschwarze 243020fa2881Sschwarze /* Mark the last named section. */ 243120fa2881Sschwarze 243220fa2881Sschwarze mdoc->lastnamed = sec; 243320fa2881Sschwarze 243420fa2881Sschwarze /* Check particular section/manual conventions. */ 243520fa2881Sschwarze 2436396853b5Sschwarze if (mdoc->meta.msec == NULL) 243798b8f00aSschwarze return; 243820fa2881Sschwarze 243951fcab2fSschwarze goodsec = NULL; 244020fa2881Sschwarze switch (sec) { 244149aff9f8Sschwarze case SEC_ERRORS: 2442be89e780Sschwarze if (*mdoc->meta.msec == '4') 2443be89e780Sschwarze break; 244451fcab2fSschwarze goodsec = "2, 3, 4, 9"; 2445be89e780Sschwarze /* FALLTHROUGH */ 244649aff9f8Sschwarze case SEC_RETURN_VALUES: 244749aff9f8Sschwarze case SEC_LIBRARY: 244892c0ca7fSschwarze if (*mdoc->meta.msec == '2') 2449f73abda9Skristaps break; 245092c0ca7fSschwarze if (*mdoc->meta.msec == '3') 245192c0ca7fSschwarze break; 245251fcab2fSschwarze if (NULL == goodsec) 245351fcab2fSschwarze goodsec = "2, 3, 9"; 245403ab2f23Sdlg /* FALLTHROUGH */ 245549aff9f8Sschwarze case SEC_CONTEXT: 245692c0ca7fSschwarze if (*mdoc->meta.msec == '9') 245792c0ca7fSschwarze break; 245851fcab2fSschwarze if (NULL == goodsec) 245951fcab2fSschwarze goodsec = "9"; 246051fcab2fSschwarze mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, 246151fcab2fSschwarze mdoc->last->line, mdoc->last->pos, 2462396853b5Sschwarze "Sh %s for %s only", secnames[sec], goodsec); 246320fa2881Sschwarze break; 2464f73abda9Skristaps default: 2465f73abda9Skristaps break; 2466f73abda9Skristaps } 2467f73abda9Skristaps } 2468d39b9a9cSschwarze 246998b8f00aSschwarze static void 24705ae08040Sschwarze post_xr(POST_ARGS) 24715ae08040Sschwarze { 24725ae08040Sschwarze struct roff_node *n, *nch; 24735ae08040Sschwarze 24745ae08040Sschwarze n = mdoc->last; 24755ae08040Sschwarze nch = n->child; 24765ae08040Sschwarze if (nch->next == NULL) { 24775ae08040Sschwarze mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, 24785ae08040Sschwarze n->line, n->pos, "Xr %s", nch->string); 247919b6bef7Sschwarze } else { 24805ae08040Sschwarze assert(nch->next == n->last); 248152d11c96Sschwarze if(mandoc_xr_add(nch->next->string, nch->string, 248252d11c96Sschwarze nch->line, nch->pos)) 248352d11c96Sschwarze mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse, 248452d11c96Sschwarze nch->line, nch->pos, "Xr %s %s", 248552d11c96Sschwarze nch->string, nch->next->string); 248619b6bef7Sschwarze } 2487fe8e59edSschwarze post_delim_nb(mdoc); 24885ae08040Sschwarze } 24895ae08040Sschwarze 24905ae08040Sschwarze static void 2491f6127a73Sschwarze post_ignpar(POST_ARGS) 2492f6127a73Sschwarze { 24933a0d07afSschwarze struct roff_node *np; 2494f6127a73Sschwarze 2495b7530f2fSschwarze switch (mdoc->last->type) { 24969c7c6a1fSschwarze case ROFFT_BLOCK: 24979c7c6a1fSschwarze post_prevpar(mdoc); 24989c7c6a1fSschwarze return; 2499d1982c71Sschwarze case ROFFT_HEAD: 2500fe8e59edSschwarze post_delim(mdoc); 2501753701eeSschwarze post_hyph(mdoc); 250298b8f00aSschwarze return; 2503d1982c71Sschwarze case ROFFT_BODY: 2504b7530f2fSschwarze break; 2505b7530f2fSschwarze default: 2506b7530f2fSschwarze return; 2507b7530f2fSschwarze } 2508f6127a73Sschwarze 2509f051602aSschwarze if ((np = mdoc->last->child) != NULL) 2510*7c539ecbSschwarze if (np->tok == MDOC_Pp) { 251120369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 251220369664Sschwarze mdoc->parse, np->line, np->pos, 251314a309e3Sschwarze "%s after %s", roff_name[np->tok], 251414a309e3Sschwarze roff_name[mdoc->last->tok]); 2515fa2127f9Sschwarze roff_node_delete(mdoc, np); 2516f6127a73Sschwarze } 2517f6127a73Sschwarze 2518f051602aSschwarze if ((np = mdoc->last->last) != NULL) 2519*7c539ecbSschwarze if (np->tok == MDOC_Pp) { 252020369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 252120369664Sschwarze np->line, np->pos, "%s at the end of %s", 252214a309e3Sschwarze roff_name[np->tok], 252314a309e3Sschwarze roff_name[mdoc->last->tok]); 2524fa2127f9Sschwarze roff_node_delete(mdoc, np); 2525f6127a73Sschwarze } 2526f6127a73Sschwarze } 2527f6127a73Sschwarze 252898b8f00aSschwarze static void 25293e642ba0Sschwarze post_prevpar(POST_ARGS) 2530d39b9a9cSschwarze { 25313e642ba0Sschwarze struct roff_node *n; 2532d39b9a9cSschwarze 25333e642ba0Sschwarze n = mdoc->last; 25343e642ba0Sschwarze if (NULL == n->prev) 253598b8f00aSschwarze return; 2536d1982c71Sschwarze if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 253798b8f00aSschwarze return; 2538d39b9a9cSschwarze 253920fa2881Sschwarze /* 2540*7c539ecbSschwarze * Don't allow `Pp' prior to a paragraph-type 2541*7c539ecbSschwarze * block: `Pp' or non-compact `Bd' or `Bl'. 254220fa2881Sschwarze */ 2543d39b9a9cSschwarze 2544*7c539ecbSschwarze if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br) 254598b8f00aSschwarze return; 25463e642ba0Sschwarze if (n->tok == MDOC_Bl && n->norm->Bl.comp) 254798b8f00aSschwarze return; 25483e642ba0Sschwarze if (n->tok == MDOC_Bd && n->norm->Bd.comp) 254998b8f00aSschwarze return; 25503e642ba0Sschwarze if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 255198b8f00aSschwarze return; 2552d39b9a9cSschwarze 255320369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 255414a309e3Sschwarze n->prev->line, n->prev->pos, "%s before %s", 255514a309e3Sschwarze roff_name[n->prev->tok], roff_name[n->tok]); 25563e642ba0Sschwarze roff_node_delete(mdoc, n->prev); 2557d39b9a9cSschwarze } 255820fa2881Sschwarze 255998b8f00aSschwarze static void 2560e0dd4c9cSschwarze post_par(POST_ARGS) 2561e0dd4c9cSschwarze { 25623a0d07afSschwarze struct roff_node *np; 2563e0dd4c9cSschwarze 25643798fb25Sschwarze np = mdoc->last; 25656561cb23Sschwarze if (np->tok != ROFF_br && np->tok != ROFF_sp) 25663e642ba0Sschwarze post_prevpar(mdoc); 2567753701eeSschwarze 25686561cb23Sschwarze if (np->tok == ROFF_sp) { 256930e5ee06Sschwarze if (np->child != NULL && np->child->next != NULL) 25703798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 25713798fb25Sschwarze np->child->next->line, np->child->next->pos, 25723798fb25Sschwarze "sp ... %s", np->child->next->string); 25733798fb25Sschwarze } else if (np->child != NULL) 25743798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, 25753798fb25Sschwarze mdoc->parse, np->line, np->pos, "%s %s", 257614a309e3Sschwarze roff_name[np->tok], np->child->string); 2577e0dd4c9cSschwarze 2578f051602aSschwarze if ((np = mdoc->last->prev) == NULL) { 257920369664Sschwarze np = mdoc->last->parent; 2580f051602aSschwarze if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) 258198b8f00aSschwarze return; 2582*7c539ecbSschwarze } else if (np->tok != MDOC_Pp && 258329478532Sschwarze (mdoc->last->tok != ROFF_br || 25846561cb23Sschwarze (np->tok != ROFF_sp && np->tok != ROFF_br))) 258598b8f00aSschwarze return; 2586e0dd4c9cSschwarze 258720369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 258814a309e3Sschwarze mdoc->last->line, mdoc->last->pos, "%s after %s", 258914a309e3Sschwarze roff_name[mdoc->last->tok], roff_name[np->tok]); 2590fa2127f9Sschwarze roff_node_delete(mdoc, mdoc->last); 2591e0dd4c9cSschwarze } 2592e0dd4c9cSschwarze 259398b8f00aSschwarze static void 259420fa2881Sschwarze post_dd(POST_ARGS) 259520fa2881Sschwarze { 25963a0d07afSschwarze struct roff_node *n; 259783af2bccSschwarze char *datestr; 259820fa2881Sschwarze 2599b058e777Sschwarze n = mdoc->last; 260043808411Sschwarze n->flags |= NODE_NOPRT; 260143808411Sschwarze 2602396853b5Sschwarze if (mdoc->meta.date != NULL) { 2603396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2604396853b5Sschwarze n->line, n->pos, "Dd"); 2605396853b5Sschwarze free(mdoc->meta.date); 2606396853b5Sschwarze } else if (mdoc->flags & MDOC_PBODY) 2607396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2608396853b5Sschwarze n->line, n->pos, "Dd"); 2609396853b5Sschwarze else if (mdoc->meta.title != NULL) 2610396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2611396853b5Sschwarze n->line, n->pos, "Dd after Dt"); 2612396853b5Sschwarze else if (mdoc->meta.os != NULL) 2613396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2614396853b5Sschwarze n->line, n->pos, "Dd after Os"); 2615396853b5Sschwarze 2616f051602aSschwarze if (n->child == NULL || n->child->string[0] == '\0') { 2617231c7061Sschwarze mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 26183427e516Sschwarze mandoc_normdate(mdoc, NULL, n->line, n->pos); 261943808411Sschwarze return; 262020fa2881Sschwarze } 262120fa2881Sschwarze 262283af2bccSschwarze datestr = NULL; 2623423631c9Sschwarze deroff(&datestr, n); 262483af2bccSschwarze if (mdoc->quick) 262583af2bccSschwarze mdoc->meta.date = datestr; 262683af2bccSschwarze else { 26273427e516Sschwarze mdoc->meta.date = mandoc_normdate(mdoc, 262883af2bccSschwarze datestr, n->line, n->pos); 262983af2bccSschwarze free(datestr); 263004e980cbSschwarze } 263120fa2881Sschwarze } 263220fa2881Sschwarze 263398b8f00aSschwarze static void 263420fa2881Sschwarze post_dt(POST_ARGS) 263520fa2881Sschwarze { 26363a0d07afSschwarze struct roff_node *nn, *n; 263720fa2881Sschwarze const char *cp; 263820fa2881Sschwarze char *p; 263920fa2881Sschwarze 264020fa2881Sschwarze n = mdoc->last; 264143808411Sschwarze n->flags |= NODE_NOPRT; 264243808411Sschwarze 2643396853b5Sschwarze if (mdoc->flags & MDOC_PBODY) { 2644396853b5Sschwarze mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, 2645396853b5Sschwarze n->line, n->pos, "Dt"); 264643808411Sschwarze return; 2647396853b5Sschwarze } 2648396853b5Sschwarze 2649396853b5Sschwarze if (mdoc->meta.title != NULL) 2650396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2651396853b5Sschwarze n->line, n->pos, "Dt"); 2652396853b5Sschwarze else if (mdoc->meta.os != NULL) 2653396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2654396853b5Sschwarze n->line, n->pos, "Dt after Os"); 265520fa2881Sschwarze 265620fa2881Sschwarze free(mdoc->meta.title); 26573fdead0cSschwarze free(mdoc->meta.msec); 265820fa2881Sschwarze free(mdoc->meta.vol); 265920fa2881Sschwarze free(mdoc->meta.arch); 266020fa2881Sschwarze 26613fdead0cSschwarze mdoc->meta.title = NULL; 26623fdead0cSschwarze mdoc->meta.msec = NULL; 26633fdead0cSschwarze mdoc->meta.vol = NULL; 26643fdead0cSschwarze mdoc->meta.arch = NULL; 266520fa2881Sschwarze 26669a928a59Sschwarze /* Mandatory first argument: title. */ 266720fa2881Sschwarze 26689a928a59Sschwarze nn = n->child; 26699a928a59Sschwarze if (nn == NULL || *nn->string == '\0') { 26709a928a59Sschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, 26719a928a59Sschwarze mdoc->parse, n->line, n->pos, "Dt"); 26729a928a59Sschwarze mdoc->meta.title = mandoc_strdup("UNTITLED"); 26739a928a59Sschwarze } else { 26749a928a59Sschwarze mdoc->meta.title = mandoc_strdup(nn->string); 26759a928a59Sschwarze 26769a928a59Sschwarze /* Check that all characters are uppercase. */ 26779a928a59Sschwarze 26789a928a59Sschwarze for (p = nn->string; *p != '\0'; p++) 26799a928a59Sschwarze if (islower((unsigned char)*p)) { 2680bd594191Sschwarze mandoc_vmsg(MANDOCERR_TITLE_CASE, 268151fcab2fSschwarze mdoc->parse, nn->line, 268251fcab2fSschwarze nn->pos + (p - nn->string), 2683bd594191Sschwarze "Dt %s", nn->string); 268420fa2881Sschwarze break; 268520fa2881Sschwarze } 268620fa2881Sschwarze } 268720fa2881Sschwarze 26885ae08040Sschwarze /* Mandatory second argument: section. */ 268920fa2881Sschwarze 26909a928a59Sschwarze if (nn != NULL) 26919a928a59Sschwarze nn = nn->next; 269220fa2881Sschwarze 26939a928a59Sschwarze if (nn == NULL) { 26943fdead0cSschwarze mandoc_vmsg(MANDOCERR_MSEC_MISSING, 26953fdead0cSschwarze mdoc->parse, n->line, n->pos, 26963fdead0cSschwarze "Dt %s", mdoc->meta.title); 269720fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 269843808411Sschwarze return; /* msec and arch remain NULL. */ 269920fa2881Sschwarze } 270020fa2881Sschwarze 27019a928a59Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 27029a928a59Sschwarze 27039a928a59Sschwarze /* Infer volume title from section number. */ 270420fa2881Sschwarze 270588ec69e3Sschwarze cp = mandoc_a2msec(nn->string); 27069a928a59Sschwarze if (cp == NULL) { 2707bd594191Sschwarze mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, 2708bd594191Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string); 270920fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 27109a928a59Sschwarze } else 27119a928a59Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 271220fa2881Sschwarze 27139a928a59Sschwarze /* Optional third argument: architecture. */ 271420fa2881Sschwarze 27159a928a59Sschwarze if ((nn = nn->next) == NULL) 271643808411Sschwarze return; 27179a928a59Sschwarze 27189a928a59Sschwarze for (p = nn->string; *p != '\0'; p++) 2719b94f27c5Sschwarze *p = tolower((unsigned char)*p); 2720b94f27c5Sschwarze mdoc->meta.arch = mandoc_strdup(nn->string); 272120fa2881Sschwarze 27229a928a59Sschwarze /* Ignore fourth and later arguments. */ 27239a928a59Sschwarze 27249a928a59Sschwarze if ((nn = nn->next) != NULL) 27259a928a59Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 27269a928a59Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string); 272720fa2881Sschwarze } 272820fa2881Sschwarze 272998b8f00aSschwarze static void 2730992063deSschwarze post_bx(POST_ARGS) 2731992063deSschwarze { 27323af8e8d7Sschwarze struct roff_node *n, *nch; 2733f0c18971Sschwarze const char *macro; 27343af8e8d7Sschwarze 2735fe8e59edSschwarze post_delim_nb(mdoc); 273604fbb99fSschwarze 27373af8e8d7Sschwarze n = mdoc->last; 27383af8e8d7Sschwarze nch = n->child; 27393af8e8d7Sschwarze 27403af8e8d7Sschwarze if (nch != NULL) { 2741f0c18971Sschwarze macro = !strcmp(nch->string, "Open") ? "Ox" : 2742f0c18971Sschwarze !strcmp(nch->string, "Net") ? "Nx" : 2743f0c18971Sschwarze !strcmp(nch->string, "Free") ? "Fx" : 2744f0c18971Sschwarze !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; 2745f0c18971Sschwarze if (macro != NULL) 2746f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 2747f0c18971Sschwarze n->line, n->pos, macro); 27483af8e8d7Sschwarze mdoc->last = nch; 27493af8e8d7Sschwarze nch = nch->next; 27503af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 27513af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 27523af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 27533af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 27543af8e8d7Sschwarze } else 27553af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD; 27563af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 27573af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 27583af8e8d7Sschwarze 27593af8e8d7Sschwarze if (nch == NULL) { 27603af8e8d7Sschwarze mdoc->last = n; 27613af8e8d7Sschwarze return; 27623af8e8d7Sschwarze } 27633af8e8d7Sschwarze 27643af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 27653af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 27663af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 27673af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "-"); 27683af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 27693af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 27703af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 27713af8e8d7Sschwarze mdoc->last = n; 2772992063deSschwarze 2773992063deSschwarze /* 2774992063deSschwarze * Make `Bx's second argument always start with an uppercase 2775992063deSschwarze * letter. Groff checks if it's an "accepted" term, but we just 2776992063deSschwarze * uppercase blindly. 2777992063deSschwarze */ 2778992063deSschwarze 27793af8e8d7Sschwarze *nch->string = (char)toupper((unsigned char)*nch->string); 2780992063deSschwarze } 2781992063deSschwarze 278298b8f00aSschwarze static void 278320fa2881Sschwarze post_os(POST_ARGS) 278420fa2881Sschwarze { 278520fa2881Sschwarze #ifndef OSNAME 278620fa2881Sschwarze struct utsname utsname; 27874c468128Sschwarze static char *defbuf; 278820fa2881Sschwarze #endif 27893a0d07afSschwarze struct roff_node *n; 279020fa2881Sschwarze 279120fa2881Sschwarze n = mdoc->last; 279243808411Sschwarze n->flags |= NODE_NOPRT; 279343808411Sschwarze 2794396853b5Sschwarze if (mdoc->meta.os != NULL) 2795396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2796396853b5Sschwarze n->line, n->pos, "Os"); 2797396853b5Sschwarze else if (mdoc->flags & MDOC_PBODY) 2798396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2799396853b5Sschwarze n->line, n->pos, "Os"); 280020fa2881Sschwarze 2801fe8e59edSschwarze post_delim(mdoc); 2802fe8e59edSschwarze 280320fa2881Sschwarze /* 2804353fa9ecSschwarze * Set the operating system by way of the `Os' macro. 2805353fa9ecSschwarze * The order of precedence is: 2806353fa9ecSschwarze * 1. the argument of the `Os' macro, unless empty 2807353fa9ecSschwarze * 2. the -Ios=foo command line argument, if provided 2808353fa9ecSschwarze * 3. -DOSNAME="\"foo\"", if provided during compilation 2809353fa9ecSschwarze * 4. "sysname release" from uname(3) 281020fa2881Sschwarze */ 281120fa2881Sschwarze 281220fa2881Sschwarze free(mdoc->meta.os); 281383af2bccSschwarze mdoc->meta.os = NULL; 2814423631c9Sschwarze deroff(&mdoc->meta.os, n); 281583af2bccSschwarze if (mdoc->meta.os) 2816ce0ef847Sschwarze goto out; 28174c468128Sschwarze 2818f3476b07Sschwarze if (mdoc->os_s != NULL) { 2819f3476b07Sschwarze mdoc->meta.os = mandoc_strdup(mdoc->os_s); 2820ce0ef847Sschwarze goto out; 2821353fa9ecSschwarze } 28224c468128Sschwarze 282320fa2881Sschwarze #ifdef OSNAME 28244c468128Sschwarze mdoc->meta.os = mandoc_strdup(OSNAME); 282520fa2881Sschwarze #else /*!OSNAME */ 2826f051602aSschwarze if (defbuf == NULL) { 2827f051602aSschwarze if (uname(&utsname) == -1) { 2828f79e7afeSschwarze mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, 2829f79e7afeSschwarze n->line, n->pos, "Os"); 28304c468128Sschwarze defbuf = mandoc_strdup("UNKNOWN"); 2831a450f7c4Sschwarze } else 2832a450f7c4Sschwarze mandoc_asprintf(&defbuf, "%s %s", 2833a450f7c4Sschwarze utsname.sysname, utsname.release); 283420fa2881Sschwarze } 28354c468128Sschwarze mdoc->meta.os = mandoc_strdup(defbuf); 283620fa2881Sschwarze #endif /*!OSNAME*/ 2837ce0ef847Sschwarze 2838f3476b07Sschwarze out: 2839f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OTHER) { 2840f3476b07Sschwarze if (strstr(mdoc->meta.os, "OpenBSD") != NULL) 2841f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_OPENBSD; 2842f3476b07Sschwarze else if (strstr(mdoc->meta.os, "NetBSD") != NULL) 2843f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_NETBSD; 2844f3476b07Sschwarze } 28453427e516Sschwarze 28463427e516Sschwarze /* 28473427e516Sschwarze * This is the earliest point where we can check 28483427e516Sschwarze * Mdocdate conventions because we don't know 28493427e516Sschwarze * the operating system earlier. 28503427e516Sschwarze */ 28513427e516Sschwarze 28522f84042eSschwarze if (n->child != NULL) 28532f84042eSschwarze mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse, 28542f84042eSschwarze n->child->line, n->child->pos, 28552f84042eSschwarze "Os %s (%s)", n->child->string, 28562f84042eSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 28572f84042eSschwarze "OpenBSD" : "NetBSD"); 28582f84042eSschwarze 28593427e516Sschwarze while (n->tok != MDOC_Dd) 28603427e516Sschwarze if ((n = n->prev) == NULL) 28613427e516Sschwarze return; 28623427e516Sschwarze if ((n = n->child) == NULL) 28633427e516Sschwarze return; 286407ec32d0Sschwarze if (strncmp(n->string, "$" "Mdocdate", 9)) { 2865f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) 28663427e516Sschwarze mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING, 28673427e516Sschwarze mdoc->parse, n->line, n->pos, 2868f3476b07Sschwarze "Dd %s (OpenBSD)", n->string); 28693427e516Sschwarze } else { 2870f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_NETBSD) 28713427e516Sschwarze mandoc_vmsg(MANDOCERR_MDOCDATE, 28723427e516Sschwarze mdoc->parse, n->line, n->pos, 2873f3476b07Sschwarze "Dd %s (NetBSD)", n->string); 28743427e516Sschwarze } 287520fa2881Sschwarze } 287620fa2881Sschwarze 2877396853b5Sschwarze enum roff_sec 2878396853b5Sschwarze mdoc_a2sec(const char *p) 287919a69263Sschwarze { 288019a69263Sschwarze int i; 288119a69263Sschwarze 288219a69263Sschwarze for (i = 0; i < (int)SEC__MAX; i++) 288319a69263Sschwarze if (secnames[i] && 0 == strcmp(p, secnames[i])) 2884526e306bSschwarze return (enum roff_sec)i; 288519a69263Sschwarze 2886526e306bSschwarze return SEC_CUSTOM; 288719a69263Sschwarze } 288819a69263Sschwarze 288919a69263Sschwarze static size_t 289014a309e3Sschwarze macro2len(enum roff_tok macro) 289119a69263Sschwarze { 289219a69263Sschwarze 289319a69263Sschwarze switch (macro) { 289449aff9f8Sschwarze case MDOC_Ad: 2895526e306bSschwarze return 12; 289649aff9f8Sschwarze case MDOC_Ao: 2897526e306bSschwarze return 12; 289849aff9f8Sschwarze case MDOC_An: 2899526e306bSschwarze return 12; 290049aff9f8Sschwarze case MDOC_Aq: 2901526e306bSschwarze return 12; 290249aff9f8Sschwarze case MDOC_Ar: 2903526e306bSschwarze return 12; 290449aff9f8Sschwarze case MDOC_Bo: 2905526e306bSschwarze return 12; 290649aff9f8Sschwarze case MDOC_Bq: 2907526e306bSschwarze return 12; 290849aff9f8Sschwarze case MDOC_Cd: 2909526e306bSschwarze return 12; 291049aff9f8Sschwarze case MDOC_Cm: 2911526e306bSschwarze return 10; 291249aff9f8Sschwarze case MDOC_Do: 2913526e306bSschwarze return 10; 291449aff9f8Sschwarze case MDOC_Dq: 2915526e306bSschwarze return 12; 291649aff9f8Sschwarze case MDOC_Dv: 2917526e306bSschwarze return 12; 291849aff9f8Sschwarze case MDOC_Eo: 2919526e306bSschwarze return 12; 292049aff9f8Sschwarze case MDOC_Em: 2921526e306bSschwarze return 10; 292249aff9f8Sschwarze case MDOC_Er: 2923526e306bSschwarze return 17; 292449aff9f8Sschwarze case MDOC_Ev: 2925526e306bSschwarze return 15; 292649aff9f8Sschwarze case MDOC_Fa: 2927526e306bSschwarze return 12; 292849aff9f8Sschwarze case MDOC_Fl: 2929526e306bSschwarze return 10; 293049aff9f8Sschwarze case MDOC_Fo: 2931526e306bSschwarze return 16; 293249aff9f8Sschwarze case MDOC_Fn: 2933526e306bSschwarze return 16; 293449aff9f8Sschwarze case MDOC_Ic: 2935526e306bSschwarze return 10; 293649aff9f8Sschwarze case MDOC_Li: 2937526e306bSschwarze return 16; 293849aff9f8Sschwarze case MDOC_Ms: 2939526e306bSschwarze return 6; 294049aff9f8Sschwarze case MDOC_Nm: 2941526e306bSschwarze return 10; 294249aff9f8Sschwarze case MDOC_No: 2943526e306bSschwarze return 12; 294449aff9f8Sschwarze case MDOC_Oo: 2945526e306bSschwarze return 10; 294649aff9f8Sschwarze case MDOC_Op: 2947526e306bSschwarze return 14; 294849aff9f8Sschwarze case MDOC_Pa: 2949526e306bSschwarze return 32; 295049aff9f8Sschwarze case MDOC_Pf: 2951526e306bSschwarze return 12; 295249aff9f8Sschwarze case MDOC_Po: 2953526e306bSschwarze return 12; 295449aff9f8Sschwarze case MDOC_Pq: 2955526e306bSschwarze return 12; 295649aff9f8Sschwarze case MDOC_Ql: 2957526e306bSschwarze return 16; 295849aff9f8Sschwarze case MDOC_Qo: 2959526e306bSschwarze return 12; 296049aff9f8Sschwarze case MDOC_So: 2961526e306bSschwarze return 12; 296249aff9f8Sschwarze case MDOC_Sq: 2963526e306bSschwarze return 12; 296449aff9f8Sschwarze case MDOC_Sy: 2965526e306bSschwarze return 6; 296649aff9f8Sschwarze case MDOC_Sx: 2967526e306bSschwarze return 16; 296849aff9f8Sschwarze case MDOC_Tn: 2969526e306bSschwarze return 10; 297049aff9f8Sschwarze case MDOC_Va: 2971526e306bSschwarze return 12; 297249aff9f8Sschwarze case MDOC_Vt: 2973526e306bSschwarze return 12; 297449aff9f8Sschwarze case MDOC_Xr: 2975526e306bSschwarze return 10; 297619a69263Sschwarze default: 297719a69263Sschwarze break; 297819a69263Sschwarze }; 2979526e306bSschwarze return 0; 298019a69263Sschwarze } 2981