1 /* $Id: mdoc_term.c,v 1.145 2012/07/11 16:55:29 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2012 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 #include <sys/types.h> 19 20 #include <assert.h> 21 #include <ctype.h> 22 #include <stdint.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include "mandoc.h" 28 #include "out.h" 29 #include "term.h" 30 #include "mdoc.h" 31 #include "main.h" 32 33 struct termpair { 34 struct termpair *ppair; 35 int count; 36 }; 37 38 #define DECL_ARGS struct termp *p, \ 39 struct termpair *pair, \ 40 const struct mdoc_meta *m, \ 41 const struct mdoc_node *n 42 43 struct termact { 44 int (*pre)(DECL_ARGS); 45 void (*post)(DECL_ARGS); 46 }; 47 48 static size_t a2width(const struct termp *, const char *); 49 static size_t a2height(const struct termp *, const char *); 50 static size_t a2offs(const struct termp *, const char *); 51 52 static void print_bvspace(struct termp *, 53 const struct mdoc_node *, 54 const struct mdoc_node *); 55 static void print_mdoc_node(DECL_ARGS); 56 static void print_mdoc_nodelist(DECL_ARGS); 57 static void print_mdoc_head(struct termp *, const void *); 58 static void print_mdoc_foot(struct termp *, const void *); 59 static void synopsis_pre(struct termp *, 60 const struct mdoc_node *); 61 62 static void termp____post(DECL_ARGS); 63 static void termp__t_post(DECL_ARGS); 64 static void termp_an_post(DECL_ARGS); 65 static void termp_bd_post(DECL_ARGS); 66 static void termp_bk_post(DECL_ARGS); 67 static void termp_bl_post(DECL_ARGS); 68 static void termp_fd_post(DECL_ARGS); 69 static void termp_fo_post(DECL_ARGS); 70 static void termp_in_post(DECL_ARGS); 71 static void termp_it_post(DECL_ARGS); 72 static void termp_lb_post(DECL_ARGS); 73 static void termp_nm_post(DECL_ARGS); 74 static void termp_pf_post(DECL_ARGS); 75 static void termp_quote_post(DECL_ARGS); 76 static void termp_sh_post(DECL_ARGS); 77 static void termp_ss_post(DECL_ARGS); 78 79 static int termp__a_pre(DECL_ARGS); 80 static int termp__t_pre(DECL_ARGS); 81 static int termp_an_pre(DECL_ARGS); 82 static int termp_ap_pre(DECL_ARGS); 83 static int termp_bd_pre(DECL_ARGS); 84 static int termp_bf_pre(DECL_ARGS); 85 static int termp_bk_pre(DECL_ARGS); 86 static int termp_bl_pre(DECL_ARGS); 87 static int termp_bold_pre(DECL_ARGS); 88 static int termp_bt_pre(DECL_ARGS); 89 static int termp_bx_pre(DECL_ARGS); 90 static int termp_cd_pre(DECL_ARGS); 91 static int termp_d1_pre(DECL_ARGS); 92 static int termp_ex_pre(DECL_ARGS); 93 static int termp_fa_pre(DECL_ARGS); 94 static int termp_fd_pre(DECL_ARGS); 95 static int termp_fl_pre(DECL_ARGS); 96 static int termp_fn_pre(DECL_ARGS); 97 static int termp_fo_pre(DECL_ARGS); 98 static int termp_ft_pre(DECL_ARGS); 99 static int termp_igndelim_pre(DECL_ARGS); 100 static int termp_in_pre(DECL_ARGS); 101 static int termp_it_pre(DECL_ARGS); 102 static int termp_li_pre(DECL_ARGS); 103 static int termp_lk_pre(DECL_ARGS); 104 static int termp_nd_pre(DECL_ARGS); 105 static int termp_nm_pre(DECL_ARGS); 106 static int termp_ns_pre(DECL_ARGS); 107 static int termp_quote_pre(DECL_ARGS); 108 static int termp_rs_pre(DECL_ARGS); 109 static int termp_rv_pre(DECL_ARGS); 110 static int termp_sh_pre(DECL_ARGS); 111 static int termp_sm_pre(DECL_ARGS); 112 static int termp_sp_pre(DECL_ARGS); 113 static int termp_ss_pre(DECL_ARGS); 114 static int termp_under_pre(DECL_ARGS); 115 static int termp_ud_pre(DECL_ARGS); 116 static int termp_vt_pre(DECL_ARGS); 117 static int termp_xr_pre(DECL_ARGS); 118 static int termp_xx_pre(DECL_ARGS); 119 120 static const struct termact termacts[MDOC_MAX] = { 121 { termp_ap_pre, NULL }, /* Ap */ 122 { NULL, NULL }, /* Dd */ 123 { NULL, NULL }, /* Dt */ 124 { NULL, NULL }, /* Os */ 125 { termp_sh_pre, termp_sh_post }, /* Sh */ 126 { termp_ss_pre, termp_ss_post }, /* Ss */ 127 { termp_sp_pre, NULL }, /* Pp */ 128 { termp_d1_pre, termp_bl_post }, /* D1 */ 129 { termp_d1_pre, termp_bl_post }, /* Dl */ 130 { termp_bd_pre, termp_bd_post }, /* Bd */ 131 { NULL, NULL }, /* Ed */ 132 { termp_bl_pre, termp_bl_post }, /* Bl */ 133 { NULL, NULL }, /* El */ 134 { termp_it_pre, termp_it_post }, /* It */ 135 { termp_under_pre, NULL }, /* Ad */ 136 { termp_an_pre, termp_an_post }, /* An */ 137 { termp_under_pre, NULL }, /* Ar */ 138 { termp_cd_pre, NULL }, /* Cd */ 139 { termp_bold_pre, NULL }, /* Cm */ 140 { NULL, NULL }, /* Dv */ 141 { NULL, NULL }, /* Er */ 142 { NULL, NULL }, /* Ev */ 143 { termp_ex_pre, NULL }, /* Ex */ 144 { termp_fa_pre, NULL }, /* Fa */ 145 { termp_fd_pre, termp_fd_post }, /* Fd */ 146 { termp_fl_pre, NULL }, /* Fl */ 147 { termp_fn_pre, NULL }, /* Fn */ 148 { termp_ft_pre, NULL }, /* Ft */ 149 { termp_bold_pre, NULL }, /* Ic */ 150 { termp_in_pre, termp_in_post }, /* In */ 151 { termp_li_pre, NULL }, /* Li */ 152 { termp_nd_pre, NULL }, /* Nd */ 153 { termp_nm_pre, termp_nm_post }, /* Nm */ 154 { termp_quote_pre, termp_quote_post }, /* Op */ 155 { NULL, NULL }, /* Ot */ 156 { termp_under_pre, NULL }, /* Pa */ 157 { termp_rv_pre, NULL }, /* Rv */ 158 { NULL, NULL }, /* St */ 159 { termp_under_pre, NULL }, /* Va */ 160 { termp_vt_pre, NULL }, /* Vt */ 161 { termp_xr_pre, NULL }, /* Xr */ 162 { termp__a_pre, termp____post }, /* %A */ 163 { termp_under_pre, termp____post }, /* %B */ 164 { NULL, termp____post }, /* %D */ 165 { termp_under_pre, termp____post }, /* %I */ 166 { termp_under_pre, termp____post }, /* %J */ 167 { NULL, termp____post }, /* %N */ 168 { NULL, termp____post }, /* %O */ 169 { NULL, termp____post }, /* %P */ 170 { NULL, termp____post }, /* %R */ 171 { termp__t_pre, termp__t_post }, /* %T */ 172 { NULL, termp____post }, /* %V */ 173 { NULL, NULL }, /* Ac */ 174 { termp_quote_pre, termp_quote_post }, /* Ao */ 175 { termp_quote_pre, termp_quote_post }, /* Aq */ 176 { NULL, NULL }, /* At */ 177 { NULL, NULL }, /* Bc */ 178 { termp_bf_pre, NULL }, /* Bf */ 179 { termp_quote_pre, termp_quote_post }, /* Bo */ 180 { termp_quote_pre, termp_quote_post }, /* Bq */ 181 { termp_xx_pre, NULL }, /* Bsx */ 182 { termp_bx_pre, NULL }, /* Bx */ 183 { NULL, NULL }, /* Db */ 184 { NULL, NULL }, /* Dc */ 185 { termp_quote_pre, termp_quote_post }, /* Do */ 186 { termp_quote_pre, termp_quote_post }, /* Dq */ 187 { NULL, NULL }, /* Ec */ /* FIXME: no space */ 188 { NULL, NULL }, /* Ef */ 189 { termp_under_pre, NULL }, /* Em */ 190 { termp_quote_pre, termp_quote_post }, /* Eo */ 191 { termp_xx_pre, NULL }, /* Fx */ 192 { termp_bold_pre, NULL }, /* Ms */ 193 { termp_igndelim_pre, NULL }, /* No */ 194 { termp_ns_pre, NULL }, /* Ns */ 195 { termp_xx_pre, NULL }, /* Nx */ 196 { termp_xx_pre, NULL }, /* Ox */ 197 { NULL, NULL }, /* Pc */ 198 { termp_igndelim_pre, termp_pf_post }, /* Pf */ 199 { termp_quote_pre, termp_quote_post }, /* Po */ 200 { termp_quote_pre, termp_quote_post }, /* Pq */ 201 { NULL, NULL }, /* Qc */ 202 { termp_quote_pre, termp_quote_post }, /* Ql */ 203 { termp_quote_pre, termp_quote_post }, /* Qo */ 204 { termp_quote_pre, termp_quote_post }, /* Qq */ 205 { NULL, NULL }, /* Re */ 206 { termp_rs_pre, NULL }, /* Rs */ 207 { NULL, NULL }, /* Sc */ 208 { termp_quote_pre, termp_quote_post }, /* So */ 209 { termp_quote_pre, termp_quote_post }, /* Sq */ 210 { termp_sm_pre, NULL }, /* Sm */ 211 { termp_under_pre, NULL }, /* Sx */ 212 { termp_bold_pre, NULL }, /* Sy */ 213 { NULL, NULL }, /* Tn */ 214 { termp_xx_pre, NULL }, /* Ux */ 215 { NULL, NULL }, /* Xc */ 216 { NULL, NULL }, /* Xo */ 217 { termp_fo_pre, termp_fo_post }, /* Fo */ 218 { NULL, NULL }, /* Fc */ 219 { termp_quote_pre, termp_quote_post }, /* Oo */ 220 { NULL, NULL }, /* Oc */ 221 { termp_bk_pre, termp_bk_post }, /* Bk */ 222 { NULL, NULL }, /* Ek */ 223 { termp_bt_pre, NULL }, /* Bt */ 224 { NULL, NULL }, /* Hf */ 225 { NULL, NULL }, /* Fr */ 226 { termp_ud_pre, NULL }, /* Ud */ 227 { NULL, termp_lb_post }, /* Lb */ 228 { termp_sp_pre, NULL }, /* Lp */ 229 { termp_lk_pre, NULL }, /* Lk */ 230 { termp_under_pre, NULL }, /* Mt */ 231 { termp_quote_pre, termp_quote_post }, /* Brq */ 232 { termp_quote_pre, termp_quote_post }, /* Bro */ 233 { NULL, NULL }, /* Brc */ 234 { NULL, termp____post }, /* %C */ 235 { NULL, NULL }, /* Es */ /* TODO */ 236 { NULL, NULL }, /* En */ /* TODO */ 237 { termp_xx_pre, NULL }, /* Dx */ 238 { NULL, termp____post }, /* %Q */ 239 { termp_sp_pre, NULL }, /* br */ 240 { termp_sp_pre, NULL }, /* sp */ 241 { NULL, termp____post }, /* %U */ 242 { NULL, NULL }, /* Ta */ 243 }; 244 245 246 void 247 terminal_mdoc(void *arg, const struct mdoc *mdoc) 248 { 249 const struct mdoc_node *n; 250 const struct mdoc_meta *m; 251 struct termp *p; 252 253 p = (struct termp *)arg; 254 255 if (0 == p->defindent) 256 p->defindent = 5; 257 258 p->overstep = 0; 259 p->maxrmargin = p->defrmargin; 260 p->tabwidth = term_len(p, 5); 261 262 if (NULL == p->symtab) 263 p->symtab = mchars_alloc(); 264 265 n = mdoc_node(mdoc); 266 m = mdoc_meta(mdoc); 267 268 term_begin(p, print_mdoc_head, print_mdoc_foot, m); 269 270 if (n->child) 271 print_mdoc_nodelist(p, NULL, m, n->child); 272 273 term_end(p); 274 } 275 276 277 static void 278 print_mdoc_nodelist(DECL_ARGS) 279 { 280 281 print_mdoc_node(p, pair, m, n); 282 if (n->next) 283 print_mdoc_nodelist(p, pair, m, n->next); 284 } 285 286 287 /* ARGSUSED */ 288 static void 289 print_mdoc_node(DECL_ARGS) 290 { 291 int chld; 292 const void *font; 293 struct termpair npair; 294 size_t offset, rmargin; 295 296 chld = 1; 297 offset = p->offset; 298 rmargin = p->rmargin; 299 font = term_fontq(p); 300 301 memset(&npair, 0, sizeof(struct termpair)); 302 npair.ppair = pair; 303 304 /* 305 * Keeps only work until the end of a line. If a keep was 306 * invoked in a prior line, revert it to PREKEEP. 307 * 308 * Also let SYNPRETTY sections behave as if they were wrapped 309 * in a `Bk' block. 310 */ 311 312 if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) { 313 if (n->prev && n->prev->line != n->line) { 314 p->flags &= ~TERMP_KEEP; 315 p->flags |= TERMP_PREKEEP; 316 } else if (NULL == n->prev) { 317 if (n->parent && n->parent->line != n->line) { 318 p->flags &= ~TERMP_KEEP; 319 p->flags |= TERMP_PREKEEP; 320 } 321 } 322 } 323 324 /* 325 * Since SYNPRETTY sections aren't "turned off" with `Ek', 326 * we have to intuit whether we should disable formatting. 327 */ 328 329 if ( ! (MDOC_SYNPRETTY & n->flags) && 330 ((n->prev && MDOC_SYNPRETTY & n->prev->flags) || 331 (n->parent && MDOC_SYNPRETTY & n->parent->flags))) 332 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 333 334 /* 335 * After the keep flags have been set up, we may now 336 * produce output. Note that some pre-handlers do so. 337 */ 338 339 switch (n->type) { 340 case (MDOC_TEXT): 341 if (' ' == *n->string && MDOC_LINE & n->flags) 342 term_newln(p); 343 if (MDOC_DELIMC & n->flags) 344 p->flags |= TERMP_NOSPACE; 345 term_word(p, n->string); 346 if (MDOC_DELIMO & n->flags) 347 p->flags |= TERMP_NOSPACE; 348 break; 349 case (MDOC_EQN): 350 term_eqn(p, n->eqn); 351 break; 352 case (MDOC_TBL): 353 term_tbl(p, n->span); 354 break; 355 default: 356 if (termacts[n->tok].pre && ENDBODY_NOT == n->end) 357 chld = (*termacts[n->tok].pre) 358 (p, &npair, m, n); 359 break; 360 } 361 362 if (chld && n->child) 363 print_mdoc_nodelist(p, &npair, m, n->child); 364 365 term_fontpopq(p, font); 366 367 switch (n->type) { 368 case (MDOC_TEXT): 369 break; 370 case (MDOC_TBL): 371 break; 372 case (MDOC_EQN): 373 break; 374 default: 375 if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags) 376 break; 377 (void)(*termacts[n->tok].post)(p, &npair, m, n); 378 379 /* 380 * Explicit end tokens not only call the post 381 * handler, but also tell the respective block 382 * that it must not call the post handler again. 383 */ 384 if (ENDBODY_NOT != n->end) 385 n->pending->flags |= MDOC_ENDED; 386 387 /* 388 * End of line terminating an implicit block 389 * while an explicit block is still open. 390 * Continue the explicit block without spacing. 391 */ 392 if (ENDBODY_NOSPACE == n->end) 393 p->flags |= TERMP_NOSPACE; 394 break; 395 } 396 397 if (MDOC_EOS & n->flags) 398 p->flags |= TERMP_SENTENCE; 399 400 p->offset = offset; 401 p->rmargin = rmargin; 402 } 403 404 405 static void 406 print_mdoc_foot(struct termp *p, const void *arg) 407 { 408 const struct mdoc_meta *m; 409 410 m = (const struct mdoc_meta *)arg; 411 412 term_fontrepl(p, TERMFONT_NONE); 413 414 /* 415 * Output the footer in new-groff style, that is, three columns 416 * with the middle being the manual date and flanking columns 417 * being the operating system: 418 * 419 * SYSTEM DATE SYSTEM 420 */ 421 422 term_vspace(p); 423 424 p->offset = 0; 425 p->rmargin = (p->maxrmargin - 426 term_strlen(p, m->date) + term_len(p, 1)) / 2; 427 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 428 429 term_word(p, m->os); 430 term_flushln(p); 431 432 p->offset = p->rmargin; 433 p->rmargin = p->maxrmargin - term_strlen(p, m->os); 434 p->flags |= TERMP_NOSPACE; 435 436 term_word(p, m->date); 437 term_flushln(p); 438 439 p->offset = p->rmargin; 440 p->rmargin = p->maxrmargin; 441 p->flags &= ~TERMP_NOBREAK; 442 p->flags |= TERMP_NOSPACE; 443 444 term_word(p, m->os); 445 term_flushln(p); 446 447 p->offset = 0; 448 p->rmargin = p->maxrmargin; 449 p->flags = 0; 450 } 451 452 453 static void 454 print_mdoc_head(struct termp *p, const void *arg) 455 { 456 char buf[BUFSIZ], title[BUFSIZ]; 457 size_t buflen, titlen; 458 const struct mdoc_meta *m; 459 460 m = (const struct mdoc_meta *)arg; 461 462 /* 463 * The header is strange. It has three components, which are 464 * really two with the first duplicated. It goes like this: 465 * 466 * IDENTIFIER TITLE IDENTIFIER 467 * 468 * The IDENTIFIER is NAME(SECTION), which is the command-name 469 * (if given, or "unknown" if not) followed by the manual page 470 * section. These are given in `Dt'. The TITLE is a free-form 471 * string depending on the manual volume. If not specified, it 472 * switches on the manual section. 473 */ 474 475 p->offset = 0; 476 p->rmargin = p->maxrmargin; 477 478 assert(m->vol); 479 strlcpy(buf, m->vol, BUFSIZ); 480 buflen = term_strlen(p, buf); 481 482 if (m->arch) { 483 strlcat(buf, " (", BUFSIZ); 484 strlcat(buf, m->arch, BUFSIZ); 485 strlcat(buf, ")", BUFSIZ); 486 } 487 488 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 489 titlen = term_strlen(p, title); 490 491 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 492 p->offset = 0; 493 p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? 494 (p->maxrmargin - 495 term_strlen(p, buf) + term_len(p, 1)) / 2 : 496 p->maxrmargin - buflen; 497 498 term_word(p, title); 499 term_flushln(p); 500 501 p->flags |= TERMP_NOSPACE; 502 p->offset = p->rmargin; 503 p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? 504 p->maxrmargin - titlen : p->maxrmargin; 505 506 term_word(p, buf); 507 term_flushln(p); 508 509 p->flags &= ~TERMP_NOBREAK; 510 if (p->rmargin + titlen <= p->maxrmargin) { 511 p->flags |= TERMP_NOSPACE; 512 p->offset = p->rmargin; 513 p->rmargin = p->maxrmargin; 514 term_word(p, title); 515 term_flushln(p); 516 } 517 518 p->flags &= ~TERMP_NOSPACE; 519 p->offset = 0; 520 p->rmargin = p->maxrmargin; 521 } 522 523 524 static size_t 525 a2height(const struct termp *p, const char *v) 526 { 527 struct roffsu su; 528 529 530 assert(v); 531 if ( ! a2roffsu(v, &su, SCALE_VS)) 532 SCALE_VS_INIT(&su, atoi(v)); 533 534 return(term_vspan(p, &su)); 535 } 536 537 538 static size_t 539 a2width(const struct termp *p, const char *v) 540 { 541 struct roffsu su; 542 543 assert(v); 544 if ( ! a2roffsu(v, &su, SCALE_MAX)) 545 SCALE_HS_INIT(&su, term_strlen(p, v)); 546 547 return(term_hspan(p, &su)); 548 } 549 550 551 static size_t 552 a2offs(const struct termp *p, const char *v) 553 { 554 struct roffsu su; 555 556 if ('\0' == *v) 557 return(0); 558 else if (0 == strcmp(v, "left")) 559 return(0); 560 else if (0 == strcmp(v, "indent")) 561 return(term_len(p, p->defindent + 1)); 562 else if (0 == strcmp(v, "indent-two")) 563 return(term_len(p, (p->defindent + 1) * 2)); 564 else if ( ! a2roffsu(v, &su, SCALE_MAX)) 565 SCALE_HS_INIT(&su, term_strlen(p, v)); 566 567 return(term_hspan(p, &su)); 568 } 569 570 571 /* 572 * Determine how much space to print out before block elements of `It' 573 * (and thus `Bl') and `Bd'. And then go ahead and print that space, 574 * too. 575 */ 576 static void 577 print_bvspace(struct termp *p, 578 const struct mdoc_node *bl, 579 const struct mdoc_node *n) 580 { 581 const struct mdoc_node *nn; 582 583 assert(n); 584 585 term_newln(p); 586 587 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) 588 return; 589 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) 590 return; 591 592 /* Do not vspace directly after Ss/Sh. */ 593 594 for (nn = n; nn; nn = nn->parent) { 595 if (MDOC_BLOCK != nn->type) 596 continue; 597 if (MDOC_Ss == nn->tok) 598 return; 599 if (MDOC_Sh == nn->tok) 600 return; 601 if (NULL == nn->prev) 602 continue; 603 break; 604 } 605 606 /* A `-column' does not assert vspace within the list. */ 607 608 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) 609 if (n->prev && MDOC_It == n->prev->tok) 610 return; 611 612 /* A `-diag' without body does not vspace. */ 613 614 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) 615 if (n->prev && MDOC_It == n->prev->tok) { 616 assert(n->prev->body); 617 if (NULL == n->prev->body->child) 618 return; 619 } 620 621 term_vspace(p); 622 } 623 624 625 /* ARGSUSED */ 626 static int 627 termp_it_pre(DECL_ARGS) 628 { 629 const struct mdoc_node *bl, *nn; 630 char buf[7]; 631 int i; 632 size_t width, offset, ncols, dcol; 633 enum mdoc_list type; 634 635 if (MDOC_BLOCK == n->type) { 636 print_bvspace(p, n->parent->parent, n); 637 return(1); 638 } 639 640 bl = n->parent->parent->parent; 641 type = bl->norm->Bl.type; 642 643 /* 644 * First calculate width and offset. This is pretty easy unless 645 * we're a -column list, in which case all prior columns must 646 * be accounted for. 647 */ 648 649 width = offset = 0; 650 651 if (bl->norm->Bl.offs) 652 offset = a2offs(p, bl->norm->Bl.offs); 653 654 switch (type) { 655 case (LIST_column): 656 if (MDOC_HEAD == n->type) 657 break; 658 659 /* 660 * Imitate groff's column handling: 661 * - For each earlier column, add its width. 662 * - For less than 5 columns, add four more blanks per 663 * column. 664 * - For exactly 5 columns, add three more blank per 665 * column. 666 * - For more than 5 columns, add only one column. 667 */ 668 ncols = bl->norm->Bl.ncols; 669 670 /* LINTED */ 671 dcol = ncols < 5 ? term_len(p, 4) : 672 ncols == 5 ? term_len(p, 3) : term_len(p, 1); 673 674 /* 675 * Calculate the offset by applying all prior MDOC_BODY, 676 * so we stop at the MDOC_HEAD (NULL == nn->prev). 677 */ 678 679 for (i = 0, nn = n->prev; 680 nn->prev && i < (int)ncols; 681 nn = nn->prev, i++) 682 offset += dcol + a2width 683 (p, bl->norm->Bl.cols[i]); 684 685 /* 686 * When exceeding the declared number of columns, leave 687 * the remaining widths at 0. This will later be 688 * adjusted to the default width of 10, or, for the last 689 * column, stretched to the right margin. 690 */ 691 if (i >= (int)ncols) 692 break; 693 694 /* 695 * Use the declared column widths, extended as explained 696 * in the preceding paragraph. 697 */ 698 width = a2width(p, bl->norm->Bl.cols[i]) + dcol; 699 break; 700 default: 701 if (NULL == bl->norm->Bl.width) 702 break; 703 704 /* 705 * Note: buffer the width by 2, which is groff's magic 706 * number for buffering single arguments. See the above 707 * handling for column for how this changes. 708 */ 709 assert(bl->norm->Bl.width); 710 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); 711 break; 712 } 713 714 /* 715 * List-type can override the width in the case of fixed-head 716 * values (bullet, dash/hyphen, enum). Tags need a non-zero 717 * offset. 718 */ 719 720 switch (type) { 721 case (LIST_bullet): 722 /* FALLTHROUGH */ 723 case (LIST_dash): 724 /* FALLTHROUGH */ 725 case (LIST_hyphen): 726 /* FALLTHROUGH */ 727 case (LIST_enum): 728 if (width < term_len(p, 2)) 729 width = term_len(p, 2); 730 break; 731 case (LIST_hang): 732 if (0 == width) 733 width = term_len(p, 8); 734 break; 735 case (LIST_column): 736 /* FALLTHROUGH */ 737 case (LIST_tag): 738 if (0 == width) 739 width = term_len(p, 10); 740 break; 741 default: 742 break; 743 } 744 745 /* 746 * Whitespace control. Inset bodies need an initial space, 747 * while diagonal bodies need two. 748 */ 749 750 p->flags |= TERMP_NOSPACE; 751 752 switch (type) { 753 case (LIST_diag): 754 if (MDOC_BODY == n->type) 755 term_word(p, "\\ \\ "); 756 break; 757 case (LIST_inset): 758 if (MDOC_BODY == n->type) 759 term_word(p, "\\ "); 760 break; 761 default: 762 break; 763 } 764 765 p->flags |= TERMP_NOSPACE; 766 767 switch (type) { 768 case (LIST_diag): 769 if (MDOC_HEAD == n->type) 770 term_fontpush(p, TERMFONT_BOLD); 771 break; 772 default: 773 break; 774 } 775 776 /* 777 * Pad and break control. This is the tricky part. These flags 778 * are documented in term_flushln() in term.c. Note that we're 779 * going to unset all of these flags in termp_it_post() when we 780 * exit. 781 */ 782 783 switch (type) { 784 case (LIST_enum): 785 /* 786 * Weird special case. 787 * Very narrow enum lists actually hang. 788 */ 789 if (width == term_len(p, 2)) 790 p->flags |= TERMP_HANG; 791 /* FALLTHROUGH */ 792 case (LIST_bullet): 793 /* FALLTHROUGH */ 794 case (LIST_dash): 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 void 1411 termp_fd_post(DECL_ARGS) 1412 { 1413 1414 term_newln(p); 1415 } 1416 1417 1418 /* ARGSUSED */ 1419 static int 1420 termp_sh_pre(DECL_ARGS) 1421 { 1422 1423 /* No vspace between consecutive `Sh' calls. */ 1424 1425 switch (n->type) { 1426 case (MDOC_BLOCK): 1427 if (n->prev && MDOC_Sh == n->prev->tok) 1428 if (NULL == n->prev->body->child) 1429 break; 1430 term_vspace(p); 1431 break; 1432 case (MDOC_HEAD): 1433 term_fontpush(p, TERMFONT_BOLD); 1434 break; 1435 case (MDOC_BODY): 1436 p->offset = term_len(p, p->defindent); 1437 if (SEC_AUTHORS == n->sec) 1438 p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); 1439 break; 1440 default: 1441 break; 1442 } 1443 return(1); 1444 } 1445 1446 1447 /* ARGSUSED */ 1448 static void 1449 termp_sh_post(DECL_ARGS) 1450 { 1451 1452 switch (n->type) { 1453 case (MDOC_HEAD): 1454 term_newln(p); 1455 break; 1456 case (MDOC_BODY): 1457 term_newln(p); 1458 p->offset = 0; 1459 break; 1460 default: 1461 break; 1462 } 1463 } 1464 1465 1466 /* ARGSUSED */ 1467 static int 1468 termp_bt_pre(DECL_ARGS) 1469 { 1470 1471 term_word(p, "is currently in beta test."); 1472 p->flags |= TERMP_SENTENCE; 1473 return(0); 1474 } 1475 1476 1477 /* ARGSUSED */ 1478 static void 1479 termp_lb_post(DECL_ARGS) 1480 { 1481 1482 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags) 1483 term_newln(p); 1484 } 1485 1486 1487 /* ARGSUSED */ 1488 static int 1489 termp_ud_pre(DECL_ARGS) 1490 { 1491 1492 term_word(p, "currently under development."); 1493 p->flags |= TERMP_SENTENCE; 1494 return(0); 1495 } 1496 1497 1498 /* ARGSUSED */ 1499 static int 1500 termp_d1_pre(DECL_ARGS) 1501 { 1502 1503 if (MDOC_BLOCK != n->type) 1504 return(1); 1505 term_newln(p); 1506 p->offset += term_len(p, p->defindent + 1); 1507 return(1); 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 *link, *descr; 2161 2162 if (NULL == (link = n->child)) 2163 return(0); 2164 2165 if (NULL != (descr = link->next)) { 2166 term_fontpush(p, TERMFONT_UNDER); 2167 while (NULL != descr) { 2168 term_word(p, descr->string); 2169 descr = descr->next; 2170 } 2171 p->flags |= TERMP_NOSPACE; 2172 term_word(p, ":"); 2173 term_fontpop(p); 2174 } 2175 2176 term_fontpush(p, TERMFONT_BOLD); 2177 term_word(p, link->string); 2178 term_fontpop(p); 2179 2180 return(0); 2181 } 2182 2183 2184 /* ARGSUSED */ 2185 static int 2186 termp_bk_pre(DECL_ARGS) 2187 { 2188 2189 switch (n->type) { 2190 case (MDOC_BLOCK): 2191 break; 2192 case (MDOC_HEAD): 2193 return(0); 2194 case (MDOC_BODY): 2195 if (n->parent->args || 0 == n->prev->nchild) 2196 p->flags |= TERMP_PREKEEP; 2197 break; 2198 default: 2199 abort(); 2200 /* NOTREACHED */ 2201 } 2202 2203 return(1); 2204 } 2205 2206 2207 /* ARGSUSED */ 2208 static void 2209 termp_bk_post(DECL_ARGS) 2210 { 2211 2212 if (MDOC_BODY == n->type) 2213 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 2214 } 2215 2216 /* ARGSUSED */ 2217 static void 2218 termp__t_post(DECL_ARGS) 2219 { 2220 2221 /* 2222 * If we're in an `Rs' and there's a journal present, then quote 2223 * us instead of underlining us (for disambiguation). 2224 */ 2225 if (n->parent && MDOC_Rs == n->parent->tok && 2226 n->parent->norm->Rs.quote_T) 2227 termp_quote_post(p, pair, m, n); 2228 2229 termp____post(p, pair, m, n); 2230 } 2231 2232 /* ARGSUSED */ 2233 static int 2234 termp__t_pre(DECL_ARGS) 2235 { 2236 2237 /* 2238 * If we're in an `Rs' and there's a journal present, then quote 2239 * us instead of underlining us (for disambiguation). 2240 */ 2241 if (n->parent && MDOC_Rs == n->parent->tok && 2242 n->parent->norm->Rs.quote_T) 2243 return(termp_quote_pre(p, pair, m, n)); 2244 2245 term_fontpush(p, TERMFONT_UNDER); 2246 return(1); 2247 } 2248 2249 /* ARGSUSED */ 2250 static int 2251 termp_under_pre(DECL_ARGS) 2252 { 2253 2254 term_fontpush(p, TERMFONT_UNDER); 2255 return(1); 2256 } 2257