1 /* $Id: man_term.c,v 1.94 2011/01/04 01:23:18 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010 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 "chars.h" 35 #include "main.h" 36 37 #define INDENT 7 38 #define HALFINDENT 3 39 40 /* FIXME: have PD set the default vspace width. */ 41 42 struct mtermp { 43 int fl; 44 #define MANT_LITERAL (1 << 0) 45 /* 46 * Default amount to indent the left margin after leading text 47 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body 48 * indent). This needs to be saved because `HP' and so on, if 49 * not having a specified value, must default. 50 * 51 * Note that this is the indentation AFTER the left offset, so 52 * the total offset is usually offset + lmargin. 53 */ 54 size_t lmargin; 55 /* 56 * The default offset, i.e., the amount between any text and the 57 * page boundary. 58 */ 59 size_t offset; 60 }; 61 62 #define DECL_ARGS struct termp *p, \ 63 struct mtermp *mt, \ 64 const struct man_node *n, \ 65 const struct man_meta *m 66 67 struct termact { 68 int (*pre)(DECL_ARGS); 69 void (*post)(DECL_ARGS); 70 int flags; 71 #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 72 }; 73 74 static int a2width(const struct termp *, const char *); 75 static size_t a2height(const struct termp *, const char *); 76 77 static void print_man_nodelist(DECL_ARGS); 78 static void print_man_node(DECL_ARGS); 79 static void print_man_head(struct termp *, const void *); 80 static void print_man_foot(struct termp *, const void *); 81 static void print_bvspace(struct termp *, 82 const struct man_node *); 83 84 static int pre_alternate(DECL_ARGS); 85 static int pre_B(DECL_ARGS); 86 static int pre_HP(DECL_ARGS); 87 static int pre_I(DECL_ARGS); 88 static int pre_IP(DECL_ARGS); 89 static int pre_PP(DECL_ARGS); 90 static int pre_RS(DECL_ARGS); 91 static int pre_SH(DECL_ARGS); 92 static int pre_SS(DECL_ARGS); 93 static int pre_TP(DECL_ARGS); 94 static int pre_ign(DECL_ARGS); 95 static int pre_in(DECL_ARGS); 96 static int pre_literal(DECL_ARGS); 97 static int pre_sp(DECL_ARGS); 98 static int pre_ft(DECL_ARGS); 99 100 static void post_IP(DECL_ARGS); 101 static void post_HP(DECL_ARGS); 102 static void post_RS(DECL_ARGS); 103 static void post_SH(DECL_ARGS); 104 static void post_SS(DECL_ARGS); 105 static void post_TP(DECL_ARGS); 106 107 static const struct termact termacts[MAN_MAX] = { 108 { pre_sp, NULL, MAN_NOTEXT }, /* br */ 109 { NULL, NULL, 0 }, /* TH */ 110 { pre_SH, post_SH, 0 }, /* SH */ 111 { pre_SS, post_SS, 0 }, /* SS */ 112 { pre_TP, post_TP, 0 }, /* TP */ 113 { pre_PP, NULL, 0 }, /* LP */ 114 { pre_PP, NULL, 0 }, /* PP */ 115 { pre_PP, NULL, 0 }, /* P */ 116 { pre_IP, post_IP, 0 }, /* IP */ 117 { pre_HP, post_HP, 0 }, /* HP */ 118 { NULL, NULL, 0 }, /* SM */ 119 { pre_B, NULL, 0 }, /* SB */ 120 { pre_alternate, NULL, 0 }, /* BI */ 121 { pre_alternate, NULL, 0 }, /* IB */ 122 { pre_alternate, NULL, 0 }, /* BR */ 123 { pre_alternate, NULL, 0 }, /* RB */ 124 { NULL, NULL, 0 }, /* R */ 125 { pre_B, NULL, 0 }, /* B */ 126 { pre_I, NULL, 0 }, /* I */ 127 { pre_alternate, NULL, 0 }, /* IR */ 128 { pre_alternate, NULL, 0 }, /* RI */ 129 { NULL, NULL, MAN_NOTEXT }, /* na */ 130 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 131 { pre_literal, NULL, 0 }, /* nf */ 132 { pre_literal, NULL, 0 }, /* fi */ 133 { NULL, NULL, 0 }, /* RE */ 134 { pre_RS, post_RS, 0 }, /* RS */ 135 { pre_ign, NULL, 0 }, /* DT */ 136 { pre_ign, NULL, 0 }, /* UC */ 137 { pre_ign, NULL, 0 }, /* PD */ 138 { pre_ign, NULL, 0 }, /* AT */ 139 { pre_in, NULL, MAN_NOTEXT }, /* in */ 140 { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 141 }; 142 143 144 145 void 146 terminal_man(void *arg, const struct man *man) 147 { 148 struct termp *p; 149 const struct man_node *n; 150 const struct man_meta *m; 151 struct mtermp mt; 152 153 p = (struct termp *)arg; 154 155 p->overstep = 0; 156 p->maxrmargin = p->defrmargin; 157 p->tabwidth = term_len(p, 5); 158 159 if (NULL == p->symtab) 160 switch (p->enc) { 161 case (TERMENC_ASCII): 162 p->symtab = chars_init(CHARS_ASCII); 163 break; 164 default: 165 abort(); 166 /* NOTREACHED */ 167 } 168 169 n = man_node(man); 170 m = man_meta(man); 171 172 term_begin(p, print_man_head, print_man_foot, m); 173 p->flags |= TERMP_NOSPACE; 174 175 mt.fl = 0; 176 mt.lmargin = term_len(p, INDENT); 177 mt.offset = term_len(p, INDENT); 178 179 if (n->child) 180 print_man_nodelist(p, &mt, n->child, m); 181 182 term_end(p); 183 } 184 185 186 static size_t 187 a2height(const struct termp *p, const char *cp) 188 { 189 struct roffsu su; 190 191 if ( ! a2roffsu(cp, &su, SCALE_VS)) 192 SCALE_VS_INIT(&su, term_strlen(p, cp)); 193 194 return(term_vspan(p, &su)); 195 } 196 197 198 static int 199 a2width(const struct termp *p, const char *cp) 200 { 201 struct roffsu su; 202 203 if ( ! a2roffsu(cp, &su, SCALE_BU)) 204 return(-1); 205 206 return((int)term_hspan(p, &su)); 207 } 208 209 210 static void 211 print_bvspace(struct termp *p, const struct man_node *n) 212 { 213 term_newln(p); 214 215 if (NULL == n->prev) 216 return; 217 218 if (MAN_SS == n->prev->tok) 219 return; 220 if (MAN_SH == n->prev->tok) 221 return; 222 223 term_vspace(p); 224 } 225 226 227 /* ARGSUSED */ 228 static int 229 pre_ign(DECL_ARGS) 230 { 231 232 return(0); 233 } 234 235 236 /* ARGSUSED */ 237 static int 238 pre_I(DECL_ARGS) 239 { 240 241 term_fontrepl(p, TERMFONT_UNDER); 242 return(1); 243 } 244 245 246 /* ARGSUSED */ 247 static int 248 pre_literal(DECL_ARGS) 249 { 250 251 term_newln(p); 252 253 if (MAN_nf == n->tok) 254 mt->fl |= MANT_LITERAL; 255 else 256 mt->fl &= ~MANT_LITERAL; 257 258 return(1); 259 } 260 261 /* ARGSUSED */ 262 static int 263 pre_alternate(DECL_ARGS) 264 { 265 enum termfont font[2]; 266 const struct man_node *nn; 267 int savelit, i; 268 269 switch (n->tok) { 270 case (MAN_RB): 271 font[0] = TERMFONT_NONE; 272 font[1] = TERMFONT_BOLD; 273 break; 274 case (MAN_RI): 275 font[0] = TERMFONT_NONE; 276 font[1] = TERMFONT_UNDER; 277 break; 278 case (MAN_BR): 279 font[0] = TERMFONT_BOLD; 280 font[1] = TERMFONT_NONE; 281 break; 282 case (MAN_BI): 283 font[0] = TERMFONT_BOLD; 284 font[1] = TERMFONT_UNDER; 285 break; 286 case (MAN_IR): 287 font[0] = TERMFONT_UNDER; 288 font[1] = TERMFONT_NONE; 289 break; 290 case (MAN_IB): 291 font[0] = TERMFONT_UNDER; 292 font[1] = TERMFONT_BOLD; 293 break; 294 default: 295 abort(); 296 } 297 298 savelit = MANT_LITERAL & mt->fl; 299 mt->fl &= ~MANT_LITERAL; 300 301 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 302 term_fontrepl(p, font[i]); 303 if (savelit && NULL == nn->next) 304 mt->fl |= MANT_LITERAL; 305 print_man_node(p, mt, nn, m); 306 if (nn->next) 307 p->flags |= TERMP_NOSPACE; 308 } 309 310 return(0); 311 } 312 313 /* ARGSUSED */ 314 static int 315 pre_B(DECL_ARGS) 316 { 317 318 term_fontrepl(p, TERMFONT_BOLD); 319 return(1); 320 } 321 322 /* ARGSUSED */ 323 static int 324 pre_ft(DECL_ARGS) 325 { 326 const char *cp; 327 328 if (NULL == n->child) { 329 term_fontlast(p); 330 return(0); 331 } 332 333 cp = n->child->string; 334 switch (*cp) { 335 case ('4'): 336 /* FALLTHROUGH */ 337 case ('3'): 338 /* FALLTHROUGH */ 339 case ('B'): 340 term_fontrepl(p, TERMFONT_BOLD); 341 break; 342 case ('2'): 343 /* FALLTHROUGH */ 344 case ('I'): 345 term_fontrepl(p, TERMFONT_UNDER); 346 break; 347 case ('P'): 348 term_fontlast(p); 349 break; 350 case ('1'): 351 /* FALLTHROUGH */ 352 case ('C'): 353 /* FALLTHROUGH */ 354 case ('R'): 355 term_fontrepl(p, TERMFONT_NONE); 356 break; 357 default: 358 break; 359 } 360 return(0); 361 } 362 363 /* ARGSUSED */ 364 static int 365 pre_in(DECL_ARGS) 366 { 367 int len, less; 368 size_t v; 369 const char *cp; 370 371 term_newln(p); 372 373 if (NULL == n->child) { 374 p->offset = mt->offset; 375 return(0); 376 } 377 378 cp = n->child->string; 379 less = 0; 380 381 if ('-' == *cp) 382 less = -1; 383 else if ('+' == *cp) 384 less = 1; 385 else 386 cp--; 387 388 if ((len = a2width(p, ++cp)) < 0) 389 return(0); 390 391 v = (size_t)len; 392 393 if (less < 0) 394 p->offset -= p->offset > v ? v : p->offset; 395 else if (less > 0) 396 p->offset += v; 397 else 398 p->offset = v; 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 c = 1; 854 855 switch (n->type) { 856 case(MAN_TEXT): 857 if (0 == *n->string) { 858 term_vspace(p); 859 break; 860 } 861 862 term_word(p, n->string); 863 864 /* FIXME: this means that macro lines are munged! */ 865 866 if (MANT_LITERAL & mt->fl) { 867 rm = p->rmargin; 868 rmax = p->maxrmargin; 869 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 870 p->flags |= TERMP_NOSPACE; 871 term_flushln(p); 872 p->flags &= ~TERMP_NOLPAD; 873 p->rmargin = rm; 874 p->maxrmargin = rmax; 875 } 876 break; 877 case (MAN_TBL): 878 if (TBL_SPAN_FIRST & n->span->flags) 879 term_newln(p); 880 term_tbl(p, n->span); 881 break; 882 default: 883 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 884 term_fontrepl(p, TERMFONT_NONE); 885 if (termacts[n->tok].pre) 886 c = (*termacts[n->tok].pre)(p, mt, n, m); 887 break; 888 } 889 890 if (c && n->child) 891 print_man_nodelist(p, mt, n->child, m); 892 893 switch (n->type) { 894 case (MAN_TEXT): 895 /* FALLTHROUGH */ 896 case (MAN_TBL): 897 break; 898 default: 899 if (termacts[n->tok].post) 900 (*termacts[n->tok].post)(p, mt, n, m); 901 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 902 term_fontrepl(p, TERMFONT_NONE); 903 break; 904 } 905 906 if (MAN_EOS & n->flags) 907 p->flags |= TERMP_SENTENCE; 908 } 909 910 911 static void 912 print_man_nodelist(DECL_ARGS) 913 { 914 915 print_man_node(p, mt, n, m); 916 if ( ! n->next) 917 return; 918 print_man_nodelist(p, mt, n->next, m); 919 } 920 921 922 static void 923 print_man_foot(struct termp *p, const void *arg) 924 { 925 char buf[DATESIZ]; 926 const struct man_meta *meta; 927 928 meta = (const struct man_meta *)arg; 929 930 term_fontrepl(p, TERMFONT_NONE); 931 932 if (meta->rawdate) 933 strlcpy(buf, meta->rawdate, DATESIZ); 934 else 935 time2a(meta->date, buf, DATESIZ); 936 937 term_vspace(p); 938 term_vspace(p); 939 term_vspace(p); 940 941 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 942 p->rmargin = p->maxrmargin - term_strlen(p, buf); 943 p->offset = 0; 944 945 /* term_strlen() can return zero. */ 946 if (p->rmargin == p->maxrmargin) 947 p->rmargin--; 948 949 if (meta->source) 950 term_word(p, meta->source); 951 if (meta->source) 952 term_word(p, ""); 953 term_flushln(p); 954 955 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 956 p->offset = p->rmargin; 957 p->rmargin = p->maxrmargin; 958 p->flags &= ~TERMP_NOBREAK; 959 960 term_word(p, buf); 961 term_flushln(p); 962 } 963 964 965 static void 966 print_man_head(struct termp *p, const void *arg) 967 { 968 char buf[BUFSIZ], title[BUFSIZ]; 969 size_t buflen, titlen; 970 const struct man_meta *m; 971 972 m = (const struct man_meta *)arg; 973 974 /* 975 * Note that old groff would spit out some spaces before the 976 * header. We discontinue this strange behaviour, but at one 977 * point we did so here. 978 */ 979 980 p->rmargin = p->maxrmargin; 981 982 p->offset = 0; 983 buf[0] = title[0] = '\0'; 984 985 if (m->vol) 986 strlcpy(buf, m->vol, BUFSIZ); 987 buflen = term_strlen(p, buf); 988 989 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 990 titlen = term_strlen(p, title); 991 992 p->offset = 0; 993 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 994 (p->maxrmargin - 995 term_strlen(p, buf) + term_len(p, 1)) / 2 : 996 p->maxrmargin - buflen; 997 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 998 999 term_word(p, title); 1000 term_flushln(p); 1001 1002 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 1003 p->offset = p->rmargin; 1004 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 1005 p->maxrmargin - titlen : p->maxrmargin; 1006 1007 term_word(p, buf); 1008 term_flushln(p); 1009 1010 p->flags &= ~TERMP_NOBREAK; 1011 if (p->rmargin + titlen <= p->maxrmargin) { 1012 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 1013 p->offset = p->rmargin; 1014 p->rmargin = p->maxrmargin; 1015 term_word(p, title); 1016 term_flushln(p); 1017 } 1018 1019 p->rmargin = p->maxrmargin; 1020 p->offset = 0; 1021 p->flags &= ~TERMP_NOSPACE; 1022 1023 /* 1024 * Groff likes to have some leading spaces before content. Well 1025 * that's fine by me. 1026 */ 1027 1028 term_vspace(p); 1029 term_vspace(p); 1030 term_vspace(p); 1031 } 1032