1*cec8643bSMichal Nowak /* $Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3698f87a4SGarrett D'Amore * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4*cec8643bSMichal Nowak * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> 5260e9a87SYuri Pankov * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 6f6b3f249SJason King * Copyright 2018, Joyent, Inc. 795c635efSGarrett D'Amore * 895c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 995c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 1095c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 1195c635efSGarrett D'Amore * 12371584c2SYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1395c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14371584c2SYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1595c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1695c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1795c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1895c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1995c635efSGarrett D'Amore */ 2095c635efSGarrett D'Amore #include "config.h" 2195c635efSGarrett D'Amore 22260e9a87SYuri Pankov #include <sys/types.h> 23698f87a4SGarrett D'Amore #ifndef OSNAME 2495c635efSGarrett D'Amore #include <sys/utsname.h> 2595c635efSGarrett D'Amore #endif 2695c635efSGarrett D'Amore 2795c635efSGarrett D'Amore #include <assert.h> 2895c635efSGarrett D'Amore #include <ctype.h> 2995c635efSGarrett D'Amore #include <limits.h> 3095c635efSGarrett D'Amore #include <stdio.h> 3195c635efSGarrett D'Amore #include <stdlib.h> 3295c635efSGarrett D'Amore #include <string.h> 3395c635efSGarrett D'Amore #include <time.h> 3495c635efSGarrett D'Amore 35260e9a87SYuri Pankov #include "mandoc_aux.h" 36371584c2SYuri Pankov #include "mandoc.h" 37c66b8046SYuri Pankov #include "mandoc_xr.h" 38371584c2SYuri Pankov #include "roff.h" 39371584c2SYuri Pankov #include "mdoc.h" 4095c635efSGarrett D'Amore #include "libmandoc.h" 41371584c2SYuri Pankov #include "roff_int.h" 42371584c2SYuri Pankov #include "libmdoc.h" 4395c635efSGarrett D'Amore 4495c635efSGarrett D'Amore /* FIXME: .Bl -diag can't have non-text children in HEAD. */ 4595c635efSGarrett D'Amore 46371584c2SYuri Pankov #define POST_ARGS struct roff_man *mdoc 4795c635efSGarrett D'Amore 4895c635efSGarrett D'Amore enum check_ineq { 4995c635efSGarrett D'Amore CHECK_LT, 5095c635efSGarrett D'Amore CHECK_GT, 5195c635efSGarrett D'Amore CHECK_EQ 5295c635efSGarrett D'Amore }; 5395c635efSGarrett D'Amore 54260e9a87SYuri Pankov typedef void (*v_post)(POST_ARGS); 5595c635efSGarrett D'Amore 56a40ea1a7SYuri Pankov static int build_list(struct roff_man *, int); 57371584c2SYuri Pankov static void check_argv(struct roff_man *, 58371584c2SYuri Pankov struct roff_node *, struct mdoc_argv *); 59371584c2SYuri Pankov static void check_args(struct roff_man *, struct roff_node *); 606640c13bSYuri Pankov static void check_text(struct roff_man *, int, int, char *); 616640c13bSYuri Pankov static void check_text_em(struct roff_man *, int, int, char *); 62c66b8046SYuri Pankov static void check_toptext(struct roff_man *, int, int, const char *); 63371584c2SYuri Pankov static int child_an(const struct roff_node *); 64c66b8046SYuri Pankov static size_t macro2len(enum roff_tok); 65c66b8046SYuri Pankov static void rewrite_macro2len(struct roff_man *, char **); 66c66b8046SYuri Pankov static int similar(const char *, const char *); 67*cec8643bSMichal Nowak static void post_abort(POST_ARGS); 68260e9a87SYuri Pankov 69260e9a87SYuri Pankov static void post_an(POST_ARGS); 70371584c2SYuri Pankov static void post_an_norm(POST_ARGS); 71260e9a87SYuri Pankov static void post_at(POST_ARGS); 72371584c2SYuri Pankov static void post_bd(POST_ARGS); 73260e9a87SYuri Pankov static void post_bf(POST_ARGS); 74260e9a87SYuri Pankov static void post_bk(POST_ARGS); 75260e9a87SYuri Pankov static void post_bl(POST_ARGS); 76260e9a87SYuri Pankov static void post_bl_block(POST_ARGS); 77260e9a87SYuri Pankov static void post_bl_head(POST_ARGS); 78371584c2SYuri Pankov static void post_bl_norm(POST_ARGS); 79260e9a87SYuri Pankov static void post_bx(POST_ARGS); 80260e9a87SYuri Pankov static void post_defaults(POST_ARGS); 81371584c2SYuri Pankov static void post_display(POST_ARGS); 82260e9a87SYuri Pankov static void post_dd(POST_ARGS); 83c66b8046SYuri Pankov static void post_delim(POST_ARGS); 84c66b8046SYuri Pankov static void post_delim_nb(POST_ARGS); 85260e9a87SYuri Pankov static void post_dt(POST_ARGS); 86260e9a87SYuri Pankov static void post_en(POST_ARGS); 87260e9a87SYuri Pankov static void post_es(POST_ARGS); 88260e9a87SYuri Pankov static void post_eoln(POST_ARGS); 89260e9a87SYuri Pankov static void post_ex(POST_ARGS); 90260e9a87SYuri Pankov static void post_fa(POST_ARGS); 91260e9a87SYuri Pankov static void post_fn(POST_ARGS); 92260e9a87SYuri Pankov static void post_fname(POST_ARGS); 93260e9a87SYuri Pankov static void post_fo(POST_ARGS); 94260e9a87SYuri Pankov static void post_hyph(POST_ARGS); 95260e9a87SYuri Pankov static void post_ignpar(POST_ARGS); 96260e9a87SYuri Pankov static void post_it(POST_ARGS); 97260e9a87SYuri Pankov static void post_lb(POST_ARGS); 98260e9a87SYuri Pankov static void post_nd(POST_ARGS); 99260e9a87SYuri Pankov static void post_nm(POST_ARGS); 100260e9a87SYuri Pankov static void post_ns(POST_ARGS); 101371584c2SYuri Pankov static void post_obsolete(POST_ARGS); 102260e9a87SYuri Pankov static void post_os(POST_ARGS); 103260e9a87SYuri Pankov static void post_par(POST_ARGS); 104371584c2SYuri Pankov static void post_prevpar(POST_ARGS); 105260e9a87SYuri Pankov static void post_root(POST_ARGS); 106260e9a87SYuri Pankov static void post_rs(POST_ARGS); 107a40ea1a7SYuri Pankov static void post_rv(POST_ARGS); 108260e9a87SYuri Pankov static void post_sh(POST_ARGS); 109260e9a87SYuri Pankov static void post_sh_head(POST_ARGS); 110260e9a87SYuri Pankov static void post_sh_name(POST_ARGS); 111260e9a87SYuri Pankov static void post_sh_see_also(POST_ARGS); 112260e9a87SYuri Pankov static void post_sh_authors(POST_ARGS); 113260e9a87SYuri Pankov static void post_sm(POST_ARGS); 114260e9a87SYuri Pankov static void post_st(POST_ARGS); 115371584c2SYuri Pankov static void post_std(POST_ARGS); 116c66b8046SYuri Pankov static void post_sx(POST_ARGS); 117c66b8046SYuri Pankov static void post_useless(POST_ARGS); 118a40ea1a7SYuri Pankov static void post_xr(POST_ARGS); 119a40ea1a7SYuri Pankov static void post_xx(POST_ARGS); 120371584c2SYuri Pankov 121*cec8643bSMichal Nowak static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { 122371584c2SYuri Pankov post_dd, /* Dd */ 123371584c2SYuri Pankov post_dt, /* Dt */ 124371584c2SYuri Pankov post_os, /* Os */ 125371584c2SYuri Pankov post_sh, /* Sh */ 126371584c2SYuri Pankov post_ignpar, /* Ss */ 127371584c2SYuri Pankov post_par, /* Pp */ 128371584c2SYuri Pankov post_display, /* D1 */ 129371584c2SYuri Pankov post_display, /* Dl */ 130371584c2SYuri Pankov post_display, /* Bd */ 131371584c2SYuri Pankov NULL, /* Ed */ 132371584c2SYuri Pankov post_bl, /* Bl */ 133371584c2SYuri Pankov NULL, /* El */ 134371584c2SYuri Pankov post_it, /* It */ 135c66b8046SYuri Pankov post_delim_nb, /* Ad */ 136371584c2SYuri Pankov post_an, /* An */ 137c66b8046SYuri Pankov NULL, /* Ap */ 138371584c2SYuri Pankov post_defaults, /* Ar */ 139371584c2SYuri Pankov NULL, /* Cd */ 140c66b8046SYuri Pankov post_delim_nb, /* Cm */ 141c66b8046SYuri Pankov post_delim_nb, /* Dv */ 142c66b8046SYuri Pankov post_delim_nb, /* Er */ 143c66b8046SYuri Pankov post_delim_nb, /* Ev */ 144371584c2SYuri Pankov post_ex, /* Ex */ 145371584c2SYuri Pankov post_fa, /* Fa */ 146371584c2SYuri Pankov NULL, /* Fd */ 147c66b8046SYuri Pankov post_delim_nb, /* Fl */ 148371584c2SYuri Pankov post_fn, /* Fn */ 149c66b8046SYuri Pankov post_delim_nb, /* Ft */ 150c66b8046SYuri Pankov post_delim_nb, /* Ic */ 151c66b8046SYuri Pankov post_delim_nb, /* In */ 152371584c2SYuri Pankov post_defaults, /* Li */ 153371584c2SYuri Pankov post_nd, /* Nd */ 154371584c2SYuri Pankov post_nm, /* Nm */ 155c66b8046SYuri Pankov post_delim_nb, /* Op */ 156*cec8643bSMichal Nowak post_abort, /* Ot */ 157371584c2SYuri Pankov post_defaults, /* Pa */ 158a40ea1a7SYuri Pankov post_rv, /* Rv */ 159371584c2SYuri Pankov post_st, /* St */ 160c66b8046SYuri Pankov post_delim_nb, /* Va */ 161c66b8046SYuri Pankov post_delim_nb, /* Vt */ 162a40ea1a7SYuri Pankov post_xr, /* Xr */ 163371584c2SYuri Pankov NULL, /* %A */ 164371584c2SYuri Pankov post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ 165371584c2SYuri Pankov NULL, /* %D */ 166371584c2SYuri Pankov NULL, /* %I */ 167371584c2SYuri Pankov NULL, /* %J */ 168371584c2SYuri Pankov post_hyph, /* %N */ 169371584c2SYuri Pankov post_hyph, /* %O */ 170371584c2SYuri Pankov NULL, /* %P */ 171371584c2SYuri Pankov post_hyph, /* %R */ 172371584c2SYuri Pankov post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ 173371584c2SYuri Pankov NULL, /* %V */ 174371584c2SYuri Pankov NULL, /* Ac */ 1756640c13bSYuri Pankov NULL, /* Ao */ 176c66b8046SYuri Pankov post_delim_nb, /* Aq */ 177371584c2SYuri Pankov post_at, /* At */ 178371584c2SYuri Pankov NULL, /* Bc */ 179371584c2SYuri Pankov post_bf, /* Bf */ 1806640c13bSYuri Pankov NULL, /* Bo */ 181371584c2SYuri Pankov NULL, /* Bq */ 182a40ea1a7SYuri Pankov post_xx, /* Bsx */ 183371584c2SYuri Pankov post_bx, /* Bx */ 184371584c2SYuri Pankov post_obsolete, /* Db */ 185371584c2SYuri Pankov NULL, /* Dc */ 186371584c2SYuri Pankov NULL, /* Do */ 187371584c2SYuri Pankov NULL, /* Dq */ 188371584c2SYuri Pankov NULL, /* Ec */ 189371584c2SYuri Pankov NULL, /* Ef */ 190c66b8046SYuri Pankov post_delim_nb, /* Em */ 191371584c2SYuri Pankov NULL, /* Eo */ 192a40ea1a7SYuri Pankov post_xx, /* Fx */ 193c66b8046SYuri Pankov post_delim_nb, /* Ms */ 194371584c2SYuri Pankov NULL, /* No */ 195371584c2SYuri Pankov post_ns, /* Ns */ 196a40ea1a7SYuri Pankov post_xx, /* Nx */ 197a40ea1a7SYuri Pankov post_xx, /* Ox */ 198371584c2SYuri Pankov NULL, /* Pc */ 199371584c2SYuri Pankov NULL, /* Pf */ 2006640c13bSYuri Pankov NULL, /* Po */ 201c66b8046SYuri Pankov post_delim_nb, /* Pq */ 202371584c2SYuri Pankov NULL, /* Qc */ 203c66b8046SYuri Pankov post_delim_nb, /* Ql */ 2046640c13bSYuri Pankov NULL, /* Qo */ 205c66b8046SYuri Pankov post_delim_nb, /* Qq */ 206371584c2SYuri Pankov NULL, /* Re */ 207371584c2SYuri Pankov post_rs, /* Rs */ 208371584c2SYuri Pankov NULL, /* Sc */ 2096640c13bSYuri Pankov NULL, /* So */ 210c66b8046SYuri Pankov post_delim_nb, /* Sq */ 211371584c2SYuri Pankov post_sm, /* Sm */ 212c66b8046SYuri Pankov post_sx, /* Sx */ 213c66b8046SYuri Pankov post_delim_nb, /* Sy */ 214c66b8046SYuri Pankov post_useless, /* Tn */ 215a40ea1a7SYuri Pankov post_xx, /* Ux */ 216371584c2SYuri Pankov NULL, /* Xc */ 217371584c2SYuri Pankov NULL, /* Xo */ 218371584c2SYuri Pankov post_fo, /* Fo */ 219371584c2SYuri Pankov NULL, /* Fc */ 2206640c13bSYuri Pankov NULL, /* Oo */ 221371584c2SYuri Pankov NULL, /* Oc */ 222371584c2SYuri Pankov post_bk, /* Bk */ 223371584c2SYuri Pankov NULL, /* Ek */ 224371584c2SYuri Pankov post_eoln, /* Bt */ 225c66b8046SYuri Pankov post_obsolete, /* Hf */ 226371584c2SYuri Pankov post_obsolete, /* Fr */ 227371584c2SYuri Pankov post_eoln, /* Ud */ 228371584c2SYuri Pankov post_lb, /* Lb */ 229*cec8643bSMichal Nowak post_abort, /* Lp */ 230c66b8046SYuri Pankov post_delim_nb, /* Lk */ 231371584c2SYuri Pankov post_defaults, /* Mt */ 232c66b8046SYuri Pankov post_delim_nb, /* Brq */ 2336640c13bSYuri Pankov NULL, /* Bro */ 234371584c2SYuri Pankov NULL, /* Brc */ 235371584c2SYuri Pankov NULL, /* %C */ 236371584c2SYuri Pankov post_es, /* Es */ 237371584c2SYuri Pankov post_en, /* En */ 238a40ea1a7SYuri Pankov post_xx, /* Dx */ 239371584c2SYuri Pankov NULL, /* %Q */ 240371584c2SYuri Pankov NULL, /* %U */ 241371584c2SYuri Pankov NULL, /* Ta */ 24295c635efSGarrett D'Amore }; 24395c635efSGarrett D'Amore 24495c635efSGarrett D'Amore #define RSORD_MAX 14 /* Number of `Rs' blocks. */ 24595c635efSGarrett D'Amore 246c66b8046SYuri Pankov static const enum roff_tok rsord[RSORD_MAX] = { 24795c635efSGarrett D'Amore MDOC__A, 24895c635efSGarrett D'Amore MDOC__T, 24995c635efSGarrett D'Amore MDOC__B, 25095c635efSGarrett D'Amore MDOC__I, 25195c635efSGarrett D'Amore MDOC__J, 25295c635efSGarrett D'Amore MDOC__R, 25395c635efSGarrett D'Amore MDOC__N, 25495c635efSGarrett D'Amore MDOC__V, 255698f87a4SGarrett D'Amore MDOC__U, 25695c635efSGarrett D'Amore MDOC__P, 25795c635efSGarrett D'Amore MDOC__Q, 25895c635efSGarrett D'Amore MDOC__C, 259698f87a4SGarrett D'Amore MDOC__D, 260698f87a4SGarrett D'Amore MDOC__O 26195c635efSGarrett D'Amore }; 26295c635efSGarrett D'Amore 26395c635efSGarrett D'Amore static const char * const secnames[SEC__MAX] = { 26495c635efSGarrett D'Amore NULL, 26595c635efSGarrett D'Amore "NAME", 26695c635efSGarrett D'Amore "LIBRARY", 26795c635efSGarrett D'Amore "SYNOPSIS", 26895c635efSGarrett D'Amore "DESCRIPTION", 269260e9a87SYuri Pankov "CONTEXT", 27095c635efSGarrett D'Amore "IMPLEMENTATION NOTES", 27195c635efSGarrett D'Amore "RETURN VALUES", 27295c635efSGarrett D'Amore "ENVIRONMENT", 27395c635efSGarrett D'Amore "FILES", 27495c635efSGarrett D'Amore "EXIT STATUS", 27595c635efSGarrett D'Amore "EXAMPLES", 27695c635efSGarrett D'Amore "DIAGNOSTICS", 27795c635efSGarrett D'Amore "COMPATIBILITY", 27895c635efSGarrett D'Amore "ERRORS", 27995c635efSGarrett D'Amore "SEE ALSO", 28095c635efSGarrett D'Amore "STANDARDS", 28195c635efSGarrett D'Amore "HISTORY", 28295c635efSGarrett D'Amore "AUTHORS", 28395c635efSGarrett D'Amore "CAVEATS", 28495c635efSGarrett D'Amore "BUGS", 28595c635efSGarrett D'Amore "SECURITY CONSIDERATIONS", 28695c635efSGarrett D'Amore NULL 28795c635efSGarrett D'Amore }; 28895c635efSGarrett D'Amore 289260e9a87SYuri Pankov 290*cec8643bSMichal Nowak /* Validate the subtree rooted at mdoc->last. */ 291260e9a87SYuri Pankov void 292*cec8643bSMichal Nowak mdoc_validate(struct roff_man *mdoc) 29395c635efSGarrett D'Amore { 2946640c13bSYuri Pankov struct roff_node *n, *np; 295c66b8046SYuri Pankov const v_post *p; 29695c635efSGarrett D'Amore 297*cec8643bSMichal Nowak /* 298*cec8643bSMichal Nowak * Translate obsolete macros to modern macros first 299*cec8643bSMichal Nowak * such that later code does not need to look 300*cec8643bSMichal Nowak * for the obsolete versions. 301*cec8643bSMichal Nowak */ 302*cec8643bSMichal Nowak 303371584c2SYuri Pankov n = mdoc->last; 304*cec8643bSMichal Nowak switch (n->tok) { 305*cec8643bSMichal Nowak case MDOC_Lp: 306*cec8643bSMichal Nowak n->tok = MDOC_Pp; 307*cec8643bSMichal Nowak break; 308*cec8643bSMichal Nowak case MDOC_Ot: 309*cec8643bSMichal Nowak post_obsolete(mdoc); 310*cec8643bSMichal Nowak n->tok = MDOC_Ft; 311*cec8643bSMichal Nowak break; 312*cec8643bSMichal Nowak default: 313*cec8643bSMichal Nowak break; 314*cec8643bSMichal Nowak } 315*cec8643bSMichal Nowak 316*cec8643bSMichal Nowak /* 317*cec8643bSMichal Nowak * Iterate over all children, recursing into each one 318*cec8643bSMichal Nowak * in turn, depth-first. 319*cec8643bSMichal Nowak */ 320*cec8643bSMichal Nowak 321371584c2SYuri Pankov mdoc->last = mdoc->last->child; 322371584c2SYuri Pankov while (mdoc->last != NULL) { 323*cec8643bSMichal Nowak mdoc_validate(mdoc); 324371584c2SYuri Pankov if (mdoc->last == n) 325371584c2SYuri Pankov mdoc->last = mdoc->last->child; 326371584c2SYuri Pankov else 327371584c2SYuri Pankov mdoc->last = mdoc->last->next; 328371584c2SYuri Pankov } 329371584c2SYuri Pankov 330*cec8643bSMichal Nowak /* Finally validate the macro itself. */ 331*cec8643bSMichal Nowak 332371584c2SYuri Pankov mdoc->last = n; 333371584c2SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 33495c635efSGarrett D'Amore switch (n->type) { 335371584c2SYuri Pankov case ROFFT_TEXT: 3366640c13bSYuri Pankov np = n->parent; 337a40ea1a7SYuri Pankov if (n->sec != SEC_SYNOPSIS || 3386640c13bSYuri Pankov (np->tok != MDOC_Cd && np->tok != MDOC_Fd)) 339260e9a87SYuri Pankov check_text(mdoc, n->line, n->pos, n->string); 340*cec8643bSMichal Nowak if ((n->flags & NODE_NOFILL) == 0 && 3416640c13bSYuri Pankov (np->tok != MDOC_It || np->type != ROFFT_HEAD || 3426640c13bSYuri Pankov np->parent->parent->norm->Bl.type != LIST_diag)) 3436640c13bSYuri Pankov check_text_em(mdoc, n->line, n->pos, n->string); 3446640c13bSYuri Pankov if (np->tok == MDOC_It || (np->type == ROFFT_BODY && 3456640c13bSYuri Pankov (np->tok == MDOC_Sh || np->tok == MDOC_Ss))) 346c66b8046SYuri Pankov check_toptext(mdoc, n->line, n->pos, n->string); 34795c635efSGarrett D'Amore break; 3486640c13bSYuri Pankov case ROFFT_COMMENT: 349371584c2SYuri Pankov case ROFFT_EQN: 350371584c2SYuri Pankov case ROFFT_TBL: 35195c635efSGarrett D'Amore break; 352371584c2SYuri Pankov case ROFFT_ROOT: 353260e9a87SYuri Pankov post_root(mdoc); 35495c635efSGarrett D'Amore break; 35595c635efSGarrett D'Amore default: 356371584c2SYuri Pankov check_args(mdoc, mdoc->last); 35795c635efSGarrett D'Amore 358260e9a87SYuri Pankov /* 359260e9a87SYuri Pankov * Closing delimiters are not special at the 360260e9a87SYuri Pankov * beginning of a block, opening delimiters 361260e9a87SYuri Pankov * are not special at the end. 362260e9a87SYuri Pankov */ 36395c635efSGarrett D'Amore 364260e9a87SYuri Pankov if (n->child != NULL) 365a40ea1a7SYuri Pankov n->child->flags &= ~NODE_DELIMC; 366260e9a87SYuri Pankov if (n->last != NULL) 367a40ea1a7SYuri Pankov n->last->flags &= ~NODE_DELIMO; 36895c635efSGarrett D'Amore 369260e9a87SYuri Pankov /* Call the macro's postprocessor. */ 37095c635efSGarrett D'Amore 371c66b8046SYuri Pankov if (n->tok < ROFF_MAX) { 372*cec8643bSMichal Nowak roff_validate(mdoc); 373c66b8046SYuri Pankov break; 374c66b8046SYuri Pankov } 375c66b8046SYuri Pankov 376c66b8046SYuri Pankov assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 377*cec8643bSMichal Nowak p = mdoc_valids + (n->tok - MDOC_Dd); 378260e9a87SYuri Pankov if (*p) 379260e9a87SYuri Pankov (*p)(mdoc); 380371584c2SYuri Pankov if (mdoc->last == n) 381371584c2SYuri Pankov mdoc_state(mdoc, n); 382260e9a87SYuri Pankov break; 383260e9a87SYuri Pankov } 38495c635efSGarrett D'Amore } 38595c635efSGarrett D'Amore 38695c635efSGarrett D'Amore static void 387371584c2SYuri Pankov check_args(struct roff_man *mdoc, struct roff_node *n) 38895c635efSGarrett D'Amore { 38995c635efSGarrett D'Amore int i; 39095c635efSGarrett D'Amore 39195c635efSGarrett D'Amore if (NULL == n->args) 39295c635efSGarrett D'Amore return; 39395c635efSGarrett D'Amore 39495c635efSGarrett D'Amore assert(n->args->argc); 39595c635efSGarrett D'Amore for (i = 0; i < (int)n->args->argc; i++) 396698f87a4SGarrett D'Amore check_argv(mdoc, n, &n->args->argv[i]); 39795c635efSGarrett D'Amore } 39895c635efSGarrett D'Amore 39995c635efSGarrett D'Amore static void 400371584c2SYuri Pankov check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) 40195c635efSGarrett D'Amore { 40295c635efSGarrett D'Amore int i; 40395c635efSGarrett D'Amore 40495c635efSGarrett D'Amore for (i = 0; i < (int)v->sz; i++) 405698f87a4SGarrett D'Amore check_text(mdoc, v->line, v->pos, v->value[i]); 40695c635efSGarrett D'Amore } 40795c635efSGarrett D'Amore 40895c635efSGarrett D'Amore static void 409371584c2SYuri Pankov check_text(struct roff_man *mdoc, int ln, int pos, char *p) 41095c635efSGarrett D'Amore { 41195c635efSGarrett D'Amore char *cp; 41295c635efSGarrett D'Amore 413*cec8643bSMichal Nowak if (mdoc->last->flags & NODE_NOFILL) 41495c635efSGarrett D'Amore return; 41595c635efSGarrett D'Amore 41695c635efSGarrett D'Amore for (cp = p; NULL != (p = strchr(p, '\t')); p++) 417*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL); 41895c635efSGarrett D'Amore } 41995c635efSGarrett D'Amore 4206640c13bSYuri Pankov static void 4216640c13bSYuri Pankov check_text_em(struct roff_man *mdoc, int ln, int pos, char *p) 4226640c13bSYuri Pankov { 4236640c13bSYuri Pankov const struct roff_node *np, *nn; 4246640c13bSYuri Pankov char *cp; 4256640c13bSYuri Pankov 4266640c13bSYuri Pankov np = mdoc->last->prev; 4276640c13bSYuri Pankov nn = mdoc->last->next; 4286640c13bSYuri Pankov 4296640c13bSYuri Pankov /* Look for em-dashes wrongly encoded as "--". */ 4306640c13bSYuri Pankov 4316640c13bSYuri Pankov for (cp = p; *cp != '\0'; cp++) { 4326640c13bSYuri Pankov if (cp[0] != '-' || cp[1] != '-') 4336640c13bSYuri Pankov continue; 4346640c13bSYuri Pankov cp++; 4356640c13bSYuri Pankov 4366640c13bSYuri Pankov /* Skip input sequences of more than two '-'. */ 4376640c13bSYuri Pankov 4386640c13bSYuri Pankov if (cp[1] == '-') { 4396640c13bSYuri Pankov while (cp[1] == '-') 4406640c13bSYuri Pankov cp++; 4416640c13bSYuri Pankov continue; 4426640c13bSYuri Pankov } 4436640c13bSYuri Pankov 4446640c13bSYuri Pankov /* Skip "--" directly attached to something else. */ 4456640c13bSYuri Pankov 4466640c13bSYuri Pankov if ((cp - p > 1 && cp[-2] != ' ') || 4476640c13bSYuri Pankov (cp[1] != '\0' && cp[1] != ' ')) 4486640c13bSYuri Pankov continue; 4496640c13bSYuri Pankov 4506640c13bSYuri Pankov /* Require a letter right before or right afterwards. */ 4516640c13bSYuri Pankov 4526640c13bSYuri Pankov if ((cp - p > 2 ? 4536640c13bSYuri Pankov isalpha((unsigned char)cp[-3]) : 4546640c13bSYuri Pankov np != NULL && 4556640c13bSYuri Pankov np->type == ROFFT_TEXT && 4566640c13bSYuri Pankov *np->string != '\0' && 4576640c13bSYuri Pankov isalpha((unsigned char)np->string[ 4586640c13bSYuri Pankov strlen(np->string) - 1])) || 4596640c13bSYuri Pankov (cp[1] != '\0' && cp[2] != '\0' ? 4606640c13bSYuri Pankov isalpha((unsigned char)cp[2]) : 4616640c13bSYuri Pankov nn != NULL && 4626640c13bSYuri Pankov nn->type == ROFFT_TEXT && 4636640c13bSYuri Pankov isalpha((unsigned char)*nn->string))) { 464*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DASHDASH, 4656640c13bSYuri Pankov ln, pos + (int)(cp - p) - 1, NULL); 4666640c13bSYuri Pankov break; 4676640c13bSYuri Pankov } 4686640c13bSYuri Pankov } 4696640c13bSYuri Pankov } 4706640c13bSYuri Pankov 471c66b8046SYuri Pankov static void 472c66b8046SYuri Pankov check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) 473c66b8046SYuri Pankov { 474c66b8046SYuri Pankov const char *cp, *cpr; 475c66b8046SYuri Pankov 476c66b8046SYuri Pankov if (*p == '\0') 477c66b8046SYuri Pankov return; 478c66b8046SYuri Pankov 479c66b8046SYuri Pankov if ((cp = strstr(p, "OpenBSD")) != NULL) 480*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox"); 481c66b8046SYuri Pankov if ((cp = strstr(p, "NetBSD")) != NULL) 482*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx"); 483c66b8046SYuri Pankov if ((cp = strstr(p, "FreeBSD")) != NULL) 484*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx"); 485c66b8046SYuri Pankov if ((cp = strstr(p, "DragonFly")) != NULL) 486*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx"); 487c66b8046SYuri Pankov 488c66b8046SYuri Pankov cp = p; 489c66b8046SYuri Pankov while ((cp = strstr(cp + 1, "()")) != NULL) { 490c66b8046SYuri Pankov for (cpr = cp - 1; cpr >= p; cpr--) 491c66b8046SYuri Pankov if (*cpr != '_' && !isalnum((unsigned char)*cpr)) 492c66b8046SYuri Pankov break; 493c66b8046SYuri Pankov if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { 494c66b8046SYuri Pankov cpr++; 495*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p), 496c66b8046SYuri Pankov "%.*s()", (int)(cp - cpr), cpr); 497c66b8046SYuri Pankov } 498c66b8046SYuri Pankov } 499c66b8046SYuri Pankov } 500c66b8046SYuri Pankov 501*cec8643bSMichal Nowak static void 502*cec8643bSMichal Nowak post_abort(POST_ARGS) 503*cec8643bSMichal Nowak { 504*cec8643bSMichal Nowak abort(); 505*cec8643bSMichal Nowak } 506*cec8643bSMichal Nowak 507c66b8046SYuri Pankov static void 508c66b8046SYuri Pankov post_delim(POST_ARGS) 509c66b8046SYuri Pankov { 510c66b8046SYuri Pankov const struct roff_node *nch; 511c66b8046SYuri Pankov const char *lc; 512c66b8046SYuri Pankov enum mdelim delim; 513c66b8046SYuri Pankov enum roff_tok tok; 514c66b8046SYuri Pankov 515c66b8046SYuri Pankov tok = mdoc->last->tok; 516c66b8046SYuri Pankov nch = mdoc->last->last; 517c66b8046SYuri Pankov if (nch == NULL || nch->type != ROFFT_TEXT) 518c66b8046SYuri Pankov return; 519c66b8046SYuri Pankov lc = strchr(nch->string, '\0') - 1; 520c66b8046SYuri Pankov if (lc < nch->string) 521c66b8046SYuri Pankov return; 522c66b8046SYuri Pankov delim = mdoc_isdelim(lc); 523c66b8046SYuri Pankov if (delim == DELIM_NONE || delim == DELIM_OPEN) 524c66b8046SYuri Pankov return; 525c66b8046SYuri Pankov if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh || 526c66b8046SYuri Pankov tok == MDOC_Ss || tok == MDOC_Fo)) 527c66b8046SYuri Pankov return; 528c66b8046SYuri Pankov 529*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DELIM, nch->line, 530*cec8643bSMichal Nowak nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], 531c66b8046SYuri Pankov nch == mdoc->last->child ? "" : " ...", nch->string); 532c66b8046SYuri Pankov } 533c66b8046SYuri Pankov 534c66b8046SYuri Pankov static void 535c66b8046SYuri Pankov post_delim_nb(POST_ARGS) 536c66b8046SYuri Pankov { 537c66b8046SYuri Pankov const struct roff_node *nch; 538c66b8046SYuri Pankov const char *lc, *cp; 539c66b8046SYuri Pankov int nw; 540c66b8046SYuri Pankov enum mdelim delim; 541c66b8046SYuri Pankov enum roff_tok tok; 542c66b8046SYuri Pankov 543c66b8046SYuri Pankov /* 544c66b8046SYuri Pankov * Find candidates: at least two bytes, 545c66b8046SYuri Pankov * the last one a closing or middle delimiter. 546c66b8046SYuri Pankov */ 547c66b8046SYuri Pankov 548c66b8046SYuri Pankov tok = mdoc->last->tok; 549c66b8046SYuri Pankov nch = mdoc->last->last; 550c66b8046SYuri Pankov if (nch == NULL || nch->type != ROFFT_TEXT) 551c66b8046SYuri Pankov return; 552c66b8046SYuri Pankov lc = strchr(nch->string, '\0') - 1; 553c66b8046SYuri Pankov if (lc <= nch->string) 554c66b8046SYuri Pankov return; 555c66b8046SYuri Pankov delim = mdoc_isdelim(lc); 556c66b8046SYuri Pankov if (delim == DELIM_NONE || delim == DELIM_OPEN) 557c66b8046SYuri Pankov return; 558c66b8046SYuri Pankov 559c66b8046SYuri Pankov /* 560c66b8046SYuri Pankov * Reduce false positives by allowing various cases. 561c66b8046SYuri Pankov */ 562c66b8046SYuri Pankov 563c66b8046SYuri Pankov /* Escaped delimiters. */ 564c66b8046SYuri Pankov if (lc > nch->string + 1 && lc[-2] == '\\' && 565c66b8046SYuri Pankov (lc[-1] == '&' || lc[-1] == 'e')) 566c66b8046SYuri Pankov return; 567c66b8046SYuri Pankov 568c66b8046SYuri Pankov /* Specific byte sequences. */ 569c66b8046SYuri Pankov switch (*lc) { 570c66b8046SYuri Pankov case ')': 571c66b8046SYuri Pankov for (cp = lc; cp >= nch->string; cp--) 572c66b8046SYuri Pankov if (*cp == '(') 573c66b8046SYuri Pankov return; 574c66b8046SYuri Pankov break; 575c66b8046SYuri Pankov case '.': 576c66b8046SYuri Pankov if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') 577c66b8046SYuri Pankov return; 578c66b8046SYuri Pankov if (lc[-1] == '.') 579c66b8046SYuri Pankov return; 580c66b8046SYuri Pankov break; 581c66b8046SYuri Pankov case ';': 582c66b8046SYuri Pankov if (tok == MDOC_Vt) 583c66b8046SYuri Pankov return; 584c66b8046SYuri Pankov break; 585c66b8046SYuri Pankov case '?': 586c66b8046SYuri Pankov if (lc[-1] == '?') 587c66b8046SYuri Pankov return; 588c66b8046SYuri Pankov break; 589c66b8046SYuri Pankov case ']': 590c66b8046SYuri Pankov for (cp = lc; cp >= nch->string; cp--) 591c66b8046SYuri Pankov if (*cp == '[') 592c66b8046SYuri Pankov return; 593c66b8046SYuri Pankov break; 594c66b8046SYuri Pankov case '|': 595c66b8046SYuri Pankov if (lc == nch->string + 1 && lc[-1] == '|') 596c66b8046SYuri Pankov return; 597c66b8046SYuri Pankov default: 598c66b8046SYuri Pankov break; 599c66b8046SYuri Pankov } 600c66b8046SYuri Pankov 601c66b8046SYuri Pankov /* Exactly two non-alphanumeric bytes. */ 602c66b8046SYuri Pankov if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) 603c66b8046SYuri Pankov return; 604c66b8046SYuri Pankov 605c66b8046SYuri Pankov /* At least three alphabetic words with a sentence ending. */ 606c66b8046SYuri Pankov if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || 6076640c13bSYuri Pankov tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) { 608c66b8046SYuri Pankov nw = 0; 609c66b8046SYuri Pankov for (cp = lc - 1; cp >= nch->string; cp--) { 610c66b8046SYuri Pankov if (*cp == ' ') { 611c66b8046SYuri Pankov nw++; 612c66b8046SYuri Pankov if (cp > nch->string && cp[-1] == ',') 613c66b8046SYuri Pankov cp--; 614c66b8046SYuri Pankov } else if (isalpha((unsigned int)*cp)) { 615c66b8046SYuri Pankov if (nw > 1) 616c66b8046SYuri Pankov return; 617c66b8046SYuri Pankov } else 618c66b8046SYuri Pankov break; 619c66b8046SYuri Pankov } 620c66b8046SYuri Pankov } 621c66b8046SYuri Pankov 622*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DELIM_NB, nch->line, 623*cec8643bSMichal Nowak nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], 624c66b8046SYuri Pankov nch == mdoc->last->child ? "" : " ...", nch->string); 625c66b8046SYuri Pankov } 626c66b8046SYuri Pankov 627260e9a87SYuri Pankov static void 628371584c2SYuri Pankov post_bl_norm(POST_ARGS) 62995c635efSGarrett D'Amore { 630371584c2SYuri Pankov struct roff_node *n; 631260e9a87SYuri Pankov struct mdoc_argv *argv, *wa; 632260e9a87SYuri Pankov int i; 633260e9a87SYuri Pankov enum mdocargt mdoclt; 63495c635efSGarrett D'Amore enum mdoc_list lt; 63595c635efSGarrett D'Amore 636371584c2SYuri Pankov n = mdoc->last->parent; 637371584c2SYuri Pankov n->norm->Bl.type = LIST__NONE; 63895c635efSGarrett D'Amore 639260e9a87SYuri Pankov /* 64095c635efSGarrett D'Amore * First figure out which kind of list to use: bind ourselves to 64195c635efSGarrett D'Amore * the first mentioned list type and warn about any remaining 64295c635efSGarrett D'Amore * ones. If we find no list type, we default to LIST_item. 64395c635efSGarrett D'Amore */ 64495c635efSGarrett D'Amore 645260e9a87SYuri Pankov wa = (n->args == NULL) ? NULL : n->args->argv; 646260e9a87SYuri Pankov mdoclt = MDOC_ARG_MAX; 64795c635efSGarrett D'Amore for (i = 0; n->args && i < (int)n->args->argc; i++) { 648260e9a87SYuri Pankov argv = n->args->argv + i; 64995c635efSGarrett D'Amore lt = LIST__NONE; 650260e9a87SYuri Pankov switch (argv->arg) { 65195c635efSGarrett D'Amore /* Set list types. */ 652260e9a87SYuri Pankov case MDOC_Bullet: 65395c635efSGarrett D'Amore lt = LIST_bullet; 65495c635efSGarrett D'Amore break; 655260e9a87SYuri Pankov case MDOC_Dash: 65695c635efSGarrett D'Amore lt = LIST_dash; 65795c635efSGarrett D'Amore break; 658260e9a87SYuri Pankov case MDOC_Enum: 65995c635efSGarrett D'Amore lt = LIST_enum; 66095c635efSGarrett D'Amore break; 661260e9a87SYuri Pankov case MDOC_Hyphen: 66295c635efSGarrett D'Amore lt = LIST_hyphen; 66395c635efSGarrett D'Amore break; 664260e9a87SYuri Pankov case MDOC_Item: 66595c635efSGarrett D'Amore lt = LIST_item; 66695c635efSGarrett D'Amore break; 667260e9a87SYuri Pankov case MDOC_Tag: 66895c635efSGarrett D'Amore lt = LIST_tag; 66995c635efSGarrett D'Amore break; 670260e9a87SYuri Pankov case MDOC_Diag: 67195c635efSGarrett D'Amore lt = LIST_diag; 67295c635efSGarrett D'Amore break; 673260e9a87SYuri Pankov case MDOC_Hang: 67495c635efSGarrett D'Amore lt = LIST_hang; 67595c635efSGarrett D'Amore break; 676260e9a87SYuri Pankov case MDOC_Ohang: 67795c635efSGarrett D'Amore lt = LIST_ohang; 67895c635efSGarrett D'Amore break; 679260e9a87SYuri Pankov case MDOC_Inset: 68095c635efSGarrett D'Amore lt = LIST_inset; 68195c635efSGarrett D'Amore break; 682260e9a87SYuri Pankov case MDOC_Column: 68395c635efSGarrett D'Amore lt = LIST_column; 68495c635efSGarrett D'Amore break; 68595c635efSGarrett D'Amore /* Set list arguments. */ 686260e9a87SYuri Pankov case MDOC_Compact: 687260e9a87SYuri Pankov if (n->norm->Bl.comp) 688260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_REP, 689*cec8643bSMichal Nowak argv->line, argv->pos, "Bl -compact"); 690260e9a87SYuri Pankov n->norm->Bl.comp = 1; 69195c635efSGarrett D'Amore break; 692260e9a87SYuri Pankov case MDOC_Width: 693260e9a87SYuri Pankov wa = argv; 694260e9a87SYuri Pankov if (0 == argv->sz) { 695260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_EMPTY, 696*cec8643bSMichal Nowak argv->line, argv->pos, "Bl -width"); 697260e9a87SYuri Pankov n->norm->Bl.width = "0n"; 69895c635efSGarrett D'Amore break; 69995c635efSGarrett D'Amore } 700260e9a87SYuri Pankov if (NULL != n->norm->Bl.width) 701*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_REP, 702*cec8643bSMichal Nowak argv->line, argv->pos, 703*cec8643bSMichal Nowak "Bl -width %s", argv->value[0]); 704c66b8046SYuri Pankov rewrite_macro2len(mdoc, argv->value); 705260e9a87SYuri Pankov n->norm->Bl.width = argv->value[0]; 70695c635efSGarrett D'Amore break; 707260e9a87SYuri Pankov case MDOC_Offset: 708260e9a87SYuri Pankov if (0 == argv->sz) { 709260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_EMPTY, 710*cec8643bSMichal Nowak argv->line, argv->pos, "Bl -offset"); 71195c635efSGarrett D'Amore break; 71295c635efSGarrett D'Amore } 713260e9a87SYuri Pankov if (NULL != n->norm->Bl.offs) 714*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_REP, 715*cec8643bSMichal Nowak argv->line, argv->pos, 716*cec8643bSMichal Nowak "Bl -offset %s", argv->value[0]); 717c66b8046SYuri Pankov rewrite_macro2len(mdoc, argv->value); 718260e9a87SYuri Pankov n->norm->Bl.offs = argv->value[0]; 71995c635efSGarrett D'Amore break; 72095c635efSGarrett D'Amore default: 72195c635efSGarrett D'Amore continue; 72295c635efSGarrett D'Amore } 723260e9a87SYuri Pankov if (LIST__NONE == lt) 724260e9a87SYuri Pankov continue; 725260e9a87SYuri Pankov mdoclt = argv->arg; 72695c635efSGarrett D'Amore 72795c635efSGarrett D'Amore /* Check: multiple list types. */ 72895c635efSGarrett D'Amore 729260e9a87SYuri Pankov if (LIST__NONE != n->norm->Bl.type) { 730*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos, 731260e9a87SYuri Pankov "Bl -%s", mdoc_argnames[argv->arg]); 732260e9a87SYuri Pankov continue; 73395c635efSGarrett D'Amore } 73495c635efSGarrett D'Amore 73595c635efSGarrett D'Amore /* The list type should come first. */ 73695c635efSGarrett D'Amore 737260e9a87SYuri Pankov if (n->norm->Bl.width || 738260e9a87SYuri Pankov n->norm->Bl.offs || 739260e9a87SYuri Pankov n->norm->Bl.comp) 740*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_LATETYPE, 741*cec8643bSMichal Nowak n->line, n->pos, "Bl -%s", 742260e9a87SYuri Pankov mdoc_argnames[n->args->argv[0].arg]); 743260e9a87SYuri Pankov 744260e9a87SYuri Pankov n->norm->Bl.type = lt; 745260e9a87SYuri Pankov if (LIST_column == lt) { 746260e9a87SYuri Pankov n->norm->Bl.ncols = argv->sz; 747260e9a87SYuri Pankov n->norm->Bl.cols = (void *)argv->value; 748260e9a87SYuri Pankov } 74995c635efSGarrett D'Amore } 75095c635efSGarrett D'Amore 75195c635efSGarrett D'Amore /* Allow lists to default to LIST_item. */ 75295c635efSGarrett D'Amore 75395c635efSGarrett D'Amore if (LIST__NONE == n->norm->Bl.type) { 754*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl"); 75595c635efSGarrett D'Amore n->norm->Bl.type = LIST_item; 756a40ea1a7SYuri Pankov mdoclt = MDOC_Item; 75795c635efSGarrett D'Amore } 75895c635efSGarrett D'Amore 759260e9a87SYuri Pankov /* 76095c635efSGarrett D'Amore * Validate the width field. Some list types don't need width 76195c635efSGarrett D'Amore * types and should be warned about them. Others should have it 762698f87a4SGarrett D'Amore * and must also be warned. Yet others have a default and need 763698f87a4SGarrett D'Amore * no warning. 76495c635efSGarrett D'Amore */ 76595c635efSGarrett D'Amore 76695c635efSGarrett D'Amore switch (n->norm->Bl.type) { 767260e9a87SYuri Pankov case LIST_tag: 768c66b8046SYuri Pankov if (n->norm->Bl.width == NULL) 769*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_NOWIDTH, 770260e9a87SYuri Pankov n->line, n->pos, "Bl -tag"); 77195c635efSGarrett D'Amore break; 772260e9a87SYuri Pankov case LIST_column: 773260e9a87SYuri Pankov case LIST_diag: 774260e9a87SYuri Pankov case LIST_ohang: 775260e9a87SYuri Pankov case LIST_inset: 776260e9a87SYuri Pankov case LIST_item: 777c66b8046SYuri Pankov if (n->norm->Bl.width != NULL) 778*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos, 779*cec8643bSMichal Nowak "Bl -%s", mdoc_argnames[mdoclt]); 780c66b8046SYuri Pankov n->norm->Bl.width = NULL; 78195c635efSGarrett D'Amore break; 782260e9a87SYuri Pankov case LIST_bullet: 783260e9a87SYuri Pankov case LIST_dash: 784260e9a87SYuri Pankov case LIST_hyphen: 785c66b8046SYuri Pankov if (n->norm->Bl.width == NULL) 786698f87a4SGarrett D'Amore n->norm->Bl.width = "2n"; 787698f87a4SGarrett D'Amore break; 788260e9a87SYuri Pankov case LIST_enum: 789c66b8046SYuri Pankov if (n->norm->Bl.width == NULL) 790698f87a4SGarrett D'Amore n->norm->Bl.width = "3n"; 791698f87a4SGarrett D'Amore break; 79295c635efSGarrett D'Amore default: 79395c635efSGarrett D'Amore break; 79495c635efSGarrett D'Amore } 79595c635efSGarrett D'Amore } 79695c635efSGarrett D'Amore 797260e9a87SYuri Pankov static void 798371584c2SYuri Pankov post_bd(POST_ARGS) 79995c635efSGarrett D'Amore { 800371584c2SYuri Pankov struct roff_node *n; 801260e9a87SYuri Pankov struct mdoc_argv *argv; 802260e9a87SYuri Pankov int i; 803260e9a87SYuri Pankov enum mdoc_disp dt; 80495c635efSGarrett D'Amore 805371584c2SYuri Pankov n = mdoc->last; 80695c635efSGarrett D'Amore for (i = 0; n->args && i < (int)n->args->argc; i++) { 807260e9a87SYuri Pankov argv = n->args->argv + i; 80895c635efSGarrett D'Amore dt = DISP__NONE; 80995c635efSGarrett D'Amore 810260e9a87SYuri Pankov switch (argv->arg) { 811260e9a87SYuri Pankov case MDOC_Centred: 812260e9a87SYuri Pankov dt = DISP_centered; 81395c635efSGarrett D'Amore break; 814260e9a87SYuri Pankov case MDOC_Ragged: 81595c635efSGarrett D'Amore dt = DISP_ragged; 81695c635efSGarrett D'Amore break; 817260e9a87SYuri Pankov case MDOC_Unfilled: 81895c635efSGarrett D'Amore dt = DISP_unfilled; 81995c635efSGarrett D'Amore break; 820260e9a87SYuri Pankov case MDOC_Filled: 82195c635efSGarrett D'Amore dt = DISP_filled; 82295c635efSGarrett D'Amore break; 823260e9a87SYuri Pankov case MDOC_Literal: 82495c635efSGarrett D'Amore dt = DISP_literal; 82595c635efSGarrett D'Amore break; 826260e9a87SYuri Pankov case MDOC_File: 827*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL); 828260e9a87SYuri Pankov break; 829260e9a87SYuri Pankov case MDOC_Offset: 830260e9a87SYuri Pankov if (0 == argv->sz) { 831260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_EMPTY, 832*cec8643bSMichal Nowak argv->line, argv->pos, "Bd -offset"); 83395c635efSGarrett D'Amore break; 83495c635efSGarrett D'Amore } 835260e9a87SYuri Pankov if (NULL != n->norm->Bd.offs) 836*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_REP, 837*cec8643bSMichal Nowak argv->line, argv->pos, 838*cec8643bSMichal Nowak "Bd -offset %s", argv->value[0]); 839c66b8046SYuri Pankov rewrite_macro2len(mdoc, argv->value); 840260e9a87SYuri Pankov n->norm->Bd.offs = argv->value[0]; 84195c635efSGarrett D'Amore break; 842260e9a87SYuri Pankov case MDOC_Compact: 843260e9a87SYuri Pankov if (n->norm->Bd.comp) 844260e9a87SYuri Pankov mandoc_msg(MANDOCERR_ARG_REP, 845*cec8643bSMichal Nowak argv->line, argv->pos, "Bd -compact"); 846260e9a87SYuri Pankov n->norm->Bd.comp = 1; 84795c635efSGarrett D'Amore break; 84895c635efSGarrett D'Amore default: 84995c635efSGarrett D'Amore abort(); 85095c635efSGarrett D'Amore } 851260e9a87SYuri Pankov if (DISP__NONE == dt) 852260e9a87SYuri Pankov continue; 85395c635efSGarrett D'Amore 854260e9a87SYuri Pankov if (DISP__NONE == n->norm->Bd.type) 85595c635efSGarrett D'Amore n->norm->Bd.type = dt; 856260e9a87SYuri Pankov else 857*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos, 858260e9a87SYuri Pankov "Bd -%s", mdoc_argnames[argv->arg]); 85995c635efSGarrett D'Amore } 86095c635efSGarrett D'Amore 86195c635efSGarrett D'Amore if (DISP__NONE == n->norm->Bd.type) { 862*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd"); 86395c635efSGarrett D'Amore n->norm->Bd.type = DISP_ragged; 86495c635efSGarrett D'Amore } 86595c635efSGarrett D'Amore } 86695c635efSGarrett D'Amore 867a40ea1a7SYuri Pankov /* 868a40ea1a7SYuri Pankov * Stand-alone line macros. 869a40ea1a7SYuri Pankov */ 870a40ea1a7SYuri Pankov 871260e9a87SYuri Pankov static void 872371584c2SYuri Pankov post_an_norm(POST_ARGS) 87395c635efSGarrett D'Amore { 874371584c2SYuri Pankov struct roff_node *n; 875260e9a87SYuri Pankov struct mdoc_argv *argv; 876260e9a87SYuri Pankov size_t i; 87795c635efSGarrett D'Amore 878371584c2SYuri Pankov n = mdoc->last; 879260e9a87SYuri Pankov if (n->args == NULL) 880260e9a87SYuri Pankov return; 88195c635efSGarrett D'Amore 882260e9a87SYuri Pankov for (i = 1; i < n->args->argc; i++) { 883260e9a87SYuri Pankov argv = n->args->argv + i; 884*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos, 885260e9a87SYuri Pankov "An -%s", mdoc_argnames[argv->arg]); 886260e9a87SYuri Pankov } 887260e9a87SYuri Pankov 888260e9a87SYuri Pankov argv = n->args->argv; 889260e9a87SYuri Pankov if (argv->arg == MDOC_Split) 89095c635efSGarrett D'Amore n->norm->An.auth = AUTH_split; 891260e9a87SYuri Pankov else if (argv->arg == MDOC_Nosplit) 89295c635efSGarrett D'Amore n->norm->An.auth = AUTH_nosplit; 89395c635efSGarrett D'Amore else 89495c635efSGarrett D'Amore abort(); 89595c635efSGarrett D'Amore } 89695c635efSGarrett D'Amore 897a40ea1a7SYuri Pankov static void 898a40ea1a7SYuri Pankov post_eoln(POST_ARGS) 899a40ea1a7SYuri Pankov { 900a40ea1a7SYuri Pankov struct roff_node *n; 901a40ea1a7SYuri Pankov 902c66b8046SYuri Pankov post_useless(mdoc); 903a40ea1a7SYuri Pankov n = mdoc->last; 904a40ea1a7SYuri Pankov if (n->child != NULL) 905*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, n->line, 906c66b8046SYuri Pankov n->pos, "%s %s", roff_name[n->tok], n->child->string); 907a40ea1a7SYuri Pankov 908a40ea1a7SYuri Pankov while (n->child != NULL) 909a40ea1a7SYuri Pankov roff_node_delete(mdoc, n->child); 910a40ea1a7SYuri Pankov 911a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? 912a40ea1a7SYuri Pankov "is currently in beta test." : "currently under development."); 913a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 914a40ea1a7SYuri Pankov mdoc->last = n; 915a40ea1a7SYuri Pankov } 916a40ea1a7SYuri Pankov 917a40ea1a7SYuri Pankov static int 918a40ea1a7SYuri Pankov build_list(struct roff_man *mdoc, int tok) 919a40ea1a7SYuri Pankov { 920a40ea1a7SYuri Pankov struct roff_node *n; 921a40ea1a7SYuri Pankov int ic; 922a40ea1a7SYuri Pankov 923a40ea1a7SYuri Pankov n = mdoc->last->next; 924a40ea1a7SYuri Pankov for (ic = 1;; ic++) { 925a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, tok); 926a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 927*cec8643bSMichal Nowak roff_node_relink(mdoc, n); 928a40ea1a7SYuri Pankov n = mdoc->last = mdoc->last->parent; 929a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 930a40ea1a7SYuri Pankov if (n->next == NULL) 931a40ea1a7SYuri Pankov return ic; 932a40ea1a7SYuri Pankov if (ic > 1 || n->next->next != NULL) { 933a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, ","); 934a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC; 935a40ea1a7SYuri Pankov } 936a40ea1a7SYuri Pankov n = mdoc->last->next; 937a40ea1a7SYuri Pankov if (n->next == NULL) { 938a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "and"); 939a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 940a40ea1a7SYuri Pankov } 941a40ea1a7SYuri Pankov } 942a40ea1a7SYuri Pankov } 943a40ea1a7SYuri Pankov 944a40ea1a7SYuri Pankov static void 945a40ea1a7SYuri Pankov post_ex(POST_ARGS) 946a40ea1a7SYuri Pankov { 947a40ea1a7SYuri Pankov struct roff_node *n; 948a40ea1a7SYuri Pankov int ic; 949a40ea1a7SYuri Pankov 950a40ea1a7SYuri Pankov post_std(mdoc); 951a40ea1a7SYuri Pankov 952a40ea1a7SYuri Pankov n = mdoc->last; 953a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 954a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "The"); 955a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 956a40ea1a7SYuri Pankov 957a40ea1a7SYuri Pankov if (mdoc->last->next != NULL) 958a40ea1a7SYuri Pankov ic = build_list(mdoc, MDOC_Nm); 959a40ea1a7SYuri Pankov else if (mdoc->meta.name != NULL) { 960a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); 961a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 962a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 963a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 964a40ea1a7SYuri Pankov mdoc->last = mdoc->last->parent; 965a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 966a40ea1a7SYuri Pankov ic = 1; 967a40ea1a7SYuri Pankov } else { 968*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex"); 969a40ea1a7SYuri Pankov ic = 0; 970a40ea1a7SYuri Pankov } 971a40ea1a7SYuri Pankov 972a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, 973a40ea1a7SYuri Pankov ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); 974a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 975a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, 976a40ea1a7SYuri Pankov "on success, and\\~>0 if an error occurs."); 977a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 978a40ea1a7SYuri Pankov mdoc->last = n; 979a40ea1a7SYuri Pankov } 980a40ea1a7SYuri Pankov 981a40ea1a7SYuri Pankov static void 982a40ea1a7SYuri Pankov post_lb(POST_ARGS) 983a40ea1a7SYuri Pankov { 984a40ea1a7SYuri Pankov struct roff_node *n; 985a40ea1a7SYuri Pankov const char *p; 986a40ea1a7SYuri Pankov 987c66b8046SYuri Pankov post_delim_nb(mdoc); 988c66b8046SYuri Pankov 989a40ea1a7SYuri Pankov n = mdoc->last; 990a40ea1a7SYuri Pankov assert(n->child->type == ROFFT_TEXT); 991a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 992a40ea1a7SYuri Pankov 993a40ea1a7SYuri Pankov if ((p = mdoc_a2lib(n->child->string)) != NULL) { 994a40ea1a7SYuri Pankov n->child->flags |= NODE_NOPRT; 995a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, p); 996a40ea1a7SYuri Pankov mdoc->last->flags = NODE_NOSRC; 997a40ea1a7SYuri Pankov mdoc->last = n; 998a40ea1a7SYuri Pankov return; 999a40ea1a7SYuri Pankov } 1000a40ea1a7SYuri Pankov 1001*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_LB_BAD, n->child->line, 1002c66b8046SYuri Pankov n->child->pos, "Lb %s", n->child->string); 1003c66b8046SYuri Pankov 1004a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "library"); 1005a40ea1a7SYuri Pankov mdoc->last->flags = NODE_NOSRC; 10066640c13bSYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "\\(lq"); 1007a40ea1a7SYuri Pankov mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 1008a40ea1a7SYuri Pankov mdoc->last = mdoc->last->next; 10096640c13bSYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "\\(rq"); 1010a40ea1a7SYuri Pankov mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; 1011a40ea1a7SYuri Pankov mdoc->last = n; 1012a40ea1a7SYuri Pankov } 1013a40ea1a7SYuri Pankov 1014a40ea1a7SYuri Pankov static void 1015a40ea1a7SYuri Pankov post_rv(POST_ARGS) 1016a40ea1a7SYuri Pankov { 1017a40ea1a7SYuri Pankov struct roff_node *n; 1018a40ea1a7SYuri Pankov int ic; 1019a40ea1a7SYuri Pankov 1020a40ea1a7SYuri Pankov post_std(mdoc); 1021a40ea1a7SYuri Pankov 1022a40ea1a7SYuri Pankov n = mdoc->last; 1023a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1024a40ea1a7SYuri Pankov if (n->child != NULL) { 1025a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "The"); 1026a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1027a40ea1a7SYuri Pankov ic = build_list(mdoc, MDOC_Fn); 1028a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, 1029a40ea1a7SYuri Pankov ic > 1 ? "functions return" : "function returns"); 1030a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1031a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, 1032a40ea1a7SYuri Pankov "the value\\~0 if successful;"); 1033a40ea1a7SYuri Pankov } else 1034a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " 1035a40ea1a7SYuri Pankov "completion, the value\\~0 is returned;"); 1036a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1037a40ea1a7SYuri Pankov 1038a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "otherwise " 1039a40ea1a7SYuri Pankov "the value\\~\\-1 is returned and the global variable"); 1040a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1041a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); 1042a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1043a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "errno"); 1044a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1045a40ea1a7SYuri Pankov mdoc->last = mdoc->last->parent; 1046a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 1047a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, 1048a40ea1a7SYuri Pankov "is set to indicate the error."); 1049a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 1050a40ea1a7SYuri Pankov mdoc->last = n; 1051a40ea1a7SYuri Pankov } 1052a40ea1a7SYuri Pankov 1053260e9a87SYuri Pankov static void 1054371584c2SYuri Pankov post_std(POST_ARGS) 105595c635efSGarrett D'Amore { 1056371584c2SYuri Pankov struct roff_node *n; 105795c635efSGarrett D'Amore 1058c66b8046SYuri Pankov post_delim(mdoc); 1059c66b8046SYuri Pankov 1060371584c2SYuri Pankov n = mdoc->last; 1061371584c2SYuri Pankov if (n->args && n->args->argc == 1) 1062371584c2SYuri Pankov if (n->args->argv[0].arg == MDOC_Std) 1063260e9a87SYuri Pankov return; 106495c635efSGarrett D'Amore 1065*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos, 1066*cec8643bSMichal Nowak "%s", roff_name[n->tok]); 106795c635efSGarrett D'Amore } 106895c635efSGarrett D'Amore 1069a40ea1a7SYuri Pankov static void 1070a40ea1a7SYuri Pankov post_st(POST_ARGS) 1071a40ea1a7SYuri Pankov { 1072a40ea1a7SYuri Pankov struct roff_node *n, *nch; 1073a40ea1a7SYuri Pankov const char *p; 1074a40ea1a7SYuri Pankov 1075a40ea1a7SYuri Pankov n = mdoc->last; 1076a40ea1a7SYuri Pankov nch = n->child; 1077a40ea1a7SYuri Pankov assert(nch->type == ROFFT_TEXT); 1078a40ea1a7SYuri Pankov 1079a40ea1a7SYuri Pankov if ((p = mdoc_a2st(nch->string)) == NULL) { 1080*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ST_BAD, 1081a40ea1a7SYuri Pankov nch->line, nch->pos, "St %s", nch->string); 1082a40ea1a7SYuri Pankov roff_node_delete(mdoc, n); 1083a40ea1a7SYuri Pankov return; 1084a40ea1a7SYuri Pankov } 1085a40ea1a7SYuri Pankov 1086a40ea1a7SYuri Pankov nch->flags |= NODE_NOPRT; 1087a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1088a40ea1a7SYuri Pankov roff_word_alloc(mdoc, nch->line, nch->pos, p); 1089a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1090a40ea1a7SYuri Pankov mdoc->last= n; 1091a40ea1a7SYuri Pankov } 1092a40ea1a7SYuri Pankov 1093260e9a87SYuri Pankov static void 1094371584c2SYuri Pankov post_obsolete(POST_ARGS) 109595c635efSGarrett D'Amore { 1096371584c2SYuri Pankov struct roff_node *n; 109795c635efSGarrett D'Amore 1098371584c2SYuri Pankov n = mdoc->last; 1099371584c2SYuri Pankov if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 1100*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, 1101*cec8643bSMichal Nowak "%s", roff_name[n->tok]); 1102c66b8046SYuri Pankov } 1103c66b8046SYuri Pankov 1104c66b8046SYuri Pankov static void 1105c66b8046SYuri Pankov post_useless(POST_ARGS) 1106c66b8046SYuri Pankov { 1107c66b8046SYuri Pankov struct roff_node *n; 1108c66b8046SYuri Pankov 1109c66b8046SYuri Pankov n = mdoc->last; 1110*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, 1111*cec8643bSMichal Nowak "%s", roff_name[n->tok]); 1112260e9a87SYuri Pankov } 111395c635efSGarrett D'Amore 1114a40ea1a7SYuri Pankov /* 1115a40ea1a7SYuri Pankov * Block macros. 1116a40ea1a7SYuri Pankov */ 1117a40ea1a7SYuri Pankov 1118260e9a87SYuri Pankov static void 111995c635efSGarrett D'Amore post_bf(POST_ARGS) 112095c635efSGarrett D'Amore { 1121371584c2SYuri Pankov struct roff_node *np, *nch; 112295c635efSGarrett D'Amore 112395c635efSGarrett D'Amore /* 112495c635efSGarrett D'Amore * Unlike other data pointers, these are "housed" by the HEAD 112595c635efSGarrett D'Amore * element, which contains the goods. 112695c635efSGarrett D'Amore */ 112795c635efSGarrett D'Amore 112895c635efSGarrett D'Amore np = mdoc->last; 1129371584c2SYuri Pankov if (np->type != ROFFT_HEAD) 1130260e9a87SYuri Pankov return; 1131260e9a87SYuri Pankov 1132371584c2SYuri Pankov assert(np->parent->type == ROFFT_BLOCK); 1133371584c2SYuri Pankov assert(np->parent->tok == MDOC_Bf); 113495c635efSGarrett D'Amore 1135260e9a87SYuri Pankov /* Check the number of arguments. */ 113695c635efSGarrett D'Amore 1137260e9a87SYuri Pankov nch = np->child; 1138371584c2SYuri Pankov if (np->parent->args == NULL) { 1139371584c2SYuri Pankov if (nch == NULL) { 1140*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BF_NOFONT, 1141260e9a87SYuri Pankov np->line, np->pos, "Bf"); 1142260e9a87SYuri Pankov return; 1143260e9a87SYuri Pankov } 1144260e9a87SYuri Pankov nch = nch->next; 114595c635efSGarrett D'Amore } 1146371584c2SYuri Pankov if (nch != NULL) 1147*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, 1148260e9a87SYuri Pankov nch->line, nch->pos, "Bf ... %s", nch->string); 114995c635efSGarrett D'Amore 115095c635efSGarrett D'Amore /* Extract argument into data. */ 1151260e9a87SYuri Pankov 1152371584c2SYuri Pankov if (np->parent->args != NULL) { 1153371584c2SYuri Pankov switch (np->parent->args->argv[0].arg) { 1154371584c2SYuri Pankov case MDOC_Emphasis: 115595c635efSGarrett D'Amore np->norm->Bf.font = FONT_Em; 1156371584c2SYuri Pankov break; 1157371584c2SYuri Pankov case MDOC_Literal: 115895c635efSGarrett D'Amore np->norm->Bf.font = FONT_Li; 1159371584c2SYuri Pankov break; 1160371584c2SYuri Pankov case MDOC_Symbolic: 116195c635efSGarrett D'Amore np->norm->Bf.font = FONT_Sy; 1162371584c2SYuri Pankov break; 1163371584c2SYuri Pankov default: 116495c635efSGarrett D'Amore abort(); 1165371584c2SYuri Pankov } 1166260e9a87SYuri Pankov return; 116795c635efSGarrett D'Amore } 116895c635efSGarrett D'Amore 116995c635efSGarrett D'Amore /* Extract parameter into data. */ 117095c635efSGarrett D'Amore 1171371584c2SYuri Pankov if ( ! strcmp(np->child->string, "Em")) 117295c635efSGarrett D'Amore np->norm->Bf.font = FONT_Em; 1173371584c2SYuri Pankov else if ( ! strcmp(np->child->string, "Li")) 117495c635efSGarrett D'Amore np->norm->Bf.font = FONT_Li; 1175371584c2SYuri Pankov else if ( ! strcmp(np->child->string, "Sy")) 117695c635efSGarrett D'Amore np->norm->Bf.font = FONT_Sy; 1177260e9a87SYuri Pankov else 1178*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, 1179*cec8643bSMichal Nowak np->child->pos, "Bf %s", np->child->string); 118095c635efSGarrett D'Amore } 118195c635efSGarrett D'Amore 1182260e9a87SYuri Pankov static void 1183260e9a87SYuri Pankov post_fname(POST_ARGS) 1184260e9a87SYuri Pankov { 1185371584c2SYuri Pankov const struct roff_node *n; 1186260e9a87SYuri Pankov const char *cp; 1187260e9a87SYuri Pankov size_t pos; 118895c635efSGarrett D'Amore 1189260e9a87SYuri Pankov n = mdoc->last->child; 1190260e9a87SYuri Pankov pos = strcspn(n->string, "()"); 1191260e9a87SYuri Pankov cp = n->string + pos; 1192260e9a87SYuri Pankov if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) 1193*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, 1194*cec8643bSMichal Nowak "%s", n->string); 119595c635efSGarrett D'Amore } 119695c635efSGarrett D'Amore 1197260e9a87SYuri Pankov static void 1198260e9a87SYuri Pankov post_fn(POST_ARGS) 119995c635efSGarrett D'Amore { 120095c635efSGarrett D'Amore 1201260e9a87SYuri Pankov post_fname(mdoc); 1202260e9a87SYuri Pankov post_fa(mdoc); 120395c635efSGarrett D'Amore } 120495c635efSGarrett D'Amore 1205260e9a87SYuri Pankov static void 1206260e9a87SYuri Pankov post_fo(POST_ARGS) 1207260e9a87SYuri Pankov { 1208371584c2SYuri Pankov const struct roff_node *n; 120995c635efSGarrett D'Amore 1210260e9a87SYuri Pankov n = mdoc->last; 1211260e9a87SYuri Pankov 1212371584c2SYuri Pankov if (n->type != ROFFT_HEAD) 1213260e9a87SYuri Pankov return; 1214260e9a87SYuri Pankov 1215260e9a87SYuri Pankov if (n->child == NULL) { 1216*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); 1217260e9a87SYuri Pankov return; 1218260e9a87SYuri Pankov } 1219260e9a87SYuri Pankov if (n->child != n->last) { 1220*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, 1221260e9a87SYuri Pankov n->child->next->line, n->child->next->pos, 1222260e9a87SYuri Pankov "Fo ... %s", n->child->next->string); 1223260e9a87SYuri Pankov while (n->child != n->last) 1224371584c2SYuri Pankov roff_node_delete(mdoc, n->last); 1225c66b8046SYuri Pankov } else 1226c66b8046SYuri Pankov post_delim(mdoc); 1227260e9a87SYuri Pankov 1228260e9a87SYuri Pankov post_fname(mdoc); 1229260e9a87SYuri Pankov } 1230260e9a87SYuri Pankov 1231260e9a87SYuri Pankov static void 1232260e9a87SYuri Pankov post_fa(POST_ARGS) 1233260e9a87SYuri Pankov { 1234371584c2SYuri Pankov const struct roff_node *n; 1235260e9a87SYuri Pankov const char *cp; 1236260e9a87SYuri Pankov 1237260e9a87SYuri Pankov for (n = mdoc->last->child; n != NULL; n = n->next) { 1238260e9a87SYuri Pankov for (cp = n->string; *cp != '\0'; cp++) { 1239260e9a87SYuri Pankov /* Ignore callbacks and alterations. */ 1240260e9a87SYuri Pankov if (*cp == '(' || *cp == '{') 1241260e9a87SYuri Pankov break; 1242260e9a87SYuri Pankov if (*cp != ',') 1243260e9a87SYuri Pankov continue; 1244*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_FA_COMMA, n->line, 1245*cec8643bSMichal Nowak n->pos + (int)(cp - n->string), "%s", n->string); 1246260e9a87SYuri Pankov break; 1247260e9a87SYuri Pankov } 1248260e9a87SYuri Pankov } 1249c66b8046SYuri Pankov post_delim_nb(mdoc); 1250260e9a87SYuri Pankov } 1251260e9a87SYuri Pankov 1252260e9a87SYuri Pankov static void 125395c635efSGarrett D'Amore post_nm(POST_ARGS) 125495c635efSGarrett D'Amore { 1255371584c2SYuri Pankov struct roff_node *n; 1256260e9a87SYuri Pankov 1257260e9a87SYuri Pankov n = mdoc->last; 1258260e9a87SYuri Pankov 1259c66b8046SYuri Pankov if (n->sec == SEC_NAME && n->child != NULL && 1260c66b8046SYuri Pankov n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL) 1261c66b8046SYuri Pankov mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); 1262c66b8046SYuri Pankov 1263*cec8643bSMichal Nowak if (n->last != NULL && n->last->tok == MDOC_Pp) 1264*cec8643bSMichal Nowak roff_node_relink(mdoc, n->last); 126595c635efSGarrett D'Amore 1266371584c2SYuri Pankov if (mdoc->meta.name == NULL) 1267a40ea1a7SYuri Pankov deroff(&mdoc->meta.name, n); 1268a40ea1a7SYuri Pankov 1269a40ea1a7SYuri Pankov if (mdoc->meta.name == NULL || 1270a40ea1a7SYuri Pankov (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1271*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); 1272a40ea1a7SYuri Pankov 1273c66b8046SYuri Pankov switch (n->type) { 1274c66b8046SYuri Pankov case ROFFT_ELEM: 1275c66b8046SYuri Pankov post_delim_nb(mdoc); 1276c66b8046SYuri Pankov break; 1277c66b8046SYuri Pankov case ROFFT_HEAD: 1278c66b8046SYuri Pankov post_delim(mdoc); 1279c66b8046SYuri Pankov break; 1280c66b8046SYuri Pankov default: 1281c66b8046SYuri Pankov return; 1282c66b8046SYuri Pankov } 1283c66b8046SYuri Pankov 1284c66b8046SYuri Pankov if ((n->child != NULL && n->child->type == ROFFT_TEXT) || 1285a40ea1a7SYuri Pankov mdoc->meta.name == NULL) 1286a40ea1a7SYuri Pankov return; 1287a40ea1a7SYuri Pankov 1288a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1289a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 1290a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1291a40ea1a7SYuri Pankov mdoc->last = n; 129295c635efSGarrett D'Amore } 129395c635efSGarrett D'Amore 1294260e9a87SYuri Pankov static void 1295260e9a87SYuri Pankov post_nd(POST_ARGS) 1296260e9a87SYuri Pankov { 1297371584c2SYuri Pankov struct roff_node *n; 1298260e9a87SYuri Pankov 1299260e9a87SYuri Pankov n = mdoc->last; 1300260e9a87SYuri Pankov 1301371584c2SYuri Pankov if (n->type != ROFFT_BODY) 1302260e9a87SYuri Pankov return; 1303260e9a87SYuri Pankov 1304c66b8046SYuri Pankov if (n->sec != SEC_NAME) 1305*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); 1306c66b8046SYuri Pankov 1307260e9a87SYuri Pankov if (n->child == NULL) 1308*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); 1309c66b8046SYuri Pankov else 1310c66b8046SYuri Pankov post_delim(mdoc); 1311260e9a87SYuri Pankov 1312260e9a87SYuri Pankov post_hyph(mdoc); 1313260e9a87SYuri Pankov } 1314260e9a87SYuri Pankov 1315260e9a87SYuri Pankov static void 1316371584c2SYuri Pankov post_display(POST_ARGS) 1317260e9a87SYuri Pankov { 1318371584c2SYuri Pankov struct roff_node *n, *np; 1319260e9a87SYuri Pankov 1320260e9a87SYuri Pankov n = mdoc->last; 1321371584c2SYuri Pankov switch (n->type) { 1322371584c2SYuri Pankov case ROFFT_BODY: 1323a40ea1a7SYuri Pankov if (n->end != ENDBODY_NOT) { 1324a40ea1a7SYuri Pankov if (n->tok == MDOC_Bd && 1325a40ea1a7SYuri Pankov n->body->parent->args == NULL) 1326a40ea1a7SYuri Pankov roff_node_delete(mdoc, n); 1327a40ea1a7SYuri Pankov } else if (n->child == NULL) 1328*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, 1329*cec8643bSMichal Nowak "%s", roff_name[n->tok]); 1330371584c2SYuri Pankov else if (n->tok == MDOC_D1) 1331371584c2SYuri Pankov post_hyph(mdoc); 1332371584c2SYuri Pankov break; 1333371584c2SYuri Pankov case ROFFT_BLOCK: 1334371584c2SYuri Pankov if (n->tok == MDOC_Bd) { 1335371584c2SYuri Pankov if (n->args == NULL) { 1336371584c2SYuri Pankov mandoc_msg(MANDOCERR_BD_NOARG, 1337*cec8643bSMichal Nowak n->line, n->pos, "Bd"); 1338371584c2SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 1339371584c2SYuri Pankov while (n->body->child != NULL) 1340*cec8643bSMichal Nowak roff_node_relink(mdoc, 1341371584c2SYuri Pankov n->body->child); 1342371584c2SYuri Pankov roff_node_delete(mdoc, n); 1343371584c2SYuri Pankov break; 1344371584c2SYuri Pankov } 1345371584c2SYuri Pankov post_bd(mdoc); 1346371584c2SYuri Pankov post_prevpar(mdoc); 1347371584c2SYuri Pankov } 1348371584c2SYuri Pankov for (np = n->parent; np != NULL; np = np->parent) { 1349371584c2SYuri Pankov if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 1350*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BD_NEST, n->line, 1351*cec8643bSMichal Nowak n->pos, "%s in Bd", roff_name[n->tok]); 1352371584c2SYuri Pankov break; 1353371584c2SYuri Pankov } 1354371584c2SYuri Pankov } 1355371584c2SYuri Pankov break; 1356371584c2SYuri Pankov default: 1357371584c2SYuri Pankov break; 1358371584c2SYuri Pankov } 135995c635efSGarrett D'Amore } 136095c635efSGarrett D'Amore 1361260e9a87SYuri Pankov static void 136295c635efSGarrett D'Amore post_defaults(POST_ARGS) 136395c635efSGarrett D'Amore { 1364371584c2SYuri Pankov struct roff_node *nn; 136595c635efSGarrett D'Amore 1366c66b8046SYuri Pankov if (mdoc->last->child != NULL) { 1367c66b8046SYuri Pankov post_delim_nb(mdoc); 1368c66b8046SYuri Pankov return; 1369c66b8046SYuri Pankov } 1370c66b8046SYuri Pankov 137195c635efSGarrett D'Amore /* 137295c635efSGarrett D'Amore * The `Ar' defaults to "file ..." if no value is provided as an 137395c635efSGarrett D'Amore * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 137495c635efSGarrett D'Amore * gets an empty string. 137595c635efSGarrett D'Amore */ 137695c635efSGarrett D'Amore 137795c635efSGarrett D'Amore nn = mdoc->last; 137895c635efSGarrett D'Amore switch (nn->tok) { 1379260e9a87SYuri Pankov case MDOC_Ar: 1380371584c2SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1381371584c2SYuri Pankov roff_word_alloc(mdoc, nn->line, nn->pos, "file"); 1382a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1383371584c2SYuri Pankov roff_word_alloc(mdoc, nn->line, nn->pos, "..."); 1384a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 138595c635efSGarrett D'Amore break; 1386260e9a87SYuri Pankov case MDOC_Pa: 1387260e9a87SYuri Pankov case MDOC_Mt: 1388371584c2SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1389371584c2SYuri Pankov roff_word_alloc(mdoc, nn->line, nn->pos, "~"); 1390a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 139195c635efSGarrett D'Amore break; 139295c635efSGarrett D'Amore default: 139395c635efSGarrett D'Amore abort(); 1394260e9a87SYuri Pankov } 139595c635efSGarrett D'Amore mdoc->last = nn; 139695c635efSGarrett D'Amore } 139795c635efSGarrett D'Amore 1398260e9a87SYuri Pankov static void 139995c635efSGarrett D'Amore post_at(POST_ARGS) 140095c635efSGarrett D'Amore { 1401a40ea1a7SYuri Pankov struct roff_node *n, *nch; 1402a40ea1a7SYuri Pankov const char *att; 1403260e9a87SYuri Pankov 1404260e9a87SYuri Pankov n = mdoc->last; 1405a40ea1a7SYuri Pankov nch = n->child; 140695c635efSGarrett D'Amore 140795c635efSGarrett D'Amore /* 140895c635efSGarrett D'Amore * If we have a child, look it up in the standard keys. If a 140995c635efSGarrett D'Amore * key exist, use that instead of the child; if it doesn't, 141095c635efSGarrett D'Amore * prefix "AT&T UNIX " to the existing data. 141195c635efSGarrett D'Amore */ 141295c635efSGarrett D'Amore 1413a40ea1a7SYuri Pankov att = NULL; 1414a40ea1a7SYuri Pankov if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1415*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_AT_BAD, 1416a40ea1a7SYuri Pankov nch->line, nch->pos, "At %s", nch->string); 141795c635efSGarrett D'Amore 1418a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1419a40ea1a7SYuri Pankov if (att != NULL) { 1420a40ea1a7SYuri Pankov roff_word_alloc(mdoc, nch->line, nch->pos, att); 1421a40ea1a7SYuri Pankov nch->flags |= NODE_NOPRT; 1422a40ea1a7SYuri Pankov } else 1423a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 1424a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1425a40ea1a7SYuri Pankov mdoc->last = n; 142695c635efSGarrett D'Amore } 142795c635efSGarrett D'Amore 1428260e9a87SYuri Pankov static void 142995c635efSGarrett D'Amore post_an(POST_ARGS) 143095c635efSGarrett D'Amore { 1431371584c2SYuri Pankov struct roff_node *np, *nch; 1432371584c2SYuri Pankov 1433371584c2SYuri Pankov post_an_norm(mdoc); 143495c635efSGarrett D'Amore 143595c635efSGarrett D'Amore np = mdoc->last; 1436260e9a87SYuri Pankov nch = np->child; 1437260e9a87SYuri Pankov if (np->norm->An.auth == AUTH__NONE) { 1438260e9a87SYuri Pankov if (nch == NULL) 1439*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MACRO_EMPTY, 1440260e9a87SYuri Pankov np->line, np->pos, "An"); 1441c66b8046SYuri Pankov else 1442c66b8046SYuri Pankov post_delim_nb(mdoc); 1443260e9a87SYuri Pankov } else if (nch != NULL) 1444*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, 1445260e9a87SYuri Pankov nch->line, nch->pos, "An ... %s", nch->string); 1446260e9a87SYuri Pankov } 1447260e9a87SYuri Pankov 1448260e9a87SYuri Pankov static void 1449260e9a87SYuri Pankov post_en(POST_ARGS) 1450260e9a87SYuri Pankov { 145195c635efSGarrett D'Amore 1452371584c2SYuri Pankov post_obsolete(mdoc); 1453371584c2SYuri Pankov if (mdoc->last->type == ROFFT_BLOCK) 1454260e9a87SYuri Pankov mdoc->last->norm->Es = mdoc->last_es; 145595c635efSGarrett D'Amore } 145695c635efSGarrett D'Amore 1457260e9a87SYuri Pankov static void 1458260e9a87SYuri Pankov post_es(POST_ARGS) 1459260e9a87SYuri Pankov { 146095c635efSGarrett D'Amore 1461371584c2SYuri Pankov post_obsolete(mdoc); 1462260e9a87SYuri Pankov mdoc->last_es = mdoc->last; 1463260e9a87SYuri Pankov } 1464260e9a87SYuri Pankov 1465a40ea1a7SYuri Pankov static void 1466a40ea1a7SYuri Pankov post_xx(POST_ARGS) 1467a40ea1a7SYuri Pankov { 1468a40ea1a7SYuri Pankov struct roff_node *n; 1469a40ea1a7SYuri Pankov const char *os; 1470c66b8046SYuri Pankov char *v; 1471c66b8046SYuri Pankov 1472c66b8046SYuri Pankov post_delim_nb(mdoc); 1473a40ea1a7SYuri Pankov 1474a40ea1a7SYuri Pankov n = mdoc->last; 1475a40ea1a7SYuri Pankov switch (n->tok) { 1476a40ea1a7SYuri Pankov case MDOC_Bsx: 1477a40ea1a7SYuri Pankov os = "BSD/OS"; 1478a40ea1a7SYuri Pankov break; 1479a40ea1a7SYuri Pankov case MDOC_Dx: 1480a40ea1a7SYuri Pankov os = "DragonFly"; 1481a40ea1a7SYuri Pankov break; 1482a40ea1a7SYuri Pankov case MDOC_Fx: 1483a40ea1a7SYuri Pankov os = "FreeBSD"; 1484a40ea1a7SYuri Pankov break; 1485a40ea1a7SYuri Pankov case MDOC_Nx: 1486a40ea1a7SYuri Pankov os = "NetBSD"; 1487c66b8046SYuri Pankov if (n->child == NULL) 1488c66b8046SYuri Pankov break; 1489c66b8046SYuri Pankov v = n->child->string; 1490c66b8046SYuri Pankov if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || 1491c66b8046SYuri Pankov v[2] < '0' || v[2] > '9' || 1492c66b8046SYuri Pankov v[3] < 'a' || v[3] > 'z' || v[4] != '\0') 1493c66b8046SYuri Pankov break; 1494c66b8046SYuri Pankov n->child->flags |= NODE_NOPRT; 1495c66b8046SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1496c66b8046SYuri Pankov roff_word_alloc(mdoc, n->child->line, n->child->pos, v); 1497c66b8046SYuri Pankov v = mdoc->last->string; 1498c66b8046SYuri Pankov v[3] = toupper((unsigned char)v[3]); 1499c66b8046SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1500c66b8046SYuri Pankov mdoc->last = n; 1501a40ea1a7SYuri Pankov break; 1502a40ea1a7SYuri Pankov case MDOC_Ox: 1503a40ea1a7SYuri Pankov os = "OpenBSD"; 1504a40ea1a7SYuri Pankov break; 1505a40ea1a7SYuri Pankov case MDOC_Ux: 1506a40ea1a7SYuri Pankov os = "UNIX"; 1507a40ea1a7SYuri Pankov break; 1508a40ea1a7SYuri Pankov default: 1509a40ea1a7SYuri Pankov abort(); 1510a40ea1a7SYuri Pankov } 1511a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 1512a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, os); 1513a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 1514a40ea1a7SYuri Pankov mdoc->last = n; 1515a40ea1a7SYuri Pankov } 1516a40ea1a7SYuri Pankov 1517260e9a87SYuri Pankov static void 151895c635efSGarrett D'Amore post_it(POST_ARGS) 151995c635efSGarrett D'Amore { 1520371584c2SYuri Pankov struct roff_node *nbl, *nit, *nch; 152195c635efSGarrett D'Amore int i, cols; 152295c635efSGarrett D'Amore enum mdoc_list lt; 1523371584c2SYuri Pankov 1524371584c2SYuri Pankov post_prevpar(mdoc); 152595c635efSGarrett D'Amore 1526260e9a87SYuri Pankov nit = mdoc->last; 1527371584c2SYuri Pankov if (nit->type != ROFFT_BLOCK) 1528260e9a87SYuri Pankov return; 152995c635efSGarrett D'Amore 1530260e9a87SYuri Pankov nbl = nit->parent->parent; 1531260e9a87SYuri Pankov lt = nbl->norm->Bl.type; 153295c635efSGarrett D'Amore 153395c635efSGarrett D'Amore switch (lt) { 1534260e9a87SYuri Pankov case LIST_tag: 1535260e9a87SYuri Pankov case LIST_hang: 1536260e9a87SYuri Pankov case LIST_ohang: 1537260e9a87SYuri Pankov case LIST_inset: 1538260e9a87SYuri Pankov case LIST_diag: 1539260e9a87SYuri Pankov if (nit->head->child == NULL) 1540*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NOHEAD, 1541*cec8643bSMichal Nowak nit->line, nit->pos, "Bl -%s It", 1542260e9a87SYuri Pankov mdoc_argnames[nbl->args->argv[0].arg]); 154395c635efSGarrett D'Amore break; 1544260e9a87SYuri Pankov case LIST_bullet: 1545260e9a87SYuri Pankov case LIST_dash: 1546260e9a87SYuri Pankov case LIST_enum: 1547260e9a87SYuri Pankov case LIST_hyphen: 1548260e9a87SYuri Pankov if (nit->body == NULL || nit->body->child == NULL) 1549*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NOBODY, 1550*cec8643bSMichal Nowak nit->line, nit->pos, "Bl -%s It", 1551260e9a87SYuri Pankov mdoc_argnames[nbl->args->argv[0].arg]); 155295c635efSGarrett D'Amore /* FALLTHROUGH */ 1553260e9a87SYuri Pankov case LIST_item: 1554a40ea1a7SYuri Pankov if ((nch = nit->head->child) != NULL) 1555*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, 1556c66b8046SYuri Pankov nit->line, nit->pos, "It %s", 1557c66b8046SYuri Pankov nch->string == NULL ? roff_name[nch->tok] : 1558c66b8046SYuri Pankov nch->string); 155995c635efSGarrett D'Amore break; 1560260e9a87SYuri Pankov case LIST_column: 1561260e9a87SYuri Pankov cols = (int)nbl->norm->Bl.ncols; 156295c635efSGarrett D'Amore 1563260e9a87SYuri Pankov assert(nit->head->child == NULL); 156495c635efSGarrett D'Amore 1565c66b8046SYuri Pankov if (nit->head->next->child == NULL && 1566c66b8046SYuri Pankov nit->head->next->next == NULL) { 1567*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MACRO_EMPTY, 1568c66b8046SYuri Pankov nit->line, nit->pos, "It"); 1569c66b8046SYuri Pankov roff_node_delete(mdoc, nit); 1570c66b8046SYuri Pankov break; 1571c66b8046SYuri Pankov } 157295c635efSGarrett D'Amore 1573c66b8046SYuri Pankov i = 0; 1574c66b8046SYuri Pankov for (nch = nit->child; nch != NULL; nch = nch->next) { 1575c66b8046SYuri Pankov if (nch->type != ROFFT_BODY) 1576c66b8046SYuri Pankov continue; 1577c66b8046SYuri Pankov if (i++ && nch->flags & NODE_LINE) 1578*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_TA_LINE, 1579c66b8046SYuri Pankov nch->line, nch->pos, "Ta"); 1580c66b8046SYuri Pankov } 1581260e9a87SYuri Pankov if (i < cols || i > cols + 1) 1582*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, 1583260e9a87SYuri Pankov "%d columns, %d cells", cols, i); 1584c66b8046SYuri Pankov else if (nit->head->next->child != NULL && 1585*cec8643bSMichal Nowak nit->head->next->child->flags & NODE_LINE) 1586*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_IT_NOARG, 1587c66b8046SYuri Pankov nit->line, nit->pos, "Bl -column It"); 158895c635efSGarrett D'Amore break; 1589260e9a87SYuri Pankov default: 1590260e9a87SYuri Pankov abort(); 159195c635efSGarrett D'Amore } 159295c635efSGarrett D'Amore } 159395c635efSGarrett D'Amore 1594260e9a87SYuri Pankov static void 1595260e9a87SYuri Pankov post_bl_block(POST_ARGS) 159695c635efSGarrett D'Amore { 1597371584c2SYuri Pankov struct roff_node *n, *ni, *nc; 1598371584c2SYuri Pankov 1599371584c2SYuri Pankov post_prevpar(mdoc); 160095c635efSGarrett D'Amore 160195c635efSGarrett D'Amore n = mdoc->last; 1602371584c2SYuri Pankov for (ni = n->body->child; ni != NULL; ni = ni->next) { 1603371584c2SYuri Pankov if (ni->body == NULL) 1604698f87a4SGarrett D'Amore continue; 1605698f87a4SGarrett D'Amore nc = ni->body->last; 1606371584c2SYuri Pankov while (nc != NULL) { 1607698f87a4SGarrett D'Amore switch (nc->tok) { 1608260e9a87SYuri Pankov case MDOC_Pp: 1609c66b8046SYuri Pankov case ROFF_br: 1610698f87a4SGarrett D'Amore break; 1611698f87a4SGarrett D'Amore default: 1612698f87a4SGarrett D'Amore nc = NULL; 1613698f87a4SGarrett D'Amore continue; 1614698f87a4SGarrett D'Amore } 1615371584c2SYuri Pankov if (ni->next == NULL) { 1616*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, 1617*cec8643bSMichal Nowak nc->pos, "%s", roff_name[nc->tok]); 1618*cec8643bSMichal Nowak roff_node_relink(mdoc, nc); 1619371584c2SYuri Pankov } else if (n->norm->Bl.comp == 0 && 1620371584c2SYuri Pankov n->norm->Bl.type != LIST_column) { 1621*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PAR_SKIP, 1622*cec8643bSMichal Nowak nc->line, nc->pos, 1623c66b8046SYuri Pankov "%s before It", roff_name[nc->tok]); 1624371584c2SYuri Pankov roff_node_delete(mdoc, nc); 1625698f87a4SGarrett D'Amore } else 1626698f87a4SGarrett D'Amore break; 1627698f87a4SGarrett D'Amore nc = ni->body->last; 1628698f87a4SGarrett D'Amore } 1629698f87a4SGarrett D'Amore } 163095c635efSGarrett D'Amore } 163195c635efSGarrett D'Amore 1632260e9a87SYuri Pankov /* 1633260e9a87SYuri Pankov * If the argument of -offset or -width is a macro, 1634260e9a87SYuri Pankov * replace it with the associated default width. 1635260e9a87SYuri Pankov */ 1636c66b8046SYuri Pankov static void 1637c66b8046SYuri Pankov rewrite_macro2len(struct roff_man *mdoc, char **arg) 163895c635efSGarrett D'Amore { 163995c635efSGarrett D'Amore size_t width; 1640c66b8046SYuri Pankov enum roff_tok tok; 164195c635efSGarrett D'Amore 1642260e9a87SYuri Pankov if (*arg == NULL) 1643260e9a87SYuri Pankov return; 1644260e9a87SYuri Pankov else if ( ! strcmp(*arg, "Ds")) 164595c635efSGarrett D'Amore width = 6; 1646c66b8046SYuri Pankov else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) 1647260e9a87SYuri Pankov return; 1648260e9a87SYuri Pankov else 1649260e9a87SYuri Pankov width = macro2len(tok); 165095c635efSGarrett D'Amore 1651260e9a87SYuri Pankov free(*arg); 1652260e9a87SYuri Pankov mandoc_asprintf(arg, "%zun", width); 165395c635efSGarrett D'Amore } 165495c635efSGarrett D'Amore 1655260e9a87SYuri Pankov static void 1656260e9a87SYuri Pankov post_bl_head(POST_ARGS) 165795c635efSGarrett D'Amore { 1658371584c2SYuri Pankov struct roff_node *nbl, *nh, *nch, *nnext; 1659260e9a87SYuri Pankov struct mdoc_argv *argv; 166095c635efSGarrett D'Amore int i, j; 166195c635efSGarrett D'Amore 1662371584c2SYuri Pankov post_bl_norm(mdoc); 1663260e9a87SYuri Pankov 1664371584c2SYuri Pankov nh = mdoc->last; 1665260e9a87SYuri Pankov if (nh->norm->Bl.type != LIST_column) { 1666260e9a87SYuri Pankov if ((nch = nh->child) == NULL) 1667260e9a87SYuri Pankov return; 1668*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, 1669260e9a87SYuri Pankov nch->line, nch->pos, "Bl ... %s", nch->string); 1670260e9a87SYuri Pankov while (nch != NULL) { 1671371584c2SYuri Pankov roff_node_delete(mdoc, nch); 1672260e9a87SYuri Pankov nch = nh->child; 1673260e9a87SYuri Pankov } 1674260e9a87SYuri Pankov return; 1675260e9a87SYuri Pankov } 167695c635efSGarrett D'Amore 167795c635efSGarrett D'Amore /* 1678260e9a87SYuri Pankov * Append old-style lists, where the column width specifiers 167995c635efSGarrett D'Amore * trail as macro parameters, to the new-style ("normal-form") 168095c635efSGarrett D'Amore * lists where they're argument values following -column. 168195c635efSGarrett D'Amore */ 168295c635efSGarrett D'Amore 1683260e9a87SYuri Pankov if (nh->child == NULL) 1684260e9a87SYuri Pankov return; 168595c635efSGarrett D'Amore 1686260e9a87SYuri Pankov nbl = nh->parent; 1687260e9a87SYuri Pankov for (j = 0; j < (int)nbl->args->argc; j++) 1688260e9a87SYuri Pankov if (nbl->args->argv[j].arg == MDOC_Column) 168995c635efSGarrett D'Amore break; 169095c635efSGarrett D'Amore 1691260e9a87SYuri Pankov assert(j < (int)nbl->args->argc); 169295c635efSGarrett D'Amore 169395c635efSGarrett D'Amore /* 169495c635efSGarrett D'Amore * Accommodate for new-style groff column syntax. Shuffle the 169595c635efSGarrett D'Amore * child nodes, all of which must be TEXT, as arguments for the 169695c635efSGarrett D'Amore * column field. Then, delete the head children. 169795c635efSGarrett D'Amore */ 169895c635efSGarrett D'Amore 1699260e9a87SYuri Pankov argv = nbl->args->argv + j; 1700260e9a87SYuri Pankov i = argv->sz; 1701371584c2SYuri Pankov for (nch = nh->child; nch != NULL; nch = nch->next) 1702371584c2SYuri Pankov argv->sz++; 1703260e9a87SYuri Pankov argv->value = mandoc_reallocarray(argv->value, 1704260e9a87SYuri Pankov argv->sz, sizeof(char *)); 170595c635efSGarrett D'Amore 1706260e9a87SYuri Pankov nh->norm->Bl.ncols = argv->sz; 1707260e9a87SYuri Pankov nh->norm->Bl.cols = (void *)argv->value; 170895c635efSGarrett D'Amore 1709260e9a87SYuri Pankov for (nch = nh->child; nch != NULL; nch = nnext) { 1710260e9a87SYuri Pankov argv->value[i++] = nch->string; 1711260e9a87SYuri Pankov nch->string = NULL; 1712260e9a87SYuri Pankov nnext = nch->next; 1713371584c2SYuri Pankov roff_node_delete(NULL, nch); 171495c635efSGarrett D'Amore } 1715260e9a87SYuri Pankov nh->child = NULL; 171695c635efSGarrett D'Amore } 171795c635efSGarrett D'Amore 1718260e9a87SYuri Pankov static void 171995c635efSGarrett D'Amore post_bl(POST_ARGS) 172095c635efSGarrett D'Amore { 1721371584c2SYuri Pankov struct roff_node *nparent, *nprev; /* of the Bl block */ 1722371584c2SYuri Pankov struct roff_node *nblock, *nbody; /* of the Bl */ 1723371584c2SYuri Pankov struct roff_node *nchild, *nnext; /* of the Bl body */ 1724c66b8046SYuri Pankov const char *prev_Er; 1725c66b8046SYuri Pankov int order; 172695c635efSGarrett D'Amore 1727698f87a4SGarrett D'Amore nbody = mdoc->last; 1728698f87a4SGarrett D'Amore switch (nbody->type) { 1729371584c2SYuri Pankov case ROFFT_BLOCK: 1730260e9a87SYuri Pankov post_bl_block(mdoc); 1731260e9a87SYuri Pankov return; 1732371584c2SYuri Pankov case ROFFT_HEAD: 1733260e9a87SYuri Pankov post_bl_head(mdoc); 1734260e9a87SYuri Pankov return; 1735371584c2SYuri Pankov case ROFFT_BODY: 1736698f87a4SGarrett D'Amore break; 1737698f87a4SGarrett D'Amore default: 1738260e9a87SYuri Pankov return; 1739698f87a4SGarrett D'Amore } 1740371584c2SYuri Pankov if (nbody->end != ENDBODY_NOT) 1741371584c2SYuri Pankov return; 174295c635efSGarrett D'Amore 1743698f87a4SGarrett D'Amore nchild = nbody->child; 1744260e9a87SYuri Pankov if (nchild == NULL) { 1745*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_EMPTY, 1746260e9a87SYuri Pankov nbody->line, nbody->pos, "Bl"); 1747260e9a87SYuri Pankov return; 1748260e9a87SYuri Pankov } 1749260e9a87SYuri Pankov while (nchild != NULL) { 1750a40ea1a7SYuri Pankov nnext = nchild->next; 1751260e9a87SYuri Pankov if (nchild->tok == MDOC_It || 1752260e9a87SYuri Pankov (nchild->tok == MDOC_Sm && 1753a40ea1a7SYuri Pankov nnext != NULL && nnext->tok == MDOC_It)) { 1754a40ea1a7SYuri Pankov nchild = nnext; 1755a40ea1a7SYuri Pankov continue; 1756a40ea1a7SYuri Pankov } 1757a40ea1a7SYuri Pankov 1758a40ea1a7SYuri Pankov /* 1759a40ea1a7SYuri Pankov * In .Bl -column, the first rows may be implicit, 1760a40ea1a7SYuri Pankov * that is, they may not start with .It macros. 1761a40ea1a7SYuri Pankov * Such rows may be followed by nodes generated on the 1762a40ea1a7SYuri Pankov * roff level, for example .TS, which cannot be moved 1763a40ea1a7SYuri Pankov * out of the list. In that case, wrap such roff nodes 1764a40ea1a7SYuri Pankov * into an implicit row. 1765a40ea1a7SYuri Pankov */ 1766a40ea1a7SYuri Pankov 1767a40ea1a7SYuri Pankov if (nchild->prev != NULL) { 1768a40ea1a7SYuri Pankov mdoc->last = nchild; 1769a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 1770a40ea1a7SYuri Pankov roff_block_alloc(mdoc, nchild->line, 1771a40ea1a7SYuri Pankov nchild->pos, MDOC_It); 1772a40ea1a7SYuri Pankov roff_head_alloc(mdoc, nchild->line, 1773a40ea1a7SYuri Pankov nchild->pos, MDOC_It); 1774a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 1775a40ea1a7SYuri Pankov roff_body_alloc(mdoc, nchild->line, 1776a40ea1a7SYuri Pankov nchild->pos, MDOC_It); 1777a40ea1a7SYuri Pankov while (nchild->tok != MDOC_It) { 1778*cec8643bSMichal Nowak roff_node_relink(mdoc, nchild); 1779a40ea1a7SYuri Pankov if ((nchild = nnext) == NULL) 1780a40ea1a7SYuri Pankov break; 1781a40ea1a7SYuri Pankov nnext = nchild->next; 1782a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 1783a40ea1a7SYuri Pankov } 1784a40ea1a7SYuri Pankov mdoc->last = nbody; 178595c635efSGarrett D'Amore continue; 178695c635efSGarrett D'Amore } 178795c635efSGarrett D'Amore 1788*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos, 1789*cec8643bSMichal Nowak "%s", roff_name[nchild->tok]); 1790698f87a4SGarrett D'Amore 1791698f87a4SGarrett D'Amore /* 1792698f87a4SGarrett D'Amore * Move the node out of the Bl block. 1793698f87a4SGarrett D'Amore * First, collect all required node pointers. 1794698f87a4SGarrett D'Amore */ 1795698f87a4SGarrett D'Amore 1796698f87a4SGarrett D'Amore nblock = nbody->parent; 1797698f87a4SGarrett D'Amore nprev = nblock->prev; 1798698f87a4SGarrett D'Amore nparent = nblock->parent; 1799698f87a4SGarrett D'Amore 1800698f87a4SGarrett D'Amore /* 1801698f87a4SGarrett D'Amore * Unlink this child. 1802698f87a4SGarrett D'Amore */ 1803698f87a4SGarrett D'Amore 1804371584c2SYuri Pankov nbody->child = nnext; 1805371584c2SYuri Pankov if (nnext == NULL) 1806698f87a4SGarrett D'Amore nbody->last = NULL; 1807371584c2SYuri Pankov else 1808698f87a4SGarrett D'Amore nnext->prev = NULL; 1809698f87a4SGarrett D'Amore 1810698f87a4SGarrett D'Amore /* 1811698f87a4SGarrett D'Amore * Relink this child. 1812698f87a4SGarrett D'Amore */ 1813698f87a4SGarrett D'Amore 1814698f87a4SGarrett D'Amore nchild->parent = nparent; 1815698f87a4SGarrett D'Amore nchild->prev = nprev; 1816698f87a4SGarrett D'Amore nchild->next = nblock; 1817698f87a4SGarrett D'Amore 1818698f87a4SGarrett D'Amore nblock->prev = nchild; 1819371584c2SYuri Pankov if (nprev == NULL) 1820698f87a4SGarrett D'Amore nparent->child = nchild; 1821698f87a4SGarrett D'Amore else 1822698f87a4SGarrett D'Amore nprev->next = nchild; 1823698f87a4SGarrett D'Amore 1824698f87a4SGarrett D'Amore nchild = nnext; 182595c635efSGarrett D'Amore } 1826c66b8046SYuri Pankov 1827c66b8046SYuri Pankov if (mdoc->meta.os_e != MANDOC_OS_NETBSD) 1828c66b8046SYuri Pankov return; 1829c66b8046SYuri Pankov 1830c66b8046SYuri Pankov prev_Er = NULL; 1831c66b8046SYuri Pankov for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { 1832c66b8046SYuri Pankov if (nchild->tok != MDOC_It) 1833c66b8046SYuri Pankov continue; 1834c66b8046SYuri Pankov if ((nnext = nchild->head->child) == NULL) 1835c66b8046SYuri Pankov continue; 1836c66b8046SYuri Pankov if (nnext->type == ROFFT_BLOCK) 1837c66b8046SYuri Pankov nnext = nnext->body->child; 1838c66b8046SYuri Pankov if (nnext == NULL || nnext->tok != MDOC_Er) 1839c66b8046SYuri Pankov continue; 1840c66b8046SYuri Pankov nnext = nnext->child; 1841c66b8046SYuri Pankov if (prev_Er != NULL) { 1842c66b8046SYuri Pankov order = strcmp(prev_Er, nnext->string); 1843c66b8046SYuri Pankov if (order > 0) 1844*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ER_ORDER, 1845*cec8643bSMichal Nowak nnext->line, nnext->pos, 1846c66b8046SYuri Pankov "Er %s %s (NetBSD)", 1847c66b8046SYuri Pankov prev_Er, nnext->string); 1848c66b8046SYuri Pankov else if (order == 0) 1849*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ER_REP, 1850*cec8643bSMichal Nowak nnext->line, nnext->pos, 1851c66b8046SYuri Pankov "Er %s (NetBSD)", prev_Er); 1852c66b8046SYuri Pankov } 1853c66b8046SYuri Pankov prev_Er = nnext->string; 1854c66b8046SYuri Pankov } 1855260e9a87SYuri Pankov } 1856260e9a87SYuri Pankov 1857260e9a87SYuri Pankov static void 1858260e9a87SYuri Pankov post_bk(POST_ARGS) 1859260e9a87SYuri Pankov { 1860371584c2SYuri Pankov struct roff_node *n; 1861260e9a87SYuri Pankov 1862260e9a87SYuri Pankov n = mdoc->last; 186395c635efSGarrett D'Amore 1864371584c2SYuri Pankov if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 1865*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); 1866371584c2SYuri Pankov roff_node_delete(mdoc, n); 1867260e9a87SYuri Pankov } 186895c635efSGarrett D'Amore } 186995c635efSGarrett D'Amore 1870260e9a87SYuri Pankov static void 1871371584c2SYuri Pankov post_sm(POST_ARGS) 187295c635efSGarrett D'Amore { 1873371584c2SYuri Pankov struct roff_node *nch; 187495c635efSGarrett D'Amore 1875260e9a87SYuri Pankov nch = mdoc->last->child; 1876260e9a87SYuri Pankov 1877260e9a87SYuri Pankov if (nch == NULL) { 1878260e9a87SYuri Pankov mdoc->flags ^= MDOC_SMOFF; 1879260e9a87SYuri Pankov return; 188095c635efSGarrett D'Amore } 188195c635efSGarrett D'Amore 1882371584c2SYuri Pankov assert(nch->type == ROFFT_TEXT); 188395c635efSGarrett D'Amore 1884260e9a87SYuri Pankov if ( ! strcmp(nch->string, "on")) { 1885260e9a87SYuri Pankov mdoc->flags &= ~MDOC_SMOFF; 1886260e9a87SYuri Pankov return; 1887698f87a4SGarrett D'Amore } 1888260e9a87SYuri Pankov if ( ! strcmp(nch->string, "off")) { 1889260e9a87SYuri Pankov mdoc->flags |= MDOC_SMOFF; 1890260e9a87SYuri Pankov return; 1891698f87a4SGarrett D'Amore } 189295c635efSGarrett D'Amore 1893*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, 1894c66b8046SYuri Pankov "%s %s", roff_name[mdoc->last->tok], nch->string); 1895*cec8643bSMichal Nowak roff_node_relink(mdoc, nch); 1896260e9a87SYuri Pankov return; 189795c635efSGarrett D'Amore } 189895c635efSGarrett D'Amore 1899260e9a87SYuri Pankov static void 190095c635efSGarrett D'Amore post_root(POST_ARGS) 190195c635efSGarrett D'Amore { 1902371584c2SYuri Pankov struct roff_node *n; 190395c635efSGarrett D'Amore 1904260e9a87SYuri Pankov /* Add missing prologue data. */ 190595c635efSGarrett D'Amore 1906260e9a87SYuri Pankov if (mdoc->meta.date == NULL) 1907c66b8046SYuri Pankov mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 1908c66b8046SYuri Pankov mandoc_normdate(mdoc, NULL, 0, 0); 190995c635efSGarrett D'Amore 1910260e9a87SYuri Pankov if (mdoc->meta.title == NULL) { 1911*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); 1912260e9a87SYuri Pankov mdoc->meta.title = mandoc_strdup("UNTITLED"); 191395c635efSGarrett D'Amore } 191495c635efSGarrett D'Amore 1915260e9a87SYuri Pankov if (mdoc->meta.vol == NULL) 1916260e9a87SYuri Pankov mdoc->meta.vol = mandoc_strdup("LOCAL"); 191795c635efSGarrett D'Amore 1918260e9a87SYuri Pankov if (mdoc->meta.os == NULL) { 1919*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL); 1920260e9a87SYuri Pankov mdoc->meta.os = mandoc_strdup(""); 1921c66b8046SYuri Pankov } else if (mdoc->meta.os_e && 1922c66b8046SYuri Pankov (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) 1923*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, 1924c66b8046SYuri Pankov mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 1925c66b8046SYuri Pankov "(OpenBSD)" : "(NetBSD)"); 1926c66b8046SYuri Pankov 1927c66b8046SYuri Pankov if (mdoc->meta.arch != NULL && 1928*cec8643bSMichal Nowak arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { 1929*cec8643bSMichal Nowak n = mdoc->meta.first->child; 1930*cec8643bSMichal Nowak while (n->tok != MDOC_Dt || 1931*cec8643bSMichal Nowak n->child == NULL || 1932*cec8643bSMichal Nowak n->child->next == NULL || 1933*cec8643bSMichal Nowak n->child->next->next == NULL) 1934*cec8643bSMichal Nowak n = n->next; 1935*cec8643bSMichal Nowak n = n->child->next->next; 1936*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, 1937*cec8643bSMichal Nowak "Dt ... %s %s", mdoc->meta.arch, 1938*cec8643bSMichal Nowak mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 1939*cec8643bSMichal Nowak "(OpenBSD)" : "(NetBSD)"); 194095c635efSGarrett D'Amore } 194195c635efSGarrett D'Amore 1942260e9a87SYuri Pankov /* Check that we begin with a proper `Sh'. */ 1943260e9a87SYuri Pankov 1944*cec8643bSMichal Nowak n = mdoc->meta.first->child; 19456640c13bSYuri Pankov while (n != NULL && 19466640c13bSYuri Pankov (n->type == ROFFT_COMMENT || 19476640c13bSYuri Pankov (n->tok >= MDOC_Dd && 1948*cec8643bSMichal Nowak mdoc_macro(n->tok)->flags & MDOC_PROLOGUE))) 1949260e9a87SYuri Pankov n = n->next; 1950260e9a87SYuri Pankov 1951260e9a87SYuri Pankov if (n == NULL) 1952*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL); 1953260e9a87SYuri Pankov else if (n->tok != MDOC_Sh) 1954*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, 1955*cec8643bSMichal Nowak "%s", roff_name[n->tok]); 195695c635efSGarrett D'Amore } 195795c635efSGarrett D'Amore 1958260e9a87SYuri Pankov static void 195995c635efSGarrett D'Amore post_rs(POST_ARGS) 196095c635efSGarrett D'Amore { 1961371584c2SYuri Pankov struct roff_node *np, *nch, *next, *prev; 196295c635efSGarrett D'Amore int i, j; 196395c635efSGarrett D'Amore 1964260e9a87SYuri Pankov np = mdoc->last; 196595c635efSGarrett D'Amore 1966371584c2SYuri Pankov if (np->type != ROFFT_BODY) 1967260e9a87SYuri Pankov return; 196895c635efSGarrett D'Amore 1969260e9a87SYuri Pankov if (np->child == NULL) { 1970*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); 1971260e9a87SYuri Pankov return; 197295c635efSGarrett D'Amore } 197395c635efSGarrett D'Amore 197495c635efSGarrett D'Amore /* 197595c635efSGarrett D'Amore * The full `Rs' block needs special handling to order the 197695c635efSGarrett D'Amore * sub-elements according to `rsord'. Pick through each element 1977260e9a87SYuri Pankov * and correctly order it. This is an insertion sort. 197895c635efSGarrett D'Amore */ 197995c635efSGarrett D'Amore 198095c635efSGarrett D'Amore next = NULL; 1981260e9a87SYuri Pankov for (nch = np->child->next; nch != NULL; nch = next) { 1982260e9a87SYuri Pankov /* Determine order number of this child. */ 198395c635efSGarrett D'Amore for (i = 0; i < RSORD_MAX; i++) 1984260e9a87SYuri Pankov if (rsord[i] == nch->tok) 198595c635efSGarrett D'Amore break; 198695c635efSGarrett D'Amore 1987260e9a87SYuri Pankov if (i == RSORD_MAX) { 1988*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, 1989*cec8643bSMichal Nowak "%s", roff_name[nch->tok]); 1990260e9a87SYuri Pankov i = -1; 1991260e9a87SYuri Pankov } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 1992260e9a87SYuri Pankov np->norm->Rs.quote_T++; 1993260e9a87SYuri Pankov 1994260e9a87SYuri Pankov /* 1995260e9a87SYuri Pankov * Remove this child from the chain. This somewhat 1996371584c2SYuri Pankov * repeats roff_node_unlink(), but since we're 199795c635efSGarrett D'Amore * just re-ordering, there's no need for the 199895c635efSGarrett D'Amore * full unlink process. 199995c635efSGarrett D'Amore */ 200095c635efSGarrett D'Amore 2001260e9a87SYuri Pankov if ((next = nch->next) != NULL) 2002260e9a87SYuri Pankov next->prev = nch->prev; 200395c635efSGarrett D'Amore 2004260e9a87SYuri Pankov if ((prev = nch->prev) != NULL) 2005260e9a87SYuri Pankov prev->next = nch->next; 200695c635efSGarrett D'Amore 2007260e9a87SYuri Pankov nch->prev = nch->next = NULL; 2008260e9a87SYuri Pankov 2009260e9a87SYuri Pankov /* 201095c635efSGarrett D'Amore * Scan back until we reach a node that's 2011260e9a87SYuri Pankov * to be ordered before this child. 201295c635efSGarrett D'Amore */ 201395c635efSGarrett D'Amore 201495c635efSGarrett D'Amore for ( ; prev ; prev = prev->prev) { 201595c635efSGarrett D'Amore /* Determine order of `prev'. */ 201695c635efSGarrett D'Amore for (j = 0; j < RSORD_MAX; j++) 201795c635efSGarrett D'Amore if (rsord[j] == prev->tok) 201895c635efSGarrett D'Amore break; 2019260e9a87SYuri Pankov if (j == RSORD_MAX) 2020260e9a87SYuri Pankov j = -1; 202195c635efSGarrett D'Amore 202295c635efSGarrett D'Amore if (j <= i) 202395c635efSGarrett D'Amore break; 202495c635efSGarrett D'Amore } 202595c635efSGarrett D'Amore 202695c635efSGarrett D'Amore /* 2027260e9a87SYuri Pankov * Set this child back into its correct place 2028260e9a87SYuri Pankov * in front of the `prev' node. 202995c635efSGarrett D'Amore */ 203095c635efSGarrett D'Amore 2031260e9a87SYuri Pankov nch->prev = prev; 203295c635efSGarrett D'Amore 2033260e9a87SYuri Pankov if (prev == NULL) { 2034260e9a87SYuri Pankov np->child->prev = nch; 2035260e9a87SYuri Pankov nch->next = np->child; 2036260e9a87SYuri Pankov np->child = nch; 203795c635efSGarrett D'Amore } else { 2038260e9a87SYuri Pankov if (prev->next) 2039260e9a87SYuri Pankov prev->next->prev = nch; 2040260e9a87SYuri Pankov nch->next = prev->next; 2041260e9a87SYuri Pankov prev->next = nch; 204295c635efSGarrett D'Amore } 204395c635efSGarrett D'Amore } 204495c635efSGarrett D'Amore } 204595c635efSGarrett D'Amore 2046698f87a4SGarrett D'Amore /* 2047698f87a4SGarrett D'Amore * For some arguments of some macros, 2048698f87a4SGarrett D'Amore * convert all breakable hyphens into ASCII_HYPH. 2049698f87a4SGarrett D'Amore */ 2050260e9a87SYuri Pankov static void 2051698f87a4SGarrett D'Amore post_hyph(POST_ARGS) 2052698f87a4SGarrett D'Amore { 2053371584c2SYuri Pankov struct roff_node *nch; 2054698f87a4SGarrett D'Amore char *cp; 2055698f87a4SGarrett D'Amore 2056260e9a87SYuri Pankov for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { 2057371584c2SYuri Pankov if (nch->type != ROFFT_TEXT) 2058698f87a4SGarrett D'Amore continue; 2059698f87a4SGarrett D'Amore cp = nch->string; 2060260e9a87SYuri Pankov if (*cp == '\0') 2061698f87a4SGarrett D'Amore continue; 2062260e9a87SYuri Pankov while (*(++cp) != '\0') 2063260e9a87SYuri Pankov if (*cp == '-' && 2064698f87a4SGarrett D'Amore isalpha((unsigned char)cp[-1]) && 2065698f87a4SGarrett D'Amore isalpha((unsigned char)cp[1])) 2066698f87a4SGarrett D'Amore *cp = ASCII_HYPH; 2067698f87a4SGarrett D'Amore } 2068698f87a4SGarrett D'Amore } 2069698f87a4SGarrett D'Amore 2070260e9a87SYuri Pankov static void 207195c635efSGarrett D'Amore post_ns(POST_ARGS) 207295c635efSGarrett D'Amore { 2073c66b8046SYuri Pankov struct roff_node *n; 207495c635efSGarrett D'Amore 2075c66b8046SYuri Pankov n = mdoc->last; 2076c66b8046SYuri Pankov if (n->flags & NODE_LINE || 2077c66b8046SYuri Pankov (n->next != NULL && n->next->flags & NODE_DELIMC)) 2078*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL); 2079c66b8046SYuri Pankov } 2080c66b8046SYuri Pankov 2081c66b8046SYuri Pankov static void 2082c66b8046SYuri Pankov post_sx(POST_ARGS) 2083c66b8046SYuri Pankov { 2084c66b8046SYuri Pankov post_delim(mdoc); 2085c66b8046SYuri Pankov post_hyph(mdoc); 208695c635efSGarrett D'Amore } 208795c635efSGarrett D'Amore 2088260e9a87SYuri Pankov static void 208995c635efSGarrett D'Amore post_sh(POST_ARGS) 209095c635efSGarrett D'Amore { 209195c635efSGarrett D'Amore 2092260e9a87SYuri Pankov post_ignpar(mdoc); 209395c635efSGarrett D'Amore 2094260e9a87SYuri Pankov switch (mdoc->last->type) { 2095371584c2SYuri Pankov case ROFFT_HEAD: 2096260e9a87SYuri Pankov post_sh_head(mdoc); 2097260e9a87SYuri Pankov break; 2098371584c2SYuri Pankov case ROFFT_BODY: 2099260e9a87SYuri Pankov switch (mdoc->lastsec) { 2100260e9a87SYuri Pankov case SEC_NAME: 2101260e9a87SYuri Pankov post_sh_name(mdoc); 2102260e9a87SYuri Pankov break; 2103260e9a87SYuri Pankov case SEC_SEE_ALSO: 2104260e9a87SYuri Pankov post_sh_see_also(mdoc); 2105260e9a87SYuri Pankov break; 2106260e9a87SYuri Pankov case SEC_AUTHORS: 2107260e9a87SYuri Pankov post_sh_authors(mdoc); 2108260e9a87SYuri Pankov break; 2109260e9a87SYuri Pankov default: 2110260e9a87SYuri Pankov break; 2111260e9a87SYuri Pankov } 2112260e9a87SYuri Pankov break; 2113260e9a87SYuri Pankov default: 2114260e9a87SYuri Pankov break; 2115260e9a87SYuri Pankov } 211695c635efSGarrett D'Amore } 211795c635efSGarrett D'Amore 2118260e9a87SYuri Pankov static void 2119260e9a87SYuri Pankov post_sh_name(POST_ARGS) 212095c635efSGarrett D'Amore { 2121371584c2SYuri Pankov struct roff_node *n; 2122260e9a87SYuri Pankov int hasnm, hasnd; 212395c635efSGarrett D'Amore 2124260e9a87SYuri Pankov hasnm = hasnd = 0; 212595c635efSGarrett D'Amore 2126260e9a87SYuri Pankov for (n = mdoc->last->child; n != NULL; n = n->next) { 2127260e9a87SYuri Pankov switch (n->tok) { 2128260e9a87SYuri Pankov case MDOC_Nm: 2129a40ea1a7SYuri Pankov if (hasnm && n->child != NULL) 2130*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESEC_PUNCT, 2131*cec8643bSMichal Nowak n->line, n->pos, 2132a40ea1a7SYuri Pankov "Nm %s", n->child->string); 2133260e9a87SYuri Pankov hasnm = 1; 2134a40ea1a7SYuri Pankov continue; 2135260e9a87SYuri Pankov case MDOC_Nd: 2136260e9a87SYuri Pankov hasnd = 1; 2137260e9a87SYuri Pankov if (n->next != NULL) 2138260e9a87SYuri Pankov mandoc_msg(MANDOCERR_NAMESEC_ND, 2139*cec8643bSMichal Nowak n->line, n->pos, NULL); 2140260e9a87SYuri Pankov break; 2141371584c2SYuri Pankov case TOKEN_NONE: 2142a40ea1a7SYuri Pankov if (n->type == ROFFT_TEXT && 2143a40ea1a7SYuri Pankov n->string[0] == ',' && n->string[1] == '\0' && 2144a40ea1a7SYuri Pankov n->next != NULL && n->next->tok == MDOC_Nm) { 2145a40ea1a7SYuri Pankov n = n->next; 2146a40ea1a7SYuri Pankov continue; 2147a40ea1a7SYuri Pankov } 2148260e9a87SYuri Pankov /* FALLTHROUGH */ 2149260e9a87SYuri Pankov default: 2150*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESEC_BAD, 2151*cec8643bSMichal Nowak n->line, n->pos, "%s", roff_name[n->tok]); 2152a40ea1a7SYuri Pankov continue; 2153260e9a87SYuri Pankov } 2154a40ea1a7SYuri Pankov break; 215595c635efSGarrett D'Amore } 215695c635efSGarrett D'Amore 2157260e9a87SYuri Pankov if ( ! hasnm) 2158*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESEC_NONM, 2159260e9a87SYuri Pankov mdoc->last->line, mdoc->last->pos, NULL); 2160260e9a87SYuri Pankov if ( ! hasnd) 2161*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESEC_NOND, 2162260e9a87SYuri Pankov mdoc->last->line, mdoc->last->pos, NULL); 2163260e9a87SYuri Pankov } 2164260e9a87SYuri Pankov 2165260e9a87SYuri Pankov static void 2166260e9a87SYuri Pankov post_sh_see_also(POST_ARGS) 2167260e9a87SYuri Pankov { 2168371584c2SYuri Pankov const struct roff_node *n; 2169260e9a87SYuri Pankov const char *name, *sec; 2170260e9a87SYuri Pankov const char *lastname, *lastsec, *lastpunct; 2171260e9a87SYuri Pankov int cmp; 2172260e9a87SYuri Pankov 2173260e9a87SYuri Pankov n = mdoc->last->child; 2174260e9a87SYuri Pankov lastname = lastsec = lastpunct = NULL; 2175260e9a87SYuri Pankov while (n != NULL) { 2176371584c2SYuri Pankov if (n->tok != MDOC_Xr || 2177371584c2SYuri Pankov n->child == NULL || 2178371584c2SYuri Pankov n->child->next == NULL) 2179260e9a87SYuri Pankov break; 2180260e9a87SYuri Pankov 2181260e9a87SYuri Pankov /* Process one .Xr node. */ 2182260e9a87SYuri Pankov 2183260e9a87SYuri Pankov name = n->child->string; 2184260e9a87SYuri Pankov sec = n->child->next->string; 2185260e9a87SYuri Pankov if (lastsec != NULL) { 2186260e9a87SYuri Pankov if (lastpunct[0] != ',' || lastpunct[1] != '\0') 2187*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2188*cec8643bSMichal Nowak n->pos, "%s before %s(%s)", 2189*cec8643bSMichal Nowak lastpunct, name, sec); 2190260e9a87SYuri Pankov cmp = strcmp(lastsec, sec); 2191260e9a87SYuri Pankov if (cmp > 0) 2192*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2193*cec8643bSMichal Nowak n->pos, "%s(%s) after %s(%s)", 2194*cec8643bSMichal Nowak name, sec, lastname, lastsec); 2195260e9a87SYuri Pankov else if (cmp == 0 && 2196260e9a87SYuri Pankov strcasecmp(lastname, name) > 0) 2197*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2198*cec8643bSMichal Nowak n->pos, "%s after %s", name, lastname); 2199260e9a87SYuri Pankov } 2200260e9a87SYuri Pankov lastname = name; 2201260e9a87SYuri Pankov lastsec = sec; 2202260e9a87SYuri Pankov 2203260e9a87SYuri Pankov /* Process the following node. */ 2204260e9a87SYuri Pankov 2205260e9a87SYuri Pankov n = n->next; 2206260e9a87SYuri Pankov if (n == NULL) 2207260e9a87SYuri Pankov break; 2208260e9a87SYuri Pankov if (n->tok == MDOC_Xr) { 2209260e9a87SYuri Pankov lastpunct = "none"; 221095c635efSGarrett D'Amore continue; 2211260e9a87SYuri Pankov } 2212371584c2SYuri Pankov if (n->type != ROFFT_TEXT) 2213260e9a87SYuri Pankov break; 2214260e9a87SYuri Pankov for (name = n->string; *name != '\0'; name++) 2215260e9a87SYuri Pankov if (isalpha((const unsigned char)*name)) 2216260e9a87SYuri Pankov return; 2217260e9a87SYuri Pankov lastpunct = n->string; 2218c66b8046SYuri Pankov if (n->next == NULL || n->next->tok == MDOC_Rs) 2219*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2220*cec8643bSMichal Nowak n->pos, "%s after %s(%s)", 2221260e9a87SYuri Pankov lastpunct, lastname, lastsec); 2222260e9a87SYuri Pankov n = n->next; 222395c635efSGarrett D'Amore } 2224260e9a87SYuri Pankov } 222595c635efSGarrett D'Amore 2226260e9a87SYuri Pankov static int 2227371584c2SYuri Pankov child_an(const struct roff_node *n) 2228260e9a87SYuri Pankov { 222995c635efSGarrett D'Amore 2230260e9a87SYuri Pankov for (n = n->child; n != NULL; n = n->next) 2231371584c2SYuri Pankov if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 2232371584c2SYuri Pankov return 1; 2233371584c2SYuri Pankov return 0; 223495c635efSGarrett D'Amore } 223595c635efSGarrett D'Amore 2236260e9a87SYuri Pankov static void 2237260e9a87SYuri Pankov post_sh_authors(POST_ARGS) 2238260e9a87SYuri Pankov { 2239260e9a87SYuri Pankov 2240260e9a87SYuri Pankov if ( ! child_an(mdoc->last)) 2241*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_AN_MISSING, 2242260e9a87SYuri Pankov mdoc->last->line, mdoc->last->pos, NULL); 2243260e9a87SYuri Pankov } 2244260e9a87SYuri Pankov 2245c66b8046SYuri Pankov /* 2246c66b8046SYuri Pankov * Return an upper bound for the string distance (allowing 2247c66b8046SYuri Pankov * transpositions). Not a full Levenshtein implementation 2248c66b8046SYuri Pankov * because Levenshtein is quadratic in the string length 2249c66b8046SYuri Pankov * and this function is called for every standard name, 2250c66b8046SYuri Pankov * so the check for each custom name would be cubic. 2251c66b8046SYuri Pankov * The following crude heuristics is linear, resulting 2252c66b8046SYuri Pankov * in quadratic behaviour for checking one custom name, 2253c66b8046SYuri Pankov * which does not cause measurable slowdown. 2254c66b8046SYuri Pankov */ 2255c66b8046SYuri Pankov static int 2256c66b8046SYuri Pankov similar(const char *s1, const char *s2) 2257c66b8046SYuri Pankov { 2258c66b8046SYuri Pankov const int maxdist = 3; 2259c66b8046SYuri Pankov int dist = 0; 2260c66b8046SYuri Pankov 2261c66b8046SYuri Pankov while (s1[0] != '\0' && s2[0] != '\0') { 2262c66b8046SYuri Pankov if (s1[0] == s2[0]) { 2263c66b8046SYuri Pankov s1++; 2264c66b8046SYuri Pankov s2++; 2265c66b8046SYuri Pankov continue; 2266c66b8046SYuri Pankov } 2267c66b8046SYuri Pankov if (++dist > maxdist) 2268c66b8046SYuri Pankov return INT_MAX; 2269c66b8046SYuri Pankov if (s1[1] == s2[1]) { /* replacement */ 2270c66b8046SYuri Pankov s1++; 2271c66b8046SYuri Pankov s2++; 2272c66b8046SYuri Pankov } else if (s1[0] == s2[1] && s1[1] == s2[0]) { 2273c66b8046SYuri Pankov s1 += 2; /* transposition */ 2274c66b8046SYuri Pankov s2 += 2; 2275c66b8046SYuri Pankov } else if (s1[0] == s2[1]) /* insertion */ 2276c66b8046SYuri Pankov s2++; 2277c66b8046SYuri Pankov else if (s1[1] == s2[0]) /* deletion */ 2278c66b8046SYuri Pankov s1++; 2279c66b8046SYuri Pankov else 2280c66b8046SYuri Pankov return INT_MAX; 2281c66b8046SYuri Pankov } 2282c66b8046SYuri Pankov dist += strlen(s1) + strlen(s2); 2283c66b8046SYuri Pankov return dist > maxdist ? INT_MAX : dist; 2284c66b8046SYuri Pankov } 2285c66b8046SYuri Pankov 2286260e9a87SYuri Pankov static void 228795c635efSGarrett D'Amore post_sh_head(POST_ARGS) 228895c635efSGarrett D'Amore { 2289a40ea1a7SYuri Pankov struct roff_node *nch; 2290a40ea1a7SYuri Pankov const char *goodsec; 2291c66b8046SYuri Pankov const char *const *testsec; 2292c66b8046SYuri Pankov int dist, mindist; 2293a40ea1a7SYuri Pankov enum roff_sec sec; 229495c635efSGarrett D'Amore 229595c635efSGarrett D'Amore /* 229695c635efSGarrett D'Amore * Process a new section. Sections are either "named" or 229795c635efSGarrett D'Amore * "custom". Custom sections are user-defined, while named ones 229895c635efSGarrett D'Amore * follow a conventional order and may only appear in certain 229995c635efSGarrett D'Amore * manual sections. 230095c635efSGarrett D'Amore */ 230195c635efSGarrett D'Amore 2302371584c2SYuri Pankov sec = mdoc->last->sec; 230395c635efSGarrett D'Amore 230495c635efSGarrett D'Amore /* The NAME should be first. */ 230595c635efSGarrett D'Amore 2306a40ea1a7SYuri Pankov if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 2307*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_NAMESEC_FIRST, 2308a40ea1a7SYuri Pankov mdoc->last->line, mdoc->last->pos, "Sh %s", 2309a40ea1a7SYuri Pankov sec != SEC_CUSTOM ? secnames[sec] : 2310a40ea1a7SYuri Pankov (nch = mdoc->last->child) == NULL ? "" : 2311a40ea1a7SYuri Pankov nch->type == ROFFT_TEXT ? nch->string : 2312c66b8046SYuri Pankov roff_name[nch->tok]); 231395c635efSGarrett D'Amore 231495c635efSGarrett D'Amore /* The SYNOPSIS gets special attention in other areas. */ 231595c635efSGarrett D'Amore 2316371584c2SYuri Pankov if (sec == SEC_SYNOPSIS) { 2317698f87a4SGarrett D'Amore roff_setreg(mdoc->roff, "nS", 1, '='); 231895c635efSGarrett D'Amore mdoc->flags |= MDOC_SYNOPSIS; 2319698f87a4SGarrett D'Amore } else { 2320698f87a4SGarrett D'Amore roff_setreg(mdoc->roff, "nS", 0, '='); 232195c635efSGarrett D'Amore mdoc->flags &= ~MDOC_SYNOPSIS; 2322698f87a4SGarrett D'Amore } 232395c635efSGarrett D'Amore 232495c635efSGarrett D'Amore /* Mark our last section. */ 232595c635efSGarrett D'Amore 232695c635efSGarrett D'Amore mdoc->lastsec = sec; 232795c635efSGarrett D'Amore 232895c635efSGarrett D'Amore /* We don't care about custom sections after this. */ 232995c635efSGarrett D'Amore 2330c66b8046SYuri Pankov if (sec == SEC_CUSTOM) { 2331c66b8046SYuri Pankov if ((nch = mdoc->last->child) == NULL || 2332c66b8046SYuri Pankov nch->type != ROFFT_TEXT || nch->next != NULL) 2333c66b8046SYuri Pankov return; 2334c66b8046SYuri Pankov goodsec = NULL; 2335c66b8046SYuri Pankov mindist = INT_MAX; 2336c66b8046SYuri Pankov for (testsec = secnames + 1; *testsec != NULL; testsec++) { 2337c66b8046SYuri Pankov dist = similar(nch->string, *testsec); 2338c66b8046SYuri Pankov if (dist < mindist) { 2339c66b8046SYuri Pankov goodsec = *testsec; 2340c66b8046SYuri Pankov mindist = dist; 2341c66b8046SYuri Pankov } 2342c66b8046SYuri Pankov } 2343c66b8046SYuri Pankov if (goodsec != NULL) 2344*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, 2345*cec8643bSMichal Nowak "Sh %s instead of %s", nch->string, goodsec); 2346260e9a87SYuri Pankov return; 2347c66b8046SYuri Pankov } 234895c635efSGarrett D'Amore 234995c635efSGarrett D'Amore /* 235095c635efSGarrett D'Amore * Check whether our non-custom section is being repeated or is 235195c635efSGarrett D'Amore * out of order. 235295c635efSGarrett D'Amore */ 235395c635efSGarrett D'Amore 235495c635efSGarrett D'Amore if (sec == mdoc->lastnamed) 2355*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, 2356*cec8643bSMichal Nowak mdoc->last->pos, "Sh %s", secnames[sec]); 235795c635efSGarrett D'Amore 235895c635efSGarrett D'Amore if (sec < mdoc->lastnamed) 2359*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, 2360*cec8643bSMichal Nowak mdoc->last->pos, "Sh %s", secnames[sec]); 236195c635efSGarrett D'Amore 236295c635efSGarrett D'Amore /* Mark the last named section. */ 236395c635efSGarrett D'Amore 236495c635efSGarrett D'Amore mdoc->lastnamed = sec; 236595c635efSGarrett D'Amore 236695c635efSGarrett D'Amore /* Check particular section/manual conventions. */ 236795c635efSGarrett D'Amore 2368371584c2SYuri Pankov if (mdoc->meta.msec == NULL) 2369260e9a87SYuri Pankov return; 237095c635efSGarrett D'Amore 2371260e9a87SYuri Pankov goodsec = NULL; 237295c635efSGarrett D'Amore switch (sec) { 2373260e9a87SYuri Pankov case SEC_ERRORS: 2374260e9a87SYuri Pankov if (*mdoc->meta.msec == '4') 2375260e9a87SYuri Pankov break; 2376f6b3f249SJason King if (*mdoc->meta.msec == '7' && *(mdoc->meta.msec + 1) == 'I') 2377f6b3f249SJason King break; 2378f6b3f249SJason King goodsec = "2, 3, 4, 7I, 9"; 237995c635efSGarrett D'Amore /* FALLTHROUGH */ 2380260e9a87SYuri Pankov case SEC_RETURN_VALUES: 2381260e9a87SYuri Pankov case SEC_LIBRARY: 238295c635efSGarrett D'Amore if (*mdoc->meta.msec == '2') 238395c635efSGarrett D'Amore break; 238495c635efSGarrett D'Amore if (*mdoc->meta.msec == '3') 238595c635efSGarrett D'Amore break; 2386260e9a87SYuri Pankov if (NULL == goodsec) 2387260e9a87SYuri Pankov goodsec = "2, 3, 9"; 2388260e9a87SYuri Pankov /* FALLTHROUGH */ 2389260e9a87SYuri Pankov case SEC_CONTEXT: 239095c635efSGarrett D'Amore if (*mdoc->meta.msec == '9') 239195c635efSGarrett D'Amore break; 2392260e9a87SYuri Pankov if (NULL == goodsec) 2393260e9a87SYuri Pankov goodsec = "9"; 2394*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_SEC_MSEC, 2395260e9a87SYuri Pankov mdoc->last->line, mdoc->last->pos, 2396371584c2SYuri Pankov "Sh %s for %s only", secnames[sec], goodsec); 239795c635efSGarrett D'Amore break; 239895c635efSGarrett D'Amore default: 239995c635efSGarrett D'Amore break; 240095c635efSGarrett D'Amore } 240195c635efSGarrett D'Amore } 240295c635efSGarrett D'Amore 2403a40ea1a7SYuri Pankov static void 2404a40ea1a7SYuri Pankov post_xr(POST_ARGS) 2405a40ea1a7SYuri Pankov { 2406a40ea1a7SYuri Pankov struct roff_node *n, *nch; 2407a40ea1a7SYuri Pankov 2408a40ea1a7SYuri Pankov n = mdoc->last; 2409a40ea1a7SYuri Pankov nch = n->child; 2410a40ea1a7SYuri Pankov if (nch->next == NULL) { 2411*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_NOSEC, 2412a40ea1a7SYuri Pankov n->line, n->pos, "Xr %s", nch->string); 2413c66b8046SYuri Pankov } else { 2414c66b8046SYuri Pankov assert(nch->next == n->last); 2415c66b8046SYuri Pankov if(mandoc_xr_add(nch->next->string, nch->string, 2416c66b8046SYuri Pankov nch->line, nch->pos)) 2417*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_XR_SELF, 2418c66b8046SYuri Pankov nch->line, nch->pos, "Xr %s %s", 2419c66b8046SYuri Pankov nch->string, nch->next->string); 2420a40ea1a7SYuri Pankov } 2421c66b8046SYuri Pankov post_delim_nb(mdoc); 2422a40ea1a7SYuri Pankov } 2423a40ea1a7SYuri Pankov 2424260e9a87SYuri Pankov static void 242595c635efSGarrett D'Amore post_ignpar(POST_ARGS) 242695c635efSGarrett D'Amore { 2427371584c2SYuri Pankov struct roff_node *np; 242895c635efSGarrett D'Amore 2429260e9a87SYuri Pankov switch (mdoc->last->type) { 2430c66b8046SYuri Pankov case ROFFT_BLOCK: 2431c66b8046SYuri Pankov post_prevpar(mdoc); 2432c66b8046SYuri Pankov return; 2433371584c2SYuri Pankov case ROFFT_HEAD: 2434c66b8046SYuri Pankov post_delim(mdoc); 2435260e9a87SYuri Pankov post_hyph(mdoc); 2436260e9a87SYuri Pankov return; 2437371584c2SYuri Pankov case ROFFT_BODY: 2438260e9a87SYuri Pankov break; 2439260e9a87SYuri Pankov default: 2440260e9a87SYuri Pankov return; 2441260e9a87SYuri Pankov } 244295c635efSGarrett D'Amore 2443371584c2SYuri Pankov if ((np = mdoc->last->child) != NULL) 2444*cec8643bSMichal Nowak if (np->tok == MDOC_Pp || 2445*cec8643bSMichal Nowak np->tok == ROFF_br || np->tok == ROFF_sp) { 2446*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, 2447c66b8046SYuri Pankov "%s after %s", roff_name[np->tok], 2448c66b8046SYuri Pankov roff_name[mdoc->last->tok]); 2449371584c2SYuri Pankov roff_node_delete(mdoc, np); 245095c635efSGarrett D'Amore } 245195c635efSGarrett D'Amore 2452371584c2SYuri Pankov if ((np = mdoc->last->last) != NULL) 2453*cec8643bSMichal Nowak if (np->tok == MDOC_Pp || np->tok == ROFF_br) { 2454*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, 2455*cec8643bSMichal Nowak "%s at the end of %s", roff_name[np->tok], 2456c66b8046SYuri Pankov roff_name[mdoc->last->tok]); 2457371584c2SYuri Pankov roff_node_delete(mdoc, np); 245895c635efSGarrett D'Amore } 245995c635efSGarrett D'Amore } 246095c635efSGarrett D'Amore 2461260e9a87SYuri Pankov static void 2462371584c2SYuri Pankov post_prevpar(POST_ARGS) 246395c635efSGarrett D'Amore { 2464371584c2SYuri Pankov struct roff_node *n; 246595c635efSGarrett D'Amore 2466371584c2SYuri Pankov n = mdoc->last; 2467371584c2SYuri Pankov if (NULL == n->prev) 2468260e9a87SYuri Pankov return; 2469371584c2SYuri Pankov if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 2470260e9a87SYuri Pankov return; 247195c635efSGarrett D'Amore 2472260e9a87SYuri Pankov /* 2473*cec8643bSMichal Nowak * Don't allow `Pp' prior to a paragraph-type 2474*cec8643bSMichal Nowak * block: `Pp' or non-compact `Bd' or `Bl'. 247595c635efSGarrett D'Amore */ 247695c635efSGarrett D'Amore 2477*cec8643bSMichal Nowak if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br) 2478260e9a87SYuri Pankov return; 2479371584c2SYuri Pankov if (n->tok == MDOC_Bl && n->norm->Bl.comp) 2480260e9a87SYuri Pankov return; 2481371584c2SYuri Pankov if (n->tok == MDOC_Bd && n->norm->Bd.comp) 2482260e9a87SYuri Pankov return; 2483371584c2SYuri Pankov if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 2484260e9a87SYuri Pankov return; 248595c635efSGarrett D'Amore 2486*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PAR_SKIP, n->prev->line, n->prev->pos, 2487*cec8643bSMichal Nowak "%s before %s", roff_name[n->prev->tok], roff_name[n->tok]); 2488371584c2SYuri Pankov roff_node_delete(mdoc, n->prev); 248995c635efSGarrett D'Amore } 249095c635efSGarrett D'Amore 2491260e9a87SYuri Pankov static void 2492698f87a4SGarrett D'Amore post_par(POST_ARGS) 2493698f87a4SGarrett D'Amore { 2494371584c2SYuri Pankov struct roff_node *np; 2495698f87a4SGarrett D'Amore 2496*cec8643bSMichal Nowak post_prevpar(mdoc); 2497698f87a4SGarrett D'Amore 2498*cec8643bSMichal Nowak np = mdoc->last; 2499*cec8643bSMichal Nowak if (np->child != NULL) 2500*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, 2501*cec8643bSMichal Nowak "%s %s", roff_name[np->tok], np->child->string); 250295c635efSGarrett D'Amore } 250395c635efSGarrett D'Amore 2504260e9a87SYuri Pankov static void 250595c635efSGarrett D'Amore post_dd(POST_ARGS) 250695c635efSGarrett D'Amore { 2507371584c2SYuri Pankov struct roff_node *n; 2508260e9a87SYuri Pankov char *datestr; 250995c635efSGarrett D'Amore 2510371584c2SYuri Pankov n = mdoc->last; 2511a40ea1a7SYuri Pankov n->flags |= NODE_NOPRT; 2512a40ea1a7SYuri Pankov 2513371584c2SYuri Pankov if (mdoc->meta.date != NULL) { 2514*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); 251595c635efSGarrett D'Amore free(mdoc->meta.date); 2516371584c2SYuri Pankov } else if (mdoc->flags & MDOC_PBODY) 2517*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); 2518371584c2SYuri Pankov else if (mdoc->meta.title != NULL) 2519*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_ORDER, 2520371584c2SYuri Pankov n->line, n->pos, "Dd after Dt"); 2521371584c2SYuri Pankov else if (mdoc->meta.os != NULL) 2522*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_ORDER, 2523371584c2SYuri Pankov n->line, n->pos, "Dd after Os"); 252495c635efSGarrett D'Amore 2525371584c2SYuri Pankov if (n->child == NULL || n->child->string[0] == '\0') { 2526260e9a87SYuri Pankov mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 2527c66b8046SYuri Pankov mandoc_normdate(mdoc, NULL, n->line, n->pos); 2528a40ea1a7SYuri Pankov return; 252995c635efSGarrett D'Amore } 253095c635efSGarrett D'Amore 2531260e9a87SYuri Pankov datestr = NULL; 2532371584c2SYuri Pankov deroff(&datestr, n); 2533260e9a87SYuri Pankov if (mdoc->quick) 2534260e9a87SYuri Pankov mdoc->meta.date = datestr; 2535260e9a87SYuri Pankov else { 2536c66b8046SYuri Pankov mdoc->meta.date = mandoc_normdate(mdoc, 2537260e9a87SYuri Pankov datestr, n->line, n->pos); 2538260e9a87SYuri Pankov free(datestr); 253995c635efSGarrett D'Amore } 254095c635efSGarrett D'Amore } 254195c635efSGarrett D'Amore 2542260e9a87SYuri Pankov static void 254395c635efSGarrett D'Amore post_dt(POST_ARGS) 254495c635efSGarrett D'Amore { 2545371584c2SYuri Pankov struct roff_node *nn, *n; 254695c635efSGarrett D'Amore const char *cp; 254795c635efSGarrett D'Amore char *p; 254895c635efSGarrett D'Amore 254995c635efSGarrett D'Amore n = mdoc->last; 2550a40ea1a7SYuri Pankov n->flags |= NODE_NOPRT; 2551a40ea1a7SYuri Pankov 2552371584c2SYuri Pankov if (mdoc->flags & MDOC_PBODY) { 2553*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); 2554a40ea1a7SYuri Pankov return; 2555371584c2SYuri Pankov } 2556371584c2SYuri Pankov 2557371584c2SYuri Pankov if (mdoc->meta.title != NULL) 2558*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); 2559371584c2SYuri Pankov else if (mdoc->meta.os != NULL) 2560*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_ORDER, 2561371584c2SYuri Pankov n->line, n->pos, "Dt after Os"); 256295c635efSGarrett D'Amore 2563260e9a87SYuri Pankov free(mdoc->meta.title); 2564260e9a87SYuri Pankov free(mdoc->meta.msec); 2565260e9a87SYuri Pankov free(mdoc->meta.vol); 2566260e9a87SYuri Pankov free(mdoc->meta.arch); 256795c635efSGarrett D'Amore 2568260e9a87SYuri Pankov mdoc->meta.title = NULL; 2569260e9a87SYuri Pankov mdoc->meta.msec = NULL; 2570260e9a87SYuri Pankov mdoc->meta.vol = NULL; 2571260e9a87SYuri Pankov mdoc->meta.arch = NULL; 257295c635efSGarrett D'Amore 2573260e9a87SYuri Pankov /* Mandatory first argument: title. */ 257495c635efSGarrett D'Amore 2575260e9a87SYuri Pankov nn = n->child; 2576260e9a87SYuri Pankov if (nn == NULL || *nn->string == '\0') { 2577*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); 2578260e9a87SYuri Pankov mdoc->meta.title = mandoc_strdup("UNTITLED"); 2579260e9a87SYuri Pankov } else { 2580260e9a87SYuri Pankov mdoc->meta.title = mandoc_strdup(nn->string); 258195c635efSGarrett D'Amore 2582260e9a87SYuri Pankov /* Check that all characters are uppercase. */ 258395c635efSGarrett D'Amore 2584260e9a87SYuri Pankov for (p = nn->string; *p != '\0'; p++) 2585260e9a87SYuri Pankov if (islower((unsigned char)*p)) { 2586*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, 2587*cec8643bSMichal Nowak nn->pos + (int)(p - nn->string), 2588260e9a87SYuri Pankov "Dt %s", nn->string); 2589260e9a87SYuri Pankov break; 2590260e9a87SYuri Pankov } 259195c635efSGarrett D'Amore } 259295c635efSGarrett D'Amore 2593a40ea1a7SYuri Pankov /* Mandatory second argument: section. */ 259495c635efSGarrett D'Amore 2595260e9a87SYuri Pankov if (nn != NULL) 2596260e9a87SYuri Pankov nn = nn->next; 259795c635efSGarrett D'Amore 2598260e9a87SYuri Pankov if (nn == NULL) { 2599*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, 2600260e9a87SYuri Pankov "Dt %s", mdoc->meta.title); 260195c635efSGarrett D'Amore mdoc->meta.vol = mandoc_strdup("LOCAL"); 2602a40ea1a7SYuri Pankov return; /* msec and arch remain NULL. */ 260395c635efSGarrett D'Amore } 260495c635efSGarrett D'Amore 2605260e9a87SYuri Pankov mdoc->meta.msec = mandoc_strdup(nn->string); 2606260e9a87SYuri Pankov 2607260e9a87SYuri Pankov /* Infer volume title from section number. */ 260895c635efSGarrett D'Amore 260995c635efSGarrett D'Amore cp = mandoc_a2msec(nn->string); 2610260e9a87SYuri Pankov if (cp == NULL) { 2611*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MSEC_BAD, 2612260e9a87SYuri Pankov nn->line, nn->pos, "Dt ... %s", nn->string); 261395c635efSGarrett D'Amore mdoc->meta.vol = mandoc_strdup(nn->string); 2614260e9a87SYuri Pankov } else 2615260e9a87SYuri Pankov mdoc->meta.vol = mandoc_strdup(cp); 261695c635efSGarrett D'Amore 2617260e9a87SYuri Pankov /* Optional third argument: architecture. */ 261895c635efSGarrett D'Amore 2619260e9a87SYuri Pankov if ((nn = nn->next) == NULL) 2620a40ea1a7SYuri Pankov return; 262195c635efSGarrett D'Amore 2622260e9a87SYuri Pankov for (p = nn->string; *p != '\0'; p++) 2623260e9a87SYuri Pankov *p = tolower((unsigned char)*p); 2624260e9a87SYuri Pankov mdoc->meta.arch = mandoc_strdup(nn->string); 262595c635efSGarrett D'Amore 2626260e9a87SYuri Pankov /* Ignore fourth and later arguments. */ 262795c635efSGarrett D'Amore 2628260e9a87SYuri Pankov if ((nn = nn->next) != NULL) 2629*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_ARG_EXCESS, 2630260e9a87SYuri Pankov nn->line, nn->pos, "Dt ... %s", nn->string); 263195c635efSGarrett D'Amore } 263295c635efSGarrett D'Amore 2633260e9a87SYuri Pankov static void 263495c635efSGarrett D'Amore post_bx(POST_ARGS) 263595c635efSGarrett D'Amore { 2636a40ea1a7SYuri Pankov struct roff_node *n, *nch; 2637c66b8046SYuri Pankov const char *macro; 2638c66b8046SYuri Pankov 2639c66b8046SYuri Pankov post_delim_nb(mdoc); 2640a40ea1a7SYuri Pankov 2641a40ea1a7SYuri Pankov n = mdoc->last; 2642a40ea1a7SYuri Pankov nch = n->child; 2643a40ea1a7SYuri Pankov 2644a40ea1a7SYuri Pankov if (nch != NULL) { 2645c66b8046SYuri Pankov macro = !strcmp(nch->string, "Open") ? "Ox" : 2646c66b8046SYuri Pankov !strcmp(nch->string, "Net") ? "Nx" : 2647c66b8046SYuri Pankov !strcmp(nch->string, "Free") ? "Fx" : 2648c66b8046SYuri Pankov !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; 2649c66b8046SYuri Pankov if (macro != NULL) 2650*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_BX, 2651*cec8643bSMichal Nowak n->line, n->pos, "%s", macro); 2652a40ea1a7SYuri Pankov mdoc->last = nch; 2653a40ea1a7SYuri Pankov nch = nch->next; 2654a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 2655a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2656a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 2657a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 2658a40ea1a7SYuri Pankov } else 2659a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_CHILD; 2660a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 2661a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 2662a40ea1a7SYuri Pankov 2663a40ea1a7SYuri Pankov if (nch == NULL) { 2664a40ea1a7SYuri Pankov mdoc->last = n; 2665a40ea1a7SYuri Pankov return; 2666a40ea1a7SYuri Pankov } 2667a40ea1a7SYuri Pankov 2668a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2669a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 2670a40ea1a7SYuri Pankov mdoc->next = ROFF_NEXT_SIBLING; 2671a40ea1a7SYuri Pankov roff_word_alloc(mdoc, n->line, n->pos, "-"); 2672a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 2673a40ea1a7SYuri Pankov roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2674a40ea1a7SYuri Pankov mdoc->last->flags |= NODE_NOSRC; 2675a40ea1a7SYuri Pankov mdoc->last = n; 267695c635efSGarrett D'Amore 2677260e9a87SYuri Pankov /* 267895c635efSGarrett D'Amore * Make `Bx's second argument always start with an uppercase 267995c635efSGarrett D'Amore * letter. Groff checks if it's an "accepted" term, but we just 268095c635efSGarrett D'Amore * uppercase blindly. 268195c635efSGarrett D'Amore */ 268295c635efSGarrett D'Amore 2683a40ea1a7SYuri Pankov *nch->string = (char)toupper((unsigned char)*nch->string); 268495c635efSGarrett D'Amore } 268595c635efSGarrett D'Amore 2686260e9a87SYuri Pankov static void 268795c635efSGarrett D'Amore post_os(POST_ARGS) 268895c635efSGarrett D'Amore { 268995c635efSGarrett D'Amore #ifndef OSNAME 269095c635efSGarrett D'Amore struct utsname utsname; 2691260e9a87SYuri Pankov static char *defbuf; 269295c635efSGarrett D'Amore #endif 2693371584c2SYuri Pankov struct roff_node *n; 269495c635efSGarrett D'Amore 269595c635efSGarrett D'Amore n = mdoc->last; 2696a40ea1a7SYuri Pankov n->flags |= NODE_NOPRT; 2697a40ea1a7SYuri Pankov 2698371584c2SYuri Pankov if (mdoc->meta.os != NULL) 2699*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); 2700371584c2SYuri Pankov else if (mdoc->flags & MDOC_PBODY) 2701*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); 270295c635efSGarrett D'Amore 2703c66b8046SYuri Pankov post_delim(mdoc); 2704c66b8046SYuri Pankov 270595c635efSGarrett D'Amore /* 2706698f87a4SGarrett D'Amore * Set the operating system by way of the `Os' macro. 2707698f87a4SGarrett D'Amore * The order of precedence is: 2708698f87a4SGarrett D'Amore * 1. the argument of the `Os' macro, unless empty 2709698f87a4SGarrett D'Amore * 2. the -Ios=foo command line argument, if provided 2710698f87a4SGarrett D'Amore * 3. -DOSNAME="\"foo\"", if provided during compilation 2711698f87a4SGarrett D'Amore * 4. "sysname release" from uname(3) 2712260e9a87SYuri Pankov */ 271395c635efSGarrett D'Amore 2714698f87a4SGarrett D'Amore free(mdoc->meta.os); 2715260e9a87SYuri Pankov mdoc->meta.os = NULL; 2716371584c2SYuri Pankov deroff(&mdoc->meta.os, n); 2717260e9a87SYuri Pankov if (mdoc->meta.os) 2718c66b8046SYuri Pankov goto out; 271995c635efSGarrett D'Amore 2720c66b8046SYuri Pankov if (mdoc->os_s != NULL) { 2721c66b8046SYuri Pankov mdoc->meta.os = mandoc_strdup(mdoc->os_s); 2722c66b8046SYuri Pankov goto out; 272395c635efSGarrett D'Amore } 272495c635efSGarrett D'Amore 272595c635efSGarrett D'Amore #ifdef OSNAME 2726260e9a87SYuri Pankov mdoc->meta.os = mandoc_strdup(OSNAME); 272795c635efSGarrett D'Amore #else /*!OSNAME */ 2728371584c2SYuri Pankov if (defbuf == NULL) { 2729371584c2SYuri Pankov if (uname(&utsname) == -1) { 2730*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); 2731260e9a87SYuri Pankov defbuf = mandoc_strdup("UNKNOWN"); 2732260e9a87SYuri Pankov } else 2733260e9a87SYuri Pankov mandoc_asprintf(&defbuf, "%s %s", 2734260e9a87SYuri Pankov utsname.sysname, utsname.release); 273595c635efSGarrett D'Amore } 2736260e9a87SYuri Pankov mdoc->meta.os = mandoc_strdup(defbuf); 2737260e9a87SYuri Pankov #endif /*!OSNAME*/ 2738c66b8046SYuri Pankov 2739c66b8046SYuri Pankov out: 2740c66b8046SYuri Pankov if (mdoc->meta.os_e == MANDOC_OS_OTHER) { 2741c66b8046SYuri Pankov if (strstr(mdoc->meta.os, "OpenBSD") != NULL) 2742c66b8046SYuri Pankov mdoc->meta.os_e = MANDOC_OS_OPENBSD; 2743c66b8046SYuri Pankov else if (strstr(mdoc->meta.os, "NetBSD") != NULL) 2744c66b8046SYuri Pankov mdoc->meta.os_e = MANDOC_OS_NETBSD; 2745c66b8046SYuri Pankov } 2746c66b8046SYuri Pankov 2747c66b8046SYuri Pankov /* 2748c66b8046SYuri Pankov * This is the earliest point where we can check 2749c66b8046SYuri Pankov * Mdocdate conventions because we don't know 2750c66b8046SYuri Pankov * the operating system earlier. 2751c66b8046SYuri Pankov */ 2752c66b8046SYuri Pankov 2753c66b8046SYuri Pankov if (n->child != NULL) 2754*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, 2755c66b8046SYuri Pankov "Os %s (%s)", n->child->string, 2756c66b8046SYuri Pankov mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2757c66b8046SYuri Pankov "OpenBSD" : "NetBSD"); 2758c66b8046SYuri Pankov 2759c66b8046SYuri Pankov while (n->tok != MDOC_Dd) 2760c66b8046SYuri Pankov if ((n = n->prev) == NULL) 2761c66b8046SYuri Pankov return; 2762c66b8046SYuri Pankov if ((n = n->child) == NULL) 2763c66b8046SYuri Pankov return; 2764c66b8046SYuri Pankov if (strncmp(n->string, "$" "Mdocdate", 9)) { 2765c66b8046SYuri Pankov if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) 2766*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, 2767*cec8643bSMichal Nowak n->pos, "Dd %s (OpenBSD)", n->string); 2768c66b8046SYuri Pankov } else { 2769c66b8046SYuri Pankov if (mdoc->meta.os_e == MANDOC_OS_NETBSD) 2770*cec8643bSMichal Nowak mandoc_msg(MANDOCERR_MDOCDATE, n->line, 2771*cec8643bSMichal Nowak n->pos, "Dd %s (NetBSD)", n->string); 2772c66b8046SYuri Pankov } 277395c635efSGarrett D'Amore } 277495c635efSGarrett D'Amore 2775371584c2SYuri Pankov enum roff_sec 2776371584c2SYuri Pankov mdoc_a2sec(const char *p) 277795c635efSGarrett D'Amore { 277895c635efSGarrett D'Amore int i; 277995c635efSGarrett D'Amore 2780260e9a87SYuri Pankov for (i = 0; i < (int)SEC__MAX; i++) 278195c635efSGarrett D'Amore if (secnames[i] && 0 == strcmp(p, secnames[i])) 2782371584c2SYuri Pankov return (enum roff_sec)i; 278395c635efSGarrett D'Amore 2784371584c2SYuri Pankov return SEC_CUSTOM; 278595c635efSGarrett D'Amore } 278695c635efSGarrett D'Amore 278795c635efSGarrett D'Amore static size_t 2788c66b8046SYuri Pankov macro2len(enum roff_tok macro) 278995c635efSGarrett D'Amore { 279095c635efSGarrett D'Amore 279195c635efSGarrett D'Amore switch (macro) { 2792260e9a87SYuri Pankov case MDOC_Ad: 2793371584c2SYuri Pankov return 12; 2794260e9a87SYuri Pankov case MDOC_Ao: 2795371584c2SYuri Pankov return 12; 2796260e9a87SYuri Pankov case MDOC_An: 2797371584c2SYuri Pankov return 12; 2798260e9a87SYuri Pankov case MDOC_Aq: 2799371584c2SYuri Pankov return 12; 2800260e9a87SYuri Pankov case MDOC_Ar: 2801371584c2SYuri Pankov return 12; 2802260e9a87SYuri Pankov case MDOC_Bo: 2803371584c2SYuri Pankov return 12; 2804260e9a87SYuri Pankov case MDOC_Bq: 2805371584c2SYuri Pankov return 12; 2806260e9a87SYuri Pankov case MDOC_Cd: 2807371584c2SYuri Pankov return 12; 2808260e9a87SYuri Pankov case MDOC_Cm: 2809371584c2SYuri Pankov return 10; 2810260e9a87SYuri Pankov case MDOC_Do: 2811371584c2SYuri Pankov return 10; 2812260e9a87SYuri Pankov case MDOC_Dq: 2813371584c2SYuri Pankov return 12; 2814260e9a87SYuri Pankov case MDOC_Dv: 2815371584c2SYuri Pankov return 12; 2816260e9a87SYuri Pankov case MDOC_Eo: 2817371584c2SYuri Pankov return 12; 2818260e9a87SYuri Pankov case MDOC_Em: 2819371584c2SYuri Pankov return 10; 2820260e9a87SYuri Pankov case MDOC_Er: 2821371584c2SYuri Pankov return 17; 2822260e9a87SYuri Pankov case MDOC_Ev: 2823371584c2SYuri Pankov return 15; 2824260e9a87SYuri Pankov case MDOC_Fa: 2825371584c2SYuri Pankov return 12; 2826260e9a87SYuri Pankov case MDOC_Fl: 2827371584c2SYuri Pankov return 10; 2828260e9a87SYuri Pankov case MDOC_Fo: 2829371584c2SYuri Pankov return 16; 2830260e9a87SYuri Pankov case MDOC_Fn: 2831371584c2SYuri Pankov return 16; 2832260e9a87SYuri Pankov case MDOC_Ic: 2833371584c2SYuri Pankov return 10; 2834260e9a87SYuri Pankov case MDOC_Li: 2835371584c2SYuri Pankov return 16; 2836260e9a87SYuri Pankov case MDOC_Ms: 2837371584c2SYuri Pankov return 6; 2838260e9a87SYuri Pankov case MDOC_Nm: 2839371584c2SYuri Pankov return 10; 2840260e9a87SYuri Pankov case MDOC_No: 2841371584c2SYuri Pankov return 12; 2842260e9a87SYuri Pankov case MDOC_Oo: 2843371584c2SYuri Pankov return 10; 2844260e9a87SYuri Pankov case MDOC_Op: 2845371584c2SYuri Pankov return 14; 2846260e9a87SYuri Pankov case MDOC_Pa: 2847371584c2SYuri Pankov return 32; 2848260e9a87SYuri Pankov case MDOC_Pf: 2849371584c2SYuri Pankov return 12; 2850260e9a87SYuri Pankov case MDOC_Po: 2851371584c2SYuri Pankov return 12; 2852260e9a87SYuri Pankov case MDOC_Pq: 2853371584c2SYuri Pankov return 12; 2854260e9a87SYuri Pankov case MDOC_Ql: 2855371584c2SYuri Pankov return 16; 2856260e9a87SYuri Pankov case MDOC_Qo: 2857371584c2SYuri Pankov return 12; 2858260e9a87SYuri Pankov case MDOC_So: 2859371584c2SYuri Pankov return 12; 2860260e9a87SYuri Pankov case MDOC_Sq: 2861371584c2SYuri Pankov return 12; 2862260e9a87SYuri Pankov case MDOC_Sy: 2863371584c2SYuri Pankov return 6; 2864260e9a87SYuri Pankov case MDOC_Sx: 2865371584c2SYuri Pankov return 16; 2866260e9a87SYuri Pankov case MDOC_Tn: 2867371584c2SYuri Pankov return 10; 2868260e9a87SYuri Pankov case MDOC_Va: 2869371584c2SYuri Pankov return 12; 2870260e9a87SYuri Pankov case MDOC_Vt: 2871371584c2SYuri Pankov return 12; 2872260e9a87SYuri Pankov case MDOC_Xr: 2873371584c2SYuri Pankov return 10; 287495c635efSGarrett D'Amore default: 287595c635efSGarrett D'Amore break; 287695c635efSGarrett D'Amore }; 2877371584c2SYuri Pankov return 0; 287895c635efSGarrett D'Amore } 2879