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