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