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