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