| File: | src/usr.bin/mandoc/mdoc_validate.c |
| Warning: | line 1108, column 13 Access to field 'child' results in a dereference of a null pointer (loaded from variable 'n') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: mdoc_validate.c,v 1.305 2021/10/04 14:18:42 schwarze Exp $ */ | |||
| 2 | /* | |||
| 3 | * Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org> | |||
| 4 | * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> | |||
| 5 | * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> | |||
| 6 | * | |||
| 7 | * Permission to use, copy, modify, and distribute this software for any | |||
| 8 | * purpose with or without fee is hereby granted, provided that the above | |||
| 9 | * copyright notice and this permission notice appear in all copies. | |||
| 10 | * | |||
| 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES | |||
| 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR | |||
| 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 18 | * | |||
| 19 | * Validation module for mdoc(7) syntax trees used by mandoc(1). | |||
| 20 | */ | |||
| 21 | #include <sys/types.h> | |||
| 22 | #ifndef OSNAME | |||
| 23 | #include <sys/utsname.h> | |||
| 24 | #endif | |||
| 25 | ||||
| 26 | #include <assert.h> | |||
| 27 | #include <ctype.h> | |||
| 28 | #include <limits.h> | |||
| 29 | #include <stdio.h> | |||
| 30 | #include <stdlib.h> | |||
| 31 | #include <string.h> | |||
| 32 | #include <time.h> | |||
| 33 | ||||
| 34 | #include "mandoc_aux.h" | |||
| 35 | #include "mandoc.h" | |||
| 36 | #include "mandoc_xr.h" | |||
| 37 | #include "roff.h" | |||
| 38 | #include "mdoc.h" | |||
| 39 | #include "libmandoc.h" | |||
| 40 | #include "roff_int.h" | |||
| 41 | #include "libmdoc.h" | |||
| 42 | #include "tag.h" | |||
| 43 | ||||
| 44 | /* FIXME: .Bl -diag can't have non-text children in HEAD. */ | |||
| 45 | ||||
| 46 | #define POST_ARGSstruct roff_man *mdoc struct roff_man *mdoc | |||
| 47 | ||||
| 48 | enum check_ineq { | |||
| 49 | CHECK_LT, | |||
| 50 | CHECK_GT, | |||
| 51 | CHECK_EQ | |||
| 52 | }; | |||
| 53 | ||||
| 54 | typedef void (*v_post)(POST_ARGSstruct roff_man *mdoc); | |||
| 55 | ||||
| 56 | static int build_list(struct roff_man *, int); | |||
| 57 | static void check_argv(struct roff_man *, | |||
| 58 | struct roff_node *, struct mdoc_argv *); | |||
| 59 | static void check_args(struct roff_man *, struct roff_node *); | |||
| 60 | static void check_text(struct roff_man *, int, int, char *); | |||
| 61 | static void check_text_em(struct roff_man *, int, int, char *); | |||
| 62 | static void check_toptext(struct roff_man *, int, int, const char *); | |||
| 63 | static int child_an(const struct roff_node *); | |||
| 64 | static size_t macro2len(enum roff_tok); | |||
| 65 | static void rewrite_macro2len(struct roff_man *, char **); | |||
| 66 | static int similar(const char *, const char *); | |||
| 67 | ||||
| 68 | static void post_abort(POST_ARGSstruct roff_man *mdoc) __attribute__((__noreturn__)); | |||
| 69 | static void post_an(POST_ARGSstruct roff_man *mdoc); | |||
| 70 | static void post_an_norm(POST_ARGSstruct roff_man *mdoc); | |||
| 71 | static void post_at(POST_ARGSstruct roff_man *mdoc); | |||
| 72 | static void post_bd(POST_ARGSstruct roff_man *mdoc); | |||
| 73 | static void post_bf(POST_ARGSstruct roff_man *mdoc); | |||
| 74 | static void post_bk(POST_ARGSstruct roff_man *mdoc); | |||
| 75 | static void post_bl(POST_ARGSstruct roff_man *mdoc); | |||
| 76 | static void post_bl_block(POST_ARGSstruct roff_man *mdoc); | |||
| 77 | static void post_bl_head(POST_ARGSstruct roff_man *mdoc); | |||
| 78 | static void post_bl_norm(POST_ARGSstruct roff_man *mdoc); | |||
| 79 | static void post_bx(POST_ARGSstruct roff_man *mdoc); | |||
| 80 | static void post_defaults(POST_ARGSstruct roff_man *mdoc); | |||
| 81 | static void post_display(POST_ARGSstruct roff_man *mdoc); | |||
| 82 | static void post_dd(POST_ARGSstruct roff_man *mdoc); | |||
| 83 | static void post_delim(POST_ARGSstruct roff_man *mdoc); | |||
| 84 | static void post_delim_nb(POST_ARGSstruct roff_man *mdoc); | |||
| 85 | static void post_dt(POST_ARGSstruct roff_man *mdoc); | |||
| 86 | static void post_em(POST_ARGSstruct roff_man *mdoc); | |||
| 87 | static void post_en(POST_ARGSstruct roff_man *mdoc); | |||
| 88 | static void post_er(POST_ARGSstruct roff_man *mdoc); | |||
| 89 | static void post_es(POST_ARGSstruct roff_man *mdoc); | |||
| 90 | static void post_eoln(POST_ARGSstruct roff_man *mdoc); | |||
| 91 | static void post_ex(POST_ARGSstruct roff_man *mdoc); | |||
| 92 | static void post_fa(POST_ARGSstruct roff_man *mdoc); | |||
| 93 | static void post_fl(POST_ARGSstruct roff_man *mdoc); | |||
| 94 | static void post_fn(POST_ARGSstruct roff_man *mdoc); | |||
| 95 | static void post_fname(POST_ARGSstruct roff_man *mdoc); | |||
| 96 | static void post_fo(POST_ARGSstruct roff_man *mdoc); | |||
| 97 | static void post_hyph(POST_ARGSstruct roff_man *mdoc); | |||
| 98 | static void post_it(POST_ARGSstruct roff_man *mdoc); | |||
| 99 | static void post_lb(POST_ARGSstruct roff_man *mdoc); | |||
| 100 | static void post_nd(POST_ARGSstruct roff_man *mdoc); | |||
| 101 | static void post_nm(POST_ARGSstruct roff_man *mdoc); | |||
| 102 | static void post_ns(POST_ARGSstruct roff_man *mdoc); | |||
| 103 | static void post_obsolete(POST_ARGSstruct roff_man *mdoc); | |||
| 104 | static void post_os(POST_ARGSstruct roff_man *mdoc); | |||
| 105 | static void post_par(POST_ARGSstruct roff_man *mdoc); | |||
| 106 | static void post_prevpar(POST_ARGSstruct roff_man *mdoc); | |||
| 107 | static void post_root(POST_ARGSstruct roff_man *mdoc); | |||
| 108 | static void post_rs(POST_ARGSstruct roff_man *mdoc); | |||
| 109 | static void post_rv(POST_ARGSstruct roff_man *mdoc); | |||
| 110 | static void post_section(POST_ARGSstruct roff_man *mdoc); | |||
| 111 | static void post_sh(POST_ARGSstruct roff_man *mdoc); | |||
| 112 | static void post_sh_head(POST_ARGSstruct roff_man *mdoc); | |||
| 113 | static void post_sh_name(POST_ARGSstruct roff_man *mdoc); | |||
| 114 | static void post_sh_see_also(POST_ARGSstruct roff_man *mdoc); | |||
| 115 | static void post_sh_authors(POST_ARGSstruct roff_man *mdoc); | |||
| 116 | static void post_sm(POST_ARGSstruct roff_man *mdoc); | |||
| 117 | static void post_st(POST_ARGSstruct roff_man *mdoc); | |||
| 118 | static void post_std(POST_ARGSstruct roff_man *mdoc); | |||
| 119 | static void post_sx(POST_ARGSstruct roff_man *mdoc); | |||
| 120 | static void post_tag(POST_ARGSstruct roff_man *mdoc); | |||
| 121 | static void post_tg(POST_ARGSstruct roff_man *mdoc); | |||
| 122 | static void post_useless(POST_ARGSstruct roff_man *mdoc); | |||
| 123 | static void post_xr(POST_ARGSstruct roff_man *mdoc); | |||
| 124 | static void post_xx(POST_ARGSstruct roff_man *mdoc); | |||
| 125 | ||||
| 126 | static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { | |||
| 127 | post_dd, /* Dd */ | |||
| 128 | post_dt, /* Dt */ | |||
| 129 | post_os, /* Os */ | |||
| 130 | post_sh, /* Sh */ | |||
| 131 | post_section, /* Ss */ | |||
| 132 | post_par, /* Pp */ | |||
| 133 | post_display, /* D1 */ | |||
| 134 | post_display, /* Dl */ | |||
| 135 | post_display, /* Bd */ | |||
| 136 | NULL((void *)0), /* Ed */ | |||
| 137 | post_bl, /* Bl */ | |||
| 138 | NULL((void *)0), /* El */ | |||
| 139 | post_it, /* It */ | |||
| 140 | post_delim_nb, /* Ad */ | |||
| 141 | post_an, /* An */ | |||
| 142 | NULL((void *)0), /* Ap */ | |||
| 143 | post_defaults, /* Ar */ | |||
| 144 | NULL((void *)0), /* Cd */ | |||
| 145 | post_tag, /* Cm */ | |||
| 146 | post_tag, /* Dv */ | |||
| 147 | post_er, /* Er */ | |||
| 148 | post_tag, /* Ev */ | |||
| 149 | post_ex, /* Ex */ | |||
| 150 | post_fa, /* Fa */ | |||
| 151 | NULL((void *)0), /* Fd */ | |||
| 152 | post_fl, /* Fl */ | |||
| 153 | post_fn, /* Fn */ | |||
| 154 | post_delim_nb, /* Ft */ | |||
| 155 | post_tag, /* Ic */ | |||
| 156 | post_delim_nb, /* In */ | |||
| 157 | post_tag, /* Li */ | |||
| 158 | post_nd, /* Nd */ | |||
| 159 | post_nm, /* Nm */ | |||
| 160 | post_delim_nb, /* Op */ | |||
| 161 | post_abort, /* Ot */ | |||
| 162 | post_defaults, /* Pa */ | |||
| 163 | post_rv, /* Rv */ | |||
| 164 | post_st, /* St */ | |||
| 165 | post_tag, /* Va */ | |||
| 166 | post_delim_nb, /* Vt */ | |||
| 167 | post_xr, /* Xr */ | |||
| 168 | NULL((void *)0), /* %A */ | |||
| 169 | post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ | |||
| 170 | NULL((void *)0), /* %D */ | |||
| 171 | NULL((void *)0), /* %I */ | |||
| 172 | NULL((void *)0), /* %J */ | |||
| 173 | post_hyph, /* %N */ | |||
| 174 | post_hyph, /* %O */ | |||
| 175 | NULL((void *)0), /* %P */ | |||
| 176 | post_hyph, /* %R */ | |||
| 177 | post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ | |||
| 178 | NULL((void *)0), /* %V */ | |||
| 179 | NULL((void *)0), /* Ac */ | |||
| 180 | NULL((void *)0), /* Ao */ | |||
| 181 | post_delim_nb, /* Aq */ | |||
| 182 | post_at, /* At */ | |||
| 183 | NULL((void *)0), /* Bc */ | |||
| 184 | post_bf, /* Bf */ | |||
| 185 | NULL((void *)0), /* Bo */ | |||
| 186 | NULL((void *)0), /* Bq */ | |||
| 187 | post_xx, /* Bsx */ | |||
| 188 | post_bx, /* Bx */ | |||
| 189 | post_obsolete, /* Db */ | |||
| 190 | NULL((void *)0), /* Dc */ | |||
| 191 | NULL((void *)0), /* Do */ | |||
| 192 | NULL((void *)0), /* Dq */ | |||
| 193 | NULL((void *)0), /* Ec */ | |||
| 194 | NULL((void *)0), /* Ef */ | |||
| 195 | post_em, /* Em */ | |||
| 196 | NULL((void *)0), /* Eo */ | |||
| 197 | post_xx, /* Fx */ | |||
| 198 | post_tag, /* Ms */ | |||
| 199 | post_tag, /* No */ | |||
| 200 | post_ns, /* Ns */ | |||
| 201 | post_xx, /* Nx */ | |||
| 202 | post_xx, /* Ox */ | |||
| 203 | NULL((void *)0), /* Pc */ | |||
| 204 | NULL((void *)0), /* Pf */ | |||
| 205 | NULL((void *)0), /* Po */ | |||
| 206 | post_delim_nb, /* Pq */ | |||
| 207 | NULL((void *)0), /* Qc */ | |||
| 208 | post_delim_nb, /* Ql */ | |||
| 209 | NULL((void *)0), /* Qo */ | |||
| 210 | post_delim_nb, /* Qq */ | |||
| 211 | NULL((void *)0), /* Re */ | |||
| 212 | post_rs, /* Rs */ | |||
| 213 | NULL((void *)0), /* Sc */ | |||
| 214 | NULL((void *)0), /* So */ | |||
| 215 | post_delim_nb, /* Sq */ | |||
| 216 | post_sm, /* Sm */ | |||
| 217 | post_sx, /* Sx */ | |||
| 218 | post_em, /* Sy */ | |||
| 219 | post_useless, /* Tn */ | |||
| 220 | post_xx, /* Ux */ | |||
| 221 | NULL((void *)0), /* Xc */ | |||
| 222 | NULL((void *)0), /* Xo */ | |||
| 223 | post_fo, /* Fo */ | |||
| 224 | NULL((void *)0), /* Fc */ | |||
| 225 | NULL((void *)0), /* Oo */ | |||
| 226 | NULL((void *)0), /* Oc */ | |||
| 227 | post_bk, /* Bk */ | |||
| 228 | NULL((void *)0), /* Ek */ | |||
| 229 | post_eoln, /* Bt */ | |||
| 230 | post_obsolete, /* Hf */ | |||
| 231 | post_obsolete, /* Fr */ | |||
| 232 | post_eoln, /* Ud */ | |||
| 233 | post_lb, /* Lb */ | |||
| 234 | post_abort, /* Lp */ | |||
| 235 | post_delim_nb, /* Lk */ | |||
| 236 | post_defaults, /* Mt */ | |||
| 237 | post_delim_nb, /* Brq */ | |||
| 238 | NULL((void *)0), /* Bro */ | |||
| 239 | NULL((void *)0), /* Brc */ | |||
| 240 | NULL((void *)0), /* %C */ | |||
| 241 | post_es, /* Es */ | |||
| 242 | post_en, /* En */ | |||
| 243 | post_xx, /* Dx */ | |||
| 244 | NULL((void *)0), /* %Q */ | |||
| 245 | NULL((void *)0), /* %U */ | |||
| 246 | NULL((void *)0), /* Ta */ | |||
| 247 | post_tg, /* Tg */ | |||
| 248 | }; | |||
| 249 | ||||
| 250 | #define RSORD_MAX14 14 /* Number of `Rs' blocks. */ | |||
| 251 | ||||
| 252 | static const enum roff_tok rsord[RSORD_MAX14] = { | |||
| 253 | MDOC__A, | |||
| 254 | MDOC__T, | |||
| 255 | MDOC__B, | |||
| 256 | MDOC__I, | |||
| 257 | MDOC__J, | |||
| 258 | MDOC__R, | |||
| 259 | MDOC__N, | |||
| 260 | MDOC__V, | |||
| 261 | MDOC__U, | |||
| 262 | MDOC__P, | |||
| 263 | MDOC__Q, | |||
| 264 | MDOC__C, | |||
| 265 | MDOC__D, | |||
| 266 | MDOC__O | |||
| 267 | }; | |||
| 268 | ||||
| 269 | static const char * const secnames[SEC__MAX] = { | |||
| 270 | NULL((void *)0), | |||
| 271 | "NAME", | |||
| 272 | "LIBRARY", | |||
| 273 | "SYNOPSIS", | |||
| 274 | "DESCRIPTION", | |||
| 275 | "CONTEXT", | |||
| 276 | "IMPLEMENTATION NOTES", | |||
| 277 | "RETURN VALUES", | |||
| 278 | "ENVIRONMENT", | |||
| 279 | "FILES", | |||
| 280 | "EXIT STATUS", | |||
| 281 | "EXAMPLES", | |||
| 282 | "DIAGNOSTICS", | |||
| 283 | "COMPATIBILITY", | |||
| 284 | "ERRORS", | |||
| 285 | "SEE ALSO", | |||
| 286 | "STANDARDS", | |||
| 287 | "HISTORY", | |||
| 288 | "AUTHORS", | |||
| 289 | "CAVEATS", | |||
| 290 | "BUGS", | |||
| 291 | "SECURITY CONSIDERATIONS", | |||
| 292 | NULL((void *)0) | |||
| 293 | }; | |||
| 294 | ||||
| 295 | static int fn_prio = TAG_STRONG2; | |||
| 296 | ||||
| 297 | ||||
| 298 | /* Validate the subtree rooted at mdoc->last. */ | |||
| 299 | void | |||
| 300 | mdoc_validate(struct roff_man *mdoc) | |||
| 301 | { | |||
| 302 | struct roff_node *n, *np; | |||
| 303 | const v_post *p; | |||
| 304 | ||||
| 305 | /* | |||
| 306 | * Translate obsolete macros to modern macros first | |||
| 307 | * such that later code does not need to look | |||
| 308 | * for the obsolete versions. | |||
| 309 | */ | |||
| 310 | ||||
| 311 | n = mdoc->last; | |||
| 312 | switch (n->tok) { | |||
| 313 | case MDOC_Lp: | |||
| 314 | n->tok = MDOC_Pp; | |||
| 315 | break; | |||
| 316 | case MDOC_Ot: | |||
| 317 | post_obsolete(mdoc); | |||
| 318 | n->tok = MDOC_Ft; | |||
| 319 | break; | |||
| 320 | default: | |||
| 321 | break; | |||
| 322 | } | |||
| 323 | ||||
| 324 | /* | |||
| 325 | * Iterate over all children, recursing into each one | |||
| 326 | * in turn, depth-first. | |||
| 327 | */ | |||
| 328 | ||||
| 329 | mdoc->last = mdoc->last->child; | |||
| 330 | while (mdoc->last != NULL((void *)0)) { | |||
| 331 | mdoc_validate(mdoc); | |||
| 332 | if (mdoc->last == n) | |||
| 333 | mdoc->last = mdoc->last->child; | |||
| 334 | else | |||
| 335 | mdoc->last = mdoc->last->next; | |||
| 336 | } | |||
| 337 | ||||
| 338 | /* Finally validate the macro itself. */ | |||
| 339 | ||||
| 340 | mdoc->last = n; | |||
| 341 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 342 | switch (n->type) { | |||
| 343 | case ROFFT_TEXT: | |||
| 344 | np = n->parent; | |||
| 345 | if (n->sec != SEC_SYNOPSIS || | |||
| 346 | (np->tok != MDOC_Cd && np->tok != MDOC_Fd)) | |||
| 347 | check_text(mdoc, n->line, n->pos, n->string); | |||
| 348 | if ((n->flags & NODE_NOFILL(1 << 8)) == 0 && | |||
| 349 | (np->tok != MDOC_It || np->type != ROFFT_HEAD || | |||
| 350 | np->parent->parent->norm->Bl.type != LIST_diag)) | |||
| 351 | check_text_em(mdoc, n->line, n->pos, n->string); | |||
| 352 | if (np->tok == MDOC_It || (np->type == ROFFT_BODY && | |||
| 353 | (np->tok == MDOC_Sh || np->tok == MDOC_Ss))) | |||
| 354 | check_toptext(mdoc, n->line, n->pos, n->string); | |||
| 355 | break; | |||
| 356 | case ROFFT_COMMENT: | |||
| 357 | case ROFFT_EQN: | |||
| 358 | case ROFFT_TBL: | |||
| 359 | break; | |||
| 360 | case ROFFT_ROOT: | |||
| 361 | post_root(mdoc); | |||
| 362 | break; | |||
| 363 | default: | |||
| 364 | check_args(mdoc, mdoc->last); | |||
| 365 | ||||
| 366 | /* | |||
| 367 | * Closing delimiters are not special at the | |||
| 368 | * beginning of a block, opening delimiters | |||
| 369 | * are not special at the end. | |||
| 370 | */ | |||
| 371 | ||||
| 372 | if (n->child != NULL((void *)0)) | |||
| 373 | n->child->flags &= ~NODE_DELIMC(1 << 5); | |||
| 374 | if (n->last != NULL((void *)0)) | |||
| 375 | n->last->flags &= ~NODE_DELIMO(1 << 4); | |||
| 376 | ||||
| 377 | /* Call the macro's postprocessor. */ | |||
| 378 | ||||
| 379 | if (n->tok < ROFF_MAX) { | |||
| 380 | roff_validate(mdoc); | |||
| 381 | break; | |||
| 382 | } | |||
| 383 | ||||
| 384 | assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX)((n->tok >= MDOC_Dd && n->tok < MDOC_MAX) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 384, __func__, "n->tok >= MDOC_Dd && n->tok < MDOC_MAX" )); | |||
| 385 | p = mdoc_valids + (n->tok - MDOC_Dd); | |||
| 386 | if (*p) | |||
| 387 | (*p)(mdoc); | |||
| 388 | if (mdoc->last == n) | |||
| 389 | mdoc_state(mdoc, n); | |||
| 390 | break; | |||
| 391 | } | |||
| 392 | } | |||
| 393 | ||||
| 394 | static void | |||
| 395 | check_args(struct roff_man *mdoc, struct roff_node *n) | |||
| 396 | { | |||
| 397 | int i; | |||
| 398 | ||||
| 399 | if (NULL((void *)0) == n->args) | |||
| 400 | return; | |||
| 401 | ||||
| 402 | assert(n->args->argc)((n->args->argc) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 402, __func__, "n->args->argc")); | |||
| 403 | for (i = 0; i < (int)n->args->argc; i++) | |||
| 404 | check_argv(mdoc, n, &n->args->argv[i]); | |||
| 405 | } | |||
| 406 | ||||
| 407 | static void | |||
| 408 | check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v) | |||
| 409 | { | |||
| 410 | int i; | |||
| 411 | ||||
| 412 | for (i = 0; i < (int)v->sz; i++) | |||
| 413 | check_text(mdoc, v->line, v->pos, v->value[i]); | |||
| 414 | } | |||
| 415 | ||||
| 416 | static void | |||
| 417 | check_text(struct roff_man *mdoc, int ln, int pos, char *p) | |||
| 418 | { | |||
| 419 | char *cp; | |||
| 420 | ||||
| 421 | if (mdoc->last->flags & NODE_NOFILL(1 << 8)) | |||
| 422 | return; | |||
| 423 | ||||
| 424 | for (cp = p; NULL((void *)0) != (p = strchr(p, '\t')); p++) | |||
| 425 | mandoc_msg(MANDOCERR_FI_TAB, ln, pos + (int)(p - cp), NULL((void *)0)); | |||
| 426 | } | |||
| 427 | ||||
| 428 | static void | |||
| 429 | check_text_em(struct roff_man *mdoc, int ln, int pos, char *p) | |||
| 430 | { | |||
| 431 | const struct roff_node *np, *nn; | |||
| 432 | char *cp; | |||
| 433 | ||||
| 434 | np = mdoc->last->prev; | |||
| 435 | nn = mdoc->last->next; | |||
| 436 | ||||
| 437 | /* Look for em-dashes wrongly encoded as "--". */ | |||
| 438 | ||||
| 439 | for (cp = p; *cp != '\0'; cp++) { | |||
| 440 | if (cp[0] != '-' || cp[1] != '-') | |||
| 441 | continue; | |||
| 442 | cp++; | |||
| 443 | ||||
| 444 | /* Skip input sequences of more than two '-'. */ | |||
| 445 | ||||
| 446 | if (cp[1] == '-') { | |||
| 447 | while (cp[1] == '-') | |||
| 448 | cp++; | |||
| 449 | continue; | |||
| 450 | } | |||
| 451 | ||||
| 452 | /* Skip "--" directly attached to something else. */ | |||
| 453 | ||||
| 454 | if ((cp - p > 1 && cp[-2] != ' ') || | |||
| 455 | (cp[1] != '\0' && cp[1] != ' ')) | |||
| 456 | continue; | |||
| 457 | ||||
| 458 | /* Require a letter right before or right afterwards. */ | |||
| 459 | ||||
| 460 | if ((cp - p > 2 ? | |||
| 461 | isalpha((unsigned char)cp[-3]) : | |||
| 462 | np != NULL((void *)0) && | |||
| 463 | np->type == ROFFT_TEXT && | |||
| 464 | *np->string != '\0' && | |||
| 465 | isalpha((unsigned char)np->string[ | |||
| 466 | strlen(np->string) - 1])) || | |||
| 467 | (cp[1] != '\0' && cp[2] != '\0' ? | |||
| 468 | isalpha((unsigned char)cp[2]) : | |||
| 469 | nn != NULL((void *)0) && | |||
| 470 | nn->type == ROFFT_TEXT && | |||
| 471 | isalpha((unsigned char)*nn->string))) { | |||
| 472 | mandoc_msg(MANDOCERR_DASHDASH, | |||
| 473 | ln, pos + (int)(cp - p) - 1, NULL((void *)0)); | |||
| 474 | break; | |||
| 475 | } | |||
| 476 | } | |||
| 477 | } | |||
| 478 | ||||
| 479 | static void | |||
| 480 | check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) | |||
| 481 | { | |||
| 482 | const char *cp, *cpr; | |||
| 483 | ||||
| 484 | if (*p == '\0') | |||
| 485 | return; | |||
| 486 | ||||
| 487 | if ((cp = strstr(p, "OpenBSD")) != NULL((void *)0)) | |||
| 488 | mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Ox"); | |||
| 489 | if ((cp = strstr(p, "NetBSD")) != NULL((void *)0)) | |||
| 490 | mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Nx"); | |||
| 491 | if ((cp = strstr(p, "FreeBSD")) != NULL((void *)0)) | |||
| 492 | mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Fx"); | |||
| 493 | if ((cp = strstr(p, "DragonFly")) != NULL((void *)0)) | |||
| 494 | mandoc_msg(MANDOCERR_BX, ln, pos + (int)(cp - p), "Dx"); | |||
| 495 | ||||
| 496 | cp = p; | |||
| 497 | while ((cp = strstr(cp + 1, "()")) != NULL((void *)0)) { | |||
| 498 | for (cpr = cp - 1; cpr >= p; cpr--) | |||
| 499 | if (*cpr != '_' && !isalnum((unsigned char)*cpr)) | |||
| 500 | break; | |||
| 501 | if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { | |||
| 502 | cpr++; | |||
| 503 | mandoc_msg(MANDOCERR_FUNC, ln, pos + (int)(cpr - p), | |||
| 504 | "%.*s()", (int)(cp - cpr), cpr); | |||
| 505 | } | |||
| 506 | } | |||
| 507 | } | |||
| 508 | ||||
| 509 | static void | |||
| 510 | post_abort(POST_ARGSstruct roff_man *mdoc) | |||
| 511 | { | |||
| 512 | abort(); | |||
| 513 | } | |||
| 514 | ||||
| 515 | static void | |||
| 516 | post_delim(POST_ARGSstruct roff_man *mdoc) | |||
| 517 | { | |||
| 518 | const struct roff_node *nch; | |||
| 519 | const char *lc; | |||
| 520 | enum mdelim delim; | |||
| 521 | enum roff_tok tok; | |||
| 522 | ||||
| 523 | tok = mdoc->last->tok; | |||
| 524 | nch = mdoc->last->last; | |||
| 525 | if (nch == NULL((void *)0) || nch->type != ROFFT_TEXT) | |||
| 526 | return; | |||
| 527 | lc = strchr(nch->string, '\0') - 1; | |||
| 528 | if (lc < nch->string) | |||
| 529 | return; | |||
| 530 | delim = mdoc_isdelim(lc); | |||
| 531 | if (delim == DELIM_NONE || delim == DELIM_OPEN) | |||
| 532 | return; | |||
| 533 | if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh || | |||
| 534 | tok == MDOC_Ss || tok == MDOC_Fo)) | |||
| 535 | return; | |||
| 536 | ||||
| 537 | mandoc_msg(MANDOCERR_DELIM, nch->line, | |||
| 538 | nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], | |||
| 539 | nch == mdoc->last->child ? "" : " ...", nch->string); | |||
| 540 | } | |||
| 541 | ||||
| 542 | static void | |||
| 543 | post_delim_nb(POST_ARGSstruct roff_man *mdoc) | |||
| 544 | { | |||
| 545 | const struct roff_node *nch; | |||
| 546 | const char *lc, *cp; | |||
| 547 | int nw; | |||
| 548 | enum mdelim delim; | |||
| 549 | enum roff_tok tok; | |||
| 550 | ||||
| 551 | /* | |||
| 552 | * Find candidates: at least two bytes, | |||
| 553 | * the last one a closing or middle delimiter. | |||
| 554 | */ | |||
| 555 | ||||
| 556 | tok = mdoc->last->tok; | |||
| 557 | nch = mdoc->last->last; | |||
| 558 | if (nch == NULL((void *)0) || nch->type != ROFFT_TEXT) | |||
| 559 | return; | |||
| 560 | lc = strchr(nch->string, '\0') - 1; | |||
| 561 | if (lc <= nch->string) | |||
| 562 | return; | |||
| 563 | delim = mdoc_isdelim(lc); | |||
| 564 | if (delim == DELIM_NONE || delim == DELIM_OPEN) | |||
| 565 | return; | |||
| 566 | ||||
| 567 | /* | |||
| 568 | * Reduce false positives by allowing various cases. | |||
| 569 | */ | |||
| 570 | ||||
| 571 | /* Escaped delimiters. */ | |||
| 572 | if (lc > nch->string + 1 && lc[-2] == '\\' && | |||
| 573 | (lc[-1] == '&' || lc[-1] == 'e')) | |||
| 574 | return; | |||
| 575 | ||||
| 576 | /* Specific byte sequences. */ | |||
| 577 | switch (*lc) { | |||
| 578 | case ')': | |||
| 579 | for (cp = lc; cp >= nch->string; cp--) | |||
| 580 | if (*cp == '(') | |||
| 581 | return; | |||
| 582 | break; | |||
| 583 | case '.': | |||
| 584 | if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') | |||
| 585 | return; | |||
| 586 | if (lc[-1] == '.') | |||
| 587 | return; | |||
| 588 | break; | |||
| 589 | case ';': | |||
| 590 | if (tok == MDOC_Vt) | |||
| 591 | return; | |||
| 592 | break; | |||
| 593 | case '?': | |||
| 594 | if (lc[-1] == '?') | |||
| 595 | return; | |||
| 596 | break; | |||
| 597 | case ']': | |||
| 598 | for (cp = lc; cp >= nch->string; cp--) | |||
| 599 | if (*cp == '[') | |||
| 600 | return; | |||
| 601 | break; | |||
| 602 | case '|': | |||
| 603 | if (lc == nch->string + 1 && lc[-1] == '|') | |||
| 604 | return; | |||
| 605 | default: | |||
| 606 | break; | |||
| 607 | } | |||
| 608 | ||||
| 609 | /* Exactly two non-alphanumeric bytes. */ | |||
| 610 | if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) | |||
| 611 | return; | |||
| 612 | ||||
| 613 | /* At least three alphabetic words with a sentence ending. */ | |||
| 614 | if (strchr("!.:?", *lc) != NULL((void *)0) && (tok == MDOC_Em || | |||
| 615 | tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) { | |||
| 616 | nw = 0; | |||
| 617 | for (cp = lc - 1; cp >= nch->string; cp--) { | |||
| 618 | if (*cp == ' ') { | |||
| 619 | nw++; | |||
| 620 | if (cp > nch->string && cp[-1] == ',') | |||
| 621 | cp--; | |||
| 622 | } else if (isalpha((unsigned int)*cp)) { | |||
| 623 | if (nw > 1) | |||
| 624 | return; | |||
| 625 | } else | |||
| 626 | break; | |||
| 627 | } | |||
| 628 | } | |||
| 629 | ||||
| 630 | mandoc_msg(MANDOCERR_DELIM_NB, nch->line, | |||
| 631 | nch->pos + (int)(lc - nch->string), "%s%s %s", roff_name[tok], | |||
| 632 | nch == mdoc->last->child ? "" : " ...", nch->string); | |||
| 633 | } | |||
| 634 | ||||
| 635 | static void | |||
| 636 | post_bl_norm(POST_ARGSstruct roff_man *mdoc) | |||
| 637 | { | |||
| 638 | struct roff_node *n; | |||
| 639 | struct mdoc_argv *argv, *wa; | |||
| 640 | int i; | |||
| 641 | enum mdocargt mdoclt; | |||
| 642 | enum mdoc_list lt; | |||
| 643 | ||||
| 644 | n = mdoc->last->parent; | |||
| 645 | n->norm->Bl.type = LIST__NONE; | |||
| 646 | ||||
| 647 | /* | |||
| 648 | * First figure out which kind of list to use: bind ourselves to | |||
| 649 | * the first mentioned list type and warn about any remaining | |||
| 650 | * ones. If we find no list type, we default to LIST_item. | |||
| 651 | */ | |||
| 652 | ||||
| 653 | wa = (n->args == NULL((void *)0)) ? NULL((void *)0) : n->args->argv; | |||
| 654 | mdoclt = MDOC_ARG_MAX; | |||
| 655 | for (i = 0; n->args && i < (int)n->args->argc; i++) { | |||
| 656 | argv = n->args->argv + i; | |||
| 657 | lt = LIST__NONE; | |||
| 658 | switch (argv->arg) { | |||
| 659 | /* Set list types. */ | |||
| 660 | case MDOC_Bullet: | |||
| 661 | lt = LIST_bullet; | |||
| 662 | break; | |||
| 663 | case MDOC_Dash: | |||
| 664 | lt = LIST_dash; | |||
| 665 | break; | |||
| 666 | case MDOC_Enum: | |||
| 667 | lt = LIST_enum; | |||
| 668 | break; | |||
| 669 | case MDOC_Hyphen: | |||
| 670 | lt = LIST_hyphen; | |||
| 671 | break; | |||
| 672 | case MDOC_Item: | |||
| 673 | lt = LIST_item; | |||
| 674 | break; | |||
| 675 | case MDOC_Tag: | |||
| 676 | lt = LIST_tag; | |||
| 677 | break; | |||
| 678 | case MDOC_Diag: | |||
| 679 | lt = LIST_diag; | |||
| 680 | break; | |||
| 681 | case MDOC_Hang: | |||
| 682 | lt = LIST_hang; | |||
| 683 | break; | |||
| 684 | case MDOC_Ohang: | |||
| 685 | lt = LIST_ohang; | |||
| 686 | break; | |||
| 687 | case MDOC_Inset: | |||
| 688 | lt = LIST_inset; | |||
| 689 | break; | |||
| 690 | case MDOC_Column: | |||
| 691 | lt = LIST_column; | |||
| 692 | break; | |||
| 693 | /* Set list arguments. */ | |||
| 694 | case MDOC_Compact: | |||
| 695 | if (n->norm->Bl.comp) | |||
| 696 | mandoc_msg(MANDOCERR_ARG_REP, | |||
| 697 | argv->line, argv->pos, "Bl -compact"); | |||
| 698 | n->norm->Bl.comp = 1; | |||
| 699 | break; | |||
| 700 | case MDOC_Width: | |||
| 701 | wa = argv; | |||
| 702 | if (0 == argv->sz) { | |||
| 703 | mandoc_msg(MANDOCERR_ARG_EMPTY, | |||
| 704 | argv->line, argv->pos, "Bl -width"); | |||
| 705 | n->norm->Bl.width = "0n"; | |||
| 706 | break; | |||
| 707 | } | |||
| 708 | if (NULL((void *)0) != n->norm->Bl.width) | |||
| 709 | mandoc_msg(MANDOCERR_ARG_REP, | |||
| 710 | argv->line, argv->pos, | |||
| 711 | "Bl -width %s", argv->value[0]); | |||
| 712 | rewrite_macro2len(mdoc, argv->value); | |||
| 713 | n->norm->Bl.width = argv->value[0]; | |||
| 714 | break; | |||
| 715 | case MDOC_Offset: | |||
| 716 | if (0 == argv->sz) { | |||
| 717 | mandoc_msg(MANDOCERR_ARG_EMPTY, | |||
| 718 | argv->line, argv->pos, "Bl -offset"); | |||
| 719 | break; | |||
| 720 | } | |||
| 721 | if (NULL((void *)0) != n->norm->Bl.offs) | |||
| 722 | mandoc_msg(MANDOCERR_ARG_REP, | |||
| 723 | argv->line, argv->pos, | |||
| 724 | "Bl -offset %s", argv->value[0]); | |||
| 725 | rewrite_macro2len(mdoc, argv->value); | |||
| 726 | n->norm->Bl.offs = argv->value[0]; | |||
| 727 | break; | |||
| 728 | default: | |||
| 729 | continue; | |||
| 730 | } | |||
| 731 | if (LIST__NONE == lt) | |||
| 732 | continue; | |||
| 733 | mdoclt = argv->arg; | |||
| 734 | ||||
| 735 | /* Check: multiple list types. */ | |||
| 736 | ||||
| 737 | if (LIST__NONE != n->norm->Bl.type) { | |||
| 738 | mandoc_msg(MANDOCERR_BL_REP, n->line, n->pos, | |||
| 739 | "Bl -%s", mdoc_argnames[argv->arg]); | |||
| 740 | continue; | |||
| 741 | } | |||
| 742 | ||||
| 743 | /* The list type should come first. */ | |||
| 744 | ||||
| 745 | if (n->norm->Bl.width || | |||
| 746 | n->norm->Bl.offs || | |||
| 747 | n->norm->Bl.comp) | |||
| 748 | mandoc_msg(MANDOCERR_BL_LATETYPE, | |||
| 749 | n->line, n->pos, "Bl -%s", | |||
| 750 | mdoc_argnames[n->args->argv[0].arg]); | |||
| 751 | ||||
| 752 | n->norm->Bl.type = lt; | |||
| 753 | if (LIST_column == lt) { | |||
| 754 | n->norm->Bl.ncols = argv->sz; | |||
| 755 | n->norm->Bl.cols = (void *)argv->value; | |||
| 756 | } | |||
| 757 | } | |||
| 758 | ||||
| 759 | /* Allow lists to default to LIST_item. */ | |||
| 760 | ||||
| 761 | if (LIST__NONE == n->norm->Bl.type) { | |||
| 762 | mandoc_msg(MANDOCERR_BL_NOTYPE, n->line, n->pos, "Bl"); | |||
| 763 | n->norm->Bl.type = LIST_item; | |||
| 764 | mdoclt = MDOC_Item; | |||
| 765 | } | |||
| 766 | ||||
| 767 | /* | |||
| 768 | * Validate the width field. Some list types don't need width | |||
| 769 | * types and should be warned about them. Others should have it | |||
| 770 | * and must also be warned. Yet others have a default and need | |||
| 771 | * no warning. | |||
| 772 | */ | |||
| 773 | ||||
| 774 | switch (n->norm->Bl.type) { | |||
| 775 | case LIST_tag: | |||
| 776 | if (n->norm->Bl.width == NULL((void *)0)) | |||
| 777 | mandoc_msg(MANDOCERR_BL_NOWIDTH, | |||
| 778 | n->line, n->pos, "Bl -tag"); | |||
| 779 | break; | |||
| 780 | case LIST_column: | |||
| 781 | case LIST_diag: | |||
| 782 | case LIST_ohang: | |||
| 783 | case LIST_inset: | |||
| 784 | case LIST_item: | |||
| 785 | if (n->norm->Bl.width != NULL((void *)0)) | |||
| 786 | mandoc_msg(MANDOCERR_BL_SKIPW, wa->line, wa->pos, | |||
| 787 | "Bl -%s", mdoc_argnames[mdoclt]); | |||
| 788 | n->norm->Bl.width = NULL((void *)0); | |||
| 789 | break; | |||
| 790 | case LIST_bullet: | |||
| 791 | case LIST_dash: | |||
| 792 | case LIST_hyphen: | |||
| 793 | if (n->norm->Bl.width == NULL((void *)0)) | |||
| 794 | n->norm->Bl.width = "2n"; | |||
| 795 | break; | |||
| 796 | case LIST_enum: | |||
| 797 | if (n->norm->Bl.width == NULL((void *)0)) | |||
| 798 | n->norm->Bl.width = "3n"; | |||
| 799 | break; | |||
| 800 | default: | |||
| 801 | break; | |||
| 802 | } | |||
| 803 | } | |||
| 804 | ||||
| 805 | static void | |||
| 806 | post_bd(POST_ARGSstruct roff_man *mdoc) | |||
| 807 | { | |||
| 808 | struct roff_node *n; | |||
| 809 | struct mdoc_argv *argv; | |||
| 810 | int i; | |||
| 811 | enum mdoc_disp dt; | |||
| 812 | ||||
| 813 | n = mdoc->last; | |||
| 814 | for (i = 0; n->args && i < (int)n->args->argc; i++) { | |||
| 815 | argv = n->args->argv + i; | |||
| 816 | dt = DISP__NONE; | |||
| 817 | ||||
| 818 | switch (argv->arg) { | |||
| 819 | case MDOC_Centred: | |||
| 820 | dt = DISP_centered; | |||
| 821 | break; | |||
| 822 | case MDOC_Ragged: | |||
| 823 | dt = DISP_ragged; | |||
| 824 | break; | |||
| 825 | case MDOC_Unfilled: | |||
| 826 | dt = DISP_unfilled; | |||
| 827 | break; | |||
| 828 | case MDOC_Filled: | |||
| 829 | dt = DISP_filled; | |||
| 830 | break; | |||
| 831 | case MDOC_Literal: | |||
| 832 | dt = DISP_literal; | |||
| 833 | break; | |||
| 834 | case MDOC_File: | |||
| 835 | mandoc_msg(MANDOCERR_BD_FILE, n->line, n->pos, NULL((void *)0)); | |||
| 836 | break; | |||
| 837 | case MDOC_Offset: | |||
| 838 | if (0 == argv->sz) { | |||
| 839 | mandoc_msg(MANDOCERR_ARG_EMPTY, | |||
| 840 | argv->line, argv->pos, "Bd -offset"); | |||
| 841 | break; | |||
| 842 | } | |||
| 843 | if (NULL((void *)0) != n->norm->Bd.offs) | |||
| 844 | mandoc_msg(MANDOCERR_ARG_REP, | |||
| 845 | argv->line, argv->pos, | |||
| 846 | "Bd -offset %s", argv->value[0]); | |||
| 847 | rewrite_macro2len(mdoc, argv->value); | |||
| 848 | n->norm->Bd.offs = argv->value[0]; | |||
| 849 | break; | |||
| 850 | case MDOC_Compact: | |||
| 851 | if (n->norm->Bd.comp) | |||
| 852 | mandoc_msg(MANDOCERR_ARG_REP, | |||
| 853 | argv->line, argv->pos, "Bd -compact"); | |||
| 854 | n->norm->Bd.comp = 1; | |||
| 855 | break; | |||
| 856 | default: | |||
| 857 | abort(); | |||
| 858 | } | |||
| 859 | if (DISP__NONE == dt) | |||
| 860 | continue; | |||
| 861 | ||||
| 862 | if (DISP__NONE == n->norm->Bd.type) | |||
| 863 | n->norm->Bd.type = dt; | |||
| 864 | else | |||
| 865 | mandoc_msg(MANDOCERR_BD_REP, n->line, n->pos, | |||
| 866 | "Bd -%s", mdoc_argnames[argv->arg]); | |||
| 867 | } | |||
| 868 | ||||
| 869 | if (DISP__NONE == n->norm->Bd.type) { | |||
| 870 | mandoc_msg(MANDOCERR_BD_NOTYPE, n->line, n->pos, "Bd"); | |||
| 871 | n->norm->Bd.type = DISP_ragged; | |||
| 872 | } | |||
| 873 | } | |||
| 874 | ||||
| 875 | /* | |||
| 876 | * Stand-alone line macros. | |||
| 877 | */ | |||
| 878 | ||||
| 879 | static void | |||
| 880 | post_an_norm(POST_ARGSstruct roff_man *mdoc) | |||
| 881 | { | |||
| 882 | struct roff_node *n; | |||
| 883 | struct mdoc_argv *argv; | |||
| 884 | size_t i; | |||
| 885 | ||||
| 886 | n = mdoc->last; | |||
| 887 | if (n->args == NULL((void *)0)) | |||
| 888 | return; | |||
| 889 | ||||
| 890 | for (i = 1; i < n->args->argc; i++) { | |||
| 891 | argv = n->args->argv + i; | |||
| 892 | mandoc_msg(MANDOCERR_AN_REP, argv->line, argv->pos, | |||
| 893 | "An -%s", mdoc_argnames[argv->arg]); | |||
| 894 | } | |||
| 895 | ||||
| 896 | argv = n->args->argv; | |||
| 897 | if (argv->arg == MDOC_Split) | |||
| 898 | n->norm->An.auth = AUTH_split; | |||
| 899 | else if (argv->arg == MDOC_Nosplit) | |||
| 900 | n->norm->An.auth = AUTH_nosplit; | |||
| 901 | else | |||
| 902 | abort(); | |||
| 903 | } | |||
| 904 | ||||
| 905 | static void | |||
| 906 | post_eoln(POST_ARGSstruct roff_man *mdoc) | |||
| 907 | { | |||
| 908 | struct roff_node *n; | |||
| 909 | ||||
| 910 | post_useless(mdoc); | |||
| 911 | n = mdoc->last; | |||
| 912 | if (n->child != NULL((void *)0)) | |||
| 913 | mandoc_msg(MANDOCERR_ARG_SKIP, n->line, | |||
| 914 | n->pos, "%s %s", roff_name[n->tok], n->child->string); | |||
| 915 | ||||
| 916 | while (n->child != NULL((void *)0)) | |||
| 917 | roff_node_delete(mdoc, n->child); | |||
| 918 | ||||
| 919 | roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ? | |||
| 920 | "is currently in beta test." : "currently under development."); | |||
| 921 | mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9); | |||
| 922 | mdoc->last = n; | |||
| 923 | } | |||
| 924 | ||||
| 925 | static int | |||
| 926 | build_list(struct roff_man *mdoc, int tok) | |||
| 927 | { | |||
| 928 | struct roff_node *n; | |||
| 929 | int ic; | |||
| 930 | ||||
| 931 | n = mdoc->last->next; | |||
| 932 | for (ic = 1;; ic++) { | |||
| 933 | roff_elem_alloc(mdoc, n->line, n->pos, tok); | |||
| 934 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 935 | roff_node_relink(mdoc, n); | |||
| 936 | n = mdoc->last = mdoc->last->parent; | |||
| 937 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 938 | if (n->next == NULL((void *)0)) | |||
| 939 | return ic; | |||
| 940 | if (ic > 1 || n->next->next != NULL((void *)0)) { | |||
| 941 | roff_word_alloc(mdoc, n->line, n->pos, ","); | |||
| 942 | mdoc->last->flags |= NODE_DELIMC(1 << 5) | NODE_NOSRC(1 << 9); | |||
| 943 | } | |||
| 944 | n = mdoc->last->next; | |||
| 945 | if (n->next == NULL((void *)0)) { | |||
| 946 | roff_word_alloc(mdoc, n->line, n->pos, "and"); | |||
| 947 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 948 | } | |||
| 949 | } | |||
| 950 | } | |||
| 951 | ||||
| 952 | static void | |||
| 953 | post_ex(POST_ARGSstruct roff_man *mdoc) | |||
| 954 | { | |||
| 955 | struct roff_node *n; | |||
| 956 | int ic; | |||
| 957 | ||||
| 958 | post_std(mdoc); | |||
| 959 | ||||
| 960 | n = mdoc->last; | |||
| 961 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 962 | roff_word_alloc(mdoc, n->line, n->pos, "The"); | |||
| 963 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 964 | ||||
| 965 | if (mdoc->last->next != NULL((void *)0)) | |||
| 966 | ic = build_list(mdoc, MDOC_Nm); | |||
| 967 | else if (mdoc->meta.name != NULL((void *)0)) { | |||
| 968 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm); | |||
| 969 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 970 | roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | |||
| 971 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 972 | mdoc->last = mdoc->last->parent; | |||
| 973 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 974 | ic = 1; | |||
| 975 | } else { | |||
| 976 | mandoc_msg(MANDOCERR_EX_NONAME, n->line, n->pos, "Ex"); | |||
| 977 | ic = 0; | |||
| 978 | } | |||
| 979 | ||||
| 980 | roff_word_alloc(mdoc, n->line, n->pos, | |||
| 981 | ic > 1 ? "utilities exit\\~0" : "utility exits\\~0"); | |||
| 982 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 983 | roff_word_alloc(mdoc, n->line, n->pos, | |||
| 984 | "on success, and\\~>0 if an error occurs."); | |||
| 985 | mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9); | |||
| 986 | mdoc->last = n; | |||
| 987 | } | |||
| 988 | ||||
| 989 | static void | |||
| 990 | post_lb(POST_ARGSstruct roff_man *mdoc) | |||
| 991 | { | |||
| 992 | struct roff_node *n; | |||
| 993 | ||||
| 994 | post_delim_nb(mdoc); | |||
| 995 | ||||
| 996 | n = mdoc->last; | |||
| 997 | assert(n->child->type == ROFFT_TEXT)((n->child->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 997, __func__, "n->child->type == ROFFT_TEXT")); | |||
| 998 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 999 | roff_word_alloc(mdoc, n->line, n->pos, "library"); | |||
| 1000 | mdoc->last->flags = NODE_NOSRC(1 << 9); | |||
| 1001 | roff_word_alloc(mdoc, n->line, n->pos, "\\(lq"); | |||
| 1002 | mdoc->last->flags = NODE_DELIMO(1 << 4) | NODE_NOSRC(1 << 9); | |||
| 1003 | mdoc->last = mdoc->last->next; | |||
| 1004 | roff_word_alloc(mdoc, n->line, n->pos, "\\(rq"); | |||
| 1005 | mdoc->last->flags = NODE_DELIMC(1 << 5) | NODE_NOSRC(1 << 9); | |||
| 1006 | mdoc->last = n; | |||
| 1007 | } | |||
| 1008 | ||||
| 1009 | static void | |||
| 1010 | post_rv(POST_ARGSstruct roff_man *mdoc) | |||
| 1011 | { | |||
| 1012 | struct roff_node *n; | |||
| 1013 | int ic; | |||
| 1014 | ||||
| 1015 | post_std(mdoc); | |||
| 1016 | ||||
| 1017 | n = mdoc->last; | |||
| 1018 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1019 | if (n->child != NULL((void *)0)) { | |||
| 1020 | roff_word_alloc(mdoc, n->line, n->pos, "The"); | |||
| 1021 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1022 | ic = build_list(mdoc, MDOC_Fn); | |||
| 1023 | roff_word_alloc(mdoc, n->line, n->pos, | |||
| 1024 | ic > 1 ? "functions return" : "function returns"); | |||
| 1025 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1026 | roff_word_alloc(mdoc, n->line, n->pos, | |||
| 1027 | "the value\\~0 if successful;"); | |||
| 1028 | } else | |||
| 1029 | roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " | |||
| 1030 | "completion, the value\\~0 is returned;"); | |||
| 1031 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1032 | ||||
| 1033 | roff_word_alloc(mdoc, n->line, n->pos, "otherwise " | |||
| 1034 | "the value\\~\\-1 is returned and the global variable"); | |||
| 1035 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1036 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); | |||
| 1037 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1038 | roff_word_alloc(mdoc, n->line, n->pos, "errno"); | |||
| 1039 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1040 | mdoc->last = mdoc->last->parent; | |||
| 1041 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1042 | roff_word_alloc(mdoc, n->line, n->pos, | |||
| 1043 | "is set to indicate the error."); | |||
| 1044 | mdoc->last->flags |= NODE_EOS(1 << 6) | NODE_NOSRC(1 << 9); | |||
| 1045 | mdoc->last = n; | |||
| 1046 | } | |||
| 1047 | ||||
| 1048 | static void | |||
| 1049 | post_std(POST_ARGSstruct roff_man *mdoc) | |||
| 1050 | { | |||
| 1051 | struct roff_node *n; | |||
| 1052 | ||||
| 1053 | post_delim(mdoc); | |||
| 1054 | ||||
| 1055 | n = mdoc->last; | |||
| 1056 | if (n->args && n->args->argc == 1) | |||
| 1057 | if (n->args->argv[0].arg == MDOC_Std) | |||
| 1058 | return; | |||
| 1059 | ||||
| 1060 | mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos, | |||
| 1061 | "%s", roff_name[n->tok]); | |||
| 1062 | } | |||
| 1063 | ||||
| 1064 | static void | |||
| 1065 | post_st(POST_ARGSstruct roff_man *mdoc) | |||
| 1066 | { | |||
| 1067 | struct roff_node *n, *nch; | |||
| 1068 | const char *p; | |||
| 1069 | ||||
| 1070 | n = mdoc->last; | |||
| 1071 | nch = n->child; | |||
| 1072 | assert(nch->type == ROFFT_TEXT)((nch->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 1072, __func__, "nch->type == ROFFT_TEXT")); | |||
| 1073 | ||||
| 1074 | if ((p = mdoc_a2st(nch->string)) == NULL((void *)0)) { | |||
| 1075 | mandoc_msg(MANDOCERR_ST_BAD, | |||
| 1076 | nch->line, nch->pos, "St %s", nch->string); | |||
| 1077 | roff_node_delete(mdoc, n); | |||
| 1078 | return; | |||
| 1079 | } | |||
| 1080 | ||||
| 1081 | nch->flags |= NODE_NOPRT(1 << 10); | |||
| 1082 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1083 | roff_word_alloc(mdoc, nch->line, nch->pos, p); | |||
| 1084 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1085 | mdoc->last= n; | |||
| 1086 | } | |||
| 1087 | ||||
| 1088 | static void | |||
| 1089 | post_tg(POST_ARGSstruct roff_man *mdoc) | |||
| 1090 | { | |||
| 1091 | struct roff_node *n; /* The .Tg node. */ | |||
| 1092 | struct roff_node *nch; /* The first child of the .Tg node. */ | |||
| 1093 | struct roff_node *nn; /* The next node after the .Tg node. */ | |||
| 1094 | struct roff_node *np; /* The parent of the next node. */ | |||
| 1095 | struct roff_node *nt; /* The TEXT node containing the tag. */ | |||
| 1096 | size_t len; /* The number of bytes in the tag. */ | |||
| 1097 | ||||
| 1098 | /* Find the next node. */ | |||
| 1099 | n = mdoc->last; | |||
| ||||
| 1100 | for (nn = n; nn != NULL((void *)0); nn = nn->parent) { | |||
| 1101 | if (nn->next != NULL((void *)0)) { | |||
| 1102 | nn = nn->next; | |||
| 1103 | break; | |||
| 1104 | } | |||
| 1105 | } | |||
| 1106 | ||||
| 1107 | /* Find the tag. */ | |||
| 1108 | nt = nch = n->child; | |||
| ||||
| 1109 | if (nch == NULL((void *)0) && nn != NULL((void *)0) && nn->child != NULL((void *)0) && | |||
| 1110 | nn->child->type == ROFFT_TEXT) | |||
| 1111 | nt = nn->child; | |||
| 1112 | ||||
| 1113 | /* Validate the tag. */ | |||
| 1114 | if (nt == NULL((void *)0) || *nt->string == '\0') | |||
| 1115 | mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); | |||
| 1116 | if (nt == NULL((void *)0)) { | |||
| 1117 | roff_node_delete(mdoc, n); | |||
| 1118 | return; | |||
| 1119 | } | |||
| 1120 | len = strcspn(nt->string, " \t\\"); | |||
| 1121 | if (nt->string[len] != '\0') | |||
| 1122 | mandoc_msg(MANDOCERR_TG_SPC, nt->line, | |||
| 1123 | nt->pos + len, "Tg %s", nt->string); | |||
| 1124 | ||||
| 1125 | /* Keep only the first argument. */ | |||
| 1126 | if (nch != NULL((void *)0) && nch->next != NULL((void *)0)) { | |||
| 1127 | mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, | |||
| 1128 | nch->next->pos, "Tg ... %s", nch->next->string); | |||
| 1129 | while (nch->next != NULL((void *)0)) | |||
| 1130 | roff_node_delete(mdoc, nch->next); | |||
| 1131 | } | |||
| 1132 | ||||
| 1133 | /* Drop the macro if the first argument is invalid. */ | |||
| 1134 | if (len == 0 || nt->string[len] != '\0') { | |||
| 1135 | roff_node_delete(mdoc, n); | |||
| 1136 | return; | |||
| 1137 | } | |||
| 1138 | ||||
| 1139 | /* By default, tag the .Tg node itself. */ | |||
| 1140 | if (nn == NULL((void *)0) || nn->flags & NODE_ID(1 << 11)) | |||
| 1141 | nn = n; | |||
| 1142 | ||||
| 1143 | /* Explicit tagging of specific macros. */ | |||
| 1144 | switch (nn->tok) { | |||
| 1145 | case MDOC_Sh: | |||
| 1146 | case MDOC_Ss: | |||
| 1147 | case MDOC_Fo: | |||
| 1148 | nn = nn->head->child == NULL((void *)0) ? n : nn->head; | |||
| 1149 | break; | |||
| 1150 | case MDOC_It: | |||
| 1151 | np = nn->parent; | |||
| 1152 | while (np->tok != MDOC_Bl) | |||
| 1153 | np = np->parent; | |||
| 1154 | switch (np->norm->Bl.type) { | |||
| 1155 | case LIST_column: | |||
| 1156 | break; | |||
| 1157 | case LIST_diag: | |||
| 1158 | case LIST_hang: | |||
| 1159 | case LIST_inset: | |||
| 1160 | case LIST_ohang: | |||
| 1161 | case LIST_tag: | |||
| 1162 | nn = nn->head; | |||
| 1163 | break; | |||
| 1164 | case LIST_bullet: | |||
| 1165 | case LIST_dash: | |||
| 1166 | case LIST_enum: | |||
| 1167 | case LIST_hyphen: | |||
| 1168 | case LIST_item: | |||
| 1169 | nn = nn->body->child == NULL((void *)0) ? n : nn->body; | |||
| 1170 | break; | |||
| 1171 | default: | |||
| 1172 | abort(); | |||
| 1173 | } | |||
| 1174 | break; | |||
| 1175 | case MDOC_Bd: | |||
| 1176 | case MDOC_Bl: | |||
| 1177 | case MDOC_D1: | |||
| 1178 | case MDOC_Dl: | |||
| 1179 | nn = nn->body->child == NULL((void *)0) ? n : nn->body; | |||
| 1180 | break; | |||
| 1181 | case MDOC_Pp: | |||
| 1182 | break; | |||
| 1183 | case MDOC_Cm: | |||
| 1184 | case MDOC_Dv: | |||
| 1185 | case MDOC_Em: | |||
| 1186 | case MDOC_Er: | |||
| 1187 | case MDOC_Ev: | |||
| 1188 | case MDOC_Fl: | |||
| 1189 | case MDOC_Fn: | |||
| 1190 | case MDOC_Ic: | |||
| 1191 | case MDOC_Li: | |||
| 1192 | case MDOC_Ms: | |||
| 1193 | case MDOC_No: | |||
| 1194 | case MDOC_Sy: | |||
| 1195 | if (nn->child == NULL((void *)0)) | |||
| 1196 | nn = n; | |||
| 1197 | break; | |||
| 1198 | default: | |||
| 1199 | nn = n; | |||
| 1200 | break; | |||
| 1201 | } | |||
| 1202 | tag_put(nt->string, TAG_MANUAL1, nn); | |||
| 1203 | if (nn != n) | |||
| 1204 | n->flags |= NODE_NOPRT(1 << 10); | |||
| 1205 | } | |||
| 1206 | ||||
| 1207 | static void | |||
| 1208 | post_obsolete(POST_ARGSstruct roff_man *mdoc) | |||
| 1209 | { | |||
| 1210 | struct roff_node *n; | |||
| 1211 | ||||
| 1212 | n = mdoc->last; | |||
| 1213 | if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) | |||
| 1214 | mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, | |||
| 1215 | "%s", roff_name[n->tok]); | |||
| 1216 | } | |||
| 1217 | ||||
| 1218 | static void | |||
| 1219 | post_useless(POST_ARGSstruct roff_man *mdoc) | |||
| 1220 | { | |||
| 1221 | struct roff_node *n; | |||
| 1222 | ||||
| 1223 | n = mdoc->last; | |||
| 1224 | mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, | |||
| 1225 | "%s", roff_name[n->tok]); | |||
| 1226 | } | |||
| 1227 | ||||
| 1228 | /* | |||
| 1229 | * Block macros. | |||
| 1230 | */ | |||
| 1231 | ||||
| 1232 | static void | |||
| 1233 | post_bf(POST_ARGSstruct roff_man *mdoc) | |||
| 1234 | { | |||
| 1235 | struct roff_node *np, *nch; | |||
| 1236 | ||||
| 1237 | /* | |||
| 1238 | * Unlike other data pointers, these are "housed" by the HEAD | |||
| 1239 | * element, which contains the goods. | |||
| 1240 | */ | |||
| 1241 | ||||
| 1242 | np = mdoc->last; | |||
| 1243 | if (np->type != ROFFT_HEAD) | |||
| 1244 | return; | |||
| 1245 | ||||
| 1246 | assert(np->parent->type == ROFFT_BLOCK)((np->parent->type == ROFFT_BLOCK) ? (void)0 : __assert2 ("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1246, __func__, "np->parent->type == ROFFT_BLOCK" )); | |||
| 1247 | assert(np->parent->tok == MDOC_Bf)((np->parent->tok == MDOC_Bf) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 1247, __func__, "np->parent->tok == MDOC_Bf")); | |||
| 1248 | ||||
| 1249 | /* Check the number of arguments. */ | |||
| 1250 | ||||
| 1251 | nch = np->child; | |||
| 1252 | if (np->parent->args == NULL((void *)0)) { | |||
| 1253 | if (nch == NULL((void *)0)) { | |||
| 1254 | mandoc_msg(MANDOCERR_BF_NOFONT, | |||
| 1255 | np->line, np->pos, "Bf"); | |||
| 1256 | return; | |||
| 1257 | } | |||
| 1258 | nch = nch->next; | |||
| 1259 | } | |||
| 1260 | if (nch != NULL((void *)0)) | |||
| 1261 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
| 1262 | nch->line, nch->pos, "Bf ... %s", nch->string); | |||
| 1263 | ||||
| 1264 | /* Extract argument into data. */ | |||
| 1265 | ||||
| 1266 | if (np->parent->args != NULL((void *)0)) { | |||
| 1267 | switch (np->parent->args->argv[0].arg) { | |||
| 1268 | case MDOC_Emphasis: | |||
| 1269 | np->norm->Bf.font = FONT_Em; | |||
| 1270 | break; | |||
| 1271 | case MDOC_Literal: | |||
| 1272 | np->norm->Bf.font = FONT_Li; | |||
| 1273 | break; | |||
| 1274 | case MDOC_Symbolic: | |||
| 1275 | np->norm->Bf.font = FONT_Sy; | |||
| 1276 | break; | |||
| 1277 | default: | |||
| 1278 | abort(); | |||
| 1279 | } | |||
| 1280 | return; | |||
| 1281 | } | |||
| 1282 | ||||
| 1283 | /* Extract parameter into data. */ | |||
| 1284 | ||||
| 1285 | if ( ! strcmp(np->child->string, "Em")) | |||
| 1286 | np->norm->Bf.font = FONT_Em; | |||
| 1287 | else if ( ! strcmp(np->child->string, "Li")) | |||
| 1288 | np->norm->Bf.font = FONT_Li; | |||
| 1289 | else if ( ! strcmp(np->child->string, "Sy")) | |||
| 1290 | np->norm->Bf.font = FONT_Sy; | |||
| 1291 | else | |||
| 1292 | mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, | |||
| 1293 | np->child->pos, "Bf %s", np->child->string); | |||
| 1294 | } | |||
| 1295 | ||||
| 1296 | static void | |||
| 1297 | post_fname(POST_ARGSstruct roff_man *mdoc) | |||
| 1298 | { | |||
| 1299 | struct roff_node *n, *nch; | |||
| 1300 | const char *cp; | |||
| 1301 | size_t pos; | |||
| 1302 | ||||
| 1303 | n = mdoc->last; | |||
| 1304 | nch = n->child; | |||
| 1305 | cp = nch->string; | |||
| 1306 | if (*cp == '(') { | |||
| 1307 | if (cp[strlen(cp + 1)] == ')') | |||
| 1308 | return; | |||
| 1309 | pos = 0; | |||
| 1310 | } else { | |||
| 1311 | pos = strcspn(cp, "()"); | |||
| 1312 | if (cp[pos] == '\0') { | |||
| 1313 | if (n->sec == SEC_DESCRIPTION || | |||
| 1314 | n->sec == SEC_CUSTOM) | |||
| 1315 | tag_put(NULL((void *)0), fn_prio++, n); | |||
| 1316 | return; | |||
| 1317 | } | |||
| 1318 | } | |||
| 1319 | mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); | |||
| 1320 | } | |||
| 1321 | ||||
| 1322 | static void | |||
| 1323 | post_fn(POST_ARGSstruct roff_man *mdoc) | |||
| 1324 | { | |||
| 1325 | post_fname(mdoc); | |||
| 1326 | post_fa(mdoc); | |||
| 1327 | } | |||
| 1328 | ||||
| 1329 | static void | |||
| 1330 | post_fo(POST_ARGSstruct roff_man *mdoc) | |||
| 1331 | { | |||
| 1332 | const struct roff_node *n; | |||
| 1333 | ||||
| 1334 | n = mdoc->last; | |||
| 1335 | ||||
| 1336 | if (n->type != ROFFT_HEAD) | |||
| 1337 | return; | |||
| 1338 | ||||
| 1339 | if (n->child == NULL((void *)0)) { | |||
| 1340 | mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); | |||
| 1341 | return; | |||
| 1342 | } | |||
| 1343 | if (n->child != n->last) { | |||
| 1344 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
| 1345 | n->child->next->line, n->child->next->pos, | |||
| 1346 | "Fo ... %s", n->child->next->string); | |||
| 1347 | while (n->child != n->last) | |||
| 1348 | roff_node_delete(mdoc, n->last); | |||
| 1349 | } else | |||
| 1350 | post_delim(mdoc); | |||
| 1351 | ||||
| 1352 | post_fname(mdoc); | |||
| 1353 | } | |||
| 1354 | ||||
| 1355 | static void | |||
| 1356 | post_fa(POST_ARGSstruct roff_man *mdoc) | |||
| 1357 | { | |||
| 1358 | const struct roff_node *n; | |||
| 1359 | const char *cp; | |||
| 1360 | ||||
| 1361 | for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) { | |||
| 1362 | for (cp = n->string; *cp != '\0'; cp++) { | |||
| 1363 | /* Ignore callbacks and alterations. */ | |||
| 1364 | if (*cp == '(' || *cp == '{') | |||
| 1365 | break; | |||
| 1366 | if (*cp != ',') | |||
| 1367 | continue; | |||
| 1368 | mandoc_msg(MANDOCERR_FA_COMMA, n->line, | |||
| 1369 | n->pos + (int)(cp - n->string), "%s", n->string); | |||
| 1370 | break; | |||
| 1371 | } | |||
| 1372 | } | |||
| 1373 | post_delim_nb(mdoc); | |||
| 1374 | } | |||
| 1375 | ||||
| 1376 | static void | |||
| 1377 | post_nm(POST_ARGSstruct roff_man *mdoc) | |||
| 1378 | { | |||
| 1379 | struct roff_node *n; | |||
| 1380 | ||||
| 1381 | n = mdoc->last; | |||
| 1382 | ||||
| 1383 | if (n->sec == SEC_NAME && n->child != NULL((void *)0) && | |||
| 1384 | n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL((void *)0)) | |||
| 1385 | mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); | |||
| 1386 | ||||
| 1387 | if (n->last != NULL((void *)0) && n->last->tok == MDOC_Pp) | |||
| 1388 | roff_node_relink(mdoc, n->last); | |||
| 1389 | ||||
| 1390 | if (mdoc->meta.name == NULL((void *)0)) | |||
| 1391 | deroff(&mdoc->meta.name, n); | |||
| 1392 | ||||
| 1393 | if (mdoc->meta.name == NULL((void *)0) || | |||
| 1394 | (mdoc->lastsec == SEC_NAME && n->child == NULL((void *)0))) | |||
| 1395 | mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); | |||
| 1396 | ||||
| 1397 | switch (n->type) { | |||
| 1398 | case ROFFT_ELEM: | |||
| 1399 | post_delim_nb(mdoc); | |||
| 1400 | break; | |||
| 1401 | case ROFFT_HEAD: | |||
| 1402 | post_delim(mdoc); | |||
| 1403 | break; | |||
| 1404 | default: | |||
| 1405 | return; | |||
| 1406 | } | |||
| 1407 | ||||
| 1408 | if ((n->child != NULL((void *)0) && n->child->type == ROFFT_TEXT) || | |||
| 1409 | mdoc->meta.name == NULL((void *)0)) | |||
| 1410 | return; | |||
| 1411 | ||||
| 1412 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1413 | roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); | |||
| 1414 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1415 | mdoc->last = n; | |||
| 1416 | } | |||
| 1417 | ||||
| 1418 | static void | |||
| 1419 | post_nd(POST_ARGSstruct roff_man *mdoc) | |||
| 1420 | { | |||
| 1421 | struct roff_node *n; | |||
| 1422 | ||||
| 1423 | n = mdoc->last; | |||
| 1424 | ||||
| 1425 | if (n->type != ROFFT_BODY) | |||
| 1426 | return; | |||
| 1427 | ||||
| 1428 | if (n->sec != SEC_NAME) | |||
| 1429 | mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); | |||
| 1430 | ||||
| 1431 | if (n->child == NULL((void *)0)) | |||
| 1432 | mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); | |||
| 1433 | else | |||
| 1434 | post_delim(mdoc); | |||
| 1435 | ||||
| 1436 | post_hyph(mdoc); | |||
| 1437 | } | |||
| 1438 | ||||
| 1439 | static void | |||
| 1440 | post_display(POST_ARGSstruct roff_man *mdoc) | |||
| 1441 | { | |||
| 1442 | struct roff_node *n, *np; | |||
| 1443 | ||||
| 1444 | n = mdoc->last; | |||
| 1445 | switch (n->type) { | |||
| 1446 | case ROFFT_BODY: | |||
| 1447 | if (n->end != ENDBODY_NOT) { | |||
| 1448 | if (n->tok == MDOC_Bd && | |||
| 1449 | n->body->parent->args == NULL((void *)0)) | |||
| 1450 | roff_node_delete(mdoc, n); | |||
| 1451 | } else if (n->child == NULL((void *)0)) | |||
| 1452 | mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, | |||
| 1453 | "%s", roff_name[n->tok]); | |||
| 1454 | else if (n->tok == MDOC_D1) | |||
| 1455 | post_hyph(mdoc); | |||
| 1456 | break; | |||
| 1457 | case ROFFT_BLOCK: | |||
| 1458 | if (n->tok == MDOC_Bd) { | |||
| 1459 | if (n->args == NULL((void *)0)) { | |||
| 1460 | mandoc_msg(MANDOCERR_BD_NOARG, | |||
| 1461 | n->line, n->pos, "Bd"); | |||
| 1462 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1463 | while (n->body->child != NULL((void *)0)) | |||
| 1464 | roff_node_relink(mdoc, | |||
| 1465 | n->body->child); | |||
| 1466 | roff_node_delete(mdoc, n); | |||
| 1467 | break; | |||
| 1468 | } | |||
| 1469 | post_bd(mdoc); | |||
| 1470 | post_prevpar(mdoc); | |||
| 1471 | } | |||
| 1472 | for (np = n->parent; np != NULL((void *)0); np = np->parent) { | |||
| 1473 | if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { | |||
| 1474 | mandoc_msg(MANDOCERR_BD_NEST, n->line, | |||
| 1475 | n->pos, "%s in Bd", roff_name[n->tok]); | |||
| 1476 | break; | |||
| 1477 | } | |||
| 1478 | } | |||
| 1479 | break; | |||
| 1480 | default: | |||
| 1481 | break; | |||
| 1482 | } | |||
| 1483 | } | |||
| 1484 | ||||
| 1485 | static void | |||
| 1486 | post_defaults(POST_ARGSstruct roff_man *mdoc) | |||
| 1487 | { | |||
| 1488 | struct roff_node *n; | |||
| 1489 | ||||
| 1490 | n = mdoc->last; | |||
| 1491 | if (n->child != NULL((void *)0)) { | |||
| 1492 | post_delim_nb(mdoc); | |||
| 1493 | return; | |||
| 1494 | } | |||
| 1495 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1496 | switch (n->tok) { | |||
| 1497 | case MDOC_Ar: | |||
| 1498 | roff_word_alloc(mdoc, n->line, n->pos, "file"); | |||
| 1499 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1500 | roff_word_alloc(mdoc, n->line, n->pos, "..."); | |||
| 1501 | break; | |||
| 1502 | case MDOC_Pa: | |||
| 1503 | case MDOC_Mt: | |||
| 1504 | roff_word_alloc(mdoc, n->line, n->pos, "~"); | |||
| 1505 | break; | |||
| 1506 | default: | |||
| 1507 | abort(); | |||
| 1508 | } | |||
| 1509 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1510 | mdoc->last = n; | |||
| 1511 | } | |||
| 1512 | ||||
| 1513 | static void | |||
| 1514 | post_at(POST_ARGSstruct roff_man *mdoc) | |||
| 1515 | { | |||
| 1516 | struct roff_node *n, *nch; | |||
| 1517 | const char *att; | |||
| 1518 | ||||
| 1519 | n = mdoc->last; | |||
| 1520 | nch = n->child; | |||
| 1521 | ||||
| 1522 | /* | |||
| 1523 | * If we have a child, look it up in the standard keys. If a | |||
| 1524 | * key exist, use that instead of the child; if it doesn't, | |||
| 1525 | * prefix "AT&T UNIX " to the existing data. | |||
| 1526 | */ | |||
| 1527 | ||||
| 1528 | att = NULL((void *)0); | |||
| 1529 | if (nch != NULL((void *)0) && ((att = mdoc_a2att(nch->string)) == NULL((void *)0))) | |||
| 1530 | mandoc_msg(MANDOCERR_AT_BAD, | |||
| 1531 | nch->line, nch->pos, "At %s", nch->string); | |||
| 1532 | ||||
| 1533 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1534 | if (att != NULL((void *)0)) { | |||
| 1535 | roff_word_alloc(mdoc, nch->line, nch->pos, att); | |||
| 1536 | nch->flags |= NODE_NOPRT(1 << 10); | |||
| 1537 | } else | |||
| 1538 | roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); | |||
| 1539 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1540 | mdoc->last = n; | |||
| 1541 | } | |||
| 1542 | ||||
| 1543 | static void | |||
| 1544 | post_an(POST_ARGSstruct roff_man *mdoc) | |||
| 1545 | { | |||
| 1546 | struct roff_node *np, *nch; | |||
| 1547 | ||||
| 1548 | post_an_norm(mdoc); | |||
| 1549 | ||||
| 1550 | np = mdoc->last; | |||
| 1551 | nch = np->child; | |||
| 1552 | if (np->norm->An.auth == AUTH__NONE) { | |||
| 1553 | if (nch == NULL((void *)0)) | |||
| 1554 | mandoc_msg(MANDOCERR_MACRO_EMPTY, | |||
| 1555 | np->line, np->pos, "An"); | |||
| 1556 | else | |||
| 1557 | post_delim_nb(mdoc); | |||
| 1558 | } else if (nch != NULL((void *)0)) | |||
| 1559 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
| 1560 | nch->line, nch->pos, "An ... %s", nch->string); | |||
| 1561 | } | |||
| 1562 | ||||
| 1563 | static void | |||
| 1564 | post_em(POST_ARGSstruct roff_man *mdoc) | |||
| 1565 | { | |||
| 1566 | post_tag(mdoc); | |||
| 1567 | tag_put(NULL((void *)0), TAG_FALLBACK(2147483647 - 1), mdoc->last); | |||
| 1568 | } | |||
| 1569 | ||||
| 1570 | static void | |||
| 1571 | post_en(POST_ARGSstruct roff_man *mdoc) | |||
| 1572 | { | |||
| 1573 | post_obsolete(mdoc); | |||
| 1574 | if (mdoc->last->type == ROFFT_BLOCK) | |||
| 1575 | mdoc->last->norm->Es = mdoc->last_es; | |||
| 1576 | } | |||
| 1577 | ||||
| 1578 | static void | |||
| 1579 | post_er(POST_ARGSstruct roff_man *mdoc) | |||
| 1580 | { | |||
| 1581 | struct roff_node *n; | |||
| 1582 | ||||
| 1583 | n = mdoc->last; | |||
| 1584 | if (n->sec == SEC_ERRORS && | |||
| 1585 | (n->parent->tok == MDOC_It || | |||
| 1586 | (n->parent->tok == MDOC_Bq && | |||
| 1587 | n->parent->parent->parent->tok == MDOC_It))) | |||
| 1588 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
| 1589 | post_delim_nb(mdoc); | |||
| 1590 | } | |||
| 1591 | ||||
| 1592 | static void | |||
| 1593 | post_tag(POST_ARGSstruct roff_man *mdoc) | |||
| 1594 | { | |||
| 1595 | struct roff_node *n; | |||
| 1596 | ||||
| 1597 | n = mdoc->last; | |||
| 1598 | if ((n->prev == NULL((void *)0) || | |||
| 1599 | (n->prev->type == ROFFT_TEXT && | |||
| 1600 | strcmp(n->prev->string, "|") == 0)) && | |||
| 1601 | (n->parent->tok == MDOC_It || | |||
| 1602 | (n->parent->tok == MDOC_Xo && | |||
| 1603 | n->parent->parent->prev == NULL((void *)0) && | |||
| 1604 | n->parent->parent->parent->tok == MDOC_It))) | |||
| 1605 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
| 1606 | post_delim_nb(mdoc); | |||
| 1607 | } | |||
| 1608 | ||||
| 1609 | static void | |||
| 1610 | post_es(POST_ARGSstruct roff_man *mdoc) | |||
| 1611 | { | |||
| 1612 | post_obsolete(mdoc); | |||
| 1613 | mdoc->last_es = mdoc->last; | |||
| 1614 | } | |||
| 1615 | ||||
| 1616 | static void | |||
| 1617 | post_fl(POST_ARGSstruct roff_man *mdoc) | |||
| 1618 | { | |||
| 1619 | struct roff_node *n; | |||
| 1620 | char *cp; | |||
| 1621 | ||||
| 1622 | /* | |||
| 1623 | * Transform ".Fl Fl long" to ".Fl \-long", | |||
| 1624 | * resulting for example in better HTML output. | |||
| 1625 | */ | |||
| 1626 | ||||
| 1627 | n = mdoc->last; | |||
| 1628 | if (n->prev != NULL((void *)0) && n->prev->tok == MDOC_Fl && | |||
| 1629 | n->prev->child == NULL((void *)0) && n->child != NULL((void *)0) && | |||
| 1630 | (n->flags & NODE_LINE(1 << 3)) == 0) { | |||
| 1631 | mandoc_asprintf(&cp, "\\-%s", n->child->string); | |||
| 1632 | free(n->child->string); | |||
| 1633 | n->child->string = cp; | |||
| 1634 | roff_node_delete(mdoc, n->prev); | |||
| 1635 | } | |||
| 1636 | post_tag(mdoc); | |||
| 1637 | } | |||
| 1638 | ||||
| 1639 | static void | |||
| 1640 | post_xx(POST_ARGSstruct roff_man *mdoc) | |||
| 1641 | { | |||
| 1642 | struct roff_node *n; | |||
| 1643 | const char *os; | |||
| 1644 | char *v; | |||
| 1645 | ||||
| 1646 | post_delim_nb(mdoc); | |||
| 1647 | ||||
| 1648 | n = mdoc->last; | |||
| 1649 | switch (n->tok) { | |||
| 1650 | case MDOC_Bsx: | |||
| 1651 | os = "BSD/OS"; | |||
| 1652 | break; | |||
| 1653 | case MDOC_Dx: | |||
| 1654 | os = "DragonFly"; | |||
| 1655 | break; | |||
| 1656 | case MDOC_Fx: | |||
| 1657 | os = "FreeBSD"; | |||
| 1658 | break; | |||
| 1659 | case MDOC_Nx: | |||
| 1660 | os = "NetBSD"; | |||
| 1661 | if (n->child == NULL((void *)0)) | |||
| 1662 | break; | |||
| 1663 | v = n->child->string; | |||
| 1664 | if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || | |||
| 1665 | v[2] < '0' || v[2] > '9' || | |||
| 1666 | v[3] < 'a' || v[3] > 'z' || v[4] != '\0') | |||
| 1667 | break; | |||
| 1668 | n->child->flags |= NODE_NOPRT(1 << 10); | |||
| 1669 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1670 | roff_word_alloc(mdoc, n->child->line, n->child->pos, v); | |||
| 1671 | v = mdoc->last->string; | |||
| 1672 | v[3] = toupper((unsigned char)v[3]); | |||
| 1673 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1674 | mdoc->last = n; | |||
| 1675 | break; | |||
| 1676 | case MDOC_Ox: | |||
| 1677 | os = "OpenBSD"; | |||
| 1678 | break; | |||
| 1679 | case MDOC_Ux: | |||
| 1680 | os = "UNIX"; | |||
| 1681 | break; | |||
| 1682 | default: | |||
| 1683 | abort(); | |||
| 1684 | } | |||
| 1685 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1686 | roff_word_alloc(mdoc, n->line, n->pos, os); | |||
| 1687 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 1688 | mdoc->last = n; | |||
| 1689 | } | |||
| 1690 | ||||
| 1691 | static void | |||
| 1692 | post_it(POST_ARGSstruct roff_man *mdoc) | |||
| 1693 | { | |||
| 1694 | struct roff_node *nbl, *nit, *nch; | |||
| 1695 | int i, cols; | |||
| 1696 | enum mdoc_list lt; | |||
| 1697 | ||||
| 1698 | post_prevpar(mdoc); | |||
| 1699 | ||||
| 1700 | nit = mdoc->last; | |||
| 1701 | if (nit->type != ROFFT_BLOCK) | |||
| 1702 | return; | |||
| 1703 | ||||
| 1704 | nbl = nit->parent->parent; | |||
| 1705 | lt = nbl->norm->Bl.type; | |||
| 1706 | ||||
| 1707 | switch (lt) { | |||
| 1708 | case LIST_tag: | |||
| 1709 | case LIST_hang: | |||
| 1710 | case LIST_ohang: | |||
| 1711 | case LIST_inset: | |||
| 1712 | case LIST_diag: | |||
| 1713 | if (nit->head->child == NULL((void *)0)) | |||
| 1714 | mandoc_msg(MANDOCERR_IT_NOHEAD, | |||
| 1715 | nit->line, nit->pos, "Bl -%s It", | |||
| 1716 | mdoc_argnames[nbl->args->argv[0].arg]); | |||
| 1717 | break; | |||
| 1718 | case LIST_bullet: | |||
| 1719 | case LIST_dash: | |||
| 1720 | case LIST_enum: | |||
| 1721 | case LIST_hyphen: | |||
| 1722 | if (nit->body == NULL((void *)0) || nit->body->child == NULL((void *)0)) | |||
| 1723 | mandoc_msg(MANDOCERR_IT_NOBODY, | |||
| 1724 | nit->line, nit->pos, "Bl -%s It", | |||
| 1725 | mdoc_argnames[nbl->args->argv[0].arg]); | |||
| 1726 | /* FALLTHROUGH */ | |||
| 1727 | case LIST_item: | |||
| 1728 | if ((nch = nit->head->child) != NULL((void *)0)) | |||
| 1729 | mandoc_msg(MANDOCERR_ARG_SKIP, | |||
| 1730 | nit->line, nit->pos, "It %s", | |||
| 1731 | nch->type == ROFFT_TEXT ? nch->string : | |||
| 1732 | roff_name[nch->tok]); | |||
| 1733 | break; | |||
| 1734 | case LIST_column: | |||
| 1735 | cols = (int)nbl->norm->Bl.ncols; | |||
| 1736 | ||||
| 1737 | assert(nit->head->child == NULL)((nit->head->child == ((void *)0)) ? (void)0 : __assert2 ("/usr/src/usr.bin/mandoc/mdoc_validate.c", 1737, __func__, "nit->head->child == NULL" )); | |||
| 1738 | ||||
| 1739 | if (nit->head->next->child == NULL((void *)0) && | |||
| 1740 | nit->head->next->next == NULL((void *)0)) { | |||
| 1741 | mandoc_msg(MANDOCERR_MACRO_EMPTY, | |||
| 1742 | nit->line, nit->pos, "It"); | |||
| 1743 | roff_node_delete(mdoc, nit); | |||
| 1744 | break; | |||
| 1745 | } | |||
| 1746 | ||||
| 1747 | i = 0; | |||
| 1748 | for (nch = nit->child; nch != NULL((void *)0); nch = nch->next) { | |||
| 1749 | if (nch->type != ROFFT_BODY) | |||
| 1750 | continue; | |||
| 1751 | if (i++ && nch->flags & NODE_LINE(1 << 3)) | |||
| 1752 | mandoc_msg(MANDOCERR_TA_LINE, | |||
| 1753 | nch->line, nch->pos, "Ta"); | |||
| 1754 | } | |||
| 1755 | if (i < cols || i > cols + 1) | |||
| 1756 | mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, | |||
| 1757 | "%d columns, %d cells", cols, i); | |||
| 1758 | else if (nit->head->next->child != NULL((void *)0) && | |||
| 1759 | nit->head->next->child->flags & NODE_LINE(1 << 3)) | |||
| 1760 | mandoc_msg(MANDOCERR_IT_NOARG, | |||
| 1761 | nit->line, nit->pos, "Bl -column It"); | |||
| 1762 | break; | |||
| 1763 | default: | |||
| 1764 | abort(); | |||
| 1765 | } | |||
| 1766 | } | |||
| 1767 | ||||
| 1768 | static void | |||
| 1769 | post_bl_block(POST_ARGSstruct roff_man *mdoc) | |||
| 1770 | { | |||
| 1771 | struct roff_node *n, *ni, *nc; | |||
| 1772 | ||||
| 1773 | post_prevpar(mdoc); | |||
| 1774 | ||||
| 1775 | n = mdoc->last; | |||
| 1776 | for (ni = n->body->child; ni != NULL((void *)0); ni = ni->next) { | |||
| 1777 | if (ni->body == NULL((void *)0)) | |||
| 1778 | continue; | |||
| 1779 | nc = ni->body->last; | |||
| 1780 | while (nc != NULL((void *)0)) { | |||
| 1781 | switch (nc->tok) { | |||
| 1782 | case MDOC_Pp: | |||
| 1783 | case ROFF_br: | |||
| 1784 | break; | |||
| 1785 | default: | |||
| 1786 | nc = NULL((void *)0); | |||
| 1787 | continue; | |||
| 1788 | } | |||
| 1789 | if (ni->next == NULL((void *)0)) { | |||
| 1790 | mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, | |||
| 1791 | nc->pos, "%s", roff_name[nc->tok]); | |||
| 1792 | roff_node_relink(mdoc, nc); | |||
| 1793 | } else if (n->norm->Bl.comp == 0 && | |||
| 1794 | n->norm->Bl.type != LIST_column) { | |||
| 1795 | mandoc_msg(MANDOCERR_PAR_SKIP, | |||
| 1796 | nc->line, nc->pos, | |||
| 1797 | "%s before It", roff_name[nc->tok]); | |||
| 1798 | roff_node_delete(mdoc, nc); | |||
| 1799 | } else | |||
| 1800 | break; | |||
| 1801 | nc = ni->body->last; | |||
| 1802 | } | |||
| 1803 | } | |||
| 1804 | } | |||
| 1805 | ||||
| 1806 | /* | |||
| 1807 | * If the argument of -offset or -width is a macro, | |||
| 1808 | * replace it with the associated default width. | |||
| 1809 | */ | |||
| 1810 | static void | |||
| 1811 | rewrite_macro2len(struct roff_man *mdoc, char **arg) | |||
| 1812 | { | |||
| 1813 | size_t width; | |||
| 1814 | enum roff_tok tok; | |||
| 1815 | ||||
| 1816 | if (*arg == NULL((void *)0)) | |||
| 1817 | return; | |||
| 1818 | else if ( ! strcmp(*arg, "Ds")) | |||
| 1819 | width = 6; | |||
| 1820 | else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) | |||
| 1821 | return; | |||
| 1822 | else | |||
| 1823 | width = macro2len(tok); | |||
| 1824 | ||||
| 1825 | free(*arg); | |||
| 1826 | mandoc_asprintf(arg, "%zun", width); | |||
| 1827 | } | |||
| 1828 | ||||
| 1829 | static void | |||
| 1830 | post_bl_head(POST_ARGSstruct roff_man *mdoc) | |||
| 1831 | { | |||
| 1832 | struct roff_node *nbl, *nh, *nch, *nnext; | |||
| 1833 | struct mdoc_argv *argv; | |||
| 1834 | int i, j; | |||
| 1835 | ||||
| 1836 | post_bl_norm(mdoc); | |||
| 1837 | ||||
| 1838 | nh = mdoc->last; | |||
| 1839 | if (nh->norm->Bl.type != LIST_column) { | |||
| 1840 | if ((nch = nh->child) == NULL((void *)0)) | |||
| 1841 | return; | |||
| 1842 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
| 1843 | nch->line, nch->pos, "Bl ... %s", nch->string); | |||
| 1844 | while (nch != NULL((void *)0)) { | |||
| 1845 | roff_node_delete(mdoc, nch); | |||
| 1846 | nch = nh->child; | |||
| 1847 | } | |||
| 1848 | return; | |||
| 1849 | } | |||
| 1850 | ||||
| 1851 | /* | |||
| 1852 | * Append old-style lists, where the column width specifiers | |||
| 1853 | * trail as macro parameters, to the new-style ("normal-form") | |||
| 1854 | * lists where they're argument values following -column. | |||
| 1855 | */ | |||
| 1856 | ||||
| 1857 | if (nh->child == NULL((void *)0)) | |||
| 1858 | return; | |||
| 1859 | ||||
| 1860 | nbl = nh->parent; | |||
| 1861 | for (j = 0; j < (int)nbl->args->argc; j++) | |||
| 1862 | if (nbl->args->argv[j].arg == MDOC_Column) | |||
| 1863 | break; | |||
| 1864 | ||||
| 1865 | assert(j < (int)nbl->args->argc)((j < (int)nbl->args->argc) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 1865, __func__, "j < (int)nbl->args->argc")); | |||
| 1866 | ||||
| 1867 | /* | |||
| 1868 | * Accommodate for new-style groff column syntax. Shuffle the | |||
| 1869 | * child nodes, all of which must be TEXT, as arguments for the | |||
| 1870 | * column field. Then, delete the head children. | |||
| 1871 | */ | |||
| 1872 | ||||
| 1873 | argv = nbl->args->argv + j; | |||
| 1874 | i = argv->sz; | |||
| 1875 | for (nch = nh->child; nch != NULL((void *)0); nch = nch->next) | |||
| 1876 | argv->sz++; | |||
| 1877 | argv->value = mandoc_reallocarray(argv->value, | |||
| 1878 | argv->sz, sizeof(char *)); | |||
| 1879 | ||||
| 1880 | nh->norm->Bl.ncols = argv->sz; | |||
| 1881 | nh->norm->Bl.cols = (void *)argv->value; | |||
| 1882 | ||||
| 1883 | for (nch = nh->child; nch != NULL((void *)0); nch = nnext) { | |||
| 1884 | argv->value[i++] = nch->string; | |||
| 1885 | nch->string = NULL((void *)0); | |||
| 1886 | nnext = nch->next; | |||
| 1887 | roff_node_delete(NULL((void *)0), nch); | |||
| 1888 | } | |||
| 1889 | nh->child = NULL((void *)0); | |||
| 1890 | } | |||
| 1891 | ||||
| 1892 | static void | |||
| 1893 | post_bl(POST_ARGSstruct roff_man *mdoc) | |||
| 1894 | { | |||
| 1895 | struct roff_node *nbody; /* of the Bl */ | |||
| 1896 | struct roff_node *nchild, *nnext; /* of the Bl body */ | |||
| 1897 | const char *prev_Er; | |||
| 1898 | int order; | |||
| 1899 | ||||
| 1900 | nbody = mdoc->last; | |||
| 1901 | switch (nbody->type) { | |||
| 1902 | case ROFFT_BLOCK: | |||
| 1903 | post_bl_block(mdoc); | |||
| 1904 | return; | |||
| 1905 | case ROFFT_HEAD: | |||
| 1906 | post_bl_head(mdoc); | |||
| 1907 | return; | |||
| 1908 | case ROFFT_BODY: | |||
| 1909 | break; | |||
| 1910 | default: | |||
| 1911 | return; | |||
| 1912 | } | |||
| 1913 | if (nbody->end != ENDBODY_NOT) | |||
| 1914 | return; | |||
| 1915 | ||||
| 1916 | /* | |||
| 1917 | * Up to the first item, move nodes before the list, | |||
| 1918 | * but leave transparent nodes where they are | |||
| 1919 | * if they precede an item. | |||
| 1920 | * The next non-transparent node is kept in nchild. | |||
| 1921 | * It only needs to be updated after a non-transparent | |||
| 1922 | * node was moved out, and at the very beginning | |||
| 1923 | * when no node at all was moved yet. | |||
| 1924 | */ | |||
| 1925 | ||||
| 1926 | nchild = mdoc->last; | |||
| 1927 | for (;;) { | |||
| 1928 | if (nchild == mdoc->last) | |||
| 1929 | nchild = roff_node_child(nbody); | |||
| 1930 | if (nchild == NULL((void *)0)) { | |||
| 1931 | mdoc->last = nbody; | |||
| 1932 | mandoc_msg(MANDOCERR_BLK_EMPTY, | |||
| 1933 | nbody->line, nbody->pos, "Bl"); | |||
| 1934 | return; | |||
| 1935 | } | |||
| 1936 | if (nchild->tok == MDOC_It) { | |||
| 1937 | mdoc->last = nbody; | |||
| 1938 | break; | |||
| 1939 | } | |||
| 1940 | mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, | |||
| 1941 | nbody->child->pos, "%s", roff_name[nbody->child->tok]); | |||
| 1942 | if (nbody->parent->prev == NULL((void *)0)) { | |||
| 1943 | mdoc->last = nbody->parent->parent; | |||
| 1944 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 1945 | } else { | |||
| 1946 | mdoc->last = nbody->parent->prev; | |||
| 1947 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1948 | } | |||
| 1949 | roff_node_relink(mdoc, nbody->child); | |||
| 1950 | } | |||
| 1951 | ||||
| 1952 | /* | |||
| 1953 | * We have reached the first item, | |||
| 1954 | * so moving nodes out is no longer possible. | |||
| 1955 | * But in .Bl -column, the first rows may be implicit, | |||
| 1956 | * that is, they may not start with .It macros. | |||
| 1957 | * Such rows may be followed by nodes generated on the | |||
| 1958 | * roff level, for example .TS. | |||
| 1959 | * Wrap such roff nodes into an implicit row. | |||
| 1960 | */ | |||
| 1961 | ||||
| 1962 | while (nchild != NULL((void *)0)) { | |||
| 1963 | if (nchild->tok == MDOC_It) { | |||
| 1964 | nchild = roff_node_next(nchild); | |||
| 1965 | continue; | |||
| 1966 | } | |||
| 1967 | nnext = nchild->next; | |||
| 1968 | mdoc->last = nchild->prev; | |||
| 1969 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1970 | roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
| 1971 | roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
| 1972 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1973 | roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); | |||
| 1974 | while (nchild->tok != MDOC_It) { | |||
| 1975 | roff_node_relink(mdoc, nchild); | |||
| 1976 | if (nnext == NULL((void *)0)) | |||
| 1977 | break; | |||
| 1978 | nchild = nnext; | |||
| 1979 | nnext = nchild->next; | |||
| 1980 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 1981 | } | |||
| 1982 | mdoc->last = nbody; | |||
| 1983 | } | |||
| 1984 | ||||
| 1985 | if (mdoc->meta.os_e != MANDOC_OS_NETBSD) | |||
| 1986 | return; | |||
| 1987 | ||||
| 1988 | prev_Er = NULL((void *)0); | |||
| 1989 | for (nchild = nbody->child; nchild != NULL((void *)0); nchild = nchild->next) { | |||
| 1990 | if (nchild->tok != MDOC_It) | |||
| 1991 | continue; | |||
| 1992 | if ((nnext = nchild->head->child) == NULL((void *)0)) | |||
| 1993 | continue; | |||
| 1994 | if (nnext->type == ROFFT_BLOCK) | |||
| 1995 | nnext = nnext->body->child; | |||
| 1996 | if (nnext == NULL((void *)0) || nnext->tok != MDOC_Er) | |||
| 1997 | continue; | |||
| 1998 | nnext = nnext->child; | |||
| 1999 | if (prev_Er != NULL((void *)0)) { | |||
| 2000 | order = strcmp(prev_Er, nnext->string); | |||
| 2001 | if (order > 0) | |||
| 2002 | mandoc_msg(MANDOCERR_ER_ORDER, | |||
| 2003 | nnext->line, nnext->pos, | |||
| 2004 | "Er %s %s (NetBSD)", | |||
| 2005 | prev_Er, nnext->string); | |||
| 2006 | else if (order == 0) | |||
| 2007 | mandoc_msg(MANDOCERR_ER_REP, | |||
| 2008 | nnext->line, nnext->pos, | |||
| 2009 | "Er %s (NetBSD)", prev_Er); | |||
| 2010 | } | |||
| 2011 | prev_Er = nnext->string; | |||
| 2012 | } | |||
| 2013 | } | |||
| 2014 | ||||
| 2015 | static void | |||
| 2016 | post_bk(POST_ARGSstruct roff_man *mdoc) | |||
| 2017 | { | |||
| 2018 | struct roff_node *n; | |||
| 2019 | ||||
| 2020 | n = mdoc->last; | |||
| 2021 | ||||
| 2022 | if (n->type == ROFFT_BLOCK && n->body->child == NULL((void *)0)) { | |||
| 2023 | mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); | |||
| 2024 | roff_node_delete(mdoc, n); | |||
| 2025 | } | |||
| 2026 | } | |||
| 2027 | ||||
| 2028 | static void | |||
| 2029 | post_sm(POST_ARGSstruct roff_man *mdoc) | |||
| 2030 | { | |||
| 2031 | struct roff_node *nch; | |||
| 2032 | ||||
| 2033 | nch = mdoc->last->child; | |||
| 2034 | ||||
| 2035 | if (nch == NULL((void *)0)) { | |||
| 2036 | mdoc->flags ^= MDOC_SMOFF(1 << 9); | |||
| 2037 | return; | |||
| 2038 | } | |||
| 2039 | ||||
| 2040 | assert(nch->type == ROFFT_TEXT)((nch->type == ROFFT_TEXT) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 2040, __func__, "nch->type == ROFFT_TEXT")); | |||
| 2041 | ||||
| 2042 | if ( ! strcmp(nch->string, "on")) { | |||
| 2043 | mdoc->flags &= ~MDOC_SMOFF(1 << 9); | |||
| 2044 | return; | |||
| 2045 | } | |||
| 2046 | if ( ! strcmp(nch->string, "off")) { | |||
| 2047 | mdoc->flags |= MDOC_SMOFF(1 << 9); | |||
| 2048 | return; | |||
| 2049 | } | |||
| 2050 | ||||
| 2051 | mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, | |||
| 2052 | "%s %s", roff_name[mdoc->last->tok], nch->string); | |||
| 2053 | roff_node_relink(mdoc, nch); | |||
| 2054 | return; | |||
| 2055 | } | |||
| 2056 | ||||
| 2057 | static void | |||
| 2058 | post_root(POST_ARGSstruct roff_man *mdoc) | |||
| 2059 | { | |||
| 2060 | struct roff_node *n; | |||
| 2061 | ||||
| 2062 | /* Add missing prologue data. */ | |||
| 2063 | ||||
| 2064 | if (mdoc->meta.date == NULL((void *)0)) | |||
| 2065 | mdoc->meta.date = mandoc_normdate(NULL((void *)0), NULL((void *)0)); | |||
| 2066 | ||||
| 2067 | if (mdoc->meta.title == NULL((void *)0)) { | |||
| 2068 | mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); | |||
| 2069 | mdoc->meta.title = mandoc_strdup("UNTITLED"); | |||
| 2070 | } | |||
| 2071 | ||||
| 2072 | if (mdoc->meta.vol == NULL((void *)0)) | |||
| 2073 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |||
| 2074 | ||||
| 2075 | if (mdoc->meta.os == NULL((void *)0)) { | |||
| 2076 | mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL((void *)0)); | |||
| 2077 | mdoc->meta.os = mandoc_strdup(""); | |||
| 2078 | } else if (mdoc->meta.os_e && | |||
| 2079 | (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) | |||
| 2080 | mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, | |||
| 2081 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
| 2082 | "(OpenBSD)" : "(NetBSD)"); | |||
| 2083 | ||||
| 2084 | if (mdoc->meta.arch != NULL((void *)0) && | |||
| 2085 | arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { | |||
| 2086 | n = mdoc->meta.first->child; | |||
| 2087 | while (n->tok != MDOC_Dt || | |||
| 2088 | n->child == NULL((void *)0) || | |||
| 2089 | n->child->next == NULL((void *)0) || | |||
| 2090 | n->child->next->next == NULL((void *)0)) | |||
| 2091 | n = n->next; | |||
| 2092 | n = n->child->next->next; | |||
| 2093 | mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, | |||
| 2094 | "Dt ... %s %s", mdoc->meta.arch, | |||
| 2095 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
| 2096 | "(OpenBSD)" : "(NetBSD)"); | |||
| 2097 | } | |||
| 2098 | ||||
| 2099 | /* Check that we begin with a proper `Sh'. */ | |||
| 2100 | ||||
| 2101 | n = mdoc->meta.first->child; | |||
| 2102 | while (n != NULL((void *)0) && | |||
| 2103 | (n->type == ROFFT_COMMENT || | |||
| 2104 | (n->tok >= MDOC_Dd && | |||
| 2105 | mdoc_macro(n->tok)->flags & MDOC_PROLOGUE(1 << 3)))) | |||
| 2106 | n = n->next; | |||
| 2107 | ||||
| 2108 | if (n == NULL((void *)0)) | |||
| 2109 | mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL((void *)0)); | |||
| 2110 | else if (n->tok != MDOC_Sh) | |||
| 2111 | mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, | |||
| 2112 | "%s", roff_name[n->tok]); | |||
| 2113 | } | |||
| 2114 | ||||
| 2115 | static void | |||
| 2116 | post_rs(POST_ARGSstruct roff_man *mdoc) | |||
| 2117 | { | |||
| 2118 | struct roff_node *np, *nch, *next, *prev; | |||
| 2119 | int i, j; | |||
| 2120 | ||||
| 2121 | np = mdoc->last; | |||
| 2122 | ||||
| 2123 | if (np->type != ROFFT_BODY) | |||
| 2124 | return; | |||
| 2125 | ||||
| 2126 | if (np->child == NULL((void *)0)) { | |||
| 2127 | mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); | |||
| 2128 | return; | |||
| 2129 | } | |||
| 2130 | ||||
| 2131 | /* | |||
| 2132 | * The full `Rs' block needs special handling to order the | |||
| 2133 | * sub-elements according to `rsord'. Pick through each element | |||
| 2134 | * and correctly order it. This is an insertion sort. | |||
| 2135 | */ | |||
| 2136 | ||||
| 2137 | next = NULL((void *)0); | |||
| 2138 | for (nch = np->child->next; nch != NULL((void *)0); nch = next) { | |||
| 2139 | /* Determine order number of this child. */ | |||
| 2140 | for (i = 0; i < RSORD_MAX14; i++) | |||
| 2141 | if (rsord[i] == nch->tok) | |||
| 2142 | break; | |||
| 2143 | ||||
| 2144 | if (i == RSORD_MAX14) { | |||
| 2145 | mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, | |||
| 2146 | "%s", roff_name[nch->tok]); | |||
| 2147 | i = -1; | |||
| 2148 | } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) | |||
| 2149 | np->norm->Rs.quote_T++; | |||
| 2150 | ||||
| 2151 | /* | |||
| 2152 | * Remove this child from the chain. This somewhat | |||
| 2153 | * repeats roff_node_unlink(), but since we're | |||
| 2154 | * just re-ordering, there's no need for the | |||
| 2155 | * full unlink process. | |||
| 2156 | */ | |||
| 2157 | ||||
| 2158 | if ((next = nch->next) != NULL((void *)0)) | |||
| 2159 | next->prev = nch->prev; | |||
| 2160 | ||||
| 2161 | if ((prev = nch->prev) != NULL((void *)0)) | |||
| 2162 | prev->next = nch->next; | |||
| 2163 | ||||
| 2164 | nch->prev = nch->next = NULL((void *)0); | |||
| 2165 | ||||
| 2166 | /* | |||
| 2167 | * Scan back until we reach a node that's | |||
| 2168 | * to be ordered before this child. | |||
| 2169 | */ | |||
| 2170 | ||||
| 2171 | for ( ; prev ; prev = prev->prev) { | |||
| 2172 | /* Determine order of `prev'. */ | |||
| 2173 | for (j = 0; j < RSORD_MAX14; j++) | |||
| 2174 | if (rsord[j] == prev->tok) | |||
| 2175 | break; | |||
| 2176 | if (j == RSORD_MAX14) | |||
| 2177 | j = -1; | |||
| 2178 | ||||
| 2179 | if (j <= i) | |||
| 2180 | break; | |||
| 2181 | } | |||
| 2182 | ||||
| 2183 | /* | |||
| 2184 | * Set this child back into its correct place | |||
| 2185 | * in front of the `prev' node. | |||
| 2186 | */ | |||
| 2187 | ||||
| 2188 | nch->prev = prev; | |||
| 2189 | ||||
| 2190 | if (prev == NULL((void *)0)) { | |||
| 2191 | np->child->prev = nch; | |||
| 2192 | nch->next = np->child; | |||
| 2193 | np->child = nch; | |||
| 2194 | } else { | |||
| 2195 | if (prev->next) | |||
| 2196 | prev->next->prev = nch; | |||
| 2197 | nch->next = prev->next; | |||
| 2198 | prev->next = nch; | |||
| 2199 | } | |||
| 2200 | } | |||
| 2201 | } | |||
| 2202 | ||||
| 2203 | /* | |||
| 2204 | * For some arguments of some macros, | |||
| 2205 | * convert all breakable hyphens into ASCII_HYPH. | |||
| 2206 | */ | |||
| 2207 | static void | |||
| 2208 | post_hyph(POST_ARGSstruct roff_man *mdoc) | |||
| 2209 | { | |||
| 2210 | struct roff_node *n, *nch; | |||
| 2211 | char *cp; | |||
| 2212 | ||||
| 2213 | n = mdoc->last; | |||
| 2214 | for (nch = n->child; nch != NULL((void *)0); nch = nch->next) { | |||
| 2215 | if (nch->type != ROFFT_TEXT) | |||
| 2216 | continue; | |||
| 2217 | cp = nch->string; | |||
| 2218 | if (*cp == '\0') | |||
| 2219 | continue; | |||
| 2220 | while (*(++cp) != '\0') | |||
| 2221 | if (*cp == '-' && | |||
| 2222 | isalpha((unsigned char)cp[-1]) && | |||
| 2223 | isalpha((unsigned char)cp[1])) { | |||
| 2224 | if (n->tag == NULL((void *)0) && n->flags & NODE_ID(1 << 11)) | |||
| 2225 | n->tag = mandoc_strdup(nch->string); | |||
| 2226 | *cp = ASCII_HYPH30; | |||
| 2227 | } | |||
| 2228 | } | |||
| 2229 | } | |||
| 2230 | ||||
| 2231 | static void | |||
| 2232 | post_ns(POST_ARGSstruct roff_man *mdoc) | |||
| 2233 | { | |||
| 2234 | struct roff_node *n; | |||
| 2235 | ||||
| 2236 | n = mdoc->last; | |||
| 2237 | if (n->flags & NODE_LINE(1 << 3) || | |||
| 2238 | (n->next != NULL((void *)0) && n->next->flags & NODE_DELIMC(1 << 5))) | |||
| 2239 | mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL((void *)0)); | |||
| 2240 | } | |||
| 2241 | ||||
| 2242 | static void | |||
| 2243 | post_sx(POST_ARGSstruct roff_man *mdoc) | |||
| 2244 | { | |||
| 2245 | post_delim(mdoc); | |||
| 2246 | post_hyph(mdoc); | |||
| 2247 | } | |||
| 2248 | ||||
| 2249 | static void | |||
| 2250 | post_sh(POST_ARGSstruct roff_man *mdoc) | |||
| 2251 | { | |||
| 2252 | post_section(mdoc); | |||
| 2253 | ||||
| 2254 | switch (mdoc->last->type) { | |||
| 2255 | case ROFFT_HEAD: | |||
| 2256 | post_sh_head(mdoc); | |||
| 2257 | break; | |||
| 2258 | case ROFFT_BODY: | |||
| 2259 | switch (mdoc->lastsec) { | |||
| 2260 | case SEC_NAME: | |||
| 2261 | post_sh_name(mdoc); | |||
| 2262 | break; | |||
| 2263 | case SEC_SEE_ALSO: | |||
| 2264 | post_sh_see_also(mdoc); | |||
| 2265 | break; | |||
| 2266 | case SEC_AUTHORS: | |||
| 2267 | post_sh_authors(mdoc); | |||
| 2268 | break; | |||
| 2269 | default: | |||
| 2270 | break; | |||
| 2271 | } | |||
| 2272 | break; | |||
| 2273 | default: | |||
| 2274 | break; | |||
| 2275 | } | |||
| 2276 | } | |||
| 2277 | ||||
| 2278 | static void | |||
| 2279 | post_sh_name(POST_ARGSstruct roff_man *mdoc) | |||
| 2280 | { | |||
| 2281 | struct roff_node *n; | |||
| 2282 | int hasnm, hasnd; | |||
| 2283 | ||||
| 2284 | hasnm = hasnd = 0; | |||
| 2285 | ||||
| 2286 | for (n = mdoc->last->child; n != NULL((void *)0); n = n->next) { | |||
| 2287 | switch (n->tok) { | |||
| 2288 | case MDOC_Nm: | |||
| 2289 | if (hasnm && n->child != NULL((void *)0)) | |||
| 2290 | mandoc_msg(MANDOCERR_NAMESEC_PUNCT, | |||
| 2291 | n->line, n->pos, | |||
| 2292 | "Nm %s", n->child->string); | |||
| 2293 | hasnm = 1; | |||
| 2294 | continue; | |||
| 2295 | case MDOC_Nd: | |||
| 2296 | hasnd = 1; | |||
| 2297 | if (n->next != NULL((void *)0)) | |||
| 2298 | mandoc_msg(MANDOCERR_NAMESEC_ND, | |||
| 2299 | n->line, n->pos, NULL((void *)0)); | |||
| 2300 | break; | |||
| 2301 | case TOKEN_NONE: | |||
| 2302 | if (n->type == ROFFT_TEXT && | |||
| 2303 | n->string[0] == ',' && n->string[1] == '\0' && | |||
| 2304 | n->next != NULL((void *)0) && n->next->tok == MDOC_Nm) { | |||
| 2305 | n = n->next; | |||
| 2306 | continue; | |||
| 2307 | } | |||
| 2308 | /* FALLTHROUGH */ | |||
| 2309 | default: | |||
| 2310 | mandoc_msg(MANDOCERR_NAMESEC_BAD, | |||
| 2311 | n->line, n->pos, "%s", roff_name[n->tok]); | |||
| 2312 | continue; | |||
| 2313 | } | |||
| 2314 | break; | |||
| 2315 | } | |||
| 2316 | ||||
| 2317 | if ( ! hasnm) | |||
| 2318 | mandoc_msg(MANDOCERR_NAMESEC_NONM, | |||
| 2319 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
| 2320 | if ( ! hasnd) | |||
| 2321 | mandoc_msg(MANDOCERR_NAMESEC_NOND, | |||
| 2322 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
| 2323 | } | |||
| 2324 | ||||
| 2325 | static void | |||
| 2326 | post_sh_see_also(POST_ARGSstruct roff_man *mdoc) | |||
| 2327 | { | |||
| 2328 | const struct roff_node *n; | |||
| 2329 | const char *name, *sec; | |||
| 2330 | const char *lastname, *lastsec, *lastpunct; | |||
| 2331 | int cmp; | |||
| 2332 | ||||
| 2333 | n = mdoc->last->child; | |||
| 2334 | lastname = lastsec = lastpunct = NULL((void *)0); | |||
| 2335 | while (n != NULL((void *)0)) { | |||
| 2336 | if (n->tok != MDOC_Xr || | |||
| 2337 | n->child == NULL((void *)0) || | |||
| 2338 | n->child->next == NULL((void *)0)) | |||
| 2339 | break; | |||
| 2340 | ||||
| 2341 | /* Process one .Xr node. */ | |||
| 2342 | ||||
| 2343 | name = n->child->string; | |||
| 2344 | sec = n->child->next->string; | |||
| 2345 | if (lastsec != NULL((void *)0)) { | |||
| 2346 | if (lastpunct[0] != ',' || lastpunct[1] != '\0') | |||
| 2347 | mandoc_msg(MANDOCERR_XR_PUNCT, n->line, | |||
| 2348 | n->pos, "%s before %s(%s)", | |||
| 2349 | lastpunct, name, sec); | |||
| 2350 | cmp = strcmp(lastsec, sec); | |||
| 2351 | if (cmp > 0) | |||
| 2352 | mandoc_msg(MANDOCERR_XR_ORDER, n->line, | |||
| 2353 | n->pos, "%s(%s) after %s(%s)", | |||
| 2354 | name, sec, lastname, lastsec); | |||
| 2355 | else if (cmp == 0 && | |||
| 2356 | strcasecmp(lastname, name) > 0) | |||
| 2357 | mandoc_msg(MANDOCERR_XR_ORDER, n->line, | |||
| 2358 | n->pos, "%s after %s", name, lastname); | |||
| 2359 | } | |||
| 2360 | lastname = name; | |||
| 2361 | lastsec = sec; | |||
| 2362 | ||||
| 2363 | /* Process the following node. */ | |||
| 2364 | ||||
| 2365 | n = n->next; | |||
| 2366 | if (n == NULL((void *)0)) | |||
| 2367 | break; | |||
| 2368 | if (n->tok == MDOC_Xr) { | |||
| 2369 | lastpunct = "none"; | |||
| 2370 | continue; | |||
| 2371 | } | |||
| 2372 | if (n->type != ROFFT_TEXT) | |||
| 2373 | break; | |||
| 2374 | for (name = n->string; *name != '\0'; name++) | |||
| 2375 | if (isalpha((const unsigned char)*name)) | |||
| 2376 | return; | |||
| 2377 | lastpunct = n->string; | |||
| 2378 | if (n->next == NULL((void *)0) || n->next->tok == MDOC_Rs) | |||
| 2379 | mandoc_msg(MANDOCERR_XR_PUNCT, n->line, | |||
| 2380 | n->pos, "%s after %s(%s)", | |||
| 2381 | lastpunct, lastname, lastsec); | |||
| 2382 | n = n->next; | |||
| 2383 | } | |||
| 2384 | } | |||
| 2385 | ||||
| 2386 | static int | |||
| 2387 | child_an(const struct roff_node *n) | |||
| 2388 | { | |||
| 2389 | ||||
| 2390 | for (n = n->child; n != NULL((void *)0); n = n->next) | |||
| 2391 | if ((n->tok == MDOC_An && n->child != NULL((void *)0)) || child_an(n)) | |||
| 2392 | return 1; | |||
| 2393 | return 0; | |||
| 2394 | } | |||
| 2395 | ||||
| 2396 | static void | |||
| 2397 | post_sh_authors(POST_ARGSstruct roff_man *mdoc) | |||
| 2398 | { | |||
| 2399 | ||||
| 2400 | if ( ! child_an(mdoc->last)) | |||
| 2401 | mandoc_msg(MANDOCERR_AN_MISSING, | |||
| 2402 | mdoc->last->line, mdoc->last->pos, NULL((void *)0)); | |||
| 2403 | } | |||
| 2404 | ||||
| 2405 | /* | |||
| 2406 | * Return an upper bound for the string distance (allowing | |||
| 2407 | * transpositions). Not a full Levenshtein implementation | |||
| 2408 | * because Levenshtein is quadratic in the string length | |||
| 2409 | * and this function is called for every standard name, | |||
| 2410 | * so the check for each custom name would be cubic. | |||
| 2411 | * The following crude heuristics is linear, resulting | |||
| 2412 | * in quadratic behaviour for checking one custom name, | |||
| 2413 | * which does not cause measurable slowdown. | |||
| 2414 | */ | |||
| 2415 | static int | |||
| 2416 | similar(const char *s1, const char *s2) | |||
| 2417 | { | |||
| 2418 | const int maxdist = 3; | |||
| 2419 | int dist = 0; | |||
| 2420 | ||||
| 2421 | while (s1[0] != '\0' && s2[0] != '\0') { | |||
| 2422 | if (s1[0] == s2[0]) { | |||
| 2423 | s1++; | |||
| 2424 | s2++; | |||
| 2425 | continue; | |||
| 2426 | } | |||
| 2427 | if (++dist > maxdist) | |||
| 2428 | return INT_MAX2147483647; | |||
| 2429 | if (s1[1] == s2[1]) { /* replacement */ | |||
| 2430 | s1++; | |||
| 2431 | s2++; | |||
| 2432 | } else if (s1[0] == s2[1] && s1[1] == s2[0]) { | |||
| 2433 | s1 += 2; /* transposition */ | |||
| 2434 | s2 += 2; | |||
| 2435 | } else if (s1[0] == s2[1]) /* insertion */ | |||
| 2436 | s2++; | |||
| 2437 | else if (s1[1] == s2[0]) /* deletion */ | |||
| 2438 | s1++; | |||
| 2439 | else | |||
| 2440 | return INT_MAX2147483647; | |||
| 2441 | } | |||
| 2442 | dist += strlen(s1) + strlen(s2); | |||
| 2443 | return dist > maxdist ? INT_MAX2147483647 : dist; | |||
| 2444 | } | |||
| 2445 | ||||
| 2446 | static void | |||
| 2447 | post_sh_head(POST_ARGSstruct roff_man *mdoc) | |||
| 2448 | { | |||
| 2449 | struct roff_node *nch; | |||
| 2450 | const char *goodsec; | |||
| 2451 | const char *const *testsec; | |||
| 2452 | int dist, mindist; | |||
| 2453 | enum roff_sec sec; | |||
| 2454 | ||||
| 2455 | /* | |||
| 2456 | * Process a new section. Sections are either "named" or | |||
| 2457 | * "custom". Custom sections are user-defined, while named ones | |||
| 2458 | * follow a conventional order and may only appear in certain | |||
| 2459 | * manual sections. | |||
| 2460 | */ | |||
| 2461 | ||||
| 2462 | sec = mdoc->last->sec; | |||
| 2463 | ||||
| 2464 | /* The NAME should be first. */ | |||
| 2465 | ||||
| 2466 | if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) | |||
| 2467 | mandoc_msg(MANDOCERR_NAMESEC_FIRST, | |||
| 2468 | mdoc->last->line, mdoc->last->pos, "Sh %s", | |||
| 2469 | sec != SEC_CUSTOM ? secnames[sec] : | |||
| 2470 | (nch = mdoc->last->child) == NULL((void *)0) ? "" : | |||
| 2471 | nch->type == ROFFT_TEXT ? nch->string : | |||
| 2472 | roff_name[nch->tok]); | |||
| 2473 | ||||
| 2474 | /* The SYNOPSIS gets special attention in other areas. */ | |||
| 2475 | ||||
| 2476 | if (sec == SEC_SYNOPSIS) { | |||
| 2477 | roff_setreg(mdoc->roff, "nS", 1, '='); | |||
| 2478 | mdoc->flags |= MDOC_SYNOPSIS(1 << 7); | |||
| 2479 | } else { | |||
| 2480 | roff_setreg(mdoc->roff, "nS", 0, '='); | |||
| 2481 | mdoc->flags &= ~MDOC_SYNOPSIS(1 << 7); | |||
| 2482 | } | |||
| 2483 | if (sec == SEC_DESCRIPTION) | |||
| 2484 | fn_prio = TAG_STRONG2; | |||
| 2485 | ||||
| 2486 | /* Mark our last section. */ | |||
| 2487 | ||||
| 2488 | mdoc->lastsec = sec; | |||
| 2489 | ||||
| 2490 | /* We don't care about custom sections after this. */ | |||
| 2491 | ||||
| 2492 | if (sec == SEC_CUSTOM) { | |||
| 2493 | if ((nch = mdoc->last->child) == NULL((void *)0) || | |||
| 2494 | nch->type != ROFFT_TEXT || nch->next != NULL((void *)0)) | |||
| 2495 | return; | |||
| 2496 | goodsec = NULL((void *)0); | |||
| 2497 | mindist = INT_MAX2147483647; | |||
| 2498 | for (testsec = secnames + 1; *testsec != NULL((void *)0); testsec++) { | |||
| 2499 | dist = similar(nch->string, *testsec); | |||
| 2500 | if (dist < mindist) { | |||
| 2501 | goodsec = *testsec; | |||
| 2502 | mindist = dist; | |||
| 2503 | } | |||
| 2504 | } | |||
| 2505 | if (goodsec != NULL((void *)0)) | |||
| 2506 | mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, | |||
| 2507 | "Sh %s instead of %s", nch->string, goodsec); | |||
| 2508 | return; | |||
| 2509 | } | |||
| 2510 | ||||
| 2511 | /* | |||
| 2512 | * Check whether our non-custom section is being repeated or is | |||
| 2513 | * out of order. | |||
| 2514 | */ | |||
| 2515 | ||||
| 2516 | if (sec == mdoc->lastnamed) | |||
| 2517 | mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, | |||
| 2518 | mdoc->last->pos, "Sh %s", secnames[sec]); | |||
| 2519 | ||||
| 2520 | if (sec < mdoc->lastnamed) | |||
| 2521 | mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, | |||
| 2522 | mdoc->last->pos, "Sh %s", secnames[sec]); | |||
| 2523 | ||||
| 2524 | /* Mark the last named section. */ | |||
| 2525 | ||||
| 2526 | mdoc->lastnamed = sec; | |||
| 2527 | ||||
| 2528 | /* Check particular section/manual conventions. */ | |||
| 2529 | ||||
| 2530 | if (mdoc->meta.msec == NULL((void *)0)) | |||
| 2531 | return; | |||
| 2532 | ||||
| 2533 | goodsec = NULL((void *)0); | |||
| 2534 | switch (sec) { | |||
| 2535 | case SEC_ERRORS: | |||
| 2536 | if (*mdoc->meta.msec == '4') | |||
| 2537 | break; | |||
| 2538 | goodsec = "2, 3, 4, 9"; | |||
| 2539 | /* FALLTHROUGH */ | |||
| 2540 | case SEC_RETURN_VALUES: | |||
| 2541 | case SEC_LIBRARY: | |||
| 2542 | if (*mdoc->meta.msec == '2') | |||
| 2543 | break; | |||
| 2544 | if (*mdoc->meta.msec == '3') | |||
| 2545 | break; | |||
| 2546 | if (NULL((void *)0) == goodsec) | |||
| 2547 | goodsec = "2, 3, 9"; | |||
| 2548 | /* FALLTHROUGH */ | |||
| 2549 | case SEC_CONTEXT: | |||
| 2550 | if (*mdoc->meta.msec == '9') | |||
| 2551 | break; | |||
| 2552 | if (NULL((void *)0) == goodsec) | |||
| 2553 | goodsec = "9"; | |||
| 2554 | mandoc_msg(MANDOCERR_SEC_MSEC, | |||
| 2555 | mdoc->last->line, mdoc->last->pos, | |||
| 2556 | "Sh %s for %s only", secnames[sec], goodsec); | |||
| 2557 | break; | |||
| 2558 | default: | |||
| 2559 | break; | |||
| 2560 | } | |||
| 2561 | } | |||
| 2562 | ||||
| 2563 | static void | |||
| 2564 | post_xr(POST_ARGSstruct roff_man *mdoc) | |||
| 2565 | { | |||
| 2566 | struct roff_node *n, *nch; | |||
| 2567 | ||||
| 2568 | n = mdoc->last; | |||
| 2569 | nch = n->child; | |||
| 2570 | if (nch->next == NULL((void *)0)) { | |||
| 2571 | mandoc_msg(MANDOCERR_XR_NOSEC, | |||
| 2572 | n->line, n->pos, "Xr %s", nch->string); | |||
| 2573 | } else { | |||
| 2574 | assert(nch->next == n->last)((nch->next == n->last) ? (void)0 : __assert2("/usr/src/usr.bin/mandoc/mdoc_validate.c" , 2574, __func__, "nch->next == n->last")); | |||
| 2575 | if(mandoc_xr_add(nch->next->string, nch->string, | |||
| 2576 | nch->line, nch->pos)) | |||
| 2577 | mandoc_msg(MANDOCERR_XR_SELF, | |||
| 2578 | nch->line, nch->pos, "Xr %s %s", | |||
| 2579 | nch->string, nch->next->string); | |||
| 2580 | } | |||
| 2581 | post_delim_nb(mdoc); | |||
| 2582 | } | |||
| 2583 | ||||
| 2584 | static void | |||
| 2585 | post_section(POST_ARGSstruct roff_man *mdoc) | |||
| 2586 | { | |||
| 2587 | struct roff_node *n, *nch; | |||
| 2588 | char *cp, *tag; | |||
| 2589 | ||||
| 2590 | n = mdoc->last; | |||
| 2591 | switch (n->type) { | |||
| 2592 | case ROFFT_BLOCK: | |||
| 2593 | post_prevpar(mdoc); | |||
| 2594 | return; | |||
| 2595 | case ROFFT_HEAD: | |||
| 2596 | tag = NULL((void *)0); | |||
| 2597 | deroff(&tag, n); | |||
| 2598 | if (tag != NULL((void *)0)) { | |||
| 2599 | for (cp = tag; *cp != '\0'; cp++) | |||
| 2600 | if (*cp == ' ') | |||
| 2601 | *cp = '_'; | |||
| 2602 | if ((nch = n->child) != NULL((void *)0) && | |||
| 2603 | nch->type == ROFFT_TEXT && | |||
| 2604 | strcmp(nch->string, tag) == 0) | |||
| 2605 | tag_put(NULL((void *)0), TAG_STRONG2, n); | |||
| 2606 | else | |||
| 2607 | tag_put(tag, TAG_FALLBACK(2147483647 - 1), n); | |||
| 2608 | free(tag); | |||
| 2609 | } | |||
| 2610 | post_delim(mdoc); | |||
| 2611 | post_hyph(mdoc); | |||
| 2612 | return; | |||
| 2613 | case ROFFT_BODY: | |||
| 2614 | break; | |||
| 2615 | default: | |||
| 2616 | return; | |||
| 2617 | } | |||
| 2618 | if ((nch = n->child) != NULL((void *)0) && | |||
| 2619 | (nch->tok == MDOC_Pp || nch->tok == ROFF_br || | |||
| 2620 | nch->tok == ROFF_sp)) { | |||
| 2621 | mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, | |||
| 2622 | "%s after %s", roff_name[nch->tok], | |||
| 2623 | roff_name[n->tok]); | |||
| 2624 | roff_node_delete(mdoc, nch); | |||
| 2625 | } | |||
| 2626 | if ((nch = n->last) != NULL((void *)0) && | |||
| 2627 | (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { | |||
| 2628 | mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, | |||
| 2629 | "%s at the end of %s", roff_name[nch->tok], | |||
| 2630 | roff_name[n->tok]); | |||
| 2631 | roff_node_delete(mdoc, nch); | |||
| 2632 | } | |||
| 2633 | } | |||
| 2634 | ||||
| 2635 | static void | |||
| 2636 | post_prevpar(POST_ARGSstruct roff_man *mdoc) | |||
| 2637 | { | |||
| 2638 | struct roff_node *n, *np; | |||
| 2639 | ||||
| 2640 | n = mdoc->last; | |||
| 2641 | if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) | |||
| 2642 | return; | |||
| 2643 | if ((np = roff_node_prev(n)) == NULL((void *)0)) | |||
| 2644 | return; | |||
| 2645 | ||||
| 2646 | /* | |||
| 2647 | * Don't allow `Pp' prior to a paragraph-type | |||
| 2648 | * block: `Pp' or non-compact `Bd' or `Bl'. | |||
| 2649 | */ | |||
| 2650 | ||||
| 2651 | if (np->tok != MDOC_Pp && np->tok != ROFF_br) | |||
| 2652 | return; | |||
| 2653 | if (n->tok == MDOC_Bl && n->norm->Bl.comp) | |||
| 2654 | return; | |||
| 2655 | if (n->tok == MDOC_Bd && n->norm->Bd.comp) | |||
| 2656 | return; | |||
| 2657 | if (n->tok == MDOC_It && n->parent->norm->Bl.comp) | |||
| 2658 | return; | |||
| 2659 | ||||
| 2660 | mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, | |||
| 2661 | "%s before %s", roff_name[np->tok], roff_name[n->tok]); | |||
| 2662 | roff_node_delete(mdoc, np); | |||
| 2663 | } | |||
| 2664 | ||||
| 2665 | static void | |||
| 2666 | post_par(POST_ARGSstruct roff_man *mdoc) | |||
| 2667 | { | |||
| 2668 | struct roff_node *np; | |||
| 2669 | ||||
| 2670 | fn_prio = TAG_STRONG2; | |||
| 2671 | post_prevpar(mdoc); | |||
| 2672 | ||||
| 2673 | np = mdoc->last; | |||
| 2674 | if (np->child != NULL((void *)0)) | |||
| 2675 | mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, | |||
| 2676 | "%s %s", roff_name[np->tok], np->child->string); | |||
| 2677 | } | |||
| 2678 | ||||
| 2679 | static void | |||
| 2680 | post_dd(POST_ARGSstruct roff_man *mdoc) | |||
| 2681 | { | |||
| 2682 | struct roff_node *n; | |||
| 2683 | ||||
| 2684 | n = mdoc->last; | |||
| 2685 | n->flags |= NODE_NOPRT(1 << 10); | |||
| 2686 | ||||
| 2687 | if (mdoc->meta.date != NULL((void *)0)) { | |||
| 2688 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); | |||
| 2689 | free(mdoc->meta.date); | |||
| 2690 | } else if (mdoc->flags & MDOC_PBODY(1 << 2)) | |||
| 2691 | mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); | |||
| 2692 | else if (mdoc->meta.title != NULL((void *)0)) | |||
| 2693 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
| 2694 | n->line, n->pos, "Dd after Dt"); | |||
| 2695 | else if (mdoc->meta.os != NULL((void *)0)) | |||
| 2696 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
| 2697 | n->line, n->pos, "Dd after Os"); | |||
| 2698 | ||||
| 2699 | if (mdoc->quick && n != NULL((void *)0)) | |||
| 2700 | mdoc->meta.date = mandoc_strdup(""); | |||
| 2701 | else | |||
| 2702 | mdoc->meta.date = mandoc_normdate(n->child, n); | |||
| 2703 | } | |||
| 2704 | ||||
| 2705 | static void | |||
| 2706 | post_dt(POST_ARGSstruct roff_man *mdoc) | |||
| 2707 | { | |||
| 2708 | struct roff_node *nn, *n; | |||
| 2709 | const char *cp; | |||
| 2710 | char *p; | |||
| 2711 | ||||
| 2712 | n = mdoc->last; | |||
| 2713 | n->flags |= NODE_NOPRT(1 << 10); | |||
| 2714 | ||||
| 2715 | if (mdoc->flags & MDOC_PBODY(1 << 2)) { | |||
| 2716 | mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); | |||
| 2717 | return; | |||
| 2718 | } | |||
| 2719 | ||||
| 2720 | if (mdoc->meta.title != NULL((void *)0)) | |||
| 2721 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); | |||
| 2722 | else if (mdoc->meta.os != NULL((void *)0)) | |||
| 2723 | mandoc_msg(MANDOCERR_PROLOG_ORDER, | |||
| 2724 | n->line, n->pos, "Dt after Os"); | |||
| 2725 | ||||
| 2726 | free(mdoc->meta.title); | |||
| 2727 | free(mdoc->meta.msec); | |||
| 2728 | free(mdoc->meta.vol); | |||
| 2729 | free(mdoc->meta.arch); | |||
| 2730 | ||||
| 2731 | mdoc->meta.title = NULL((void *)0); | |||
| 2732 | mdoc->meta.msec = NULL((void *)0); | |||
| 2733 | mdoc->meta.vol = NULL((void *)0); | |||
| 2734 | mdoc->meta.arch = NULL((void *)0); | |||
| 2735 | ||||
| 2736 | /* Mandatory first argument: title. */ | |||
| 2737 | ||||
| 2738 | nn = n->child; | |||
| 2739 | if (nn == NULL((void *)0) || *nn->string == '\0') { | |||
| 2740 | mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); | |||
| 2741 | mdoc->meta.title = mandoc_strdup("UNTITLED"); | |||
| 2742 | } else { | |||
| 2743 | mdoc->meta.title = mandoc_strdup(nn->string); | |||
| 2744 | ||||
| 2745 | /* Check that all characters are uppercase. */ | |||
| 2746 | ||||
| 2747 | for (p = nn->string; *p != '\0'; p++) | |||
| 2748 | if (islower((unsigned char)*p)) { | |||
| 2749 | mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, | |||
| 2750 | nn->pos + (int)(p - nn->string), | |||
| 2751 | "Dt %s", nn->string); | |||
| 2752 | break; | |||
| 2753 | } | |||
| 2754 | } | |||
| 2755 | ||||
| 2756 | /* Mandatory second argument: section. */ | |||
| 2757 | ||||
| 2758 | if (nn != NULL((void *)0)) | |||
| 2759 | nn = nn->next; | |||
| 2760 | ||||
| 2761 | if (nn == NULL((void *)0)) { | |||
| 2762 | mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, | |||
| 2763 | "Dt %s", mdoc->meta.title); | |||
| 2764 | mdoc->meta.vol = mandoc_strdup("LOCAL"); | |||
| 2765 | return; /* msec and arch remain NULL. */ | |||
| 2766 | } | |||
| 2767 | ||||
| 2768 | mdoc->meta.msec = mandoc_strdup(nn->string); | |||
| 2769 | ||||
| 2770 | /* Infer volume title from section number. */ | |||
| 2771 | ||||
| 2772 | cp = mandoc_a2msec(nn->string); | |||
| 2773 | if (cp == NULL((void *)0)) { | |||
| 2774 | mandoc_msg(MANDOCERR_MSEC_BAD, | |||
| 2775 | nn->line, nn->pos, "Dt ... %s", nn->string); | |||
| 2776 | mdoc->meta.vol = mandoc_strdup(nn->string); | |||
| 2777 | } else { | |||
| 2778 | mdoc->meta.vol = mandoc_strdup(cp); | |||
| 2779 | if (mdoc->filesec != '\0' && | |||
| 2780 | mdoc->filesec != *nn->string && | |||
| 2781 | *nn->string >= '1' && *nn->string <= '9') | |||
| 2782 | mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, | |||
| 2783 | "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); | |||
| 2784 | } | |||
| 2785 | ||||
| 2786 | /* Optional third argument: architecture. */ | |||
| 2787 | ||||
| 2788 | if ((nn = nn->next) == NULL((void *)0)) | |||
| 2789 | return; | |||
| 2790 | ||||
| 2791 | for (p = nn->string; *p != '\0'; p++) | |||
| 2792 | *p = tolower((unsigned char)*p); | |||
| 2793 | mdoc->meta.arch = mandoc_strdup(nn->string); | |||
| 2794 | ||||
| 2795 | /* Ignore fourth and later arguments. */ | |||
| 2796 | ||||
| 2797 | if ((nn = nn->next) != NULL((void *)0)) | |||
| 2798 | mandoc_msg(MANDOCERR_ARG_EXCESS, | |||
| 2799 | nn->line, nn->pos, "Dt ... %s", nn->string); | |||
| 2800 | } | |||
| 2801 | ||||
| 2802 | static void | |||
| 2803 | post_bx(POST_ARGSstruct roff_man *mdoc) | |||
| 2804 | { | |||
| 2805 | struct roff_node *n, *nch; | |||
| 2806 | const char *macro; | |||
| 2807 | ||||
| 2808 | post_delim_nb(mdoc); | |||
| 2809 | ||||
| 2810 | n = mdoc->last; | |||
| 2811 | nch = n->child; | |||
| 2812 | ||||
| 2813 | if (nch != NULL((void *)0)) { | |||
| 2814 | macro = !strcmp(nch->string, "Open") ? "Ox" : | |||
| 2815 | !strcmp(nch->string, "Net") ? "Nx" : | |||
| 2816 | !strcmp(nch->string, "Free") ? "Fx" : | |||
| 2817 | !strcmp(nch->string, "DragonFly") ? "Dx" : NULL((void *)0); | |||
| 2818 | if (macro != NULL((void *)0)) | |||
| 2819 | mandoc_msg(MANDOCERR_BX, | |||
| 2820 | n->line, n->pos, "%s", macro); | |||
| 2821 | mdoc->last = nch; | |||
| 2822 | nch = nch->next; | |||
| 2823 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 2824 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
| 2825 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 2826 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 2827 | } else | |||
| 2828 | mdoc->next = ROFF_NEXT_CHILD; | |||
| 2829 | roff_word_alloc(mdoc, n->line, n->pos, "BSD"); | |||
| 2830 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 2831 | ||||
| 2832 | if (nch == NULL((void *)0)) { | |||
| 2833 | mdoc->last = n; | |||
| 2834 | return; | |||
| 2835 | } | |||
| 2836 | ||||
| 2837 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
| 2838 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 2839 | mdoc->next = ROFF_NEXT_SIBLING; | |||
| 2840 | roff_word_alloc(mdoc, n->line, n->pos, "-"); | |||
| 2841 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 2842 | roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); | |||
| 2843 | mdoc->last->flags |= NODE_NOSRC(1 << 9); | |||
| 2844 | mdoc->last = n; | |||
| 2845 | ||||
| 2846 | /* | |||
| 2847 | * Make `Bx's second argument always start with an uppercase | |||
| 2848 | * letter. Groff checks if it's an "accepted" term, but we just | |||
| 2849 | * uppercase blindly. | |||
| 2850 | */ | |||
| 2851 | ||||
| 2852 | *nch->string = (char)toupper((unsigned char)*nch->string); | |||
| 2853 | } | |||
| 2854 | ||||
| 2855 | static void | |||
| 2856 | post_os(POST_ARGSstruct roff_man *mdoc) | |||
| 2857 | { | |||
| 2858 | #ifndef OSNAME | |||
| 2859 | struct utsname utsname; | |||
| 2860 | #endif | |||
| 2861 | struct roff_node *n; | |||
| 2862 | ||||
| 2863 | n = mdoc->last; | |||
| 2864 | n->flags |= NODE_NOPRT(1 << 10); | |||
| 2865 | ||||
| 2866 | if (mdoc->meta.os != NULL((void *)0)) | |||
| 2867 | mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); | |||
| 2868 | else if (mdoc->flags & MDOC_PBODY(1 << 2)) | |||
| 2869 | mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); | |||
| 2870 | ||||
| 2871 | post_delim(mdoc); | |||
| 2872 | ||||
| 2873 | /* | |||
| 2874 | * Set the operating system by way of the `Os' macro. | |||
| 2875 | * The order of precedence is: | |||
| 2876 | * 1. the argument of the `Os' macro, unless empty | |||
| 2877 | * 2. the -Ios=foo command line argument, if provided | |||
| 2878 | * 3. -DOSNAME="\"foo\"", if provided during compilation | |||
| 2879 | * 4. "sysname release" from uname(3) | |||
| 2880 | */ | |||
| 2881 | ||||
| 2882 | free(mdoc->meta.os); | |||
| 2883 | mdoc->meta.os = NULL((void *)0); | |||
| 2884 | deroff(&mdoc->meta.os, n); | |||
| 2885 | if (mdoc->meta.os) | |||
| 2886 | goto out; | |||
| 2887 | ||||
| 2888 | if (mdoc->os_s != NULL((void *)0)) { | |||
| 2889 | mdoc->meta.os = mandoc_strdup(mdoc->os_s); | |||
| 2890 | goto out; | |||
| 2891 | } | |||
| 2892 | ||||
| 2893 | #ifdef OSNAME | |||
| 2894 | mdoc->meta.os = mandoc_strdup(OSNAME); | |||
| 2895 | #else /*!OSNAME */ | |||
| 2896 | if (mdoc->os_r == NULL((void *)0)) { | |||
| 2897 | if (uname(&utsname) == -1) { | |||
| 2898 | mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); | |||
| 2899 | mdoc->os_r = mandoc_strdup("UNKNOWN"); | |||
| 2900 | } else | |||
| 2901 | mandoc_asprintf(&mdoc->os_r, "%s %s", | |||
| 2902 | utsname.sysname, utsname.release); | |||
| 2903 | } | |||
| 2904 | mdoc->meta.os = mandoc_strdup(mdoc->os_r); | |||
| 2905 | #endif /*!OSNAME*/ | |||
| 2906 | ||||
| 2907 | out: | |||
| 2908 | if (mdoc->meta.os_e == MANDOC_OS_OTHER) { | |||
| 2909 | if (strstr(mdoc->meta.os, "OpenBSD") != NULL((void *)0)) | |||
| 2910 | mdoc->meta.os_e = MANDOC_OS_OPENBSD; | |||
| 2911 | else if (strstr(mdoc->meta.os, "NetBSD") != NULL((void *)0)) | |||
| 2912 | mdoc->meta.os_e = MANDOC_OS_NETBSD; | |||
| 2913 | } | |||
| 2914 | ||||
| 2915 | /* | |||
| 2916 | * This is the earliest point where we can check | |||
| 2917 | * Mdocdate conventions because we don't know | |||
| 2918 | * the operating system earlier. | |||
| 2919 | */ | |||
| 2920 | ||||
| 2921 | if (n->child != NULL((void *)0)) | |||
| 2922 | mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, | |||
| 2923 | "Os %s (%s)", n->child->string, | |||
| 2924 | mdoc->meta.os_e == MANDOC_OS_OPENBSD ? | |||
| 2925 | "OpenBSD" : "NetBSD"); | |||
| 2926 | ||||
| 2927 | while (n->tok != MDOC_Dd) | |||
| 2928 | if ((n = n->prev) == NULL((void *)0)) | |||
| 2929 | return; | |||
| 2930 | if ((n = n->child) == NULL((void *)0)) | |||
| 2931 | return; | |||
| 2932 | if (strncmp(n->string, "$" "Mdocdate", 9)) { | |||
| 2933 | if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) | |||
| 2934 | mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, | |||
| 2935 | n->pos, "Dd %s (OpenBSD)", n->string); | |||
| 2936 | } else { | |||
| 2937 | if (mdoc->meta.os_e == MANDOC_OS_NETBSD) | |||
| 2938 | mandoc_msg(MANDOCERR_MDOCDATE, n->line, | |||
| 2939 | n->pos, "Dd %s (NetBSD)", n->string); | |||
| 2940 | } | |||
| 2941 | } | |||
| 2942 | ||||
| 2943 | enum roff_sec | |||
| 2944 | mdoc_a2sec(const char *p) | |||
| 2945 | { | |||
| 2946 | int i; | |||
| 2947 | ||||
| 2948 | for (i = 0; i < (int)SEC__MAX; i++) | |||
| 2949 | if (secnames[i] && 0 == strcmp(p, secnames[i])) | |||
| 2950 | return (enum roff_sec)i; | |||
| 2951 | ||||
| 2952 | return SEC_CUSTOM; | |||
| 2953 | } | |||
| 2954 | ||||
| 2955 | static size_t | |||
| 2956 | macro2len(enum roff_tok macro) | |||
| 2957 | { | |||
| 2958 | ||||
| 2959 | switch (macro) { | |||
| 2960 | case MDOC_Ad: | |||
| 2961 | return 12; | |||
| 2962 | case MDOC_Ao: | |||
| 2963 | return 12; | |||
| 2964 | case MDOC_An: | |||
| 2965 | return 12; | |||
| 2966 | case MDOC_Aq: | |||
| 2967 | return 12; | |||
| 2968 | case MDOC_Ar: | |||
| 2969 | return 12; | |||
| 2970 | case MDOC_Bo: | |||
| 2971 | return 12; | |||
| 2972 | case MDOC_Bq: | |||
| 2973 | return 12; | |||
| 2974 | case MDOC_Cd: | |||
| 2975 | return 12; | |||
| 2976 | case MDOC_Cm: | |||
| 2977 | return 10; | |||
| 2978 | case MDOC_Do: | |||
| 2979 | return 10; | |||
| 2980 | case MDOC_Dq: | |||
| 2981 | return 12; | |||
| 2982 | case MDOC_Dv: | |||
| 2983 | return 12; | |||
| 2984 | case MDOC_Eo: | |||
| 2985 | return 12; | |||
| 2986 | case MDOC_Em: | |||
| 2987 | return 10; | |||
| 2988 | case MDOC_Er: | |||
| 2989 | return 17; | |||
| 2990 | case MDOC_Ev: | |||
| 2991 | return 15; | |||
| 2992 | case MDOC_Fa: | |||
| 2993 | return 12; | |||
| 2994 | case MDOC_Fl: | |||
| 2995 | return 10; | |||
| 2996 | case MDOC_Fo: | |||
| 2997 | return 16; | |||
| 2998 | case MDOC_Fn: | |||
| 2999 | return 16; | |||
| 3000 | case MDOC_Ic: | |||
| 3001 | return 10; | |||
| 3002 | case MDOC_Li: | |||
| 3003 | return 16; | |||
| 3004 | case MDOC_Ms: | |||
| 3005 | return 6; | |||
| 3006 | case MDOC_Nm: | |||
| 3007 | return 10; | |||
| 3008 | case MDOC_No: | |||
| 3009 | return 12; | |||
| 3010 | case MDOC_Oo: | |||
| 3011 | return 10; | |||
| 3012 | case MDOC_Op: | |||
| 3013 | return 14; | |||
| 3014 | case MDOC_Pa: | |||
| 3015 | return 32; | |||
| 3016 | case MDOC_Pf: | |||
| 3017 | return 12; | |||
| 3018 | case MDOC_Po: | |||
| 3019 | return 12; | |||
| 3020 | case MDOC_Pq: | |||
| 3021 | return 12; | |||
| 3022 | case MDOC_Ql: | |||
| 3023 | return 16; | |||
| 3024 | case MDOC_Qo: | |||
| 3025 | return 12; | |||
| 3026 | case MDOC_So: | |||
| 3027 | return 12; | |||
| 3028 | case MDOC_Sq: | |||
| 3029 | return 12; | |||
| 3030 | case MDOC_Sy: | |||
| 3031 | return 6; | |||
| 3032 | case MDOC_Sx: | |||
| 3033 | return 16; | |||
| 3034 | case MDOC_Tn: | |||
| 3035 | return 10; | |||
| 3036 | case MDOC_Va: | |||
| 3037 | return 12; | |||
| 3038 | case MDOC_Vt: | |||
| 3039 | return 12; | |||
| 3040 | case MDOC_Xr: | |||
| 3041 | return 10; | |||
| 3042 | default: | |||
| 3043 | break; | |||
| 3044 | }; | |||
| 3045 | return 0; | |||
| 3046 | } |