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