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