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 } 972 973 post_fname(mdoc); 974 } 975 976 static void 977 post_fa(POST_ARGS) 978 { 979 const struct roff_node *n; 980 const char *cp; 981 982 for (n = mdoc->last->child; n != NULL; n = n->next) { 983 for (cp = n->string; *cp != '\0'; cp++) { 984 /* Ignore callbacks and alterations. */ 985 if (*cp == '(' || *cp == '{') 986 break; 987 if (*cp != ',') 988 continue; 989 mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse, 990 n->line, n->pos + (cp - n->string), 991 n->string); 992 break; 993 } 994 } 995 } 996 997 static void 998 post_nm(POST_ARGS) 999 { 1000 struct roff_node *n; 1001 1002 n = mdoc->last; 1003 1004 if (n->last != NULL && 1005 (n->last->tok == MDOC_Pp || 1006 n->last->tok == MDOC_Lp)) 1007 mdoc_node_relink(mdoc, n->last); 1008 1009 if (mdoc->meta.name == NULL) 1010 deroff(&mdoc->meta.name, n); 1011 1012 if (mdoc->meta.name == NULL || 1013 (mdoc->lastsec == SEC_NAME && n->child == NULL)) 1014 mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, 1015 n->line, n->pos, "Nm"); 1016 1017 if ((n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) || 1018 (n->child != NULL && n->child->type == ROFFT_TEXT) || 1019 mdoc->meta.name == NULL) 1020 return; 1021 1022 mdoc->next = ROFF_NEXT_CHILD; 1023 roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); 1024 mdoc->last->flags |= NODE_NOSRC; 1025 mdoc->last = n; 1026 } 1027 1028 static void 1029 post_nd(POST_ARGS) 1030 { 1031 struct roff_node *n; 1032 1033 n = mdoc->last; 1034 1035 if (n->type != ROFFT_BODY) 1036 return; 1037 1038 if (n->child == NULL) 1039 mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, 1040 n->line, n->pos, "Nd"); 1041 1042 post_hyph(mdoc); 1043 } 1044 1045 static void 1046 post_display(POST_ARGS) 1047 { 1048 struct roff_node *n, *np; 1049 1050 n = mdoc->last; 1051 switch (n->type) { 1052 case ROFFT_BODY: 1053 if (n->end != ENDBODY_NOT) { 1054 if (n->tok == MDOC_Bd && 1055 n->body->parent->args == NULL) 1056 roff_node_delete(mdoc, n); 1057 } else if (n->child == NULL) 1058 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 1059 n->line, n->pos, mdoc_macronames[n->tok]); 1060 else if (n->tok == MDOC_D1) 1061 post_hyph(mdoc); 1062 break; 1063 case ROFFT_BLOCK: 1064 if (n->tok == MDOC_Bd) { 1065 if (n->args == NULL) { 1066 mandoc_msg(MANDOCERR_BD_NOARG, 1067 mdoc->parse, n->line, n->pos, "Bd"); 1068 mdoc->next = ROFF_NEXT_SIBLING; 1069 while (n->body->child != NULL) 1070 mdoc_node_relink(mdoc, 1071 n->body->child); 1072 roff_node_delete(mdoc, n); 1073 break; 1074 } 1075 post_bd(mdoc); 1076 post_prevpar(mdoc); 1077 } 1078 for (np = n->parent; np != NULL; np = np->parent) { 1079 if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { 1080 mandoc_vmsg(MANDOCERR_BD_NEST, 1081 mdoc->parse, n->line, n->pos, 1082 "%s in Bd", mdoc_macronames[n->tok]); 1083 break; 1084 } 1085 } 1086 break; 1087 default: 1088 break; 1089 } 1090 } 1091 1092 static void 1093 post_defaults(POST_ARGS) 1094 { 1095 struct roff_node *nn; 1096 1097 /* 1098 * The `Ar' defaults to "file ..." if no value is provided as an 1099 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just 1100 * gets an empty string. 1101 */ 1102 1103 if (mdoc->last->child != NULL) 1104 return; 1105 1106 nn = mdoc->last; 1107 1108 switch (nn->tok) { 1109 case MDOC_Ar: 1110 mdoc->next = ROFF_NEXT_CHILD; 1111 roff_word_alloc(mdoc, nn->line, nn->pos, "file"); 1112 mdoc->last->flags |= NODE_NOSRC; 1113 roff_word_alloc(mdoc, nn->line, nn->pos, "..."); 1114 mdoc->last->flags |= NODE_NOSRC; 1115 break; 1116 case MDOC_Pa: 1117 case MDOC_Mt: 1118 mdoc->next = ROFF_NEXT_CHILD; 1119 roff_word_alloc(mdoc, nn->line, nn->pos, "~"); 1120 mdoc->last->flags |= NODE_NOSRC; 1121 break; 1122 default: 1123 abort(); 1124 } 1125 mdoc->last = nn; 1126 } 1127 1128 static void 1129 post_at(POST_ARGS) 1130 { 1131 struct roff_node *n, *nch; 1132 const char *att; 1133 1134 n = mdoc->last; 1135 nch = n->child; 1136 1137 /* 1138 * If we have a child, look it up in the standard keys. If a 1139 * key exist, use that instead of the child; if it doesn't, 1140 * prefix "AT&T UNIX " to the existing data. 1141 */ 1142 1143 att = NULL; 1144 if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL)) 1145 mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse, 1146 nch->line, nch->pos, "At %s", nch->string); 1147 1148 mdoc->next = ROFF_NEXT_CHILD; 1149 if (att != NULL) { 1150 roff_word_alloc(mdoc, nch->line, nch->pos, att); 1151 nch->flags |= NODE_NOPRT; 1152 } else 1153 roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"); 1154 mdoc->last->flags |= NODE_NOSRC; 1155 mdoc->last = n; 1156 } 1157 1158 static void 1159 post_an(POST_ARGS) 1160 { 1161 struct roff_node *np, *nch; 1162 1163 post_an_norm(mdoc); 1164 1165 np = mdoc->last; 1166 nch = np->child; 1167 if (np->norm->An.auth == AUTH__NONE) { 1168 if (nch == NULL) 1169 mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, 1170 np->line, np->pos, "An"); 1171 } else if (nch != NULL) 1172 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1173 nch->line, nch->pos, "An ... %s", nch->string); 1174 } 1175 1176 static void 1177 post_en(POST_ARGS) 1178 { 1179 1180 post_obsolete(mdoc); 1181 if (mdoc->last->type == ROFFT_BLOCK) 1182 mdoc->last->norm->Es = mdoc->last_es; 1183 } 1184 1185 static void 1186 post_es(POST_ARGS) 1187 { 1188 1189 post_obsolete(mdoc); 1190 mdoc->last_es = mdoc->last; 1191 } 1192 1193 static void 1194 post_xx(POST_ARGS) 1195 { 1196 struct roff_node *n; 1197 const char *os; 1198 1199 n = mdoc->last; 1200 switch (n->tok) { 1201 case MDOC_Bsx: 1202 os = "BSD/OS"; 1203 break; 1204 case MDOC_Dx: 1205 os = "DragonFly"; 1206 break; 1207 case MDOC_Fx: 1208 os = "FreeBSD"; 1209 break; 1210 case MDOC_Nx: 1211 os = "NetBSD"; 1212 break; 1213 case MDOC_Ox: 1214 os = "OpenBSD"; 1215 break; 1216 case MDOC_Ux: 1217 os = "UNIX"; 1218 break; 1219 default: 1220 abort(); 1221 } 1222 mdoc->next = ROFF_NEXT_CHILD; 1223 roff_word_alloc(mdoc, n->line, n->pos, os); 1224 mdoc->last->flags |= NODE_NOSRC; 1225 mdoc->last = n; 1226 } 1227 1228 static void 1229 post_it(POST_ARGS) 1230 { 1231 struct roff_node *nbl, *nit, *nch; 1232 int i, cols; 1233 enum mdoc_list lt; 1234 1235 post_prevpar(mdoc); 1236 1237 nit = mdoc->last; 1238 if (nit->type != ROFFT_BLOCK) 1239 return; 1240 1241 nbl = nit->parent->parent; 1242 lt = nbl->norm->Bl.type; 1243 1244 switch (lt) { 1245 case LIST_tag: 1246 case LIST_hang: 1247 case LIST_ohang: 1248 case LIST_inset: 1249 case LIST_diag: 1250 if (nit->head->child == NULL) 1251 mandoc_vmsg(MANDOCERR_IT_NOHEAD, 1252 mdoc->parse, nit->line, nit->pos, 1253 "Bl -%s It", 1254 mdoc_argnames[nbl->args->argv[0].arg]); 1255 break; 1256 case LIST_bullet: 1257 case LIST_dash: 1258 case LIST_enum: 1259 case LIST_hyphen: 1260 if (nit->body == NULL || nit->body->child == NULL) 1261 mandoc_vmsg(MANDOCERR_IT_NOBODY, 1262 mdoc->parse, nit->line, nit->pos, 1263 "Bl -%s It", 1264 mdoc_argnames[nbl->args->argv[0].arg]); 1265 /* FALLTHROUGH */ 1266 case LIST_item: 1267 if ((nch = nit->head->child) != NULL) 1268 mandoc_vmsg(MANDOCERR_ARG_SKIP, 1269 mdoc->parse, nit->line, nit->pos, 1270 "It %s", nch->string == NULL ? 1271 mdoc_macronames[nch->tok] : nch->string); 1272 break; 1273 case LIST_column: 1274 cols = (int)nbl->norm->Bl.ncols; 1275 1276 assert(nit->head->child == NULL); 1277 1278 i = 0; 1279 for (nch = nit->child; nch != NULL; nch = nch->next) 1280 if (nch->type == ROFFT_BODY) 1281 i++; 1282 1283 if (i < cols || i > cols + 1) 1284 mandoc_vmsg(MANDOCERR_BL_COL, 1285 mdoc->parse, nit->line, nit->pos, 1286 "%d columns, %d cells", cols, i); 1287 break; 1288 default: 1289 abort(); 1290 } 1291 } 1292 1293 static void 1294 post_bl_block(POST_ARGS) 1295 { 1296 struct roff_node *n, *ni, *nc; 1297 1298 post_prevpar(mdoc); 1299 1300 n = mdoc->last; 1301 for (ni = n->body->child; ni != NULL; ni = ni->next) { 1302 if (ni->body == NULL) 1303 continue; 1304 nc = ni->body->last; 1305 while (nc != NULL) { 1306 switch (nc->tok) { 1307 case MDOC_Pp: 1308 case MDOC_Lp: 1309 case MDOC_br: 1310 break; 1311 default: 1312 nc = NULL; 1313 continue; 1314 } 1315 if (ni->next == NULL) { 1316 mandoc_msg(MANDOCERR_PAR_MOVE, 1317 mdoc->parse, nc->line, nc->pos, 1318 mdoc_macronames[nc->tok]); 1319 mdoc_node_relink(mdoc, nc); 1320 } else if (n->norm->Bl.comp == 0 && 1321 n->norm->Bl.type != LIST_column) { 1322 mandoc_vmsg(MANDOCERR_PAR_SKIP, 1323 mdoc->parse, nc->line, nc->pos, 1324 "%s before It", 1325 mdoc_macronames[nc->tok]); 1326 roff_node_delete(mdoc, nc); 1327 } else 1328 break; 1329 nc = ni->body->last; 1330 } 1331 } 1332 } 1333 1334 /* 1335 * If the argument of -offset or -width is a macro, 1336 * replace it with the associated default width. 1337 */ 1338 void 1339 rewrite_macro2len(char **arg) 1340 { 1341 size_t width; 1342 int tok; 1343 1344 if (*arg == NULL) 1345 return; 1346 else if ( ! strcmp(*arg, "Ds")) 1347 width = 6; 1348 else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE) 1349 return; 1350 else 1351 width = macro2len(tok); 1352 1353 free(*arg); 1354 mandoc_asprintf(arg, "%zun", width); 1355 } 1356 1357 static void 1358 post_bl_head(POST_ARGS) 1359 { 1360 struct roff_node *nbl, *nh, *nch, *nnext; 1361 struct mdoc_argv *argv; 1362 int i, j; 1363 1364 post_bl_norm(mdoc); 1365 1366 nh = mdoc->last; 1367 if (nh->norm->Bl.type != LIST_column) { 1368 if ((nch = nh->child) == NULL) 1369 return; 1370 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 1371 nch->line, nch->pos, "Bl ... %s", nch->string); 1372 while (nch != NULL) { 1373 roff_node_delete(mdoc, nch); 1374 nch = nh->child; 1375 } 1376 return; 1377 } 1378 1379 /* 1380 * Append old-style lists, where the column width specifiers 1381 * trail as macro parameters, to the new-style ("normal-form") 1382 * lists where they're argument values following -column. 1383 */ 1384 1385 if (nh->child == NULL) 1386 return; 1387 1388 nbl = nh->parent; 1389 for (j = 0; j < (int)nbl->args->argc; j++) 1390 if (nbl->args->argv[j].arg == MDOC_Column) 1391 break; 1392 1393 assert(j < (int)nbl->args->argc); 1394 1395 /* 1396 * Accommodate for new-style groff column syntax. Shuffle the 1397 * child nodes, all of which must be TEXT, as arguments for the 1398 * column field. Then, delete the head children. 1399 */ 1400 1401 argv = nbl->args->argv + j; 1402 i = argv->sz; 1403 for (nch = nh->child; nch != NULL; nch = nch->next) 1404 argv->sz++; 1405 argv->value = mandoc_reallocarray(argv->value, 1406 argv->sz, sizeof(char *)); 1407 1408 nh->norm->Bl.ncols = argv->sz; 1409 nh->norm->Bl.cols = (void *)argv->value; 1410 1411 for (nch = nh->child; nch != NULL; nch = nnext) { 1412 argv->value[i++] = nch->string; 1413 nch->string = NULL; 1414 nnext = nch->next; 1415 roff_node_delete(NULL, nch); 1416 } 1417 nh->child = NULL; 1418 } 1419 1420 static void 1421 post_bl(POST_ARGS) 1422 { 1423 struct roff_node *nparent, *nprev; /* of the Bl block */ 1424 struct roff_node *nblock, *nbody; /* of the Bl */ 1425 struct roff_node *nchild, *nnext; /* of the Bl body */ 1426 1427 nbody = mdoc->last; 1428 switch (nbody->type) { 1429 case ROFFT_BLOCK: 1430 post_bl_block(mdoc); 1431 return; 1432 case ROFFT_HEAD: 1433 post_bl_head(mdoc); 1434 return; 1435 case ROFFT_BODY: 1436 break; 1437 default: 1438 return; 1439 } 1440 if (nbody->end != ENDBODY_NOT) 1441 return; 1442 1443 nchild = nbody->child; 1444 if (nchild == NULL) { 1445 mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, 1446 nbody->line, nbody->pos, "Bl"); 1447 return; 1448 } 1449 while (nchild != NULL) { 1450 nnext = nchild->next; 1451 if (nchild->tok == MDOC_It || 1452 (nchild->tok == MDOC_Sm && 1453 nnext != NULL && nnext->tok == MDOC_It)) { 1454 nchild = nnext; 1455 continue; 1456 } 1457 1458 /* 1459 * In .Bl -column, the first rows may be implicit, 1460 * that is, they may not start with .It macros. 1461 * Such rows may be followed by nodes generated on the 1462 * roff level, for example .TS, which cannot be moved 1463 * out of the list. In that case, wrap such roff nodes 1464 * into an implicit row. 1465 */ 1466 1467 if (nchild->prev != NULL) { 1468 mdoc->last = nchild; 1469 mdoc->next = ROFF_NEXT_SIBLING; 1470 roff_block_alloc(mdoc, nchild->line, 1471 nchild->pos, MDOC_It); 1472 roff_head_alloc(mdoc, nchild->line, 1473 nchild->pos, MDOC_It); 1474 mdoc->next = ROFF_NEXT_SIBLING; 1475 roff_body_alloc(mdoc, nchild->line, 1476 nchild->pos, MDOC_It); 1477 while (nchild->tok != MDOC_It) { 1478 mdoc_node_relink(mdoc, nchild); 1479 if ((nchild = nnext) == NULL) 1480 break; 1481 nnext = nchild->next; 1482 mdoc->next = ROFF_NEXT_SIBLING; 1483 } 1484 mdoc->last = nbody; 1485 continue; 1486 } 1487 1488 mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, 1489 nchild->line, nchild->pos, 1490 mdoc_macronames[nchild->tok]); 1491 1492 /* 1493 * Move the node out of the Bl block. 1494 * First, collect all required node pointers. 1495 */ 1496 1497 nblock = nbody->parent; 1498 nprev = nblock->prev; 1499 nparent = nblock->parent; 1500 1501 /* 1502 * Unlink this child. 1503 */ 1504 1505 nbody->child = nnext; 1506 if (nnext == NULL) 1507 nbody->last = NULL; 1508 else 1509 nnext->prev = NULL; 1510 1511 /* 1512 * Relink this child. 1513 */ 1514 1515 nchild->parent = nparent; 1516 nchild->prev = nprev; 1517 nchild->next = nblock; 1518 1519 nblock->prev = nchild; 1520 if (nprev == NULL) 1521 nparent->child = nchild; 1522 else 1523 nprev->next = nchild; 1524 1525 nchild = nnext; 1526 } 1527 } 1528 1529 static void 1530 post_bk(POST_ARGS) 1531 { 1532 struct roff_node *n; 1533 1534 n = mdoc->last; 1535 1536 if (n->type == ROFFT_BLOCK && n->body->child == NULL) { 1537 mandoc_msg(MANDOCERR_BLK_EMPTY, 1538 mdoc->parse, n->line, n->pos, "Bk"); 1539 roff_node_delete(mdoc, n); 1540 } 1541 } 1542 1543 static void 1544 post_sm(POST_ARGS) 1545 { 1546 struct roff_node *nch; 1547 1548 nch = mdoc->last->child; 1549 1550 if (nch == NULL) { 1551 mdoc->flags ^= MDOC_SMOFF; 1552 return; 1553 } 1554 1555 assert(nch->type == ROFFT_TEXT); 1556 1557 if ( ! strcmp(nch->string, "on")) { 1558 mdoc->flags &= ~MDOC_SMOFF; 1559 return; 1560 } 1561 if ( ! strcmp(nch->string, "off")) { 1562 mdoc->flags |= MDOC_SMOFF; 1563 return; 1564 } 1565 1566 mandoc_vmsg(MANDOCERR_SM_BAD, 1567 mdoc->parse, nch->line, nch->pos, 1568 "%s %s", mdoc_macronames[mdoc->last->tok], nch->string); 1569 mdoc_node_relink(mdoc, nch); 1570 return; 1571 } 1572 1573 static void 1574 post_root(POST_ARGS) 1575 { 1576 struct roff_node *n; 1577 1578 /* Add missing prologue data. */ 1579 1580 if (mdoc->meta.date == NULL) 1581 mdoc->meta.date = mdoc->quick ? 1582 mandoc_strdup("") : 1583 mandoc_normdate(mdoc->parse, NULL, 0, 0); 1584 1585 if (mdoc->meta.title == NULL) { 1586 mandoc_msg(MANDOCERR_DT_NOTITLE, 1587 mdoc->parse, 0, 0, "EOF"); 1588 mdoc->meta.title = mandoc_strdup("UNTITLED"); 1589 } 1590 1591 if (mdoc->meta.vol == NULL) 1592 mdoc->meta.vol = mandoc_strdup("LOCAL"); 1593 1594 if (mdoc->meta.os == NULL) { 1595 mandoc_msg(MANDOCERR_OS_MISSING, 1596 mdoc->parse, 0, 0, NULL); 1597 mdoc->meta.os = mandoc_strdup(""); 1598 } 1599 1600 /* Check that we begin with a proper `Sh'. */ 1601 1602 n = mdoc->first->child; 1603 while (n != NULL && n->tok != TOKEN_NONE && 1604 mdoc_macros[n->tok].flags & MDOC_PROLOGUE) 1605 n = n->next; 1606 1607 if (n == NULL) 1608 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); 1609 else if (n->tok != MDOC_Sh) 1610 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, 1611 n->line, n->pos, mdoc_macronames[n->tok]); 1612 } 1613 1614 static void 1615 post_rs(POST_ARGS) 1616 { 1617 struct roff_node *np, *nch, *next, *prev; 1618 int i, j; 1619 1620 np = mdoc->last; 1621 1622 if (np->type != ROFFT_BODY) 1623 return; 1624 1625 if (np->child == NULL) { 1626 mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse, 1627 np->line, np->pos, "Rs"); 1628 return; 1629 } 1630 1631 /* 1632 * The full `Rs' block needs special handling to order the 1633 * sub-elements according to `rsord'. Pick through each element 1634 * and correctly order it. This is an insertion sort. 1635 */ 1636 1637 next = NULL; 1638 for (nch = np->child->next; nch != NULL; nch = next) { 1639 /* Determine order number of this child. */ 1640 for (i = 0; i < RSORD_MAX; i++) 1641 if (rsord[i] == nch->tok) 1642 break; 1643 1644 if (i == RSORD_MAX) { 1645 mandoc_msg(MANDOCERR_RS_BAD, 1646 mdoc->parse, nch->line, nch->pos, 1647 mdoc_macronames[nch->tok]); 1648 i = -1; 1649 } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) 1650 np->norm->Rs.quote_T++; 1651 1652 /* 1653 * Remove this child from the chain. This somewhat 1654 * repeats roff_node_unlink(), but since we're 1655 * just re-ordering, there's no need for the 1656 * full unlink process. 1657 */ 1658 1659 if ((next = nch->next) != NULL) 1660 next->prev = nch->prev; 1661 1662 if ((prev = nch->prev) != NULL) 1663 prev->next = nch->next; 1664 1665 nch->prev = nch->next = NULL; 1666 1667 /* 1668 * Scan back until we reach a node that's 1669 * to be ordered before this child. 1670 */ 1671 1672 for ( ; prev ; prev = prev->prev) { 1673 /* Determine order of `prev'. */ 1674 for (j = 0; j < RSORD_MAX; j++) 1675 if (rsord[j] == prev->tok) 1676 break; 1677 if (j == RSORD_MAX) 1678 j = -1; 1679 1680 if (j <= i) 1681 break; 1682 } 1683 1684 /* 1685 * Set this child back into its correct place 1686 * in front of the `prev' node. 1687 */ 1688 1689 nch->prev = prev; 1690 1691 if (prev == NULL) { 1692 np->child->prev = nch; 1693 nch->next = np->child; 1694 np->child = nch; 1695 } else { 1696 if (prev->next) 1697 prev->next->prev = nch; 1698 nch->next = prev->next; 1699 prev->next = nch; 1700 } 1701 } 1702 } 1703 1704 /* 1705 * For some arguments of some macros, 1706 * convert all breakable hyphens into ASCII_HYPH. 1707 */ 1708 static void 1709 post_hyph(POST_ARGS) 1710 { 1711 struct roff_node *nch; 1712 char *cp; 1713 1714 for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { 1715 if (nch->type != ROFFT_TEXT) 1716 continue; 1717 cp = nch->string; 1718 if (*cp == '\0') 1719 continue; 1720 while (*(++cp) != '\0') 1721 if (*cp == '-' && 1722 isalpha((unsigned char)cp[-1]) && 1723 isalpha((unsigned char)cp[1])) 1724 *cp = ASCII_HYPH; 1725 } 1726 } 1727 1728 static void 1729 post_ns(POST_ARGS) 1730 { 1731 1732 if (mdoc->last->flags & NODE_LINE) 1733 mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, 1734 mdoc->last->line, mdoc->last->pos, NULL); 1735 } 1736 1737 static void 1738 post_sh(POST_ARGS) 1739 { 1740 1741 post_ignpar(mdoc); 1742 1743 switch (mdoc->last->type) { 1744 case ROFFT_HEAD: 1745 post_sh_head(mdoc); 1746 break; 1747 case ROFFT_BODY: 1748 switch (mdoc->lastsec) { 1749 case SEC_NAME: 1750 post_sh_name(mdoc); 1751 break; 1752 case SEC_SEE_ALSO: 1753 post_sh_see_also(mdoc); 1754 break; 1755 case SEC_AUTHORS: 1756 post_sh_authors(mdoc); 1757 break; 1758 default: 1759 break; 1760 } 1761 break; 1762 default: 1763 break; 1764 } 1765 } 1766 1767 static void 1768 post_sh_name(POST_ARGS) 1769 { 1770 struct roff_node *n; 1771 int hasnm, hasnd; 1772 1773 hasnm = hasnd = 0; 1774 1775 for (n = mdoc->last->child; n != NULL; n = n->next) { 1776 switch (n->tok) { 1777 case MDOC_Nm: 1778 if (hasnm && n->child != NULL) 1779 mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT, 1780 mdoc->parse, n->line, n->pos, 1781 "Nm %s", n->child->string); 1782 hasnm = 1; 1783 continue; 1784 case MDOC_Nd: 1785 hasnd = 1; 1786 if (n->next != NULL) 1787 mandoc_msg(MANDOCERR_NAMESEC_ND, 1788 mdoc->parse, n->line, n->pos, NULL); 1789 break; 1790 case TOKEN_NONE: 1791 if (n->type == ROFFT_TEXT && 1792 n->string[0] == ',' && n->string[1] == '\0' && 1793 n->next != NULL && n->next->tok == MDOC_Nm) { 1794 n = n->next; 1795 continue; 1796 } 1797 /* FALLTHROUGH */ 1798 default: 1799 mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, 1800 n->line, n->pos, mdoc_macronames[n->tok]); 1801 continue; 1802 } 1803 break; 1804 } 1805 1806 if ( ! hasnm) 1807 mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse, 1808 mdoc->last->line, mdoc->last->pos, NULL); 1809 if ( ! hasnd) 1810 mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse, 1811 mdoc->last->line, mdoc->last->pos, NULL); 1812 } 1813 1814 static void 1815 post_sh_see_also(POST_ARGS) 1816 { 1817 const struct roff_node *n; 1818 const char *name, *sec; 1819 const char *lastname, *lastsec, *lastpunct; 1820 int cmp; 1821 1822 n = mdoc->last->child; 1823 lastname = lastsec = lastpunct = NULL; 1824 while (n != NULL) { 1825 if (n->tok != MDOC_Xr || 1826 n->child == NULL || 1827 n->child->next == NULL) 1828 break; 1829 1830 /* Process one .Xr node. */ 1831 1832 name = n->child->string; 1833 sec = n->child->next->string; 1834 if (lastsec != NULL) { 1835 if (lastpunct[0] != ',' || lastpunct[1] != '\0') 1836 mandoc_vmsg(MANDOCERR_XR_PUNCT, 1837 mdoc->parse, n->line, n->pos, 1838 "%s before %s(%s)", lastpunct, 1839 name, sec); 1840 cmp = strcmp(lastsec, sec); 1841 if (cmp > 0) 1842 mandoc_vmsg(MANDOCERR_XR_ORDER, 1843 mdoc->parse, n->line, n->pos, 1844 "%s(%s) after %s(%s)", name, 1845 sec, lastname, lastsec); 1846 else if (cmp == 0 && 1847 strcasecmp(lastname, name) > 0) 1848 mandoc_vmsg(MANDOCERR_XR_ORDER, 1849 mdoc->parse, n->line, n->pos, 1850 "%s after %s", name, lastname); 1851 } 1852 lastname = name; 1853 lastsec = sec; 1854 1855 /* Process the following node. */ 1856 1857 n = n->next; 1858 if (n == NULL) 1859 break; 1860 if (n->tok == MDOC_Xr) { 1861 lastpunct = "none"; 1862 continue; 1863 } 1864 if (n->type != ROFFT_TEXT) 1865 break; 1866 for (name = n->string; *name != '\0'; name++) 1867 if (isalpha((const unsigned char)*name)) 1868 return; 1869 lastpunct = n->string; 1870 if (n->next == NULL) 1871 mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, 1872 n->line, n->pos, "%s after %s(%s)", 1873 lastpunct, lastname, lastsec); 1874 n = n->next; 1875 } 1876 } 1877 1878 static int 1879 child_an(const struct roff_node *n) 1880 { 1881 1882 for (n = n->child; n != NULL; n = n->next) 1883 if ((n->tok == MDOC_An && n->child != NULL) || child_an(n)) 1884 return 1; 1885 return 0; 1886 } 1887 1888 static void 1889 post_sh_authors(POST_ARGS) 1890 { 1891 1892 if ( ! child_an(mdoc->last)) 1893 mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse, 1894 mdoc->last->line, mdoc->last->pos, NULL); 1895 } 1896 1897 static void 1898 post_sh_head(POST_ARGS) 1899 { 1900 struct roff_node *nch; 1901 const char *goodsec; 1902 enum roff_sec sec; 1903 1904 /* 1905 * Process a new section. Sections are either "named" or 1906 * "custom". Custom sections are user-defined, while named ones 1907 * follow a conventional order and may only appear in certain 1908 * manual sections. 1909 */ 1910 1911 sec = mdoc->last->sec; 1912 1913 /* The NAME should be first. */ 1914 1915 if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE) 1916 mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse, 1917 mdoc->last->line, mdoc->last->pos, "Sh %s", 1918 sec != SEC_CUSTOM ? secnames[sec] : 1919 (nch = mdoc->last->child) == NULL ? "" : 1920 nch->type == ROFFT_TEXT ? nch->string : 1921 mdoc_macronames[nch->tok]); 1922 1923 /* The SYNOPSIS gets special attention in other areas. */ 1924 1925 if (sec == SEC_SYNOPSIS) { 1926 roff_setreg(mdoc->roff, "nS", 1, '='); 1927 mdoc->flags |= MDOC_SYNOPSIS; 1928 } else { 1929 roff_setreg(mdoc->roff, "nS", 0, '='); 1930 mdoc->flags &= ~MDOC_SYNOPSIS; 1931 } 1932 1933 /* Mark our last section. */ 1934 1935 mdoc->lastsec = sec; 1936 1937 /* We don't care about custom sections after this. */ 1938 1939 if (sec == SEC_CUSTOM) 1940 return; 1941 1942 /* 1943 * Check whether our non-custom section is being repeated or is 1944 * out of order. 1945 */ 1946 1947 if (sec == mdoc->lastnamed) 1948 mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse, 1949 mdoc->last->line, mdoc->last->pos, 1950 "Sh %s", secnames[sec]); 1951 1952 if (sec < mdoc->lastnamed) 1953 mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse, 1954 mdoc->last->line, mdoc->last->pos, 1955 "Sh %s", secnames[sec]); 1956 1957 /* Mark the last named section. */ 1958 1959 mdoc->lastnamed = sec; 1960 1961 /* Check particular section/manual conventions. */ 1962 1963 if (mdoc->meta.msec == NULL) 1964 return; 1965 1966 goodsec = NULL; 1967 switch (sec) { 1968 case SEC_ERRORS: 1969 if (*mdoc->meta.msec == '4') 1970 break; 1971 goodsec = "2, 3, 4, 9"; 1972 /* FALLTHROUGH */ 1973 case SEC_RETURN_VALUES: 1974 case SEC_LIBRARY: 1975 if (*mdoc->meta.msec == '2') 1976 break; 1977 if (*mdoc->meta.msec == '3') 1978 break; 1979 if (NULL == goodsec) 1980 goodsec = "2, 3, 9"; 1981 /* FALLTHROUGH */ 1982 case SEC_CONTEXT: 1983 if (*mdoc->meta.msec == '9') 1984 break; 1985 if (NULL == goodsec) 1986 goodsec = "9"; 1987 mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse, 1988 mdoc->last->line, mdoc->last->pos, 1989 "Sh %s for %s only", secnames[sec], goodsec); 1990 break; 1991 default: 1992 break; 1993 } 1994 } 1995 1996 static void 1997 post_xr(POST_ARGS) 1998 { 1999 struct roff_node *n, *nch; 2000 2001 n = mdoc->last; 2002 nch = n->child; 2003 if (nch->next == NULL) { 2004 mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, 2005 n->line, n->pos, "Xr %s", nch->string); 2006 return; 2007 } 2008 assert(nch->next == n->last); 2009 } 2010 2011 static void 2012 post_ignpar(POST_ARGS) 2013 { 2014 struct roff_node *np; 2015 2016 switch (mdoc->last->type) { 2017 case ROFFT_HEAD: 2018 post_hyph(mdoc); 2019 return; 2020 case ROFFT_BODY: 2021 break; 2022 default: 2023 return; 2024 } 2025 2026 if ((np = mdoc->last->child) != NULL) 2027 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 2028 mandoc_vmsg(MANDOCERR_PAR_SKIP, 2029 mdoc->parse, np->line, np->pos, 2030 "%s after %s", mdoc_macronames[np->tok], 2031 mdoc_macronames[mdoc->last->tok]); 2032 roff_node_delete(mdoc, np); 2033 } 2034 2035 if ((np = mdoc->last->last) != NULL) 2036 if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { 2037 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 2038 np->line, np->pos, "%s at the end of %s", 2039 mdoc_macronames[np->tok], 2040 mdoc_macronames[mdoc->last->tok]); 2041 roff_node_delete(mdoc, np); 2042 } 2043 } 2044 2045 static void 2046 post_prevpar(POST_ARGS) 2047 { 2048 struct roff_node *n; 2049 2050 n = mdoc->last; 2051 if (NULL == n->prev) 2052 return; 2053 if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) 2054 return; 2055 2056 /* 2057 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type 2058 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. 2059 */ 2060 2061 if (n->prev->tok != MDOC_Pp && 2062 n->prev->tok != MDOC_Lp && 2063 n->prev->tok != MDOC_br) 2064 return; 2065 if (n->tok == MDOC_Bl && n->norm->Bl.comp) 2066 return; 2067 if (n->tok == MDOC_Bd && n->norm->Bd.comp) 2068 return; 2069 if (n->tok == MDOC_It && n->parent->norm->Bl.comp) 2070 return; 2071 2072 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 2073 n->prev->line, n->prev->pos, 2074 "%s before %s", mdoc_macronames[n->prev->tok], 2075 mdoc_macronames[n->tok]); 2076 roff_node_delete(mdoc, n->prev); 2077 } 2078 2079 static void 2080 post_par(POST_ARGS) 2081 { 2082 struct roff_node *np; 2083 2084 np = mdoc->last; 2085 if (np->tok != MDOC_br && np->tok != MDOC_sp) 2086 post_prevpar(mdoc); 2087 2088 if (np->tok == MDOC_sp) { 2089 if (np->child != NULL && np->child->next != NULL) 2090 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 2091 np->child->next->line, np->child->next->pos, 2092 "sp ... %s", np->child->next->string); 2093 } else if (np->child != NULL) 2094 mandoc_vmsg(MANDOCERR_ARG_SKIP, 2095 mdoc->parse, np->line, np->pos, "%s %s", 2096 mdoc_macronames[np->tok], np->child->string); 2097 2098 if ((np = mdoc->last->prev) == NULL) { 2099 np = mdoc->last->parent; 2100 if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) 2101 return; 2102 } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && 2103 (mdoc->last->tok != MDOC_br || 2104 (np->tok != MDOC_sp && np->tok != MDOC_br))) 2105 return; 2106 2107 mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, 2108 mdoc->last->line, mdoc->last->pos, 2109 "%s after %s", mdoc_macronames[mdoc->last->tok], 2110 mdoc_macronames[np->tok]); 2111 roff_node_delete(mdoc, mdoc->last); 2112 } 2113 2114 static void 2115 post_dd(POST_ARGS) 2116 { 2117 struct roff_node *n; 2118 char *datestr; 2119 2120 n = mdoc->last; 2121 n->flags |= NODE_NOPRT; 2122 2123 if (mdoc->meta.date != NULL) { 2124 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2125 n->line, n->pos, "Dd"); 2126 free(mdoc->meta.date); 2127 } else if (mdoc->flags & MDOC_PBODY) 2128 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2129 n->line, n->pos, "Dd"); 2130 else if (mdoc->meta.title != NULL) 2131 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2132 n->line, n->pos, "Dd after Dt"); 2133 else if (mdoc->meta.os != NULL) 2134 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2135 n->line, n->pos, "Dd after Os"); 2136 2137 if (n->child == NULL || n->child->string[0] == '\0') { 2138 mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : 2139 mandoc_normdate(mdoc->parse, NULL, n->line, n->pos); 2140 return; 2141 } 2142 2143 datestr = NULL; 2144 deroff(&datestr, n); 2145 if (mdoc->quick) 2146 mdoc->meta.date = datestr; 2147 else { 2148 mdoc->meta.date = mandoc_normdate(mdoc->parse, 2149 datestr, n->line, n->pos); 2150 free(datestr); 2151 } 2152 } 2153 2154 static void 2155 post_dt(POST_ARGS) 2156 { 2157 struct roff_node *nn, *n; 2158 const char *cp; 2159 char *p; 2160 2161 n = mdoc->last; 2162 n->flags |= NODE_NOPRT; 2163 2164 if (mdoc->flags & MDOC_PBODY) { 2165 mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse, 2166 n->line, n->pos, "Dt"); 2167 return; 2168 } 2169 2170 if (mdoc->meta.title != NULL) 2171 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2172 n->line, n->pos, "Dt"); 2173 else if (mdoc->meta.os != NULL) 2174 mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse, 2175 n->line, n->pos, "Dt after Os"); 2176 2177 free(mdoc->meta.title); 2178 free(mdoc->meta.msec); 2179 free(mdoc->meta.vol); 2180 free(mdoc->meta.arch); 2181 2182 mdoc->meta.title = NULL; 2183 mdoc->meta.msec = NULL; 2184 mdoc->meta.vol = NULL; 2185 mdoc->meta.arch = NULL; 2186 2187 /* Mandatory first argument: title. */ 2188 2189 nn = n->child; 2190 if (nn == NULL || *nn->string == '\0') { 2191 mandoc_msg(MANDOCERR_DT_NOTITLE, 2192 mdoc->parse, n->line, n->pos, "Dt"); 2193 mdoc->meta.title = mandoc_strdup("UNTITLED"); 2194 } else { 2195 mdoc->meta.title = mandoc_strdup(nn->string); 2196 2197 /* Check that all characters are uppercase. */ 2198 2199 for (p = nn->string; *p != '\0'; p++) 2200 if (islower((unsigned char)*p)) { 2201 mandoc_vmsg(MANDOCERR_TITLE_CASE, 2202 mdoc->parse, nn->line, 2203 nn->pos + (p - nn->string), 2204 "Dt %s", nn->string); 2205 break; 2206 } 2207 } 2208 2209 /* Mandatory second argument: section. */ 2210 2211 if (nn != NULL) 2212 nn = nn->next; 2213 2214 if (nn == NULL) { 2215 mandoc_vmsg(MANDOCERR_MSEC_MISSING, 2216 mdoc->parse, n->line, n->pos, 2217 "Dt %s", mdoc->meta.title); 2218 mdoc->meta.vol = mandoc_strdup("LOCAL"); 2219 return; /* msec and arch remain NULL. */ 2220 } 2221 2222 mdoc->meta.msec = mandoc_strdup(nn->string); 2223 2224 /* Infer volume title from section number. */ 2225 2226 cp = mandoc_a2msec(nn->string); 2227 if (cp == NULL) { 2228 mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse, 2229 nn->line, nn->pos, "Dt ... %s", nn->string); 2230 mdoc->meta.vol = mandoc_strdup(nn->string); 2231 } else 2232 mdoc->meta.vol = mandoc_strdup(cp); 2233 2234 /* Optional third argument: architecture. */ 2235 2236 if ((nn = nn->next) == NULL) 2237 return; 2238 2239 for (p = nn->string; *p != '\0'; p++) 2240 *p = tolower((unsigned char)*p); 2241 mdoc->meta.arch = mandoc_strdup(nn->string); 2242 2243 /* Ignore fourth and later arguments. */ 2244 2245 if ((nn = nn->next) != NULL) 2246 mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, 2247 nn->line, nn->pos, "Dt ... %s", nn->string); 2248 } 2249 2250 static void 2251 post_bx(POST_ARGS) 2252 { 2253 struct roff_node *n, *nch; 2254 2255 n = mdoc->last; 2256 nch = n->child; 2257 2258 if (nch != NULL) { 2259 mdoc->last = nch; 2260 nch = nch->next; 2261 mdoc->next = ROFF_NEXT_SIBLING; 2262 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2263 mdoc->last->flags |= NODE_NOSRC; 2264 mdoc->next = ROFF_NEXT_SIBLING; 2265 } else 2266 mdoc->next = ROFF_NEXT_CHILD; 2267 roff_word_alloc(mdoc, n->line, n->pos, "BSD"); 2268 mdoc->last->flags |= NODE_NOSRC; 2269 2270 if (nch == NULL) { 2271 mdoc->last = n; 2272 return; 2273 } 2274 2275 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2276 mdoc->last->flags |= NODE_NOSRC; 2277 mdoc->next = ROFF_NEXT_SIBLING; 2278 roff_word_alloc(mdoc, n->line, n->pos, "-"); 2279 mdoc->last->flags |= NODE_NOSRC; 2280 roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns); 2281 mdoc->last->flags |= NODE_NOSRC; 2282 mdoc->last = n; 2283 2284 /* 2285 * Make `Bx's second argument always start with an uppercase 2286 * letter. Groff checks if it's an "accepted" term, but we just 2287 * uppercase blindly. 2288 */ 2289 2290 *nch->string = (char)toupper((unsigned char)*nch->string); 2291 } 2292 2293 static void 2294 post_os(POST_ARGS) 2295 { 2296 #ifndef OSNAME 2297 struct utsname utsname; 2298 static char *defbuf; 2299 #endif 2300 struct roff_node *n; 2301 2302 n = mdoc->last; 2303 n->flags |= NODE_NOPRT; 2304 2305 if (mdoc->meta.os != NULL) 2306 mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse, 2307 n->line, n->pos, "Os"); 2308 else if (mdoc->flags & MDOC_PBODY) 2309 mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, 2310 n->line, n->pos, "Os"); 2311 2312 /* 2313 * Set the operating system by way of the `Os' macro. 2314 * The order of precedence is: 2315 * 1. the argument of the `Os' macro, unless empty 2316 * 2. the -Ios=foo command line argument, if provided 2317 * 3. -DOSNAME="\"foo\"", if provided during compilation 2318 * 4. "sysname release" from uname(3) 2319 */ 2320 2321 free(mdoc->meta.os); 2322 mdoc->meta.os = NULL; 2323 deroff(&mdoc->meta.os, n); 2324 if (mdoc->meta.os) 2325 return; 2326 2327 if (mdoc->defos) { 2328 mdoc->meta.os = mandoc_strdup(mdoc->defos); 2329 return; 2330 } 2331 2332 #ifdef OSNAME 2333 mdoc->meta.os = mandoc_strdup(OSNAME); 2334 #else /*!OSNAME */ 2335 if (defbuf == NULL) { 2336 if (uname(&utsname) == -1) { 2337 mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse, 2338 n->line, n->pos, "Os"); 2339 defbuf = mandoc_strdup("UNKNOWN"); 2340 } else 2341 mandoc_asprintf(&defbuf, "%s %s", 2342 utsname.sysname, utsname.release); 2343 } 2344 mdoc->meta.os = mandoc_strdup(defbuf); 2345 #endif /*!OSNAME*/ 2346 } 2347 2348 enum roff_sec 2349 mdoc_a2sec(const char *p) 2350 { 2351 int i; 2352 2353 for (i = 0; i < (int)SEC__MAX; i++) 2354 if (secnames[i] && 0 == strcmp(p, secnames[i])) 2355 return (enum roff_sec)i; 2356 2357 return SEC_CUSTOM; 2358 } 2359 2360 static size_t 2361 macro2len(int macro) 2362 { 2363 2364 switch (macro) { 2365 case MDOC_Ad: 2366 return 12; 2367 case MDOC_Ao: 2368 return 12; 2369 case MDOC_An: 2370 return 12; 2371 case MDOC_Aq: 2372 return 12; 2373 case MDOC_Ar: 2374 return 12; 2375 case MDOC_Bo: 2376 return 12; 2377 case MDOC_Bq: 2378 return 12; 2379 case MDOC_Cd: 2380 return 12; 2381 case MDOC_Cm: 2382 return 10; 2383 case MDOC_Do: 2384 return 10; 2385 case MDOC_Dq: 2386 return 12; 2387 case MDOC_Dv: 2388 return 12; 2389 case MDOC_Eo: 2390 return 12; 2391 case MDOC_Em: 2392 return 10; 2393 case MDOC_Er: 2394 return 17; 2395 case MDOC_Ev: 2396 return 15; 2397 case MDOC_Fa: 2398 return 12; 2399 case MDOC_Fl: 2400 return 10; 2401 case MDOC_Fo: 2402 return 16; 2403 case MDOC_Fn: 2404 return 16; 2405 case MDOC_Ic: 2406 return 10; 2407 case MDOC_Li: 2408 return 16; 2409 case MDOC_Ms: 2410 return 6; 2411 case MDOC_Nm: 2412 return 10; 2413 case MDOC_No: 2414 return 12; 2415 case MDOC_Oo: 2416 return 10; 2417 case MDOC_Op: 2418 return 14; 2419 case MDOC_Pa: 2420 return 32; 2421 case MDOC_Pf: 2422 return 12; 2423 case MDOC_Po: 2424 return 12; 2425 case MDOC_Pq: 2426 return 12; 2427 case MDOC_Ql: 2428 return 16; 2429 case MDOC_Qo: 2430 return 12; 2431 case MDOC_So: 2432 return 12; 2433 case MDOC_Sq: 2434 return 12; 2435 case MDOC_Sy: 2436 return 6; 2437 case MDOC_Sx: 2438 return 16; 2439 case MDOC_Tn: 2440 return 10; 2441 case MDOC_Va: 2442 return 12; 2443 case MDOC_Vt: 2444 return 12; 2445 case MDOC_Xr: 2446 return 10; 2447 default: 2448 break; 2449 }; 2450 return 0; 2451 } 2452