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