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