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