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