1 /* $Id: man_term.c,v 1.149 2014/06/20 23:02:31 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 #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 "mandoc_aux.h" 32 #include "out.h" 33 #include "man.h" 34 #include "term.h" 35 #include "main.h" 36 37 #define MAXMARGINS 64 /* maximum number of indented scopes */ 38 39 struct mtermp { 40 int fl; 41 #define MANT_LITERAL (1 << 0) 42 size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ 43 int lmargincur; /* index of current margin */ 44 int lmarginsz; /* actual number of nested margins */ 45 size_t offset; /* default offset to visible page */ 46 int pardist; /* vert. space before par., unit: [v] */ 47 }; 48 49 #define DECL_ARGS struct termp *p, \ 50 struct mtermp *mt, \ 51 const struct man_node *n, \ 52 const struct man_meta *meta 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 *, int); 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_PD(DECL_ARGS); 77 static int pre_PP(DECL_ARGS); 78 static int pre_RS(DECL_ARGS); 79 static int pre_SH(DECL_ARGS); 80 static int pre_SS(DECL_ARGS); 81 static int pre_TP(DECL_ARGS); 82 static int pre_UR(DECL_ARGS); 83 static int pre_alternate(DECL_ARGS); 84 static int pre_ft(DECL_ARGS); 85 static int pre_ign(DECL_ARGS); 86 static int pre_in(DECL_ARGS); 87 static int pre_literal(DECL_ARGS); 88 static int pre_ll(DECL_ARGS); 89 static int pre_sp(DECL_ARGS); 90 91 static void post_IP(DECL_ARGS); 92 static void post_HP(DECL_ARGS); 93 static void post_RS(DECL_ARGS); 94 static void post_SH(DECL_ARGS); 95 static void post_SS(DECL_ARGS); 96 static void post_TP(DECL_ARGS); 97 static void post_UR(DECL_ARGS); 98 99 static const struct termact termacts[MAN_MAX] = { 100 { pre_sp, NULL, MAN_NOTEXT }, /* br */ 101 { NULL, NULL, 0 }, /* TH */ 102 { pre_SH, post_SH, 0 }, /* SH */ 103 { pre_SS, post_SS, 0 }, /* SS */ 104 { pre_TP, post_TP, 0 }, /* TP */ 105 { pre_PP, NULL, 0 }, /* LP */ 106 { pre_PP, NULL, 0 }, /* PP */ 107 { pre_PP, NULL, 0 }, /* P */ 108 { pre_IP, post_IP, 0 }, /* IP */ 109 { pre_HP, post_HP, 0 }, /* HP */ 110 { NULL, NULL, 0 }, /* SM */ 111 { pre_B, NULL, 0 }, /* SB */ 112 { pre_alternate, NULL, 0 }, /* BI */ 113 { pre_alternate, NULL, 0 }, /* IB */ 114 { pre_alternate, NULL, 0 }, /* BR */ 115 { pre_alternate, NULL, 0 }, /* RB */ 116 { NULL, NULL, 0 }, /* R */ 117 { pre_B, NULL, 0 }, /* B */ 118 { pre_I, NULL, 0 }, /* I */ 119 { pre_alternate, NULL, 0 }, /* IR */ 120 { pre_alternate, NULL, 0 }, /* RI */ 121 { pre_ign, NULL, MAN_NOTEXT }, /* na */ 122 { pre_sp, NULL, MAN_NOTEXT }, /* sp */ 123 { pre_literal, NULL, 0 }, /* nf */ 124 { pre_literal, NULL, 0 }, /* fi */ 125 { NULL, NULL, 0 }, /* RE */ 126 { pre_RS, post_RS, 0 }, /* RS */ 127 { pre_ign, NULL, 0 }, /* DT */ 128 { pre_ign, NULL, 0 }, /* UC */ 129 { pre_PD, NULL, MAN_NOTEXT }, /* PD */ 130 { pre_ign, NULL, 0 }, /* AT */ 131 { pre_in, NULL, MAN_NOTEXT }, /* in */ 132 { pre_ft, NULL, MAN_NOTEXT }, /* ft */ 133 { pre_OP, NULL, 0 }, /* OP */ 134 { pre_literal, NULL, 0 }, /* EX */ 135 { pre_literal, NULL, 0 }, /* EE */ 136 { pre_UR, post_UR, 0 }, /* UR */ 137 { NULL, NULL, 0 }, /* UE */ 138 { pre_ll, NULL, MAN_NOTEXT }, /* ll */ 139 }; 140 141 142 void 143 terminal_man(void *arg, const struct man *man) 144 { 145 struct termp *p; 146 const struct man_node *n; 147 const struct man_meta *meta; 148 struct mtermp mt; 149 150 p = (struct termp *)arg; 151 152 if (0 == p->defindent) 153 p->defindent = 7; 154 155 p->overstep = 0; 156 p->maxrmargin = p->defrmargin; 157 p->tabwidth = term_len(p, 5); 158 159 if (NULL == p->symtab) 160 p->symtab = mchars_alloc(); 161 162 n = man_node(man); 163 meta = man_meta(man); 164 165 term_begin(p, print_man_head, print_man_foot, meta); 166 p->flags |= TERMP_NOSPACE; 167 168 memset(&mt, 0, sizeof(struct mtermp)); 169 170 mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); 171 mt.offset = term_len(p, p->defindent); 172 mt.pardist = 1; 173 174 if (n->child) 175 print_man_nodelist(p, &mt, n->child, meta); 176 177 term_end(p); 178 } 179 180 181 static size_t 182 a2height(const struct termp *p, const char *cp) 183 { 184 struct roffsu su; 185 186 if ( ! a2roffsu(cp, &su, SCALE_VS)) 187 SCALE_VS_INIT(&su, atoi(cp)); 188 189 return(term_vspan(p, &su)); 190 } 191 192 static int 193 a2width(const struct termp *p, const char *cp) 194 { 195 struct roffsu su; 196 197 if ( ! a2roffsu(cp, &su, SCALE_BU)) 198 return(-1); 199 200 return((int)term_hspan(p, &su)); 201 } 202 203 /* 204 * Printing leading vertical space before a block. 205 * This is used for the paragraph macros. 206 * The rules are pretty simple, since there's very little nesting going 207 * on here. Basically, if we're the first within another block (SS/SH), 208 * then don't emit vertical space. If we are (RS), then do. If not the 209 * first, print it. 210 */ 211 static void 212 print_bvspace(struct termp *p, const struct man_node *n, int pardist) 213 { 214 int i; 215 216 term_newln(p); 217 218 if (n->body && n->body->child) 219 if (MAN_TBL == n->body->child->type) 220 return; 221 222 if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) 223 if (NULL == n->prev) 224 return; 225 226 for (i = 0; i < pardist; i++) 227 term_vspace(p); 228 } 229 230 231 static int 232 pre_ign(DECL_ARGS) 233 { 234 235 return(0); 236 } 237 238 static int 239 pre_ll(DECL_ARGS) 240 { 241 242 term_setwidth(p, n->nchild ? n->child->string : NULL); 243 return(0); 244 } 245 246 static int 247 pre_I(DECL_ARGS) 248 { 249 250 term_fontrepl(p, TERMFONT_UNDER); 251 return(1); 252 } 253 254 static int 255 pre_literal(DECL_ARGS) 256 { 257 258 term_newln(p); 259 260 if (MAN_nf == n->tok || MAN_EX == n->tok) 261 mt->fl |= MANT_LITERAL; 262 else 263 mt->fl &= ~MANT_LITERAL; 264 265 /* 266 * Unlike .IP and .TP, .HP does not have a HEAD. 267 * So in case a second call to term_flushln() is needed, 268 * indentation has to be set up explicitly. 269 */ 270 if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { 271 p->offset = p->rmargin; 272 p->rmargin = p->maxrmargin; 273 p->trailspace = 0; 274 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); 275 p->flags |= TERMP_NOSPACE; 276 } 277 278 return(0); 279 } 280 281 static int 282 pre_PD(DECL_ARGS) 283 { 284 285 n = n->child; 286 if (0 == n) { 287 mt->pardist = 1; 288 return(0); 289 } 290 assert(MAN_TEXT == n->type); 291 mt->pardist = atoi(n->string); 292 return(0); 293 } 294 295 static int 296 pre_alternate(DECL_ARGS) 297 { 298 enum termfont font[2]; 299 const struct man_node *nn; 300 int savelit, i; 301 302 switch (n->tok) { 303 case MAN_RB: 304 font[0] = TERMFONT_NONE; 305 font[1] = TERMFONT_BOLD; 306 break; 307 case MAN_RI: 308 font[0] = TERMFONT_NONE; 309 font[1] = TERMFONT_UNDER; 310 break; 311 case MAN_BR: 312 font[0] = TERMFONT_BOLD; 313 font[1] = TERMFONT_NONE; 314 break; 315 case MAN_BI: 316 font[0] = TERMFONT_BOLD; 317 font[1] = TERMFONT_UNDER; 318 break; 319 case MAN_IR: 320 font[0] = TERMFONT_UNDER; 321 font[1] = TERMFONT_NONE; 322 break; 323 case MAN_IB: 324 font[0] = TERMFONT_UNDER; 325 font[1] = TERMFONT_BOLD; 326 break; 327 default: 328 abort(); 329 } 330 331 savelit = MANT_LITERAL & mt->fl; 332 mt->fl &= ~MANT_LITERAL; 333 334 for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { 335 term_fontrepl(p, font[i]); 336 if (savelit && NULL == nn->next) 337 mt->fl |= MANT_LITERAL; 338 print_man_node(p, mt, nn, meta); 339 if (nn->next) 340 p->flags |= TERMP_NOSPACE; 341 } 342 343 return(0); 344 } 345 346 static int 347 pre_B(DECL_ARGS) 348 { 349 350 term_fontrepl(p, TERMFONT_BOLD); 351 return(1); 352 } 353 354 static int 355 pre_OP(DECL_ARGS) 356 { 357 358 term_word(p, "["); 359 p->flags |= TERMP_NOSPACE; 360 361 if (NULL != (n = n->child)) { 362 term_fontrepl(p, TERMFONT_BOLD); 363 term_word(p, n->string); 364 } 365 if (NULL != n && NULL != n->next) { 366 term_fontrepl(p, TERMFONT_UNDER); 367 term_word(p, n->next->string); 368 } 369 370 term_fontrepl(p, TERMFONT_NONE); 371 p->flags |= TERMP_NOSPACE; 372 term_word(p, "]"); 373 return(0); 374 } 375 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 static int 417 pre_in(DECL_ARGS) 418 { 419 int len, less; 420 size_t v; 421 const char *cp; 422 423 term_newln(p); 424 425 if (NULL == n->child) { 426 p->offset = mt->offset; 427 return(0); 428 } 429 430 cp = n->child->string; 431 less = 0; 432 433 if ('-' == *cp) 434 less = -1; 435 else if ('+' == *cp) 436 less = 1; 437 else 438 cp--; 439 440 if ((len = a2width(p, ++cp)) < 0) 441 return(0); 442 443 v = (size_t)len; 444 445 if (less < 0) 446 p->offset -= p->offset > v ? v : p->offset; 447 else if (less > 0) 448 p->offset += v; 449 else 450 p->offset = v; 451 452 /* Don't let this creep beyond the right margin. */ 453 454 if (p->offset > p->rmargin) 455 p->offset = p->rmargin; 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 > p->offset ? 651 p->maxrmargin : p->offset; 652 break; 653 default: 654 break; 655 } 656 657 return(1); 658 } 659 660 static void 661 post_IP(DECL_ARGS) 662 { 663 664 switch (n->type) { 665 case MAN_HEAD: 666 term_flushln(p); 667 p->flags &= ~TERMP_NOBREAK; 668 p->trailspace = 0; 669 p->rmargin = p->maxrmargin; 670 break; 671 case MAN_BODY: 672 term_newln(p); 673 p->offset = mt->offset; 674 break; 675 default: 676 break; 677 } 678 } 679 680 static int 681 pre_TP(DECL_ARGS) 682 { 683 const struct man_node *nn; 684 size_t len; 685 int savelit, ival; 686 687 switch (n->type) { 688 case MAN_HEAD: 689 p->flags |= TERMP_NOBREAK; 690 p->trailspace = 1; 691 break; 692 case MAN_BODY: 693 p->flags |= TERMP_NOSPACE; 694 break; 695 case MAN_BLOCK: 696 print_bvspace(p, n, mt->pardist); 697 /* FALLTHROUGH */ 698 default: 699 return(1); 700 } 701 702 len = (size_t)mt->lmargin[mt->lmargincur]; 703 ival = -1; 704 705 /* Calculate offset. */ 706 707 if (NULL != (nn = n->parent->head->child)) 708 if (nn->string && 0 == (MAN_LINE & nn->flags)) 709 if ((ival = a2width(p, nn->string)) >= 0) 710 len = (size_t)ival; 711 712 switch (n->type) { 713 case MAN_HEAD: 714 /* Handle zero-length properly. */ 715 if (0 == len) 716 len = term_len(p, 1); 717 718 p->offset = mt->offset; 719 p->rmargin = mt->offset + len; 720 721 savelit = MANT_LITERAL & mt->fl; 722 mt->fl &= ~MANT_LITERAL; 723 724 /* Don't print same-line elements. */ 725 nn = n->child; 726 while (NULL != nn && 0 == (MAN_LINE & nn->flags)) 727 nn = nn->next; 728 729 while (NULL != nn) { 730 print_man_node(p, mt, nn, meta); 731 nn = nn->next; 732 } 733 734 if (savelit) 735 mt->fl |= MANT_LITERAL; 736 if (ival >= 0) 737 mt->lmargin[mt->lmargincur] = (size_t)ival; 738 739 return(0); 740 case MAN_BODY: 741 p->offset = mt->offset + len; 742 p->rmargin = p->maxrmargin > p->offset ? 743 p->maxrmargin : p->offset; 744 p->trailspace = 0; 745 p->flags &= ~TERMP_NOBREAK; 746 break; 747 default: 748 break; 749 } 750 751 return(1); 752 } 753 754 static void 755 post_TP(DECL_ARGS) 756 { 757 758 switch (n->type) { 759 case MAN_HEAD: 760 term_flushln(p); 761 break; 762 case MAN_BODY: 763 term_newln(p); 764 p->offset = mt->offset; 765 break; 766 default: 767 break; 768 } 769 } 770 771 static int 772 pre_SS(DECL_ARGS) 773 { 774 int i; 775 776 switch (n->type) { 777 case MAN_BLOCK: 778 mt->fl &= ~MANT_LITERAL; 779 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 780 mt->offset = term_len(p, p->defindent); 781 /* If following a prior empty `SS', no vspace. */ 782 if (n->prev && MAN_SS == n->prev->tok) 783 if (NULL == n->prev->body->child) 784 break; 785 if (NULL == n->prev) 786 break; 787 for (i = 0; i < mt->pardist; i++) 788 term_vspace(p); 789 break; 790 case MAN_HEAD: 791 term_fontrepl(p, TERMFONT_BOLD); 792 p->offset = term_len(p, 3); 793 break; 794 case MAN_BODY: 795 p->offset = mt->offset; 796 break; 797 default: 798 break; 799 } 800 801 return(1); 802 } 803 804 static void 805 post_SS(DECL_ARGS) 806 { 807 808 switch (n->type) { 809 case MAN_HEAD: 810 term_newln(p); 811 break; 812 case MAN_BODY: 813 term_newln(p); 814 break; 815 default: 816 break; 817 } 818 } 819 820 static int 821 pre_SH(DECL_ARGS) 822 { 823 int i; 824 825 switch (n->type) { 826 case MAN_BLOCK: 827 mt->fl &= ~MANT_LITERAL; 828 mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); 829 mt->offset = term_len(p, p->defindent); 830 /* If following a prior empty `SH', no vspace. */ 831 if (n->prev && MAN_SH == n->prev->tok) 832 if (NULL == n->prev->body->child) 833 break; 834 /* If the first macro, no vspae. */ 835 if (NULL == n->prev) 836 break; 837 for (i = 0; i < mt->pardist; i++) 838 term_vspace(p); 839 break; 840 case MAN_HEAD: 841 term_fontrepl(p, TERMFONT_BOLD); 842 p->offset = 0; 843 break; 844 case MAN_BODY: 845 p->offset = mt->offset; 846 break; 847 default: 848 break; 849 } 850 851 return(1); 852 } 853 854 static void 855 post_SH(DECL_ARGS) 856 { 857 858 switch (n->type) { 859 case MAN_HEAD: 860 term_newln(p); 861 break; 862 case MAN_BODY: 863 term_newln(p); 864 break; 865 default: 866 break; 867 } 868 } 869 870 static int 871 pre_RS(DECL_ARGS) 872 { 873 int ival; 874 size_t sz; 875 876 switch (n->type) { 877 case MAN_BLOCK: 878 term_newln(p); 879 return(1); 880 case MAN_HEAD: 881 return(0); 882 default: 883 break; 884 } 885 886 sz = term_len(p, p->defindent); 887 888 if (NULL != (n = n->parent->head->child)) 889 if ((ival = a2width(p, n->string)) >= 0) 890 sz = (size_t)ival; 891 892 mt->offset += sz; 893 p->offset = mt->offset; 894 p->rmargin = p->maxrmargin > p->offset ? 895 p->maxrmargin : p->offset; 896 897 if (++mt->lmarginsz < MAXMARGINS) 898 mt->lmargincur = mt->lmarginsz; 899 900 mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; 901 return(1); 902 } 903 904 static void 905 post_RS(DECL_ARGS) 906 { 907 int ival; 908 size_t sz; 909 910 switch (n->type) { 911 case MAN_BLOCK: 912 return; 913 case MAN_HEAD: 914 return; 915 default: 916 term_newln(p); 917 break; 918 } 919 920 sz = term_len(p, p->defindent); 921 922 if (NULL != (n = n->parent->head->child)) 923 if ((ival = a2width(p, n->string)) >= 0) 924 sz = (size_t)ival; 925 926 mt->offset = mt->offset < sz ? 0 : mt->offset - sz; 927 p->offset = mt->offset; 928 929 if (--mt->lmarginsz < MAXMARGINS) 930 mt->lmargincur = mt->lmarginsz; 931 } 932 933 static int 934 pre_UR(DECL_ARGS) 935 { 936 937 return (MAN_HEAD != n->type); 938 } 939 940 static void 941 post_UR(DECL_ARGS) 942 { 943 944 if (MAN_BLOCK != n->type) 945 return; 946 947 term_word(p, "<"); 948 p->flags |= TERMP_NOSPACE; 949 950 if (NULL != n->child->child) 951 print_man_node(p, mt, n->child->child, meta); 952 953 p->flags |= TERMP_NOSPACE; 954 term_word(p, ">"); 955 } 956 957 static void 958 print_man_node(DECL_ARGS) 959 { 960 size_t rm, rmax; 961 int c; 962 963 switch (n->type) { 964 case MAN_TEXT: 965 /* 966 * If we have a blank line, output a vertical space. 967 * If we have a space as the first character, break 968 * before printing the line's data. 969 */ 970 if ('\0' == *n->string) { 971 term_vspace(p); 972 return; 973 } else if (' ' == *n->string && MAN_LINE & n->flags) 974 term_newln(p); 975 976 term_word(p, n->string); 977 goto out; 978 979 case MAN_EQN: 980 term_eqn(p, n->eqn); 981 return; 982 case MAN_TBL: 983 /* 984 * Tables are preceded by a newline. Then process a 985 * table line, which will cause line termination, 986 */ 987 if (TBL_SPAN_FIRST & n->span->flags) 988 term_newln(p); 989 term_tbl(p, n->span); 990 return; 991 default: 992 break; 993 } 994 995 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 996 term_fontrepl(p, TERMFONT_NONE); 997 998 c = 1; 999 if (termacts[n->tok].pre) 1000 c = (*termacts[n->tok].pre)(p, mt, n, meta); 1001 1002 if (c && n->child) 1003 print_man_nodelist(p, mt, n->child, meta); 1004 1005 if (termacts[n->tok].post) 1006 (*termacts[n->tok].post)(p, mt, n, meta); 1007 if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) 1008 term_fontrepl(p, TERMFONT_NONE); 1009 1010 out: 1011 /* 1012 * If we're in a literal context, make sure that words 1013 * together on the same line stay together. This is a 1014 * POST-printing call, so we check the NEXT word. Since 1015 * -man doesn't have nested macros, we don't need to be 1016 * more specific than this. 1017 */ 1018 if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) && 1019 (NULL == n->next || MAN_LINE & n->next->flags)) { 1020 rm = p->rmargin; 1021 rmax = p->maxrmargin; 1022 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1023 p->flags |= TERMP_NOSPACE; 1024 if (NULL != n->string && '\0' != *n->string) 1025 term_flushln(p); 1026 else 1027 term_newln(p); 1028 if (rm < rmax && n->parent->tok == MAN_HP) { 1029 p->offset = rm; 1030 p->rmargin = rmax; 1031 } else 1032 p->rmargin = rm; 1033 p->maxrmargin = rmax; 1034 } 1035 if (MAN_EOS & n->flags) 1036 p->flags |= TERMP_SENTENCE; 1037 } 1038 1039 1040 static void 1041 print_man_nodelist(DECL_ARGS) 1042 { 1043 1044 print_man_node(p, mt, n, meta); 1045 if ( ! n->next) 1046 return; 1047 print_man_nodelist(p, mt, n->next, meta); 1048 } 1049 1050 static void 1051 print_man_foot(struct termp *p, const void *arg) 1052 { 1053 const struct man_meta *meta; 1054 char *title; 1055 size_t datelen; 1056 1057 meta = (const struct man_meta *)arg; 1058 assert(meta->title); 1059 assert(meta->msec); 1060 assert(meta->date); 1061 1062 term_fontrepl(p, TERMFONT_NONE); 1063 1064 if (meta->hasbody) 1065 term_vspace(p); 1066 1067 /* 1068 * Temporary, undocumented option to imitate mdoc(7) output. 1069 * In the bottom right corner, use the source instead of 1070 * the title. 1071 */ 1072 1073 if ( ! p->mdocstyle) { 1074 if (meta->hasbody) { 1075 term_vspace(p); 1076 term_vspace(p); 1077 } 1078 mandoc_asprintf(&title, "%s(%s)", 1079 meta->title, meta->msec); 1080 } else if (meta->source) { 1081 title = mandoc_strdup(meta->source); 1082 } else { 1083 title = mandoc_strdup(""); 1084 } 1085 datelen = term_strlen(p, meta->date); 1086 1087 /* Bottom left corner: manual source. */ 1088 1089 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1090 p->trailspace = 1; 1091 p->offset = 0; 1092 p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2; 1093 1094 if (meta->source) 1095 term_word(p, meta->source); 1096 term_flushln(p); 1097 1098 /* At the bottom in the middle: manual date. */ 1099 1100 p->flags |= TERMP_NOSPACE; 1101 p->offset = p->rmargin; 1102 p->rmargin = p->maxrmargin - term_strlen(p, title); 1103 if (p->offset + datelen >= p->rmargin) 1104 p->rmargin = p->offset + datelen; 1105 1106 term_word(p, meta->date); 1107 term_flushln(p); 1108 1109 /* Bottom right corner: manual title and section. */ 1110 1111 p->flags &= ~TERMP_NOBREAK; 1112 p->flags |= TERMP_NOSPACE; 1113 p->trailspace = 0; 1114 p->offset = p->rmargin; 1115 p->rmargin = p->maxrmargin; 1116 1117 term_word(p, title); 1118 term_flushln(p); 1119 free(title); 1120 } 1121 1122 static void 1123 print_man_head(struct termp *p, const void *arg) 1124 { 1125 const struct man_meta *meta; 1126 const char *volume; 1127 char *title; 1128 size_t vollen, titlen; 1129 1130 meta = (const struct man_meta *)arg; 1131 assert(meta->title); 1132 assert(meta->msec); 1133 1134 volume = NULL == meta->vol ? "" : meta->vol; 1135 vollen = term_strlen(p, volume); 1136 1137 /* Top left corner: manual title and section. */ 1138 1139 mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); 1140 titlen = term_strlen(p, title); 1141 1142 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 1143 p->trailspace = 1; 1144 p->offset = 0; 1145 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? 1146 (p->maxrmargin - vollen + term_len(p, 1)) / 2 : 1147 p->maxrmargin - vollen; 1148 1149 term_word(p, title); 1150 term_flushln(p); 1151 1152 /* At the top in the middle: manual volume. */ 1153 1154 p->flags |= TERMP_NOSPACE; 1155 p->offset = p->rmargin; 1156 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? 1157 p->maxrmargin - titlen : p->maxrmargin; 1158 1159 term_word(p, volume); 1160 term_flushln(p); 1161 1162 /* Top right corner: title and section, again. */ 1163 1164 p->flags &= ~TERMP_NOBREAK; 1165 p->trailspace = 0; 1166 if (p->rmargin + titlen <= p->maxrmargin) { 1167 p->flags |= TERMP_NOSPACE; 1168 p->offset = p->rmargin; 1169 p->rmargin = p->maxrmargin; 1170 term_word(p, title); 1171 term_flushln(p); 1172 } 1173 1174 p->flags &= ~TERMP_NOSPACE; 1175 p->offset = 0; 1176 p->rmargin = p->maxrmargin; 1177 1178 /* 1179 * Groff prints three blank lines before the content. 1180 * Do the same, except in the temporary, undocumented 1181 * mode imitating mdoc(7) output. 1182 */ 1183 1184 term_vspace(p); 1185 if ( ! p->mdocstyle) { 1186 term_vspace(p); 1187 term_vspace(p); 1188 } 1189 free(title); 1190 } 1191