1 /* $Vendor-Id: mdoc_term.c,v 1.208 2011/01/06 14:05:12 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010 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 <stdint.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include "mandoc.h" 32 #include "out.h" 33 #include "term.h" 34 #include "mdoc.h" 35 #include "chars.h" 36 #include "main.h" 37 38 #define INDENT 5 39 #define HALFINDENT 3 40 41 struct termpair { 42 struct termpair *ppair; 43 int count; 44 }; 45 46 #define DECL_ARGS struct termp *p, \ 47 struct termpair *pair, \ 48 const struct mdoc_meta *m, \ 49 const struct mdoc_node *n 50 51 struct termact { 52 int (*pre)(DECL_ARGS); 53 void (*post)(DECL_ARGS); 54 }; 55 56 static size_t a2width(const struct termp *, const char *); 57 static size_t a2height(const struct termp *, const char *); 58 static size_t a2offs(const struct termp *, const char *); 59 60 static void print_bvspace(struct termp *, 61 const struct mdoc_node *, 62 const struct mdoc_node *); 63 static void print_mdoc_node(DECL_ARGS); 64 static void print_mdoc_nodelist(DECL_ARGS); 65 static void print_mdoc_head(struct termp *, const void *); 66 static void print_mdoc_foot(struct termp *, const void *); 67 static void synopsis_pre(struct termp *, 68 const struct mdoc_node *); 69 70 static void termp____post(DECL_ARGS); 71 static void termp__t_post(DECL_ARGS); 72 static void termp_an_post(DECL_ARGS); 73 static void termp_bd_post(DECL_ARGS); 74 static void termp_bk_post(DECL_ARGS); 75 static void termp_bl_post(DECL_ARGS); 76 static void termp_bx_post(DECL_ARGS); 77 static void termp_d1_post(DECL_ARGS); 78 static void termp_fo_post(DECL_ARGS); 79 static void termp_in_post(DECL_ARGS); 80 static void termp_it_post(DECL_ARGS); 81 static void termp_lb_post(DECL_ARGS); 82 static void termp_nm_post(DECL_ARGS); 83 static void termp_pf_post(DECL_ARGS); 84 static void termp_quote_post(DECL_ARGS); 85 static void termp_sh_post(DECL_ARGS); 86 static void termp_ss_post(DECL_ARGS); 87 88 static int termp__a_pre(DECL_ARGS); 89 static int termp__t_pre(DECL_ARGS); 90 static int termp_an_pre(DECL_ARGS); 91 static int termp_ap_pre(DECL_ARGS); 92 static int termp_bd_pre(DECL_ARGS); 93 static int termp_bf_pre(DECL_ARGS); 94 static int termp_bk_pre(DECL_ARGS); 95 static int termp_bl_pre(DECL_ARGS); 96 static int termp_bold_pre(DECL_ARGS); 97 static int termp_bt_pre(DECL_ARGS); 98 static int termp_cd_pre(DECL_ARGS); 99 static int termp_d1_pre(DECL_ARGS); 100 static int termp_ex_pre(DECL_ARGS); 101 static int termp_fa_pre(DECL_ARGS); 102 static int termp_fd_pre(DECL_ARGS); 103 static int termp_fl_pre(DECL_ARGS); 104 static int termp_fn_pre(DECL_ARGS); 105 static int termp_fo_pre(DECL_ARGS); 106 static int termp_ft_pre(DECL_ARGS); 107 static int termp_igndelim_pre(DECL_ARGS); 108 static int termp_in_pre(DECL_ARGS); 109 static int termp_it_pre(DECL_ARGS); 110 static int termp_li_pre(DECL_ARGS); 111 static int termp_lk_pre(DECL_ARGS); 112 static int termp_nd_pre(DECL_ARGS); 113 static int termp_nm_pre(DECL_ARGS); 114 static int termp_ns_pre(DECL_ARGS); 115 static int termp_quote_pre(DECL_ARGS); 116 static int termp_rs_pre(DECL_ARGS); 117 static int termp_rv_pre(DECL_ARGS); 118 static int termp_sh_pre(DECL_ARGS); 119 static int termp_sm_pre(DECL_ARGS); 120 static int termp_sp_pre(DECL_ARGS); 121 static int termp_ss_pre(DECL_ARGS); 122 static int termp_under_pre(DECL_ARGS); 123 static int termp_ud_pre(DECL_ARGS); 124 static int termp_vt_pre(DECL_ARGS); 125 static int termp_xr_pre(DECL_ARGS); 126 static int termp_xx_pre(DECL_ARGS); 127 128 static const struct termact termacts[MDOC_MAX] = { 129 { termp_ap_pre, NULL }, /* Ap */ 130 { NULL, NULL }, /* Dd */ 131 { NULL, NULL }, /* Dt */ 132 { NULL, NULL }, /* Os */ 133 { termp_sh_pre, termp_sh_post }, /* Sh */ 134 { termp_ss_pre, termp_ss_post }, /* Ss */ 135 { termp_sp_pre, NULL }, /* Pp */ 136 { termp_d1_pre, termp_d1_post }, /* D1 */ 137 { termp_d1_pre, termp_d1_post }, /* Dl */ 138 { termp_bd_pre, termp_bd_post }, /* Bd */ 139 { NULL, NULL }, /* Ed */ 140 { termp_bl_pre, termp_bl_post }, /* Bl */ 141 { NULL, NULL }, /* El */ 142 { termp_it_pre, termp_it_post }, /* It */ 143 { termp_under_pre, NULL }, /* Ad */ 144 { termp_an_pre, termp_an_post }, /* An */ 145 { termp_under_pre, NULL }, /* Ar */ 146 { termp_cd_pre, NULL }, /* Cd */ 147 { termp_bold_pre, NULL }, /* Cm */ 148 { NULL, NULL }, /* Dv */ 149 { NULL, NULL }, /* Er */ 150 { NULL, NULL }, /* Ev */ 151 { termp_ex_pre, NULL }, /* Ex */ 152 { termp_fa_pre, NULL }, /* Fa */ 153 { termp_fd_pre, NULL }, /* Fd */ 154 { termp_fl_pre, NULL }, /* Fl */ 155 { termp_fn_pre, NULL }, /* Fn */ 156 { termp_ft_pre, NULL }, /* Ft */ 157 { termp_bold_pre, NULL }, /* Ic */ 158 { termp_in_pre, termp_in_post }, /* In */ 159 { termp_li_pre, NULL }, /* Li */ 160 { termp_nd_pre, NULL }, /* Nd */ 161 { termp_nm_pre, termp_nm_post }, /* Nm */ 162 { termp_quote_pre, termp_quote_post }, /* Op */ 163 { NULL, NULL }, /* Ot */ 164 { termp_under_pre, NULL }, /* Pa */ 165 { termp_rv_pre, NULL }, /* Rv */ 166 { NULL, NULL }, /* St */ 167 { termp_under_pre, NULL }, /* Va */ 168 { termp_vt_pre, NULL }, /* Vt */ 169 { termp_xr_pre, NULL }, /* Xr */ 170 { termp__a_pre, termp____post }, /* %A */ 171 { termp_under_pre, termp____post }, /* %B */ 172 { NULL, termp____post }, /* %D */ 173 { termp_under_pre, termp____post }, /* %I */ 174 { termp_under_pre, termp____post }, /* %J */ 175 { NULL, termp____post }, /* %N */ 176 { NULL, termp____post }, /* %O */ 177 { NULL, termp____post }, /* %P */ 178 { NULL, termp____post }, /* %R */ 179 { termp__t_pre, termp__t_post }, /* %T */ 180 { NULL, termp____post }, /* %V */ 181 { NULL, NULL }, /* Ac */ 182 { termp_quote_pre, termp_quote_post }, /* Ao */ 183 { termp_quote_pre, termp_quote_post }, /* Aq */ 184 { NULL, NULL }, /* At */ 185 { NULL, NULL }, /* Bc */ 186 { termp_bf_pre, NULL }, /* Bf */ 187 { termp_quote_pre, termp_quote_post }, /* Bo */ 188 { termp_quote_pre, termp_quote_post }, /* Bq */ 189 { termp_xx_pre, NULL }, /* Bsx */ 190 { NULL, termp_bx_post }, /* Bx */ 191 { NULL, NULL }, /* Db */ 192 { NULL, NULL }, /* Dc */ 193 { termp_quote_pre, termp_quote_post }, /* Do */ 194 { termp_quote_pre, termp_quote_post }, /* Dq */ 195 { NULL, NULL }, /* Ec */ /* FIXME: no space */ 196 { NULL, NULL }, /* Ef */ 197 { termp_under_pre, NULL }, /* Em */ 198 { NULL, NULL }, /* Eo */ 199 { termp_xx_pre, NULL }, /* Fx */ 200 { termp_bold_pre, NULL }, /* Ms */ 201 { termp_igndelim_pre, NULL }, /* No */ 202 { termp_ns_pre, NULL }, /* Ns */ 203 { termp_xx_pre, NULL }, /* Nx */ 204 { termp_xx_pre, NULL }, /* Ox */ 205 { NULL, NULL }, /* Pc */ 206 { termp_igndelim_pre, termp_pf_post }, /* Pf */ 207 { termp_quote_pre, termp_quote_post }, /* Po */ 208 { termp_quote_pre, termp_quote_post }, /* Pq */ 209 { NULL, NULL }, /* Qc */ 210 { termp_quote_pre, termp_quote_post }, /* Ql */ 211 { termp_quote_pre, termp_quote_post }, /* Qo */ 212 { termp_quote_pre, termp_quote_post }, /* Qq */ 213 { NULL, NULL }, /* Re */ 214 { termp_rs_pre, NULL }, /* Rs */ 215 { NULL, NULL }, /* Sc */ 216 { termp_quote_pre, termp_quote_post }, /* So */ 217 { termp_quote_pre, termp_quote_post }, /* Sq */ 218 { termp_sm_pre, NULL }, /* Sm */ 219 { termp_under_pre, NULL }, /* Sx */ 220 { termp_bold_pre, NULL }, /* Sy */ 221 { NULL, NULL }, /* Tn */ 222 { termp_xx_pre, NULL }, /* Ux */ 223 { NULL, NULL }, /* Xc */ 224 { NULL, NULL }, /* Xo */ 225 { termp_fo_pre, termp_fo_post }, /* Fo */ 226 { NULL, NULL }, /* Fc */ 227 { termp_quote_pre, termp_quote_post }, /* Oo */ 228 { NULL, NULL }, /* Oc */ 229 { termp_bk_pre, termp_bk_post }, /* Bk */ 230 { NULL, NULL }, /* Ek */ 231 { termp_bt_pre, NULL }, /* Bt */ 232 { NULL, NULL }, /* Hf */ 233 { NULL, NULL }, /* Fr */ 234 { termp_ud_pre, NULL }, /* Ud */ 235 { NULL, termp_lb_post }, /* Lb */ 236 { termp_sp_pre, NULL }, /* Lp */ 237 { termp_lk_pre, NULL }, /* Lk */ 238 { termp_under_pre, NULL }, /* Mt */ 239 { termp_quote_pre, termp_quote_post }, /* Brq */ 240 { termp_quote_pre, termp_quote_post }, /* Bro */ 241 { NULL, NULL }, /* Brc */ 242 { NULL, termp____post }, /* %C */ 243 { NULL, NULL }, /* Es */ /* TODO */ 244 { NULL, NULL }, /* En */ /* TODO */ 245 { termp_xx_pre, NULL }, /* Dx */ 246 { NULL, termp____post }, /* %Q */ 247 { termp_sp_pre, NULL }, /* br */ 248 { termp_sp_pre, NULL }, /* sp */ 249 { termp_under_pre, termp____post }, /* %U */ 250 { NULL, NULL }, /* Ta */ 251 }; 252 253 254 void 255 terminal_mdoc(void *arg, const struct mdoc *mdoc) 256 { 257 const struct mdoc_node *n; 258 const struct mdoc_meta *m; 259 struct termp *p; 260 261 p = (struct termp *)arg; 262 263 p->overstep = 0; 264 p->maxrmargin = p->defrmargin; 265 p->tabwidth = term_len(p, 5); 266 267 if (NULL == p->symtab) 268 switch (p->enc) { 269 case (TERMENC_ASCII): 270 p->symtab = chars_init(CHARS_ASCII); 271 break; 272 default: 273 abort(); 274 /* NOTREACHED */ 275 } 276 277 n = mdoc_node(mdoc); 278 m = mdoc_meta(mdoc); 279 280 term_begin(p, print_mdoc_head, print_mdoc_foot, m); 281 282 if (n->child) 283 print_mdoc_nodelist(p, NULL, m, n->child); 284 285 term_end(p); 286 } 287 288 289 static void 290 print_mdoc_nodelist(DECL_ARGS) 291 { 292 293 print_mdoc_node(p, pair, m, n); 294 if (n->next) 295 print_mdoc_nodelist(p, pair, m, n->next); 296 } 297 298 299 /* ARGSUSED */ 300 static void 301 print_mdoc_node(DECL_ARGS) 302 { 303 int chld; 304 const void *font; 305 struct termpair npair; 306 size_t offset, rmargin; 307 308 chld = 1; 309 offset = p->offset; 310 rmargin = p->rmargin; 311 font = term_fontq(p); 312 313 memset(&npair, 0, sizeof(struct termpair)); 314 npair.ppair = pair; 315 316 switch (n->type) { 317 case (MDOC_TEXT): 318 term_word(p, n->string); 319 break; 320 case (MDOC_TBL): 321 term_tbl(p, n->span); 322 break; 323 default: 324 if (termacts[n->tok].pre && ENDBODY_NOT == n->end) 325 chld = (*termacts[n->tok].pre) 326 (p, &npair, m, n); 327 break; 328 } 329 330 /* 331 * Keeps only work until the end of a line. If a keep was 332 * invoked in a prior line, revert it to PREKEEP. 333 * 334 * Also let SYNPRETTY sections behave as if they were wrapped 335 * in a `Bk' block. 336 */ 337 338 if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) { 339 if (n->prev && n->prev->line != n->line) { 340 p->flags &= ~TERMP_KEEP; 341 p->flags |= TERMP_PREKEEP; 342 } else if (NULL == n->prev) { 343 if (n->parent && n->parent->line != n->line) { 344 p->flags &= ~TERMP_KEEP; 345 p->flags |= TERMP_PREKEEP; 346 } 347 } 348 } 349 350 /* 351 * Since SYNPRETTY sections aren't "turned off" with `Ek', 352 * we have to intuit whether we should disable formatting. 353 */ 354 355 if ( ! (MDOC_SYNPRETTY & n->flags) && 356 ((n->prev && MDOC_SYNPRETTY & n->prev->flags) || 357 (n->parent && MDOC_SYNPRETTY & n->parent->flags))) 358 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 359 360 if (chld && n->child) 361 print_mdoc_nodelist(p, &npair, m, n->child); 362 363 term_fontpopq(p, font); 364 365 switch (n->type) { 366 case (MDOC_TEXT): 367 break; 368 case (MDOC_TBL): 369 break; 370 default: 371 if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags) 372 break; 373 (void)(*termacts[n->tok].post)(p, &npair, m, n); 374 375 /* 376 * Explicit end tokens not only call the post 377 * handler, but also tell the respective block 378 * that it must not call the post handler again. 379 */ 380 if (ENDBODY_NOT != n->end) 381 n->pending->flags |= MDOC_ENDED; 382 383 /* 384 * End of line terminating an implicit block 385 * while an explicit block is still open. 386 * Continue the explicit block without spacing. 387 */ 388 if (ENDBODY_NOSPACE == n->end) 389 p->flags |= TERMP_NOSPACE; 390 break; 391 } 392 393 if (MDOC_EOS & n->flags) 394 p->flags |= TERMP_SENTENCE; 395 396 p->offset = offset; 397 p->rmargin = rmargin; 398 } 399 400 401 static void 402 print_mdoc_foot(struct termp *p, const void *arg) 403 { 404 char buf[DATESIZ], os[BUFSIZ]; 405 const struct mdoc_meta *m; 406 407 m = (const struct mdoc_meta *)arg; 408 409 term_fontrepl(p, TERMFONT_NONE); 410 411 /* 412 * Output the footer in new-groff style, that is, three columns 413 * with the middle being the manual date and flanking columns 414 * being the operating system: 415 * 416 * SYSTEM DATE SYSTEM 417 */ 418 419 time2a(m->date, buf, DATESIZ); 420 strlcpy(os, m->os, BUFSIZ); 421 422 term_vspace(p); 423 424 p->offset = 0; 425 p->rmargin = (p->maxrmargin - 426 term_strlen(p, buf) + term_len(p, 1)) / 2; 427 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 428 429 term_word(p, os); 430 term_flushln(p); 431 432 p->offset = p->rmargin; 433 p->rmargin = p->maxrmargin - term_strlen(p, os); 434 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 435 436 term_word(p, buf); 437 term_flushln(p); 438 439 p->offset = p->rmargin; 440 p->rmargin = p->maxrmargin; 441 p->flags &= ~TERMP_NOBREAK; 442 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 443 444 term_word(p, os); 445 term_flushln(p); 446 447 p->offset = 0; 448 p->rmargin = p->maxrmargin; 449 p->flags = 0; 450 } 451 452 453 static void 454 print_mdoc_head(struct termp *p, const void *arg) 455 { 456 char buf[BUFSIZ], title[BUFSIZ]; 457 const struct mdoc_meta *m; 458 459 m = (const struct mdoc_meta *)arg; 460 461 p->rmargin = p->maxrmargin; 462 p->offset = 0; 463 464 /* 465 * The header is strange. It has three components, which are 466 * really two with the first duplicated. It goes like this: 467 * 468 * IDENTIFIER TITLE IDENTIFIER 469 * 470 * The IDENTIFIER is NAME(SECTION), which is the command-name 471 * (if given, or "unknown" if not) followed by the manual page 472 * section. These are given in `Dt'. The TITLE is a free-form 473 * string depending on the manual volume. If not specified, it 474 * switches on the manual section. 475 */ 476 477 assert(m->vol); 478 strlcpy(buf, m->vol, BUFSIZ); 479 480 if (m->arch) { 481 strlcat(buf, " (", BUFSIZ); 482 strlcat(buf, m->arch, BUFSIZ); 483 strlcat(buf, ")", BUFSIZ); 484 } 485 486 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 487 488 p->offset = 0; 489 p->rmargin = (p->maxrmargin - 490 term_strlen(p, buf) + term_len(p, 1)) / 2; 491 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 492 493 term_word(p, title); 494 term_flushln(p); 495 496 p->offset = p->rmargin; 497 p->rmargin = p->maxrmargin - term_strlen(p, title); 498 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 499 500 term_word(p, buf); 501 term_flushln(p); 502 503 p->offset = p->rmargin; 504 p->rmargin = p->maxrmargin; 505 p->flags &= ~TERMP_NOBREAK; 506 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 507 508 term_word(p, title); 509 term_flushln(p); 510 511 p->offset = 0; 512 p->rmargin = p->maxrmargin; 513 p->flags &= ~TERMP_NOSPACE; 514 } 515 516 517 static size_t 518 a2height(const struct termp *p, const char *v) 519 { 520 struct roffsu su; 521 522 assert(v); 523 if ( ! a2roffsu(v, &su, SCALE_VS)) 524 SCALE_VS_INIT(&su, term_len(p, 1)); 525 526 return(term_vspan(p, &su)); 527 } 528 529 530 static size_t 531 a2width(const struct termp *p, const char *v) 532 { 533 struct roffsu su; 534 535 assert(v); 536 if ( ! a2roffsu(v, &su, SCALE_MAX)) 537 SCALE_HS_INIT(&su, term_strlen(p, v)); 538 539 return(term_hspan(p, &su)); 540 } 541 542 543 static size_t 544 a2offs(const struct termp *p, const char *v) 545 { 546 struct roffsu su; 547 548 if ('\0' == *v) 549 return(0); 550 else if (0 == strcmp(v, "left")) 551 return(0); 552 else if (0 == strcmp(v, "indent")) 553 return(term_len(p, INDENT + 1)); 554 else if (0 == strcmp(v, "indent-two")) 555 return(term_len(p, (INDENT + 1) * 2)); 556 else if ( ! a2roffsu(v, &su, SCALE_MAX)) 557 SCALE_HS_INIT(&su, term_strlen(p, v)); 558 559 return(term_hspan(p, &su)); 560 } 561 562 563 /* 564 * Determine how much space to print out before block elements of `It' 565 * (and thus `Bl') and `Bd'. And then go ahead and print that space, 566 * too. 567 */ 568 static void 569 print_bvspace(struct termp *p, 570 const struct mdoc_node *bl, 571 const struct mdoc_node *n) 572 { 573 const struct mdoc_node *nn; 574 575 term_newln(p); 576 577 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) 578 return; 579 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) 580 return; 581 582 /* Do not vspace directly after Ss/Sh. */ 583 584 for (nn = n; nn; nn = nn->parent) { 585 if (MDOC_BLOCK != nn->type) 586 continue; 587 if (MDOC_Ss == nn->tok) 588 return; 589 if (MDOC_Sh == nn->tok) 590 return; 591 if (NULL == nn->prev) 592 continue; 593 break; 594 } 595 596 /* A `-column' does not assert vspace within the list. */ 597 598 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) 599 if (n->prev && MDOC_It == n->prev->tok) 600 return; 601 602 /* A `-diag' without body does not vspace. */ 603 604 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) 605 if (n->prev && MDOC_It == n->prev->tok) { 606 assert(n->prev->body); 607 if (NULL == n->prev->body->child) 608 return; 609 } 610 611 term_vspace(p); 612 } 613 614 615 /* ARGSUSED */ 616 static int 617 termp_it_pre(DECL_ARGS) 618 { 619 const struct mdoc_node *bl, *nn; 620 char buf[7]; 621 int i; 622 size_t width, offset, ncols, dcol; 623 enum mdoc_list type; 624 625 if (MDOC_BLOCK == n->type) { 626 print_bvspace(p, n->parent->parent, n); 627 return(1); 628 } 629 630 bl = n->parent->parent->parent; 631 type = bl->norm->Bl.type; 632 633 /* 634 * First calculate width and offset. This is pretty easy unless 635 * we're a -column list, in which case all prior columns must 636 * be accounted for. 637 */ 638 639 width = offset = 0; 640 641 if (bl->norm->Bl.offs) 642 offset = a2offs(p, bl->norm->Bl.offs); 643 644 switch (type) { 645 case (LIST_column): 646 if (MDOC_HEAD == n->type) 647 break; 648 649 /* 650 * Imitate groff's column handling: 651 * - For each earlier column, add its width. 652 * - For less than 5 columns, add four more blanks per 653 * column. 654 * - For exactly 5 columns, add three more blank per 655 * column. 656 * - For more than 5 columns, add only one column. 657 */ 658 ncols = bl->norm->Bl.ncols; 659 660 /* LINTED */ 661 dcol = ncols < 5 ? term_len(p, 4) : 662 ncols == 5 ? term_len(p, 3) : term_len(p, 1); 663 664 /* 665 * Calculate the offset by applying all prior MDOC_BODY, 666 * so we stop at the MDOC_HEAD (NULL == nn->prev). 667 */ 668 669 for (i = 0, nn = n->prev; 670 nn->prev && i < (int)ncols; 671 nn = nn->prev, i++) 672 offset += dcol + a2width 673 (p, bl->norm->Bl.cols[i]); 674 675 /* 676 * When exceeding the declared number of columns, leave 677 * the remaining widths at 0. This will later be 678 * adjusted to the default width of 10, or, for the last 679 * column, stretched to the right margin. 680 */ 681 if (i >= (int)ncols) 682 break; 683 684 /* 685 * Use the declared column widths, extended as explained 686 * in the preceding paragraph. 687 */ 688 width = a2width(p, bl->norm->Bl.cols[i]) + dcol; 689 break; 690 default: 691 if (NULL == bl->norm->Bl.width) 692 break; 693 694 /* 695 * Note: buffer the width by 2, which is groff's magic 696 * number for buffering single arguments. See the above 697 * handling for column for how this changes. 698 */ 699 assert(bl->norm->Bl.width); 700 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); 701 break; 702 } 703 704 /* 705 * List-type can override the width in the case of fixed-head 706 * values (bullet, dash/hyphen, enum). Tags need a non-zero 707 * offset. 708 */ 709 710 switch (type) { 711 case (LIST_bullet): 712 /* FALLTHROUGH */ 713 case (LIST_dash): 714 /* FALLTHROUGH */ 715 case (LIST_hyphen): 716 if (width < term_len(p, 4)) 717 width = term_len(p, 4); 718 break; 719 case (LIST_enum): 720 if (width < term_len(p, 5)) 721 width = term_len(p, 5); 722 break; 723 case (LIST_hang): 724 if (0 == width) 725 width = term_len(p, 8); 726 break; 727 case (LIST_column): 728 /* FALLTHROUGH */ 729 case (LIST_tag): 730 if (0 == width) 731 width = term_len(p, 10); 732 break; 733 default: 734 break; 735 } 736 737 /* 738 * Whitespace control. Inset bodies need an initial space, 739 * while diagonal bodies need two. 740 */ 741 742 p->flags |= TERMP_NOSPACE; 743 744 switch (type) { 745 case (LIST_diag): 746 if (MDOC_BODY == n->type) 747 term_word(p, "\\ \\ "); 748 break; 749 case (LIST_inset): 750 if (MDOC_BODY == n->type) 751 term_word(p, "\\ "); 752 break; 753 default: 754 break; 755 } 756 757 p->flags |= TERMP_NOSPACE; 758 759 switch (type) { 760 case (LIST_diag): 761 if (MDOC_HEAD == n->type) 762 term_fontpush(p, TERMFONT_BOLD); 763 break; 764 default: 765 break; 766 } 767 768 /* 769 * Pad and break control. This is the tricky part. These flags 770 * are documented in term_flushln() in term.c. Note that we're 771 * going to unset all of these flags in termp_it_post() when we 772 * exit. 773 */ 774 775 switch (type) { 776 case (LIST_bullet): 777 /* FALLTHROUGH */ 778 case (LIST_dash): 779 /* FALLTHROUGH */ 780 case (LIST_enum): 781 /* FALLTHROUGH */ 782 case (LIST_hyphen): 783 if (MDOC_HEAD == n->type) 784 p->flags |= TERMP_NOBREAK; 785 else 786 p->flags |= TERMP_NOLPAD; 787 break; 788 case (LIST_hang): 789 if (MDOC_HEAD == n->type) 790 p->flags |= TERMP_NOBREAK; 791 else 792 p->flags |= TERMP_NOLPAD; 793 794 if (MDOC_HEAD != n->type) 795 break; 796 797 /* 798 * This is ugly. If `-hang' is specified and the body 799 * is a `Bl' or `Bd', then we want basically to nullify 800 * the "overstep" effect in term_flushln() and treat 801 * this as a `-ohang' list instead. 802 */ 803 if (n->next->child && 804 (MDOC_Bl == n->next->child->tok || 805 MDOC_Bd == n->next->child->tok)) { 806 p->flags &= ~TERMP_NOBREAK; 807 p->flags &= ~TERMP_NOLPAD; 808 } else 809 p->flags |= TERMP_HANG; 810 break; 811 case (LIST_tag): 812 if (MDOC_HEAD == n->type) 813 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE; 814 else 815 p->flags |= TERMP_NOLPAD; 816 817 if (MDOC_HEAD != n->type) 818 break; 819 if (NULL == n->next || NULL == n->next->child) 820 p->flags |= TERMP_DANGLE; 821 break; 822 case (LIST_column): 823 if (MDOC_HEAD == n->type) 824 break; 825 826 if (NULL == n->next) 827 p->flags &= ~TERMP_NOBREAK; 828 else 829 p->flags |= TERMP_NOBREAK; 830 831 assert(n->prev); 832 if (MDOC_BODY == n->prev->type) 833 p->flags |= TERMP_NOLPAD; 834 835 break; 836 case (LIST_diag): 837 if (MDOC_HEAD == n->type) 838 p->flags |= TERMP_NOBREAK; 839 break; 840 default: 841 break; 842 } 843 844 /* 845 * Margin control. Set-head-width lists have their right 846 * margins shortened. The body for these lists has the offset 847 * necessarily lengthened. Everybody gets the offset. 848 */ 849 850 p->offset += offset; 851 852 switch (type) { 853 case (LIST_hang): 854 /* 855 * Same stipulation as above, regarding `-hang'. We 856 * don't want to recalculate rmargin and offsets when 857 * using `Bd' or `Bl' within `-hang' overstep lists. 858 */ 859 if (MDOC_HEAD == n->type && n->next->child && 860 (MDOC_Bl == n->next->child->tok || 861 MDOC_Bd == n->next->child->tok)) 862 break; 863 /* FALLTHROUGH */ 864 case (LIST_bullet): 865 /* FALLTHROUGH */ 866 case (LIST_dash): 867 /* FALLTHROUGH */ 868 case (LIST_enum): 869 /* FALLTHROUGH */ 870 case (LIST_hyphen): 871 /* FALLTHROUGH */ 872 case (LIST_tag): 873 assert(width); 874 if (MDOC_HEAD == n->type) 875 p->rmargin = p->offset + width; 876 else 877 p->offset += width; 878 break; 879 case (LIST_column): 880 assert(width); 881 p->rmargin = p->offset + width; 882 /* 883 * XXX - this behaviour is not documented: the 884 * right-most column is filled to the right margin. 885 */ 886 if (MDOC_HEAD == n->type) 887 break; 888 if (NULL == n->next && p->rmargin < p->maxrmargin) 889 p->rmargin = p->maxrmargin; 890 break; 891 default: 892 break; 893 } 894 895 /* 896 * The dash, hyphen, bullet and enum lists all have a special 897 * HEAD character (temporarily bold, in some cases). 898 */ 899 900 if (MDOC_HEAD == n->type) 901 switch (type) { 902 case (LIST_bullet): 903 term_fontpush(p, TERMFONT_BOLD); 904 term_word(p, "\\[bu]"); 905 term_fontpop(p); 906 break; 907 case (LIST_dash): 908 /* FALLTHROUGH */ 909 case (LIST_hyphen): 910 term_fontpush(p, TERMFONT_BOLD); 911 term_word(p, "\\(hy"); 912 term_fontpop(p); 913 break; 914 case (LIST_enum): 915 (pair->ppair->ppair->count)++; 916 snprintf(buf, sizeof(buf), "%d.", 917 pair->ppair->ppair->count); 918 term_word(p, buf); 919 break; 920 default: 921 break; 922 } 923 924 /* 925 * If we're not going to process our children, indicate so here. 926 */ 927 928 switch (type) { 929 case (LIST_bullet): 930 /* FALLTHROUGH */ 931 case (LIST_item): 932 /* FALLTHROUGH */ 933 case (LIST_dash): 934 /* FALLTHROUGH */ 935 case (LIST_hyphen): 936 /* FALLTHROUGH */ 937 case (LIST_enum): 938 if (MDOC_HEAD == n->type) 939 return(0); 940 break; 941 case (LIST_column): 942 if (MDOC_HEAD == n->type) 943 return(0); 944 break; 945 default: 946 break; 947 } 948 949 return(1); 950 } 951 952 953 /* ARGSUSED */ 954 static void 955 termp_it_post(DECL_ARGS) 956 { 957 enum mdoc_list type; 958 959 if (MDOC_BLOCK == n->type) 960 return; 961 962 type = n->parent->parent->parent->norm->Bl.type; 963 964 switch (type) { 965 case (LIST_item): 966 /* FALLTHROUGH */ 967 case (LIST_diag): 968 /* FALLTHROUGH */ 969 case (LIST_inset): 970 if (MDOC_BODY == n->type) 971 term_newln(p); 972 break; 973 case (LIST_column): 974 if (MDOC_BODY == n->type) 975 term_flushln(p); 976 break; 977 default: 978 term_newln(p); 979 break; 980 } 981 982 /* 983 * Now that our output is flushed, we can reset our tags. Since 984 * only `It' sets these flags, we're free to assume that nobody 985 * has munged them in the meanwhile. 986 */ 987 988 p->flags &= ~TERMP_DANGLE; 989 p->flags &= ~TERMP_NOBREAK; 990 p->flags &= ~TERMP_TWOSPACE; 991 p->flags &= ~TERMP_NOLPAD; 992 p->flags &= ~TERMP_HANG; 993 } 994 995 996 /* ARGSUSED */ 997 static int 998 termp_nm_pre(DECL_ARGS) 999 { 1000 1001 if (MDOC_BLOCK == n->type) 1002 return(1); 1003 1004 if (MDOC_BODY == n->type) { 1005 if (NULL == n->child) 1006 return(0); 1007 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 1008 p->offset += term_len(p, 1) + 1009 (NULL == n->prev->child ? term_strlen(p, m->name) : 1010 MDOC_TEXT == n->prev->child->type ? 1011 term_strlen(p, n->prev->child->string) : 1012 term_len(p, 5)); 1013 return(1); 1014 } 1015 1016 if (NULL == n->child && NULL == m->name) 1017 return(0); 1018 1019 if (MDOC_HEAD == n->type) 1020 synopsis_pre(p, n->parent); 1021 1022 if (MDOC_HEAD == n->type && n->next->child) { 1023 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1024 p->rmargin = p->offset + term_len(p, 1); 1025 if (NULL == n->child) { 1026 p->rmargin += term_strlen(p, m->name); 1027 } else if (MDOC_TEXT == n->child->type) { 1028 p->rmargin += term_strlen(p, n->child->string); 1029 if (n->child->next) 1030 p->flags |= TERMP_HANG; 1031 } else { 1032 p->rmargin += term_len(p, 5); 1033 p->flags |= TERMP_HANG; 1034 } 1035 } 1036 1037 term_fontpush(p, TERMFONT_BOLD); 1038 if (NULL == n->child) 1039 term_word(p, m->name); 1040 return(1); 1041 } 1042 1043 1044 /* ARGSUSED */ 1045 static void 1046 termp_nm_post(DECL_ARGS) 1047 { 1048 1049 if (MDOC_HEAD == n->type && n->next->child) { 1050 term_flushln(p); 1051 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG); 1052 } else if (MDOC_BODY == n->type && n->child) { 1053 term_flushln(p); 1054 p->flags &= ~TERMP_NOLPAD; 1055 } 1056 } 1057 1058 1059 /* ARGSUSED */ 1060 static int 1061 termp_fl_pre(DECL_ARGS) 1062 { 1063 1064 term_fontpush(p, TERMFONT_BOLD); 1065 term_word(p, "\\-"); 1066 1067 if (n->child) 1068 p->flags |= TERMP_NOSPACE; 1069 else if (n->next && n->next->line == n->line) 1070 p->flags |= TERMP_NOSPACE; 1071 1072 return(1); 1073 } 1074 1075 1076 /* ARGSUSED */ 1077 static int 1078 termp__a_pre(DECL_ARGS) 1079 { 1080 1081 if (n->prev && MDOC__A == n->prev->tok) 1082 if (NULL == n->next || MDOC__A != n->next->tok) 1083 term_word(p, "and"); 1084 1085 return(1); 1086 } 1087 1088 1089 /* ARGSUSED */ 1090 static int 1091 termp_an_pre(DECL_ARGS) 1092 { 1093 1094 if (NULL == n->child) 1095 return(1); 1096 1097 /* 1098 * If not in the AUTHORS section, `An -split' will cause 1099 * newlines to occur before the author name. If in the AUTHORS 1100 * section, by default, the first `An' invocation is nosplit, 1101 * then all subsequent ones, regardless of whether interspersed 1102 * with other macros/text, are split. -split, in this case, 1103 * will override the condition of the implied first -nosplit. 1104 */ 1105 1106 if (n->sec == SEC_AUTHORS) { 1107 if ( ! (TERMP_ANPREC & p->flags)) { 1108 if (TERMP_SPLIT & p->flags) 1109 term_newln(p); 1110 return(1); 1111 } 1112 if (TERMP_NOSPLIT & p->flags) 1113 return(1); 1114 term_newln(p); 1115 return(1); 1116 } 1117 1118 if (TERMP_SPLIT & p->flags) 1119 term_newln(p); 1120 1121 return(1); 1122 } 1123 1124 1125 /* ARGSUSED */ 1126 static void 1127 termp_an_post(DECL_ARGS) 1128 { 1129 1130 if (n->child) { 1131 if (SEC_AUTHORS == n->sec) 1132 p->flags |= TERMP_ANPREC; 1133 return; 1134 } 1135 1136 if (AUTH_split == n->norm->An.auth) { 1137 p->flags &= ~TERMP_NOSPLIT; 1138 p->flags |= TERMP_SPLIT; 1139 } else if (AUTH_nosplit == n->norm->An.auth) { 1140 p->flags &= ~TERMP_SPLIT; 1141 p->flags |= TERMP_NOSPLIT; 1142 } 1143 1144 } 1145 1146 1147 /* ARGSUSED */ 1148 static int 1149 termp_ns_pre(DECL_ARGS) 1150 { 1151 1152 p->flags |= TERMP_NOSPACE; 1153 return(1); 1154 } 1155 1156 1157 /* ARGSUSED */ 1158 static int 1159 termp_rs_pre(DECL_ARGS) 1160 { 1161 1162 if (SEC_SEE_ALSO != n->sec) 1163 return(1); 1164 if (MDOC_BLOCK == n->type && n->prev) 1165 term_vspace(p); 1166 return(1); 1167 } 1168 1169 1170 /* ARGSUSED */ 1171 static int 1172 termp_rv_pre(DECL_ARGS) 1173 { 1174 const struct mdoc_node *nn; 1175 1176 term_newln(p); 1177 term_word(p, "The"); 1178 1179 for (nn = n->child; nn; nn = nn->next) { 1180 term_fontpush(p, TERMFONT_BOLD); 1181 term_word(p, nn->string); 1182 term_fontpop(p); 1183 p->flags |= TERMP_NOSPACE; 1184 if (nn->next && NULL == nn->next->next) 1185 term_word(p, "(), and"); 1186 else if (nn->next) 1187 term_word(p, "(),"); 1188 else 1189 term_word(p, "()"); 1190 } 1191 1192 if (n->child && n->child->next) 1193 term_word(p, "functions return"); 1194 else 1195 term_word(p, "function returns"); 1196 1197 term_word(p, "the value 0 if successful; otherwise the value " 1198 "-1 is returned and the global variable"); 1199 1200 term_fontpush(p, TERMFONT_UNDER); 1201 term_word(p, "errno"); 1202 term_fontpop(p); 1203 1204 term_word(p, "is set to indicate the error."); 1205 p->flags |= TERMP_SENTENCE; 1206 1207 return(0); 1208 } 1209 1210 1211 /* ARGSUSED */ 1212 static int 1213 termp_ex_pre(DECL_ARGS) 1214 { 1215 const struct mdoc_node *nn; 1216 1217 term_word(p, "The"); 1218 1219 for (nn = n->child; nn; nn = nn->next) { 1220 term_fontpush(p, TERMFONT_BOLD); 1221 term_word(p, nn->string); 1222 term_fontpop(p); 1223 p->flags |= TERMP_NOSPACE; 1224 if (nn->next && NULL == nn->next->next) 1225 term_word(p, ", and"); 1226 else if (nn->next) 1227 term_word(p, ","); 1228 else 1229 p->flags &= ~TERMP_NOSPACE; 1230 } 1231 1232 if (n->child && n->child->next) 1233 term_word(p, "utilities exit"); 1234 else 1235 term_word(p, "utility exits"); 1236 1237 term_word(p, "0 on success, and >0 if an error occurs."); 1238 p->flags |= TERMP_SENTENCE; 1239 1240 return(0); 1241 } 1242 1243 1244 /* ARGSUSED */ 1245 static int 1246 termp_nd_pre(DECL_ARGS) 1247 { 1248 1249 if (MDOC_BODY != n->type) 1250 return(1); 1251 1252 #if defined(__OpenBSD__) || defined(__linux__) 1253 term_word(p, "\\(en"); 1254 #else 1255 term_word(p, "\\(em"); 1256 #endif 1257 return(1); 1258 } 1259 1260 1261 /* ARGSUSED */ 1262 static int 1263 termp_bl_pre(DECL_ARGS) 1264 { 1265 1266 return(MDOC_HEAD != n->type); 1267 } 1268 1269 1270 /* ARGSUSED */ 1271 static void 1272 termp_bl_post(DECL_ARGS) 1273 { 1274 1275 if (MDOC_BLOCK == n->type) 1276 term_newln(p); 1277 } 1278 1279 1280 /* ARGSUSED */ 1281 static int 1282 termp_xr_pre(DECL_ARGS) 1283 { 1284 const struct mdoc_node *nn; 1285 1286 if (NULL == n->child) 1287 return(0); 1288 1289 assert(MDOC_TEXT == n->child->type); 1290 nn = n->child; 1291 1292 term_word(p, nn->string); 1293 if (NULL == (nn = nn->next)) 1294 return(0); 1295 p->flags |= TERMP_NOSPACE; 1296 term_word(p, "("); 1297 term_word(p, nn->string); 1298 term_word(p, ")"); 1299 1300 return(0); 1301 } 1302 1303 1304 /* 1305 * This decides how to assert whitespace before any of the SYNOPSIS set 1306 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain 1307 * macro combos). 1308 */ 1309 static void 1310 synopsis_pre(struct termp *p, const struct mdoc_node *n) 1311 { 1312 /* 1313 * Obviously, if we're not in a SYNOPSIS or no prior macros 1314 * exist, do nothing. 1315 */ 1316 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 1317 return; 1318 1319 /* 1320 * If we're the second in a pair of like elements, emit our 1321 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which 1322 * case we soldier on. 1323 */ 1324 if (n->prev->tok == n->tok && 1325 MDOC_Ft != n->tok && 1326 MDOC_Fo != n->tok && 1327 MDOC_Fn != n->tok) { 1328 term_newln(p); 1329 return; 1330 } 1331 1332 /* 1333 * If we're one of the SYNOPSIS set and non-like pair-wise after 1334 * another (or Fn/Fo, which we've let slip through) then assert 1335 * vertical space, else only newline and move on. 1336 */ 1337 switch (n->prev->tok) { 1338 case (MDOC_Fd): 1339 /* FALLTHROUGH */ 1340 case (MDOC_Fn): 1341 /* FALLTHROUGH */ 1342 case (MDOC_Fo): 1343 /* FALLTHROUGH */ 1344 case (MDOC_In): 1345 /* FALLTHROUGH */ 1346 case (MDOC_Vt): 1347 term_vspace(p); 1348 break; 1349 case (MDOC_Ft): 1350 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 1351 term_vspace(p); 1352 break; 1353 } 1354 /* FALLTHROUGH */ 1355 default: 1356 term_newln(p); 1357 break; 1358 } 1359 } 1360 1361 1362 static int 1363 termp_vt_pre(DECL_ARGS) 1364 { 1365 1366 if (MDOC_ELEM == n->type) { 1367 synopsis_pre(p, n); 1368 return(termp_under_pre(p, pair, m, n)); 1369 } else if (MDOC_BLOCK == n->type) { 1370 synopsis_pre(p, n); 1371 return(1); 1372 } else if (MDOC_HEAD == n->type) 1373 return(0); 1374 1375 return(termp_under_pre(p, pair, m, n)); 1376 } 1377 1378 1379 /* ARGSUSED */ 1380 static int 1381 termp_bold_pre(DECL_ARGS) 1382 { 1383 1384 term_fontpush(p, TERMFONT_BOLD); 1385 return(1); 1386 } 1387 1388 1389 /* ARGSUSED */ 1390 static int 1391 termp_fd_pre(DECL_ARGS) 1392 { 1393 1394 synopsis_pre(p, n); 1395 return(termp_bold_pre(p, pair, m, n)); 1396 } 1397 1398 1399 /* ARGSUSED */ 1400 static int 1401 termp_sh_pre(DECL_ARGS) 1402 { 1403 1404 /* No vspace between consecutive `Sh' calls. */ 1405 1406 switch (n->type) { 1407 case (MDOC_BLOCK): 1408 if (n->prev && MDOC_Sh == n->prev->tok) 1409 if (NULL == n->prev->body->child) 1410 break; 1411 term_vspace(p); 1412 break; 1413 case (MDOC_HEAD): 1414 term_fontpush(p, TERMFONT_BOLD); 1415 break; 1416 case (MDOC_BODY): 1417 p->offset = term_len(p, INDENT); 1418 break; 1419 default: 1420 break; 1421 } 1422 return(1); 1423 } 1424 1425 1426 /* ARGSUSED */ 1427 static void 1428 termp_sh_post(DECL_ARGS) 1429 { 1430 1431 switch (n->type) { 1432 case (MDOC_HEAD): 1433 term_newln(p); 1434 break; 1435 case (MDOC_BODY): 1436 term_newln(p); 1437 p->offset = 0; 1438 break; 1439 default: 1440 break; 1441 } 1442 } 1443 1444 1445 /* ARGSUSED */ 1446 static int 1447 termp_bt_pre(DECL_ARGS) 1448 { 1449 1450 term_word(p, "is currently in beta test."); 1451 p->flags |= TERMP_SENTENCE; 1452 return(0); 1453 } 1454 1455 1456 /* ARGSUSED */ 1457 static void 1458 termp_lb_post(DECL_ARGS) 1459 { 1460 1461 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags) 1462 term_newln(p); 1463 } 1464 1465 1466 /* ARGSUSED */ 1467 static int 1468 termp_ud_pre(DECL_ARGS) 1469 { 1470 1471 term_word(p, "currently under development."); 1472 p->flags |= TERMP_SENTENCE; 1473 return(0); 1474 } 1475 1476 1477 /* ARGSUSED */ 1478 static int 1479 termp_d1_pre(DECL_ARGS) 1480 { 1481 1482 if (MDOC_BLOCK != n->type) 1483 return(1); 1484 term_newln(p); 1485 p->offset += term_len(p, (INDENT + 1)); 1486 return(1); 1487 } 1488 1489 1490 /* ARGSUSED */ 1491 static void 1492 termp_d1_post(DECL_ARGS) 1493 { 1494 1495 if (MDOC_BLOCK != n->type) 1496 return; 1497 term_newln(p); 1498 } 1499 1500 1501 /* ARGSUSED */ 1502 static int 1503 termp_ft_pre(DECL_ARGS) 1504 { 1505 1506 /* NB: MDOC_LINE does not effect this! */ 1507 synopsis_pre(p, n); 1508 term_fontpush(p, TERMFONT_UNDER); 1509 return(1); 1510 } 1511 1512 1513 /* ARGSUSED */ 1514 static int 1515 termp_fn_pre(DECL_ARGS) 1516 { 1517 const struct mdoc_node *nn; 1518 1519 synopsis_pre(p, n); 1520 1521 term_fontpush(p, TERMFONT_BOLD); 1522 term_word(p, n->child->string); 1523 term_fontpop(p); 1524 1525 p->flags |= TERMP_NOSPACE; 1526 term_word(p, "("); 1527 1528 for (nn = n->child->next; nn; nn = nn->next) { 1529 term_fontpush(p, TERMFONT_UNDER); 1530 term_word(p, nn->string); 1531 term_fontpop(p); 1532 1533 if (nn->next) 1534 term_word(p, ","); 1535 } 1536 1537 term_word(p, ")"); 1538 1539 if (MDOC_SYNPRETTY & n->flags) 1540 term_word(p, ";"); 1541 1542 return(0); 1543 } 1544 1545 1546 /* ARGSUSED */ 1547 static int 1548 termp_fa_pre(DECL_ARGS) 1549 { 1550 const struct mdoc_node *nn; 1551 1552 if (n->parent->tok != MDOC_Fo) { 1553 term_fontpush(p, TERMFONT_UNDER); 1554 return(1); 1555 } 1556 1557 for (nn = n->child; nn; nn = nn->next) { 1558 term_fontpush(p, TERMFONT_UNDER); 1559 term_word(p, nn->string); 1560 term_fontpop(p); 1561 1562 if (nn->next) 1563 term_word(p, ","); 1564 } 1565 1566 if (n->child && n->next && n->next->tok == MDOC_Fa) 1567 term_word(p, ","); 1568 1569 return(0); 1570 } 1571 1572 1573 /* ARGSUSED */ 1574 static int 1575 termp_bd_pre(DECL_ARGS) 1576 { 1577 size_t tabwidth, rm, rmax; 1578 const struct mdoc_node *nn; 1579 1580 if (MDOC_BLOCK == n->type) { 1581 print_bvspace(p, n, n); 1582 return(1); 1583 } else if (MDOC_HEAD == n->type) 1584 return(0); 1585 1586 if (n->norm->Bd.offs) 1587 p->offset += a2offs(p, n->norm->Bd.offs); 1588 1589 /* 1590 * If -ragged or -filled are specified, the block does nothing 1591 * but change the indentation. If -unfilled or -literal are 1592 * specified, text is printed exactly as entered in the display: 1593 * for macro lines, a newline is appended to the line. Blank 1594 * lines are allowed. 1595 */ 1596 1597 if (DISP_literal != n->norm->Bd.type && 1598 DISP_unfilled != n->norm->Bd.type) 1599 return(1); 1600 1601 tabwidth = p->tabwidth; 1602 if (DISP_literal == n->norm->Bd.type) 1603 p->tabwidth = term_len(p, 8); 1604 1605 rm = p->rmargin; 1606 rmax = p->maxrmargin; 1607 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1608 1609 for (nn = n->child; nn; nn = nn->next) { 1610 print_mdoc_node(p, pair, m, nn); 1611 /* 1612 * If the printed node flushes its own line, then we 1613 * needn't do it here as well. This is hacky, but the 1614 * notion of selective eoln whitespace is pretty dumb 1615 * anyway, so don't sweat it. 1616 */ 1617 switch (nn->tok) { 1618 case (MDOC_Sm): 1619 /* FALLTHROUGH */ 1620 case (MDOC_br): 1621 /* FALLTHROUGH */ 1622 case (MDOC_sp): 1623 /* FALLTHROUGH */ 1624 case (MDOC_Bl): 1625 /* FALLTHROUGH */ 1626 case (MDOC_D1): 1627 /* FALLTHROUGH */ 1628 case (MDOC_Dl): 1629 /* FALLTHROUGH */ 1630 case (MDOC_Lp): 1631 /* FALLTHROUGH */ 1632 case (MDOC_Pp): 1633 continue; 1634 default: 1635 break; 1636 } 1637 if (nn->next && nn->next->line == nn->line) 1638 continue; 1639 term_flushln(p); 1640 p->flags |= TERMP_NOSPACE; 1641 } 1642 1643 p->tabwidth = tabwidth; 1644 p->rmargin = rm; 1645 p->maxrmargin = rmax; 1646 return(0); 1647 } 1648 1649 1650 /* ARGSUSED */ 1651 static void 1652 termp_bd_post(DECL_ARGS) 1653 { 1654 size_t rm, rmax; 1655 1656 if (MDOC_BODY != n->type) 1657 return; 1658 1659 rm = p->rmargin; 1660 rmax = p->maxrmargin; 1661 1662 if (DISP_literal == n->norm->Bd.type || 1663 DISP_unfilled == n->norm->Bd.type) 1664 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1665 1666 p->flags |= TERMP_NOSPACE; 1667 term_newln(p); 1668 1669 p->rmargin = rm; 1670 p->maxrmargin = rmax; 1671 } 1672 1673 1674 /* ARGSUSED */ 1675 static void 1676 termp_bx_post(DECL_ARGS) 1677 { 1678 1679 if (n->child) 1680 p->flags |= TERMP_NOSPACE; 1681 term_word(p, "BSD"); 1682 } 1683 1684 1685 /* ARGSUSED */ 1686 static int 1687 termp_xx_pre(DECL_ARGS) 1688 { 1689 const char *pp; 1690 1691 pp = NULL; 1692 switch (n->tok) { 1693 case (MDOC_Bsx): 1694 pp = "BSD/OS"; 1695 break; 1696 case (MDOC_Dx): 1697 pp = "DragonFly"; 1698 break; 1699 case (MDOC_Fx): 1700 pp = "FreeBSD"; 1701 break; 1702 case (MDOC_Nx): 1703 pp = "NetBSD"; 1704 break; 1705 case (MDOC_Ox): 1706 pp = "OpenBSD"; 1707 break; 1708 case (MDOC_Ux): 1709 pp = "UNIX"; 1710 break; 1711 default: 1712 break; 1713 } 1714 1715 assert(pp); 1716 term_word(p, pp); 1717 return(1); 1718 } 1719 1720 1721 /* ARGSUSED */ 1722 static int 1723 termp_igndelim_pre(DECL_ARGS) 1724 { 1725 1726 p->flags |= TERMP_IGNDELIM; 1727 return(1); 1728 } 1729 1730 1731 /* ARGSUSED */ 1732 static void 1733 termp_pf_post(DECL_ARGS) 1734 { 1735 1736 p->flags |= TERMP_NOSPACE; 1737 } 1738 1739 1740 /* ARGSUSED */ 1741 static int 1742 termp_ss_pre(DECL_ARGS) 1743 { 1744 1745 switch (n->type) { 1746 case (MDOC_BLOCK): 1747 term_newln(p); 1748 if (n->prev) 1749 term_vspace(p); 1750 break; 1751 case (MDOC_HEAD): 1752 term_fontpush(p, TERMFONT_BOLD); 1753 p->offset = term_len(p, HALFINDENT); 1754 break; 1755 default: 1756 break; 1757 } 1758 1759 return(1); 1760 } 1761 1762 1763 /* ARGSUSED */ 1764 static void 1765 termp_ss_post(DECL_ARGS) 1766 { 1767 1768 if (MDOC_HEAD == n->type) 1769 term_newln(p); 1770 } 1771 1772 1773 /* ARGSUSED */ 1774 static int 1775 termp_cd_pre(DECL_ARGS) 1776 { 1777 1778 synopsis_pre(p, n); 1779 term_fontpush(p, TERMFONT_BOLD); 1780 return(1); 1781 } 1782 1783 1784 /* ARGSUSED */ 1785 static int 1786 termp_in_pre(DECL_ARGS) 1787 { 1788 1789 synopsis_pre(p, n); 1790 1791 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) { 1792 term_fontpush(p, TERMFONT_BOLD); 1793 term_word(p, "#include"); 1794 term_word(p, "<"); 1795 } else { 1796 term_word(p, "<"); 1797 term_fontpush(p, TERMFONT_UNDER); 1798 } 1799 1800 p->flags |= TERMP_NOSPACE; 1801 return(1); 1802 } 1803 1804 1805 /* ARGSUSED */ 1806 static void 1807 termp_in_post(DECL_ARGS) 1808 { 1809 1810 if (MDOC_SYNPRETTY & n->flags) 1811 term_fontpush(p, TERMFONT_BOLD); 1812 1813 p->flags |= TERMP_NOSPACE; 1814 term_word(p, ">"); 1815 1816 if (MDOC_SYNPRETTY & n->flags) 1817 term_fontpop(p); 1818 } 1819 1820 1821 /* ARGSUSED */ 1822 static int 1823 termp_sp_pre(DECL_ARGS) 1824 { 1825 size_t i, len; 1826 1827 switch (n->tok) { 1828 case (MDOC_sp): 1829 len = n->child ? a2height(p, n->child->string) : 1; 1830 break; 1831 case (MDOC_br): 1832 len = 0; 1833 break; 1834 default: 1835 len = 1; 1836 break; 1837 } 1838 1839 if (0 == len) 1840 term_newln(p); 1841 for (i = 0; i < len; i++) 1842 term_vspace(p); 1843 1844 return(0); 1845 } 1846 1847 1848 /* ARGSUSED */ 1849 static int 1850 termp_quote_pre(DECL_ARGS) 1851 { 1852 1853 if (MDOC_BODY != n->type && MDOC_ELEM != n->type) 1854 return(1); 1855 1856 switch (n->tok) { 1857 case (MDOC_Ao): 1858 /* FALLTHROUGH */ 1859 case (MDOC_Aq): 1860 term_word(p, "<"); 1861 break; 1862 case (MDOC_Bro): 1863 /* FALLTHROUGH */ 1864 case (MDOC_Brq): 1865 term_word(p, "{"); 1866 break; 1867 case (MDOC_Oo): 1868 /* FALLTHROUGH */ 1869 case (MDOC_Op): 1870 /* FALLTHROUGH */ 1871 case (MDOC_Bo): 1872 /* FALLTHROUGH */ 1873 case (MDOC_Bq): 1874 term_word(p, "["); 1875 break; 1876 case (MDOC_Do): 1877 /* FALLTHROUGH */ 1878 case (MDOC_Dq): 1879 term_word(p, "``"); 1880 break; 1881 case (MDOC_Po): 1882 /* FALLTHROUGH */ 1883 case (MDOC_Pq): 1884 term_word(p, "("); 1885 break; 1886 case (MDOC__T): 1887 /* FALLTHROUGH */ 1888 case (MDOC_Qo): 1889 /* FALLTHROUGH */ 1890 case (MDOC_Qq): 1891 term_word(p, "\""); 1892 break; 1893 case (MDOC_Ql): 1894 /* FALLTHROUGH */ 1895 case (MDOC_So): 1896 /* FALLTHROUGH */ 1897 case (MDOC_Sq): 1898 term_word(p, "`"); 1899 break; 1900 default: 1901 abort(); 1902 /* NOTREACHED */ 1903 } 1904 1905 p->flags |= TERMP_NOSPACE; 1906 return(1); 1907 } 1908 1909 1910 /* ARGSUSED */ 1911 static void 1912 termp_quote_post(DECL_ARGS) 1913 { 1914 1915 if (MDOC_BODY != n->type && MDOC_ELEM != n->type) 1916 return; 1917 1918 p->flags |= TERMP_NOSPACE; 1919 1920 switch (n->tok) { 1921 case (MDOC_Ao): 1922 /* FALLTHROUGH */ 1923 case (MDOC_Aq): 1924 term_word(p, ">"); 1925 break; 1926 case (MDOC_Bro): 1927 /* FALLTHROUGH */ 1928 case (MDOC_Brq): 1929 term_word(p, "}"); 1930 break; 1931 case (MDOC_Oo): 1932 /* FALLTHROUGH */ 1933 case (MDOC_Op): 1934 /* FALLTHROUGH */ 1935 case (MDOC_Bo): 1936 /* FALLTHROUGH */ 1937 case (MDOC_Bq): 1938 term_word(p, "]"); 1939 break; 1940 case (MDOC_Do): 1941 /* FALLTHROUGH */ 1942 case (MDOC_Dq): 1943 term_word(p, "''"); 1944 break; 1945 case (MDOC_Po): 1946 /* FALLTHROUGH */ 1947 case (MDOC_Pq): 1948 term_word(p, ")"); 1949 break; 1950 case (MDOC__T): 1951 /* FALLTHROUGH */ 1952 case (MDOC_Qo): 1953 /* FALLTHROUGH */ 1954 case (MDOC_Qq): 1955 term_word(p, "\""); 1956 break; 1957 case (MDOC_Ql): 1958 /* FALLTHROUGH */ 1959 case (MDOC_So): 1960 /* FALLTHROUGH */ 1961 case (MDOC_Sq): 1962 term_word(p, "'"); 1963 break; 1964 default: 1965 abort(); 1966 /* NOTREACHED */ 1967 } 1968 } 1969 1970 1971 /* ARGSUSED */ 1972 static int 1973 termp_fo_pre(DECL_ARGS) 1974 { 1975 1976 if (MDOC_BLOCK == n->type) { 1977 synopsis_pre(p, n); 1978 return(1); 1979 } else if (MDOC_BODY == n->type) { 1980 p->flags |= TERMP_NOSPACE; 1981 term_word(p, "("); 1982 return(1); 1983 } 1984 1985 if (NULL == n->child) 1986 return(0); 1987 1988 /* XXX: we drop non-initial arguments as per groff. */ 1989 1990 assert(n->child->string); 1991 term_fontpush(p, TERMFONT_BOLD); 1992 term_word(p, n->child->string); 1993 return(0); 1994 } 1995 1996 1997 /* ARGSUSED */ 1998 static void 1999 termp_fo_post(DECL_ARGS) 2000 { 2001 2002 if (MDOC_BODY != n->type) 2003 return; 2004 2005 term_word(p, ")"); 2006 2007 if (MDOC_SYNPRETTY & n->flags) 2008 term_word(p, ";"); 2009 } 2010 2011 2012 /* ARGSUSED */ 2013 static int 2014 termp_bf_pre(DECL_ARGS) 2015 { 2016 2017 if (MDOC_HEAD == n->type) 2018 return(0); 2019 else if (MDOC_BLOCK != n->type) 2020 return(1); 2021 2022 if (FONT_Em == n->norm->Bf.font) 2023 term_fontpush(p, TERMFONT_UNDER); 2024 else if (FONT_Sy == n->norm->Bf.font) 2025 term_fontpush(p, TERMFONT_BOLD); 2026 else 2027 term_fontpush(p, TERMFONT_NONE); 2028 2029 return(1); 2030 } 2031 2032 2033 /* ARGSUSED */ 2034 static int 2035 termp_sm_pre(DECL_ARGS) 2036 { 2037 2038 assert(n->child && MDOC_TEXT == n->child->type); 2039 if (0 == strcmp("on", n->child->string)) { 2040 if (p->col) 2041 p->flags &= ~TERMP_NOSPACE; 2042 p->flags &= ~TERMP_NONOSPACE; 2043 } else 2044 p->flags |= TERMP_NONOSPACE; 2045 2046 return(0); 2047 } 2048 2049 2050 /* ARGSUSED */ 2051 static int 2052 termp_ap_pre(DECL_ARGS) 2053 { 2054 2055 p->flags |= TERMP_NOSPACE; 2056 term_word(p, "'"); 2057 p->flags |= TERMP_NOSPACE; 2058 return(1); 2059 } 2060 2061 2062 /* ARGSUSED */ 2063 static void 2064 termp____post(DECL_ARGS) 2065 { 2066 2067 /* 2068 * Handle lists of authors. In general, print each followed by 2069 * a comma. Don't print the comma if there are only two 2070 * authors. 2071 */ 2072 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 2073 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 2074 if (NULL == n->prev || MDOC__A != n->prev->tok) 2075 return; 2076 2077 /* TODO: %U. */ 2078 2079 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 2080 return; 2081 2082 if (NULL == n->next) { 2083 term_word(p, "."); 2084 p->flags |= TERMP_SENTENCE; 2085 } else 2086 term_word(p, ","); 2087 } 2088 2089 2090 /* ARGSUSED */ 2091 static int 2092 termp_li_pre(DECL_ARGS) 2093 { 2094 2095 term_fontpush(p, TERMFONT_NONE); 2096 return(1); 2097 } 2098 2099 2100 /* ARGSUSED */ 2101 static int 2102 termp_lk_pre(DECL_ARGS) 2103 { 2104 const struct mdoc_node *nn, *sv; 2105 2106 term_fontpush(p, TERMFONT_UNDER); 2107 2108 nn = sv = n->child; 2109 2110 if (NULL == nn || NULL == nn->next) 2111 return(1); 2112 2113 for (nn = nn->next; nn; nn = nn->next) 2114 term_word(p, nn->string); 2115 2116 term_fontpop(p); 2117 2118 term_word(p, ":"); 2119 2120 term_fontpush(p, TERMFONT_BOLD); 2121 term_word(p, sv->string); 2122 term_fontpop(p); 2123 2124 return(0); 2125 } 2126 2127 2128 /* ARGSUSED */ 2129 static int 2130 termp_bk_pre(DECL_ARGS) 2131 { 2132 2133 switch (n->type) { 2134 case (MDOC_BLOCK): 2135 break; 2136 case (MDOC_HEAD): 2137 return(0); 2138 case (MDOC_BODY): 2139 if (n->parent->args || 0 == n->prev->nchild) 2140 p->flags |= TERMP_PREKEEP; 2141 break; 2142 default: 2143 abort(); 2144 /* NOTREACHED */ 2145 } 2146 2147 return(1); 2148 } 2149 2150 2151 /* ARGSUSED */ 2152 static void 2153 termp_bk_post(DECL_ARGS) 2154 { 2155 2156 if (MDOC_BODY == n->type) 2157 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 2158 } 2159 2160 /* ARGSUSED */ 2161 static void 2162 termp__t_post(DECL_ARGS) 2163 { 2164 2165 /* 2166 * If we're in an `Rs' and there's a journal present, then quote 2167 * us instead of underlining us (for disambiguation). 2168 */ 2169 if (n->parent && MDOC_Rs == n->parent->tok && 2170 n->parent->norm->Rs.child_J) 2171 termp_quote_post(p, pair, m, n); 2172 2173 termp____post(p, pair, m, n); 2174 } 2175 2176 /* ARGSUSED */ 2177 static int 2178 termp__t_pre(DECL_ARGS) 2179 { 2180 2181 /* 2182 * If we're in an `Rs' and there's a journal present, then quote 2183 * us instead of underlining us (for disambiguation). 2184 */ 2185 if (n->parent && MDOC_Rs == n->parent->tok && 2186 n->parent->norm->Rs.child_J) 2187 return(termp_quote_pre(p, pair, m, n)); 2188 2189 term_fontpush(p, TERMFONT_UNDER); 2190 return(1); 2191 } 2192 2193 /* ARGSUSED */ 2194 static int 2195 termp_under_pre(DECL_ARGS) 2196 { 2197 2198 term_fontpush(p, TERMFONT_UNDER); 2199 return(1); 2200 } 2201