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