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