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