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