1 /* $Id: mdoc_validate.c,v 1.396 2025/07/26 12:23:16 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2010-2022, 2025 Ingo Schwarze <schwarze@openbsd.org> 4 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 * 19 * Validation module for mdoc(7) syntax trees used by mandoc(1). 20 */ 21 #include "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, *nch; 995 const char *ccp; 996 char *cp; 997 998 post_delim_nb(mdoc); 999 1000 n = mdoc->last; 1001 nch = n->child; 1002 assert(nch->type == ROFFT_TEXT); 1003 mdoc->next = ROFF_NEXT_CHILD; 1004 1005 if (n->sec == SEC_SYNOPSIS) { 1006 roff_word_alloc(mdoc, n->line, n->pos, "/*"); 1007 mdoc->last->flags = NODE_NOSRC; 1008 while (nch != NULL) { 1009 roff_word_alloc(mdoc, n->line, n->pos, "-l"); 1010 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 1011 mdoc->last = nch; 1012 assert(nch->type == ROFFT_TEXT); 1013 cp = nch->string; 1014 if (strncmp(cp, "lib", 3) == 0) 1015 memmove(cp, cp + 3, strlen(cp) - 3 + 1); 1016 nch = nch->next; 1017 } 1018 roff_word_alloc(mdoc, n->line, n->pos, "*/"); 1019 mdoc->last->flags = NODE_NOSRC; 1020 mdoc->last = n; 1021 return; 1022 } 1023 1024 if ((ccp = mdoc_a2lib(n->child->string)) != NULL) { 1025 n->child->flags |= NODE_NOPRT; 1026 roff_word_alloc(mdoc, n->line, n->pos, ccp); 1027 mdoc->last->flags = NODE_NOSRC; 1028 mdoc->last = n; 1029 return; 1030 } 1031 1032 mandoc_msg(MANDOCERR_LB_BAD, n->child->line, 1033 n->child->pos, "Lb %s", n->child->string); 1034 1035 roff_word_alloc(mdoc, n->line, n->pos, "library"); 1036 mdoc->last->flags = NODE_NOSRC; 1037 roff_word_alloc(mdoc, n->line, n->pos, "\\(lq"); 1038 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC; 1039 mdoc->last = mdoc->last->next; 1040 roff_word_alloc(mdoc, n->line, n->pos, "\\(rq"); 1041 mdoc->last->flags = NODE_DELIMC | NODE_NOSRC; 1042 mdoc->last = n; 1043 } 1044 1045 static void 1046 post_rv(POST_ARGS) 1047 { 1048 struct roff_node *n; 1049 int ic; 1050 1051 post_std(mdoc); 1052 1053 n = mdoc->last; 1054 mdoc->next = ROFF_NEXT_CHILD; 1055 if (n->child != NULL) { 1056 roff_word_alloc(mdoc, n->line, n->pos, "The"); 1057 mdoc->last->flags |= NODE_NOSRC; 1058 ic = build_list(mdoc, MDOC_Fn); 1059 roff_word_alloc(mdoc, n->line, n->pos, 1060 ic > 1 ? "functions return" : "function returns"); 1061 mdoc->last->flags |= NODE_NOSRC; 1062 roff_word_alloc(mdoc, n->line, n->pos, 1063 "the value\\~0 if successful;"); 1064 } else 1065 roff_word_alloc(mdoc, n->line, n->pos, "Upon successful " 1066 "completion, the value\\~0 is returned;"); 1067 mdoc->last->flags |= NODE_NOSRC; 1068 1069 roff_word_alloc(mdoc, n->line, n->pos, "otherwise " 1070 "the value\\~\\-1 is returned and the global variable"); 1071 mdoc->last->flags |= NODE_NOSRC; 1072 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va); 1073 mdoc->last->flags |= NODE_NOSRC; 1074 roff_word_alloc(mdoc, n->line, n->pos, "errno"); 1075 mdoc->last->flags |= NODE_NOSRC; 1076 mdoc->last = mdoc->last->parent; 1077 mdoc->next = ROFF_NEXT_SIBLING; 1078 roff_word_alloc(mdoc, n->line, n->pos, 1079 "is set to indicate the error."); 1080 mdoc->last->flags |= NODE_EOS | NODE_NOSRC; 1081 mdoc->last = n; 1082 } 1083 1084 static void 1085 post_std(POST_ARGS) 1086 { 1087 struct roff_node *n; 1088 1089 post_delim(mdoc); 1090 1091 n = mdoc->last; 1092 if (n->args && n->args->argc == 1) 1093 if (n->args->argv[0].arg == MDOC_Std) 1094 return; 1095 1096 mandoc_msg(MANDOCERR_ARG_STD, n->line, n->pos, 1097 "%s", roff_name[n->tok]); 1098 } 1099 1100 static void 1101 post_st(POST_ARGS) 1102 { 1103 struct roff_node *n, *nch; 1104 const char *p; 1105 1106 n = mdoc->last; 1107 nch = n->child; 1108 assert(nch->type == ROFFT_TEXT); 1109 1110 if ((p = mdoc_a2st(nch->string)) == NULL) { 1111 mandoc_msg(MANDOCERR_ST_BAD, 1112 nch->line, nch->pos, "St %s", nch->string); 1113 roff_node_delete(mdoc, n); 1114 return; 1115 } 1116 1117 nch->flags |= NODE_NOPRT; 1118 mdoc->next = ROFF_NEXT_CHILD; 1119 roff_word_alloc(mdoc, nch->line, nch->pos, p); 1120 mdoc->last->flags |= NODE_NOSRC; 1121 mdoc->last= n; 1122 } 1123 1124 static void 1125 post_tg(POST_ARGS) 1126 { 1127 struct roff_node *n; /* The .Tg node. */ 1128 struct roff_node *nch; /* The first child of the .Tg node. */ 1129 struct roff_node *nn; /* The next node after the .Tg node. */ 1130 struct roff_node *np; /* The parent of the next node. */ 1131 struct roff_node *nt; /* The TEXT node containing the tag. */ 1132 size_t len; /* The number of bytes in the tag. */ 1133 1134 /* Find the next node. */ 1135 n = mdoc->last; 1136 for (nn = n; nn != NULL; nn = nn->parent) { 1137 if (nn->type != ROFFT_HEAD && nn->type != ROFFT_BODY && 1138 nn->type != ROFFT_TAIL && nn->next != NULL) { 1139 nn = nn->next; 1140 break; 1141 } 1142 } 1143 1144 /* Find the tag. */ 1145 nt = nch = n->child; 1146 if (nch == NULL && nn != NULL && nn->child != NULL && 1147 nn->child->type == ROFFT_TEXT) 1148 nt = nn->child; 1149 1150 /* Validate the tag. */ 1151 if (nt == NULL || *nt->string == '\0') 1152 mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); 1153 if (nt == NULL) { 1154 roff_node_delete(mdoc, n); 1155 return; 1156 } 1157 len = strcspn(nt->string, " \t\\"); 1158 if (nt->string[len] != '\0') 1159 mandoc_msg(MANDOCERR_TG_SPC, nt->line, 1160 nt->pos + len, "Tg %s", nt->string); 1161 1162 /* Keep only the first argument. */ 1163 if (nch != NULL && nch->next != NULL) { 1164 mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, 1165 nch->next->pos, "Tg ... %s", nch->next->string); 1166 while (nch->next != NULL) 1167 roff_node_delete(mdoc, nch->next); 1168 } 1169 1170 /* Drop the macro if the first argument is invalid. */ 1171 if (len == 0 || nt->string[len] != '\0') { 1172 roff_node_delete(mdoc, n); 1173 return; 1174 } 1175 1176 /* By default, tag the .Tg node itself. */ 1177 if (nn == NULL || nn->flags & NODE_ID) 1178 nn = n; 1179 1180 /* Explicit tagging of specific macros. */ 1181 switch (nn->tok) { 1182 case MDOC_Sh: 1183 case MDOC_Ss: 1184 case MDOC_Fo: 1185 nn = nn->head->child == NULL ? n : nn->head; 1186 break; 1187 case MDOC_It: 1188 np = nn->parent; 1189 while (np->tok != MDOC_Bl) 1190 np = np->parent; 1191 switch (np->norm->Bl.type) { 1192 case LIST_column: 1193 break; 1194 case LIST_diag: 1195 case LIST_hang: 1196 case LIST_inset: 1197 case LIST_ohang: 1198 case LIST_tag: 1199 nn = nn->head; 1200 break; 1201 case LIST_bullet: 1202 case LIST_dash: 1203 case LIST_enum: 1204 case LIST_hyphen: 1205 case LIST_item: 1206 nn = nn->body->child == NULL ? n : nn->body; 1207 break; 1208 default: 1209 abort(); 1210 } 1211 break; 1212 case MDOC_Bd: 1213 case MDOC_Bl: 1214 case MDOC_D1: 1215 case MDOC_Dl: 1216 nn = nn->body->child == NULL ? n : nn->body; 1217 break; 1218 case MDOC_Pp: 1219 break; 1220 case MDOC_Cm: 1221 case MDOC_Dv: 1222 case MDOC_Em: 1223 case MDOC_Er: 1224 case MDOC_Ev: 1225 case MDOC_Fl: 1226 case MDOC_Fn: 1227 case MDOC_Ic: 1228 case MDOC_Li: 1229 case MDOC_Ms: 1230 case MDOC_No: 1231 case MDOC_Sy: 1232 if (nn->child == NULL) 1233 nn = n; 1234 break; 1235 default: 1236 nn = n; 1237 break; 1238 } 1239 tag_put(nt->string, TAG_MANUAL, nn); 1240 if (nn != n) 1241 n->flags |= NODE_NOPRT; 1242 } 1243 1244 static void 1245 post_obsolete(POST_ARGS) 1246 { 1247 struct roff_node *n; 1248 1249 n = mdoc->last; 1250 if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) 1251 mandoc_msg(MANDOCERR_MACRO_OBS, n->line, n->pos, 1252 "%s", roff_name[n->tok]); 1253 } 1254 1255 static void 1256 post_useless(POST_ARGS) 1257 { 1258 struct roff_node *n; 1259 1260 n = mdoc->last; 1261 mandoc_msg(MANDOCERR_MACRO_USELESS, n->line, n->pos, 1262 "%s", roff_name[n->tok]); 1263 } 1264 1265 /* 1266 * Block macros. 1267 */ 1268 1269 static void 1270 post_bf(POST_ARGS) 1271 { 1272 struct roff_node *np, *nch; 1273 1274 /* 1275 * Unlike other data pointers, these are "housed" by the HEAD 1276 * element, which contains the goods. 1277 */ 1278 1279 np = mdoc->last; 1280 if (np->type != ROFFT_HEAD) 1281 return; 1282 1283 assert(np->parent->type == ROFFT_BLOCK); 1284 assert(np->parent->tok == MDOC_Bf); 1285 1286 /* Check the number of arguments. */ 1287 1288 nch = np->child; 1289 if (np->parent->args == NULL) { 1290 if (nch == NULL) { 1291 mandoc_msg(MANDOCERR_BF_NOFONT, 1292 np->line, np->pos, "Bf"); 1293 return; 1294 } 1295 nch = nch->next; 1296 } 1297 if (nch != NULL) 1298 mandoc_msg(MANDOCERR_ARG_EXCESS, 1299 nch->line, nch->pos, "Bf ... %s", nch->string); 1300 1301 /* Extract argument into data. */ 1302 1303 if (np->parent->args != NULL) { 1304 switch (np->parent->args->argv[0].arg) { 1305 case MDOC_Emphasis: 1306 np->norm->Bf.font = FONT_Em; 1307 break; 1308 case MDOC_Literal: 1309 np->norm->Bf.font = FONT_Li; 1310 break; 1311 case MDOC_Symbolic: 1312 np->norm->Bf.font = FONT_Sy; 1313 break; 1314 default: 1315 abort(); 1316 } 1317 return; 1318 } 1319 1320 /* Extract parameter into data. */ 1321 1322 if ( ! strcmp(np->child->string, "Em")) 1323 np->norm->Bf.font = FONT_Em; 1324 else if ( ! strcmp(np->child->string, "Li")) 1325 np->norm->Bf.font = FONT_Li; 1326 else if ( ! strcmp(np->child->string, "Sy")) 1327 np->norm->Bf.font = FONT_Sy; 1328 else 1329 mandoc_msg(MANDOCERR_BF_BADFONT, np->child->line, 1330 np->child->pos, "Bf %s", np->child->string); 1331 } 1332 1333 static void 1334 post_fname(POST_ARGS) 1335 { 1336 struct roff_node *n, *nch; 1337 const char *cp; 1338 size_t pos; 1339 1340 n = mdoc->last; 1341 nch = n->child; 1342 cp = nch->string; 1343 if (*cp == '(') { 1344 if (cp[strlen(cp + 1)] == ')') 1345 return; 1346 pos = 0; 1347 } else { 1348 pos = strcspn(cp, "()"); 1349 if (cp[pos] == '\0') { 1350 if (n->sec == SEC_DESCRIPTION || 1351 n->sec == SEC_CUSTOM) 1352 tag_put(NULL, fn_prio++, n); 1353 return; 1354 } 1355 } 1356 mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); 1357 } 1358 1359 static void 1360 post_fn(POST_ARGS) 1361 { 1362 post_fname(mdoc); 1363 post_fa(mdoc); 1364 } 1365 1366 static void 1367 post_fo(POST_ARGS) 1368 { 1369 const struct roff_node *n; 1370 1371 n = mdoc->last; 1372 1373 if (n->type != ROFFT_HEAD) 1374 return; 1375 1376 if (n->child == NULL) { 1377 mandoc_msg(MANDOCERR_FO_NOHEAD, n->line, n->pos, "Fo"); 1378 return; 1379 } 1380 if (n->child != n->last) { 1381 mandoc_msg(MANDOCERR_ARG_EXCESS, 1382 n->child->next->line, n->child->next->pos, 1383 "Fo ... %s", n->child->next->string); 1384 while (n->child != n->last) 1385 roff_node_delete(mdoc, n->last); 1386 } else 1387 post_delim(mdoc); 1388 1389 post_fname(mdoc); 1390 } 1391 1392 static void 1393 post_fa(POST_ARGS) 1394 { 1395 const struct roff_node *n; 1396 const char *cp; 1397 1398 for (n = mdoc->last->child; n != NULL; n = n->next) { 1399 for (cp = n->string; *cp != '\0'; cp++) { 1400 /* Ignore callbacks and alterations. */ 1401 if (*cp == '(' || *cp == '{') 1402 break; 1403 if (*cp != ',') 1404 continue; 1405 mandoc_msg(MANDOCERR_FA_COMMA, n->line, 1406 n->pos + (int)(cp - n->string), "%s", n->string); 1407 break; 1408 } 1409 } 1410 post_delim_nb(mdoc); 1411 } 1412 1413 static void 1414 post_nm(POST_ARGS) 1415 { 1416 struct roff_node *n; 1417 1418 n = mdoc->last; 1419 1420 if (n->sec == SEC_NAME && n->child != NULL && 1421 n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL) 1422 mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); 1423 1424 if (n->last != NULL && n->last->tok == MDOC_Pp) 1425 roff_node_relink(mdoc, n->last); 1426 1427 if (mdoc->meta.name == NULL) 1428 deroff(&mdoc->meta.name, n); 1429 1430 if (mdoc->meta.name == NULL || 1431 (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1432 mandoc_msg(MANDOCERR_NM_NONAME, n->line, n->pos, "Nm"); 1433 1434 switch (n->type) { 1435 case ROFFT_ELEM: 1436 post_delim_nb(mdoc); 1437 break; 1438 case ROFFT_HEAD: 1439 post_delim(mdoc); 1440 break; 1441 default: 1442 return; 1443 } 1444 1445 if ((n->child != NULL && n->child->type == ROFFT_TEXT) || 1446 mdoc->meta.name == NULL) 1447 return; 1448 1449 mdoc->next = ROFF_NEXT_CHILD; 1450 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 1451 mdoc->last->flags |= NODE_NOSRC; 1452 mdoc->last = n; 1453 } 1454 1455 static void 1456 post_nd(POST_ARGS) 1457 { 1458 struct roff_node *n; 1459 1460 n = mdoc->last; 1461 1462 if (n->type != ROFFT_BODY) 1463 return; 1464 1465 if (n->sec != SEC_NAME) 1466 mandoc_msg(MANDOCERR_ND_LATE, n->line, n->pos, "Nd"); 1467 1468 if (n->child == NULL) 1469 mandoc_msg(MANDOCERR_ND_EMPTY, n->line, n->pos, "Nd"); 1470 else 1471 post_delim(mdoc); 1472 1473 post_hyph(mdoc); 1474 } 1475 1476 static void 1477 post_display(POST_ARGS) 1478 { 1479 struct roff_node *n, *np; 1480 1481 n = mdoc->last; 1482 switch (n->type) { 1483 case ROFFT_BODY: 1484 if (n->end != ENDBODY_NOT) { 1485 if (n->tok == MDOC_Bd && 1486 n->body->parent->args == NULL) 1487 roff_node_delete(mdoc, n); 1488 } else if (n->child == NULL) 1489 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, 1490 "%s", roff_name[n->tok]); 1491 else if (n->tok == MDOC_D1) 1492 post_hyph(mdoc); 1493 break; 1494 case ROFFT_BLOCK: 1495 if (n->tok == MDOC_Bd) { 1496 if (n->args == NULL) { 1497 mandoc_msg(MANDOCERR_BD_NOARG, 1498 n->line, n->pos, "Bd"); 1499 mdoc->next = ROFF_NEXT_SIBLING; 1500 while (n->body->child != NULL) 1501 roff_node_relink(mdoc, 1502 n->body->child); 1503 roff_node_delete(mdoc, n); 1504 break; 1505 } 1506 post_bd(mdoc); 1507 post_prevpar(mdoc); 1508 } 1509 for (np = n->parent; np != NULL; np = np->parent) { 1510 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 1511 mandoc_msg(MANDOCERR_BD_NEST, n->line, 1512 n->pos, "%s in Bd", roff_name[n->tok]); 1513 break; 1514 } 1515 } 1516 break; 1517 default: 1518 break; 1519 } 1520 } 1521 1522 static void 1523 post_defaults(POST_ARGS) 1524 { 1525 struct roff_node *n; 1526 1527 n = mdoc->last; 1528 if (n->child != NULL) { 1529 post_delim_nb(mdoc); 1530 return; 1531 } 1532 mdoc->next = ROFF_NEXT_CHILD; 1533 switch (n->tok) { 1534 case MDOC_Ar: 1535 roff_word_alloc(mdoc, n->line, n->pos, "file"); 1536 mdoc->last->flags |= NODE_NOSRC; 1537 roff_word_alloc(mdoc, n->line, n->pos, "..."); 1538 break; 1539 case MDOC_Pa: 1540 case MDOC_Mt: 1541 roff_word_alloc(mdoc, n->line, n->pos, "~"); 1542 break; 1543 default: 1544 abort(); 1545 } 1546 mdoc->last->flags |= NODE_NOSRC; 1547 mdoc->last = n; 1548 } 1549 1550 static void 1551 post_at(POST_ARGS) 1552 { 1553 struct roff_node *n, *nch; 1554 const char *att; 1555 1556 n = mdoc->last; 1557 nch = n->child; 1558 1559 /* 1560 * If we have a child, look it up in the standard keys. If a 1561 * key exist, use that instead of the child; if it doesn't, 1562 * prefix "AT&T UNIX " to the existing data. 1563 */ 1564 1565 att = NULL; 1566 if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1567 mandoc_msg(MANDOCERR_AT_BAD, 1568 nch->line, nch->pos, "At %s", nch->string); 1569 1570 mdoc->next = ROFF_NEXT_CHILD; 1571 if (att != NULL) { 1572 roff_word_alloc(mdoc, nch->line, nch->pos, att); 1573 nch->flags |= NODE_NOPRT; 1574 } else 1575 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 1576 mdoc->last->flags |= NODE_NOSRC; 1577 mdoc->last = n; 1578 } 1579 1580 static void 1581 post_an(POST_ARGS) 1582 { 1583 struct roff_node *np, *nch; 1584 1585 post_an_norm(mdoc); 1586 1587 np = mdoc->last; 1588 nch = np->child; 1589 if (np->norm->An.auth == AUTH__NONE) { 1590 if (nch == NULL) 1591 mandoc_msg(MANDOCERR_MACRO_EMPTY, 1592 np->line, np->pos, "An"); 1593 else 1594 post_delim_nb(mdoc); 1595 } else if (nch != NULL) 1596 mandoc_msg(MANDOCERR_ARG_EXCESS, 1597 nch->line, nch->pos, "An ... %s", nch->string); 1598 } 1599 1600 static void 1601 post_em(POST_ARGS) 1602 { 1603 post_tag(mdoc); 1604 tag_put(NULL, TAG_FALLBACK, mdoc->last); 1605 } 1606 1607 static void 1608 post_en(POST_ARGS) 1609 { 1610 post_obsolete(mdoc); 1611 if (mdoc->last->type == ROFFT_BLOCK) 1612 mdoc->last->norm->Es = mdoc->last_es; 1613 } 1614 1615 static void 1616 post_er(POST_ARGS) 1617 { 1618 struct roff_node *n; 1619 1620 n = mdoc->last; 1621 if (n->sec == SEC_ERRORS && 1622 (n->parent->tok == MDOC_It || 1623 (n->parent->tok == MDOC_Bq && 1624 n->parent->parent->parent->tok == MDOC_It))) 1625 tag_put(NULL, TAG_STRONG, n); 1626 post_delim_nb(mdoc); 1627 } 1628 1629 static void 1630 post_tag(POST_ARGS) 1631 { 1632 struct roff_node *n; 1633 1634 n = mdoc->last; 1635 if ((n->prev == NULL || 1636 (n->prev->type == ROFFT_TEXT && 1637 strcmp(n->prev->string, "|") == 0)) && 1638 (n->parent->tok == MDOC_It || 1639 (n->parent->tok == MDOC_Xo && 1640 n->parent->parent->prev == NULL && 1641 n->parent->parent->parent->tok == MDOC_It))) 1642 tag_put(NULL, TAG_STRONG, n); 1643 post_delim_nb(mdoc); 1644 } 1645 1646 static void 1647 post_es(POST_ARGS) 1648 { 1649 post_obsolete(mdoc); 1650 mdoc->last_es = mdoc->last; 1651 } 1652 1653 static void 1654 post_fl(POST_ARGS) 1655 { 1656 struct roff_node *n; 1657 char *cp; 1658 1659 /* 1660 * Transform ".Fl Fl long" to ".Fl \-long", 1661 * resulting for example in better HTML output. 1662 */ 1663 1664 n = mdoc->last; 1665 if (n->prev != NULL && n->prev->tok == MDOC_Fl && 1666 n->prev->child == NULL && n->child != NULL && 1667 (n->flags & NODE_LINE) == 0) { 1668 mandoc_asprintf(&cp, "\\-%s", n->child->string); 1669 free(n->child->string); 1670 n->child->string = cp; 1671 roff_node_delete(mdoc, n->prev); 1672 } 1673 post_tag(mdoc); 1674 } 1675 1676 static void 1677 post_xx(POST_ARGS) 1678 { 1679 struct roff_node *n; 1680 const char *os; 1681 char *v; 1682 1683 post_delim_nb(mdoc); 1684 1685 n = mdoc->last; 1686 switch (n->tok) { 1687 case MDOC_Bsx: 1688 os = "BSD/OS"; 1689 break; 1690 case MDOC_Dx: 1691 os = "DragonFly"; 1692 break; 1693 case MDOC_Fx: 1694 os = "FreeBSD"; 1695 break; 1696 case MDOC_Nx: 1697 os = "NetBSD"; 1698 if (n->child == NULL) 1699 break; 1700 v = n->child->string; 1701 if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || 1702 v[2] < '0' || v[2] > '9' || 1703 v[3] < 'a' || v[3] > 'z' || v[4] != '\0') 1704 break; 1705 n->child->flags |= NODE_NOPRT; 1706 mdoc->next = ROFF_NEXT_CHILD; 1707 roff_word_alloc(mdoc, n->child->line, n->child->pos, v); 1708 v = mdoc->last->string; 1709 v[3] = toupper((unsigned char)v[3]); 1710 mdoc->last->flags |= NODE_NOSRC; 1711 mdoc->last = n; 1712 break; 1713 case MDOC_Ox: 1714 os = "OpenBSD"; 1715 break; 1716 case MDOC_Ux: 1717 os = "Unix"; 1718 break; 1719 default: 1720 abort(); 1721 } 1722 mdoc->next = ROFF_NEXT_CHILD; 1723 roff_word_alloc(mdoc, n->line, n->pos, os); 1724 mdoc->last->flags |= NODE_NOSRC; 1725 mdoc->last = n; 1726 } 1727 1728 static void 1729 post_it(POST_ARGS) 1730 { 1731 struct roff_node *nbl, *nit, *nch; 1732 int i, cols; 1733 enum mdoc_list lt; 1734 1735 post_prevpar(mdoc); 1736 1737 nit = mdoc->last; 1738 if (nit->type != ROFFT_BLOCK) 1739 return; 1740 1741 nbl = nit->parent->parent; 1742 lt = nbl->norm->Bl.type; 1743 1744 switch (lt) { 1745 case LIST_tag: 1746 case LIST_hang: 1747 case LIST_ohang: 1748 case LIST_inset: 1749 case LIST_diag: 1750 if (nit->head->child == NULL) 1751 mandoc_msg(MANDOCERR_IT_NOHEAD, 1752 nit->line, nit->pos, "Bl -%s It", 1753 mdoc_argnames[nbl->args->argv[0].arg]); 1754 break; 1755 case LIST_bullet: 1756 case LIST_dash: 1757 case LIST_enum: 1758 case LIST_hyphen: 1759 if (nit->body == NULL || nit->body->child == NULL) 1760 mandoc_msg(MANDOCERR_IT_NOBODY, 1761 nit->line, nit->pos, "Bl -%s It", 1762 mdoc_argnames[nbl->args->argv[0].arg]); 1763 /* FALLTHROUGH */ 1764 case LIST_item: 1765 if ((nch = nit->head->child) != NULL) 1766 mandoc_msg(MANDOCERR_ARG_SKIP, 1767 nit->line, nit->pos, "It %s", 1768 nch->type == ROFFT_TEXT ? nch->string : 1769 roff_name[nch->tok]); 1770 break; 1771 case LIST_column: 1772 cols = (int)nbl->norm->Bl.ncols; 1773 1774 assert(nit->head->child == NULL); 1775 1776 if (nit->head->next->child == NULL && 1777 nit->head->next->next == NULL) { 1778 mandoc_msg(MANDOCERR_MACRO_EMPTY, 1779 nit->line, nit->pos, "It"); 1780 roff_node_delete(mdoc, nit); 1781 break; 1782 } 1783 1784 i = 0; 1785 for (nch = nit->child; nch != NULL; nch = nch->next) { 1786 if (nch->type != ROFFT_BODY) 1787 continue; 1788 if (i++ && nch->flags & NODE_LINE) 1789 mandoc_msg(MANDOCERR_TA_LINE, 1790 nch->line, nch->pos, "Ta"); 1791 } 1792 if (i < cols || i > cols + 1) 1793 mandoc_msg(MANDOCERR_BL_COL, nit->line, nit->pos, 1794 "%d columns, %d cells", cols, i); 1795 else if (nit->head->next->child != NULL && 1796 nit->head->next->child->flags & NODE_LINE) 1797 mandoc_msg(MANDOCERR_IT_NOARG, 1798 nit->line, nit->pos, "Bl -column It"); 1799 break; 1800 default: 1801 abort(); 1802 } 1803 } 1804 1805 static void 1806 post_bl_block(POST_ARGS) 1807 { 1808 struct roff_node *n, *ni, *nc; 1809 1810 post_prevpar(mdoc); 1811 1812 n = mdoc->last; 1813 for (ni = n->body->child; ni != NULL; ni = ni->next) { 1814 if (ni->body == NULL) 1815 continue; 1816 nc = ni->body->last; 1817 while (nc != NULL) { 1818 switch (nc->tok) { 1819 case MDOC_Pp: 1820 case ROFF_br: 1821 break; 1822 default: 1823 nc = NULL; 1824 continue; 1825 } 1826 if (ni->next == NULL) { 1827 mandoc_msg(MANDOCERR_PAR_MOVE, nc->line, 1828 nc->pos, "%s", roff_name[nc->tok]); 1829 roff_node_relink(mdoc, nc); 1830 } else if (n->norm->Bl.comp == 0 && 1831 n->norm->Bl.type != LIST_column) { 1832 mandoc_msg(MANDOCERR_PAR_SKIP, 1833 nc->line, nc->pos, 1834 "%s before It", roff_name[nc->tok]); 1835 roff_node_delete(mdoc, nc); 1836 } else 1837 break; 1838 nc = ni->body->last; 1839 } 1840 } 1841 } 1842 1843 /* 1844 * If "in" begins with a dot, a word, and whitespace, return a dynamically 1845 * allocated copy of "in" that skips all of those. Otherwise, return NULL. 1846 * 1847 * This is a partial workaround for the TODO list item beginning with: 1848 * - When the -width string contains macros, the macros must be rendered 1849 */ 1850 static char * 1851 skip_leading_dot_word(const char *in) 1852 { 1853 const char *iter = in; 1854 const char *space; 1855 1856 if (*iter != '.') 1857 return NULL; 1858 iter++; 1859 1860 while (*iter != '\0' && !isspace(*iter)) 1861 iter++; 1862 /* 1863 * If the dot was followed by space or NUL, 1864 * do not skip anything. 1865 */ 1866 if (iter == in + 1) 1867 return NULL; 1868 1869 space = iter; 1870 while (isspace(*iter)) 1871 iter++; 1872 /* 1873 * If the word was not followed by space, 1874 * do not skip anything. 1875 */ 1876 if (iter == space) 1877 return NULL; 1878 1879 return strdup(iter); 1880 } 1881 1882 /* 1883 * If the argument of -offset or -width is a macro, 1884 * replace it with the associated default width. 1885 */ 1886 static void 1887 rewrite_macro2len(struct roff_man *mdoc, char **arg) 1888 { 1889 size_t width; 1890 enum roff_tok tok; 1891 char *newarg; 1892 1893 newarg = NULL; 1894 if (*arg == NULL) 1895 return; 1896 else if ( ! strcmp(*arg, "Ds")) 1897 width = 6; 1898 else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) != TOKEN_NONE) 1899 width = macro2len(tok); 1900 else if ((newarg = skip_leading_dot_word(*arg)) == NULL) 1901 return; 1902 1903 free(*arg); 1904 if (newarg != NULL) 1905 *arg = newarg; 1906 else 1907 mandoc_asprintf(arg, "%zun", width); 1908 } 1909 1910 static void 1911 post_bl_head(POST_ARGS) 1912 { 1913 struct roff_node *nbl, *nh, *nch, *nnext; 1914 struct mdoc_argv *argv; 1915 int i, j; 1916 1917 post_bl_norm(mdoc); 1918 1919 nh = mdoc->last; 1920 if (nh->norm->Bl.type != LIST_column) { 1921 if ((nch = nh->child) == NULL) 1922 return; 1923 mandoc_msg(MANDOCERR_ARG_EXCESS, 1924 nch->line, nch->pos, "Bl ... %s", nch->string); 1925 while (nch != NULL) { 1926 roff_node_delete(mdoc, nch); 1927 nch = nh->child; 1928 } 1929 return; 1930 } 1931 1932 /* 1933 * Append old-style lists, where the column width specifiers 1934 * trail as macro parameters, to the new-style ("normal-form") 1935 * lists where they're argument values following -column. 1936 */ 1937 1938 if (nh->child == NULL) 1939 return; 1940 1941 nbl = nh->parent; 1942 for (j = 0; j < (int)nbl->args->argc; j++) 1943 if (nbl->args->argv[j].arg == MDOC_Column) 1944 break; 1945 1946 assert(j < (int)nbl->args->argc); 1947 1948 /* 1949 * Accommodate for new-style groff column syntax. Shuffle the 1950 * child nodes, all of which must be TEXT, as arguments for the 1951 * column field. Then, delete the head children. 1952 */ 1953 1954 argv = nbl->args->argv + j; 1955 i = argv->sz; 1956 for (nch = nh->child; nch != NULL; nch = nch->next) 1957 argv->sz++; 1958 argv->value = mandoc_reallocarray(argv->value, 1959 argv->sz, sizeof(char *)); 1960 1961 nh->norm->Bl.ncols = argv->sz; 1962 nh->norm->Bl.cols = (void *)argv->value; 1963 1964 for (nch = nh->child; nch != NULL; nch = nnext) { 1965 argv->value[i++] = nch->string; 1966 nch->string = NULL; 1967 nnext = nch->next; 1968 roff_node_delete(NULL, nch); 1969 } 1970 nh->child = NULL; 1971 } 1972 1973 static void 1974 post_bl(POST_ARGS) 1975 { 1976 struct roff_node *nbody; /* of the Bl */ 1977 struct roff_node *nchild, *nnext; /* of the Bl body */ 1978 const char *prev_Er; 1979 int order; 1980 1981 nbody = mdoc->last; 1982 switch (nbody->type) { 1983 case ROFFT_BLOCK: 1984 post_bl_block(mdoc); 1985 return; 1986 case ROFFT_HEAD: 1987 post_bl_head(mdoc); 1988 return; 1989 case ROFFT_BODY: 1990 break; 1991 default: 1992 return; 1993 } 1994 if (nbody->end != ENDBODY_NOT) 1995 return; 1996 1997 /* 1998 * Up to the first item, move nodes before the list, 1999 * but leave transparent nodes where they are 2000 * if they precede an item. 2001 * The next non-transparent node is kept in nchild. 2002 * It only needs to be updated after a non-transparent 2003 * node was moved out, and at the very beginning 2004 * when no node at all was moved yet. 2005 */ 2006 2007 nchild = mdoc->last; 2008 for (;;) { 2009 if (nchild == mdoc->last) 2010 nchild = roff_node_child(nbody); 2011 if (nchild == NULL) { 2012 mdoc->last = nbody; 2013 mandoc_msg(MANDOCERR_BLK_EMPTY, 2014 nbody->line, nbody->pos, "Bl"); 2015 return; 2016 } 2017 if (nchild->tok == MDOC_It) { 2018 mdoc->last = nbody; 2019 break; 2020 } 2021 mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, 2022 nbody->child->pos, "%s", roff_name[nbody->child->tok]); 2023 if (nbody->parent->prev == NULL) { 2024 mdoc->last = nbody->parent->parent; 2025 mdoc->next = ROFF_NEXT_CHILD; 2026 } else { 2027 mdoc->last = nbody->parent->prev; 2028 mdoc->next = ROFF_NEXT_SIBLING; 2029 } 2030 roff_node_relink(mdoc, nbody->child); 2031 } 2032 2033 /* 2034 * We have reached the first item, 2035 * so moving nodes out is no longer possible. 2036 * But in .Bl -column, the first rows may be implicit, 2037 * that is, they may not start with .It macros. 2038 * Such rows may be followed by nodes generated on the 2039 * roff level, for example .TS. 2040 * Wrap such roff nodes into an implicit row. 2041 */ 2042 2043 while (nchild != NULL) { 2044 if (nchild->tok == MDOC_It) { 2045 nchild = roff_node_next(nchild); 2046 continue; 2047 } 2048 nnext = nchild->next; 2049 mdoc->last = nchild->prev; 2050 mdoc->next = ROFF_NEXT_SIBLING; 2051 roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 2052 roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 2053 mdoc->next = ROFF_NEXT_SIBLING; 2054 roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); 2055 while (nchild->tok != MDOC_It) { 2056 roff_node_relink(mdoc, nchild); 2057 if (nnext == NULL) 2058 break; 2059 nchild = nnext; 2060 nnext = nchild->next; 2061 mdoc->next = ROFF_NEXT_SIBLING; 2062 } 2063 mdoc->last = nbody; 2064 } 2065 2066 if (mdoc->meta.os_e != MANDOC_OS_NETBSD) 2067 return; 2068 2069 prev_Er = NULL; 2070 for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { 2071 if (nchild->tok != MDOC_It) 2072 continue; 2073 if ((nnext = nchild->head->child) == NULL) 2074 continue; 2075 if (nnext->type == ROFFT_BLOCK) 2076 nnext = nnext->body->child; 2077 if (nnext == NULL || nnext->tok != MDOC_Er) 2078 continue; 2079 nnext = nnext->child; 2080 if (prev_Er != NULL) { 2081 order = strcmp(prev_Er, nnext->string); 2082 if (order > 0) 2083 mandoc_msg(MANDOCERR_ER_ORDER, 2084 nnext->line, nnext->pos, 2085 "Er %s %s (NetBSD)", 2086 prev_Er, nnext->string); 2087 else if (order == 0) 2088 mandoc_msg(MANDOCERR_ER_REP, 2089 nnext->line, nnext->pos, 2090 "Er %s (NetBSD)", prev_Er); 2091 } 2092 prev_Er = nnext->string; 2093 } 2094 } 2095 2096 static void 2097 post_bk(POST_ARGS) 2098 { 2099 struct roff_node *n; 2100 2101 n = mdoc->last; 2102 2103 if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 2104 mandoc_msg(MANDOCERR_BLK_EMPTY, n->line, n->pos, "Bk"); 2105 roff_node_delete(mdoc, n); 2106 } 2107 } 2108 2109 static void 2110 post_sm(POST_ARGS) 2111 { 2112 struct roff_node *nch; 2113 2114 nch = mdoc->last->child; 2115 2116 if (nch == NULL) { 2117 mdoc->flags ^= MDOC_SMOFF; 2118 return; 2119 } 2120 2121 assert(nch->type == ROFFT_TEXT); 2122 2123 if ( ! strcmp(nch->string, "on")) { 2124 mdoc->flags &= ~MDOC_SMOFF; 2125 return; 2126 } 2127 if ( ! strcmp(nch->string, "off")) { 2128 mdoc->flags |= MDOC_SMOFF; 2129 return; 2130 } 2131 2132 mandoc_msg(MANDOCERR_SM_BAD, nch->line, nch->pos, 2133 "%s %s", roff_name[mdoc->last->tok], nch->string); 2134 roff_node_relink(mdoc, nch); 2135 return; 2136 } 2137 2138 static void 2139 post_root(POST_ARGS) 2140 { 2141 struct roff_node *n; 2142 2143 /* Add missing prologue data. */ 2144 2145 if (mdoc->meta.date == NULL) 2146 mdoc->meta.date = mandoc_normdate(NULL, NULL); 2147 2148 if (mdoc->meta.title == NULL) { 2149 mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); 2150 mdoc->meta.title = mandoc_strdup("UNTITLED"); 2151 } 2152 2153 if (mdoc->meta.vol == NULL) 2154 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2155 2156 if (mdoc->meta.os == NULL) { 2157 mandoc_msg(MANDOCERR_OS_MISSING, 0, 0, NULL); 2158 mdoc->meta.os = mandoc_strdup(""); 2159 } else if (mdoc->meta.os_e && 2160 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) 2161 mandoc_msg(MANDOCERR_RCS_MISSING, 0, 0, 2162 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2163 "(OpenBSD)" : "(NetBSD)"); 2164 2165 if (mdoc->meta.arch != NULL && 2166 arch_valid(mdoc->meta.arch, mdoc->meta.os_e) == 0) { 2167 n = mdoc->meta.first->child; 2168 while (n->tok != MDOC_Dt || 2169 n->child == NULL || 2170 n->child->next == NULL || 2171 n->child->next->next == NULL) 2172 n = n->next; 2173 n = n->child->next->next; 2174 mandoc_msg(MANDOCERR_ARCH_BAD, n->line, n->pos, 2175 "Dt ... %s %s", mdoc->meta.arch, 2176 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 2177 "(OpenBSD)" : "(NetBSD)"); 2178 } 2179 2180 /* Check that we begin with a proper `Sh'. */ 2181 2182 n = mdoc->meta.first->child; 2183 while (n != NULL && 2184 (n->type == ROFFT_COMMENT || 2185 (n->tok >= MDOC_Dd && 2186 mdoc_macro(n->tok)->flags & MDOC_PROLOGUE))) 2187 n = n->next; 2188 2189 if (n == NULL) 2190 mandoc_msg(MANDOCERR_DOC_EMPTY, 0, 0, NULL); 2191 else if (n->tok != MDOC_Sh) 2192 mandoc_msg(MANDOCERR_SEC_BEFORE, n->line, n->pos, 2193 "%s", roff_name[n->tok]); 2194 } 2195 2196 static void 2197 post_rs(POST_ARGS) 2198 { 2199 struct roff_node *np, *nch, *next, *prev; 2200 int i, j; 2201 2202 np = mdoc->last; 2203 2204 if (np->type != ROFFT_BODY) 2205 return; 2206 2207 if (np->child == NULL) { 2208 mandoc_msg(MANDOCERR_RS_EMPTY, np->line, np->pos, "Rs"); 2209 return; 2210 } 2211 2212 /* 2213 * The full `Rs' block needs special handling to order the 2214 * sub-elements according to `rsord'. Pick through each element 2215 * and correctly order it. This is an insertion sort. 2216 */ 2217 2218 next = NULL; 2219 for (nch = np->child->next; nch != NULL; nch = next) { 2220 /* Determine order number of this child. */ 2221 for (i = 0; i < RSORD_MAX; i++) 2222 if (rsord[i] == nch->tok) 2223 break; 2224 2225 if (i == RSORD_MAX) { 2226 mandoc_msg(MANDOCERR_RS_BAD, nch->line, nch->pos, 2227 "%s", roff_name[nch->tok]); 2228 i = -1; 2229 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 2230 np->norm->Rs.quote_T++; 2231 2232 /* 2233 * Remove this child from the chain. This somewhat 2234 * repeats roff_node_unlink(), but since we're 2235 * just re-ordering, there's no need for the 2236 * full unlink process. 2237 */ 2238 2239 if ((next = nch->next) != NULL) 2240 next->prev = nch->prev; 2241 2242 if ((prev = nch->prev) != NULL) 2243 prev->next = nch->next; 2244 2245 nch->prev = nch->next = NULL; 2246 2247 /* 2248 * Scan back until we reach a node that's 2249 * to be ordered before this child. 2250 */ 2251 2252 for ( ; prev ; prev = prev->prev) { 2253 /* Determine order of `prev'. */ 2254 for (j = 0; j < RSORD_MAX; j++) 2255 if (rsord[j] == prev->tok) 2256 break; 2257 if (j == RSORD_MAX) 2258 j = -1; 2259 2260 if (j <= i) 2261 break; 2262 } 2263 2264 /* 2265 * Set this child back into its correct place 2266 * in front of the `prev' node. 2267 */ 2268 2269 nch->prev = prev; 2270 2271 if (prev == NULL) { 2272 np->child->prev = nch; 2273 nch->next = np->child; 2274 np->child = nch; 2275 } else { 2276 if (prev->next) 2277 prev->next->prev = nch; 2278 nch->next = prev->next; 2279 prev->next = nch; 2280 } 2281 } 2282 } 2283 2284 /* 2285 * For some arguments of some macros, 2286 * convert all breakable hyphens into ASCII_HYPH. 2287 */ 2288 static void 2289 post_hyph(POST_ARGS) 2290 { 2291 struct roff_node *n, *nch; 2292 char *cp; 2293 2294 n = mdoc->last; 2295 for (nch = n->child; nch != NULL; nch = nch->next) { 2296 if (nch->type != ROFFT_TEXT) 2297 continue; 2298 cp = nch->string; 2299 if (*cp == '\0') 2300 continue; 2301 while (*(++cp) != '\0') 2302 if (*cp == '-' && 2303 isalpha((unsigned char)cp[-1]) && 2304 isalpha((unsigned char)cp[1])) { 2305 if (n->tag == NULL && n->flags & NODE_ID) 2306 n->tag = mandoc_strdup(nch->string); 2307 *cp = ASCII_HYPH; 2308 } 2309 } 2310 } 2311 2312 static void 2313 post_ns(POST_ARGS) 2314 { 2315 struct roff_node *n; 2316 2317 n = mdoc->last; 2318 if (n->flags & NODE_LINE || 2319 (n->next != NULL && n->next->flags & NODE_DELIMC)) 2320 mandoc_msg(MANDOCERR_NS_SKIP, n->line, n->pos, NULL); 2321 } 2322 2323 static void 2324 post_sx(POST_ARGS) 2325 { 2326 post_delim(mdoc); 2327 post_hyph(mdoc); 2328 } 2329 2330 static void 2331 post_sh(POST_ARGS) 2332 { 2333 post_section(mdoc); 2334 2335 switch (mdoc->last->type) { 2336 case ROFFT_HEAD: 2337 post_sh_head(mdoc); 2338 break; 2339 case ROFFT_BODY: 2340 switch (mdoc->lastsec) { 2341 case SEC_NAME: 2342 post_sh_name(mdoc); 2343 break; 2344 case SEC_SEE_ALSO: 2345 post_sh_see_also(mdoc); 2346 break; 2347 case SEC_AUTHORS: 2348 post_sh_authors(mdoc); 2349 break; 2350 default: 2351 break; 2352 } 2353 break; 2354 default: 2355 break; 2356 } 2357 } 2358 2359 static void 2360 post_sh_name(POST_ARGS) 2361 { 2362 struct roff_node *n; 2363 int hasnm, hasnd; 2364 2365 hasnm = hasnd = 0; 2366 2367 for (n = mdoc->last->child; n != NULL; n = n->next) { 2368 switch (n->tok) { 2369 case MDOC_Nm: 2370 if (hasnm && n->child != NULL) 2371 mandoc_msg(MANDOCERR_NAMESEC_PUNCT, 2372 n->line, n->pos, 2373 "Nm %s", n->child->string); 2374 hasnm = 1; 2375 continue; 2376 case MDOC_Nd: 2377 hasnd = 1; 2378 if (n->next != NULL) 2379 mandoc_msg(MANDOCERR_NAMESEC_ND, 2380 n->line, n->pos, NULL); 2381 break; 2382 case TOKEN_NONE: 2383 if (n->type == ROFFT_TEXT && 2384 n->string[0] == ',' && n->string[1] == '\0' && 2385 n->next != NULL && n->next->tok == MDOC_Nm) { 2386 n = n->next; 2387 continue; 2388 } 2389 /* FALLTHROUGH */ 2390 default: 2391 mandoc_msg(MANDOCERR_NAMESEC_BAD, 2392 n->line, n->pos, "%s", roff_name[n->tok]); 2393 continue; 2394 } 2395 break; 2396 } 2397 2398 if ( ! hasnm) 2399 mandoc_msg(MANDOCERR_NAMESEC_NONM, 2400 mdoc->last->line, mdoc->last->pos, NULL); 2401 if ( ! hasnd) 2402 mandoc_msg(MANDOCERR_NAMESEC_NOND, 2403 mdoc->last->line, mdoc->last->pos, NULL); 2404 } 2405 2406 static void 2407 post_sh_see_also(POST_ARGS) 2408 { 2409 const struct roff_node *n; 2410 const char *name, *sec; 2411 const char *lastname, *lastsec, *lastpunct; 2412 int cmp; 2413 2414 n = mdoc->last->child; 2415 lastname = lastsec = lastpunct = NULL; 2416 while (n != NULL) { 2417 if (n->tok != MDOC_Xr || 2418 n->child == NULL || 2419 n->child->next == NULL) 2420 break; 2421 2422 /* Process one .Xr node. */ 2423 2424 name = n->child->string; 2425 sec = n->child->next->string; 2426 if (lastsec != NULL) { 2427 if (lastpunct[0] != ',' || lastpunct[1] != '\0') 2428 mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2429 n->pos, "%s before %s(%s)", 2430 lastpunct, name, sec); 2431 cmp = strcmp(lastsec, sec); 2432 if (cmp > 0) 2433 mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2434 n->pos, "%s(%s) after %s(%s)", 2435 name, sec, lastname, lastsec); 2436 else if (cmp == 0 && 2437 strcasecmp(lastname, name) > 0) 2438 mandoc_msg(MANDOCERR_XR_ORDER, n->line, 2439 n->pos, "%s after %s", name, lastname); 2440 } 2441 lastname = name; 2442 lastsec = sec; 2443 2444 /* Process the following node. */ 2445 2446 n = n->next; 2447 if (n == NULL) 2448 break; 2449 if (n->tok == MDOC_Xr) { 2450 lastpunct = "none"; 2451 continue; 2452 } 2453 if (n->type != ROFFT_TEXT) 2454 break; 2455 for (name = n->string; *name != '\0'; name++) 2456 if (isalpha((const unsigned char)*name)) 2457 return; 2458 lastpunct = n->string; 2459 if (n->next == NULL || n->next->tok == MDOC_Rs) 2460 mandoc_msg(MANDOCERR_XR_PUNCT, n->line, 2461 n->pos, "%s after %s(%s)", 2462 lastpunct, lastname, lastsec); 2463 n = n->next; 2464 } 2465 } 2466 2467 static int 2468 child_an(const struct roff_node *n) 2469 { 2470 2471 for (n = n->child; n != NULL; n = n->next) 2472 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 2473 return 1; 2474 return 0; 2475 } 2476 2477 static void 2478 post_sh_authors(POST_ARGS) 2479 { 2480 2481 if ( ! child_an(mdoc->last)) 2482 mandoc_msg(MANDOCERR_AN_MISSING, 2483 mdoc->last->line, mdoc->last->pos, NULL); 2484 } 2485 2486 /* 2487 * Return an upper bound for the string distance (allowing 2488 * transpositions). Not a full Levenshtein implementation 2489 * because Levenshtein is quadratic in the string length 2490 * and this function is called for every standard name, 2491 * so the check for each custom name would be cubic. 2492 * The following crude heuristics is linear, resulting 2493 * in quadratic behaviour for checking one custom name, 2494 * which does not cause measurable slowdown. 2495 */ 2496 static int 2497 similar(const char *s1, const char *s2) 2498 { 2499 const int maxdist = 3; 2500 int dist = 0; 2501 2502 while (s1[0] != '\0' && s2[0] != '\0') { 2503 if (s1[0] == s2[0]) { 2504 s1++; 2505 s2++; 2506 continue; 2507 } 2508 if (++dist > maxdist) 2509 return INT_MAX; 2510 if (s1[1] == s2[1]) { /* replacement */ 2511 s1++; 2512 s2++; 2513 } else if (s1[0] == s2[1] && s1[1] == s2[0]) { 2514 s1 += 2; /* transposition */ 2515 s2 += 2; 2516 } else if (s1[0] == s2[1]) /* insertion */ 2517 s2++; 2518 else if (s1[1] == s2[0]) /* deletion */ 2519 s1++; 2520 else 2521 return INT_MAX; 2522 } 2523 dist += strlen(s1) + strlen(s2); 2524 return dist > maxdist ? INT_MAX : dist; 2525 } 2526 2527 static void 2528 post_sh_head(POST_ARGS) 2529 { 2530 struct roff_node *nch; 2531 const char *goodsec; 2532 const char *const *testsec; 2533 int dist, mindist; 2534 enum roff_sec sec; 2535 2536 /* 2537 * Process a new section. Sections are either "named" or 2538 * "custom". Custom sections are user-defined, while named ones 2539 * follow a conventional order and may only appear in certain 2540 * manual sections. 2541 */ 2542 2543 sec = mdoc->last->sec; 2544 2545 /* The NAME should be first. */ 2546 2547 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 2548 mandoc_msg(MANDOCERR_NAMESEC_FIRST, 2549 mdoc->last->line, mdoc->last->pos, "Sh %s", 2550 sec != SEC_CUSTOM ? secnames[sec] : 2551 (nch = mdoc->last->child) == NULL ? "" : 2552 nch->type == ROFFT_TEXT ? nch->string : 2553 roff_name[nch->tok]); 2554 2555 /* The SYNOPSIS gets special attention in other areas. */ 2556 2557 if (sec == SEC_SYNOPSIS) { 2558 roff_setreg(mdoc->roff, "nS", 1, '='); 2559 mdoc->flags |= MDOC_SYNOPSIS; 2560 } else { 2561 roff_setreg(mdoc->roff, "nS", 0, '='); 2562 mdoc->flags &= ~MDOC_SYNOPSIS; 2563 } 2564 if (sec == SEC_DESCRIPTION) 2565 fn_prio = TAG_STRONG; 2566 2567 /* Mark our last section. */ 2568 2569 mdoc->lastsec = sec; 2570 2571 /* We don't care about custom sections after this. */ 2572 2573 if (sec == SEC_CUSTOM) { 2574 if ((nch = mdoc->last->child) == NULL || 2575 nch->type != ROFFT_TEXT || nch->next != NULL) 2576 return; 2577 goodsec = NULL; 2578 mindist = INT_MAX; 2579 for (testsec = secnames + 1; *testsec != NULL; testsec++) { 2580 dist = similar(nch->string, *testsec); 2581 if (dist < mindist) { 2582 goodsec = *testsec; 2583 mindist = dist; 2584 } 2585 } 2586 if (goodsec != NULL) 2587 mandoc_msg(MANDOCERR_SEC_TYPO, nch->line, nch->pos, 2588 "Sh %s instead of %s", nch->string, goodsec); 2589 return; 2590 } 2591 2592 /* 2593 * Check whether our non-custom section is being repeated or is 2594 * out of order. 2595 */ 2596 2597 if (sec == mdoc->lastnamed) 2598 mandoc_msg(MANDOCERR_SEC_REP, mdoc->last->line, 2599 mdoc->last->pos, "Sh %s", secnames[sec]); 2600 2601 if (sec < mdoc->lastnamed) 2602 mandoc_msg(MANDOCERR_SEC_ORDER, mdoc->last->line, 2603 mdoc->last->pos, "Sh %s", secnames[sec]); 2604 2605 /* Mark the last named section. */ 2606 2607 mdoc->lastnamed = sec; 2608 2609 /* Check particular section/manual conventions. */ 2610 2611 if (mdoc->meta.msec == NULL) 2612 return; 2613 2614 goodsec = NULL; 2615 switch (sec) { 2616 case SEC_ERRORS: 2617 if (*mdoc->meta.msec == '4') 2618 break; 2619 goodsec = "2, 3, 4, 9"; 2620 /* FALLTHROUGH */ 2621 case SEC_RETURN_VALUES: 2622 case SEC_LIBRARY: 2623 if (*mdoc->meta.msec == '2') 2624 break; 2625 if (*mdoc->meta.msec == '3') 2626 break; 2627 if (NULL == goodsec) 2628 goodsec = "2, 3, 9"; 2629 /* FALLTHROUGH */ 2630 case SEC_CONTEXT: 2631 if (*mdoc->meta.msec == '9') 2632 break; 2633 if (NULL == goodsec) 2634 goodsec = "9"; 2635 mandoc_msg(MANDOCERR_SEC_MSEC, 2636 mdoc->last->line, mdoc->last->pos, 2637 "Sh %s for %s only", secnames[sec], goodsec); 2638 break; 2639 default: 2640 break; 2641 } 2642 } 2643 2644 static void 2645 post_xr(POST_ARGS) 2646 { 2647 struct roff_node *n, *nch; 2648 2649 n = mdoc->last; 2650 nch = n->child; 2651 if (nch->next == NULL) { 2652 mandoc_msg(MANDOCERR_XR_NOSEC, 2653 n->line, n->pos, "Xr %s", nch->string); 2654 } else { 2655 assert(nch->next == n->last); 2656 if(mandoc_xr_add(nch->next->string, nch->string, 2657 nch->line, nch->pos)) 2658 mandoc_msg(MANDOCERR_XR_SELF, 2659 nch->line, nch->pos, "Xr %s %s", 2660 nch->string, nch->next->string); 2661 } 2662 post_delim_nb(mdoc); 2663 } 2664 2665 static void 2666 post_section(POST_ARGS) 2667 { 2668 struct roff_node *n, *nch; 2669 char *cp, *tag; 2670 2671 n = mdoc->last; 2672 switch (n->type) { 2673 case ROFFT_BLOCK: 2674 post_prevpar(mdoc); 2675 return; 2676 case ROFFT_HEAD: 2677 tag = NULL; 2678 deroff(&tag, n); 2679 if (tag != NULL) { 2680 for (cp = tag; *cp != '\0'; cp++) 2681 if (*cp == ' ') 2682 *cp = '_'; 2683 if ((nch = n->child) != NULL && 2684 nch->type == ROFFT_TEXT && 2685 strcmp(nch->string, tag) == 0) 2686 tag_put(NULL, TAG_STRONG, n); 2687 else 2688 tag_put(tag, TAG_FALLBACK, n); 2689 free(tag); 2690 } 2691 post_delim(mdoc); 2692 post_hyph(mdoc); 2693 return; 2694 case ROFFT_BODY: 2695 break; 2696 default: 2697 return; 2698 } 2699 if ((nch = n->child) != NULL && 2700 (nch->tok == MDOC_Pp || nch->tok == ROFF_br || 2701 nch->tok == ROFF_sp)) { 2702 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, 2703 "%s after %s", roff_name[nch->tok], 2704 roff_name[n->tok]); 2705 roff_node_delete(mdoc, nch); 2706 } 2707 if ((nch = n->last) != NULL && 2708 (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { 2709 mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, 2710 "%s at the end of %s", roff_name[nch->tok], 2711 roff_name[n->tok]); 2712 roff_node_delete(mdoc, nch); 2713 } 2714 } 2715 2716 static void 2717 post_prevpar(POST_ARGS) 2718 { 2719 struct roff_node *n, *np; 2720 2721 n = mdoc->last; 2722 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 2723 return; 2724 if ((np = roff_node_prev(n)) == NULL) 2725 return; 2726 2727 /* 2728 * Don't allow `Pp' prior to a paragraph-type 2729 * block: `Pp' or non-compact `Bd' or `Bl'. 2730 */ 2731 2732 if (np->tok != MDOC_Pp && np->tok != ROFF_br) 2733 return; 2734 if (n->tok == MDOC_Bl && n->norm->Bl.comp) 2735 return; 2736 if (n->tok == MDOC_Bd && n->norm->Bd.comp) 2737 return; 2738 if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 2739 return; 2740 2741 mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, 2742 "%s before %s", roff_name[np->tok], roff_name[n->tok]); 2743 roff_node_delete(mdoc, np); 2744 } 2745 2746 static void 2747 post_par(POST_ARGS) 2748 { 2749 struct roff_node *np; 2750 2751 fn_prio = TAG_STRONG; 2752 post_prevpar(mdoc); 2753 2754 np = mdoc->last; 2755 if (np->child != NULL) 2756 mandoc_msg(MANDOCERR_ARG_SKIP, np->line, np->pos, 2757 "%s %s", roff_name[np->tok], np->child->string); 2758 } 2759 2760 static void 2761 post_dd(POST_ARGS) 2762 { 2763 struct roff_node *n; 2764 2765 n = mdoc->last; 2766 n->flags |= NODE_NOPRT; 2767 2768 if (mdoc->meta.date != NULL) { 2769 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dd"); 2770 free(mdoc->meta.date); 2771 } else if (mdoc->flags & MDOC_PBODY) 2772 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Dd"); 2773 else if (mdoc->meta.title != NULL) 2774 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2775 n->line, n->pos, "Dd after Dt"); 2776 else if (mdoc->meta.os != NULL) 2777 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2778 n->line, n->pos, "Dd after Os"); 2779 2780 if (mdoc->quick) 2781 mdoc->meta.date = mandoc_strdup(""); 2782 else 2783 mdoc->meta.date = mandoc_normdate(n->child, n); 2784 } 2785 2786 static void 2787 post_dt(POST_ARGS) 2788 { 2789 struct roff_node *nn, *n; 2790 const char *cp; 2791 char *p; 2792 2793 n = mdoc->last; 2794 n->flags |= NODE_NOPRT; 2795 2796 if (mdoc->flags & MDOC_PBODY) { 2797 mandoc_msg(MANDOCERR_DT_LATE, n->line, n->pos, "Dt"); 2798 return; 2799 } 2800 2801 if (mdoc->meta.title != NULL) 2802 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Dt"); 2803 else if (mdoc->meta.os != NULL) 2804 mandoc_msg(MANDOCERR_PROLOG_ORDER, 2805 n->line, n->pos, "Dt after Os"); 2806 2807 free(mdoc->meta.title); 2808 free(mdoc->meta.msec); 2809 free(mdoc->meta.vol); 2810 free(mdoc->meta.arch); 2811 2812 mdoc->meta.title = NULL; 2813 mdoc->meta.msec = NULL; 2814 mdoc->meta.vol = NULL; 2815 mdoc->meta.arch = NULL; 2816 2817 /* Mandatory first argument: title. */ 2818 2819 nn = n->child; 2820 if (nn == NULL || *nn->string == '\0') { 2821 mandoc_msg(MANDOCERR_DT_NOTITLE, n->line, n->pos, "Dt"); 2822 mdoc->meta.title = mandoc_strdup("UNTITLED"); 2823 } else { 2824 mdoc->meta.title = mandoc_strdup(nn->string); 2825 2826 /* Check that all characters are uppercase. */ 2827 2828 for (p = nn->string; *p != '\0'; p++) 2829 if (islower((unsigned char)*p)) { 2830 mandoc_msg(MANDOCERR_TITLE_CASE, nn->line, 2831 nn->pos + (int)(p - nn->string), 2832 "Dt %s", nn->string); 2833 break; 2834 } 2835 } 2836 2837 /* Mandatory second argument: section. */ 2838 2839 if (nn != NULL) 2840 nn = nn->next; 2841 2842 if (nn == NULL) { 2843 mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos, 2844 "Dt %s", mdoc->meta.title); 2845 return; /* msec, vol, and arch remain NULL. */ 2846 } 2847 2848 mdoc->meta.msec = mandoc_strdup(nn->string); 2849 2850 /* Infer volume title from section number. */ 2851 2852 cp = mandoc_a2msec(nn->string); 2853 if (cp == NULL) { 2854 mandoc_msg(MANDOCERR_MSEC_BAD, 2855 nn->line, nn->pos, "Dt ... %s", nn->string); 2856 } else { 2857 mdoc->meta.vol = mandoc_strdup(cp); 2858 if (mdoc->filesec != '\0' && 2859 mdoc->filesec != *nn->string && 2860 *nn->string >= '1' && *nn->string <= '9') 2861 mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, 2862 "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); 2863 } 2864 2865 /* Optional third argument: architecture. */ 2866 2867 if ((nn = nn->next) == NULL) 2868 return; 2869 2870 for (p = nn->string; *p != '\0'; p++) 2871 *p = tolower((unsigned char)*p); 2872 mdoc->meta.arch = mandoc_strdup(nn->string); 2873 2874 /* Ignore fourth and later arguments. */ 2875 2876 if ((nn = nn->next) != NULL) 2877 mandoc_msg(MANDOCERR_ARG_EXCESS, 2878 nn->line, nn->pos, "Dt ... %s", nn->string); 2879 } 2880 2881 static void 2882 post_bx(POST_ARGS) 2883 { 2884 struct roff_node *n, *nch; 2885 const char *macro; 2886 2887 post_delim_nb(mdoc); 2888 2889 n = mdoc->last; 2890 nch = n->child; 2891 2892 if (nch != NULL) { 2893 macro = !strcmp(nch->string, "Open") ? "Ox" : 2894 !strcmp(nch->string, "Net") ? "Nx" : 2895 !strcmp(nch->string, "Free") ? "Fx" : 2896 !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; 2897 if (macro != NULL) 2898 mandoc_msg(MANDOCERR_BX, 2899 n->line, n->pos, "%s", macro); 2900 mdoc->last = nch; 2901 nch = nch->next; 2902 mdoc->next = ROFF_NEXT_SIBLING; 2903 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2904 mdoc->last->flags |= NODE_NOSRC; 2905 mdoc->next = ROFF_NEXT_SIBLING; 2906 } else 2907 mdoc->next = ROFF_NEXT_CHILD; 2908 roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 2909 mdoc->last->flags |= NODE_NOSRC; 2910 2911 if (nch == NULL) { 2912 mdoc->last = n; 2913 return; 2914 } 2915 2916 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2917 mdoc->last->flags |= NODE_NOSRC; 2918 mdoc->next = ROFF_NEXT_SIBLING; 2919 roff_word_alloc(mdoc, n->line, n->pos, "-"); 2920 mdoc->last->flags |= NODE_NOSRC; 2921 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2922 mdoc->last->flags |= NODE_NOSRC; 2923 mdoc->last = n; 2924 2925 /* 2926 * Make `Bx's second argument always start with an uppercase 2927 * letter. Groff checks if it's an "accepted" term, but we just 2928 * uppercase blindly. 2929 */ 2930 2931 *nch->string = (char)toupper((unsigned char)*nch->string); 2932 } 2933 2934 static void 2935 post_os(POST_ARGS) 2936 { 2937 #ifndef OSNAME 2938 struct utsname utsname; 2939 #endif 2940 struct roff_node *n; 2941 2942 n = mdoc->last; 2943 n->flags |= NODE_NOPRT; 2944 2945 if (mdoc->meta.os != NULL) 2946 mandoc_msg(MANDOCERR_PROLOG_REP, n->line, n->pos, "Os"); 2947 else if (mdoc->flags & MDOC_PBODY) 2948 mandoc_msg(MANDOCERR_PROLOG_LATE, n->line, n->pos, "Os"); 2949 2950 post_delim(mdoc); 2951 2952 /* 2953 * Set the operating system by way of the `Os' macro. 2954 * The order of precedence is: 2955 * 1. the argument of the `Os' macro, unless empty 2956 * 2. the -Ios=foo command line argument, if provided 2957 * 3. -DOSNAME="\"foo\"", if provided during compilation 2958 * 4. "sysname release" from uname(3) 2959 */ 2960 2961 free(mdoc->meta.os); 2962 mdoc->meta.os = NULL; 2963 deroff(&mdoc->meta.os, n); 2964 if (mdoc->meta.os) 2965 goto out; 2966 2967 if (mdoc->os_s != NULL) { 2968 mdoc->meta.os = mandoc_strdup(mdoc->os_s); 2969 goto out; 2970 } 2971 2972 #ifdef OSNAME 2973 mdoc->meta.os = mandoc_strdup(OSNAME); 2974 #else /*!OSNAME */ 2975 if (mdoc->os_r == NULL) { 2976 if (uname(&utsname) == -1) { 2977 mandoc_msg(MANDOCERR_OS_UNAME, n->line, n->pos, "Os"); 2978 mdoc->os_r = mandoc_strdup("UNKNOWN"); 2979 } else 2980 mandoc_asprintf(&mdoc->os_r, "%s %s", 2981 utsname.sysname, utsname.release); 2982 } 2983 mdoc->meta.os = mandoc_strdup(mdoc->os_r); 2984 #endif /*!OSNAME*/ 2985 2986 out: 2987 if (mdoc->meta.os_e == MANDOC_OS_OTHER) { 2988 if (strstr(mdoc->meta.os, "OpenBSD") != NULL) 2989 mdoc->meta.os_e = MANDOC_OS_OPENBSD; 2990 else if (strstr(mdoc->meta.os, "NetBSD") != NULL) 2991 mdoc->meta.os_e = MANDOC_OS_NETBSD; 2992 } 2993 2994 /* 2995 * This is the earliest point where we can check 2996 * Mdocdate conventions because we don't know 2997 * the operating system earlier. 2998 */ 2999 3000 if (n->child != NULL) 3001 mandoc_msg(MANDOCERR_OS_ARG, n->child->line, n->child->pos, 3002 "Os %s (%s)", n->child->string, 3003 mdoc->meta.os_e == MANDOC_OS_OPENBSD ? 3004 "OpenBSD" : "NetBSD"); 3005 3006 while (n->tok != MDOC_Dd) 3007 if ((n = n->prev) == NULL) 3008 return; 3009 if ((n = n->child) == NULL) 3010 return; 3011 if (strncmp(n->string, "$" "Mdocdate", 9)) { 3012 if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) 3013 mandoc_msg(MANDOCERR_MDOCDATE_MISSING, n->line, 3014 n->pos, "Dd %s (OpenBSD)", n->string); 3015 } else { 3016 if (mdoc->meta.os_e == MANDOC_OS_NETBSD) 3017 mandoc_msg(MANDOCERR_MDOCDATE, n->line, 3018 n->pos, "Dd %s (NetBSD)", n->string); 3019 } 3020 } 3021 3022 enum roff_sec 3023 mdoc_a2sec(const char *p) 3024 { 3025 int i; 3026 3027 for (i = 0; i < (int)SEC__MAX; i++) 3028 if (secnames[i] && 0 == strcmp(p, secnames[i])) 3029 return (enum roff_sec)i; 3030 3031 return SEC_CUSTOM; 3032 } 3033 3034 static size_t 3035 macro2len(enum roff_tok macro) 3036 { 3037 3038 switch (macro) { 3039 case MDOC_Ad: 3040 return 12; 3041 case MDOC_Ao: 3042 return 12; 3043 case MDOC_An: 3044 return 12; 3045 case MDOC_Aq: 3046 return 12; 3047 case MDOC_Ar: 3048 return 12; 3049 case MDOC_Bo: 3050 return 12; 3051 case MDOC_Bq: 3052 return 12; 3053 case MDOC_Cd: 3054 return 12; 3055 case MDOC_Cm: 3056 return 10; 3057 case MDOC_Do: 3058 return 10; 3059 case MDOC_Dq: 3060 return 12; 3061 case MDOC_Dv: 3062 return 12; 3063 case MDOC_Eo: 3064 return 12; 3065 case MDOC_Em: 3066 return 10; 3067 case MDOC_Er: 3068 return 17; 3069 case MDOC_Ev: 3070 return 15; 3071 case MDOC_Fa: 3072 return 12; 3073 case MDOC_Fl: 3074 return 10; 3075 case MDOC_Fo: 3076 return 16; 3077 case MDOC_Fn: 3078 return 16; 3079 case MDOC_Ic: 3080 return 10; 3081 case MDOC_Li: 3082 return 16; 3083 case MDOC_Ms: 3084 return 6; 3085 case MDOC_Nm: 3086 return 10; 3087 case MDOC_No: 3088 return 12; 3089 case MDOC_Oo: 3090 return 10; 3091 case MDOC_Op: 3092 return 14; 3093 case MDOC_Pa: 3094 return 32; 3095 case MDOC_Pf: 3096 return 12; 3097 case MDOC_Po: 3098 return 12; 3099 case MDOC_Pq: 3100 return 12; 3101 case MDOC_Ql: 3102 return 16; 3103 case MDOC_Qo: 3104 return 12; 3105 case MDOC_So: 3106 return 12; 3107 case MDOC_Sq: 3108 return 12; 3109 case MDOC_Sy: 3110 return 6; 3111 case MDOC_Sx: 3112 return 16; 3113 case MDOC_Tn: 3114 return 10; 3115 case MDOC_Va: 3116 return 12; 3117 case MDOC_Vt: 3118 return 12; 3119 case MDOC_Xr: 3120 return 10; 3121 default: 3122 break; 3123 } 3124 return 0; 3125 } 3126