1*8fd2959dSschwarze /* $OpenBSD: mdoc_validate.c,v 1.257 2017/06/24 18:58:09 schwarze Exp $ */ 2f73abda9Skristaps /* 322972b14Sschwarze * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4f27faaccSschwarze * Copyright (c) 2010-2017 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" 34d1982c71Sschwarze #include "roff.h" 35d1982c71Sschwarze #include "mdoc.h" 36f6854d5cSschwarze #include "libmandoc.h" 37fa2127f9Sschwarze #include "roff_int.h" 38d1982c71Sschwarze #include "libmdoc.h" 39f73abda9Skristaps 40f73abda9Skristaps /* FIXME: .Bl -diag can't have non-text children in HEAD. */ 41f73abda9Skristaps 42ede1b9d0Sschwarze #define POST_ARGS struct roff_man *mdoc 43f73abda9Skristaps 447c2be9f8Sschwarze enum check_ineq { 457c2be9f8Sschwarze CHECK_LT, 467c2be9f8Sschwarze CHECK_GT, 477c2be9f8Sschwarze CHECK_EQ 487c2be9f8Sschwarze }; 497c2be9f8Sschwarze 5098b8f00aSschwarze typedef void (*v_post)(POST_ARGS); 51f73abda9Skristaps 528ccddcd3Sschwarze static int build_list(struct roff_man *, int); 53ede1b9d0Sschwarze static void check_text(struct roff_man *, int, int, char *); 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 *); 5748497dd5Sschwarze static void check_toptext(struct roff_man *, int, int, const char *); 583a0d07afSschwarze static int child_an(const struct roff_node *); 5914a309e3Sschwarze static size_t macro2len(enum roff_tok); 606050a3daSschwarze static void rewrite_macro2len(struct roff_man *, char **); 6167c719adSschwarze 6298b8f00aSschwarze static void post_an(POST_ARGS); 633e642ba0Sschwarze static void post_an_norm(POST_ARGS); 6498b8f00aSschwarze static void post_at(POST_ARGS); 653e642ba0Sschwarze static void post_bd(POST_ARGS); 6698b8f00aSschwarze static void post_bf(POST_ARGS); 6798b8f00aSschwarze static void post_bk(POST_ARGS); 6898b8f00aSschwarze static void post_bl(POST_ARGS); 6998b8f00aSschwarze static void post_bl_block(POST_ARGS); 7098b8f00aSschwarze static void post_bl_head(POST_ARGS); 713e642ba0Sschwarze static void post_bl_norm(POST_ARGS); 7298b8f00aSschwarze static void post_bx(POST_ARGS); 7398b8f00aSschwarze static void post_defaults(POST_ARGS); 743e642ba0Sschwarze static void post_display(POST_ARGS); 7598b8f00aSschwarze static void post_dd(POST_ARGS); 7604fbb99fSschwarze static void post_delim(POST_ARGS); 7798b8f00aSschwarze static void post_dt(POST_ARGS); 7898b8f00aSschwarze static void post_en(POST_ARGS); 7998b8f00aSschwarze static void post_es(POST_ARGS); 8098b8f00aSschwarze static void post_eoln(POST_ARGS); 8198b8f00aSschwarze static void post_ex(POST_ARGS); 8298b8f00aSschwarze static void post_fa(POST_ARGS); 8398b8f00aSschwarze static void post_fn(POST_ARGS); 8498b8f00aSschwarze static void post_fname(POST_ARGS); 8598b8f00aSschwarze static void post_fo(POST_ARGS); 8698b8f00aSschwarze static void post_hyph(POST_ARGS); 8798b8f00aSschwarze static void post_ignpar(POST_ARGS); 8898b8f00aSschwarze static void post_it(POST_ARGS); 8998b8f00aSschwarze static void post_lb(POST_ARGS); 9098b8f00aSschwarze static void post_nd(POST_ARGS); 9198b8f00aSschwarze static void post_nm(POST_ARGS); 9298b8f00aSschwarze static void post_ns(POST_ARGS); 933e642ba0Sschwarze static void post_obsolete(POST_ARGS); 9498b8f00aSschwarze static void post_os(POST_ARGS); 9598b8f00aSschwarze static void post_par(POST_ARGS); 963e642ba0Sschwarze static void post_prevpar(POST_ARGS); 9798b8f00aSschwarze static void post_root(POST_ARGS); 9898b8f00aSschwarze static void post_rs(POST_ARGS); 998ccddcd3Sschwarze static void post_rv(POST_ARGS); 10098b8f00aSschwarze static void post_sh(POST_ARGS); 10198b8f00aSschwarze static void post_sh_head(POST_ARGS); 10298b8f00aSschwarze static void post_sh_name(POST_ARGS); 10398b8f00aSschwarze static void post_sh_see_also(POST_ARGS); 10498b8f00aSschwarze static void post_sh_authors(POST_ARGS); 10598b8f00aSschwarze static void post_sm(POST_ARGS); 10698b8f00aSschwarze static void post_st(POST_ARGS); 1073e642ba0Sschwarze static void post_std(POST_ARGS); 108bc205043Sschwarze static void post_useless(POST_ARGS); 1095ae08040Sschwarze static void post_xr(POST_ARGS); 110816c3c54Sschwarze static void post_xx(POST_ARGS); 11198b8f00aSschwarze 11214a309e3Sschwarze static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { 1133e642ba0Sschwarze post_dd, /* Dd */ 1143e642ba0Sschwarze post_dt, /* Dt */ 1153e642ba0Sschwarze post_os, /* Os */ 1163e642ba0Sschwarze post_sh, /* Sh */ 1173e642ba0Sschwarze post_ignpar, /* Ss */ 1183e642ba0Sschwarze post_par, /* Pp */ 1193e642ba0Sschwarze post_display, /* D1 */ 1203e642ba0Sschwarze post_display, /* Dl */ 1213e642ba0Sschwarze post_display, /* Bd */ 1223e642ba0Sschwarze NULL, /* Ed */ 1233e642ba0Sschwarze post_bl, /* Bl */ 1243e642ba0Sschwarze NULL, /* El */ 1253e642ba0Sschwarze post_it, /* It */ 12604fbb99fSschwarze post_delim, /* Ad */ 1273e642ba0Sschwarze post_an, /* An */ 12814a309e3Sschwarze NULL, /* Ap */ 1293e642ba0Sschwarze post_defaults, /* Ar */ 1303e642ba0Sschwarze NULL, /* Cd */ 13104fbb99fSschwarze post_delim, /* Cm */ 13204fbb99fSschwarze post_delim, /* Dv */ 13304fbb99fSschwarze post_delim, /* Er */ 13404fbb99fSschwarze post_delim, /* Ev */ 1353e642ba0Sschwarze post_ex, /* Ex */ 1363e642ba0Sschwarze post_fa, /* Fa */ 1373e642ba0Sschwarze NULL, /* Fd */ 13804fbb99fSschwarze post_delim, /* Fl */ 1393e642ba0Sschwarze post_fn, /* Fn */ 14004fbb99fSschwarze post_delim, /* Ft */ 14104fbb99fSschwarze post_delim, /* Ic */ 14204fbb99fSschwarze post_delim, /* In */ 1433e642ba0Sschwarze post_defaults, /* Li */ 1443e642ba0Sschwarze post_nd, /* Nd */ 1453e642ba0Sschwarze post_nm, /* Nm */ 14604fbb99fSschwarze post_delim, /* Op */ 1473e642ba0Sschwarze post_obsolete, /* Ot */ 1483e642ba0Sschwarze post_defaults, /* Pa */ 1498ccddcd3Sschwarze post_rv, /* Rv */ 1503e642ba0Sschwarze post_st, /* St */ 15104fbb99fSschwarze post_delim, /* Va */ 15204fbb99fSschwarze post_delim, /* Vt */ 1535ae08040Sschwarze post_xr, /* Xr */ 1543e642ba0Sschwarze NULL, /* %A */ 1553e642ba0Sschwarze post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ 1563e642ba0Sschwarze NULL, /* %D */ 1573e642ba0Sschwarze NULL, /* %I */ 1583e642ba0Sschwarze NULL, /* %J */ 1593e642ba0Sschwarze post_hyph, /* %N */ 1603e642ba0Sschwarze post_hyph, /* %O */ 1613e642ba0Sschwarze NULL, /* %P */ 1623e642ba0Sschwarze post_hyph, /* %R */ 1633e642ba0Sschwarze post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ 1643e642ba0Sschwarze NULL, /* %V */ 1653e642ba0Sschwarze NULL, /* Ac */ 16604fbb99fSschwarze post_delim, /* Ao */ 16704fbb99fSschwarze post_delim, /* Aq */ 1683e642ba0Sschwarze post_at, /* At */ 1693e642ba0Sschwarze NULL, /* Bc */ 1703e642ba0Sschwarze post_bf, /* Bf */ 17104fbb99fSschwarze post_delim, /* Bo */ 1723e642ba0Sschwarze NULL, /* Bq */ 173816c3c54Sschwarze post_xx, /* Bsx */ 1743e642ba0Sschwarze post_bx, /* Bx */ 1753e642ba0Sschwarze post_obsolete, /* Db */ 1763e642ba0Sschwarze NULL, /* Dc */ 1773e642ba0Sschwarze NULL, /* Do */ 1783e642ba0Sschwarze NULL, /* Dq */ 1793e642ba0Sschwarze NULL, /* Ec */ 1803e642ba0Sschwarze NULL, /* Ef */ 18104fbb99fSschwarze post_delim, /* Em */ 1823e642ba0Sschwarze NULL, /* Eo */ 183816c3c54Sschwarze post_xx, /* Fx */ 18404fbb99fSschwarze post_delim, /* Ms */ 185abcca917Sschwarze NULL, /* No */ 1863e642ba0Sschwarze post_ns, /* Ns */ 187816c3c54Sschwarze post_xx, /* Nx */ 188816c3c54Sschwarze post_xx, /* Ox */ 1893e642ba0Sschwarze NULL, /* Pc */ 1903e642ba0Sschwarze NULL, /* Pf */ 19104fbb99fSschwarze post_delim, /* Po */ 19204fbb99fSschwarze post_delim, /* Pq */ 1933e642ba0Sschwarze NULL, /* Qc */ 19404fbb99fSschwarze post_delim, /* Ql */ 19504fbb99fSschwarze post_delim, /* Qo */ 19604fbb99fSschwarze post_delim, /* Qq */ 1973e642ba0Sschwarze NULL, /* Re */ 1983e642ba0Sschwarze post_rs, /* Rs */ 1993e642ba0Sschwarze NULL, /* Sc */ 20004fbb99fSschwarze post_delim, /* So */ 20104fbb99fSschwarze post_delim, /* Sq */ 2023e642ba0Sschwarze post_sm, /* Sm */ 2033e642ba0Sschwarze post_hyph, /* Sx */ 20404fbb99fSschwarze post_delim, /* Sy */ 205bc205043Sschwarze post_useless, /* Tn */ 206816c3c54Sschwarze post_xx, /* Ux */ 2073e642ba0Sschwarze NULL, /* Xc */ 2083e642ba0Sschwarze NULL, /* Xo */ 2093e642ba0Sschwarze post_fo, /* Fo */ 2103e642ba0Sschwarze NULL, /* Fc */ 21104fbb99fSschwarze post_delim, /* Oo */ 2123e642ba0Sschwarze NULL, /* Oc */ 2133e642ba0Sschwarze post_bk, /* Bk */ 2143e642ba0Sschwarze NULL, /* Ek */ 2153e642ba0Sschwarze post_eoln, /* Bt */ 21604fbb99fSschwarze post_obsolete, /* Hf */ 2173e642ba0Sschwarze post_obsolete, /* Fr */ 2183e642ba0Sschwarze post_eoln, /* Ud */ 2193e642ba0Sschwarze post_lb, /* Lb */ 2203e642ba0Sschwarze post_par, /* Lp */ 22104fbb99fSschwarze post_delim, /* Lk */ 2223e642ba0Sschwarze post_defaults, /* Mt */ 22304fbb99fSschwarze post_delim, /* Brq */ 22404fbb99fSschwarze post_delim, /* Bro */ 2253e642ba0Sschwarze NULL, /* Brc */ 2263e642ba0Sschwarze NULL, /* %C */ 2273e642ba0Sschwarze post_es, /* Es */ 2283e642ba0Sschwarze post_en, /* En */ 229816c3c54Sschwarze post_xx, /* Dx */ 2303e642ba0Sschwarze NULL, /* %Q */ 2313e642ba0Sschwarze NULL, /* %U */ 2323e642ba0Sschwarze NULL, /* Ta */ 233f73abda9Skristaps }; 23414a309e3Sschwarze static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd; 235f73abda9Skristaps 23620fa2881Sschwarze #define RSORD_MAX 14 /* Number of `Rs' blocks. */ 23720fa2881Sschwarze 23814a309e3Sschwarze static const enum roff_tok rsord[RSORD_MAX] = { 23920fa2881Sschwarze MDOC__A, 24020fa2881Sschwarze MDOC__T, 24120fa2881Sschwarze MDOC__B, 24220fa2881Sschwarze MDOC__I, 24320fa2881Sschwarze MDOC__J, 24420fa2881Sschwarze MDOC__R, 24520fa2881Sschwarze MDOC__N, 24620fa2881Sschwarze MDOC__V, 2470397c682Sschwarze MDOC__U, 24820fa2881Sschwarze MDOC__P, 24920fa2881Sschwarze MDOC__Q, 2504e32ec8fSschwarze MDOC__C, 25120fa2881Sschwarze MDOC__D, 2524e32ec8fSschwarze MDOC__O 25320fa2881Sschwarze }; 25420fa2881Sschwarze 25519a69263Sschwarze static const char * const secnames[SEC__MAX] = { 25619a69263Sschwarze NULL, 25719a69263Sschwarze "NAME", 25819a69263Sschwarze "LIBRARY", 25919a69263Sschwarze "SYNOPSIS", 26019a69263Sschwarze "DESCRIPTION", 26103ab2f23Sdlg "CONTEXT", 26219a69263Sschwarze "IMPLEMENTATION NOTES", 26319a69263Sschwarze "RETURN VALUES", 26419a69263Sschwarze "ENVIRONMENT", 26519a69263Sschwarze "FILES", 26619a69263Sschwarze "EXIT STATUS", 26719a69263Sschwarze "EXAMPLES", 26819a69263Sschwarze "DIAGNOSTICS", 26919a69263Sschwarze "COMPATIBILITY", 27019a69263Sschwarze "ERRORS", 27119a69263Sschwarze "SEE ALSO", 27219a69263Sschwarze "STANDARDS", 27319a69263Sschwarze "HISTORY", 27419a69263Sschwarze "AUTHORS", 27519a69263Sschwarze "CAVEATS", 27619a69263Sschwarze "BUGS", 27719a69263Sschwarze "SECURITY CONSIDERATIONS", 27819a69263Sschwarze NULL 27919a69263Sschwarze }; 280f73abda9Skristaps 28149aff9f8Sschwarze 28298b8f00aSschwarze void 283396853b5Sschwarze mdoc_node_validate(struct roff_man *mdoc) 284f73abda9Skristaps { 2853a0d07afSschwarze struct roff_node *n; 28614a309e3Sschwarze const v_post *p; 287f73abda9Skristaps 288753701eeSschwarze n = mdoc->last; 289396853b5Sschwarze mdoc->last = mdoc->last->child; 290396853b5Sschwarze while (mdoc->last != NULL) { 291396853b5Sschwarze mdoc_node_validate(mdoc); 292396853b5Sschwarze if (mdoc->last == n) 293396853b5Sschwarze mdoc->last = mdoc->last->child; 294396853b5Sschwarze else 295396853b5Sschwarze mdoc->last = mdoc->last->next; 296396853b5Sschwarze } 297f73abda9Skristaps 298396853b5Sschwarze mdoc->last = n; 299396853b5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 300753701eeSschwarze switch (n->type) { 301d1982c71Sschwarze case ROFFT_TEXT: 30279ca1811Sschwarze if (n->sec != SEC_SYNOPSIS || 30379ca1811Sschwarze (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd)) 3043e642ba0Sschwarze check_text(mdoc, n->line, n->pos, n->string); 30548497dd5Sschwarze if (n->parent->tok == MDOC_It || 30648497dd5Sschwarze (n->parent->type == ROFFT_BODY && 30748497dd5Sschwarze (n->parent->tok == MDOC_Sh || 30848497dd5Sschwarze n->parent->tok == MDOC_Ss))) 30948497dd5Sschwarze check_toptext(mdoc, n->line, n->pos, n->string); 3103e642ba0Sschwarze break; 311d1982c71Sschwarze case ROFFT_EQN: 312d1982c71Sschwarze case ROFFT_TBL: 31398b8f00aSschwarze break; 314d1982c71Sschwarze case ROFFT_ROOT: 31598b8f00aSschwarze post_root(mdoc); 31698b8f00aSschwarze break; 3172791bd1cSschwarze default: 3183e642ba0Sschwarze check_args(mdoc, mdoc->last); 3196f9818f6Sschwarze 3206f9818f6Sschwarze /* 3216f9818f6Sschwarze * Closing delimiters are not special at the 3226f9818f6Sschwarze * beginning of a block, opening delimiters 3236f9818f6Sschwarze * are not special at the end. 3246f9818f6Sschwarze */ 3256f9818f6Sschwarze 3266f9818f6Sschwarze if (n->child != NULL) 327c4b0939cSschwarze n->child->flags &= ~NODE_DELIMC; 3286f9818f6Sschwarze if (n->last != NULL) 329c4b0939cSschwarze n->last->flags &= ~NODE_DELIMO; 3306f9818f6Sschwarze 3316f9818f6Sschwarze /* Call the macro's postprocessor. */ 3326f9818f6Sschwarze 33329478532Sschwarze if (n->tok < ROFF_MAX) { 33429478532Sschwarze switch(n->tok) { 33529478532Sschwarze case ROFF_br: 3366561cb23Sschwarze case ROFF_sp: 33729478532Sschwarze post_par(mdoc); 33829478532Sschwarze break; 33929478532Sschwarze default: 340c4d3fa85Sschwarze roff_validate(mdoc); 341c4d3fa85Sschwarze break; 34229478532Sschwarze } 34329478532Sschwarze break; 34429478532Sschwarze } 34529478532Sschwarze 34629478532Sschwarze assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 3473e642ba0Sschwarze p = mdoc_valids + n->tok; 34898b8f00aSschwarze if (*p) 34998b8f00aSschwarze (*p)(mdoc); 350396853b5Sschwarze if (mdoc->last == n) 351396853b5Sschwarze mdoc_state(mdoc, n); 35298b8f00aSschwarze break; 3532791bd1cSschwarze } 354f73abda9Skristaps } 355f73abda9Skristaps 35698b8f00aSschwarze static void 357ede1b9d0Sschwarze check_args(struct roff_man *mdoc, struct roff_node *n) 358f73abda9Skristaps { 359f73abda9Skristaps int i; 360f73abda9Skristaps 361f73abda9Skristaps if (NULL == n->args) 36220fa2881Sschwarze return; 363f73abda9Skristaps 364f73abda9Skristaps assert(n->args->argc); 365f73abda9Skristaps for (i = 0; i < (int)n->args->argc; i++) 3667ead8a4eSschwarze check_argv(mdoc, n, &n->args->argv[i]); 367f73abda9Skristaps } 368f73abda9Skristaps 36920fa2881Sschwarze static void 370ede1b9d0Sschwarze check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) 371f73abda9Skristaps { 372f73abda9Skristaps int i; 373f73abda9Skristaps 374f73abda9Skristaps for (i = 0; i < (int)v->sz; i++) 3757ead8a4eSschwarze check_text(mdoc, v->line, v->pos, v->value[i]); 376f73abda9Skristaps } 377f73abda9Skristaps 37820fa2881Sschwarze static void 379ede1b9d0Sschwarze check_text(struct roff_man *mdoc, int ln, int pos, char *p) 380f73abda9Skristaps { 38104e980cbSschwarze char *cp; 382769ee804Sschwarze 3837ead8a4eSschwarze if (MDOC_LITERAL & mdoc->flags) 3841cdbf331Sschwarze return; 3851cdbf331Sschwarze 3861cdbf331Sschwarze for (cp = p; NULL != (p = strchr(p, '\t')); p++) 387dd5b31c3Sschwarze mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse, 388dd5b31c3Sschwarze ln, pos + (int)(p - cp), NULL); 389f73abda9Skristaps } 390f73abda9Skristaps 39198b8f00aSschwarze static void 39248497dd5Sschwarze check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) 393f0c18971Sschwarze { 39448497dd5Sschwarze const char *cp, *cpr; 39548497dd5Sschwarze 39648497dd5Sschwarze if (*p == '\0') 39748497dd5Sschwarze return; 398f0c18971Sschwarze 399f0c18971Sschwarze if ((cp = strstr(p, "OpenBSD")) != NULL) 400f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 401f0c18971Sschwarze ln, pos + (cp - p), "Ox"); 402f0c18971Sschwarze if ((cp = strstr(p, "NetBSD")) != NULL) 403f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 404f0c18971Sschwarze ln, pos + (cp - p), "Nx"); 405f0c18971Sschwarze if ((cp = strstr(p, "FreeBSD")) != NULL) 406f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 407f0c18971Sschwarze ln, pos + (cp - p), "Fx"); 408f0c18971Sschwarze if ((cp = strstr(p, "DragonFly")) != NULL) 409f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 410f0c18971Sschwarze ln, pos + (cp - p), "Dx"); 41148497dd5Sschwarze 41248497dd5Sschwarze cp = p; 41348497dd5Sschwarze while ((cp = strstr(cp + 1, "()")) != NULL) { 41448497dd5Sschwarze for (cpr = cp - 1; cpr >= p; cpr--) 41548497dd5Sschwarze if (*cpr != '_' && !isalnum((unsigned char)*cpr)) 41648497dd5Sschwarze break; 41748497dd5Sschwarze if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { 41848497dd5Sschwarze cpr++; 41948497dd5Sschwarze mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse, 42048497dd5Sschwarze ln, pos + (cpr - p), 42148497dd5Sschwarze "%.*s()", (int)(cp - cpr), cpr); 42248497dd5Sschwarze } 42348497dd5Sschwarze } 424f0c18971Sschwarze } 425f0c18971Sschwarze 426f0c18971Sschwarze static void 42704fbb99fSschwarze post_delim(POST_ARGS) 42804fbb99fSschwarze { 42904fbb99fSschwarze const struct roff_node *nch; 430b7c50e87Sschwarze const char *lc, *cp; 431b7c50e87Sschwarze int nw; 43204fbb99fSschwarze enum mdelim delim; 433b7c50e87Sschwarze enum roff_tok tok; 43404fbb99fSschwarze 435b7c50e87Sschwarze /* 436b7c50e87Sschwarze * Find candidates: at least two bytes, 437b7c50e87Sschwarze * the last one a closing or middle delimiter. 438b7c50e87Sschwarze */ 439b7c50e87Sschwarze 440b7c50e87Sschwarze tok = mdoc->last->tok; 44104fbb99fSschwarze nch = mdoc->last->last; 44204fbb99fSschwarze if (nch == NULL || nch->type != ROFFT_TEXT) 44304fbb99fSschwarze return; 44404fbb99fSschwarze lc = strchr(nch->string, '\0') - 1; 44504fbb99fSschwarze if (lc <= nch->string) 44604fbb99fSschwarze return; 44704fbb99fSschwarze delim = mdoc_isdelim(lc); 44804fbb99fSschwarze if (delim == DELIM_NONE || delim == DELIM_OPEN) 44904fbb99fSschwarze return; 450b7c50e87Sschwarze 451b7c50e87Sschwarze /* 452b7c50e87Sschwarze * Reduce false positives by allowing various cases. 453b7c50e87Sschwarze */ 454b7c50e87Sschwarze 455b7c50e87Sschwarze /* Escaped delimiters. */ 456b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '\\' && 457b7c50e87Sschwarze (lc[-1] == '&' || lc[-1] == 'e')) 458b7c50e87Sschwarze return; 459b7c50e87Sschwarze 460b7c50e87Sschwarze /* Specific byte sequences. */ 461b7c50e87Sschwarze switch (*lc) { 462b7c50e87Sschwarze case ')': 463b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--) 464b7c50e87Sschwarze if (*cp == '(') 465b7c50e87Sschwarze return; 466b7c50e87Sschwarze break; 467b7c50e87Sschwarze case '.': 468b7c50e87Sschwarze if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') 469b7c50e87Sschwarze return; 470b7c50e87Sschwarze if (lc[-1] == '.') 471b7c50e87Sschwarze return; 472b7c50e87Sschwarze break; 473b7c50e87Sschwarze case ';': 474b7c50e87Sschwarze if (tok == MDOC_Vt) 475b7c50e87Sschwarze return; 476b7c50e87Sschwarze break; 477b7c50e87Sschwarze case '?': 478b7c50e87Sschwarze if (lc[-1] == '?') 479b7c50e87Sschwarze return; 480b7c50e87Sschwarze break; 481b7c50e87Sschwarze case ']': 482b7c50e87Sschwarze for (cp = lc; cp >= nch->string; cp--) 483b7c50e87Sschwarze if (*cp == '[') 484b7c50e87Sschwarze return; 485b7c50e87Sschwarze break; 486b7c50e87Sschwarze case '|': 487b7c50e87Sschwarze if (lc == nch->string + 1 && lc[-1] == '|') 488b7c50e87Sschwarze return; 489b7c50e87Sschwarze default: 490b7c50e87Sschwarze break; 491b7c50e87Sschwarze } 492b7c50e87Sschwarze 493b7c50e87Sschwarze /* Exactly two non-alphanumeric bytes. */ 494b7c50e87Sschwarze if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) 495b7c50e87Sschwarze return; 496b7c50e87Sschwarze 497b7c50e87Sschwarze /* At least three alphabetic words with a sentence ending. */ 498b7c50e87Sschwarze if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || 499abcca917Sschwarze tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq || 500abcca917Sschwarze tok == MDOC_Sy)) { 501b7c50e87Sschwarze nw = 0; 502b7c50e87Sschwarze for (cp = lc - 1; cp >= nch->string; cp--) { 503b7c50e87Sschwarze if (*cp == ' ') { 504b7c50e87Sschwarze nw++; 505b7c50e87Sschwarze if (cp > nch->string && cp[-1] == ',') 506b7c50e87Sschwarze cp--; 507b7c50e87Sschwarze } else if (isalpha((unsigned int)*cp)) { 508b7c50e87Sschwarze if (nw > 1) 509b7c50e87Sschwarze return; 510b7c50e87Sschwarze } else 511b7c50e87Sschwarze break; 512b7c50e87Sschwarze } 513b7c50e87Sschwarze } 514b7c50e87Sschwarze 51504fbb99fSschwarze mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse, 51604fbb99fSschwarze nch->line, nch->pos + (lc - nch->string), 517b7c50e87Sschwarze "%s%s %s", roff_name[tok], 51804fbb99fSschwarze nch == mdoc->last->child ? "" : " ...", nch->string); 51904fbb99fSschwarze } 52004fbb99fSschwarze 52104fbb99fSschwarze static void 5223e642ba0Sschwarze post_bl_norm(POST_ARGS) 523f73abda9Skristaps { 5243e642ba0Sschwarze struct roff_node *n; 525aa99c14fSschwarze struct mdoc_argv *argv, *wa; 5264a9f685fSschwarze int i; 527aa99c14fSschwarze enum mdocargt mdoclt; 5284a9f685fSschwarze enum mdoc_list lt; 529f73abda9Skristaps 5303e642ba0Sschwarze n = mdoc->last->parent; 5313e642ba0Sschwarze n->norm->Bl.type = LIST__NONE; 532f73abda9Skristaps 5336093755cSschwarze /* 5346093755cSschwarze * First figure out which kind of list to use: bind ourselves to 5356093755cSschwarze * the first mentioned list type and warn about any remaining 5366093755cSschwarze * ones. If we find no list type, we default to LIST_item. 5376093755cSschwarze */ 538f73abda9Skristaps 539dadc3a61Sschwarze wa = (n->args == NULL) ? NULL : n->args->argv; 540aa99c14fSschwarze mdoclt = MDOC_ARG_MAX; 5416093755cSschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 5424a9f685fSschwarze argv = n->args->argv + i; 5436093755cSschwarze lt = LIST__NONE; 5444a9f685fSschwarze switch (argv->arg) { 5456093755cSschwarze /* Set list types. */ 54649aff9f8Sschwarze case MDOC_Bullet: 5476093755cSschwarze lt = LIST_bullet; 5486093755cSschwarze break; 54949aff9f8Sschwarze case MDOC_Dash: 5506093755cSschwarze lt = LIST_dash; 5516093755cSschwarze break; 55249aff9f8Sschwarze case MDOC_Enum: 5536093755cSschwarze lt = LIST_enum; 5546093755cSschwarze break; 55549aff9f8Sschwarze case MDOC_Hyphen: 5566093755cSschwarze lt = LIST_hyphen; 5576093755cSschwarze break; 55849aff9f8Sschwarze case MDOC_Item: 5596093755cSschwarze lt = LIST_item; 5606093755cSschwarze break; 56149aff9f8Sschwarze case MDOC_Tag: 5626093755cSschwarze lt = LIST_tag; 5636093755cSschwarze break; 56449aff9f8Sschwarze case MDOC_Diag: 5656093755cSschwarze lt = LIST_diag; 5666093755cSschwarze break; 56749aff9f8Sschwarze case MDOC_Hang: 5686093755cSschwarze lt = LIST_hang; 5696093755cSschwarze break; 57049aff9f8Sschwarze case MDOC_Ohang: 5716093755cSschwarze lt = LIST_ohang; 5726093755cSschwarze break; 57349aff9f8Sschwarze case MDOC_Inset: 5746093755cSschwarze lt = LIST_inset; 5756093755cSschwarze break; 57649aff9f8Sschwarze case MDOC_Column: 5776093755cSschwarze lt = LIST_column; 57864d728e4Sschwarze break; 5796093755cSschwarze /* Set list arguments. */ 58049aff9f8Sschwarze case MDOC_Compact: 5814a9f685fSschwarze if (n->norm->Bl.comp) 5824a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP, 5834a9f685fSschwarze mdoc->parse, argv->line, 5844a9f685fSschwarze argv->pos, "Bl -compact"); 5854a9f685fSschwarze n->norm->Bl.comp = 1; 58650e63e03Sschwarze break; 58749aff9f8Sschwarze case MDOC_Width: 588aa99c14fSschwarze wa = argv; 5894a9f685fSschwarze if (0 == argv->sz) { 5904a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 5914a9f685fSschwarze mdoc->parse, argv->line, 5924a9f685fSschwarze argv->pos, "Bl -width"); 5934a9f685fSschwarze n->norm->Bl.width = "0n"; 59422972b14Sschwarze break; 59522972b14Sschwarze } 5964a9f685fSschwarze if (NULL != n->norm->Bl.width) 5974a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 5984a9f685fSschwarze mdoc->parse, argv->line, 5994a9f685fSschwarze argv->pos, "Bl -width %s", 6004a9f685fSschwarze argv->value[0]); 6016050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 6024a9f685fSschwarze n->norm->Bl.width = argv->value[0]; 60364d728e4Sschwarze break; 60449aff9f8Sschwarze case MDOC_Offset: 6054a9f685fSschwarze if (0 == argv->sz) { 6064a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 6074a9f685fSschwarze mdoc->parse, argv->line, 6084a9f685fSschwarze argv->pos, "Bl -offset"); 60931e23753Sschwarze break; 61031e23753Sschwarze } 6114a9f685fSschwarze if (NULL != n->norm->Bl.offs) 6124a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 6134a9f685fSschwarze mdoc->parse, argv->line, 6144a9f685fSschwarze argv->pos, "Bl -offset %s", 6154a9f685fSschwarze argv->value[0]); 6166050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 6174a9f685fSschwarze n->norm->Bl.offs = argv->value[0]; 618f73abda9Skristaps break; 619ddce0b0cSschwarze default: 620ddce0b0cSschwarze continue; 621f73abda9Skristaps } 622dc0d8bb2Sschwarze if (LIST__NONE == lt) 623dc0d8bb2Sschwarze continue; 624aa99c14fSschwarze mdoclt = argv->arg; 625f73abda9Skristaps 6266093755cSschwarze /* Check: multiple list types. */ 6276093755cSschwarze 628dc0d8bb2Sschwarze if (LIST__NONE != n->norm->Bl.type) { 629bd594191Sschwarze mandoc_vmsg(MANDOCERR_BL_REP, 630dc0d8bb2Sschwarze mdoc->parse, n->line, n->pos, 631bd594191Sschwarze "Bl -%s", mdoc_argnames[argv->arg]); 632dc0d8bb2Sschwarze continue; 633769ee804Sschwarze } 6346093755cSschwarze 6356093755cSschwarze /* The list type should come first. */ 6366093755cSschwarze 6378c62fbf5Sschwarze if (n->norm->Bl.width || 6388c62fbf5Sschwarze n->norm->Bl.offs || 6398c62fbf5Sschwarze n->norm->Bl.comp) 640bd594191Sschwarze mandoc_vmsg(MANDOCERR_BL_LATETYPE, 641bd594191Sschwarze mdoc->parse, n->line, n->pos, "Bl -%s", 64266788495Sschwarze mdoc_argnames[n->args->argv[0].arg]); 643dc0d8bb2Sschwarze 644dc0d8bb2Sschwarze n->norm->Bl.type = lt; 645dc0d8bb2Sschwarze if (LIST_column == lt) { 646dc0d8bb2Sschwarze n->norm->Bl.ncols = argv->sz; 647dc0d8bb2Sschwarze n->norm->Bl.cols = (void *)argv->value; 648dc0d8bb2Sschwarze } 6496093755cSschwarze } 6506093755cSschwarze 6516093755cSschwarze /* Allow lists to default to LIST_item. */ 6526093755cSschwarze 6538c62fbf5Sschwarze if (LIST__NONE == n->norm->Bl.type) { 654bd594191Sschwarze mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse, 655bd594191Sschwarze n->line, n->pos, "Bl"); 6568c62fbf5Sschwarze n->norm->Bl.type = LIST_item; 6572c7eb483Sschwarze mdoclt = MDOC_Item; 6586e03d529Sschwarze } 659f73abda9Skristaps 66064d728e4Sschwarze /* 66164d728e4Sschwarze * Validate the width field. Some list types don't need width 66264d728e4Sschwarze * types and should be warned about them. Others should have it 6635eced068Sschwarze * and must also be warned. Yet others have a default and need 6645eced068Sschwarze * no warning. 66564d728e4Sschwarze */ 66664d728e4Sschwarze 6678c62fbf5Sschwarze switch (n->norm->Bl.type) { 66849aff9f8Sschwarze case LIST_tag: 6695eced068Sschwarze if (NULL == n->norm->Bl.width) 670bd594191Sschwarze mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse, 671bd594191Sschwarze n->line, n->pos, "Bl -tag"); 672f73abda9Skristaps break; 67349aff9f8Sschwarze case LIST_column: 67449aff9f8Sschwarze case LIST_diag: 67549aff9f8Sschwarze case LIST_ohang: 67649aff9f8Sschwarze case LIST_inset: 67749aff9f8Sschwarze case LIST_item: 6788c62fbf5Sschwarze if (n->norm->Bl.width) 679aa99c14fSschwarze mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, 680aa99c14fSschwarze wa->line, wa->pos, "Bl -%s", 681aa99c14fSschwarze mdoc_argnames[mdoclt]); 6826093755cSschwarze break; 68349aff9f8Sschwarze case LIST_bullet: 68449aff9f8Sschwarze case LIST_dash: 68549aff9f8Sschwarze case LIST_hyphen: 6865eced068Sschwarze if (NULL == n->norm->Bl.width) 6875eced068Sschwarze n->norm->Bl.width = "2n"; 6885eced068Sschwarze break; 68949aff9f8Sschwarze case LIST_enum: 6905eced068Sschwarze if (NULL == n->norm->Bl.width) 6915eced068Sschwarze n->norm->Bl.width = "3n"; 6925eced068Sschwarze break; 69364d728e4Sschwarze default: 694f73abda9Skristaps break; 69564d728e4Sschwarze } 696f73abda9Skristaps } 697f73abda9Skristaps 69898b8f00aSschwarze static void 6993e642ba0Sschwarze post_bd(POST_ARGS) 700f73abda9Skristaps { 7013e642ba0Sschwarze struct roff_node *n; 7024a9f685fSschwarze struct mdoc_argv *argv; 7034a9f685fSschwarze int i; 7044a9f685fSschwarze enum mdoc_disp dt; 705f73abda9Skristaps 7063e642ba0Sschwarze n = mdoc->last; 70731e23753Sschwarze for (i = 0; n->args && i < (int)n->args->argc; i++) { 7084a9f685fSschwarze argv = n->args->argv + i; 70931e23753Sschwarze dt = DISP__NONE; 71031e23753Sschwarze 7114a9f685fSschwarze switch (argv->arg) { 71249aff9f8Sschwarze case MDOC_Centred: 7132065e47aSschwarze dt = DISP_centered; 71431e23753Sschwarze break; 71549aff9f8Sschwarze case MDOC_Ragged: 71631e23753Sschwarze dt = DISP_ragged; 71731e23753Sschwarze break; 71849aff9f8Sschwarze case MDOC_Unfilled: 71931e23753Sschwarze dt = DISP_unfilled; 72031e23753Sschwarze break; 72149aff9f8Sschwarze case MDOC_Filled: 72231e23753Sschwarze dt = DISP_filled; 72331e23753Sschwarze break; 72449aff9f8Sschwarze case MDOC_Literal: 72531e23753Sschwarze dt = DISP_literal; 726f73abda9Skristaps break; 72749aff9f8Sschwarze case MDOC_File: 728bd594191Sschwarze mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse, 729bd594191Sschwarze n->line, n->pos, NULL); 7301164a325Sschwarze break; 73149aff9f8Sschwarze case MDOC_Offset: 7324a9f685fSschwarze if (0 == argv->sz) { 7334a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_EMPTY, 7344a9f685fSschwarze mdoc->parse, argv->line, 7354a9f685fSschwarze argv->pos, "Bd -offset"); 736f73abda9Skristaps break; 737f73abda9Skristaps } 7384a9f685fSschwarze if (NULL != n->norm->Bd.offs) 7394a9f685fSschwarze mandoc_vmsg(MANDOCERR_ARG_REP, 7404a9f685fSschwarze mdoc->parse, argv->line, 7414a9f685fSschwarze argv->pos, "Bd -offset %s", 7424a9f685fSschwarze argv->value[0]); 7436050a3daSschwarze rewrite_macro2len(mdoc, argv->value); 7444a9f685fSschwarze n->norm->Bd.offs = argv->value[0]; 74531e23753Sschwarze break; 74649aff9f8Sschwarze case MDOC_Compact: 7474a9f685fSschwarze if (n->norm->Bd.comp) 7484a9f685fSschwarze mandoc_msg(MANDOCERR_ARG_REP, 7494a9f685fSschwarze mdoc->parse, argv->line, 7504a9f685fSschwarze argv->pos, "Bd -compact"); 7514a9f685fSschwarze n->norm->Bd.comp = 1; 75231e23753Sschwarze break; 75331e23753Sschwarze default: 75431e23753Sschwarze abort(); 75531e23753Sschwarze } 756dc0d8bb2Sschwarze if (DISP__NONE == dt) 757dc0d8bb2Sschwarze continue; 75831e23753Sschwarze 759dc0d8bb2Sschwarze if (DISP__NONE == n->norm->Bd.type) 7608c62fbf5Sschwarze n->norm->Bd.type = dt; 761dc0d8bb2Sschwarze else 762bd594191Sschwarze mandoc_vmsg(MANDOCERR_BD_REP, 763dc0d8bb2Sschwarze mdoc->parse, n->line, n->pos, 764bd594191Sschwarze "Bd -%s", mdoc_argnames[argv->arg]); 76531e23753Sschwarze } 76631e23753Sschwarze 7678c62fbf5Sschwarze if (DISP__NONE == n->norm->Bd.type) { 768bd594191Sschwarze mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse, 769bd594191Sschwarze n->line, n->pos, "Bd"); 7708c62fbf5Sschwarze n->norm->Bd.type = DISP_ragged; 77131e23753Sschwarze } 772f73abda9Skristaps } 773f73abda9Skristaps 7748ccddcd3Sschwarze /* 7758ccddcd3Sschwarze * Stand-alone line macros. 7768ccddcd3Sschwarze */ 7778ccddcd3Sschwarze 77898b8f00aSschwarze static void 7793e642ba0Sschwarze post_an_norm(POST_ARGS) 780f73abda9Skristaps { 7813e642ba0Sschwarze struct roff_node *n; 782aa99c14fSschwarze struct mdoc_argv *argv; 783aa99c14fSschwarze size_t i; 784f73abda9Skristaps 7853e642ba0Sschwarze n = mdoc->last; 786aa99c14fSschwarze if (n->args == NULL) 78798b8f00aSschwarze return; 788769ee804Sschwarze 789aa99c14fSschwarze for (i = 1; i < n->args->argc; i++) { 790aa99c14fSschwarze argv = n->args->argv + i; 791aa99c14fSschwarze mandoc_vmsg(MANDOCERR_AN_REP, 792aa99c14fSschwarze mdoc->parse, argv->line, argv->pos, 793aa99c14fSschwarze "An -%s", mdoc_argnames[argv->arg]); 794aa99c14fSschwarze } 7957c2be9f8Sschwarze 796aa99c14fSschwarze argv = n->args->argv; 797aa99c14fSschwarze if (argv->arg == MDOC_Split) 7988c62fbf5Sschwarze n->norm->An.auth = AUTH_split; 799aa99c14fSschwarze else if (argv->arg == MDOC_Nosplit) 8008c62fbf5Sschwarze n->norm->An.auth = AUTH_nosplit; 801769ee804Sschwarze else 802769ee804Sschwarze abort(); 803f73abda9Skristaps } 804f73abda9Skristaps 80598b8f00aSschwarze static void 8068ccddcd3Sschwarze post_eoln(POST_ARGS) 8078ccddcd3Sschwarze { 8088ccddcd3Sschwarze struct roff_node *n; 8098ccddcd3Sschwarze 810bc205043Sschwarze post_useless(mdoc); 8118ccddcd3Sschwarze n = mdoc->last; 8128ccddcd3Sschwarze if (n->child != NULL) 81314a309e3Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line, 81414a309e3Sschwarze n->pos, "%s %s", roff_name[n->tok], n->child->string); 8158ccddcd3Sschwarze 8168ccddcd3Sschwarze while (n->child != NULL) 8178ccddcd3Sschwarze roff_node_delete(mdoc, n->child); 8188ccddcd3Sschwarze 8198ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? 8208ccddcd3Sschwarze "is currently in beta test." : "currently under development."); 8218ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 8228ccddcd3Sschwarze mdoc->last = n; 8238ccddcd3Sschwarze } 8248ccddcd3Sschwarze 8258ccddcd3Sschwarze static int 8268ccddcd3Sschwarze build_list(struct roff_man *mdoc, int tok) 8278ccddcd3Sschwarze { 8288ccddcd3Sschwarze struct roff_node *n; 8298ccddcd3Sschwarze int ic; 8308ccddcd3Sschwarze 8318ccddcd3Sschwarze n = mdoc->last->next; 8328ccddcd3Sschwarze for (ic = 1;; ic++) { 8338ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, tok); 8348ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8358ccddcd3Sschwarze mdoc_node_relink(mdoc, n); 8368ccddcd3Sschwarze n = mdoc->last = mdoc->last->parent; 8378ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 8388ccddcd3Sschwarze if (n->next == NULL) 8398ccddcd3Sschwarze return ic; 8408ccddcd3Sschwarze if (ic > 1 || n->next->next != NULL) { 8418ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, ","); 8428ccddcd3Sschwarze mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC; 8438ccddcd3Sschwarze } 8448ccddcd3Sschwarze n = mdoc->last->next; 8458ccddcd3Sschwarze if (n->next == NULL) { 8468ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "and"); 8478ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8488ccddcd3Sschwarze } 8498ccddcd3Sschwarze } 8508ccddcd3Sschwarze } 8518ccddcd3Sschwarze 8528ccddcd3Sschwarze static void 8538ccddcd3Sschwarze post_ex(POST_ARGS) 8548ccddcd3Sschwarze { 8558ccddcd3Sschwarze struct roff_node *n; 8568ccddcd3Sschwarze int ic; 8578ccddcd3Sschwarze 8588ccddcd3Sschwarze post_std(mdoc); 8598ccddcd3Sschwarze 8608ccddcd3Sschwarze n = mdoc->last; 8618ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 8628ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The"); 8638ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8648ccddcd3Sschwarze 8658ccddcd3Sschwarze if (mdoc->last->next != NULL) 8668ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Nm); 8678ccddcd3Sschwarze else if (mdoc->meta.name != NULL) { 8688ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); 8698ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8708ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 8718ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8728ccddcd3Sschwarze mdoc->last = mdoc->last->parent; 8738ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 8748ccddcd3Sschwarze ic = 1; 8758ccddcd3Sschwarze } else { 8768ccddcd3Sschwarze mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse, 8778ccddcd3Sschwarze n->line, n->pos, "Ex"); 8788ccddcd3Sschwarze ic = 0; 8798ccddcd3Sschwarze } 8808ccddcd3Sschwarze 8818ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 8828ccddcd3Sschwarze ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); 8838ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 8848ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 8858ccddcd3Sschwarze "on success, and\\~>0 if an error occurs."); 8868ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 8878ccddcd3Sschwarze mdoc->last = n; 8888ccddcd3Sschwarze } 8898ccddcd3Sschwarze 8908ccddcd3Sschwarze static void 8918ccddcd3Sschwarze post_lb(POST_ARGS) 8928ccddcd3Sschwarze { 8938ccddcd3Sschwarze struct roff_node *n; 8948ccddcd3Sschwarze 89504fbb99fSschwarze post_delim(mdoc); 89604fbb99fSschwarze 8978ccddcd3Sschwarze n = mdoc->last; 8988ccddcd3Sschwarze assert(n->child->type == ROFFT_TEXT); 8998ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 9008ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "library"); 9018ccddcd3Sschwarze mdoc->last->flags = NODE_NOSRC; 9028ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq"); 9038ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 9048ccddcd3Sschwarze mdoc->last = mdoc->last->next; 9058ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq"); 9068ccddcd3Sschwarze mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; 9078ccddcd3Sschwarze mdoc->last = n; 9088ccddcd3Sschwarze } 9098ccddcd3Sschwarze 9108ccddcd3Sschwarze static void 9118ccddcd3Sschwarze post_rv(POST_ARGS) 9128ccddcd3Sschwarze { 9138ccddcd3Sschwarze struct roff_node *n; 9148ccddcd3Sschwarze int ic; 9158ccddcd3Sschwarze 9168ccddcd3Sschwarze post_std(mdoc); 9178ccddcd3Sschwarze 9188ccddcd3Sschwarze n = mdoc->last; 9198ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 9208ccddcd3Sschwarze if (n->child != NULL) { 9218ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "The"); 9228ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9238ccddcd3Sschwarze ic = build_list(mdoc, MDOC_Fn); 9248ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 9258ccddcd3Sschwarze ic > 1 ? "functions return" : "function returns"); 9268ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9278ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 9288ccddcd3Sschwarze "the value\\~0 if successful;"); 9298ccddcd3Sschwarze } else 9308ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " 9318ccddcd3Sschwarze "completion, the value\\~0 is returned;"); 9328ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9338ccddcd3Sschwarze 9348ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "otherwise " 9358ccddcd3Sschwarze "the value\\~\\-1 is returned and the global variable"); 9368ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9378ccddcd3Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); 9388ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9398ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "errno"); 9408ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9418ccddcd3Sschwarze mdoc->last = mdoc->last->parent; 9428ccddcd3Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 9438ccddcd3Sschwarze roff_word_alloc(mdoc, n->line, n->pos, 9448ccddcd3Sschwarze "is set to indicate the error."); 9458ccddcd3Sschwarze mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 9468ccddcd3Sschwarze mdoc->last = n; 9478ccddcd3Sschwarze } 9488ccddcd3Sschwarze 9498ccddcd3Sschwarze static void 9503e642ba0Sschwarze post_std(POST_ARGS) 951f73abda9Skristaps { 9523e642ba0Sschwarze struct roff_node *n; 953f73abda9Skristaps 9543e642ba0Sschwarze n = mdoc->last; 9553e642ba0Sschwarze if (n->args && n->args->argc == 1) 9563e642ba0Sschwarze if (n->args->argv[0].arg == MDOC_Std) 95798b8f00aSschwarze return; 958f73abda9Skristaps 95966788495Sschwarze mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, 96014a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 9616093755cSschwarze } 9626093755cSschwarze 96398b8f00aSschwarze static void 9648ccddcd3Sschwarze post_st(POST_ARGS) 9658ccddcd3Sschwarze { 9668ccddcd3Sschwarze struct roff_node *n, *nch; 9678ccddcd3Sschwarze const char *p; 9688ccddcd3Sschwarze 9698ccddcd3Sschwarze n = mdoc->last; 9708ccddcd3Sschwarze nch = n->child; 9718ccddcd3Sschwarze assert(nch->type == ROFFT_TEXT); 9728ccddcd3Sschwarze 9738ccddcd3Sschwarze if ((p = mdoc_a2st(nch->string)) == NULL) { 9748ccddcd3Sschwarze mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse, 9758ccddcd3Sschwarze nch->line, nch->pos, "St %s", nch->string); 9768ccddcd3Sschwarze roff_node_delete(mdoc, n); 9778ccddcd3Sschwarze return; 9788ccddcd3Sschwarze } 9798ccddcd3Sschwarze 9808ccddcd3Sschwarze nch->flags |= NODE_NOPRT; 9818ccddcd3Sschwarze mdoc->next = ROFF_NEXT_CHILD; 9828ccddcd3Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, p); 9838ccddcd3Sschwarze mdoc->last->flags |= NODE_NOSRC; 9848ccddcd3Sschwarze mdoc->last= n; 9858ccddcd3Sschwarze } 9868ccddcd3Sschwarze 9878ccddcd3Sschwarze static void 9883e642ba0Sschwarze post_obsolete(POST_ARGS) 989551cd4a8Sschwarze { 9903e642ba0Sschwarze struct roff_node *n; 991551cd4a8Sschwarze 9923e642ba0Sschwarze n = mdoc->last; 993d1982c71Sschwarze if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 994551cd4a8Sschwarze mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, 99514a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 996551cd4a8Sschwarze } 997551cd4a8Sschwarze 998bc205043Sschwarze static void 999bc205043Sschwarze post_useless(POST_ARGS) 1000bc205043Sschwarze { 1001bc205043Sschwarze struct roff_node *n; 1002bc205043Sschwarze 1003bc205043Sschwarze n = mdoc->last; 1004bc205043Sschwarze mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse, 1005bc205043Sschwarze n->line, n->pos, roff_name[n->tok]); 1006bc205043Sschwarze } 1007bc205043Sschwarze 10088ccddcd3Sschwarze /* 10098ccddcd3Sschwarze * Block macros. 10108ccddcd3Sschwarze */ 10118ccddcd3Sschwarze 101298b8f00aSschwarze static void 1013f73abda9Skristaps post_bf(POST_ARGS) 1014f73abda9Skristaps { 10153a0d07afSschwarze struct roff_node *np, *nch; 1016f73abda9Skristaps 1017769ee804Sschwarze /* 1018769ee804Sschwarze * Unlike other data pointers, these are "housed" by the HEAD 1019769ee804Sschwarze * element, which contains the goods. 1020769ee804Sschwarze */ 1021769ee804Sschwarze 1022769ee804Sschwarze np = mdoc->last; 1023d1982c71Sschwarze if (np->type != ROFFT_HEAD) 1024ae2efdd8Sschwarze return; 1025ae2efdd8Sschwarze 1026d1982c71Sschwarze assert(np->parent->type == ROFFT_BLOCK); 1027f051602aSschwarze assert(np->parent->tok == MDOC_Bf); 102850d41253Sschwarze 1029ecb10c32Sschwarze /* Check the number of arguments. */ 1030f73abda9Skristaps 1031ecb10c32Sschwarze nch = np->child; 1032f051602aSschwarze if (np->parent->args == NULL) { 1033f051602aSschwarze if (nch == NULL) { 1034bd594191Sschwarze mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse, 1035bd594191Sschwarze np->line, np->pos, "Bf"); 103698b8f00aSschwarze return; 103720fa2881Sschwarze } 1038ecb10c32Sschwarze nch = nch->next; 1039ecb10c32Sschwarze } 1040f051602aSschwarze if (nch != NULL) 1041ecb10c32Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1042ecb10c32Sschwarze nch->line, nch->pos, "Bf ... %s", nch->string); 1043769ee804Sschwarze 1044769ee804Sschwarze /* Extract argument into data. */ 1045769ee804Sschwarze 1046f051602aSschwarze if (np->parent->args != NULL) { 1047f051602aSschwarze switch (np->parent->args->argv[0].arg) { 1048f051602aSschwarze case MDOC_Emphasis: 10498c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1050f051602aSschwarze break; 1051f051602aSschwarze case MDOC_Literal: 10528c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1053f051602aSschwarze break; 1054f051602aSschwarze case MDOC_Symbolic: 10558c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 1056f051602aSschwarze break; 1057f051602aSschwarze default: 1058769ee804Sschwarze abort(); 1059f051602aSschwarze } 106098b8f00aSschwarze return; 1061769ee804Sschwarze } 1062769ee804Sschwarze 1063769ee804Sschwarze /* Extract parameter into data. */ 1064769ee804Sschwarze 1065f051602aSschwarze if ( ! strcmp(np->child->string, "Em")) 10668c62fbf5Sschwarze np->norm->Bf.font = FONT_Em; 1067f051602aSschwarze else if ( ! strcmp(np->child->string, "Li")) 10688c62fbf5Sschwarze np->norm->Bf.font = FONT_Li; 1069f051602aSschwarze else if ( ! strcmp(np->child->string, "Sy")) 10708c62fbf5Sschwarze np->norm->Bf.font = FONT_Sy; 107120fa2881Sschwarze else 1072ecb10c32Sschwarze mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse, 1073ecb10c32Sschwarze np->child->line, np->child->pos, 1074ecb10c32Sschwarze "Bf %s", np->child->string); 1075f73abda9Skristaps } 1076f73abda9Skristaps 107798b8f00aSschwarze static void 10780c5064e3Sschwarze post_fname(POST_ARGS) 10790c5064e3Sschwarze { 10803a0d07afSschwarze const struct roff_node *n; 10810ff14c71Sschwarze const char *cp; 10820c5064e3Sschwarze size_t pos; 10830c5064e3Sschwarze 10840c5064e3Sschwarze n = mdoc->last->child; 10850c5064e3Sschwarze pos = strcspn(n->string, "()"); 10860ff14c71Sschwarze cp = n->string + pos; 10870ff14c71Sschwarze if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) 10880c5064e3Sschwarze mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse, 10890c5064e3Sschwarze n->line, n->pos + pos, n->string); 10900c5064e3Sschwarze } 10910c5064e3Sschwarze 109298b8f00aSschwarze static void 10930c5064e3Sschwarze post_fn(POST_ARGS) 10940c5064e3Sschwarze { 10950c5064e3Sschwarze 10960c5064e3Sschwarze post_fname(mdoc); 10970c5064e3Sschwarze post_fa(mdoc); 10980c5064e3Sschwarze } 10990c5064e3Sschwarze 110098b8f00aSschwarze static void 1101753701eeSschwarze post_fo(POST_ARGS) 1102753701eeSschwarze { 11033a0d07afSschwarze const struct roff_node *n; 1104753701eeSschwarze 1105afcd1f03Sschwarze n = mdoc->last; 1106afcd1f03Sschwarze 1107d1982c71Sschwarze if (n->type != ROFFT_HEAD) 1108afcd1f03Sschwarze return; 1109afcd1f03Sschwarze 1110afcd1f03Sschwarze if (n->child == NULL) { 1111afcd1f03Sschwarze mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse, 1112afcd1f03Sschwarze n->line, n->pos, "Fo"); 1113afcd1f03Sschwarze return; 1114afcd1f03Sschwarze } 1115afcd1f03Sschwarze if (n->child != n->last) { 1116afcd1f03Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1117afcd1f03Sschwarze n->child->next->line, n->child->next->pos, 1118afcd1f03Sschwarze "Fo ... %s", n->child->next->string); 1119afcd1f03Sschwarze while (n->child != n->last) 1120fa2127f9Sschwarze roff_node_delete(mdoc, n->last); 1121afcd1f03Sschwarze } 1122afcd1f03Sschwarze 11230c5064e3Sschwarze post_fname(mdoc); 1124753701eeSschwarze } 1125753701eeSschwarze 112698b8f00aSschwarze static void 11277e92c062Sschwarze post_fa(POST_ARGS) 11287e92c062Sschwarze { 11293a0d07afSschwarze const struct roff_node *n; 11307e92c062Sschwarze const char *cp; 11317e92c062Sschwarze 11327e92c062Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) { 11337e92c062Sschwarze for (cp = n->string; *cp != '\0'; cp++) { 11347e92c062Sschwarze /* Ignore callbacks and alterations. */ 11357e92c062Sschwarze if (*cp == '(' || *cp == '{') 11367e92c062Sschwarze break; 11377e92c062Sschwarze if (*cp != ',') 11387e92c062Sschwarze continue; 11397e92c062Sschwarze mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse, 11407e92c062Sschwarze n->line, n->pos + (cp - n->string), 11417e92c062Sschwarze n->string); 11427e92c062Sschwarze break; 11437e92c062Sschwarze } 11447e92c062Sschwarze } 114504fbb99fSschwarze post_delim(mdoc); 11467e92c062Sschwarze } 11477e92c062Sschwarze 114898b8f00aSschwarze static void 1149f73abda9Skristaps post_nm(POST_ARGS) 1150f73abda9Skristaps { 11513a0d07afSschwarze struct roff_node *n; 11522d266539Sschwarze 11532d266539Sschwarze n = mdoc->last; 11542d266539Sschwarze 11552d266539Sschwarze if (n->last != NULL && 11562d266539Sschwarze (n->last->tok == MDOC_Pp || 11572d266539Sschwarze n->last->tok == MDOC_Lp)) 11582d266539Sschwarze mdoc_node_relink(mdoc, n->last); 115920fa2881Sschwarze 1160f27faaccSschwarze if (mdoc->meta.name == NULL) 1161423631c9Sschwarze deroff(&mdoc->meta.name, n); 116220fa2881Sschwarze 1163f27faaccSschwarze if (mdoc->meta.name == NULL || 1164f27faaccSschwarze (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1165bd594191Sschwarze mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, 11662d266539Sschwarze n->line, n->pos, "Nm"); 116790957cf5Sschwarze 116804fbb99fSschwarze if (n->type == ROFFT_ELEM) 116904fbb99fSschwarze post_delim(mdoc); 117004fbb99fSschwarze 117190957cf5Sschwarze if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) || 117290957cf5Sschwarze (n->child != NULL && n->child->type == ROFFT_TEXT) || 117390957cf5Sschwarze mdoc->meta.name == NULL) 117490957cf5Sschwarze return; 117590957cf5Sschwarze 117690957cf5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 117790957cf5Sschwarze roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 117890957cf5Sschwarze mdoc->last->flags |= NODE_NOSRC; 117990957cf5Sschwarze mdoc->last = n; 118020fa2881Sschwarze } 118120fa2881Sschwarze 118298b8f00aSschwarze static void 1183753701eeSschwarze post_nd(POST_ARGS) 1184753701eeSschwarze { 11853a0d07afSschwarze struct roff_node *n; 1186648df85bSschwarze size_t sz; 1187753701eeSschwarze 11881570daf1Sschwarze n = mdoc->last; 11891570daf1Sschwarze 1190d1982c71Sschwarze if (n->type != ROFFT_BODY) 11911570daf1Sschwarze return; 11921570daf1Sschwarze 119356e9e976Sschwarze if (n->sec != SEC_NAME) 119456e9e976Sschwarze mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse, 119556e9e976Sschwarze n->line, n->pos, "Nd"); 119656e9e976Sschwarze 11971570daf1Sschwarze if (n->child == NULL) 11981570daf1Sschwarze mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, 11991570daf1Sschwarze n->line, n->pos, "Nd"); 1200648df85bSschwarze else if (n->last->type == ROFFT_TEXT && 1201648df85bSschwarze (sz = strlen(n->last->string)) != 0 && 1202648df85bSschwarze n->last->string[sz - 1] == '.') 1203648df85bSschwarze mandoc_msg(MANDOCERR_ND_DOT, mdoc->parse, 1204648df85bSschwarze n->last->line, n->last->pos + sz - 1, NULL); 12051570daf1Sschwarze 120698b8f00aSschwarze post_hyph(mdoc); 1207753701eeSschwarze } 1208753701eeSschwarze 120998b8f00aSschwarze static void 12103e642ba0Sschwarze post_display(POST_ARGS) 1211753701eeSschwarze { 12123e642ba0Sschwarze struct roff_node *n, *np; 1213753701eeSschwarze 1214b7530f2fSschwarze n = mdoc->last; 12153e642ba0Sschwarze switch (n->type) { 12163e642ba0Sschwarze case ROFFT_BODY: 12174b5c4138Sschwarze if (n->end != ENDBODY_NOT) { 1218611d9138Sschwarze if (n->tok == MDOC_Bd && 1219611d9138Sschwarze n->body->parent->args == NULL) 12204b5c4138Sschwarze roff_node_delete(mdoc, n); 12214b5c4138Sschwarze } else if (n->child == NULL) 12221d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 122314a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 12243e642ba0Sschwarze else if (n->tok == MDOC_D1) 12253e642ba0Sschwarze post_hyph(mdoc); 12263e642ba0Sschwarze break; 12273e642ba0Sschwarze case ROFFT_BLOCK: 12283e642ba0Sschwarze if (n->tok == MDOC_Bd) { 1229a43e24e2Sschwarze if (n->args == NULL) { 1230a43e24e2Sschwarze mandoc_msg(MANDOCERR_BD_NOARG, 1231a43e24e2Sschwarze mdoc->parse, n->line, n->pos, "Bd"); 1232a43e24e2Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1233a43e24e2Sschwarze while (n->body->child != NULL) 1234a43e24e2Sschwarze mdoc_node_relink(mdoc, 1235a43e24e2Sschwarze n->body->child); 1236a43e24e2Sschwarze roff_node_delete(mdoc, n); 1237a43e24e2Sschwarze break; 1238a43e24e2Sschwarze } 12393e642ba0Sschwarze post_bd(mdoc); 12403e642ba0Sschwarze post_prevpar(mdoc); 12413e642ba0Sschwarze } 12423e642ba0Sschwarze for (np = n->parent; np != NULL; np = np->parent) { 12433e642ba0Sschwarze if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 12443e642ba0Sschwarze mandoc_vmsg(MANDOCERR_BD_NEST, 12453e642ba0Sschwarze mdoc->parse, n->line, n->pos, 124614a309e3Sschwarze "%s in Bd", roff_name[n->tok]); 12473e642ba0Sschwarze break; 12483e642ba0Sschwarze } 12493e642ba0Sschwarze } 12503e642ba0Sschwarze break; 12513e642ba0Sschwarze default: 12523e642ba0Sschwarze break; 12533e642ba0Sschwarze } 125420fa2881Sschwarze } 125520fa2881Sschwarze 125698b8f00aSschwarze static void 125720fa2881Sschwarze post_defaults(POST_ARGS) 125820fa2881Sschwarze { 12593a0d07afSschwarze struct roff_node *nn; 126020fa2881Sschwarze 126104fbb99fSschwarze if (mdoc->last->child != NULL) { 126204fbb99fSschwarze post_delim(mdoc); 126304fbb99fSschwarze return; 126404fbb99fSschwarze } 126504fbb99fSschwarze 126620fa2881Sschwarze /* 126720fa2881Sschwarze * The `Ar' defaults to "file ..." if no value is provided as an 126820fa2881Sschwarze * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 126920fa2881Sschwarze * gets an empty string. 127020fa2881Sschwarze */ 1271f73abda9Skristaps 127220fa2881Sschwarze nn = mdoc->last; 127320fa2881Sschwarze switch (nn->tok) { 127449aff9f8Sschwarze case MDOC_Ar: 1275396853b5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 127669c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "file"); 127743808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 127869c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "..."); 127943808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 128020fa2881Sschwarze break; 128149aff9f8Sschwarze case MDOC_Pa: 128249aff9f8Sschwarze case MDOC_Mt: 1283396853b5Sschwarze mdoc->next = ROFF_NEXT_CHILD; 128469c34eaaSschwarze roff_word_alloc(mdoc, nn->line, nn->pos, "~"); 128543808411Sschwarze mdoc->last->flags |= NODE_NOSRC; 128620fa2881Sschwarze break; 128720fa2881Sschwarze default: 128820fa2881Sschwarze abort(); 1289f73abda9Skristaps } 129020fa2881Sschwarze mdoc->last = nn; 129120fa2881Sschwarze } 1292f73abda9Skristaps 129398b8f00aSschwarze static void 1294f73abda9Skristaps post_at(POST_ARGS) 1295f73abda9Skristaps { 12963af8e8d7Sschwarze struct roff_node *n, *nch; 12973af8e8d7Sschwarze const char *att; 129820fa2881Sschwarze 1299753701eeSschwarze n = mdoc->last; 13003af8e8d7Sschwarze nch = n->child; 1301753701eeSschwarze 130220fa2881Sschwarze /* 130320fa2881Sschwarze * If we have a child, look it up in the standard keys. If a 130420fa2881Sschwarze * key exist, use that instead of the child; if it doesn't, 130520fa2881Sschwarze * prefix "AT&T UNIX " to the existing data. 130620fa2881Sschwarze */ 1307f73abda9Skristaps 13083af8e8d7Sschwarze att = NULL; 13093af8e8d7Sschwarze if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1310bd594191Sschwarze mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, 13113af8e8d7Sschwarze nch->line, nch->pos, "At %s", nch->string); 1312f73abda9Skristaps 13133af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD; 13143af8e8d7Sschwarze if (att != NULL) { 13153af8e8d7Sschwarze roff_word_alloc(mdoc, nch->line, nch->pos, att); 13163af8e8d7Sschwarze nch->flags |= NODE_NOPRT; 13173af8e8d7Sschwarze } else 13183af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 13193af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 13203af8e8d7Sschwarze mdoc->last = n; 132120fa2881Sschwarze } 1322f73abda9Skristaps 132398b8f00aSschwarze static void 1324f73abda9Skristaps post_an(POST_ARGS) 1325f73abda9Skristaps { 13263a0d07afSschwarze struct roff_node *np, *nch; 1327f73abda9Skristaps 13283e642ba0Sschwarze post_an_norm(mdoc); 13293e642ba0Sschwarze 1330769ee804Sschwarze np = mdoc->last; 1331cba50636Sschwarze nch = np->child; 1332cba50636Sschwarze if (np->norm->An.auth == AUTH__NONE) { 1333cba50636Sschwarze if (nch == NULL) 1334cba50636Sschwarze mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 1335cba50636Sschwarze np->line, np->pos, "An"); 133604fbb99fSschwarze else 133704fbb99fSschwarze post_delim(mdoc); 1338cba50636Sschwarze } else if (nch != NULL) 13393798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 13403798fb25Sschwarze nch->line, nch->pos, "An ... %s", nch->string); 1341f73abda9Skristaps } 1342f73abda9Skristaps 134398b8f00aSschwarze static void 1344551cd4a8Sschwarze post_en(POST_ARGS) 1345551cd4a8Sschwarze { 1346551cd4a8Sschwarze 13473e642ba0Sschwarze post_obsolete(mdoc); 1348d1982c71Sschwarze if (mdoc->last->type == ROFFT_BLOCK) 1349551cd4a8Sschwarze mdoc->last->norm->Es = mdoc->last_es; 1350551cd4a8Sschwarze } 1351551cd4a8Sschwarze 135298b8f00aSschwarze static void 1353551cd4a8Sschwarze post_es(POST_ARGS) 1354551cd4a8Sschwarze { 1355551cd4a8Sschwarze 13563e642ba0Sschwarze post_obsolete(mdoc); 1357551cd4a8Sschwarze mdoc->last_es = mdoc->last; 1358551cd4a8Sschwarze } 1359551cd4a8Sschwarze 136098b8f00aSschwarze static void 1361816c3c54Sschwarze post_xx(POST_ARGS) 1362816c3c54Sschwarze { 1363816c3c54Sschwarze struct roff_node *n; 1364816c3c54Sschwarze const char *os; 1365816c3c54Sschwarze 136604fbb99fSschwarze post_delim(mdoc); 136704fbb99fSschwarze 1368816c3c54Sschwarze n = mdoc->last; 1369816c3c54Sschwarze switch (n->tok) { 1370816c3c54Sschwarze case MDOC_Bsx: 1371816c3c54Sschwarze os = "BSD/OS"; 1372816c3c54Sschwarze break; 1373816c3c54Sschwarze case MDOC_Dx: 1374816c3c54Sschwarze os = "DragonFly"; 1375816c3c54Sschwarze break; 1376816c3c54Sschwarze case MDOC_Fx: 1377816c3c54Sschwarze os = "FreeBSD"; 1378816c3c54Sschwarze break; 1379816c3c54Sschwarze case MDOC_Nx: 1380816c3c54Sschwarze os = "NetBSD"; 1381816c3c54Sschwarze break; 1382816c3c54Sschwarze case MDOC_Ox: 1383816c3c54Sschwarze os = "OpenBSD"; 1384816c3c54Sschwarze break; 1385816c3c54Sschwarze case MDOC_Ux: 1386816c3c54Sschwarze os = "UNIX"; 1387816c3c54Sschwarze break; 1388816c3c54Sschwarze default: 1389816c3c54Sschwarze abort(); 1390816c3c54Sschwarze } 1391816c3c54Sschwarze mdoc->next = ROFF_NEXT_CHILD; 1392816c3c54Sschwarze roff_word_alloc(mdoc, n->line, n->pos, os); 1393816c3c54Sschwarze mdoc->last->flags |= NODE_NOSRC; 1394816c3c54Sschwarze mdoc->last = n; 1395816c3c54Sschwarze } 1396816c3c54Sschwarze 1397816c3c54Sschwarze static void 1398f73abda9Skristaps post_it(POST_ARGS) 1399f73abda9Skristaps { 14003a0d07afSschwarze struct roff_node *nbl, *nit, *nch; 140119a69263Sschwarze int i, cols; 14026093755cSschwarze enum mdoc_list lt; 1403f73abda9Skristaps 14043e642ba0Sschwarze post_prevpar(mdoc); 14053e642ba0Sschwarze 14069530682eSschwarze nit = mdoc->last; 1407d1982c71Sschwarze if (nit->type != ROFFT_BLOCK) 140898b8f00aSschwarze return; 1409f73abda9Skristaps 14109530682eSschwarze nbl = nit->parent->parent; 14119530682eSschwarze lt = nbl->norm->Bl.type; 14126093755cSschwarze 14136093755cSschwarze switch (lt) { 141449aff9f8Sschwarze case LIST_tag: 141549aff9f8Sschwarze case LIST_hang: 141649aff9f8Sschwarze case LIST_ohang: 141749aff9f8Sschwarze case LIST_inset: 141849aff9f8Sschwarze case LIST_diag: 1419d26e35c2Sschwarze if (nit->head->child == NULL) 1420bd594191Sschwarze mandoc_vmsg(MANDOCERR_IT_NOHEAD, 14219530682eSschwarze mdoc->parse, nit->line, nit->pos, 1422bd594191Sschwarze "Bl -%s It", 14239530682eSschwarze mdoc_argnames[nbl->args->argv[0].arg]); 1424f73abda9Skristaps break; 142549aff9f8Sschwarze case LIST_bullet: 142649aff9f8Sschwarze case LIST_dash: 142749aff9f8Sschwarze case LIST_enum: 142849aff9f8Sschwarze case LIST_hyphen: 1429d26e35c2Sschwarze if (nit->body == NULL || nit->body->child == NULL) 1430bd594191Sschwarze mandoc_vmsg(MANDOCERR_IT_NOBODY, 143166788495Sschwarze mdoc->parse, nit->line, nit->pos, 1432bd594191Sschwarze "Bl -%s It", 143366788495Sschwarze mdoc_argnames[nbl->args->argv[0].arg]); 1434f73abda9Skristaps /* FALLTHROUGH */ 143549aff9f8Sschwarze case LIST_item: 143625f8eeb1Sschwarze if ((nch = nit->head->child) != NULL) 143714a309e3Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, 143814a309e3Sschwarze nit->line, nit->pos, "It %s", 143914a309e3Sschwarze nch->string == NULL ? roff_name[nch->tok] : 144014a309e3Sschwarze nch->string); 1441f73abda9Skristaps break; 144249aff9f8Sschwarze case LIST_column: 14439530682eSschwarze cols = (int)nbl->norm->Bl.ncols; 14446093755cSschwarze 1445d26e35c2Sschwarze assert(nit->head->child == NULL); 14466093755cSschwarze 1447f051602aSschwarze i = 0; 1448f051602aSschwarze for (nch = nit->child; nch != NULL; nch = nch->next) 1449d1982c71Sschwarze if (nch->type == ROFFT_BODY) 1450f73abda9Skristaps i++; 145153292e81Sschwarze 1452e14c4c11Sschwarze if (i < cols || i > cols + 1) 1453bce599dfSschwarze mandoc_vmsg(MANDOCERR_BL_COL, 1454e14c4c11Sschwarze mdoc->parse, nit->line, nit->pos, 1455bce599dfSschwarze "%d columns, %d cells", cols, i); 1456e14c4c11Sschwarze break; 1457f73abda9Skristaps default: 145866788495Sschwarze abort(); 1459f73abda9Skristaps } 1460f73abda9Skristaps } 1461f73abda9Skristaps 146298b8f00aSschwarze static void 146320fa2881Sschwarze post_bl_block(POST_ARGS) 146420fa2881Sschwarze { 14653a0d07afSschwarze struct roff_node *n, *ni, *nc; 146620fa2881Sschwarze 14673e642ba0Sschwarze post_prevpar(mdoc); 14683e642ba0Sschwarze 146920fa2881Sschwarze n = mdoc->last; 1470f051602aSschwarze for (ni = n->body->child; ni != NULL; ni = ni->next) { 1471f051602aSschwarze if (ni->body == NULL) 1472bb99f0faSschwarze continue; 1473bb99f0faSschwarze nc = ni->body->last; 1474f051602aSschwarze while (nc != NULL) { 1475bb99f0faSschwarze switch (nc->tok) { 147649aff9f8Sschwarze case MDOC_Pp: 147749aff9f8Sschwarze case MDOC_Lp: 147829478532Sschwarze case ROFF_br: 1479bb99f0faSschwarze break; 1480bb99f0faSschwarze default: 1481bb99f0faSschwarze nc = NULL; 1482bb99f0faSschwarze continue; 1483bb99f0faSschwarze } 1484f051602aSschwarze if (ni->next == NULL) { 148520369664Sschwarze mandoc_msg(MANDOCERR_PAR_MOVE, 148620369664Sschwarze mdoc->parse, nc->line, nc->pos, 148714a309e3Sschwarze roff_name[nc->tok]); 148898b8f00aSschwarze mdoc_node_relink(mdoc, nc); 1489f051602aSschwarze } else if (n->norm->Bl.comp == 0 && 1490f051602aSschwarze n->norm->Bl.type != LIST_column) { 149120369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 149220369664Sschwarze mdoc->parse, nc->line, nc->pos, 149314a309e3Sschwarze "%s before It", roff_name[nc->tok]); 1494fa2127f9Sschwarze roff_node_delete(mdoc, nc); 1495bb99f0faSschwarze } else 1496bb99f0faSschwarze break; 1497bb99f0faSschwarze nc = ni->body->last; 1498bb99f0faSschwarze } 1499bb99f0faSschwarze } 150020fa2881Sschwarze } 150120fa2881Sschwarze 150290d52a15Sschwarze /* 150390d52a15Sschwarze * If the argument of -offset or -width is a macro, 150490d52a15Sschwarze * replace it with the associated default width. 150590d52a15Sschwarze */ 15066050a3daSschwarze static void 15076050a3daSschwarze rewrite_macro2len(struct roff_man *mdoc, char **arg) 150820fa2881Sschwarze { 150920fa2881Sschwarze size_t width; 151014a309e3Sschwarze enum roff_tok tok; 151120fa2881Sschwarze 151290d52a15Sschwarze if (*arg == NULL) 151390d52a15Sschwarze return; 151490d52a15Sschwarze else if ( ! strcmp(*arg, "Ds")) 151520fa2881Sschwarze width = 6; 15166050a3daSschwarze else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) 151790d52a15Sschwarze return; 1518dc0d8bb2Sschwarze else 1519dc0d8bb2Sschwarze width = macro2len(tok); 152020fa2881Sschwarze 152190d52a15Sschwarze free(*arg); 152290d52a15Sschwarze mandoc_asprintf(arg, "%zun", width); 152320fa2881Sschwarze } 152420fa2881Sschwarze 152598b8f00aSschwarze static void 1526395185ccSschwarze post_bl_head(POST_ARGS) 1527395185ccSschwarze { 15283a0d07afSschwarze struct roff_node *nbl, *nh, *nch, *nnext; 1529f5174743Sschwarze struct mdoc_argv *argv; 153020fa2881Sschwarze int i, j; 1531395185ccSschwarze 15323e642ba0Sschwarze post_bl_norm(mdoc); 15332588c917Sschwarze 15343e642ba0Sschwarze nh = mdoc->last; 15352588c917Sschwarze if (nh->norm->Bl.type != LIST_column) { 15362588c917Sschwarze if ((nch = nh->child) == NULL) 15372588c917Sschwarze return; 15382588c917Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 15392588c917Sschwarze nch->line, nch->pos, "Bl ... %s", nch->string); 15402588c917Sschwarze while (nch != NULL) { 1541fa2127f9Sschwarze roff_node_delete(mdoc, nch); 15422588c917Sschwarze nch = nh->child; 15432588c917Sschwarze } 154498b8f00aSschwarze return; 154598b8f00aSschwarze } 1546395185ccSschwarze 154720fa2881Sschwarze /* 1548f5174743Sschwarze * Append old-style lists, where the column width specifiers 154920fa2881Sschwarze * trail as macro parameters, to the new-style ("normal-form") 155020fa2881Sschwarze * lists where they're argument values following -column. 155120fa2881Sschwarze */ 155220fa2881Sschwarze 15532588c917Sschwarze if (nh->child == NULL) 155498b8f00aSschwarze return; 155520fa2881Sschwarze 15562588c917Sschwarze nbl = nh->parent; 15572588c917Sschwarze for (j = 0; j < (int)nbl->args->argc; j++) 15582588c917Sschwarze if (nbl->args->argv[j].arg == MDOC_Column) 155920fa2881Sschwarze break; 156020fa2881Sschwarze 15612588c917Sschwarze assert(j < (int)nbl->args->argc); 156220fa2881Sschwarze 156320fa2881Sschwarze /* 1564a5e11edeSschwarze * Accommodate for new-style groff column syntax. Shuffle the 156520fa2881Sschwarze * child nodes, all of which must be TEXT, as arguments for the 156620fa2881Sschwarze * column field. Then, delete the head children. 156720fa2881Sschwarze */ 156820fa2881Sschwarze 15692588c917Sschwarze argv = nbl->args->argv + j; 1570f5174743Sschwarze i = argv->sz; 157130e5ee06Sschwarze for (nch = nh->child; nch != NULL; nch = nch->next) 157230e5ee06Sschwarze argv->sz++; 1573f5174743Sschwarze argv->value = mandoc_reallocarray(argv->value, 1574f5174743Sschwarze argv->sz, sizeof(char *)); 157520fa2881Sschwarze 15762588c917Sschwarze nh->norm->Bl.ncols = argv->sz; 15772588c917Sschwarze nh->norm->Bl.cols = (void *)argv->value; 157820fa2881Sschwarze 15792588c917Sschwarze for (nch = nh->child; nch != NULL; nch = nnext) { 15802588c917Sschwarze argv->value[i++] = nch->string; 15812588c917Sschwarze nch->string = NULL; 15822588c917Sschwarze nnext = nch->next; 1583fa2127f9Sschwarze roff_node_delete(NULL, nch); 1584395185ccSschwarze } 15852588c917Sschwarze nh->child = NULL; 1586b16e7ddfSschwarze } 1587b16e7ddfSschwarze 158898b8f00aSschwarze static void 1589f73abda9Skristaps post_bl(POST_ARGS) 1590f73abda9Skristaps { 15913a0d07afSschwarze struct roff_node *nparent, *nprev; /* of the Bl block */ 15923a0d07afSschwarze struct roff_node *nblock, *nbody; /* of the Bl */ 15933a0d07afSschwarze struct roff_node *nchild, *nnext; /* of the Bl body */ 1594ce0ef847Sschwarze const char *prev_Er; 1595ce0ef847Sschwarze int order; 1596f73abda9Skristaps 15972a427d60Sschwarze nbody = mdoc->last; 15982a427d60Sschwarze switch (nbody->type) { 1599d1982c71Sschwarze case ROFFT_BLOCK: 160098b8f00aSschwarze post_bl_block(mdoc); 160198b8f00aSschwarze return; 1602d1982c71Sschwarze case ROFFT_HEAD: 160398b8f00aSschwarze post_bl_head(mdoc); 160498b8f00aSschwarze return; 1605d1982c71Sschwarze case ROFFT_BODY: 1606f6127a73Sschwarze break; 16072a427d60Sschwarze default: 160898b8f00aSschwarze return; 1609f6127a73Sschwarze } 1610396853b5Sschwarze if (nbody->end != ENDBODY_NOT) 1611396853b5Sschwarze return; 1612f6127a73Sschwarze 16132a427d60Sschwarze nchild = nbody->child; 1614b7530f2fSschwarze if (nchild == NULL) { 16151d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 1616b7530f2fSschwarze nbody->line, nbody->pos, "Bl"); 1617b7530f2fSschwarze return; 1618b7530f2fSschwarze } 1619b7530f2fSschwarze while (nchild != NULL) { 1620ad7fa6e5Sschwarze nnext = nchild->next; 1621204684a7Sschwarze if (nchild->tok == MDOC_It || 1622204684a7Sschwarze (nchild->tok == MDOC_Sm && 1623ad7fa6e5Sschwarze nnext != NULL && nnext->tok == MDOC_It)) { 1624ad7fa6e5Sschwarze nchild = nnext; 1625ad7fa6e5Sschwarze continue; 1626ad7fa6e5Sschwarze } 1627ad7fa6e5Sschwarze 1628ad7fa6e5Sschwarze /* 1629ad7fa6e5Sschwarze * In .Bl -column, the first rows may be implicit, 1630ad7fa6e5Sschwarze * that is, they may not start with .It macros. 1631ad7fa6e5Sschwarze * Such rows may be followed by nodes generated on the 1632ad7fa6e5Sschwarze * roff level, for example .TS, which cannot be moved 1633ad7fa6e5Sschwarze * out of the list. In that case, wrap such roff nodes 1634ad7fa6e5Sschwarze * into an implicit row. 1635ad7fa6e5Sschwarze */ 1636ad7fa6e5Sschwarze 1637ad7fa6e5Sschwarze if (nchild->prev != NULL) { 1638ad7fa6e5Sschwarze mdoc->last = nchild; 1639ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1640ad7fa6e5Sschwarze roff_block_alloc(mdoc, nchild->line, 1641ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1642ad7fa6e5Sschwarze roff_head_alloc(mdoc, nchild->line, 1643ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1644ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1645ad7fa6e5Sschwarze roff_body_alloc(mdoc, nchild->line, 1646ad7fa6e5Sschwarze nchild->pos, MDOC_It); 1647ad7fa6e5Sschwarze while (nchild->tok != MDOC_It) { 1648ad7fa6e5Sschwarze mdoc_node_relink(mdoc, nchild); 1649ad7fa6e5Sschwarze if ((nchild = nnext) == NULL) 1650ad7fa6e5Sschwarze break; 1651ad7fa6e5Sschwarze nnext = nchild->next; 1652ad7fa6e5Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 1653ad7fa6e5Sschwarze } 1654ad7fa6e5Sschwarze mdoc->last = nbody; 16552a427d60Sschwarze continue; 16562a427d60Sschwarze } 16572a427d60Sschwarze 1658dd25b57cSschwarze mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, 165914a309e3Sschwarze nchild->line, nchild->pos, roff_name[nchild->tok]); 16602a427d60Sschwarze 16612a427d60Sschwarze /* 16622a427d60Sschwarze * Move the node out of the Bl block. 16632a427d60Sschwarze * First, collect all required node pointers. 16642a427d60Sschwarze */ 16652a427d60Sschwarze 16662a427d60Sschwarze nblock = nbody->parent; 16672a427d60Sschwarze nprev = nblock->prev; 16682a427d60Sschwarze nparent = nblock->parent; 16692a427d60Sschwarze 16702a427d60Sschwarze /* 16712a427d60Sschwarze * Unlink this child. 16722a427d60Sschwarze */ 16732a427d60Sschwarze 16742a427d60Sschwarze nbody->child = nnext; 167530e5ee06Sschwarze if (nnext == NULL) 167630e5ee06Sschwarze nbody->last = NULL; 167730e5ee06Sschwarze else 16782a427d60Sschwarze nnext->prev = NULL; 16792a427d60Sschwarze 16802a427d60Sschwarze /* 16812a427d60Sschwarze * Relink this child. 16822a427d60Sschwarze */ 16832a427d60Sschwarze 16842a427d60Sschwarze nchild->parent = nparent; 16852a427d60Sschwarze nchild->prev = nprev; 16862a427d60Sschwarze nchild->next = nblock; 16872a427d60Sschwarze 16882a427d60Sschwarze nblock->prev = nchild; 1689f051602aSschwarze if (nprev == NULL) 16902a427d60Sschwarze nparent->child = nchild; 16912a427d60Sschwarze else 16922a427d60Sschwarze nprev->next = nchild; 16932a427d60Sschwarze 16942a427d60Sschwarze nchild = nnext; 1695f73abda9Skristaps } 1696ce0ef847Sschwarze 1697f3476b07Sschwarze if (mdoc->meta.os_e != MANDOC_OS_NETBSD) 1698ce0ef847Sschwarze return; 1699ce0ef847Sschwarze 1700ce0ef847Sschwarze prev_Er = NULL; 1701ce0ef847Sschwarze for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { 1702ce0ef847Sschwarze if (nchild->tok != MDOC_It) 1703ce0ef847Sschwarze continue; 1704ce0ef847Sschwarze if ((nnext = nchild->head->child) == NULL) 1705ce0ef847Sschwarze continue; 1706ce0ef847Sschwarze if (nnext->type == ROFFT_BLOCK) 1707ce0ef847Sschwarze nnext = nnext->body->child; 1708ce0ef847Sschwarze if (nnext == NULL || nnext->tok != MDOC_Er) 1709ce0ef847Sschwarze continue; 1710ce0ef847Sschwarze nnext = nnext->child; 1711ce0ef847Sschwarze if (prev_Er != NULL) { 1712ce0ef847Sschwarze order = strcmp(prev_Er, nnext->string); 1713ce0ef847Sschwarze if (order > 0) 1714ce0ef847Sschwarze mandoc_vmsg(MANDOCERR_ER_ORDER, 1715ce0ef847Sschwarze mdoc->parse, nnext->line, nnext->pos, 1716f3476b07Sschwarze "Er %s %s (NetBSD)", 1717f3476b07Sschwarze prev_Er, nnext->string); 1718ce0ef847Sschwarze else if (order == 0) 1719ce0ef847Sschwarze mandoc_vmsg(MANDOCERR_ER_REP, 1720ce0ef847Sschwarze mdoc->parse, nnext->line, nnext->pos, 1721f3476b07Sschwarze "Er %s (NetBSD)", prev_Er); 1722ce0ef847Sschwarze } 1723ce0ef847Sschwarze prev_Er = nnext->string; 1724ce0ef847Sschwarze } 1725f73abda9Skristaps } 1726f73abda9Skristaps 172798b8f00aSschwarze static void 1728753701eeSschwarze post_bk(POST_ARGS) 1729753701eeSschwarze { 17303a0d07afSschwarze struct roff_node *n; 1731753701eeSschwarze 17322588c917Sschwarze n = mdoc->last; 17332588c917Sschwarze 1734d1982c71Sschwarze if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 17351d0823adSschwarze mandoc_msg(MANDOCERR_BLK_EMPTY, 17362588c917Sschwarze mdoc->parse, n->line, n->pos, "Bk"); 1737fa2127f9Sschwarze roff_node_delete(mdoc, n); 17382588c917Sschwarze } 1739753701eeSschwarze } 1740753701eeSschwarze 174198b8f00aSschwarze static void 1742f051602aSschwarze post_sm(POST_ARGS) 1743f73abda9Skristaps { 17443a0d07afSschwarze struct roff_node *nch; 1745f73abda9Skristaps 1746dc0d8bb2Sschwarze nch = mdoc->last->child; 1747dc0d8bb2Sschwarze 174878bbbab4Sschwarze if (nch == NULL) { 1749f9e7bf99Sschwarze mdoc->flags ^= MDOC_SMOFF; 175098b8f00aSschwarze return; 1751bb648afaSschwarze } 1752f9e7bf99Sschwarze 1753d1982c71Sschwarze assert(nch->type == ROFFT_TEXT); 175420fa2881Sschwarze 175578bbbab4Sschwarze if ( ! strcmp(nch->string, "on")) { 1756ec2beb53Sschwarze mdoc->flags &= ~MDOC_SMOFF; 175798b8f00aSschwarze return; 1758ec2beb53Sschwarze } 175978bbbab4Sschwarze if ( ! strcmp(nch->string, "off")) { 1760ec2beb53Sschwarze mdoc->flags |= MDOC_SMOFF; 176198b8f00aSschwarze return; 1762ec2beb53Sschwarze } 176320fa2881Sschwarze 1764dc0d8bb2Sschwarze mandoc_vmsg(MANDOCERR_SM_BAD, 1765dc0d8bb2Sschwarze mdoc->parse, nch->line, nch->pos, 176614a309e3Sschwarze "%s %s", roff_name[mdoc->last->tok], nch->string); 176798b8f00aSschwarze mdoc_node_relink(mdoc, nch); 176898b8f00aSschwarze return; 176920fa2881Sschwarze } 1770f73abda9Skristaps 177198b8f00aSschwarze static void 1772f73abda9Skristaps post_root(POST_ARGS) 1773f73abda9Skristaps { 1774*8fd2959dSschwarze const char *openbsd_arch[] = { 1775*8fd2959dSschwarze "alpha", "amd64", "arm64", "armv7", "hppa", "i386", 1776*8fd2959dSschwarze "landisk", "loongson", "luna88k", "macppc", "mips64", 1777*8fd2959dSschwarze "octeon", "sgi", "socppc", "sparc64", NULL 1778*8fd2959dSschwarze }; 1779*8fd2959dSschwarze const char *netbsd_arch[] = { 1780*8fd2959dSschwarze "acorn26", "acorn32", "algor", "alpha", "amiga", 1781*8fd2959dSschwarze "arc", "atari", 1782*8fd2959dSschwarze "bebox", "cats", "cesfic", "cobalt", "dreamcast", 1783*8fd2959dSschwarze "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5", 1784*8fd2959dSschwarze "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa", 1785*8fd2959dSschwarze "i386", "ibmnws", "luna68k", 1786*8fd2959dSschwarze "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc", 1787*8fd2959dSschwarze "netwinder", "news68k", "newsmips", "next68k", 1788*8fd2959dSschwarze "pc532", "playstation2", "pmax", "pmppc", "prep", 1789*8fd2959dSschwarze "sandpoint", "sbmips", "sgimips", "shark", 1790*8fd2959dSschwarze "sparc", "sparc64", "sun2", "sun3", 1791*8fd2959dSschwarze "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL 1792*8fd2959dSschwarze }; 1793*8fd2959dSschwarze const char **arches[] = { NULL, netbsd_arch, openbsd_arch }; 1794*8fd2959dSschwarze 17953a0d07afSschwarze struct roff_node *n; 1796*8fd2959dSschwarze const char **arch; 1797f73abda9Skristaps 1798ac1f49d0Sschwarze /* Add missing prologue data. */ 179920fa2881Sschwarze 1800ac1f49d0Sschwarze if (mdoc->meta.date == NULL) 18013427e516Sschwarze mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 18023427e516Sschwarze mandoc_normdate(mdoc, NULL, 0, 0); 18033fdead0cSschwarze 18043fdead0cSschwarze if (mdoc->meta.title == NULL) { 18053fdead0cSschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, 18063fdead0cSschwarze mdoc->parse, 0, 0, "EOF"); 18073fdead0cSschwarze mdoc->meta.title = mandoc_strdup("UNTITLED"); 18083fdead0cSschwarze } 18093fdead0cSschwarze 1810ac1f49d0Sschwarze if (mdoc->meta.vol == NULL) 1811ac1f49d0Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 18123fdead0cSschwarze 18133fdead0cSschwarze if (mdoc->meta.os == NULL) { 18143fdead0cSschwarze mandoc_msg(MANDOCERR_OS_MISSING, 18153fdead0cSschwarze mdoc->parse, 0, 0, NULL); 18163fdead0cSschwarze mdoc->meta.os = mandoc_strdup(""); 1817172864f7Sschwarze } else if (mdoc->meta.os_e && 1818172864f7Sschwarze (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) 1819f3476b07Sschwarze mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0, 1820f3476b07Sschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 1821f3476b07Sschwarze "(OpenBSD)" : "(NetBSD)"); 1822f73abda9Skristaps 1823*8fd2959dSschwarze if (mdoc->meta.arch != NULL && 1824*8fd2959dSschwarze (arch = arches[mdoc->meta.os_e]) != NULL) { 1825*8fd2959dSschwarze while (*arch != NULL && strcmp(*arch, mdoc->meta.arch)) 1826*8fd2959dSschwarze arch++; 1827*8fd2959dSschwarze if (*arch == NULL) { 1828*8fd2959dSschwarze n = mdoc->first->child; 1829*8fd2959dSschwarze while (n->tok != MDOC_Dt) 1830*8fd2959dSschwarze n = n->next; 1831*8fd2959dSschwarze n = n->child->next->next; 1832*8fd2959dSschwarze mandoc_vmsg(MANDOCERR_ARCH_BAD, 1833*8fd2959dSschwarze mdoc->parse, n->line, n->pos, 1834*8fd2959dSschwarze "Dt ... %s %s", mdoc->meta.arch, 1835*8fd2959dSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 1836*8fd2959dSschwarze "(OpenBSD)" : "(NetBSD)"); 1837*8fd2959dSschwarze } 1838*8fd2959dSschwarze } 1839*8fd2959dSschwarze 184020fa2881Sschwarze /* Check that we begin with a proper `Sh'. */ 184120fa2881Sschwarze 1842e20417bdSschwarze n = mdoc->first->child; 1843bfef5133Sschwarze while (n != NULL && n->tok != TOKEN_NONE && 1844bfef5133Sschwarze mdoc_macros[n->tok].flags & MDOC_PROLOGUE) 1845e20417bdSschwarze n = n->next; 1846e20417bdSschwarze 1847e20417bdSschwarze if (n == NULL) 1848e20417bdSschwarze mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); 1849e20417bdSschwarze else if (n->tok != MDOC_Sh) 185051fcab2fSschwarze mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, 185114a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 185220fa2881Sschwarze } 1853f73abda9Skristaps 185498b8f00aSschwarze static void 1855011fe33bSschwarze post_rs(POST_ARGS) 1856011fe33bSschwarze { 18573a0d07afSschwarze struct roff_node *np, *nch, *next, *prev; 185820fa2881Sschwarze int i, j; 1859011fe33bSschwarze 18606e96429aSschwarze np = mdoc->last; 18616e96429aSschwarze 1862d1982c71Sschwarze if (np->type != ROFFT_BODY) 186398b8f00aSschwarze return; 18646e96429aSschwarze 18656e96429aSschwarze if (np->child == NULL) { 18666e96429aSschwarze mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse, 18676e96429aSschwarze np->line, np->pos, "Rs"); 186898b8f00aSschwarze return; 1869bb648afaSschwarze } 1870011fe33bSschwarze 187120fa2881Sschwarze /* 187220fa2881Sschwarze * The full `Rs' block needs special handling to order the 187320fa2881Sschwarze * sub-elements according to `rsord'. Pick through each element 1874b538baa5Sschwarze * and correctly order it. This is an insertion sort. 187520fa2881Sschwarze */ 187620fa2881Sschwarze 187720fa2881Sschwarze next = NULL; 18786e96429aSschwarze for (nch = np->child->next; nch != NULL; nch = next) { 18796e96429aSschwarze /* Determine order number of this child. */ 188020fa2881Sschwarze for (i = 0; i < RSORD_MAX; i++) 18816e96429aSschwarze if (rsord[i] == nch->tok) 188220fa2881Sschwarze break; 188320fa2881Sschwarze 1884b538baa5Sschwarze if (i == RSORD_MAX) { 188514a309e3Sschwarze mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse, 188614a309e3Sschwarze nch->line, nch->pos, roff_name[nch->tok]); 1887b538baa5Sschwarze i = -1; 18886e96429aSschwarze } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 18896e96429aSschwarze np->norm->Rs.quote_T++; 1890b538baa5Sschwarze 189120fa2881Sschwarze /* 18926e96429aSschwarze * Remove this child from the chain. This somewhat 1893fa2127f9Sschwarze * repeats roff_node_unlink(), but since we're 189420fa2881Sschwarze * just re-ordering, there's no need for the 189520fa2881Sschwarze * full unlink process. 189620fa2881Sschwarze */ 189720fa2881Sschwarze 18986e96429aSschwarze if ((next = nch->next) != NULL) 18996e96429aSschwarze next->prev = nch->prev; 190020fa2881Sschwarze 19016e96429aSschwarze if ((prev = nch->prev) != NULL) 19026e96429aSschwarze prev->next = nch->next; 190320fa2881Sschwarze 19046e96429aSschwarze nch->prev = nch->next = NULL; 190520fa2881Sschwarze 190620fa2881Sschwarze /* 190720fa2881Sschwarze * Scan back until we reach a node that's 19086e96429aSschwarze * to be ordered before this child. 190920fa2881Sschwarze */ 191020fa2881Sschwarze 191120fa2881Sschwarze for ( ; prev ; prev = prev->prev) { 191220fa2881Sschwarze /* Determine order of `prev'. */ 191320fa2881Sschwarze for (j = 0; j < RSORD_MAX; j++) 191420fa2881Sschwarze if (rsord[j] == prev->tok) 191520fa2881Sschwarze break; 1916b538baa5Sschwarze if (j == RSORD_MAX) 1917b538baa5Sschwarze j = -1; 191820fa2881Sschwarze 191920fa2881Sschwarze if (j <= i) 192020fa2881Sschwarze break; 192120fa2881Sschwarze } 192220fa2881Sschwarze 192320fa2881Sschwarze /* 19246e96429aSschwarze * Set this child back into its correct place 19256e96429aSschwarze * in front of the `prev' node. 192620fa2881Sschwarze */ 192720fa2881Sschwarze 19286e96429aSschwarze nch->prev = prev; 192920fa2881Sschwarze 19306e96429aSschwarze if (prev == NULL) { 19316e96429aSschwarze np->child->prev = nch; 19326e96429aSschwarze nch->next = np->child; 19336e96429aSschwarze np->child = nch; 193420fa2881Sschwarze } else { 19356e96429aSschwarze if (prev->next) 19366e96429aSschwarze prev->next->prev = nch; 19376e96429aSschwarze nch->next = prev->next; 19386e96429aSschwarze prev->next = nch; 193920fa2881Sschwarze } 1940011fe33bSschwarze } 1941011fe33bSschwarze } 1942011fe33bSschwarze 19434039b21cSschwarze /* 19444039b21cSschwarze * For some arguments of some macros, 19454039b21cSschwarze * convert all breakable hyphens into ASCII_HYPH. 19464039b21cSschwarze */ 194798b8f00aSschwarze static void 19484039b21cSschwarze post_hyph(POST_ARGS) 19494039b21cSschwarze { 19503a0d07afSschwarze struct roff_node *nch; 19514039b21cSschwarze char *cp; 19524039b21cSschwarze 1953b7530f2fSschwarze for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { 1954d1982c71Sschwarze if (nch->type != ROFFT_TEXT) 19554039b21cSschwarze continue; 19564039b21cSschwarze cp = nch->string; 1957b7530f2fSschwarze if (*cp == '\0') 19584039b21cSschwarze continue; 1959b7530f2fSschwarze while (*(++cp) != '\0') 1960b7530f2fSschwarze if (*cp == '-' && 19614039b21cSschwarze isalpha((unsigned char)cp[-1]) && 19624039b21cSschwarze isalpha((unsigned char)cp[1])) 19634039b21cSschwarze *cp = ASCII_HYPH; 19644039b21cSschwarze } 19654039b21cSschwarze } 19664039b21cSschwarze 196798b8f00aSschwarze static void 1968af216717Sschwarze post_ns(POST_ARGS) 1969af216717Sschwarze { 1970af216717Sschwarze 1971c4b0939cSschwarze if (mdoc->last->flags & NODE_LINE) 197228153913Sschwarze mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, 197328153913Sschwarze mdoc->last->line, mdoc->last->pos, NULL); 1974af216717Sschwarze } 1975af216717Sschwarze 197698b8f00aSschwarze static void 1977f73abda9Skristaps post_sh(POST_ARGS) 1978f73abda9Skristaps { 1979f73abda9Skristaps 1980753701eeSschwarze post_ignpar(mdoc); 1981753701eeSschwarze 1982cd6c268fSschwarze switch (mdoc->last->type) { 1983d1982c71Sschwarze case ROFFT_HEAD: 198498b8f00aSschwarze post_sh_head(mdoc); 198598b8f00aSschwarze break; 1986d1982c71Sschwarze case ROFFT_BODY: 1987cd6c268fSschwarze switch (mdoc->lastsec) { 1988cd6c268fSschwarze case SEC_NAME: 198998b8f00aSschwarze post_sh_name(mdoc); 199098b8f00aSschwarze break; 19917c384856Sschwarze case SEC_SEE_ALSO: 199298b8f00aSschwarze post_sh_see_also(mdoc); 199398b8f00aSschwarze break; 1994cd6c268fSschwarze case SEC_AUTHORS: 199598b8f00aSschwarze post_sh_authors(mdoc); 199698b8f00aSschwarze break; 1997cd6c268fSschwarze default: 1998cd6c268fSschwarze break; 1999cd6c268fSschwarze } 2000cd6c268fSschwarze break; 2001cd6c268fSschwarze default: 2002cd6c268fSschwarze break; 2003cd6c268fSschwarze } 2004f73abda9Skristaps } 2005f73abda9Skristaps 200698b8f00aSschwarze static void 2007cd6c268fSschwarze post_sh_name(POST_ARGS) 2008f73abda9Skristaps { 20093a0d07afSschwarze struct roff_node *n; 201020e2cf25Sschwarze int hasnm, hasnd; 2011f73abda9Skristaps 201220e2cf25Sschwarze hasnm = hasnd = 0; 2013f73abda9Skristaps 201420e2cf25Sschwarze for (n = mdoc->last->child; n != NULL; n = n->next) { 201520e2cf25Sschwarze switch (n->tok) { 201620e2cf25Sschwarze case MDOC_Nm: 2017f27faaccSschwarze if (hasnm && n->child != NULL) 2018f27faaccSschwarze mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT, 2019f27faaccSschwarze mdoc->parse, n->line, n->pos, 2020f27faaccSschwarze "Nm %s", n->child->string); 202120e2cf25Sschwarze hasnm = 1; 2022f27faaccSschwarze continue; 202320e2cf25Sschwarze case MDOC_Nd: 202420e2cf25Sschwarze hasnd = 1; 202520e2cf25Sschwarze if (n->next != NULL) 202620e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_ND, 202720e2cf25Sschwarze mdoc->parse, n->line, n->pos, NULL); 202820e2cf25Sschwarze break; 20292d6f95d3Sschwarze case TOKEN_NONE: 2030f27faaccSschwarze if (n->type == ROFFT_TEXT && 2031f27faaccSschwarze n->string[0] == ',' && n->string[1] == '\0' && 2032f27faaccSschwarze n->next != NULL && n->next->tok == MDOC_Nm) { 2033f27faaccSschwarze n = n->next; 2034f27faaccSschwarze continue; 2035f27faaccSschwarze } 2036fa072f7fSschwarze /* FALLTHROUGH */ 203720e2cf25Sschwarze default: 203851fcab2fSschwarze mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 203914a309e3Sschwarze n->line, n->pos, roff_name[n->tok]); 2040f27faaccSschwarze continue; 204120e2cf25Sschwarze } 2042f27faaccSschwarze break; 2043f73abda9Skristaps } 2044f73abda9Skristaps 204520e2cf25Sschwarze if ( ! hasnm) 204620e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, 204720e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL); 204820e2cf25Sschwarze if ( ! hasnd) 204920e2cf25Sschwarze mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, 205020e2cf25Sschwarze mdoc->last->line, mdoc->last->pos, NULL); 205120fa2881Sschwarze } 2052f73abda9Skristaps 205398b8f00aSschwarze static void 20547c384856Sschwarze post_sh_see_also(POST_ARGS) 20557c384856Sschwarze { 20563a0d07afSschwarze const struct roff_node *n; 20577c384856Sschwarze const char *name, *sec; 20587c384856Sschwarze const char *lastname, *lastsec, *lastpunct; 20597c384856Sschwarze int cmp; 20607c384856Sschwarze 20617c384856Sschwarze n = mdoc->last->child; 20627c384856Sschwarze lastname = lastsec = lastpunct = NULL; 20637c384856Sschwarze while (n != NULL) { 206430e5ee06Sschwarze if (n->tok != MDOC_Xr || 206530e5ee06Sschwarze n->child == NULL || 206630e5ee06Sschwarze n->child->next == NULL) 20677c384856Sschwarze break; 20687c384856Sschwarze 20697c384856Sschwarze /* Process one .Xr node. */ 20707c384856Sschwarze 20717c384856Sschwarze name = n->child->string; 20727c384856Sschwarze sec = n->child->next->string; 20737c384856Sschwarze if (lastsec != NULL) { 20747c384856Sschwarze if (lastpunct[0] != ',' || lastpunct[1] != '\0') 20757c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_PUNCT, 20767c384856Sschwarze mdoc->parse, n->line, n->pos, 20777c384856Sschwarze "%s before %s(%s)", lastpunct, 20787c384856Sschwarze name, sec); 20797c384856Sschwarze cmp = strcmp(lastsec, sec); 20807c384856Sschwarze if (cmp > 0) 20817c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_ORDER, 20827c384856Sschwarze mdoc->parse, n->line, n->pos, 20837c384856Sschwarze "%s(%s) after %s(%s)", name, 20847c384856Sschwarze sec, lastname, lastsec); 20857c384856Sschwarze else if (cmp == 0 && 20867c384856Sschwarze strcasecmp(lastname, name) > 0) 20877c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_ORDER, 20887c384856Sschwarze mdoc->parse, n->line, n->pos, 20897c384856Sschwarze "%s after %s", name, lastname); 20907c384856Sschwarze } 20917c384856Sschwarze lastname = name; 20927c384856Sschwarze lastsec = sec; 20937c384856Sschwarze 20947c384856Sschwarze /* Process the following node. */ 20957c384856Sschwarze 20967c384856Sschwarze n = n->next; 20977c384856Sschwarze if (n == NULL) 20987c384856Sschwarze break; 20997c384856Sschwarze if (n->tok == MDOC_Xr) { 21007c384856Sschwarze lastpunct = "none"; 21017c384856Sschwarze continue; 21027c384856Sschwarze } 2103d1982c71Sschwarze if (n->type != ROFFT_TEXT) 21047c384856Sschwarze break; 21057c384856Sschwarze for (name = n->string; *name != '\0'; name++) 21067c384856Sschwarze if (isalpha((const unsigned char)*name)) 210798b8f00aSschwarze return; 21087c384856Sschwarze lastpunct = n->string; 2109c7098240Sschwarze if (n->next == NULL || n->next->tok == MDOC_Rs) 21107c384856Sschwarze mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, 21117c384856Sschwarze n->line, n->pos, "%s after %s(%s)", 21127c384856Sschwarze lastpunct, lastname, lastsec); 21137c384856Sschwarze n = n->next; 21147c384856Sschwarze } 21157c384856Sschwarze } 21167c384856Sschwarze 21177c384856Sschwarze static int 21183a0d07afSschwarze child_an(const struct roff_node *n) 2119cd6c268fSschwarze { 2120cd6c268fSschwarze 2121cd6c268fSschwarze for (n = n->child; n != NULL; n = n->next) 212230e5ee06Sschwarze if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 2123526e306bSschwarze return 1; 2124526e306bSschwarze return 0; 2125cd6c268fSschwarze } 2126cd6c268fSschwarze 212798b8f00aSschwarze static void 2128cd6c268fSschwarze post_sh_authors(POST_ARGS) 2129cd6c268fSschwarze { 2130cd6c268fSschwarze 2131cd6c268fSschwarze if ( ! child_an(mdoc->last)) 2132cd6c268fSschwarze mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse, 2133cd6c268fSschwarze mdoc->last->line, mdoc->last->pos, NULL); 2134cd6c268fSschwarze } 2135cd6c268fSschwarze 213698b8f00aSschwarze static void 2137f73abda9Skristaps post_sh_head(POST_ARGS) 2138f73abda9Skristaps { 2139cf0e2075Sschwarze struct roff_node *nch; 214051fcab2fSschwarze const char *goodsec; 21413a0d07afSschwarze enum roff_sec sec; 2142f73abda9Skristaps 2143f73abda9Skristaps /* 2144f73abda9Skristaps * Process a new section. Sections are either "named" or 214520fa2881Sschwarze * "custom". Custom sections are user-defined, while named ones 214620fa2881Sschwarze * follow a conventional order and may only appear in certain 214720fa2881Sschwarze * manual sections. 2148f73abda9Skristaps */ 2149f73abda9Skristaps 2150396853b5Sschwarze sec = mdoc->last->sec; 2151f73abda9Skristaps 215220fa2881Sschwarze /* The NAME should be first. */ 2153f73abda9Skristaps 2154d37754b9Sschwarze if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 2155bd594191Sschwarze mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, 2156d37754b9Sschwarze mdoc->last->line, mdoc->last->pos, "Sh %s", 2157cf0e2075Sschwarze sec != SEC_CUSTOM ? secnames[sec] : 2158cf0e2075Sschwarze (nch = mdoc->last->child) == NULL ? "" : 2159cf0e2075Sschwarze nch->type == ROFFT_TEXT ? nch->string : 216014a309e3Sschwarze roff_name[nch->tok]); 216120fa2881Sschwarze 216220fa2881Sschwarze /* The SYNOPSIS gets special attention in other areas. */ 216320fa2881Sschwarze 2164396853b5Sschwarze if (sec == SEC_SYNOPSIS) { 216575088a49Sschwarze roff_setreg(mdoc->roff, "nS", 1, '='); 216620fa2881Sschwarze mdoc->flags |= MDOC_SYNOPSIS; 216722881299Sschwarze } else { 216875088a49Sschwarze roff_setreg(mdoc->roff, "nS", 0, '='); 216920fa2881Sschwarze mdoc->flags &= ~MDOC_SYNOPSIS; 217022881299Sschwarze } 217120fa2881Sschwarze 217220fa2881Sschwarze /* Mark our last section. */ 217320fa2881Sschwarze 217420fa2881Sschwarze mdoc->lastsec = sec; 21751eccdf28Sschwarze 217620fa2881Sschwarze /* We don't care about custom sections after this. */ 2177fccfce9dSschwarze 2178396853b5Sschwarze if (sec == SEC_CUSTOM) 217998b8f00aSschwarze return; 2180fccfce9dSschwarze 21816be99f77Sschwarze /* 218220fa2881Sschwarze * Check whether our non-custom section is being repeated or is 218320fa2881Sschwarze * out of order. 21846be99f77Sschwarze */ 2185f73abda9Skristaps 218620fa2881Sschwarze if (sec == mdoc->lastnamed) 2187bd594191Sschwarze mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, 2188bd594191Sschwarze mdoc->last->line, mdoc->last->pos, 2189396853b5Sschwarze "Sh %s", secnames[sec]); 219020fa2881Sschwarze 219120fa2881Sschwarze if (sec < mdoc->lastnamed) 2192bd594191Sschwarze mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, 2193bd594191Sschwarze mdoc->last->line, mdoc->last->pos, 2194396853b5Sschwarze "Sh %s", secnames[sec]); 219520fa2881Sschwarze 219620fa2881Sschwarze /* Mark the last named section. */ 219720fa2881Sschwarze 219820fa2881Sschwarze mdoc->lastnamed = sec; 219920fa2881Sschwarze 220020fa2881Sschwarze /* Check particular section/manual conventions. */ 220120fa2881Sschwarze 2202396853b5Sschwarze if (mdoc->meta.msec == NULL) 220398b8f00aSschwarze return; 220420fa2881Sschwarze 220551fcab2fSschwarze goodsec = NULL; 220620fa2881Sschwarze switch (sec) { 220749aff9f8Sschwarze case SEC_ERRORS: 2208be89e780Sschwarze if (*mdoc->meta.msec == '4') 2209be89e780Sschwarze break; 221051fcab2fSschwarze goodsec = "2, 3, 4, 9"; 2211be89e780Sschwarze /* FALLTHROUGH */ 221249aff9f8Sschwarze case SEC_RETURN_VALUES: 221349aff9f8Sschwarze case SEC_LIBRARY: 221492c0ca7fSschwarze if (*mdoc->meta.msec == '2') 2215f73abda9Skristaps break; 221692c0ca7fSschwarze if (*mdoc->meta.msec == '3') 221792c0ca7fSschwarze break; 221851fcab2fSschwarze if (NULL == goodsec) 221951fcab2fSschwarze goodsec = "2, 3, 9"; 222003ab2f23Sdlg /* FALLTHROUGH */ 222149aff9f8Sschwarze case SEC_CONTEXT: 222292c0ca7fSschwarze if (*mdoc->meta.msec == '9') 222392c0ca7fSschwarze break; 222451fcab2fSschwarze if (NULL == goodsec) 222551fcab2fSschwarze goodsec = "9"; 222651fcab2fSschwarze mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, 222751fcab2fSschwarze mdoc->last->line, mdoc->last->pos, 2228396853b5Sschwarze "Sh %s for %s only", secnames[sec], goodsec); 222920fa2881Sschwarze break; 2230f73abda9Skristaps default: 2231f73abda9Skristaps break; 2232f73abda9Skristaps } 2233f73abda9Skristaps } 2234d39b9a9cSschwarze 223598b8f00aSschwarze static void 22365ae08040Sschwarze post_xr(POST_ARGS) 22375ae08040Sschwarze { 22385ae08040Sschwarze struct roff_node *n, *nch; 22395ae08040Sschwarze 22405ae08040Sschwarze n = mdoc->last; 22415ae08040Sschwarze nch = n->child; 22425ae08040Sschwarze if (nch->next == NULL) { 22435ae08040Sschwarze mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, 22445ae08040Sschwarze n->line, n->pos, "Xr %s", nch->string); 224504fbb99fSschwarze } else 22465ae08040Sschwarze assert(nch->next == n->last); 224704fbb99fSschwarze post_delim(mdoc); 22485ae08040Sschwarze } 22495ae08040Sschwarze 22505ae08040Sschwarze static void 2251f6127a73Sschwarze post_ignpar(POST_ARGS) 2252f6127a73Sschwarze { 22533a0d07afSschwarze struct roff_node *np; 2254f6127a73Sschwarze 2255b7530f2fSschwarze switch (mdoc->last->type) { 22569c7c6a1fSschwarze case ROFFT_BLOCK: 22579c7c6a1fSschwarze post_prevpar(mdoc); 22589c7c6a1fSschwarze return; 2259d1982c71Sschwarze case ROFFT_HEAD: 2260753701eeSschwarze post_hyph(mdoc); 226198b8f00aSschwarze return; 2262d1982c71Sschwarze case ROFFT_BODY: 2263b7530f2fSschwarze break; 2264b7530f2fSschwarze default: 2265b7530f2fSschwarze return; 2266b7530f2fSschwarze } 2267f6127a73Sschwarze 2268f051602aSschwarze if ((np = mdoc->last->child) != NULL) 2269f051602aSschwarze if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 227020369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, 227120369664Sschwarze mdoc->parse, np->line, np->pos, 227214a309e3Sschwarze "%s after %s", roff_name[np->tok], 227314a309e3Sschwarze roff_name[mdoc->last->tok]); 2274fa2127f9Sschwarze roff_node_delete(mdoc, np); 2275f6127a73Sschwarze } 2276f6127a73Sschwarze 2277f051602aSschwarze if ((np = mdoc->last->last) != NULL) 2278f051602aSschwarze if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 227920369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 228020369664Sschwarze np->line, np->pos, "%s at the end of %s", 228114a309e3Sschwarze roff_name[np->tok], 228214a309e3Sschwarze roff_name[mdoc->last->tok]); 2283fa2127f9Sschwarze roff_node_delete(mdoc, np); 2284f6127a73Sschwarze } 2285f6127a73Sschwarze } 2286f6127a73Sschwarze 228798b8f00aSschwarze static void 22883e642ba0Sschwarze post_prevpar(POST_ARGS) 2289d39b9a9cSschwarze { 22903e642ba0Sschwarze struct roff_node *n; 2291d39b9a9cSschwarze 22923e642ba0Sschwarze n = mdoc->last; 22933e642ba0Sschwarze if (NULL == n->prev) 229498b8f00aSschwarze return; 2295d1982c71Sschwarze if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 229698b8f00aSschwarze return; 2297d39b9a9cSschwarze 229820fa2881Sschwarze /* 229920fa2881Sschwarze * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 230020fa2881Sschwarze * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 230120fa2881Sschwarze */ 2302d39b9a9cSschwarze 23033e642ba0Sschwarze if (n->prev->tok != MDOC_Pp && 23043e642ba0Sschwarze n->prev->tok != MDOC_Lp && 230529478532Sschwarze n->prev->tok != ROFF_br) 230698b8f00aSschwarze return; 23073e642ba0Sschwarze if (n->tok == MDOC_Bl && n->norm->Bl.comp) 230898b8f00aSschwarze return; 23093e642ba0Sschwarze if (n->tok == MDOC_Bd && n->norm->Bd.comp) 231098b8f00aSschwarze return; 23113e642ba0Sschwarze if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 231298b8f00aSschwarze return; 2313d39b9a9cSschwarze 231420369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 231514a309e3Sschwarze n->prev->line, n->prev->pos, "%s before %s", 231614a309e3Sschwarze roff_name[n->prev->tok], roff_name[n->tok]); 23173e642ba0Sschwarze roff_node_delete(mdoc, n->prev); 2318d39b9a9cSschwarze } 231920fa2881Sschwarze 232098b8f00aSschwarze static void 2321e0dd4c9cSschwarze post_par(POST_ARGS) 2322e0dd4c9cSschwarze { 23233a0d07afSschwarze struct roff_node *np; 2324e0dd4c9cSschwarze 23253798fb25Sschwarze np = mdoc->last; 23266561cb23Sschwarze if (np->tok != ROFF_br && np->tok != ROFF_sp) 23273e642ba0Sschwarze post_prevpar(mdoc); 2328753701eeSschwarze 23296561cb23Sschwarze if (np->tok == ROFF_sp) { 233030e5ee06Sschwarze if (np->child != NULL && np->child->next != NULL) 23313798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 23323798fb25Sschwarze np->child->next->line, np->child->next->pos, 23333798fb25Sschwarze "sp ... %s", np->child->next->string); 23343798fb25Sschwarze } else if (np->child != NULL) 23353798fb25Sschwarze mandoc_vmsg(MANDOCERR_ARG_SKIP, 23363798fb25Sschwarze mdoc->parse, np->line, np->pos, "%s %s", 233714a309e3Sschwarze roff_name[np->tok], np->child->string); 2338e0dd4c9cSschwarze 2339f051602aSschwarze if ((np = mdoc->last->prev) == NULL) { 234020369664Sschwarze np = mdoc->last->parent; 2341f051602aSschwarze if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) 234298b8f00aSschwarze return; 2343f051602aSschwarze } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && 234429478532Sschwarze (mdoc->last->tok != ROFF_br || 23456561cb23Sschwarze (np->tok != ROFF_sp && np->tok != ROFF_br))) 234698b8f00aSschwarze return; 2347e0dd4c9cSschwarze 234820369664Sschwarze mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 234914a309e3Sschwarze mdoc->last->line, mdoc->last->pos, "%s after %s", 235014a309e3Sschwarze roff_name[mdoc->last->tok], roff_name[np->tok]); 2351fa2127f9Sschwarze roff_node_delete(mdoc, mdoc->last); 2352e0dd4c9cSschwarze } 2353e0dd4c9cSschwarze 235498b8f00aSschwarze static void 235520fa2881Sschwarze post_dd(POST_ARGS) 235620fa2881Sschwarze { 23573a0d07afSschwarze struct roff_node *n; 235883af2bccSschwarze char *datestr; 235920fa2881Sschwarze 2360b058e777Sschwarze n = mdoc->last; 236143808411Sschwarze n->flags |= NODE_NOPRT; 236243808411Sschwarze 2363396853b5Sschwarze if (mdoc->meta.date != NULL) { 2364396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2365396853b5Sschwarze n->line, n->pos, "Dd"); 2366396853b5Sschwarze free(mdoc->meta.date); 2367396853b5Sschwarze } else if (mdoc->flags & MDOC_PBODY) 2368396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2369396853b5Sschwarze n->line, n->pos, "Dd"); 2370396853b5Sschwarze else if (mdoc->meta.title != NULL) 2371396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2372396853b5Sschwarze n->line, n->pos, "Dd after Dt"); 2373396853b5Sschwarze else if (mdoc->meta.os != NULL) 2374396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2375396853b5Sschwarze n->line, n->pos, "Dd after Os"); 2376396853b5Sschwarze 2377f051602aSschwarze if (n->child == NULL || n->child->string[0] == '\0') { 2378231c7061Sschwarze mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 23793427e516Sschwarze mandoc_normdate(mdoc, NULL, n->line, n->pos); 238043808411Sschwarze return; 238120fa2881Sschwarze } 238220fa2881Sschwarze 238383af2bccSschwarze datestr = NULL; 2384423631c9Sschwarze deroff(&datestr, n); 238583af2bccSschwarze if (mdoc->quick) 238683af2bccSschwarze mdoc->meta.date = datestr; 238783af2bccSschwarze else { 23883427e516Sschwarze mdoc->meta.date = mandoc_normdate(mdoc, 238983af2bccSschwarze datestr, n->line, n->pos); 239083af2bccSschwarze free(datestr); 239104e980cbSschwarze } 239220fa2881Sschwarze } 239320fa2881Sschwarze 239498b8f00aSschwarze static void 239520fa2881Sschwarze post_dt(POST_ARGS) 239620fa2881Sschwarze { 23973a0d07afSschwarze struct roff_node *nn, *n; 239820fa2881Sschwarze const char *cp; 239920fa2881Sschwarze char *p; 240020fa2881Sschwarze 240120fa2881Sschwarze n = mdoc->last; 240243808411Sschwarze n->flags |= NODE_NOPRT; 240343808411Sschwarze 2404396853b5Sschwarze if (mdoc->flags & MDOC_PBODY) { 2405396853b5Sschwarze mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, 2406396853b5Sschwarze n->line, n->pos, "Dt"); 240743808411Sschwarze return; 2408396853b5Sschwarze } 2409396853b5Sschwarze 2410396853b5Sschwarze if (mdoc->meta.title != NULL) 2411396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2412396853b5Sschwarze n->line, n->pos, "Dt"); 2413396853b5Sschwarze else if (mdoc->meta.os != NULL) 2414396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2415396853b5Sschwarze n->line, n->pos, "Dt after Os"); 241620fa2881Sschwarze 241720fa2881Sschwarze free(mdoc->meta.title); 24183fdead0cSschwarze free(mdoc->meta.msec); 241920fa2881Sschwarze free(mdoc->meta.vol); 242020fa2881Sschwarze free(mdoc->meta.arch); 242120fa2881Sschwarze 24223fdead0cSschwarze mdoc->meta.title = NULL; 24233fdead0cSschwarze mdoc->meta.msec = NULL; 24243fdead0cSschwarze mdoc->meta.vol = NULL; 24253fdead0cSschwarze mdoc->meta.arch = NULL; 242620fa2881Sschwarze 24279a928a59Sschwarze /* Mandatory first argument: title. */ 242820fa2881Sschwarze 24299a928a59Sschwarze nn = n->child; 24309a928a59Sschwarze if (nn == NULL || *nn->string == '\0') { 24319a928a59Sschwarze mandoc_msg(MANDOCERR_DT_NOTITLE, 24329a928a59Sschwarze mdoc->parse, n->line, n->pos, "Dt"); 24339a928a59Sschwarze mdoc->meta.title = mandoc_strdup("UNTITLED"); 24349a928a59Sschwarze } else { 24359a928a59Sschwarze mdoc->meta.title = mandoc_strdup(nn->string); 24369a928a59Sschwarze 24379a928a59Sschwarze /* Check that all characters are uppercase. */ 24389a928a59Sschwarze 24399a928a59Sschwarze for (p = nn->string; *p != '\0'; p++) 24409a928a59Sschwarze if (islower((unsigned char)*p)) { 2441bd594191Sschwarze mandoc_vmsg(MANDOCERR_TITLE_CASE, 244251fcab2fSschwarze mdoc->parse, nn->line, 244351fcab2fSschwarze nn->pos + (p - nn->string), 2444bd594191Sschwarze "Dt %s", nn->string); 244520fa2881Sschwarze break; 244620fa2881Sschwarze } 244720fa2881Sschwarze } 244820fa2881Sschwarze 24495ae08040Sschwarze /* Mandatory second argument: section. */ 245020fa2881Sschwarze 24519a928a59Sschwarze if (nn != NULL) 24529a928a59Sschwarze nn = nn->next; 245320fa2881Sschwarze 24549a928a59Sschwarze if (nn == NULL) { 24553fdead0cSschwarze mandoc_vmsg(MANDOCERR_MSEC_MISSING, 24563fdead0cSschwarze mdoc->parse, n->line, n->pos, 24573fdead0cSschwarze "Dt %s", mdoc->meta.title); 245820fa2881Sschwarze mdoc->meta.vol = mandoc_strdup("LOCAL"); 245943808411Sschwarze return; /* msec and arch remain NULL. */ 246020fa2881Sschwarze } 246120fa2881Sschwarze 24629a928a59Sschwarze mdoc->meta.msec = mandoc_strdup(nn->string); 24639a928a59Sschwarze 24649a928a59Sschwarze /* Infer volume title from section number. */ 246520fa2881Sschwarze 246688ec69e3Sschwarze cp = mandoc_a2msec(nn->string); 24679a928a59Sschwarze if (cp == NULL) { 2468bd594191Sschwarze mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, 2469bd594191Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string); 247020fa2881Sschwarze mdoc->meta.vol = mandoc_strdup(nn->string); 24719a928a59Sschwarze } else 24729a928a59Sschwarze mdoc->meta.vol = mandoc_strdup(cp); 247320fa2881Sschwarze 24749a928a59Sschwarze /* Optional third argument: architecture. */ 247520fa2881Sschwarze 24769a928a59Sschwarze if ((nn = nn->next) == NULL) 247743808411Sschwarze return; 24789a928a59Sschwarze 24799a928a59Sschwarze for (p = nn->string; *p != '\0'; p++) 2480b94f27c5Sschwarze *p = tolower((unsigned char)*p); 2481b94f27c5Sschwarze mdoc->meta.arch = mandoc_strdup(nn->string); 248220fa2881Sschwarze 24839a928a59Sschwarze /* Ignore fourth and later arguments. */ 24849a928a59Sschwarze 24859a928a59Sschwarze if ((nn = nn->next) != NULL) 24869a928a59Sschwarze mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 24879a928a59Sschwarze nn->line, nn->pos, "Dt ... %s", nn->string); 248820fa2881Sschwarze } 248920fa2881Sschwarze 249098b8f00aSschwarze static void 2491992063deSschwarze post_bx(POST_ARGS) 2492992063deSschwarze { 24933af8e8d7Sschwarze struct roff_node *n, *nch; 2494f0c18971Sschwarze const char *macro; 24953af8e8d7Sschwarze 249604fbb99fSschwarze post_delim(mdoc); 249704fbb99fSschwarze 24983af8e8d7Sschwarze n = mdoc->last; 24993af8e8d7Sschwarze nch = n->child; 25003af8e8d7Sschwarze 25013af8e8d7Sschwarze if (nch != NULL) { 2502f0c18971Sschwarze macro = !strcmp(nch->string, "Open") ? "Ox" : 2503f0c18971Sschwarze !strcmp(nch->string, "Net") ? "Nx" : 2504f0c18971Sschwarze !strcmp(nch->string, "Free") ? "Fx" : 2505f0c18971Sschwarze !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; 2506f0c18971Sschwarze if (macro != NULL) 2507f0c18971Sschwarze mandoc_msg(MANDOCERR_BX, mdoc->parse, 2508f0c18971Sschwarze n->line, n->pos, macro); 25093af8e8d7Sschwarze mdoc->last = nch; 25103af8e8d7Sschwarze nch = nch->next; 25113af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 25123af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 25133af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 25143af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 25153af8e8d7Sschwarze } else 25163af8e8d7Sschwarze mdoc->next = ROFF_NEXT_CHILD; 25173af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 25183af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 25193af8e8d7Sschwarze 25203af8e8d7Sschwarze if (nch == NULL) { 25213af8e8d7Sschwarze mdoc->last = n; 25223af8e8d7Sschwarze return; 25233af8e8d7Sschwarze } 25243af8e8d7Sschwarze 25253af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 25263af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 25273af8e8d7Sschwarze mdoc->next = ROFF_NEXT_SIBLING; 25283af8e8d7Sschwarze roff_word_alloc(mdoc, n->line, n->pos, "-"); 25293af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 25303af8e8d7Sschwarze roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 25313af8e8d7Sschwarze mdoc->last->flags |= NODE_NOSRC; 25323af8e8d7Sschwarze mdoc->last = n; 2533992063deSschwarze 2534992063deSschwarze /* 2535992063deSschwarze * Make `Bx's second argument always start with an uppercase 2536992063deSschwarze * letter. Groff checks if it's an "accepted" term, but we just 2537992063deSschwarze * uppercase blindly. 2538992063deSschwarze */ 2539992063deSschwarze 25403af8e8d7Sschwarze *nch->string = (char)toupper((unsigned char)*nch->string); 2541992063deSschwarze } 2542992063deSschwarze 254398b8f00aSschwarze static void 254420fa2881Sschwarze post_os(POST_ARGS) 254520fa2881Sschwarze { 254620fa2881Sschwarze #ifndef OSNAME 254720fa2881Sschwarze struct utsname utsname; 25484c468128Sschwarze static char *defbuf; 254920fa2881Sschwarze #endif 25503a0d07afSschwarze struct roff_node *n; 255120fa2881Sschwarze 255220fa2881Sschwarze n = mdoc->last; 255343808411Sschwarze n->flags |= NODE_NOPRT; 255443808411Sschwarze 2555396853b5Sschwarze if (mdoc->meta.os != NULL) 2556396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2557396853b5Sschwarze n->line, n->pos, "Os"); 2558396853b5Sschwarze else if (mdoc->flags & MDOC_PBODY) 2559396853b5Sschwarze mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2560396853b5Sschwarze n->line, n->pos, "Os"); 256120fa2881Sschwarze 256220fa2881Sschwarze /* 2563353fa9ecSschwarze * Set the operating system by way of the `Os' macro. 2564353fa9ecSschwarze * The order of precedence is: 2565353fa9ecSschwarze * 1. the argument of the `Os' macro, unless empty 2566353fa9ecSschwarze * 2. the -Ios=foo command line argument, if provided 2567353fa9ecSschwarze * 3. -DOSNAME="\"foo\"", if provided during compilation 2568353fa9ecSschwarze * 4. "sysname release" from uname(3) 256920fa2881Sschwarze */ 257020fa2881Sschwarze 257120fa2881Sschwarze free(mdoc->meta.os); 257283af2bccSschwarze mdoc->meta.os = NULL; 2573423631c9Sschwarze deroff(&mdoc->meta.os, n); 257483af2bccSschwarze if (mdoc->meta.os) 2575ce0ef847Sschwarze goto out; 25764c468128Sschwarze 2577f3476b07Sschwarze if (mdoc->os_s != NULL) { 2578f3476b07Sschwarze mdoc->meta.os = mandoc_strdup(mdoc->os_s); 2579ce0ef847Sschwarze goto out; 2580353fa9ecSschwarze } 25814c468128Sschwarze 258220fa2881Sschwarze #ifdef OSNAME 25834c468128Sschwarze mdoc->meta.os = mandoc_strdup(OSNAME); 258420fa2881Sschwarze #else /*!OSNAME */ 2585f051602aSschwarze if (defbuf == NULL) { 2586f051602aSschwarze if (uname(&utsname) == -1) { 2587f79e7afeSschwarze mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, 2588f79e7afeSschwarze n->line, n->pos, "Os"); 25894c468128Sschwarze defbuf = mandoc_strdup("UNKNOWN"); 2590a450f7c4Sschwarze } else 2591a450f7c4Sschwarze mandoc_asprintf(&defbuf, "%s %s", 2592a450f7c4Sschwarze utsname.sysname, utsname.release); 259320fa2881Sschwarze } 25944c468128Sschwarze mdoc->meta.os = mandoc_strdup(defbuf); 259520fa2881Sschwarze #endif /*!OSNAME*/ 2596ce0ef847Sschwarze 2597f3476b07Sschwarze out: 2598f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OTHER) { 2599f3476b07Sschwarze if (strstr(mdoc->meta.os, "OpenBSD") != NULL) 2600f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_OPENBSD; 2601f3476b07Sschwarze else if (strstr(mdoc->meta.os, "NetBSD") != NULL) 2602f3476b07Sschwarze mdoc->meta.os_e = MANDOC_OS_NETBSD; 2603f3476b07Sschwarze } 26043427e516Sschwarze 26053427e516Sschwarze /* 26063427e516Sschwarze * This is the earliest point where we can check 26073427e516Sschwarze * Mdocdate conventions because we don't know 26083427e516Sschwarze * the operating system earlier. 26093427e516Sschwarze */ 26103427e516Sschwarze 26112f84042eSschwarze if (n->child != NULL) 26122f84042eSschwarze mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse, 26132f84042eSschwarze n->child->line, n->child->pos, 26142f84042eSschwarze "Os %s (%s)", n->child->string, 26152f84042eSschwarze mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 26162f84042eSschwarze "OpenBSD" : "NetBSD"); 26172f84042eSschwarze 26183427e516Sschwarze while (n->tok != MDOC_Dd) 26193427e516Sschwarze if ((n = n->prev) == NULL) 26203427e516Sschwarze return; 26213427e516Sschwarze if ((n = n->child) == NULL) 26223427e516Sschwarze return; 262307ec32d0Sschwarze if (strncmp(n->string, "$" "Mdocdate", 9)) { 2624f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) 26253427e516Sschwarze mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING, 26263427e516Sschwarze mdoc->parse, n->line, n->pos, 2627f3476b07Sschwarze "Dd %s (OpenBSD)", n->string); 26283427e516Sschwarze } else { 2629f3476b07Sschwarze if (mdoc->meta.os_e == MANDOC_OS_NETBSD) 26303427e516Sschwarze mandoc_vmsg(MANDOCERR_MDOCDATE, 26313427e516Sschwarze mdoc->parse, n->line, n->pos, 2632f3476b07Sschwarze "Dd %s (NetBSD)", n->string); 26333427e516Sschwarze } 263420fa2881Sschwarze } 263520fa2881Sschwarze 2636396853b5Sschwarze enum roff_sec 2637396853b5Sschwarze mdoc_a2sec(const char *p) 263819a69263Sschwarze { 263919a69263Sschwarze int i; 264019a69263Sschwarze 264119a69263Sschwarze for (i = 0; i < (int)SEC__MAX; i++) 264219a69263Sschwarze if (secnames[i] && 0 == strcmp(p, secnames[i])) 2643526e306bSschwarze return (enum roff_sec)i; 264419a69263Sschwarze 2645526e306bSschwarze return SEC_CUSTOM; 264619a69263Sschwarze } 264719a69263Sschwarze 264819a69263Sschwarze static size_t 264914a309e3Sschwarze macro2len(enum roff_tok macro) 265019a69263Sschwarze { 265119a69263Sschwarze 265219a69263Sschwarze switch (macro) { 265349aff9f8Sschwarze case MDOC_Ad: 2654526e306bSschwarze return 12; 265549aff9f8Sschwarze case MDOC_Ao: 2656526e306bSschwarze return 12; 265749aff9f8Sschwarze case MDOC_An: 2658526e306bSschwarze return 12; 265949aff9f8Sschwarze case MDOC_Aq: 2660526e306bSschwarze return 12; 266149aff9f8Sschwarze case MDOC_Ar: 2662526e306bSschwarze return 12; 266349aff9f8Sschwarze case MDOC_Bo: 2664526e306bSschwarze return 12; 266549aff9f8Sschwarze case MDOC_Bq: 2666526e306bSschwarze return 12; 266749aff9f8Sschwarze case MDOC_Cd: 2668526e306bSschwarze return 12; 266949aff9f8Sschwarze case MDOC_Cm: 2670526e306bSschwarze return 10; 267149aff9f8Sschwarze case MDOC_Do: 2672526e306bSschwarze return 10; 267349aff9f8Sschwarze case MDOC_Dq: 2674526e306bSschwarze return 12; 267549aff9f8Sschwarze case MDOC_Dv: 2676526e306bSschwarze return 12; 267749aff9f8Sschwarze case MDOC_Eo: 2678526e306bSschwarze return 12; 267949aff9f8Sschwarze case MDOC_Em: 2680526e306bSschwarze return 10; 268149aff9f8Sschwarze case MDOC_Er: 2682526e306bSschwarze return 17; 268349aff9f8Sschwarze case MDOC_Ev: 2684526e306bSschwarze return 15; 268549aff9f8Sschwarze case MDOC_Fa: 2686526e306bSschwarze return 12; 268749aff9f8Sschwarze case MDOC_Fl: 2688526e306bSschwarze return 10; 268949aff9f8Sschwarze case MDOC_Fo: 2690526e306bSschwarze return 16; 269149aff9f8Sschwarze case MDOC_Fn: 2692526e306bSschwarze return 16; 269349aff9f8Sschwarze case MDOC_Ic: 2694526e306bSschwarze return 10; 269549aff9f8Sschwarze case MDOC_Li: 2696526e306bSschwarze return 16; 269749aff9f8Sschwarze case MDOC_Ms: 2698526e306bSschwarze return 6; 269949aff9f8Sschwarze case MDOC_Nm: 2700526e306bSschwarze return 10; 270149aff9f8Sschwarze case MDOC_No: 2702526e306bSschwarze return 12; 270349aff9f8Sschwarze case MDOC_Oo: 2704526e306bSschwarze return 10; 270549aff9f8Sschwarze case MDOC_Op: 2706526e306bSschwarze return 14; 270749aff9f8Sschwarze case MDOC_Pa: 2708526e306bSschwarze return 32; 270949aff9f8Sschwarze case MDOC_Pf: 2710526e306bSschwarze return 12; 271149aff9f8Sschwarze case MDOC_Po: 2712526e306bSschwarze return 12; 271349aff9f8Sschwarze case MDOC_Pq: 2714526e306bSschwarze return 12; 271549aff9f8Sschwarze case MDOC_Ql: 2716526e306bSschwarze return 16; 271749aff9f8Sschwarze case MDOC_Qo: 2718526e306bSschwarze return 12; 271949aff9f8Sschwarze case MDOC_So: 2720526e306bSschwarze return 12; 272149aff9f8Sschwarze case MDOC_Sq: 2722526e306bSschwarze return 12; 272349aff9f8Sschwarze case MDOC_Sy: 2724526e306bSschwarze return 6; 272549aff9f8Sschwarze case MDOC_Sx: 2726526e306bSschwarze return 16; 272749aff9f8Sschwarze case MDOC_Tn: 2728526e306bSschwarze return 10; 272949aff9f8Sschwarze case MDOC_Va: 2730526e306bSschwarze return 12; 273149aff9f8Sschwarze case MDOC_Vt: 2732526e306bSschwarze return 12; 273349aff9f8Sschwarze case MDOC_Xr: 2734526e306bSschwarze return 10; 273519a69263Sschwarze default: 273619a69263Sschwarze break; 273719a69263Sschwarze }; 2738526e306bSschwarze return 0; 273919a69263Sschwarze } 2740