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