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