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