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