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