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