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