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