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