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