1 /* $Id: man_term.c,v 1.28 2010/04/25 16:32:19 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 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 #include <sys/types.h> 18 19 #include <assert.h> 20 #include <ctype.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "out.h" 26 #include "man.h" 27 #include "term.h" 28 #include "chars.h" 29 #include "main.h" 30 31 #define INDENT 7 32 #define HALFINDENT 3 33 34 /* FIXME: have PD set the default vspace width. */ 35 36 struct mtermp { 37 int fl; 38 #define MANT_LITERAL (1 << 0) 39 /* 40 * Default amount to indent the left margin after leading text 41 * has been printed (e.g., `HP' left-indent, `TP' and `IP' body 42 * indent). This needs to be saved because `HP' and so on, if 43 * not having a specified value, must default. 44 * 45 * Note that this is the indentation AFTER the left offset, so 46 * the total offset is usually offset + lmargin. 47 */ 48 size_t lmargin; 49 /* 50 * The default offset, i.e., the amount between any text and the 51 * page boundary. 52 */ 53 size_t offset; 54 }; 55 56 #define DECL_ARGS struct termp *p, \ 57 struct mtermp *mt, \ 58 const struct man_node *n, \ 59 const struct man_meta *m 60 61 struct termact { 62 int (*pre)(DECL_ARGS); 63 void (*post)(DECL_ARGS); 64 int flags; 65 #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 66 }; 67 68 static int a2width(const struct man_node *); 69 static int a2height(const struct man_node *); 70 71 static void print_man_head(struct termp *, 72 const struct man_meta *); 73 static void print_man_nodelist(DECL_ARGS); 74 static void print_man_node(DECL_ARGS); 75 static void print_man_foot(struct termp *, 76 const struct man_meta *); 77 static void print_bvspace(struct termp *, 78 const struct man_node *); 79 80 static int pre_B(DECL_ARGS); 81 static int pre_BI(DECL_ARGS); 82 static int pre_HP(DECL_ARGS); 83 static int pre_I(DECL_ARGS); 84 static int pre_IP(DECL_ARGS); 85 static int pre_PP(DECL_ARGS); 86 static int pre_RB(DECL_ARGS); 87 static int pre_RI(DECL_ARGS); 88 static int pre_RS(DECL_ARGS); 89 static int pre_SH(DECL_ARGS); 90 static int pre_SS(DECL_ARGS); 91 static int pre_TP(DECL_ARGS); 92 static int pre_br(DECL_ARGS); 93 static int pre_fi(DECL_ARGS); 94 static int pre_ign(DECL_ARGS); 95 static int pre_nf(DECL_ARGS); 96 static int pre_sp(DECL_ARGS); 97 98 static void post_IP(DECL_ARGS); 99 static void post_HP(DECL_ARGS); 100 static void post_RS(DECL_ARGS); 101 static void post_SH(DECL_ARGS); 102 static void post_SS(DECL_ARGS); 103 static void post_TP(DECL_ARGS); 104 105 static const struct termact termacts[MAN_MAX] = { 106 { pre_br, NULL, MAN_NOTEXT }, /* br */ 107 { NULL, NULL, 0 }, /* TH */ 108 { pre_SH, post_SH, 0 }, /* SH */ 109 { pre_SS, post_SS, 0 }, /* SS */ 110 { pre_TP, post_TP, 0 }, /* TP */ 111 { pre_PP, NULL, 0 }, /* LP */ 112 { pre_PP, NULL, 0 }, /* PP */ 113 { pre_PP, NULL, 0 }, /* P */ 114 { pre_IP, post_IP, 0 }, /* IP */ 115 { pre_HP, post_HP, 0 }, /* HP */ 116 { NULL, NULL, 0 }, /* SM */ 117 { pre_B, NULL, 0 }, /* SB */ 118 { pre_BI, NULL, 0 }, /* BI */ 119 { pre_BI, NULL, 0 }, /* IB */ 120 { pre_RB, NULL, 0 }, /* BR */ 121 { pre_RB, NULL, 0 }, /* RB */ 122 { NULL, NULL, 0 }, /* R */ 123 { pre_B, NULL, 0 }, /* B */ 124 { pre_I, NULL, 0 }, /* I */ 125 { pre_RI, NULL, 0 }, /* IR */ 126 { pre_RI, NULL, 0 }, /* RI */ 127 { NULL, NULL, MAN_NOTEXT }, /* na */ 128 { pre_I, NULL, 0 }, /* i */ 129 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 130 { pre_nf, NULL, 0 }, /* nf */ 131 { pre_fi, NULL, 0 }, /* fi */ 132 { NULL, NULL, 0 }, /* r */ 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_sp, NULL, MAN_NOTEXT }, /* Sp */ 139 { pre_nf, NULL, 0 }, /* Vb */ 140 { pre_fi, NULL, 0 }, /* Ve */ 141 { pre_ign, NULL, MAN_NOTEXT }, /* de */ 142 { pre_ign, NULL, MAN_NOTEXT }, /* dei */ 143 { pre_ign, NULL, MAN_NOTEXT }, /* am */ 144 { pre_ign, NULL, MAN_NOTEXT }, /* ami */ 145 { pre_ign, NULL, MAN_NOTEXT }, /* ig */ 146 { NULL, NULL, 0 }, /* . */ 147 { NULL, NULL, 0 }, /* if */ 148 { NULL, NULL, 0 }, /* ie */ 149 { NULL, NULL, 0 }, /* el */ 150 }; 151 152 153 154 void 155 terminal_man(void *arg, const struct man *man) 156 { 157 struct termp *p; 158 const struct man_node *n; 159 const struct man_meta *m; 160 struct mtermp mt; 161 162 p = (struct termp *)arg; 163 164 p->overstep = 0; 165 p->maxrmargin = 65; 166 167 if (NULL == p->symtab) 168 switch (p->enc) { 169 case (TERMENC_ASCII): 170 p->symtab = chars_init(CHARS_ASCII); 171 break; 172 default: 173 abort(); 174 /* NOTREACHED */ 175 } 176 177 n = man_node(man); 178 m = man_meta(man); 179 180 print_man_head(p, m); 181 p->flags |= TERMP_NOSPACE; 182 183 mt.fl = 0; 184 mt.lmargin = INDENT; 185 mt.offset = INDENT; 186 187 if (n->child) 188 print_man_nodelist(p, &mt, n->child, m); 189 print_man_foot(p, m); 190 } 191 192 193 static int 194 a2height(const struct man_node *n) 195 { 196 struct roffsu su; 197 198 assert(MAN_TEXT == n->type); 199 assert(n->string); 200 if ( ! a2roffsu(n->string, &su, SCALE_VS)) 201 SCALE_VS_INIT(&su, strlen(n->string)); 202 203 return((int)term_vspan(&su)); 204 } 205 206 207 static int 208 a2width(const struct man_node *n) 209 { 210 struct roffsu su; 211 212 assert(MAN_TEXT == n->type); 213 assert(n->string); 214 if ( ! a2roffsu(n->string, &su, SCALE_BU)) 215 return(-1); 216 217 return((int)term_hspan(&su)); 218 } 219 220 221 static void 222 print_bvspace(struct termp *p, const struct man_node *n) 223 { 224 term_newln(p); 225 226 if (NULL == n->prev) 227 return; 228 229 if (MAN_SS == n->prev->tok) 230 return; 231 if (MAN_SH == n->prev->tok) 232 return; 233 234 term_vspace(p); 235 } 236 237 238 /* ARGSUSED */ 239 static int 240 pre_ign(DECL_ARGS) 241 { 242 243 return(0); 244 } 245 246 247 /* ARGSUSED */ 248 static int 249 pre_I(DECL_ARGS) 250 { 251 252 term_fontrepl(p, TERMFONT_UNDER); 253 return(1); 254 } 255 256 257 /* ARGSUSED */ 258 static int 259 pre_fi(DECL_ARGS) 260 { 261 262 p->rmargin = p->maxrmargin = 65; 263 mt->fl &= ~MANT_LITERAL; 264 return(1); 265 } 266 267 268 /* ARGSUSED */ 269 static int 270 pre_nf(DECL_ARGS) 271 { 272 273 p->rmargin = p->maxrmargin = 78; 274 term_newln(p); 275 mt->fl |= MANT_LITERAL; 276 277 return(MAN_Vb != n->tok); 278 } 279 280 281 /* ARGSUSED */ 282 static int 283 pre_RB(DECL_ARGS) 284 { 285 const struct man_node *nn; 286 int i; 287 288 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 289 if (i % 2 && MAN_RB == n->tok) 290 term_fontrepl(p, TERMFONT_BOLD); 291 else if ( ! (i % 2) && MAN_RB != n->tok) 292 term_fontrepl(p, TERMFONT_BOLD); 293 else 294 term_fontrepl(p, TERMFONT_NONE); 295 296 if (i > 0) 297 p->flags |= TERMP_NOSPACE; 298 299 print_man_node(p, mt, nn, m); 300 } 301 return(0); 302 } 303 304 305 /* ARGSUSED */ 306 static int 307 pre_RI(DECL_ARGS) 308 { 309 const struct man_node *nn; 310 int i; 311 312 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 313 if (i % 2 && MAN_RI == n->tok) 314 term_fontrepl(p, TERMFONT_UNDER); 315 else if ( ! (i % 2) && MAN_RI != n->tok) 316 term_fontrepl(p, TERMFONT_UNDER); 317 else 318 term_fontrepl(p, TERMFONT_NONE); 319 320 if (i > 0) 321 p->flags |= TERMP_NOSPACE; 322 323 print_man_node(p, mt, nn, m); 324 } 325 return(0); 326 } 327 328 329 /* ARGSUSED */ 330 static int 331 pre_BI(DECL_ARGS) 332 { 333 const struct man_node *nn; 334 int i; 335 336 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 337 if (i % 2 && MAN_BI == n->tok) 338 term_fontrepl(p, TERMFONT_UNDER); 339 else if (i % 2) 340 term_fontrepl(p, TERMFONT_BOLD); 341 else if (MAN_BI == n->tok) 342 term_fontrepl(p, TERMFONT_BOLD); 343 else 344 term_fontrepl(p, TERMFONT_UNDER); 345 346 if (i) 347 p->flags |= TERMP_NOSPACE; 348 349 print_man_node(p, mt, nn, m); 350 } 351 return(0); 352 } 353 354 355 /* ARGSUSED */ 356 static int 357 pre_B(DECL_ARGS) 358 { 359 360 term_fontrepl(p, TERMFONT_BOLD); 361 return(1); 362 } 363 364 365 /* ARGSUSED */ 366 static int 367 pre_sp(DECL_ARGS) 368 { 369 int i, len; 370 371 len = n->child ? a2height(n->child) : 1; 372 373 if (0 == len) 374 term_newln(p); 375 for (i = 0; i < len; i++) 376 term_vspace(p); 377 378 return(0); 379 } 380 381 382 /* ARGSUSED */ 383 static int 384 pre_br(DECL_ARGS) 385 { 386 387 term_newln(p); 388 return(0); 389 } 390 391 392 /* ARGSUSED */ 393 static int 394 pre_HP(DECL_ARGS) 395 { 396 size_t len; 397 int ival; 398 const struct man_node *nn; 399 400 switch (n->type) { 401 case (MAN_BLOCK): 402 print_bvspace(p, n); 403 return(1); 404 case (MAN_BODY): 405 p->flags |= TERMP_NOBREAK; 406 p->flags |= TERMP_TWOSPACE; 407 break; 408 default: 409 return(0); 410 } 411 412 len = mt->lmargin; 413 ival = -1; 414 415 /* Calculate offset. */ 416 417 if (NULL != (nn = n->parent->head->child)) 418 if ((ival = a2width(nn)) >= 0) 419 len = (size_t)ival; 420 421 if (0 == len) 422 len = 1; 423 424 p->offset = mt->offset; 425 p->rmargin = mt->offset + len; 426 427 if (ival >= 0) 428 mt->lmargin = (size_t)ival; 429 430 return(1); 431 } 432 433 434 /* ARGSUSED */ 435 static void 436 post_HP(DECL_ARGS) 437 { 438 439 switch (n->type) { 440 case (MAN_BLOCK): 441 term_flushln(p); 442 break; 443 case (MAN_BODY): 444 term_flushln(p); 445 p->flags &= ~TERMP_NOBREAK; 446 p->flags &= ~TERMP_TWOSPACE; 447 p->offset = mt->offset; 448 p->rmargin = p->maxrmargin; 449 break; 450 default: 451 break; 452 } 453 } 454 455 456 /* ARGSUSED */ 457 static int 458 pre_PP(DECL_ARGS) 459 { 460 461 switch (n->type) { 462 case (MAN_BLOCK): 463 mt->lmargin = INDENT; 464 print_bvspace(p, n); 465 break; 466 default: 467 p->offset = mt->offset; 468 break; 469 } 470 471 return(1); 472 } 473 474 475 /* ARGSUSED */ 476 static int 477 pre_IP(DECL_ARGS) 478 { 479 const struct man_node *nn; 480 size_t len; 481 int ival; 482 483 switch (n->type) { 484 case (MAN_BODY): 485 p->flags |= TERMP_NOLPAD; 486 p->flags |= TERMP_NOSPACE; 487 break; 488 case (MAN_HEAD): 489 p->flags |= TERMP_NOBREAK; 490 p->flags |= TERMP_TWOSPACE; 491 break; 492 case (MAN_BLOCK): 493 print_bvspace(p, n); 494 /* FALLTHROUGH */ 495 default: 496 return(1); 497 } 498 499 len = mt->lmargin; 500 ival = -1; 501 502 /* Calculate offset. */ 503 504 if (NULL != (nn = n->parent->head->child)) 505 if (NULL != (nn = nn->next)) { 506 for ( ; nn->next; nn = nn->next) 507 /* Do nothing. */ ; 508 if ((ival = a2width(nn)) >= 0) 509 len = (size_t)ival; 510 } 511 512 switch (n->type) { 513 case (MAN_HEAD): 514 /* Handle zero-width lengths. */ 515 if (0 == len) 516 len = 1; 517 518 p->offset = mt->offset; 519 p->rmargin = mt->offset + len; 520 if (ival < 0) 521 break; 522 523 /* Set the saved left-margin. */ 524 mt->lmargin = (size_t)ival; 525 526 /* Don't print the length value. */ 527 for (nn = n->child; nn->next; nn = nn->next) 528 print_man_node(p, mt, nn, m); 529 return(0); 530 case (MAN_BODY): 531 p->offset = mt->offset + len; 532 p->rmargin = p->maxrmargin; 533 break; 534 default: 535 break; 536 } 537 538 return(1); 539 } 540 541 542 /* ARGSUSED */ 543 static void 544 post_IP(DECL_ARGS) 545 { 546 547 switch (n->type) { 548 case (MAN_HEAD): 549 term_flushln(p); 550 p->flags &= ~TERMP_NOBREAK; 551 p->flags &= ~TERMP_TWOSPACE; 552 p->rmargin = p->maxrmargin; 553 break; 554 case (MAN_BODY): 555 term_flushln(p); 556 p->flags &= ~TERMP_NOLPAD; 557 break; 558 default: 559 break; 560 } 561 } 562 563 564 /* ARGSUSED */ 565 static int 566 pre_TP(DECL_ARGS) 567 { 568 const struct man_node *nn; 569 size_t len; 570 int ival; 571 572 switch (n->type) { 573 case (MAN_HEAD): 574 p->flags |= TERMP_NOBREAK; 575 p->flags |= TERMP_TWOSPACE; 576 break; 577 case (MAN_BODY): 578 p->flags |= TERMP_NOLPAD; 579 p->flags |= TERMP_NOSPACE; 580 break; 581 case (MAN_BLOCK): 582 print_bvspace(p, n); 583 /* FALLTHROUGH */ 584 default: 585 return(1); 586 } 587 588 len = (size_t)mt->lmargin; 589 ival = -1; 590 591 /* Calculate offset. */ 592 593 if (NULL != (nn = n->parent->head->child)) { 594 while (nn && MAN_TEXT != nn->type) 595 nn = nn->next; 596 if (nn && nn->next) 597 if ((ival = a2width(nn)) >= 0) 598 len = (size_t)ival; 599 } 600 601 switch (n->type) { 602 case (MAN_HEAD): 603 /* Handle zero-length properly. */ 604 if (0 == len) 605 len = 1; 606 607 p->offset = mt->offset; 608 p->rmargin = mt->offset + len; 609 610 /* Don't print same-line elements. */ 611 for (nn = n->child; nn; nn = nn->next) 612 if (nn->line > n->line) 613 print_man_node(p, mt, nn, m); 614 615 if (ival >= 0) 616 mt->lmargin = (size_t)ival; 617 618 return(0); 619 case (MAN_BODY): 620 p->offset = mt->offset + len; 621 p->rmargin = p->maxrmargin; 622 break; 623 default: 624 break; 625 } 626 627 return(1); 628 } 629 630 631 /* ARGSUSED */ 632 static void 633 post_TP(DECL_ARGS) 634 { 635 636 switch (n->type) { 637 case (MAN_HEAD): 638 term_flushln(p); 639 p->flags &= ~TERMP_NOBREAK; 640 p->flags &= ~TERMP_TWOSPACE; 641 p->rmargin = p->maxrmargin; 642 break; 643 case (MAN_BODY): 644 term_flushln(p); 645 p->flags &= ~TERMP_NOLPAD; 646 break; 647 default: 648 break; 649 } 650 } 651 652 653 /* ARGSUSED */ 654 static int 655 pre_SS(DECL_ARGS) 656 { 657 658 switch (n->type) { 659 case (MAN_BLOCK): 660 mt->lmargin = INDENT; 661 mt->offset = INDENT; 662 /* If following a prior empty `SS', no vspace. */ 663 if (n->prev && MAN_SS == n->prev->tok) 664 if (NULL == n->prev->body->child) 665 break; 666 if (NULL == n->prev) 667 break; 668 term_vspace(p); 669 break; 670 case (MAN_HEAD): 671 term_fontrepl(p, TERMFONT_BOLD); 672 p->offset = HALFINDENT; 673 break; 674 case (MAN_BODY): 675 p->offset = mt->offset; 676 break; 677 default: 678 break; 679 } 680 681 return(1); 682 } 683 684 685 /* ARGSUSED */ 686 static void 687 post_SS(DECL_ARGS) 688 { 689 690 switch (n->type) { 691 case (MAN_HEAD): 692 term_newln(p); 693 break; 694 case (MAN_BODY): 695 term_newln(p); 696 break; 697 default: 698 break; 699 } 700 } 701 702 703 /* ARGSUSED */ 704 static int 705 pre_SH(DECL_ARGS) 706 { 707 708 switch (n->type) { 709 case (MAN_BLOCK): 710 mt->lmargin = INDENT; 711 mt->offset = INDENT; 712 /* If following a prior empty `SH', no vspace. */ 713 if (n->prev && MAN_SH == n->prev->tok) 714 if (NULL == n->prev->body->child) 715 break; 716 term_vspace(p); 717 break; 718 case (MAN_HEAD): 719 term_fontrepl(p, TERMFONT_BOLD); 720 p->offset = 0; 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_SH(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_RS(DECL_ARGS) 754 { 755 const struct man_node *nn; 756 int ival; 757 758 switch (n->type) { 759 case (MAN_BLOCK): 760 term_newln(p); 761 return(1); 762 case (MAN_HEAD): 763 return(0); 764 default: 765 break; 766 } 767 768 if (NULL == (nn = n->parent->head->child)) { 769 mt->offset = mt->lmargin + INDENT; 770 p->offset = mt->offset; 771 return(1); 772 } 773 774 if ((ival = a2width(nn)) < 0) 775 return(1); 776 777 mt->offset = INDENT + (size_t)ival; 778 p->offset = mt->offset; 779 780 return(1); 781 } 782 783 784 /* ARGSUSED */ 785 static void 786 post_RS(DECL_ARGS) 787 { 788 789 switch (n->type) { 790 case (MAN_BLOCK): 791 mt->offset = mt->lmargin = INDENT; 792 break; 793 case (MAN_HEAD): 794 break; 795 default: 796 term_newln(p); 797 p->offset = INDENT; 798 break; 799 } 800 } 801 802 803 static void 804 print_man_node(DECL_ARGS) 805 { 806 int c; 807 808 c = 1; 809 810 switch (n->type) { 811 case(MAN_TEXT): 812 if (0 == *n->string) { 813 term_vspace(p); 814 break; 815 } 816 817 term_word(p, n->string); 818 819 /* FIXME: this means that macro lines are munged! */ 820 821 if (MANT_LITERAL & mt->fl) { 822 p->flags |= TERMP_NOSPACE; 823 term_flushln(p); 824 } 825 break; 826 default: 827 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 828 term_fontrepl(p, TERMFONT_NONE); 829 if (termacts[n->tok].pre) 830 c = (*termacts[n->tok].pre)(p, mt, n, m); 831 break; 832 } 833 834 if (c && n->child) 835 print_man_nodelist(p, mt, n->child, m); 836 837 if (MAN_TEXT != n->type) { 838 if (termacts[n->tok].post) 839 (*termacts[n->tok].post)(p, mt, n, m); 840 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 841 term_fontrepl(p, TERMFONT_NONE); 842 } 843 } 844 845 846 static void 847 print_man_nodelist(DECL_ARGS) 848 { 849 850 print_man_node(p, mt, n, m); 851 if ( ! n->next) 852 return; 853 print_man_nodelist(p, mt, n->next, m); 854 } 855 856 857 static void 858 print_man_foot(struct termp *p, const struct man_meta *meta) 859 { 860 char buf[DATESIZ]; 861 862 term_fontrepl(p, TERMFONT_NONE); 863 864 time2a(meta->date, buf, DATESIZ); 865 866 term_vspace(p); 867 868 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 869 p->rmargin = p->maxrmargin - strlen(buf); 870 p->offset = 0; 871 872 if (meta->source) 873 term_word(p, meta->source); 874 if (meta->source) 875 term_word(p, ""); 876 term_flushln(p); 877 878 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 879 p->offset = p->rmargin; 880 p->rmargin = p->maxrmargin; 881 p->flags &= ~TERMP_NOBREAK; 882 883 term_word(p, buf); 884 term_flushln(p); 885 } 886 887 888 static void 889 print_man_head(struct termp *p, const struct man_meta *m) 890 { 891 char buf[BUFSIZ], title[BUFSIZ]; 892 size_t buflen, titlen; 893 894 p->rmargin = p->maxrmargin; 895 896 p->offset = 0; 897 buf[0] = title[0] = '\0'; 898 899 if (m->vol) 900 strlcpy(buf, m->vol, BUFSIZ); 901 buflen = strlen(buf); 902 903 snprintf(title, BUFSIZ, "%s(%d)", m->title, m->msec); 904 titlen = strlen(title); 905 906 p->offset = 0; 907 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 908 (p->maxrmargin - strlen(buf) + 1) / 2 : 909 p->maxrmargin - buflen; 910 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 911 912 term_word(p, title); 913 term_flushln(p); 914 915 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 916 p->offset = p->rmargin; 917 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 918 p->maxrmargin - titlen : p->maxrmargin; 919 920 term_word(p, buf); 921 term_flushln(p); 922 923 p->flags &= ~TERMP_NOBREAK; 924 if (p->rmargin + titlen <= p->maxrmargin) { 925 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 926 p->offset = p->rmargin; 927 p->rmargin = p->maxrmargin; 928 term_word(p, title); 929 term_flushln(p); 930 } 931 932 p->rmargin = p->maxrmargin; 933 p->offset = 0; 934 p->flags &= ~TERMP_NOSPACE; 935 } 936