1 /* $Id: man_term.c,v 1.109 2011/05/17 14:38:34 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2011 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/types.h> 23 24 #include <assert.h> 25 #include <ctype.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 #include "mandoc.h" 31 #include "out.h" 32 #include "man.h" 33 #include "term.h" 34 #include "main.h" 35 36 #define INDENT 7 37 #define HALFINDENT 3 38 39 /* FIXME: have PD set the default vspace width. */ 40 41 struct mtermp { 42 int fl; 43 #define MANT_LITERAL (1 << 0) 44 /* 45 * Default amount to indent the left margin after leading text 46 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body 47 * indent). This needs to be saved because `HP' and so on, if 48 * not having a specified value, must default. 49 * 50 * Note that this is the indentation AFTER the left offset, so 51 * the total offset is usually offset + lmargin. 52 */ 53 size_t lmargin; 54 /* 55 * The default offset, i.e., the amount between any text and the 56 * page boundary. 57 */ 58 size_t offset; 59 }; 60 61 #define DECL_ARGS struct termp *p, \ 62 struct mtermp *mt, \ 63 const struct man_node *n, \ 64 const struct man_meta *m 65 66 struct termact { 67 int (*pre)(DECL_ARGS); 68 void (*post)(DECL_ARGS); 69 int flags; 70 #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 71 }; 72 73 static int a2width(const struct termp *, const char *); 74 static size_t a2height(const struct termp *, const char *); 75 76 static void print_man_nodelist(DECL_ARGS); 77 static void print_man_node(DECL_ARGS); 78 static void print_man_head(struct termp *, const void *); 79 static void print_man_foot(struct termp *, const void *); 80 static void print_bvspace(struct termp *, 81 const struct man_node *); 82 83 static int pre_alternate(DECL_ARGS); 84 static int pre_B(DECL_ARGS); 85 static int pre_HP(DECL_ARGS); 86 static int pre_I(DECL_ARGS); 87 static int pre_IP(DECL_ARGS); 88 static int pre_PP(DECL_ARGS); 89 static int pre_RS(DECL_ARGS); 90 static int pre_SH(DECL_ARGS); 91 static int pre_SS(DECL_ARGS); 92 static int pre_TP(DECL_ARGS); 93 static int pre_ign(DECL_ARGS); 94 static int pre_in(DECL_ARGS); 95 static int pre_literal(DECL_ARGS); 96 static int pre_sp(DECL_ARGS); 97 static int pre_ft(DECL_ARGS); 98 99 static void post_IP(DECL_ARGS); 100 static void post_HP(DECL_ARGS); 101 static void post_RS(DECL_ARGS); 102 static void post_SH(DECL_ARGS); 103 static void post_SS(DECL_ARGS); 104 static void post_TP(DECL_ARGS); 105 106 static const struct termact termacts[MAN_MAX] = { 107 { pre_sp, NULL, MAN_NOTEXT }, /* br */ 108 { NULL, NULL, 0 }, /* TH */ 109 { pre_SH, post_SH, 0 }, /* SH */ 110 { pre_SS, post_SS, 0 }, /* SS */ 111 { pre_TP, post_TP, 0 }, /* TP */ 112 { pre_PP, NULL, 0 }, /* LP */ 113 { pre_PP, NULL, 0 }, /* PP */ 114 { pre_PP, NULL, 0 }, /* P */ 115 { pre_IP, post_IP, 0 }, /* IP */ 116 { pre_HP, post_HP, 0 }, /* HP */ 117 { NULL, NULL, 0 }, /* SM */ 118 { pre_B, NULL, 0 }, /* SB */ 119 { pre_alternate, NULL, 0 }, /* BI */ 120 { pre_alternate, NULL, 0 }, /* IB */ 121 { pre_alternate, NULL, 0 }, /* BR */ 122 { pre_alternate, NULL, 0 }, /* RB */ 123 { NULL, NULL, 0 }, /* R */ 124 { pre_B, NULL, 0 }, /* B */ 125 { pre_I, NULL, 0 }, /* I */ 126 { pre_alternate, NULL, 0 }, /* IR */ 127 { pre_alternate, NULL, 0 }, /* RI */ 128 { pre_ign, NULL, MAN_NOTEXT }, /* na */ 129 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 130 { pre_literal, NULL, 0 }, /* nf */ 131 { pre_literal, NULL, 0 }, /* fi */ 132 { NULL, NULL, 0 }, /* RE */ 133 { pre_RS, post_RS, 0 }, /* RS */ 134 { pre_ign, NULL, 0 }, /* DT */ 135 { pre_ign, NULL, 0 }, /* UC */ 136 { pre_ign, NULL, 0 }, /* PD */ 137 { pre_ign, NULL, 0 }, /* AT */ 138 { pre_in, NULL, MAN_NOTEXT }, /* in */ 139 { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 140 }; 141 142 143 144 void 145 terminal_man(void *arg, const struct man *man) 146 { 147 struct termp *p; 148 const struct man_node *n; 149 const struct man_meta *m; 150 struct mtermp mt; 151 152 p = (struct termp *)arg; 153 154 p->overstep = 0; 155 p->maxrmargin = p->defrmargin; 156 p->tabwidth = term_len(p, 5); 157 158 if (NULL == p->symtab) 159 p->symtab = mchars_alloc(); 160 161 n = man_node(man); 162 m = man_meta(man); 163 164 term_begin(p, print_man_head, print_man_foot, m); 165 p->flags |= TERMP_NOSPACE; 166 167 mt.fl = 0; 168 mt.lmargin = term_len(p, INDENT); 169 mt.offset = term_len(p, INDENT); 170 171 if (n->child) 172 print_man_nodelist(p, &mt, n->child, m); 173 174 term_end(p); 175 } 176 177 178 static size_t 179 a2height(const struct termp *p, const char *cp) 180 { 181 struct roffsu su; 182 183 if ( ! a2roffsu(cp, &su, SCALE_VS)) 184 SCALE_VS_INIT(&su, term_strlen(p, cp)); 185 186 return(term_vspan(p, &su)); 187 } 188 189 190 static int 191 a2width(const struct termp *p, const char *cp) 192 { 193 struct roffsu su; 194 195 if ( ! a2roffsu(cp, &su, SCALE_BU)) 196 return(-1); 197 198 return((int)term_hspan(p, &su)); 199 } 200 201 202 static void 203 print_bvspace(struct termp *p, const struct man_node *n) 204 { 205 term_newln(p); 206 207 if (n->body && n->body->child && MAN_TBL == n->body->child->type) 208 return; 209 210 if (NULL == n->prev) 211 return; 212 213 if (MAN_SS == n->prev->tok) 214 return; 215 if (MAN_SH == n->prev->tok) 216 return; 217 218 term_vspace(p); 219 } 220 221 222 /* ARGSUSED */ 223 static int 224 pre_ign(DECL_ARGS) 225 { 226 227 return(0); 228 } 229 230 231 /* ARGSUSED */ 232 static int 233 pre_I(DECL_ARGS) 234 { 235 236 term_fontrepl(p, TERMFONT_UNDER); 237 return(1); 238 } 239 240 241 /* ARGSUSED */ 242 static int 243 pre_literal(DECL_ARGS) 244 { 245 246 term_newln(p); 247 248 if (MAN_nf == n->tok) 249 mt->fl |= MANT_LITERAL; 250 else 251 mt->fl &= ~MANT_LITERAL; 252 253 return(0); 254 } 255 256 /* ARGSUSED */ 257 static int 258 pre_alternate(DECL_ARGS) 259 { 260 enum termfont font[2]; 261 const struct man_node *nn; 262 int savelit, i; 263 264 switch (n->tok) { 265 case (MAN_RB): 266 font[0] = TERMFONT_NONE; 267 font[1] = TERMFONT_BOLD; 268 break; 269 case (MAN_RI): 270 font[0] = TERMFONT_NONE; 271 font[1] = TERMFONT_UNDER; 272 break; 273 case (MAN_BR): 274 font[0] = TERMFONT_BOLD; 275 font[1] = TERMFONT_NONE; 276 break; 277 case (MAN_BI): 278 font[0] = TERMFONT_BOLD; 279 font[1] = TERMFONT_UNDER; 280 break; 281 case (MAN_IR): 282 font[0] = TERMFONT_UNDER; 283 font[1] = TERMFONT_NONE; 284 break; 285 case (MAN_IB): 286 font[0] = TERMFONT_UNDER; 287 font[1] = TERMFONT_BOLD; 288 break; 289 default: 290 abort(); 291 } 292 293 savelit = MANT_LITERAL & mt->fl; 294 mt->fl &= ~MANT_LITERAL; 295 296 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 297 term_fontrepl(p, font[i]); 298 if (savelit && NULL == nn->next) 299 mt->fl |= MANT_LITERAL; 300 print_man_node(p, mt, nn, m); 301 if (nn->next) 302 p->flags |= TERMP_NOSPACE; 303 } 304 305 return(0); 306 } 307 308 /* ARGSUSED */ 309 static int 310 pre_B(DECL_ARGS) 311 { 312 313 term_fontrepl(p, TERMFONT_BOLD); 314 return(1); 315 } 316 317 /* ARGSUSED */ 318 static int 319 pre_ft(DECL_ARGS) 320 { 321 const char *cp; 322 323 if (NULL == n->child) { 324 term_fontlast(p); 325 return(0); 326 } 327 328 cp = n->child->string; 329 switch (*cp) { 330 case ('4'): 331 /* FALLTHROUGH */ 332 case ('3'): 333 /* FALLTHROUGH */ 334 case ('B'): 335 term_fontrepl(p, TERMFONT_BOLD); 336 break; 337 case ('2'): 338 /* FALLTHROUGH */ 339 case ('I'): 340 term_fontrepl(p, TERMFONT_UNDER); 341 break; 342 case ('P'): 343 term_fontlast(p); 344 break; 345 case ('1'): 346 /* FALLTHROUGH */ 347 case ('C'): 348 /* FALLTHROUGH */ 349 case ('R'): 350 term_fontrepl(p, TERMFONT_NONE); 351 break; 352 default: 353 break; 354 } 355 return(0); 356 } 357 358 /* ARGSUSED */ 359 static int 360 pre_in(DECL_ARGS) 361 { 362 int len, less; 363 size_t v; 364 const char *cp; 365 366 term_newln(p); 367 368 if (NULL == n->child) { 369 p->offset = mt->offset; 370 return(0); 371 } 372 373 cp = n->child->string; 374 less = 0; 375 376 if ('-' == *cp) 377 less = -1; 378 else if ('+' == *cp) 379 less = 1; 380 else 381 cp--; 382 383 if ((len = a2width(p, ++cp)) < 0) 384 return(0); 385 386 v = (size_t)len; 387 388 if (less < 0) 389 p->offset -= p->offset > v ? v : p->offset; 390 else if (less > 0) 391 p->offset += v; 392 else 393 p->offset = v; 394 395 /* Don't let this creep beyond the right margin. */ 396 397 if (p->offset > p->rmargin) 398 p->offset = p->rmargin; 399 400 return(0); 401 } 402 403 404 /* ARGSUSED */ 405 static int 406 pre_sp(DECL_ARGS) 407 { 408 size_t i, len; 409 410 switch (n->tok) { 411 case (MAN_br): 412 len = 0; 413 break; 414 default: 415 len = n->child ? a2height(p, n->child->string) : 1; 416 break; 417 } 418 419 if (0 == len) 420 term_newln(p); 421 for (i = 0; i < len; i++) 422 term_vspace(p); 423 424 return(0); 425 } 426 427 428 /* ARGSUSED */ 429 static int 430 pre_HP(DECL_ARGS) 431 { 432 size_t len; 433 int ival; 434 const struct man_node *nn; 435 436 switch (n->type) { 437 case (MAN_BLOCK): 438 print_bvspace(p, n); 439 return(1); 440 case (MAN_BODY): 441 p->flags |= TERMP_NOBREAK; 442 p->flags |= TERMP_TWOSPACE; 443 break; 444 default: 445 return(0); 446 } 447 448 len = mt->lmargin; 449 ival = -1; 450 451 /* Calculate offset. */ 452 453 if (NULL != (nn = n->parent->head->child)) 454 if ((ival = a2width(p, nn->string)) >= 0) 455 len = (size_t)ival; 456 457 if (0 == len) 458 len = term_len(p, 1); 459 460 p->offset = mt->offset; 461 p->rmargin = mt->offset + len; 462 463 if (ival >= 0) 464 mt->lmargin = (size_t)ival; 465 466 return(1); 467 } 468 469 470 /* ARGSUSED */ 471 static void 472 post_HP(DECL_ARGS) 473 { 474 475 switch (n->type) { 476 case (MAN_BLOCK): 477 term_flushln(p); 478 break; 479 case (MAN_BODY): 480 term_flushln(p); 481 p->flags &= ~TERMP_NOBREAK; 482 p->flags &= ~TERMP_TWOSPACE; 483 p->offset = mt->offset; 484 p->rmargin = p->maxrmargin; 485 break; 486 default: 487 break; 488 } 489 } 490 491 492 /* ARGSUSED */ 493 static int 494 pre_PP(DECL_ARGS) 495 { 496 497 switch (n->type) { 498 case (MAN_BLOCK): 499 mt->lmargin = term_len(p, INDENT); 500 print_bvspace(p, n); 501 break; 502 default: 503 p->offset = mt->offset; 504 break; 505 } 506 507 return(MAN_HEAD != n->type); 508 } 509 510 511 /* ARGSUSED */ 512 static int 513 pre_IP(DECL_ARGS) 514 { 515 const struct man_node *nn; 516 size_t len; 517 int savelit, ival; 518 519 switch (n->type) { 520 case (MAN_BODY): 521 p->flags |= TERMP_NOLPAD; 522 p->flags |= TERMP_NOSPACE; 523 break; 524 case (MAN_HEAD): 525 p->flags |= TERMP_NOBREAK; 526 break; 527 case (MAN_BLOCK): 528 print_bvspace(p, n); 529 /* FALLTHROUGH */ 530 default: 531 return(1); 532 } 533 534 len = mt->lmargin; 535 ival = -1; 536 537 /* Calculate the offset from the optional second argument. */ 538 if (NULL != (nn = n->parent->head->child)) 539 if (NULL != (nn = nn->next)) 540 if ((ival = a2width(p, nn->string)) >= 0) 541 len = (size_t)ival; 542 543 switch (n->type) { 544 case (MAN_HEAD): 545 /* Handle zero-width lengths. */ 546 if (0 == len) 547 len = term_len(p, 1); 548 549 p->offset = mt->offset; 550 p->rmargin = mt->offset + len; 551 if (ival < 0) 552 break; 553 554 /* Set the saved left-margin. */ 555 mt->lmargin = (size_t)ival; 556 557 savelit = MANT_LITERAL & mt->fl; 558 mt->fl &= ~MANT_LITERAL; 559 560 if (n->child) 561 print_man_node(p, mt, n->child, m); 562 563 if (savelit) 564 mt->fl |= MANT_LITERAL; 565 566 return(0); 567 case (MAN_BODY): 568 p->offset = mt->offset + len; 569 p->rmargin = p->maxrmargin; 570 break; 571 default: 572 break; 573 } 574 575 return(1); 576 } 577 578 579 /* ARGSUSED */ 580 static void 581 post_IP(DECL_ARGS) 582 { 583 584 switch (n->type) { 585 case (MAN_HEAD): 586 term_flushln(p); 587 p->flags &= ~TERMP_NOBREAK; 588 p->rmargin = p->maxrmargin; 589 break; 590 case (MAN_BODY): 591 term_newln(p); 592 p->flags &= ~TERMP_NOLPAD; 593 break; 594 default: 595 break; 596 } 597 } 598 599 600 /* ARGSUSED */ 601 static int 602 pre_TP(DECL_ARGS) 603 { 604 const struct man_node *nn; 605 size_t len; 606 int savelit, ival; 607 608 switch (n->type) { 609 case (MAN_HEAD): 610 p->flags |= TERMP_NOBREAK; 611 break; 612 case (MAN_BODY): 613 p->flags |= TERMP_NOLPAD; 614 p->flags |= TERMP_NOSPACE; 615 break; 616 case (MAN_BLOCK): 617 print_bvspace(p, n); 618 /* FALLTHROUGH */ 619 default: 620 return(1); 621 } 622 623 len = (size_t)mt->lmargin; 624 ival = -1; 625 626 /* Calculate offset. */ 627 628 if (NULL != (nn = n->parent->head->child)) { 629 while (nn && MAN_TEXT != nn->type) 630 nn = nn->next; 631 if (nn && nn->next) 632 if ((ival = a2width(p, nn->string)) >= 0) 633 len = (size_t)ival; 634 } 635 636 switch (n->type) { 637 case (MAN_HEAD): 638 /* Handle zero-length properly. */ 639 if (0 == len) 640 len = term_len(p, 1); 641 642 p->offset = mt->offset; 643 p->rmargin = mt->offset + len; 644 645 savelit = MANT_LITERAL & mt->fl; 646 mt->fl &= ~MANT_LITERAL; 647 648 /* Don't print same-line elements. */ 649 for (nn = n->child; nn; nn = nn->next) 650 if (nn->line > n->line) 651 print_man_node(p, mt, nn, m); 652 653 if (savelit) 654 mt->fl |= MANT_LITERAL; 655 656 if (ival >= 0) 657 mt->lmargin = (size_t)ival; 658 659 return(0); 660 case (MAN_BODY): 661 p->offset = mt->offset + len; 662 p->rmargin = p->maxrmargin; 663 break; 664 default: 665 break; 666 } 667 668 return(1); 669 } 670 671 672 /* ARGSUSED */ 673 static void 674 post_TP(DECL_ARGS) 675 { 676 677 switch (n->type) { 678 case (MAN_HEAD): 679 term_flushln(p); 680 p->flags &= ~TERMP_NOBREAK; 681 p->flags &= ~TERMP_TWOSPACE; 682 p->rmargin = p->maxrmargin; 683 break; 684 case (MAN_BODY): 685 term_newln(p); 686 p->flags &= ~TERMP_NOLPAD; 687 break; 688 default: 689 break; 690 } 691 } 692 693 694 /* ARGSUSED */ 695 static int 696 pre_SS(DECL_ARGS) 697 { 698 699 switch (n->type) { 700 case (MAN_BLOCK): 701 mt->lmargin = term_len(p, INDENT); 702 mt->offset = term_len(p, INDENT); 703 /* If following a prior empty `SS', no vspace. */ 704 if (n->prev && MAN_SS == n->prev->tok) 705 if (NULL == n->prev->body->child) 706 break; 707 if (NULL == n->prev) 708 break; 709 term_vspace(p); 710 break; 711 case (MAN_HEAD): 712 term_fontrepl(p, TERMFONT_BOLD); 713 p->offset = term_len(p, HALFINDENT); 714 break; 715 case (MAN_BODY): 716 p->offset = mt->offset; 717 break; 718 default: 719 break; 720 } 721 722 return(1); 723 } 724 725 726 /* ARGSUSED */ 727 static void 728 post_SS(DECL_ARGS) 729 { 730 731 switch (n->type) { 732 case (MAN_HEAD): 733 term_newln(p); 734 break; 735 case (MAN_BODY): 736 term_newln(p); 737 break; 738 default: 739 break; 740 } 741 } 742 743 744 /* ARGSUSED */ 745 static int 746 pre_SH(DECL_ARGS) 747 { 748 749 switch (n->type) { 750 case (MAN_BLOCK): 751 mt->lmargin = term_len(p, INDENT); 752 mt->offset = term_len(p, INDENT); 753 /* If following a prior empty `SH', no vspace. */ 754 if (n->prev && MAN_SH == n->prev->tok) 755 if (NULL == n->prev->body->child) 756 break; 757 /* If the first macro, no vspae. */ 758 if (NULL == n->prev) 759 break; 760 term_vspace(p); 761 break; 762 case (MAN_HEAD): 763 term_fontrepl(p, TERMFONT_BOLD); 764 p->offset = 0; 765 break; 766 case (MAN_BODY): 767 p->offset = mt->offset; 768 break; 769 default: 770 break; 771 } 772 773 return(1); 774 } 775 776 777 /* ARGSUSED */ 778 static void 779 post_SH(DECL_ARGS) 780 { 781 782 switch (n->type) { 783 case (MAN_HEAD): 784 term_newln(p); 785 break; 786 case (MAN_BODY): 787 term_newln(p); 788 break; 789 default: 790 break; 791 } 792 } 793 794 795 /* ARGSUSED */ 796 static int 797 pre_RS(DECL_ARGS) 798 { 799 const struct man_node *nn; 800 int ival; 801 802 switch (n->type) { 803 case (MAN_BLOCK): 804 term_newln(p); 805 return(1); 806 case (MAN_HEAD): 807 return(0); 808 default: 809 break; 810 } 811 812 if (NULL == (nn = n->parent->head->child)) { 813 mt->offset = mt->lmargin + term_len(p, INDENT); 814 p->offset = mt->offset; 815 return(1); 816 } 817 818 if ((ival = a2width(p, nn->string)) < 0) 819 return(1); 820 821 mt->offset = term_len(p, INDENT) + (size_t)ival; 822 p->offset = mt->offset; 823 824 return(1); 825 } 826 827 828 /* ARGSUSED */ 829 static void 830 post_RS(DECL_ARGS) 831 { 832 833 switch (n->type) { 834 case (MAN_BLOCK): 835 mt->offset = mt->lmargin = term_len(p, INDENT); 836 break; 837 case (MAN_HEAD): 838 break; 839 default: 840 term_newln(p); 841 p->offset = term_len(p, INDENT); 842 break; 843 } 844 } 845 846 847 static void 848 print_man_node(DECL_ARGS) 849 { 850 size_t rm, rmax; 851 int c; 852 853 switch (n->type) { 854 case(MAN_TEXT): 855 /* 856 * If we have a blank line, output a vertical space. 857 * If we have a space as the first character, break 858 * before printing the line's data. 859 */ 860 if ('\0' == *n->string) { 861 term_vspace(p); 862 return; 863 } else if (' ' == *n->string && MAN_LINE & n->flags) 864 term_newln(p); 865 866 term_word(p, n->string); 867 868 /* 869 * If we're in a literal context, make sure that words 870 * togehter on the same line stay together. This is a 871 * POST-printing call, so we check the NEXT word. Since 872 * -man doesn't have nested macros, we don't need to be 873 * more specific than this. 874 */ 875 if (MANT_LITERAL & mt->fl && 876 (NULL == n->next || 877 n->next->line > n->line)) { 878 rm = p->rmargin; 879 rmax = p->maxrmargin; 880 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 881 p->flags |= TERMP_NOSPACE; 882 term_flushln(p); 883 p->flags &= ~TERMP_NOLPAD; 884 p->rmargin = rm; 885 p->maxrmargin = rmax; 886 } 887 888 if (MAN_EOS & n->flags) 889 p->flags |= TERMP_SENTENCE; 890 return; 891 case (MAN_EQN): 892 term_word(p, n->eqn->data); 893 return; 894 case (MAN_TBL): 895 /* 896 * Tables are preceded by a newline. Then process a 897 * table line, which will cause line termination, 898 */ 899 if (TBL_SPAN_FIRST & n->span->flags) 900 term_newln(p); 901 term_tbl(p, n->span); 902 return; 903 default: 904 break; 905 } 906 907 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 908 term_fontrepl(p, TERMFONT_NONE); 909 910 c = 1; 911 if (termacts[n->tok].pre) 912 c = (*termacts[n->tok].pre)(p, mt, n, m); 913 914 if (c && n->child) 915 print_man_nodelist(p, mt, n->child, m); 916 917 if (termacts[n->tok].post) 918 (*termacts[n->tok].post)(p, mt, n, m); 919 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 920 term_fontrepl(p, TERMFONT_NONE); 921 922 if (MAN_EOS & n->flags) 923 p->flags |= TERMP_SENTENCE; 924 } 925 926 927 static void 928 print_man_nodelist(DECL_ARGS) 929 { 930 931 print_man_node(p, mt, n, m); 932 if ( ! n->next) 933 return; 934 print_man_nodelist(p, mt, n->next, m); 935 } 936 937 938 static void 939 print_man_foot(struct termp *p, const void *arg) 940 { 941 const struct man_meta *meta; 942 943 meta = (const struct man_meta *)arg; 944 945 term_fontrepl(p, TERMFONT_NONE); 946 947 term_vspace(p); 948 term_vspace(p); 949 term_vspace(p); 950 951 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 952 p->rmargin = p->maxrmargin - term_strlen(p, meta->date); 953 p->offset = 0; 954 955 /* term_strlen() can return zero. */ 956 if (p->rmargin == p->maxrmargin) 957 p->rmargin--; 958 959 if (meta->source) 960 term_word(p, meta->source); 961 if (meta->source) 962 term_word(p, ""); 963 term_flushln(p); 964 965 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 966 p->offset = p->rmargin; 967 p->rmargin = p->maxrmargin; 968 p->flags &= ~TERMP_NOBREAK; 969 970 term_word(p, meta->date); 971 term_flushln(p); 972 } 973 974 975 static void 976 print_man_head(struct termp *p, const void *arg) 977 { 978 char buf[BUFSIZ], title[BUFSIZ]; 979 size_t buflen, titlen; 980 const struct man_meta *m; 981 982 m = (const struct man_meta *)arg; 983 984 /* 985 * Note that old groff would spit out some spaces before the 986 * header. We discontinue this strange behaviour, but at one 987 * point we did so here. 988 */ 989 990 p->rmargin = p->maxrmargin; 991 992 p->offset = 0; 993 buf[0] = title[0] = '\0'; 994 995 if (m->vol) 996 strlcpy(buf, m->vol, BUFSIZ); 997 buflen = term_strlen(p, buf); 998 999 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 1000 titlen = term_strlen(p, title); 1001 1002 p->offset = 0; 1003 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 1004 (p->maxrmargin - 1005 term_strlen(p, buf) + term_len(p, 1)) / 2 : 1006 p->maxrmargin - buflen; 1007 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1008 1009 term_word(p, title); 1010 term_flushln(p); 1011 1012 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 1013 p->offset = p->rmargin; 1014 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 1015 p->maxrmargin - titlen : p->maxrmargin; 1016 1017 term_word(p, buf); 1018 term_flushln(p); 1019 1020 p->flags &= ~TERMP_NOBREAK; 1021 if (p->rmargin + titlen <= p->maxrmargin) { 1022 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 1023 p->offset = p->rmargin; 1024 p->rmargin = p->maxrmargin; 1025 term_word(p, title); 1026 term_flushln(p); 1027 } 1028 1029 p->rmargin = p->maxrmargin; 1030 p->offset = 0; 1031 p->flags &= ~TERMP_NOSPACE; 1032 1033 /* 1034 * Groff likes to have some leading spaces before content. Well 1035 * that's fine by me. 1036 */ 1037 1038 term_vspace(p); 1039 term_vspace(p); 1040 term_vspace(p); 1041 } 1042