1 /* $Id: man_term.c,v 1.88 2012/07/29 12:35:05 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <sys/types.h> 19 20 #include <assert.h> 21 #include <ctype.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "mandoc.h" 27 #include "out.h" 28 #include "man.h" 29 #include "term.h" 30 #include "main.h" 31 32 #define MAXMARGINS 64 /* maximum number of indented scopes */ 33 34 struct mtermp { 35 int fl; 36 #define MANT_LITERAL (1 << 0) 37 size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ 38 int lmargincur; /* index of current margin */ 39 int lmarginsz; /* actual number of nested margins */ 40 size_t offset; /* default offset to visible page */ 41 int pardist; /* vert. space before par., unit: [v] */ 42 }; 43 44 #define DECL_ARGS struct termp *p, \ 45 struct mtermp *mt, \ 46 const struct man_node *n, \ 47 const struct man_meta *m 48 49 struct termact { 50 int (*pre)(DECL_ARGS); 51 void (*post)(DECL_ARGS); 52 int flags; 53 #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 54 }; 55 56 static int a2width(const struct termp *, const char *); 57 static size_t a2height(const struct termp *, const char *); 58 59 static void print_man_nodelist(DECL_ARGS); 60 static void print_man_node(DECL_ARGS); 61 static void print_man_head(struct termp *, const void *); 62 static void print_man_foot(struct termp *, const void *); 63 static void print_bvspace(struct termp *, 64 const struct man_node *, int); 65 66 static int pre_B(DECL_ARGS); 67 static int pre_HP(DECL_ARGS); 68 static int pre_I(DECL_ARGS); 69 static int pre_IP(DECL_ARGS); 70 static int pre_OP(DECL_ARGS); 71 static int pre_PD(DECL_ARGS); 72 static int pre_PP(DECL_ARGS); 73 static int pre_RS(DECL_ARGS); 74 static int pre_SH(DECL_ARGS); 75 static int pre_SS(DECL_ARGS); 76 static int pre_TP(DECL_ARGS); 77 static int pre_alternate(DECL_ARGS); 78 static int pre_ft(DECL_ARGS); 79 static int pre_ign(DECL_ARGS); 80 static int pre_in(DECL_ARGS); 81 static int pre_literal(DECL_ARGS); 82 static int pre_sp(DECL_ARGS); 83 84 static void post_IP(DECL_ARGS); 85 static void post_HP(DECL_ARGS); 86 static void post_RS(DECL_ARGS); 87 static void post_SH(DECL_ARGS); 88 static void post_SS(DECL_ARGS); 89 static void post_TP(DECL_ARGS); 90 91 static const struct termact termacts[MAN_MAX] = { 92 { pre_sp, NULL, MAN_NOTEXT }, /* br */ 93 { NULL, NULL, 0 }, /* TH */ 94 { pre_SH, post_SH, 0 }, /* SH */ 95 { pre_SS, post_SS, 0 }, /* SS */ 96 { pre_TP, post_TP, 0 }, /* TP */ 97 { pre_PP, NULL, 0 }, /* LP */ 98 { pre_PP, NULL, 0 }, /* PP */ 99 { pre_PP, NULL, 0 }, /* P */ 100 { pre_IP, post_IP, 0 }, /* IP */ 101 { pre_HP, post_HP, 0 }, /* HP */ 102 { NULL, NULL, 0 }, /* SM */ 103 { pre_B, NULL, 0 }, /* SB */ 104 { pre_alternate, NULL, 0 }, /* BI */ 105 { pre_alternate, NULL, 0 }, /* IB */ 106 { pre_alternate, NULL, 0 }, /* BR */ 107 { pre_alternate, NULL, 0 }, /* RB */ 108 { NULL, NULL, 0 }, /* R */ 109 { pre_B, NULL, 0 }, /* B */ 110 { pre_I, NULL, 0 }, /* I */ 111 { pre_alternate, NULL, 0 }, /* IR */ 112 { pre_alternate, NULL, 0 }, /* RI */ 113 { pre_ign, NULL, MAN_NOTEXT }, /* na */ 114 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 115 { pre_literal, NULL, 0 }, /* nf */ 116 { pre_literal, NULL, 0 }, /* fi */ 117 { NULL, NULL, 0 }, /* RE */ 118 { pre_RS, post_RS, 0 }, /* RS */ 119 { pre_ign, NULL, 0 }, /* DT */ 120 { pre_ign, NULL, 0 }, /* UC */ 121 { pre_PD, NULL, MAN_NOTEXT }, /* PD */ 122 { pre_ign, NULL, 0 }, /* AT */ 123 { pre_in, NULL, MAN_NOTEXT }, /* in */ 124 { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 125 { pre_OP, NULL, 0 }, /* OP */ 126 { pre_literal, NULL, 0 }, /* EX */ 127 { pre_literal, NULL, 0 }, /* EE */ 128 }; 129 130 131 132 void 133 terminal_man(void *arg, const struct man *man) 134 { 135 struct termp *p; 136 const struct man_node *n; 137 const struct man_meta *m; 138 struct mtermp mt; 139 140 p = (struct termp *)arg; 141 142 if (0 == p->defindent) 143 p->defindent = 7; 144 145 p->overstep = 0; 146 p->maxrmargin = p->defrmargin; 147 p->tabwidth = term_len(p, 5); 148 149 if (NULL == p->symtab) 150 p->symtab = mchars_alloc(); 151 152 n = man_node(man); 153 m = man_meta(man); 154 155 term_begin(p, print_man_head, print_man_foot, m); 156 p->flags |= TERMP_NOSPACE; 157 158 memset(&mt, 0, sizeof(struct mtermp)); 159 160 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); 161 mt.offset = term_len(p, p->defindent); 162 mt.pardist = 1; 163 164 if (n->child) 165 print_man_nodelist(p, &mt, n->child, m); 166 167 term_end(p); 168 } 169 170 171 static size_t 172 a2height(const struct termp *p, const char *cp) 173 { 174 struct roffsu su; 175 176 if ( ! a2roffsu(cp, &su, SCALE_VS)) 177 SCALE_VS_INIT(&su, atoi(cp)); 178 179 return(term_vspan(p, &su)); 180 } 181 182 183 static int 184 a2width(const struct termp *p, const char *cp) 185 { 186 struct roffsu su; 187 188 if ( ! a2roffsu(cp, &su, SCALE_BU)) 189 return(-1); 190 191 return((int)term_hspan(p, &su)); 192 } 193 194 /* 195 * Printing leading vertical space before a block. 196 * This is used for the paragraph macros. 197 * The rules are pretty simple, since there's very little nesting going 198 * on here. Basically, if we're the first within another block (SS/SH), 199 * then don't emit vertical space. If we are (RS), then do. If not the 200 * first, print it. 201 */ 202 static void 203 print_bvspace(struct termp *p, const struct man_node *n, int pardist) 204 { 205 int i; 206 207 term_newln(p); 208 209 if (n->body && n->body->child) 210 if (MAN_TBL == n->body->child->type) 211 return; 212 213 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) 214 if (NULL == n->prev) 215 return; 216 217 for (i = 0; i < pardist; i++) 218 term_vspace(p); 219 } 220 221 /* ARGSUSED */ 222 static int 223 pre_ign(DECL_ARGS) 224 { 225 226 return(0); 227 } 228 229 230 /* ARGSUSED */ 231 static int 232 pre_I(DECL_ARGS) 233 { 234 235 term_fontrepl(p, TERMFONT_UNDER); 236 return(1); 237 } 238 239 240 /* ARGSUSED */ 241 static int 242 pre_literal(DECL_ARGS) 243 { 244 245 term_newln(p); 246 247 if (MAN_nf == n->tok || MAN_EX == n->tok) 248 mt->fl |= MANT_LITERAL; 249 else 250 mt->fl &= ~MANT_LITERAL; 251 252 /* 253 * Unlike .IP and .TP, .HP does not have a HEAD. 254 * So in case a second call to term_flushln() is needed, 255 * indentation has to be set up explicitly. 256 */ 257 if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { 258 p->offset = p->rmargin; 259 p->rmargin = p->maxrmargin; 260 p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE); 261 p->flags |= TERMP_NOSPACE; 262 } 263 264 return(0); 265 } 266 267 /* ARGSUSED */ 268 static int 269 pre_PD(DECL_ARGS) 270 { 271 272 n = n->child; 273 if (0 == n) { 274 mt->pardist = 1; 275 return(0); 276 } 277 assert(MAN_TEXT == n->type); 278 mt->pardist = atoi(n->string); 279 return(0); 280 } 281 282 /* ARGSUSED */ 283 static int 284 pre_alternate(DECL_ARGS) 285 { 286 enum termfont font[2]; 287 const struct man_node *nn; 288 int savelit, i; 289 290 switch (n->tok) { 291 case (MAN_RB): 292 font[0] = TERMFONT_NONE; 293 font[1] = TERMFONT_BOLD; 294 break; 295 case (MAN_RI): 296 font[0] = TERMFONT_NONE; 297 font[1] = TERMFONT_UNDER; 298 break; 299 case (MAN_BR): 300 font[0] = TERMFONT_BOLD; 301 font[1] = TERMFONT_NONE; 302 break; 303 case (MAN_BI): 304 font[0] = TERMFONT_BOLD; 305 font[1] = TERMFONT_UNDER; 306 break; 307 case (MAN_IR): 308 font[0] = TERMFONT_UNDER; 309 font[1] = TERMFONT_NONE; 310 break; 311 case (MAN_IB): 312 font[0] = TERMFONT_UNDER; 313 font[1] = TERMFONT_BOLD; 314 break; 315 default: 316 abort(); 317 } 318 319 savelit = MANT_LITERAL & mt->fl; 320 mt->fl &= ~MANT_LITERAL; 321 322 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 323 term_fontrepl(p, font[i]); 324 if (savelit && NULL == nn->next) 325 mt->fl |= MANT_LITERAL; 326 print_man_node(p, mt, nn, m); 327 if (nn->next) 328 p->flags |= TERMP_NOSPACE; 329 } 330 331 return(0); 332 } 333 334 /* ARGSUSED */ 335 static int 336 pre_B(DECL_ARGS) 337 { 338 339 term_fontrepl(p, TERMFONT_BOLD); 340 return(1); 341 } 342 343 /* ARGSUSED */ 344 static int 345 pre_OP(DECL_ARGS) 346 { 347 348 term_word(p, "["); 349 p->flags |= TERMP_NOSPACE; 350 351 if (NULL != (n = n->child)) { 352 term_fontrepl(p, TERMFONT_BOLD); 353 term_word(p, n->string); 354 } 355 if (NULL != n && NULL != n->next) { 356 term_fontrepl(p, TERMFONT_UNDER); 357 term_word(p, n->next->string); 358 } 359 360 term_fontrepl(p, TERMFONT_NONE); 361 p->flags |= TERMP_NOSPACE; 362 term_word(p, "]"); 363 return(0); 364 } 365 366 /* ARGSUSED */ 367 static int 368 pre_ft(DECL_ARGS) 369 { 370 const char *cp; 371 372 if (NULL == n->child) { 373 term_fontlast(p); 374 return(0); 375 } 376 377 cp = n->child->string; 378 switch (*cp) { 379 case ('4'): 380 /* FALLTHROUGH */ 381 case ('3'): 382 /* FALLTHROUGH */ 383 case ('B'): 384 term_fontrepl(p, TERMFONT_BOLD); 385 break; 386 case ('2'): 387 /* FALLTHROUGH */ 388 case ('I'): 389 term_fontrepl(p, TERMFONT_UNDER); 390 break; 391 case ('P'): 392 term_fontlast(p); 393 break; 394 case ('1'): 395 /* FALLTHROUGH */ 396 case ('C'): 397 /* FALLTHROUGH */ 398 case ('R'): 399 term_fontrepl(p, TERMFONT_NONE); 400 break; 401 default: 402 break; 403 } 404 return(0); 405 } 406 407 /* ARGSUSED */ 408 static int 409 pre_in(DECL_ARGS) 410 { 411 int len, less; 412 size_t v; 413 const char *cp; 414 415 term_newln(p); 416 417 if (NULL == n->child) { 418 p->offset = mt->offset; 419 return(0); 420 } 421 422 cp = n->child->string; 423 less = 0; 424 425 if ('-' == *cp) 426 less = -1; 427 else if ('+' == *cp) 428 less = 1; 429 else 430 cp--; 431 432 if ((len = a2width(p, ++cp)) < 0) 433 return(0); 434 435 v = (size_t)len; 436 437 if (less < 0) 438 p->offset -= p->offset > v ? v : p->offset; 439 else if (less > 0) 440 p->offset += v; 441 else 442 p->offset = v; 443 444 /* Don't let this creep beyond the right margin. */ 445 446 if (p->offset > p->rmargin) 447 p->offset = p->rmargin; 448 449 return(0); 450 } 451 452 453 /* ARGSUSED */ 454 static int 455 pre_sp(DECL_ARGS) 456 { 457 char *s; 458 size_t i, len; 459 int neg; 460 461 if ((NULL == n->prev && n->parent)) { 462 switch (n->parent->tok) { 463 case (MAN_SH): 464 /* FALLTHROUGH */ 465 case (MAN_SS): 466 /* FALLTHROUGH */ 467 case (MAN_PP): 468 /* FALLTHROUGH */ 469 case (MAN_LP): 470 /* FALLTHROUGH */ 471 case (MAN_P): 472 /* FALLTHROUGH */ 473 return(0); 474 default: 475 break; 476 } 477 } 478 479 neg = 0; 480 switch (n->tok) { 481 case (MAN_br): 482 len = 0; 483 break; 484 default: 485 if (NULL == n->child) { 486 len = 1; 487 break; 488 } 489 s = n->child->string; 490 if ('-' == *s) { 491 neg = 1; 492 s++; 493 } 494 len = a2height(p, s); 495 break; 496 } 497 498 if (0 == len) 499 term_newln(p); 500 else if (neg) 501 p->skipvsp += len; 502 else 503 for (i = 0; i < len; i++) 504 term_vspace(p); 505 506 return(0); 507 } 508 509 510 /* ARGSUSED */ 511 static int 512 pre_HP(DECL_ARGS) 513 { 514 size_t len, one; 515 int ival; 516 const struct man_node *nn; 517 518 switch (n->type) { 519 case (MAN_BLOCK): 520 print_bvspace(p, n, mt->pardist); 521 return(1); 522 case (MAN_BODY): 523 break; 524 default: 525 return(0); 526 } 527 528 if ( ! (MANT_LITERAL & mt->fl)) { 529 p->flags |= TERMP_NOBREAK; 530 p->flags |= TERMP_TWOSPACE; 531 } 532 533 len = mt->lmargin[mt->lmargincur]; 534 ival = -1; 535 536 /* Calculate offset. */ 537 538 if (NULL != (nn = n->parent->head->child)) 539 if ((ival = a2width(p, nn->string)) >= 0) 540 len = (size_t)ival; 541 542 one = term_len(p, 1); 543 if (len < one) 544 len = one; 545 546 p->offset = mt->offset; 547 p->rmargin = mt->offset + len; 548 549 if (ival >= 0) 550 mt->lmargin[mt->lmargincur] = (size_t)ival; 551 552 return(1); 553 } 554 555 556 /* ARGSUSED */ 557 static void 558 post_HP(DECL_ARGS) 559 { 560 561 switch (n->type) { 562 case (MAN_BODY): 563 term_flushln(p); 564 p->flags &= ~TERMP_NOBREAK; 565 p->flags &= ~TERMP_TWOSPACE; 566 p->offset = mt->offset; 567 p->rmargin = p->maxrmargin; 568 break; 569 default: 570 break; 571 } 572 } 573 574 575 /* ARGSUSED */ 576 static int 577 pre_PP(DECL_ARGS) 578 { 579 580 switch (n->type) { 581 case (MAN_BLOCK): 582 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 583 print_bvspace(p, n, mt->pardist); 584 break; 585 default: 586 p->offset = mt->offset; 587 break; 588 } 589 590 return(MAN_HEAD != n->type); 591 } 592 593 594 /* ARGSUSED */ 595 static int 596 pre_IP(DECL_ARGS) 597 { 598 const struct man_node *nn; 599 size_t len; 600 int savelit, ival; 601 602 switch (n->type) { 603 case (MAN_BODY): 604 p->flags |= TERMP_NOSPACE; 605 break; 606 case (MAN_HEAD): 607 p->flags |= TERMP_NOBREAK; 608 break; 609 case (MAN_BLOCK): 610 print_bvspace(p, n, mt->pardist); 611 /* FALLTHROUGH */ 612 default: 613 return(1); 614 } 615 616 len = mt->lmargin[mt->lmargincur]; 617 ival = -1; 618 619 /* Calculate the offset from the optional second argument. */ 620 if (NULL != (nn = n->parent->head->child)) 621 if (NULL != (nn = nn->next)) 622 if ((ival = a2width(p, nn->string)) >= 0) 623 len = (size_t)ival; 624 625 switch (n->type) { 626 case (MAN_HEAD): 627 /* Handle zero-width lengths. */ 628 if (0 == len) 629 len = term_len(p, 1); 630 631 p->offset = mt->offset; 632 p->rmargin = mt->offset + len; 633 if (ival < 0) 634 break; 635 636 /* Set the saved left-margin. */ 637 mt->lmargin[mt->lmargincur] = (size_t)ival; 638 639 savelit = MANT_LITERAL & mt->fl; 640 mt->fl &= ~MANT_LITERAL; 641 642 if (n->child) 643 print_man_node(p, mt, n->child, m); 644 645 if (savelit) 646 mt->fl |= MANT_LITERAL; 647 648 return(0); 649 case (MAN_BODY): 650 p->offset = mt->offset + len; 651 p->rmargin = p->maxrmargin; 652 break; 653 default: 654 break; 655 } 656 657 return(1); 658 } 659 660 661 /* ARGSUSED */ 662 static void 663 post_IP(DECL_ARGS) 664 { 665 666 switch (n->type) { 667 case (MAN_HEAD): 668 term_flushln(p); 669 p->flags &= ~TERMP_NOBREAK; 670 p->rmargin = p->maxrmargin; 671 break; 672 case (MAN_BODY): 673 term_newln(p); 674 break; 675 default: 676 break; 677 } 678 } 679 680 681 /* ARGSUSED */ 682 static int 683 pre_TP(DECL_ARGS) 684 { 685 const struct man_node *nn; 686 size_t len; 687 int savelit, ival; 688 689 switch (n->type) { 690 case (MAN_HEAD): 691 p->flags |= TERMP_NOBREAK; 692 break; 693 case (MAN_BODY): 694 p->flags |= TERMP_NOSPACE; 695 break; 696 case (MAN_BLOCK): 697 print_bvspace(p, n, mt->pardist); 698 /* FALLTHROUGH */ 699 default: 700 return(1); 701 } 702 703 len = (size_t)mt->lmargin[mt->lmargincur]; 704 ival = -1; 705 706 /* Calculate offset. */ 707 708 if (NULL != (nn = n->parent->head->child)) 709 if (nn->string && nn->parent->line == nn->line) 710 if ((ival = a2width(p, nn->string)) >= 0) 711 len = (size_t)ival; 712 713 switch (n->type) { 714 case (MAN_HEAD): 715 /* Handle zero-length properly. */ 716 if (0 == len) 717 len = term_len(p, 1); 718 719 p->offset = mt->offset; 720 p->rmargin = mt->offset + len; 721 722 savelit = MANT_LITERAL & mt->fl; 723 mt->fl &= ~MANT_LITERAL; 724 725 /* Don't print same-line elements. */ 726 for (nn = n->child; nn; nn = nn->next) 727 if (nn->line > n->line) 728 print_man_node(p, mt, nn, m); 729 730 if (savelit) 731 mt->fl |= MANT_LITERAL; 732 if (ival >= 0) 733 mt->lmargin[mt->lmargincur] = (size_t)ival; 734 735 return(0); 736 case (MAN_BODY): 737 p->offset = mt->offset + len; 738 p->rmargin = p->maxrmargin; 739 p->flags &= ~TERMP_NOBREAK; 740 p->flags &= ~TERMP_TWOSPACE; 741 break; 742 default: 743 break; 744 } 745 746 return(1); 747 } 748 749 750 /* ARGSUSED */ 751 static void 752 post_TP(DECL_ARGS) 753 { 754 755 switch (n->type) { 756 case (MAN_HEAD): 757 term_flushln(p); 758 break; 759 case (MAN_BODY): 760 term_newln(p); 761 break; 762 default: 763 break; 764 } 765 } 766 767 768 /* ARGSUSED */ 769 static int 770 pre_SS(DECL_ARGS) 771 { 772 int i; 773 774 switch (n->type) { 775 case (MAN_BLOCK): 776 mt->fl &= ~MANT_LITERAL; 777 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 778 mt->offset = term_len(p, p->defindent); 779 /* If following a prior empty `SS', no vspace. */ 780 if (n->prev && MAN_SS == n->prev->tok) 781 if (NULL == n->prev->body->child) 782 break; 783 if (NULL == n->prev) 784 break; 785 for (i = 0; i < mt->pardist; i++) 786 term_vspace(p); 787 break; 788 case (MAN_HEAD): 789 term_fontrepl(p, TERMFONT_BOLD); 790 p->offset = term_len(p, 3); 791 break; 792 case (MAN_BODY): 793 p->offset = mt->offset; 794 break; 795 default: 796 break; 797 } 798 799 return(1); 800 } 801 802 803 /* ARGSUSED */ 804 static void 805 post_SS(DECL_ARGS) 806 { 807 808 switch (n->type) { 809 case (MAN_HEAD): 810 term_newln(p); 811 break; 812 case (MAN_BODY): 813 term_newln(p); 814 break; 815 default: 816 break; 817 } 818 } 819 820 821 /* ARGSUSED */ 822 static int 823 pre_SH(DECL_ARGS) 824 { 825 int i; 826 827 switch (n->type) { 828 case (MAN_BLOCK): 829 mt->fl &= ~MANT_LITERAL; 830 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 831 mt->offset = term_len(p, p->defindent); 832 /* If following a prior empty `SH', no vspace. */ 833 if (n->prev && MAN_SH == n->prev->tok) 834 if (NULL == n->prev->body->child) 835 break; 836 /* If the first macro, no vspae. */ 837 if (NULL == n->prev) 838 break; 839 for (i = 0; i < mt->pardist; i++) 840 term_vspace(p); 841 break; 842 case (MAN_HEAD): 843 term_fontrepl(p, TERMFONT_BOLD); 844 p->offset = 0; 845 break; 846 case (MAN_BODY): 847 p->offset = mt->offset; 848 break; 849 default: 850 break; 851 } 852 853 return(1); 854 } 855 856 857 /* ARGSUSED */ 858 static void 859 post_SH(DECL_ARGS) 860 { 861 862 switch (n->type) { 863 case (MAN_HEAD): 864 term_newln(p); 865 break; 866 case (MAN_BODY): 867 term_newln(p); 868 break; 869 default: 870 break; 871 } 872 } 873 874 /* ARGSUSED */ 875 static int 876 pre_RS(DECL_ARGS) 877 { 878 int ival; 879 size_t sz; 880 881 switch (n->type) { 882 case (MAN_BLOCK): 883 term_newln(p); 884 return(1); 885 case (MAN_HEAD): 886 return(0); 887 default: 888 break; 889 } 890 891 sz = term_len(p, p->defindent); 892 893 if (NULL != (n = n->parent->head->child)) 894 if ((ival = a2width(p, n->string)) >= 0) 895 sz = (size_t)ival; 896 897 mt->offset += sz; 898 p->rmargin = p->maxrmargin; 899 p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin; 900 901 if (++mt->lmarginsz < MAXMARGINS) 902 mt->lmargincur = mt->lmarginsz; 903 904 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; 905 return(1); 906 } 907 908 /* ARGSUSED */ 909 static void 910 post_RS(DECL_ARGS) 911 { 912 int ival; 913 size_t sz; 914 915 switch (n->type) { 916 case (MAN_BLOCK): 917 return; 918 case (MAN_HEAD): 919 return; 920 default: 921 term_newln(p); 922 break; 923 } 924 925 sz = term_len(p, p->defindent); 926 927 if (NULL != (n = n->parent->head->child)) 928 if ((ival = a2width(p, n->string)) >= 0) 929 sz = (size_t)ival; 930 931 mt->offset = mt->offset < sz ? 0 : mt->offset - sz; 932 p->offset = mt->offset; 933 934 if (--mt->lmarginsz < MAXMARGINS) 935 mt->lmargincur = mt->lmarginsz; 936 } 937 938 static void 939 print_man_node(DECL_ARGS) 940 { 941 size_t rm, rmax; 942 int c; 943 944 switch (n->type) { 945 case(MAN_TEXT): 946 /* 947 * If we have a blank line, output a vertical space. 948 * If we have a space as the first character, break 949 * before printing the line's data. 950 */ 951 if ('\0' == *n->string) { 952 term_vspace(p); 953 return; 954 } else if (' ' == *n->string && MAN_LINE & n->flags) 955 term_newln(p); 956 957 term_word(p, n->string); 958 goto out; 959 960 case (MAN_EQN): 961 term_eqn(p, n->eqn); 962 return; 963 case (MAN_TBL): 964 /* 965 * Tables are preceded by a newline. Then process a 966 * table line, which will cause line termination, 967 */ 968 if (TBL_SPAN_FIRST & n->span->flags) 969 term_newln(p); 970 term_tbl(p, n->span); 971 return; 972 default: 973 break; 974 } 975 976 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 977 term_fontrepl(p, TERMFONT_NONE); 978 979 c = 1; 980 if (termacts[n->tok].pre) 981 c = (*termacts[n->tok].pre)(p, mt, n, m); 982 983 if (c && n->child) 984 print_man_nodelist(p, mt, n->child, m); 985 986 if (termacts[n->tok].post) 987 (*termacts[n->tok].post)(p, mt, n, m); 988 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 989 term_fontrepl(p, TERMFONT_NONE); 990 991 out: 992 /* 993 * If we're in a literal context, make sure that words 994 * together on the same line stay together. This is a 995 * POST-printing call, so we check the NEXT word. Since 996 * -man doesn't have nested macros, we don't need to be 997 * more specific than this. 998 */ 999 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) && 1000 NULL != n->next && n->next->line > n->line) { 1001 rm = p->rmargin; 1002 rmax = p->maxrmargin; 1003 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1004 p->flags |= TERMP_NOSPACE; 1005 if (NULL != n->string && '\0' != *n->string) 1006 term_flushln(p); 1007 else 1008 term_newln(p); 1009 if (rm < rmax && n->parent->tok == MAN_HP) { 1010 p->offset = rm; 1011 p->rmargin = rmax; 1012 } else 1013 p->rmargin = rm; 1014 p->maxrmargin = rmax; 1015 } 1016 if (MAN_EOS & n->flags) 1017 p->flags |= TERMP_SENTENCE; 1018 } 1019 1020 1021 static void 1022 print_man_nodelist(DECL_ARGS) 1023 { 1024 1025 print_man_node(p, mt, n, m); 1026 if ( ! n->next) 1027 return; 1028 print_man_nodelist(p, mt, n->next, m); 1029 } 1030 1031 1032 static void 1033 print_man_foot(struct termp *p, const void *arg) 1034 { 1035 char title[BUFSIZ]; 1036 size_t datelen; 1037 const struct man_meta *meta; 1038 1039 meta = (const struct man_meta *)arg; 1040 assert(meta->title); 1041 assert(meta->msec); 1042 assert(meta->date); 1043 1044 term_fontrepl(p, TERMFONT_NONE); 1045 1046 term_vspace(p); 1047 1048 /* 1049 * Temporary, undocumented option to imitate mdoc(7) output. 1050 * In the bottom right corner, use the source instead of 1051 * the title. 1052 */ 1053 1054 if ( ! p->mdocstyle) { 1055 term_vspace(p); 1056 term_vspace(p); 1057 snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec); 1058 } else if (meta->source) { 1059 strlcpy(title, meta->source, BUFSIZ); 1060 } else { 1061 title[0] = '\0'; 1062 } 1063 datelen = term_strlen(p, meta->date); 1064 1065 /* Bottom left corner: manual source. */ 1066 1067 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1068 p->offset = 0; 1069 p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2; 1070 1071 if (meta->source) 1072 term_word(p, meta->source); 1073 term_flushln(p); 1074 1075 /* At the bottom in the middle: manual date. */ 1076 1077 p->flags |= TERMP_NOSPACE; 1078 p->offset = p->rmargin; 1079 p->rmargin = p->maxrmargin - term_strlen(p, title); 1080 if (p->offset + datelen >= p->rmargin) 1081 p->rmargin = p->offset + datelen; 1082 1083 term_word(p, meta->date); 1084 term_flushln(p); 1085 1086 /* Bottom right corner: manual title and section. */ 1087 1088 p->flags &= ~TERMP_NOBREAK; 1089 p->flags |= TERMP_NOSPACE; 1090 p->offset = p->rmargin; 1091 p->rmargin = p->maxrmargin; 1092 1093 term_word(p, title); 1094 term_flushln(p); 1095 } 1096 1097 1098 static void 1099 print_man_head(struct termp *p, const void *arg) 1100 { 1101 char buf[BUFSIZ], title[BUFSIZ]; 1102 size_t buflen, titlen; 1103 const struct man_meta *m; 1104 1105 m = (const struct man_meta *)arg; 1106 assert(m->title); 1107 assert(m->msec); 1108 1109 if (m->vol) 1110 strlcpy(buf, m->vol, BUFSIZ); 1111 else 1112 buf[0] = '\0'; 1113 buflen = term_strlen(p, buf); 1114 1115 /* Top left corner: manual title and section. */ 1116 1117 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 1118 titlen = term_strlen(p, title); 1119 1120 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1121 p->offset = 0; 1122 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 1123 (p->maxrmargin - 1124 term_strlen(p, buf) + term_len(p, 1)) / 2 : 1125 p->maxrmargin - buflen; 1126 1127 term_word(p, title); 1128 term_flushln(p); 1129 1130 /* At the top in the middle: manual volume. */ 1131 1132 p->flags |= TERMP_NOSPACE; 1133 p->offset = p->rmargin; 1134 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 1135 p->maxrmargin - titlen : p->maxrmargin; 1136 1137 term_word(p, buf); 1138 term_flushln(p); 1139 1140 /* Top right corner: title and section, again. */ 1141 1142 p->flags &= ~TERMP_NOBREAK; 1143 if (p->rmargin + titlen <= p->maxrmargin) { 1144 p->flags |= TERMP_NOSPACE; 1145 p->offset = p->rmargin; 1146 p->rmargin = p->maxrmargin; 1147 term_word(p, title); 1148 term_flushln(p); 1149 } 1150 1151 p->flags &= ~TERMP_NOSPACE; 1152 p->offset = 0; 1153 p->rmargin = p->maxrmargin; 1154 1155 /* 1156 * Groff prints three blank lines before the content. 1157 * Do the same, except in the temporary, undocumented 1158 * mode imitating mdoc(7) output. 1159 */ 1160 1161 term_vspace(p); 1162 if ( ! p->mdocstyle) { 1163 term_vspace(p); 1164 term_vspace(p); 1165 } 1166 } 1167