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