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