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