1 /* $Id: man_term.c,v 1.127 2012/01/03 15:16:24 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 MAXMARGINS 64 /* maximum number of indented scopes */ 37 38 /* FIXME: have PD set the default vspace width. */ 39 40 struct mtermp { 41 int fl; 42 #define MANT_LITERAL (1 << 0) 43 size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ 44 int lmargincur; /* index of current margin */ 45 int lmarginsz; /* actual number of nested margins */ 46 size_t offset; /* default offset to visible page */ 47 }; 48 49 #define DECL_ARGS struct termp *p, \ 50 struct mtermp *mt, \ 51 const struct man_node *n, \ 52 const struct man_meta *m 53 54 struct termact { 55 int (*pre)(DECL_ARGS); 56 void (*post)(DECL_ARGS); 57 int flags; 58 #define MAN_NOTEXT (1 << 0) /* Never has text children. */ 59 }; 60 61 static int a2width(const struct termp *, const char *); 62 static size_t a2height(const struct termp *, const char *); 63 64 static void print_man_nodelist(DECL_ARGS); 65 static void print_man_node(DECL_ARGS); 66 static void print_man_head(struct termp *, const void *); 67 static void print_man_foot(struct termp *, const void *); 68 static void print_bvspace(struct termp *, 69 const struct man_node *); 70 71 static int pre_B(DECL_ARGS); 72 static int pre_HP(DECL_ARGS); 73 static int pre_I(DECL_ARGS); 74 static int pre_IP(DECL_ARGS); 75 static int pre_OP(DECL_ARGS); 76 static int pre_PP(DECL_ARGS); 77 static int pre_RS(DECL_ARGS); 78 static int pre_SH(DECL_ARGS); 79 static int pre_SS(DECL_ARGS); 80 static int pre_TP(DECL_ARGS); 81 static int pre_alternate(DECL_ARGS); 82 static int pre_ft(DECL_ARGS); 83 static int pre_ign(DECL_ARGS); 84 static int pre_in(DECL_ARGS); 85 static int pre_literal(DECL_ARGS); 86 static int pre_sp(DECL_ARGS); 87 88 static void post_IP(DECL_ARGS); 89 static void post_HP(DECL_ARGS); 90 static void post_RS(DECL_ARGS); 91 static void post_SH(DECL_ARGS); 92 static void post_SS(DECL_ARGS); 93 static void post_TP(DECL_ARGS); 94 95 static const struct termact termacts[MAN_MAX] = { 96 { pre_sp, NULL, MAN_NOTEXT }, /* br */ 97 { NULL, NULL, 0 }, /* TH */ 98 { pre_SH, post_SH, 0 }, /* SH */ 99 { pre_SS, post_SS, 0 }, /* SS */ 100 { pre_TP, post_TP, 0 }, /* TP */ 101 { pre_PP, NULL, 0 }, /* LP */ 102 { pre_PP, NULL, 0 }, /* PP */ 103 { pre_PP, NULL, 0 }, /* P */ 104 { pre_IP, post_IP, 0 }, /* IP */ 105 { pre_HP, post_HP, 0 }, /* HP */ 106 { NULL, NULL, 0 }, /* SM */ 107 { pre_B, NULL, 0 }, /* SB */ 108 { pre_alternate, NULL, 0 }, /* BI */ 109 { pre_alternate, NULL, 0 }, /* IB */ 110 { pre_alternate, NULL, 0 }, /* BR */ 111 { pre_alternate, NULL, 0 }, /* RB */ 112 { NULL, NULL, 0 }, /* R */ 113 { pre_B, NULL, 0 }, /* B */ 114 { pre_I, NULL, 0 }, /* I */ 115 { pre_alternate, NULL, 0 }, /* IR */ 116 { pre_alternate, NULL, 0 }, /* RI */ 117 { pre_ign, NULL, MAN_NOTEXT }, /* na */ 118 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 119 { pre_literal, NULL, 0 }, /* nf */ 120 { pre_literal, NULL, 0 }, /* fi */ 121 { NULL, NULL, 0 }, /* RE */ 122 { pre_RS, post_RS, 0 }, /* RS */ 123 { pre_ign, NULL, 0 }, /* DT */ 124 { pre_ign, NULL, 0 }, /* UC */ 125 { pre_ign, NULL, 0 }, /* PD */ 126 { pre_ign, NULL, 0 }, /* AT */ 127 { pre_in, NULL, MAN_NOTEXT }, /* in */ 128 { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 129 { pre_OP, NULL, 0 }, /* OP */ 130 }; 131 132 133 134 void 135 terminal_man(void *arg, const struct man *man) 136 { 137 struct termp *p; 138 const struct man_node *n; 139 const struct man_meta *m; 140 struct mtermp mt; 141 142 p = (struct termp *)arg; 143 144 if (0 == p->defindent) 145 p->defindent = 7; 146 147 p->overstep = 0; 148 p->maxrmargin = p->defrmargin; 149 p->tabwidth = term_len(p, 5); 150 151 if (NULL == p->symtab) 152 p->symtab = mchars_alloc(); 153 154 n = man_node(man); 155 m = man_meta(man); 156 157 term_begin(p, print_man_head, print_man_foot, m); 158 p->flags |= TERMP_NOSPACE; 159 160 memset(&mt, 0, sizeof(struct mtermp)); 161 162 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); 163 mt.offset = term_len(p, p->defindent); 164 165 if (n->child) 166 print_man_nodelist(p, &mt, n->child, m); 167 168 term_end(p); 169 } 170 171 172 static size_t 173 a2height(const struct termp *p, const char *cp) 174 { 175 struct roffsu su; 176 177 if ( ! a2roffsu(cp, &su, SCALE_VS)) 178 SCALE_VS_INIT(&su, atoi(cp)); 179 180 return(term_vspan(p, &su)); 181 } 182 183 184 static int 185 a2width(const struct termp *p, const char *cp) 186 { 187 struct roffsu su; 188 189 if ( ! a2roffsu(cp, &su, SCALE_BU)) 190 return(-1); 191 192 return((int)term_hspan(p, &su)); 193 } 194 195 /* 196 * Printing leading vertical space before a block. 197 * This is used for the paragraph macros. 198 * The rules are pretty simple, since there's very little nesting going 199 * on here. Basically, if we're the first within another block (SS/SH), 200 * then don't emit vertical space. If we are (RS), then do. If not the 201 * first, print it. 202 */ 203 static void 204 print_bvspace(struct termp *p, const struct man_node *n) 205 { 206 207 term_newln(p); 208 209 if (n->body && n->body->child) 210 if (MAN_TBL == n->body->child->type) 211 return; 212 213 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) 214 if (NULL == n->prev) 215 return; 216 217 term_vspace(p); 218 } 219 220 /* ARGSUSED */ 221 static int 222 pre_ign(DECL_ARGS) 223 { 224 225 return(0); 226 } 227 228 229 /* ARGSUSED */ 230 static int 231 pre_I(DECL_ARGS) 232 { 233 234 term_fontrepl(p, TERMFONT_UNDER); 235 return(1); 236 } 237 238 239 /* ARGSUSED */ 240 static int 241 pre_literal(DECL_ARGS) 242 { 243 244 term_newln(p); 245 246 if (MAN_nf == n->tok) 247 mt->fl |= MANT_LITERAL; 248 else 249 mt->fl &= ~MANT_LITERAL; 250 251 /* 252 * Unlike .IP and .TP, .HP does not have a HEAD. 253 * So in case a second call to term_flushln() is needed, 254 * indentation has to be set up explicitly. 255 */ 256 if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { 257 p->offset = p->rmargin; 258 p->rmargin = p->maxrmargin; 259 p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE); 260 p->flags |= TERMP_NOSPACE; 261 } 262 263 return(0); 264 } 265 266 /* ARGSUSED */ 267 static int 268 pre_alternate(DECL_ARGS) 269 { 270 enum termfont font[2]; 271 const struct man_node *nn; 272 int savelit, i; 273 274 switch (n->tok) { 275 case (MAN_RB): 276 font[0] = TERMFONT_NONE; 277 font[1] = TERMFONT_BOLD; 278 break; 279 case (MAN_RI): 280 font[0] = TERMFONT_NONE; 281 font[1] = TERMFONT_UNDER; 282 break; 283 case (MAN_BR): 284 font[0] = TERMFONT_BOLD; 285 font[1] = TERMFONT_NONE; 286 break; 287 case (MAN_BI): 288 font[0] = TERMFONT_BOLD; 289 font[1] = TERMFONT_UNDER; 290 break; 291 case (MAN_IR): 292 font[0] = TERMFONT_UNDER; 293 font[1] = TERMFONT_NONE; 294 break; 295 case (MAN_IB): 296 font[0] = TERMFONT_UNDER; 297 font[1] = TERMFONT_BOLD; 298 break; 299 default: 300 abort(); 301 } 302 303 savelit = MANT_LITERAL & mt->fl; 304 mt->fl &= ~MANT_LITERAL; 305 306 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 307 term_fontrepl(p, font[i]); 308 if (savelit && NULL == nn->next) 309 mt->fl |= MANT_LITERAL; 310 print_man_node(p, mt, nn, m); 311 if (nn->next) 312 p->flags |= TERMP_NOSPACE; 313 } 314 315 return(0); 316 } 317 318 /* ARGSUSED */ 319 static int 320 pre_B(DECL_ARGS) 321 { 322 323 term_fontrepl(p, TERMFONT_BOLD); 324 return(1); 325 } 326 327 /* ARGSUSED */ 328 static int 329 pre_OP(DECL_ARGS) 330 { 331 332 term_word(p, "["); 333 p->flags |= TERMP_NOSPACE; 334 335 if (NULL != (n = n->child)) { 336 term_fontrepl(p, TERMFONT_BOLD); 337 term_word(p, n->string); 338 } 339 if (NULL != n && NULL != n->next) { 340 term_fontrepl(p, TERMFONT_UNDER); 341 term_word(p, n->next->string); 342 } 343 344 term_fontrepl(p, TERMFONT_NONE); 345 p->flags |= TERMP_NOSPACE; 346 term_word(p, "]"); 347 return(0); 348 } 349 350 /* ARGSUSED */ 351 static int 352 pre_ft(DECL_ARGS) 353 { 354 const char *cp; 355 356 if (NULL == n->child) { 357 term_fontlast(p); 358 return(0); 359 } 360 361 cp = n->child->string; 362 switch (*cp) { 363 case ('4'): 364 /* FALLTHROUGH */ 365 case ('3'): 366 /* FALLTHROUGH */ 367 case ('B'): 368 term_fontrepl(p, TERMFONT_BOLD); 369 break; 370 case ('2'): 371 /* FALLTHROUGH */ 372 case ('I'): 373 term_fontrepl(p, TERMFONT_UNDER); 374 break; 375 case ('P'): 376 term_fontlast(p); 377 break; 378 case ('1'): 379 /* FALLTHROUGH */ 380 case ('C'): 381 /* FALLTHROUGH */ 382 case ('R'): 383 term_fontrepl(p, TERMFONT_NONE); 384 break; 385 default: 386 break; 387 } 388 return(0); 389 } 390 391 /* ARGSUSED */ 392 static int 393 pre_in(DECL_ARGS) 394 { 395 int len, less; 396 size_t v; 397 const char *cp; 398 399 term_newln(p); 400 401 if (NULL == n->child) { 402 p->offset = mt->offset; 403 return(0); 404 } 405 406 cp = n->child->string; 407 less = 0; 408 409 if ('-' == *cp) 410 less = -1; 411 else if ('+' == *cp) 412 less = 1; 413 else 414 cp--; 415 416 if ((len = a2width(p, ++cp)) < 0) 417 return(0); 418 419 v = (size_t)len; 420 421 if (less < 0) 422 p->offset -= p->offset > v ? v : p->offset; 423 else if (less > 0) 424 p->offset += v; 425 else 426 p->offset = v; 427 428 /* Don't let this creep beyond the right margin. */ 429 430 if (p->offset > p->rmargin) 431 p->offset = p->rmargin; 432 433 return(0); 434 } 435 436 437 /* ARGSUSED */ 438 static int 439 pre_sp(DECL_ARGS) 440 { 441 size_t i, len; 442 443 if ((NULL == n->prev && n->parent)) { 444 if (MAN_SS == n->parent->tok) 445 return(0); 446 if (MAN_SH == n->parent->tok) 447 return(0); 448 } 449 450 switch (n->tok) { 451 case (MAN_br): 452 len = 0; 453 break; 454 default: 455 len = n->child ? a2height(p, n->child->string) : 1; 456 break; 457 } 458 459 if (0 == len) 460 term_newln(p); 461 for (i = 0; i < len; i++) 462 term_vspace(p); 463 464 return(0); 465 } 466 467 468 /* ARGSUSED */ 469 static int 470 pre_HP(DECL_ARGS) 471 { 472 size_t len, one; 473 int ival; 474 const struct man_node *nn; 475 476 switch (n->type) { 477 case (MAN_BLOCK): 478 print_bvspace(p, n); 479 return(1); 480 case (MAN_BODY): 481 p->flags |= TERMP_NOBREAK; 482 p->flags |= TERMP_TWOSPACE; 483 break; 484 default: 485 return(0); 486 } 487 488 len = mt->lmargin[mt->lmargincur]; 489 ival = -1; 490 491 /* Calculate offset. */ 492 493 if (NULL != (nn = n->parent->head->child)) 494 if ((ival = a2width(p, nn->string)) >= 0) 495 len = (size_t)ival; 496 497 one = term_len(p, 1); 498 if (len < one) 499 len = one; 500 501 p->offset = mt->offset; 502 p->rmargin = mt->offset + len; 503 504 if (ival >= 0) 505 mt->lmargin[mt->lmargincur] = (size_t)ival; 506 507 return(1); 508 } 509 510 511 /* ARGSUSED */ 512 static void 513 post_HP(DECL_ARGS) 514 { 515 516 switch (n->type) { 517 case (MAN_BLOCK): 518 term_flushln(p); 519 break; 520 case (MAN_BODY): 521 term_flushln(p); 522 p->flags &= ~TERMP_NOBREAK; 523 p->flags &= ~TERMP_TWOSPACE; 524 p->offset = mt->offset; 525 p->rmargin = p->maxrmargin; 526 break; 527 default: 528 break; 529 } 530 } 531 532 533 /* ARGSUSED */ 534 static int 535 pre_PP(DECL_ARGS) 536 { 537 538 switch (n->type) { 539 case (MAN_BLOCK): 540 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 541 print_bvspace(p, n); 542 break; 543 default: 544 p->offset = mt->offset; 545 break; 546 } 547 548 return(MAN_HEAD != n->type); 549 } 550 551 552 /* ARGSUSED */ 553 static int 554 pre_IP(DECL_ARGS) 555 { 556 const struct man_node *nn; 557 size_t len; 558 int savelit, ival; 559 560 switch (n->type) { 561 case (MAN_BODY): 562 p->flags |= TERMP_NOSPACE; 563 break; 564 case (MAN_HEAD): 565 p->flags |= TERMP_NOBREAK; 566 break; 567 case (MAN_BLOCK): 568 print_bvspace(p, n); 569 /* FALLTHROUGH */ 570 default: 571 return(1); 572 } 573 574 len = mt->lmargin[mt->lmargincur]; 575 ival = -1; 576 577 /* Calculate the offset from the optional second argument. */ 578 if (NULL != (nn = n->parent->head->child)) 579 if (NULL != (nn = nn->next)) 580 if ((ival = a2width(p, nn->string)) >= 0) 581 len = (size_t)ival; 582 583 switch (n->type) { 584 case (MAN_HEAD): 585 /* Handle zero-width lengths. */ 586 if (0 == len) 587 len = term_len(p, 1); 588 589 p->offset = mt->offset; 590 p->rmargin = mt->offset + len; 591 if (ival < 0) 592 break; 593 594 /* Set the saved left-margin. */ 595 mt->lmargin[mt->lmargincur] = (size_t)ival; 596 597 savelit = MANT_LITERAL & mt->fl; 598 mt->fl &= ~MANT_LITERAL; 599 600 if (n->child) 601 print_man_node(p, mt, n->child, m); 602 603 if (savelit) 604 mt->fl |= MANT_LITERAL; 605 606 return(0); 607 case (MAN_BODY): 608 p->offset = mt->offset + len; 609 p->rmargin = p->maxrmargin; 610 break; 611 default: 612 break; 613 } 614 615 return(1); 616 } 617 618 619 /* ARGSUSED */ 620 static void 621 post_IP(DECL_ARGS) 622 { 623 624 switch (n->type) { 625 case (MAN_HEAD): 626 term_flushln(p); 627 p->flags &= ~TERMP_NOBREAK; 628 p->rmargin = p->maxrmargin; 629 break; 630 case (MAN_BODY): 631 term_newln(p); 632 break; 633 default: 634 break; 635 } 636 } 637 638 639 /* ARGSUSED */ 640 static int 641 pre_TP(DECL_ARGS) 642 { 643 const struct man_node *nn; 644 size_t len; 645 int savelit, ival; 646 647 switch (n->type) { 648 case (MAN_HEAD): 649 p->flags |= TERMP_NOBREAK; 650 break; 651 case (MAN_BODY): 652 p->flags |= TERMP_NOSPACE; 653 break; 654 case (MAN_BLOCK): 655 print_bvspace(p, n); 656 /* FALLTHROUGH */ 657 default: 658 return(1); 659 } 660 661 len = (size_t)mt->lmargin[mt->lmargincur]; 662 ival = -1; 663 664 /* Calculate offset. */ 665 666 if (NULL != (nn = n->parent->head->child)) 667 if (nn->string && nn->parent->line == nn->line) 668 if ((ival = a2width(p, nn->string)) >= 0) 669 len = (size_t)ival; 670 671 switch (n->type) { 672 case (MAN_HEAD): 673 /* Handle zero-length properly. */ 674 if (0 == len) 675 len = term_len(p, 1); 676 677 p->offset = mt->offset; 678 p->rmargin = mt->offset + len; 679 680 savelit = MANT_LITERAL & mt->fl; 681 mt->fl &= ~MANT_LITERAL; 682 683 /* Don't print same-line elements. */ 684 for (nn = n->child; nn; nn = nn->next) 685 if (nn->line > n->line) 686 print_man_node(p, mt, nn, m); 687 688 if (savelit) 689 mt->fl |= MANT_LITERAL; 690 if (ival >= 0) 691 mt->lmargin[mt->lmargincur] = (size_t)ival; 692 693 return(0); 694 case (MAN_BODY): 695 p->offset = mt->offset + len; 696 p->rmargin = p->maxrmargin; 697 break; 698 default: 699 break; 700 } 701 702 return(1); 703 } 704 705 706 /* ARGSUSED */ 707 static void 708 post_TP(DECL_ARGS) 709 { 710 711 switch (n->type) { 712 case (MAN_HEAD): 713 term_flushln(p); 714 p->flags &= ~TERMP_NOBREAK; 715 p->flags &= ~TERMP_TWOSPACE; 716 p->rmargin = p->maxrmargin; 717 break; 718 case (MAN_BODY): 719 term_newln(p); 720 break; 721 default: 722 break; 723 } 724 } 725 726 727 /* ARGSUSED */ 728 static int 729 pre_SS(DECL_ARGS) 730 { 731 732 switch (n->type) { 733 case (MAN_BLOCK): 734 mt->fl &= ~MANT_LITERAL; 735 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 736 mt->offset = term_len(p, p->defindent); 737 /* If following a prior empty `SS', no vspace. */ 738 if (n->prev && MAN_SS == n->prev->tok) 739 if (NULL == n->prev->body->child) 740 break; 741 if (NULL == n->prev) 742 break; 743 term_vspace(p); 744 break; 745 case (MAN_HEAD): 746 term_fontrepl(p, TERMFONT_BOLD); 747 p->offset = term_len(p, p->defindent/2); 748 break; 749 case (MAN_BODY): 750 p->offset = mt->offset; 751 break; 752 default: 753 break; 754 } 755 756 return(1); 757 } 758 759 760 /* ARGSUSED */ 761 static void 762 post_SS(DECL_ARGS) 763 { 764 765 switch (n->type) { 766 case (MAN_HEAD): 767 term_newln(p); 768 break; 769 case (MAN_BODY): 770 term_newln(p); 771 break; 772 default: 773 break; 774 } 775 } 776 777 778 /* ARGSUSED */ 779 static int 780 pre_SH(DECL_ARGS) 781 { 782 783 switch (n->type) { 784 case (MAN_BLOCK): 785 mt->fl &= ~MANT_LITERAL; 786 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 787 mt->offset = term_len(p, p->defindent); 788 /* If following a prior empty `SH', no vspace. */ 789 if (n->prev && MAN_SH == n->prev->tok) 790 if (NULL == n->prev->body->child) 791 break; 792 /* If the first macro, no vspae. */ 793 if (NULL == n->prev) 794 break; 795 term_vspace(p); 796 break; 797 case (MAN_HEAD): 798 term_fontrepl(p, TERMFONT_BOLD); 799 p->offset = 0; 800 break; 801 case (MAN_BODY): 802 p->offset = mt->offset; 803 break; 804 default: 805 break; 806 } 807 808 return(1); 809 } 810 811 812 /* ARGSUSED */ 813 static void 814 post_SH(DECL_ARGS) 815 { 816 817 switch (n->type) { 818 case (MAN_HEAD): 819 term_newln(p); 820 break; 821 case (MAN_BODY): 822 term_newln(p); 823 break; 824 default: 825 break; 826 } 827 } 828 829 /* ARGSUSED */ 830 static int 831 pre_RS(DECL_ARGS) 832 { 833 int ival; 834 size_t sz; 835 836 switch (n->type) { 837 case (MAN_BLOCK): 838 term_newln(p); 839 return(1); 840 case (MAN_HEAD): 841 return(0); 842 default: 843 break; 844 } 845 846 sz = term_len(p, p->defindent); 847 848 if (NULL != (n = n->parent->head->child)) 849 if ((ival = a2width(p, n->string)) >= 0) 850 sz = (size_t)ival; 851 852 mt->offset += sz; 853 p->rmargin = p->maxrmargin; 854 p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin; 855 856 if (++mt->lmarginsz < MAXMARGINS) 857 mt->lmargincur = mt->lmarginsz; 858 859 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; 860 return(1); 861 } 862 863 /* ARGSUSED */ 864 static void 865 post_RS(DECL_ARGS) 866 { 867 int ival; 868 size_t sz; 869 870 switch (n->type) { 871 case (MAN_BLOCK): 872 return; 873 case (MAN_HEAD): 874 return; 875 default: 876 term_newln(p); 877 break; 878 } 879 880 sz = term_len(p, p->defindent); 881 882 if (NULL != (n = n->parent->head->child)) 883 if ((ival = a2width(p, n->string)) >= 0) 884 sz = (size_t)ival; 885 886 mt->offset = mt->offset < sz ? 0 : mt->offset - sz; 887 p->offset = mt->offset; 888 889 if (--mt->lmarginsz < MAXMARGINS) 890 mt->lmargincur = mt->lmarginsz; 891 } 892 893 static void 894 print_man_node(DECL_ARGS) 895 { 896 size_t rm, rmax; 897 int c; 898 899 switch (n->type) { 900 case(MAN_TEXT): 901 /* 902 * If we have a blank line, output a vertical space. 903 * If we have a space as the first character, break 904 * before printing the line's data. 905 */ 906 if ('\0' == *n->string) { 907 term_vspace(p); 908 return; 909 } else if (' ' == *n->string && MAN_LINE & n->flags) 910 term_newln(p); 911 912 term_word(p, n->string); 913 914 /* 915 * If we're in a literal context, make sure that words 916 * togehter on the same line stay together. This is a 917 * POST-printing call, so we check the NEXT word. Since 918 * -man doesn't have nested macros, we don't need to be 919 * more specific than this. 920 */ 921 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) && 922 (NULL == n->next || 923 n->next->line > n->line)) { 924 rm = p->rmargin; 925 rmax = p->maxrmargin; 926 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 927 p->flags |= TERMP_NOSPACE; 928 term_flushln(p); 929 p->rmargin = rm; 930 p->maxrmargin = rmax; 931 } 932 933 if (MAN_EOS & n->flags) 934 p->flags |= TERMP_SENTENCE; 935 return; 936 case (MAN_EQN): 937 term_eqn(p, n->eqn); 938 return; 939 case (MAN_TBL): 940 /* 941 * Tables are preceded by a newline. Then process a 942 * table line, which will cause line termination, 943 */ 944 if (TBL_SPAN_FIRST & n->span->flags) 945 term_newln(p); 946 term_tbl(p, n->span); 947 return; 948 default: 949 break; 950 } 951 952 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 953 term_fontrepl(p, TERMFONT_NONE); 954 955 c = 1; 956 if (termacts[n->tok].pre) 957 c = (*termacts[n->tok].pre)(p, mt, n, m); 958 959 if (c && n->child) 960 print_man_nodelist(p, mt, n->child, m); 961 962 if (termacts[n->tok].post) 963 (*termacts[n->tok].post)(p, mt, n, m); 964 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 965 term_fontrepl(p, TERMFONT_NONE); 966 967 if (MAN_EOS & n->flags) 968 p->flags |= TERMP_SENTENCE; 969 } 970 971 972 static void 973 print_man_nodelist(DECL_ARGS) 974 { 975 976 print_man_node(p, mt, n, m); 977 if ( ! n->next) 978 return; 979 print_man_nodelist(p, mt, n->next, m); 980 } 981 982 983 static void 984 print_man_foot(struct termp *p, const void *arg) 985 { 986 char title[BUFSIZ]; 987 size_t datelen; 988 const struct man_meta *meta; 989 990 meta = (const struct man_meta *)arg; 991 assert(meta->title); 992 assert(meta->msec); 993 assert(meta->date); 994 995 term_fontrepl(p, TERMFONT_NONE); 996 997 term_vspace(p); 998 999 /* 1000 * Temporary, undocumented option to imitate mdoc(7) output. 1001 * In the bottom right corner, use the source instead of 1002 * the title. 1003 */ 1004 1005 if ( ! p->mdocstyle) { 1006 term_vspace(p); 1007 term_vspace(p); 1008 snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec); 1009 } else if (meta->source) { 1010 strlcpy(title, meta->source, BUFSIZ); 1011 } else { 1012 title[0] = '\0'; 1013 } 1014 datelen = term_strlen(p, meta->date); 1015 1016 /* Bottom left corner: manual source. */ 1017 1018 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1019 p->offset = 0; 1020 p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2; 1021 1022 if (meta->source) 1023 term_word(p, meta->source); 1024 term_flushln(p); 1025 1026 /* At the bottom in the middle: manual date. */ 1027 1028 p->flags |= TERMP_NOSPACE; 1029 p->offset = p->rmargin; 1030 p->rmargin = p->maxrmargin - term_strlen(p, title); 1031 if (p->offset + datelen >= p->rmargin) 1032 p->rmargin = p->offset + datelen; 1033 1034 term_word(p, meta->date); 1035 term_flushln(p); 1036 1037 /* Bottom right corner: manual title and section. */ 1038 1039 p->flags &= ~TERMP_NOBREAK; 1040 p->flags |= TERMP_NOSPACE; 1041 p->offset = p->rmargin; 1042 p->rmargin = p->maxrmargin; 1043 1044 term_word(p, title); 1045 term_flushln(p); 1046 } 1047 1048 1049 static void 1050 print_man_head(struct termp *p, const void *arg) 1051 { 1052 char buf[BUFSIZ], title[BUFSIZ]; 1053 size_t buflen, titlen; 1054 const struct man_meta *m; 1055 1056 m = (const struct man_meta *)arg; 1057 assert(m->title); 1058 assert(m->msec); 1059 1060 if (m->vol) 1061 strlcpy(buf, m->vol, BUFSIZ); 1062 else 1063 buf[0] = '\0'; 1064 buflen = term_strlen(p, buf); 1065 1066 /* Top left corner: manual title and section. */ 1067 1068 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 1069 titlen = term_strlen(p, title); 1070 1071 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1072 p->offset = 0; 1073 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 1074 (p->maxrmargin - 1075 term_strlen(p, buf) + term_len(p, 1)) / 2 : 1076 p->maxrmargin - buflen; 1077 1078 term_word(p, title); 1079 term_flushln(p); 1080 1081 /* At the top in the middle: manual volume. */ 1082 1083 p->flags |= TERMP_NOSPACE; 1084 p->offset = p->rmargin; 1085 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 1086 p->maxrmargin - titlen : p->maxrmargin; 1087 1088 term_word(p, buf); 1089 term_flushln(p); 1090 1091 /* Top right corner: title and section, again. */ 1092 1093 p->flags &= ~TERMP_NOBREAK; 1094 if (p->rmargin + titlen <= p->maxrmargin) { 1095 p->flags |= TERMP_NOSPACE; 1096 p->offset = p->rmargin; 1097 p->rmargin = p->maxrmargin; 1098 term_word(p, title); 1099 term_flushln(p); 1100 } 1101 1102 p->flags &= ~TERMP_NOSPACE; 1103 p->offset = 0; 1104 p->rmargin = p->maxrmargin; 1105 1106 /* 1107 * Groff prints three blank lines before the content. 1108 * Do the same, except in the temporary, undocumented 1109 * mode imitating mdoc(7) output. 1110 */ 1111 1112 term_vspace(p); 1113 if ( ! p->mdocstyle) { 1114 term_vspace(p); 1115 term_vspace(p); 1116 } 1117 }