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