1 /* $Vendor-Id: man_term.c,v 1.84 2010/07/23 13:22:35 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #ifdef HAVE_CONFIG_H 18 #include "config.h" 19 #endif 20 21 #include <sys/types.h> 22 23 #include <assert.h> 24 #include <ctype.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include "mandoc.h" 30 #include "out.h" 31 #include "man.h" 32 #include "term.h" 33 #include "chars.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_B(DECL_ARGS); 84 static int pre_BI(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_RB(DECL_ARGS); 90 static int pre_RI(DECL_ARGS); 91 static int pre_RS(DECL_ARGS); 92 static int pre_SH(DECL_ARGS); 93 static int pre_SS(DECL_ARGS); 94 static int pre_TP(DECL_ARGS); 95 static int pre_ign(DECL_ARGS); 96 static int pre_in(DECL_ARGS); 97 static int pre_literal(DECL_ARGS); 98 static int pre_sp(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_BI, NULL, 0 }, /* BI */ 121 { pre_BI, NULL, 0 }, /* IB */ 122 { pre_RB, NULL, 0 }, /* BR */ 123 { pre_RB, NULL, 0 }, /* RB */ 124 { NULL, NULL, 0 }, /* R */ 125 { pre_B, NULL, 0 }, /* B */ 126 { pre_I, NULL, 0 }, /* I */ 127 { pre_RI, NULL, 0 }, /* IR */ 128 { pre_RI, NULL, 0 }, /* RI */ 129 { NULL, NULL, MAN_NOTEXT }, /* na */ 130 { pre_I, NULL, 0 }, /* i */ 131 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 132 { pre_literal, NULL, 0 }, /* nf */ 133 { pre_literal, NULL, 0 }, /* fi */ 134 { NULL, NULL, 0 }, /* r */ 135 { NULL, NULL, 0 }, /* RE */ 136 { pre_RS, post_RS, 0 }, /* RS */ 137 { pre_ign, NULL, 0 }, /* DT */ 138 { pre_ign, NULL, 0 }, /* UC */ 139 { pre_ign, NULL, 0 }, /* PD */ 140 { pre_sp, NULL, MAN_NOTEXT }, /* Sp */ 141 { pre_literal, NULL, 0 }, /* Vb */ 142 { pre_literal, NULL, 0 }, /* Ve */ 143 { pre_ign, NULL, 0 }, /* AT */ 144 { pre_in, NULL, MAN_NOTEXT }, /* in */ 145 }; 146 147 148 149 void 150 terminal_man(void *arg, const struct man *man) 151 { 152 struct termp *p; 153 const struct man_node *n; 154 const struct man_meta *m; 155 struct mtermp mt; 156 157 p = (struct termp *)arg; 158 159 p->overstep = 0; 160 p->maxrmargin = p->defrmargin; 161 p->tabwidth = term_len(p, 5); 162 163 if (NULL == p->symtab) 164 switch (p->enc) { 165 case (TERMENC_ASCII): 166 p->symtab = chars_init(CHARS_ASCII); 167 break; 168 default: 169 abort(); 170 /* NOTREACHED */ 171 } 172 173 n = man_node(man); 174 m = man_meta(man); 175 176 term_begin(p, print_man_head, print_man_foot, m); 177 p->flags |= TERMP_NOSPACE; 178 179 mt.fl = 0; 180 mt.lmargin = term_len(p, INDENT); 181 mt.offset = term_len(p, INDENT); 182 183 if (n->child) 184 print_man_nodelist(p, &mt, n->child, m); 185 186 term_end(p); 187 } 188 189 190 static size_t 191 a2height(const struct termp *p, const char *cp) 192 { 193 struct roffsu su; 194 195 if ( ! a2roffsu(cp, &su, SCALE_VS)) 196 SCALE_VS_INIT(&su, term_strlen(p, cp)); 197 198 return(term_vspan(p, &su)); 199 } 200 201 202 static int 203 a2width(const struct termp *p, const char *cp) 204 { 205 struct roffsu su; 206 207 if ( ! a2roffsu(cp, &su, SCALE_BU)) 208 return(-1); 209 210 return((int)term_hspan(p, &su)); 211 } 212 213 214 static void 215 print_bvspace(struct termp *p, const struct man_node *n) 216 { 217 term_newln(p); 218 219 if (NULL == n->prev) 220 return; 221 222 if (MAN_SS == n->prev->tok) 223 return; 224 if (MAN_SH == n->prev->tok) 225 return; 226 227 term_vspace(p); 228 } 229 230 231 /* ARGSUSED */ 232 static int 233 pre_ign(DECL_ARGS) 234 { 235 236 return(0); 237 } 238 239 240 /* ARGSUSED */ 241 static int 242 pre_I(DECL_ARGS) 243 { 244 245 term_fontrepl(p, TERMFONT_UNDER); 246 return(1); 247 } 248 249 250 /* ARGSUSED */ 251 static int 252 pre_literal(DECL_ARGS) 253 { 254 255 term_newln(p); 256 switch (n->tok) { 257 case (MAN_Vb): 258 /* FALLTHROUGH */ 259 case (MAN_nf): 260 mt->fl |= MANT_LITERAL; 261 return(MAN_Vb != n->tok); 262 default: 263 mt->fl &= ~MANT_LITERAL; 264 break; 265 } 266 267 return(1); 268 } 269 270 271 272 /* ARGSUSED */ 273 static int 274 pre_RB(DECL_ARGS) 275 { 276 const struct man_node *nn; 277 int i; 278 279 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 280 if (i % 2 && MAN_RB == n->tok) 281 term_fontrepl(p, TERMFONT_BOLD); 282 else if ( ! (i % 2) && MAN_RB != n->tok) 283 term_fontrepl(p, TERMFONT_BOLD); 284 else 285 term_fontrepl(p, TERMFONT_NONE); 286 287 if (i > 0) 288 p->flags |= TERMP_NOSPACE; 289 290 print_man_node(p, mt, nn, m); 291 } 292 return(0); 293 } 294 295 296 /* ARGSUSED */ 297 static int 298 pre_RI(DECL_ARGS) 299 { 300 const struct man_node *nn; 301 int i; 302 303 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 304 if (i % 2 && MAN_RI == n->tok) 305 term_fontrepl(p, TERMFONT_UNDER); 306 else if ( ! (i % 2) && MAN_RI != n->tok) 307 term_fontrepl(p, TERMFONT_UNDER); 308 else 309 term_fontrepl(p, TERMFONT_NONE); 310 311 if (i > 0) 312 p->flags |= TERMP_NOSPACE; 313 314 print_man_node(p, mt, nn, m); 315 } 316 return(0); 317 } 318 319 320 /* ARGSUSED */ 321 static int 322 pre_BI(DECL_ARGS) 323 { 324 const struct man_node *nn; 325 int i; 326 327 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 328 if (i % 2 && MAN_BI == n->tok) 329 term_fontrepl(p, TERMFONT_UNDER); 330 else if (i % 2) 331 term_fontrepl(p, TERMFONT_BOLD); 332 else if (MAN_BI == n->tok) 333 term_fontrepl(p, TERMFONT_BOLD); 334 else 335 term_fontrepl(p, TERMFONT_UNDER); 336 337 if (i) 338 p->flags |= TERMP_NOSPACE; 339 340 print_man_node(p, mt, nn, m); 341 } 342 return(0); 343 } 344 345 346 /* ARGSUSED */ 347 static int 348 pre_B(DECL_ARGS) 349 { 350 351 term_fontrepl(p, TERMFONT_BOLD); 352 return(1); 353 } 354 355 356 /* ARGSUSED */ 357 static int 358 pre_in(DECL_ARGS) 359 { 360 int len, less; 361 size_t v; 362 const char *cp; 363 364 term_newln(p); 365 366 if (NULL == n->child) { 367 p->offset = mt->offset; 368 return(0); 369 } 370 371 cp = n->child->string; 372 less = 0; 373 374 if ('-' == *cp) 375 less = -1; 376 else if ('+' == *cp) 377 less = 1; 378 else 379 cp--; 380 381 if ((len = a2width(p, ++cp)) < 0) 382 return(0); 383 384 v = (size_t)len; 385 386 if (less < 0) 387 p->offset -= p->offset > v ? v : p->offset; 388 else if (less > 0) 389 p->offset += v; 390 else 391 p->offset = v; 392 393 return(0); 394 } 395 396 397 /* ARGSUSED */ 398 static int 399 pre_sp(DECL_ARGS) 400 { 401 size_t i, len; 402 403 switch (n->tok) { 404 case (MAN_br): 405 len = 0; 406 break; 407 default: 408 len = n->child ? a2height(p, n->child->string) : 1; 409 break; 410 } 411 412 if (0 == len) 413 term_newln(p); 414 for (i = 0; i < len; i++) 415 term_vspace(p); 416 417 return(0); 418 } 419 420 421 /* ARGSUSED */ 422 static int 423 pre_HP(DECL_ARGS) 424 { 425 size_t len; 426 int ival; 427 const struct man_node *nn; 428 429 switch (n->type) { 430 case (MAN_BLOCK): 431 print_bvspace(p, n); 432 return(1); 433 case (MAN_BODY): 434 p->flags |= TERMP_NOBREAK; 435 p->flags |= TERMP_TWOSPACE; 436 break; 437 default: 438 return(0); 439 } 440 441 len = mt->lmargin; 442 ival = -1; 443 444 /* Calculate offset. */ 445 446 if (NULL != (nn = n->parent->head->child)) 447 if ((ival = a2width(p, nn->string)) >= 0) 448 len = (size_t)ival; 449 450 if (0 == len) 451 len = term_len(p, 1); 452 453 p->offset = mt->offset; 454 p->rmargin = mt->offset + len; 455 456 if (ival >= 0) 457 mt->lmargin = (size_t)ival; 458 459 return(1); 460 } 461 462 463 /* ARGSUSED */ 464 static void 465 post_HP(DECL_ARGS) 466 { 467 468 switch (n->type) { 469 case (MAN_BLOCK): 470 term_flushln(p); 471 break; 472 case (MAN_BODY): 473 term_flushln(p); 474 p->flags &= ~TERMP_NOBREAK; 475 p->flags &= ~TERMP_TWOSPACE; 476 p->offset = mt->offset; 477 p->rmargin = p->maxrmargin; 478 break; 479 default: 480 break; 481 } 482 } 483 484 485 /* ARGSUSED */ 486 static int 487 pre_PP(DECL_ARGS) 488 { 489 490 switch (n->type) { 491 case (MAN_BLOCK): 492 mt->lmargin = term_len(p, INDENT); 493 print_bvspace(p, n); 494 break; 495 default: 496 p->offset = mt->offset; 497 break; 498 } 499 500 return(1); 501 } 502 503 504 /* ARGSUSED */ 505 static int 506 pre_IP(DECL_ARGS) 507 { 508 const struct man_node *nn; 509 size_t len; 510 int ival; 511 512 switch (n->type) { 513 case (MAN_BODY): 514 p->flags |= TERMP_NOLPAD; 515 p->flags |= TERMP_NOSPACE; 516 break; 517 case (MAN_HEAD): 518 p->flags |= TERMP_NOBREAK; 519 break; 520 case (MAN_BLOCK): 521 print_bvspace(p, n); 522 /* FALLTHROUGH */ 523 default: 524 return(1); 525 } 526 527 len = mt->lmargin; 528 ival = -1; 529 530 /* Calculate offset. */ 531 532 if (NULL != (nn = n->parent->head->child)) 533 if (NULL != (nn = nn->next)) { 534 for ( ; nn->next; nn = nn->next) 535 /* Do nothing. */ ; 536 if ((ival = a2width(p, nn->string)) >= 0) 537 len = (size_t)ival; 538 } 539 540 switch (n->type) { 541 case (MAN_HEAD): 542 /* Handle zero-width lengths. */ 543 if (0 == len) 544 len = term_len(p, 1); 545 546 p->offset = mt->offset; 547 p->rmargin = mt->offset + len; 548 if (ival < 0) 549 break; 550 551 /* Set the saved left-margin. */ 552 mt->lmargin = (size_t)ival; 553 554 /* Don't print the length value. */ 555 for (nn = n->child; nn->next; nn = nn->next) 556 print_man_node(p, mt, nn, m); 557 return(0); 558 case (MAN_BODY): 559 p->offset = mt->offset + len; 560 p->rmargin = p->maxrmargin; 561 break; 562 default: 563 break; 564 } 565 566 return(1); 567 } 568 569 570 /* ARGSUSED */ 571 static void 572 post_IP(DECL_ARGS) 573 { 574 575 switch (n->type) { 576 case (MAN_HEAD): 577 term_flushln(p); 578 p->flags &= ~TERMP_NOBREAK; 579 p->rmargin = p->maxrmargin; 580 break; 581 case (MAN_BODY): 582 term_flushln(p); 583 p->flags &= ~TERMP_NOLPAD; 584 break; 585 default: 586 break; 587 } 588 } 589 590 591 /* ARGSUSED */ 592 static int 593 pre_TP(DECL_ARGS) 594 { 595 const struct man_node *nn; 596 size_t len; 597 int ival; 598 599 switch (n->type) { 600 case (MAN_HEAD): 601 p->flags |= TERMP_NOBREAK; 602 p->flags |= TERMP_TWOSPACE; 603 break; 604 case (MAN_BODY): 605 p->flags |= TERMP_NOLPAD; 606 p->flags |= TERMP_NOSPACE; 607 break; 608 case (MAN_BLOCK): 609 print_bvspace(p, n); 610 /* FALLTHROUGH */ 611 default: 612 return(1); 613 } 614 615 len = (size_t)mt->lmargin; 616 ival = -1; 617 618 /* Calculate offset. */ 619 620 if (NULL != (nn = n->parent->head->child)) { 621 while (nn && MAN_TEXT != nn->type) 622 nn = nn->next; 623 if (nn && nn->next) 624 if ((ival = a2width(p, nn->string)) >= 0) 625 len = (size_t)ival; 626 } 627 628 switch (n->type) { 629 case (MAN_HEAD): 630 /* Handle zero-length properly. */ 631 if (0 == len) 632 len = term_len(p, 1); 633 634 p->offset = mt->offset; 635 p->rmargin = mt->offset + len; 636 637 /* Don't print same-line elements. */ 638 for (nn = n->child; nn; nn = nn->next) 639 if (nn->line > n->line) 640 print_man_node(p, mt, nn, m); 641 642 if (ival >= 0) 643 mt->lmargin = (size_t)ival; 644 645 return(0); 646 case (MAN_BODY): 647 p->offset = mt->offset + len; 648 p->rmargin = p->maxrmargin; 649 break; 650 default: 651 break; 652 } 653 654 return(1); 655 } 656 657 658 /* ARGSUSED */ 659 static void 660 post_TP(DECL_ARGS) 661 { 662 663 switch (n->type) { 664 case (MAN_HEAD): 665 term_flushln(p); 666 p->flags &= ~TERMP_NOBREAK; 667 p->flags &= ~TERMP_TWOSPACE; 668 p->rmargin = p->maxrmargin; 669 break; 670 case (MAN_BODY): 671 term_flushln(p); 672 p->flags &= ~TERMP_NOLPAD; 673 break; 674 default: 675 break; 676 } 677 } 678 679 680 /* ARGSUSED */ 681 static int 682 pre_SS(DECL_ARGS) 683 { 684 685 switch (n->type) { 686 case (MAN_BLOCK): 687 mt->lmargin = term_len(p, INDENT); 688 mt->offset = term_len(p, INDENT); 689 /* If following a prior empty `SS', no vspace. */ 690 if (n->prev && MAN_SS == n->prev->tok) 691 if (NULL == n->prev->body->child) 692 break; 693 if (NULL == n->prev) 694 break; 695 term_vspace(p); 696 break; 697 case (MAN_HEAD): 698 term_fontrepl(p, TERMFONT_BOLD); 699 p->offset = term_len(p, HALFINDENT); 700 break; 701 case (MAN_BODY): 702 p->offset = mt->offset; 703 break; 704 default: 705 break; 706 } 707 708 return(1); 709 } 710 711 712 /* ARGSUSED */ 713 static void 714 post_SS(DECL_ARGS) 715 { 716 717 switch (n->type) { 718 case (MAN_HEAD): 719 term_newln(p); 720 break; 721 case (MAN_BODY): 722 term_newln(p); 723 break; 724 default: 725 break; 726 } 727 } 728 729 730 /* ARGSUSED */ 731 static int 732 pre_SH(DECL_ARGS) 733 { 734 735 switch (n->type) { 736 case (MAN_BLOCK): 737 mt->lmargin = term_len(p, INDENT); 738 mt->offset = term_len(p, INDENT); 739 /* If following a prior empty `SH', no vspace. */ 740 if (n->prev && MAN_SH == n->prev->tok) 741 if (NULL == n->prev->body->child) 742 break; 743 /* If the first macro, no vspae. */ 744 if (NULL == n->prev) 745 break; 746 term_vspace(p); 747 break; 748 case (MAN_HEAD): 749 term_fontrepl(p, TERMFONT_BOLD); 750 p->offset = 0; 751 break; 752 case (MAN_BODY): 753 p->offset = mt->offset; 754 break; 755 default: 756 break; 757 } 758 759 return(1); 760 } 761 762 763 /* ARGSUSED */ 764 static void 765 post_SH(DECL_ARGS) 766 { 767 768 switch (n->type) { 769 case (MAN_HEAD): 770 term_newln(p); 771 break; 772 case (MAN_BODY): 773 term_newln(p); 774 break; 775 default: 776 break; 777 } 778 } 779 780 781 /* ARGSUSED */ 782 static int 783 pre_RS(DECL_ARGS) 784 { 785 const struct man_node *nn; 786 int ival; 787 788 switch (n->type) { 789 case (MAN_BLOCK): 790 term_newln(p); 791 return(1); 792 case (MAN_HEAD): 793 return(0); 794 default: 795 break; 796 } 797 798 if (NULL == (nn = n->parent->head->child)) { 799 mt->offset = mt->lmargin + term_len(p, INDENT); 800 p->offset = mt->offset; 801 return(1); 802 } 803 804 if ((ival = a2width(p, nn->string)) < 0) 805 return(1); 806 807 mt->offset = term_len(p, INDENT) + (size_t)ival; 808 p->offset = mt->offset; 809 810 return(1); 811 } 812 813 814 /* ARGSUSED */ 815 static void 816 post_RS(DECL_ARGS) 817 { 818 819 switch (n->type) { 820 case (MAN_BLOCK): 821 mt->offset = mt->lmargin = term_len(p, INDENT); 822 break; 823 case (MAN_HEAD): 824 break; 825 default: 826 term_newln(p); 827 p->offset = term_len(p, INDENT); 828 break; 829 } 830 } 831 832 833 static void 834 print_man_node(DECL_ARGS) 835 { 836 size_t rm, rmax; 837 int c; 838 839 c = 1; 840 841 switch (n->type) { 842 case(MAN_TEXT): 843 if (0 == *n->string) { 844 term_vspace(p); 845 break; 846 } 847 848 term_word(p, n->string); 849 850 /* FIXME: this means that macro lines are munged! */ 851 852 if (MANT_LITERAL & mt->fl) { 853 rm = p->rmargin; 854 rmax = p->maxrmargin; 855 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 856 p->flags |= TERMP_NOSPACE; 857 term_flushln(p); 858 p->rmargin = rm; 859 p->maxrmargin = rmax; 860 } 861 break; 862 default: 863 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 864 term_fontrepl(p, TERMFONT_NONE); 865 if (termacts[n->tok].pre) 866 c = (*termacts[n->tok].pre)(p, mt, n, m); 867 break; 868 } 869 870 if (c && n->child) 871 print_man_nodelist(p, mt, n->child, m); 872 873 if (MAN_TEXT != n->type) { 874 if (termacts[n->tok].post) 875 (*termacts[n->tok].post)(p, mt, n, m); 876 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 877 term_fontrepl(p, TERMFONT_NONE); 878 } 879 880 if (MAN_EOS & n->flags) 881 p->flags |= TERMP_SENTENCE; 882 } 883 884 885 static void 886 print_man_nodelist(DECL_ARGS) 887 { 888 889 print_man_node(p, mt, n, m); 890 if ( ! n->next) 891 return; 892 print_man_nodelist(p, mt, n->next, m); 893 } 894 895 896 static void 897 print_man_foot(struct termp *p, const void *arg) 898 { 899 char buf[DATESIZ]; 900 const struct man_meta *meta; 901 902 meta = (const struct man_meta *)arg; 903 904 term_fontrepl(p, TERMFONT_NONE); 905 906 if (meta->rawdate) 907 strlcpy(buf, meta->rawdate, DATESIZ); 908 else 909 time2a(meta->date, buf, DATESIZ); 910 911 term_vspace(p); 912 term_vspace(p); 913 term_vspace(p); 914 915 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 916 p->rmargin = p->maxrmargin - term_strlen(p, buf); 917 p->offset = 0; 918 919 if (meta->source) 920 term_word(p, meta->source); 921 if (meta->source) 922 term_word(p, ""); 923 term_flushln(p); 924 925 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 926 p->offset = p->rmargin; 927 p->rmargin = p->maxrmargin; 928 p->flags &= ~TERMP_NOBREAK; 929 930 term_word(p, buf); 931 term_flushln(p); 932 } 933 934 935 static void 936 print_man_head(struct termp *p, const void *arg) 937 { 938 char buf[BUFSIZ], title[BUFSIZ]; 939 size_t buflen, titlen; 940 const struct man_meta *m; 941 942 m = (const struct man_meta *)arg; 943 944 /* 945 * Note that old groff would spit out some spaces before the 946 * header. We discontinue this strange behaviour, but at one 947 * point we did so here. 948 */ 949 950 p->rmargin = p->maxrmargin; 951 952 p->offset = 0; 953 buf[0] = title[0] = '\0'; 954 955 if (m->vol) 956 strlcpy(buf, m->vol, BUFSIZ); 957 buflen = term_strlen(p, buf); 958 959 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 960 titlen = term_strlen(p, title); 961 962 p->offset = 0; 963 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 964 (p->maxrmargin - 965 term_strlen(p, buf) + term_len(p, 1)) / 2 : 966 p->maxrmargin - buflen; 967 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 968 969 term_word(p, title); 970 term_flushln(p); 971 972 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 973 p->offset = p->rmargin; 974 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 975 p->maxrmargin - titlen : p->maxrmargin; 976 977 term_word(p, buf); 978 term_flushln(p); 979 980 p->flags &= ~TERMP_NOBREAK; 981 if (p->rmargin + titlen <= p->maxrmargin) { 982 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 983 p->offset = p->rmargin; 984 p->rmargin = p->maxrmargin; 985 term_word(p, title); 986 term_flushln(p); 987 } 988 989 p->rmargin = p->maxrmargin; 990 p->offset = 0; 991 p->flags &= ~TERMP_NOSPACE; 992 993 /* 994 * Groff likes to have some leading spaces before content. Well 995 * that's fine by me. 996 */ 997 998 term_vspace(p); 999 term_vspace(p); 1000 term_vspace(p); 1001 } 1002