1 /* $Id: mdoc_term.c,v 1.249 2013/06/02 18:16:57 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/types.h> 23 24 #include <assert.h> 25 #include <ctype.h> 26 #include <stdint.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include "mandoc.h" 32 #include "out.h" 33 #include "term.h" 34 #include "mdoc.h" 35 #include "main.h" 36 37 struct termpair { 38 struct termpair *ppair; 39 int count; 40 }; 41 42 #define DECL_ARGS struct termp *p, \ 43 struct termpair *pair, \ 44 const struct mdoc_meta *meta, \ 45 struct mdoc_node *n 46 47 struct termact { 48 int (*pre)(DECL_ARGS); 49 void (*post)(DECL_ARGS); 50 }; 51 52 static size_t a2width(const struct termp *, const char *); 53 static size_t a2height(const struct termp *, const char *); 54 static size_t a2offs(const struct termp *, const char *); 55 56 static void print_bvspace(struct termp *, 57 const struct mdoc_node *, 58 const struct mdoc_node *); 59 static void print_mdoc_node(DECL_ARGS); 60 static void print_mdoc_nodelist(DECL_ARGS); 61 static void print_mdoc_head(struct termp *, const void *); 62 static void print_mdoc_foot(struct termp *, const void *); 63 static void synopsis_pre(struct termp *, 64 const struct mdoc_node *); 65 66 static void termp____post(DECL_ARGS); 67 static void termp__t_post(DECL_ARGS); 68 static void termp_an_post(DECL_ARGS); 69 static void termp_bd_post(DECL_ARGS); 70 static void termp_bk_post(DECL_ARGS); 71 static void termp_bl_post(DECL_ARGS); 72 static void termp_fd_post(DECL_ARGS); 73 static void termp_fo_post(DECL_ARGS); 74 static void termp_in_post(DECL_ARGS); 75 static void termp_it_post(DECL_ARGS); 76 static void termp_lb_post(DECL_ARGS); 77 static void termp_nm_post(DECL_ARGS); 78 static void termp_pf_post(DECL_ARGS); 79 static void termp_quote_post(DECL_ARGS); 80 static void termp_sh_post(DECL_ARGS); 81 static void termp_ss_post(DECL_ARGS); 82 83 static int termp__a_pre(DECL_ARGS); 84 static int termp__t_pre(DECL_ARGS); 85 static int termp_an_pre(DECL_ARGS); 86 static int termp_ap_pre(DECL_ARGS); 87 static int termp_bd_pre(DECL_ARGS); 88 static int termp_bf_pre(DECL_ARGS); 89 static int termp_bk_pre(DECL_ARGS); 90 static int termp_bl_pre(DECL_ARGS); 91 static int termp_bold_pre(DECL_ARGS); 92 static int termp_bt_pre(DECL_ARGS); 93 static int termp_bx_pre(DECL_ARGS); 94 static int termp_cd_pre(DECL_ARGS); 95 static int termp_d1_pre(DECL_ARGS); 96 static int termp_ex_pre(DECL_ARGS); 97 static int termp_fa_pre(DECL_ARGS); 98 static int termp_fd_pre(DECL_ARGS); 99 static int termp_fl_pre(DECL_ARGS); 100 static int termp_fn_pre(DECL_ARGS); 101 static int termp_fo_pre(DECL_ARGS); 102 static int termp_ft_pre(DECL_ARGS); 103 static int termp_igndelim_pre(DECL_ARGS); 104 static int termp_in_pre(DECL_ARGS); 105 static int termp_it_pre(DECL_ARGS); 106 static int termp_li_pre(DECL_ARGS); 107 static int termp_lk_pre(DECL_ARGS); 108 static int termp_nd_pre(DECL_ARGS); 109 static int termp_nm_pre(DECL_ARGS); 110 static int termp_ns_pre(DECL_ARGS); 111 static int termp_quote_pre(DECL_ARGS); 112 static int termp_rs_pre(DECL_ARGS); 113 static int termp_rv_pre(DECL_ARGS); 114 static int termp_sh_pre(DECL_ARGS); 115 static int termp_sm_pre(DECL_ARGS); 116 static int termp_sp_pre(DECL_ARGS); 117 static int termp_ss_pre(DECL_ARGS); 118 static int termp_under_pre(DECL_ARGS); 119 static int termp_ud_pre(DECL_ARGS); 120 static int termp_vt_pre(DECL_ARGS); 121 static int termp_xr_pre(DECL_ARGS); 122 static int termp_xx_pre(DECL_ARGS); 123 124 static const struct termact termacts[MDOC_MAX] = { 125 { termp_ap_pre, NULL }, /* Ap */ 126 { NULL, NULL }, /* Dd */ 127 { NULL, NULL }, /* Dt */ 128 { NULL, NULL }, /* Os */ 129 { termp_sh_pre, termp_sh_post }, /* Sh */ 130 { termp_ss_pre, termp_ss_post }, /* Ss */ 131 { termp_sp_pre, NULL }, /* Pp */ 132 { termp_d1_pre, termp_bl_post }, /* D1 */ 133 { termp_d1_pre, termp_bl_post }, /* Dl */ 134 { termp_bd_pre, termp_bd_post }, /* Bd */ 135 { NULL, NULL }, /* Ed */ 136 { termp_bl_pre, termp_bl_post }, /* Bl */ 137 { NULL, NULL }, /* El */ 138 { termp_it_pre, termp_it_post }, /* It */ 139 { termp_under_pre, NULL }, /* Ad */ 140 { termp_an_pre, termp_an_post }, /* An */ 141 { termp_under_pre, NULL }, /* Ar */ 142 { termp_cd_pre, NULL }, /* Cd */ 143 { termp_bold_pre, NULL }, /* Cm */ 144 { NULL, NULL }, /* Dv */ 145 { NULL, NULL }, /* Er */ 146 { NULL, NULL }, /* Ev */ 147 { termp_ex_pre, NULL }, /* Ex */ 148 { termp_fa_pre, NULL }, /* Fa */ 149 { termp_fd_pre, termp_fd_post }, /* Fd */ 150 { termp_fl_pre, NULL }, /* Fl */ 151 { termp_fn_pre, NULL }, /* Fn */ 152 { termp_ft_pre, NULL }, /* Ft */ 153 { termp_bold_pre, NULL }, /* Ic */ 154 { termp_in_pre, termp_in_post }, /* In */ 155 { termp_li_pre, NULL }, /* Li */ 156 { termp_nd_pre, NULL }, /* Nd */ 157 { termp_nm_pre, termp_nm_post }, /* Nm */ 158 { termp_quote_pre, termp_quote_post }, /* Op */ 159 { NULL, NULL }, /* Ot */ 160 { termp_under_pre, NULL }, /* Pa */ 161 { termp_rv_pre, NULL }, /* Rv */ 162 { NULL, NULL }, /* St */ 163 { termp_under_pre, NULL }, /* Va */ 164 { termp_vt_pre, NULL }, /* Vt */ 165 { termp_xr_pre, NULL }, /* Xr */ 166 { termp__a_pre, termp____post }, /* %A */ 167 { termp_under_pre, termp____post }, /* %B */ 168 { NULL, termp____post }, /* %D */ 169 { termp_under_pre, termp____post }, /* %I */ 170 { termp_under_pre, termp____post }, /* %J */ 171 { NULL, termp____post }, /* %N */ 172 { NULL, termp____post }, /* %O */ 173 { NULL, termp____post }, /* %P */ 174 { NULL, termp____post }, /* %R */ 175 { termp__t_pre, termp__t_post }, /* %T */ 176 { NULL, termp____post }, /* %V */ 177 { NULL, NULL }, /* Ac */ 178 { termp_quote_pre, termp_quote_post }, /* Ao */ 179 { termp_quote_pre, termp_quote_post }, /* Aq */ 180 { NULL, NULL }, /* At */ 181 { NULL, NULL }, /* Bc */ 182 { termp_bf_pre, NULL }, /* Bf */ 183 { termp_quote_pre, termp_quote_post }, /* Bo */ 184 { termp_quote_pre, termp_quote_post }, /* Bq */ 185 { termp_xx_pre, NULL }, /* Bsx */ 186 { termp_bx_pre, NULL }, /* Bx */ 187 { NULL, NULL }, /* Db */ 188 { NULL, NULL }, /* Dc */ 189 { termp_quote_pre, termp_quote_post }, /* Do */ 190 { termp_quote_pre, termp_quote_post }, /* Dq */ 191 { NULL, NULL }, /* Ec */ /* FIXME: no space */ 192 { NULL, NULL }, /* Ef */ 193 { termp_under_pre, NULL }, /* Em */ 194 { termp_quote_pre, termp_quote_post }, /* Eo */ 195 { termp_xx_pre, NULL }, /* Fx */ 196 { termp_bold_pre, NULL }, /* Ms */ 197 { termp_igndelim_pre, NULL }, /* No */ 198 { termp_ns_pre, NULL }, /* Ns */ 199 { termp_xx_pre, NULL }, /* Nx */ 200 { termp_xx_pre, NULL }, /* Ox */ 201 { NULL, NULL }, /* Pc */ 202 { termp_igndelim_pre, termp_pf_post }, /* Pf */ 203 { termp_quote_pre, termp_quote_post }, /* Po */ 204 { termp_quote_pre, termp_quote_post }, /* Pq */ 205 { NULL, NULL }, /* Qc */ 206 { termp_quote_pre, termp_quote_post }, /* Ql */ 207 { termp_quote_pre, termp_quote_post }, /* Qo */ 208 { termp_quote_pre, termp_quote_post }, /* Qq */ 209 { NULL, NULL }, /* Re */ 210 { termp_rs_pre, NULL }, /* Rs */ 211 { NULL, NULL }, /* Sc */ 212 { termp_quote_pre, termp_quote_post }, /* So */ 213 { termp_quote_pre, termp_quote_post }, /* Sq */ 214 { termp_sm_pre, NULL }, /* Sm */ 215 { termp_under_pre, NULL }, /* Sx */ 216 { termp_bold_pre, NULL }, /* Sy */ 217 { NULL, NULL }, /* Tn */ 218 { termp_xx_pre, NULL }, /* Ux */ 219 { NULL, NULL }, /* Xc */ 220 { NULL, NULL }, /* Xo */ 221 { termp_fo_pre, termp_fo_post }, /* Fo */ 222 { NULL, NULL }, /* Fc */ 223 { termp_quote_pre, termp_quote_post }, /* Oo */ 224 { NULL, NULL }, /* Oc */ 225 { termp_bk_pre, termp_bk_post }, /* Bk */ 226 { NULL, NULL }, /* Ek */ 227 { termp_bt_pre, NULL }, /* Bt */ 228 { NULL, NULL }, /* Hf */ 229 { NULL, NULL }, /* Fr */ 230 { termp_ud_pre, NULL }, /* Ud */ 231 { NULL, termp_lb_post }, /* Lb */ 232 { termp_sp_pre, NULL }, /* Lp */ 233 { termp_lk_pre, NULL }, /* Lk */ 234 { termp_under_pre, NULL }, /* Mt */ 235 { termp_quote_pre, termp_quote_post }, /* Brq */ 236 { termp_quote_pre, termp_quote_post }, /* Bro */ 237 { NULL, NULL }, /* Brc */ 238 { NULL, termp____post }, /* %C */ 239 { NULL, NULL }, /* Es */ /* TODO */ 240 { NULL, NULL }, /* En */ /* TODO */ 241 { termp_xx_pre, NULL }, /* Dx */ 242 { NULL, termp____post }, /* %Q */ 243 { termp_sp_pre, NULL }, /* br */ 244 { termp_sp_pre, NULL }, /* sp */ 245 { NULL, termp____post }, /* %U */ 246 { NULL, NULL }, /* Ta */ 247 }; 248 249 250 void 251 terminal_mdoc(void *arg, const struct mdoc *mdoc) 252 { 253 const struct mdoc_node *n; 254 const struct mdoc_meta *meta; 255 struct termp *p; 256 257 p = (struct termp *)arg; 258 259 if (0 == p->defindent) 260 p->defindent = 5; 261 262 p->overstep = 0; 263 p->maxrmargin = p->defrmargin; 264 p->tabwidth = term_len(p, 5); 265 266 if (NULL == p->symtab) 267 p->symtab = mchars_alloc(); 268 269 n = mdoc_node(mdoc); 270 meta = mdoc_meta(mdoc); 271 272 term_begin(p, print_mdoc_head, print_mdoc_foot, meta); 273 274 if (n->child) 275 print_mdoc_nodelist(p, NULL, meta, n->child); 276 277 term_end(p); 278 } 279 280 281 static void 282 print_mdoc_nodelist(DECL_ARGS) 283 { 284 285 print_mdoc_node(p, pair, meta, n); 286 if (n->next) 287 print_mdoc_nodelist(p, pair, meta, n->next); 288 } 289 290 291 /* ARGSUSED */ 292 static void 293 print_mdoc_node(DECL_ARGS) 294 { 295 int chld; 296 struct termpair npair; 297 size_t offset, rmargin; 298 299 chld = 1; 300 offset = p->offset; 301 rmargin = p->rmargin; 302 n->prev_font = term_fontq(p); 303 304 memset(&npair, 0, sizeof(struct termpair)); 305 npair.ppair = pair; 306 307 /* 308 * Keeps only work until the end of a line. If a keep was 309 * invoked in a prior line, revert it to PREKEEP. 310 * 311 * Also let SYNPRETTY sections behave as if they were wrapped 312 * in a `Bk' block. 313 */ 314 315 if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) { 316 if (n->prev ? (n->prev->line != n->line) : 317 (n->parent && n->parent->line != n->line)) { 318 p->flags &= ~TERMP_KEEP; 319 p->flags |= TERMP_PREKEEP; 320 } 321 } 322 323 /* 324 * Since SYNPRETTY sections aren't "turned off" with `Ek', 325 * we have to intuit whether we should disable formatting. 326 */ 327 328 if ( ! (MDOC_SYNPRETTY & n->flags) && 329 ((n->prev && MDOC_SYNPRETTY & n->prev->flags) || 330 (n->parent && MDOC_SYNPRETTY & n->parent->flags))) 331 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 332 333 /* 334 * After the keep flags have been set up, we may now 335 * produce output. Note that some pre-handlers do so. 336 */ 337 338 switch (n->type) { 339 case (MDOC_TEXT): 340 if (' ' == *n->string && MDOC_LINE & n->flags) 341 term_newln(p); 342 if (MDOC_DELIMC & n->flags) 343 p->flags |= TERMP_NOSPACE; 344 term_word(p, n->string); 345 if (MDOC_DELIMO & n->flags) 346 p->flags |= TERMP_NOSPACE; 347 break; 348 case (MDOC_EQN): 349 term_eqn(p, n->eqn); 350 break; 351 case (MDOC_TBL): 352 term_tbl(p, n->span); 353 break; 354 default: 355 if (termacts[n->tok].pre && ENDBODY_NOT == n->end) 356 chld = (*termacts[n->tok].pre) 357 (p, &npair, meta, n); 358 break; 359 } 360 361 if (chld && n->child) 362 print_mdoc_nodelist(p, &npair, meta, n->child); 363 364 term_fontpopq(p, 365 (ENDBODY_NOT == n->end ? n : n->pending)->prev_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, meta, 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 *meta; 409 410 meta = (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, meta->date) + term_len(p, 1)) / 2; 427 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 428 429 term_word(p, meta->os); 430 term_flushln(p); 431 432 p->offset = p->rmargin; 433 p->rmargin = p->maxrmargin - term_strlen(p, meta->os); 434 p->flags |= TERMP_NOSPACE; 435 436 term_word(p, meta->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, meta->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 *meta; 459 460 meta = (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(meta->vol); 479 strlcpy(buf, meta->vol, BUFSIZ); 480 buflen = term_strlen(p, buf); 481 482 if (meta->arch) { 483 strlcat(buf, " (", BUFSIZ); 484 strlcat(buf, meta->arch, BUFSIZ); 485 strlcat(buf, ")", BUFSIZ); 486 } 487 488 snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->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 ? 1011 term_strlen(p, meta->name) : 1012 MDOC_TEXT == n->prev->child->type ? 1013 term_strlen(p, n->prev->child->string) : 1014 term_len(p, 5)); 1015 return(1); 1016 } 1017 1018 if (NULL == n->child && NULL == meta->name) 1019 return(0); 1020 1021 if (MDOC_HEAD == n->type) 1022 synopsis_pre(p, n->parent); 1023 1024 if (MDOC_HEAD == n->type && n->next->child) { 1025 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 1026 p->rmargin = p->offset + term_len(p, 1); 1027 if (NULL == n->child) { 1028 p->rmargin += term_strlen(p, meta->name); 1029 } else if (MDOC_TEXT == n->child->type) { 1030 p->rmargin += term_strlen(p, n->child->string); 1031 if (n->child->next) 1032 p->flags |= TERMP_HANG; 1033 } else { 1034 p->rmargin += term_len(p, 5); 1035 p->flags |= TERMP_HANG; 1036 } 1037 } 1038 1039 term_fontpush(p, TERMFONT_BOLD); 1040 if (NULL == n->child) 1041 term_word(p, meta->name); 1042 return(1); 1043 } 1044 1045 1046 /* ARGSUSED */ 1047 static void 1048 termp_nm_post(DECL_ARGS) 1049 { 1050 1051 if (MDOC_HEAD == n->type && n->next->child) { 1052 term_flushln(p); 1053 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG); 1054 } else if (MDOC_BODY == n->type && n->child) 1055 term_flushln(p); 1056 } 1057 1058 1059 /* ARGSUSED */ 1060 static int 1061 termp_fl_pre(DECL_ARGS) 1062 { 1063 1064 term_fontpush(p, TERMFONT_BOLD); 1065 term_word(p, "\\-"); 1066 1067 if (n->child) 1068 p->flags |= TERMP_NOSPACE; 1069 else if (n->next && n->next->line == n->line) 1070 p->flags |= TERMP_NOSPACE; 1071 1072 return(1); 1073 } 1074 1075 1076 /* ARGSUSED */ 1077 static int 1078 termp__a_pre(DECL_ARGS) 1079 { 1080 1081 if (n->prev && MDOC__A == n->prev->tok) 1082 if (NULL == n->next || MDOC__A != n->next->tok) 1083 term_word(p, "and"); 1084 1085 return(1); 1086 } 1087 1088 1089 /* ARGSUSED */ 1090 static int 1091 termp_an_pre(DECL_ARGS) 1092 { 1093 1094 if (NULL == n->child) 1095 return(1); 1096 1097 /* 1098 * If not in the AUTHORS section, `An -split' will cause 1099 * newlines to occur before the author name. If in the AUTHORS 1100 * section, by default, the first `An' invocation is nosplit, 1101 * then all subsequent ones, regardless of whether interspersed 1102 * with other macros/text, are split. -split, in this case, 1103 * will override the condition of the implied first -nosplit. 1104 */ 1105 1106 if (n->sec == SEC_AUTHORS) { 1107 if ( ! (TERMP_ANPREC & p->flags)) { 1108 if (TERMP_SPLIT & p->flags) 1109 term_newln(p); 1110 return(1); 1111 } 1112 if (TERMP_NOSPLIT & p->flags) 1113 return(1); 1114 term_newln(p); 1115 return(1); 1116 } 1117 1118 if (TERMP_SPLIT & p->flags) 1119 term_newln(p); 1120 1121 return(1); 1122 } 1123 1124 1125 /* ARGSUSED */ 1126 static void 1127 termp_an_post(DECL_ARGS) 1128 { 1129 1130 if (n->child) { 1131 if (SEC_AUTHORS == n->sec) 1132 p->flags |= TERMP_ANPREC; 1133 return; 1134 } 1135 1136 if (AUTH_split == n->norm->An.auth) { 1137 p->flags &= ~TERMP_NOSPLIT; 1138 p->flags |= TERMP_SPLIT; 1139 } else if (AUTH_nosplit == n->norm->An.auth) { 1140 p->flags &= ~TERMP_SPLIT; 1141 p->flags |= TERMP_NOSPLIT; 1142 } 1143 1144 } 1145 1146 1147 /* ARGSUSED */ 1148 static int 1149 termp_ns_pre(DECL_ARGS) 1150 { 1151 1152 if ( ! (MDOC_LINE & n->flags)) 1153 p->flags |= TERMP_NOSPACE; 1154 return(1); 1155 } 1156 1157 1158 /* ARGSUSED */ 1159 static int 1160 termp_rs_pre(DECL_ARGS) 1161 { 1162 1163 if (SEC_SEE_ALSO != n->sec) 1164 return(1); 1165 if (MDOC_BLOCK == n->type && n->prev) 1166 term_vspace(p); 1167 return(1); 1168 } 1169 1170 1171 /* ARGSUSED */ 1172 static int 1173 termp_rv_pre(DECL_ARGS) 1174 { 1175 int nchild; 1176 1177 term_newln(p); 1178 term_word(p, "The"); 1179 1180 nchild = n->nchild; 1181 for (n = n->child; n; n = n->next) { 1182 term_fontpush(p, TERMFONT_BOLD); 1183 term_word(p, n->string); 1184 term_fontpop(p); 1185 1186 p->flags |= TERMP_NOSPACE; 1187 term_word(p, "()"); 1188 1189 if (nchild > 2 && n->next) { 1190 p->flags |= TERMP_NOSPACE; 1191 term_word(p, ","); 1192 } 1193 1194 if (n->next && NULL == n->next->next) 1195 term_word(p, "and"); 1196 } 1197 1198 if (nchild > 1) 1199 term_word(p, "functions return"); 1200 else 1201 term_word(p, "function returns"); 1202 1203 term_word(p, "the value 0 if successful; otherwise the value " 1204 "-1 is returned and the global variable"); 1205 1206 term_fontpush(p, TERMFONT_UNDER); 1207 term_word(p, "errno"); 1208 term_fontpop(p); 1209 1210 term_word(p, "is set to indicate the error."); 1211 p->flags |= TERMP_SENTENCE; 1212 1213 return(0); 1214 } 1215 1216 1217 /* ARGSUSED */ 1218 static int 1219 termp_ex_pre(DECL_ARGS) 1220 { 1221 int nchild; 1222 1223 term_newln(p); 1224 term_word(p, "The"); 1225 1226 nchild = n->nchild; 1227 for (n = n->child; n; n = n->next) { 1228 term_fontpush(p, TERMFONT_BOLD); 1229 term_word(p, n->string); 1230 term_fontpop(p); 1231 1232 if (nchild > 2 && n->next) { 1233 p->flags |= TERMP_NOSPACE; 1234 term_word(p, ","); 1235 } 1236 1237 if (n->next && NULL == n->next->next) 1238 term_word(p, "and"); 1239 } 1240 1241 if (nchild > 1) 1242 term_word(p, "utilities exit"); 1243 else 1244 term_word(p, "utility exits"); 1245 1246 term_word(p, "0 on success, and >0 if an error occurs."); 1247 1248 p->flags |= TERMP_SENTENCE; 1249 return(0); 1250 } 1251 1252 1253 /* ARGSUSED */ 1254 static int 1255 termp_nd_pre(DECL_ARGS) 1256 { 1257 1258 if (MDOC_BODY != n->type) 1259 return(1); 1260 1261 #if defined(__OpenBSD__) || defined(__linux__) 1262 term_word(p, "\\(en"); 1263 #else 1264 term_word(p, "\\(em"); 1265 #endif 1266 return(1); 1267 } 1268 1269 1270 /* ARGSUSED */ 1271 static int 1272 termp_bl_pre(DECL_ARGS) 1273 { 1274 1275 return(MDOC_HEAD != n->type); 1276 } 1277 1278 1279 /* ARGSUSED */ 1280 static void 1281 termp_bl_post(DECL_ARGS) 1282 { 1283 1284 if (MDOC_BLOCK == n->type) 1285 term_newln(p); 1286 } 1287 1288 /* ARGSUSED */ 1289 static int 1290 termp_xr_pre(DECL_ARGS) 1291 { 1292 1293 if (NULL == (n = n->child)) 1294 return(0); 1295 1296 assert(MDOC_TEXT == n->type); 1297 term_word(p, n->string); 1298 1299 if (NULL == (n = n->next)) 1300 return(0); 1301 1302 p->flags |= TERMP_NOSPACE; 1303 term_word(p, "("); 1304 p->flags |= TERMP_NOSPACE; 1305 1306 assert(MDOC_TEXT == n->type); 1307 term_word(p, n->string); 1308 1309 p->flags |= TERMP_NOSPACE; 1310 term_word(p, ")"); 1311 1312 return(0); 1313 } 1314 1315 /* 1316 * This decides how to assert whitespace before any of the SYNOPSIS set 1317 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain 1318 * macro combos). 1319 */ 1320 static void 1321 synopsis_pre(struct termp *p, const struct mdoc_node *n) 1322 { 1323 /* 1324 * Obviously, if we're not in a SYNOPSIS or no prior macros 1325 * exist, do nothing. 1326 */ 1327 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 1328 return; 1329 1330 /* 1331 * If we're the second in a pair of like elements, emit our 1332 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which 1333 * case we soldier on. 1334 */ 1335 if (n->prev->tok == n->tok && 1336 MDOC_Ft != n->tok && 1337 MDOC_Fo != n->tok && 1338 MDOC_Fn != n->tok) { 1339 term_newln(p); 1340 return; 1341 } 1342 1343 /* 1344 * If we're one of the SYNOPSIS set and non-like pair-wise after 1345 * another (or Fn/Fo, which we've let slip through) then assert 1346 * vertical space, else only newline and move on. 1347 */ 1348 switch (n->prev->tok) { 1349 case (MDOC_Fd): 1350 /* FALLTHROUGH */ 1351 case (MDOC_Fn): 1352 /* FALLTHROUGH */ 1353 case (MDOC_Fo): 1354 /* FALLTHROUGH */ 1355 case (MDOC_In): 1356 /* FALLTHROUGH */ 1357 case (MDOC_Vt): 1358 term_vspace(p); 1359 break; 1360 case (MDOC_Ft): 1361 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 1362 term_vspace(p); 1363 break; 1364 } 1365 /* FALLTHROUGH */ 1366 default: 1367 term_newln(p); 1368 break; 1369 } 1370 } 1371 1372 1373 static int 1374 termp_vt_pre(DECL_ARGS) 1375 { 1376 1377 if (MDOC_ELEM == n->type) { 1378 synopsis_pre(p, n); 1379 return(termp_under_pre(p, pair, meta, n)); 1380 } else if (MDOC_BLOCK == n->type) { 1381 synopsis_pre(p, n); 1382 return(1); 1383 } else if (MDOC_HEAD == n->type) 1384 return(0); 1385 1386 return(termp_under_pre(p, pair, meta, n)); 1387 } 1388 1389 1390 /* ARGSUSED */ 1391 static int 1392 termp_bold_pre(DECL_ARGS) 1393 { 1394 1395 term_fontpush(p, TERMFONT_BOLD); 1396 return(1); 1397 } 1398 1399 1400 /* ARGSUSED */ 1401 static int 1402 termp_fd_pre(DECL_ARGS) 1403 { 1404 1405 synopsis_pre(p, n); 1406 return(termp_bold_pre(p, pair, meta, n)); 1407 } 1408 1409 1410 /* ARGSUSED */ 1411 static void 1412 termp_fd_post(DECL_ARGS) 1413 { 1414 1415 term_newln(p); 1416 } 1417 1418 1419 /* ARGSUSED */ 1420 static int 1421 termp_sh_pre(DECL_ARGS) 1422 { 1423 1424 /* No vspace between consecutive `Sh' calls. */ 1425 1426 switch (n->type) { 1427 case (MDOC_BLOCK): 1428 if (n->prev && MDOC_Sh == n->prev->tok) 1429 if (NULL == n->prev->body->child) 1430 break; 1431 term_vspace(p); 1432 break; 1433 case (MDOC_HEAD): 1434 term_fontpush(p, TERMFONT_BOLD); 1435 break; 1436 case (MDOC_BODY): 1437 p->offset = term_len(p, p->defindent); 1438 if (SEC_AUTHORS == n->sec) 1439 p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); 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, p->defindent + 1); 1508 return(1); 1509 } 1510 1511 1512 /* ARGSUSED */ 1513 static int 1514 termp_ft_pre(DECL_ARGS) 1515 { 1516 1517 /* NB: MDOC_LINE does not effect this! */ 1518 synopsis_pre(p, n); 1519 term_fontpush(p, TERMFONT_UNDER); 1520 return(1); 1521 } 1522 1523 1524 /* ARGSUSED */ 1525 static int 1526 termp_fn_pre(DECL_ARGS) 1527 { 1528 int pretty; 1529 1530 pretty = MDOC_SYNPRETTY & n->flags; 1531 1532 synopsis_pre(p, n); 1533 1534 if (NULL == (n = n->child)) 1535 return(0); 1536 1537 assert(MDOC_TEXT == n->type); 1538 term_fontpush(p, TERMFONT_BOLD); 1539 term_word(p, n->string); 1540 term_fontpop(p); 1541 1542 p->flags |= TERMP_NOSPACE; 1543 term_word(p, "("); 1544 p->flags |= TERMP_NOSPACE; 1545 1546 for (n = n->next; n; n = n->next) { 1547 assert(MDOC_TEXT == n->type); 1548 term_fontpush(p, TERMFONT_UNDER); 1549 term_word(p, n->string); 1550 term_fontpop(p); 1551 1552 if (n->next) { 1553 p->flags |= TERMP_NOSPACE; 1554 term_word(p, ","); 1555 } 1556 } 1557 1558 p->flags |= TERMP_NOSPACE; 1559 term_word(p, ")"); 1560 1561 if (pretty) { 1562 p->flags |= TERMP_NOSPACE; 1563 term_word(p, ";"); 1564 } 1565 1566 return(0); 1567 } 1568 1569 1570 /* ARGSUSED */ 1571 static int 1572 termp_fa_pre(DECL_ARGS) 1573 { 1574 const struct mdoc_node *nn; 1575 1576 if (n->parent->tok != MDOC_Fo) { 1577 term_fontpush(p, TERMFONT_UNDER); 1578 return(1); 1579 } 1580 1581 for (nn = n->child; nn; nn = nn->next) { 1582 term_fontpush(p, TERMFONT_UNDER); 1583 term_word(p, nn->string); 1584 term_fontpop(p); 1585 1586 if (nn->next) { 1587 p->flags |= TERMP_NOSPACE; 1588 term_word(p, ","); 1589 } 1590 } 1591 1592 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1593 p->flags |= TERMP_NOSPACE; 1594 term_word(p, ","); 1595 } 1596 1597 return(0); 1598 } 1599 1600 1601 /* ARGSUSED */ 1602 static int 1603 termp_bd_pre(DECL_ARGS) 1604 { 1605 size_t tabwidth, rm, rmax; 1606 struct mdoc_node *nn; 1607 1608 if (MDOC_BLOCK == n->type) { 1609 print_bvspace(p, n, n); 1610 return(1); 1611 } else if (MDOC_HEAD == n->type) 1612 return(0); 1613 1614 if (n->norm->Bd.offs) 1615 p->offset += a2offs(p, n->norm->Bd.offs); 1616 1617 /* 1618 * If -ragged or -filled are specified, the block does nothing 1619 * but change the indentation. If -unfilled or -literal are 1620 * specified, text is printed exactly as entered in the display: 1621 * for macro lines, a newline is appended to the line. Blank 1622 * lines are allowed. 1623 */ 1624 1625 if (DISP_literal != n->norm->Bd.type && 1626 DISP_unfilled != n->norm->Bd.type) 1627 return(1); 1628 1629 tabwidth = p->tabwidth; 1630 if (DISP_literal == n->norm->Bd.type) 1631 p->tabwidth = term_len(p, 8); 1632 1633 rm = p->rmargin; 1634 rmax = p->maxrmargin; 1635 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1636 1637 for (nn = n->child; nn; nn = nn->next) { 1638 print_mdoc_node(p, pair, meta, nn); 1639 /* 1640 * If the printed node flushes its own line, then we 1641 * needn't do it here as well. This is hacky, but the 1642 * notion of selective eoln whitespace is pretty dumb 1643 * anyway, so don't sweat it. 1644 */ 1645 switch (nn->tok) { 1646 case (MDOC_Sm): 1647 /* FALLTHROUGH */ 1648 case (MDOC_br): 1649 /* FALLTHROUGH */ 1650 case (MDOC_sp): 1651 /* FALLTHROUGH */ 1652 case (MDOC_Bl): 1653 /* FALLTHROUGH */ 1654 case (MDOC_D1): 1655 /* FALLTHROUGH */ 1656 case (MDOC_Dl): 1657 /* FALLTHROUGH */ 1658 case (MDOC_Lp): 1659 /* FALLTHROUGH */ 1660 case (MDOC_Pp): 1661 continue; 1662 default: 1663 break; 1664 } 1665 if (nn->next && nn->next->line == nn->line) 1666 continue; 1667 term_flushln(p); 1668 p->flags |= TERMP_NOSPACE; 1669 } 1670 1671 p->tabwidth = tabwidth; 1672 p->rmargin = rm; 1673 p->maxrmargin = rmax; 1674 return(0); 1675 } 1676 1677 1678 /* ARGSUSED */ 1679 static void 1680 termp_bd_post(DECL_ARGS) 1681 { 1682 size_t rm, rmax; 1683 1684 if (MDOC_BODY != n->type) 1685 return; 1686 1687 rm = p->rmargin; 1688 rmax = p->maxrmargin; 1689 1690 if (DISP_literal == n->norm->Bd.type || 1691 DISP_unfilled == n->norm->Bd.type) 1692 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1693 1694 p->flags |= TERMP_NOSPACE; 1695 term_newln(p); 1696 1697 p->rmargin = rm; 1698 p->maxrmargin = rmax; 1699 } 1700 1701 1702 /* ARGSUSED */ 1703 static int 1704 termp_bx_pre(DECL_ARGS) 1705 { 1706 1707 if (NULL != (n = n->child)) { 1708 term_word(p, n->string); 1709 p->flags |= TERMP_NOSPACE; 1710 term_word(p, "BSD"); 1711 } else { 1712 term_word(p, "BSD"); 1713 return(0); 1714 } 1715 1716 if (NULL != (n = n->next)) { 1717 p->flags |= TERMP_NOSPACE; 1718 term_word(p, "-"); 1719 p->flags |= TERMP_NOSPACE; 1720 term_word(p, n->string); 1721 } 1722 1723 return(0); 1724 } 1725 1726 1727 /* ARGSUSED */ 1728 static int 1729 termp_xx_pre(DECL_ARGS) 1730 { 1731 const char *pp; 1732 int flags; 1733 1734 pp = NULL; 1735 switch (n->tok) { 1736 case (MDOC_Bsx): 1737 pp = "BSD/OS"; 1738 break; 1739 case (MDOC_Dx): 1740 pp = "DragonFly"; 1741 break; 1742 case (MDOC_Fx): 1743 pp = "FreeBSD"; 1744 break; 1745 case (MDOC_Nx): 1746 pp = "NetBSD"; 1747 break; 1748 case (MDOC_Ox): 1749 pp = "OpenBSD"; 1750 break; 1751 case (MDOC_Ux): 1752 pp = "UNIX"; 1753 break; 1754 default: 1755 abort(); 1756 /* NOTREACHED */ 1757 } 1758 1759 term_word(p, pp); 1760 if (n->child) { 1761 flags = p->flags; 1762 p->flags |= TERMP_KEEP; 1763 term_word(p, n->child->string); 1764 p->flags = flags; 1765 } 1766 return(0); 1767 } 1768 1769 1770 /* ARGSUSED */ 1771 static int 1772 termp_igndelim_pre(DECL_ARGS) 1773 { 1774 1775 p->flags |= TERMP_IGNDELIM; 1776 return(1); 1777 } 1778 1779 1780 /* ARGSUSED */ 1781 static void 1782 termp_pf_post(DECL_ARGS) 1783 { 1784 1785 p->flags |= TERMP_NOSPACE; 1786 } 1787 1788 1789 /* ARGSUSED */ 1790 static int 1791 termp_ss_pre(DECL_ARGS) 1792 { 1793 1794 switch (n->type) { 1795 case (MDOC_BLOCK): 1796 term_newln(p); 1797 if (n->prev) 1798 term_vspace(p); 1799 break; 1800 case (MDOC_HEAD): 1801 term_fontpush(p, TERMFONT_BOLD); 1802 p->offset = term_len(p, (p->defindent+1)/2); 1803 break; 1804 default: 1805 break; 1806 } 1807 1808 return(1); 1809 } 1810 1811 1812 /* ARGSUSED */ 1813 static void 1814 termp_ss_post(DECL_ARGS) 1815 { 1816 1817 if (MDOC_HEAD == n->type) 1818 term_newln(p); 1819 } 1820 1821 1822 /* ARGSUSED */ 1823 static int 1824 termp_cd_pre(DECL_ARGS) 1825 { 1826 1827 synopsis_pre(p, n); 1828 term_fontpush(p, TERMFONT_BOLD); 1829 return(1); 1830 } 1831 1832 1833 /* ARGSUSED */ 1834 static int 1835 termp_in_pre(DECL_ARGS) 1836 { 1837 1838 synopsis_pre(p, n); 1839 1840 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) { 1841 term_fontpush(p, TERMFONT_BOLD); 1842 term_word(p, "#include"); 1843 term_word(p, "<"); 1844 } else { 1845 term_word(p, "<"); 1846 term_fontpush(p, TERMFONT_UNDER); 1847 } 1848 1849 p->flags |= TERMP_NOSPACE; 1850 return(1); 1851 } 1852 1853 1854 /* ARGSUSED */ 1855 static void 1856 termp_in_post(DECL_ARGS) 1857 { 1858 1859 if (MDOC_SYNPRETTY & n->flags) 1860 term_fontpush(p, TERMFONT_BOLD); 1861 1862 p->flags |= TERMP_NOSPACE; 1863 term_word(p, ">"); 1864 1865 if (MDOC_SYNPRETTY & n->flags) 1866 term_fontpop(p); 1867 } 1868 1869 1870 /* ARGSUSED */ 1871 static int 1872 termp_sp_pre(DECL_ARGS) 1873 { 1874 size_t i, len; 1875 1876 switch (n->tok) { 1877 case (MDOC_sp): 1878 len = n->child ? a2height(p, n->child->string) : 1; 1879 break; 1880 case (MDOC_br): 1881 len = 0; 1882 break; 1883 default: 1884 len = 1; 1885 break; 1886 } 1887 1888 if (0 == len) 1889 term_newln(p); 1890 for (i = 0; i < len; i++) 1891 term_vspace(p); 1892 1893 return(0); 1894 } 1895 1896 1897 /* ARGSUSED */ 1898 static int 1899 termp_quote_pre(DECL_ARGS) 1900 { 1901 1902 if (MDOC_BODY != n->type && MDOC_ELEM != n->type) 1903 return(1); 1904 1905 switch (n->tok) { 1906 case (MDOC_Ao): 1907 /* FALLTHROUGH */ 1908 case (MDOC_Aq): 1909 term_word(p, "<"); 1910 break; 1911 case (MDOC_Bro): 1912 /* FALLTHROUGH */ 1913 case (MDOC_Brq): 1914 term_word(p, "{"); 1915 break; 1916 case (MDOC_Oo): 1917 /* FALLTHROUGH */ 1918 case (MDOC_Op): 1919 /* FALLTHROUGH */ 1920 case (MDOC_Bo): 1921 /* FALLTHROUGH */ 1922 case (MDOC_Bq): 1923 term_word(p, "["); 1924 break; 1925 case (MDOC_Do): 1926 /* FALLTHROUGH */ 1927 case (MDOC_Dq): 1928 term_word(p, "\\(lq"); 1929 break; 1930 case (MDOC_Eo): 1931 break; 1932 case (MDOC_Po): 1933 /* FALLTHROUGH */ 1934 case (MDOC_Pq): 1935 term_word(p, "("); 1936 break; 1937 case (MDOC__T): 1938 /* FALLTHROUGH */ 1939 case (MDOC_Qo): 1940 /* FALLTHROUGH */ 1941 case (MDOC_Qq): 1942 term_word(p, "\""); 1943 break; 1944 case (MDOC_Ql): 1945 /* FALLTHROUGH */ 1946 case (MDOC_So): 1947 /* FALLTHROUGH */ 1948 case (MDOC_Sq): 1949 term_word(p, "\\(oq"); 1950 break; 1951 default: 1952 abort(); 1953 /* NOTREACHED */ 1954 } 1955 1956 p->flags |= TERMP_NOSPACE; 1957 return(1); 1958 } 1959 1960 1961 /* ARGSUSED */ 1962 static void 1963 termp_quote_post(DECL_ARGS) 1964 { 1965 1966 if (MDOC_BODY != n->type && MDOC_ELEM != n->type) 1967 return; 1968 1969 p->flags |= TERMP_NOSPACE; 1970 1971 switch (n->tok) { 1972 case (MDOC_Ao): 1973 /* FALLTHROUGH */ 1974 case (MDOC_Aq): 1975 term_word(p, ">"); 1976 break; 1977 case (MDOC_Bro): 1978 /* FALLTHROUGH */ 1979 case (MDOC_Brq): 1980 term_word(p, "}"); 1981 break; 1982 case (MDOC_Oo): 1983 /* FALLTHROUGH */ 1984 case (MDOC_Op): 1985 /* FALLTHROUGH */ 1986 case (MDOC_Bo): 1987 /* FALLTHROUGH */ 1988 case (MDOC_Bq): 1989 term_word(p, "]"); 1990 break; 1991 case (MDOC_Do): 1992 /* FALLTHROUGH */ 1993 case (MDOC_Dq): 1994 term_word(p, "\\(rq"); 1995 break; 1996 case (MDOC_Eo): 1997 break; 1998 case (MDOC_Po): 1999 /* FALLTHROUGH */ 2000 case (MDOC_Pq): 2001 term_word(p, ")"); 2002 break; 2003 case (MDOC__T): 2004 /* FALLTHROUGH */ 2005 case (MDOC_Qo): 2006 /* FALLTHROUGH */ 2007 case (MDOC_Qq): 2008 term_word(p, "\""); 2009 break; 2010 case (MDOC_Ql): 2011 /* FALLTHROUGH */ 2012 case (MDOC_So): 2013 /* FALLTHROUGH */ 2014 case (MDOC_Sq): 2015 term_word(p, "\\(cq"); 2016 break; 2017 default: 2018 abort(); 2019 /* NOTREACHED */ 2020 } 2021 } 2022 2023 2024 /* ARGSUSED */ 2025 static int 2026 termp_fo_pre(DECL_ARGS) 2027 { 2028 2029 if (MDOC_BLOCK == n->type) { 2030 synopsis_pre(p, n); 2031 return(1); 2032 } else if (MDOC_BODY == n->type) { 2033 p->flags |= TERMP_NOSPACE; 2034 term_word(p, "("); 2035 p->flags |= TERMP_NOSPACE; 2036 return(1); 2037 } 2038 2039 if (NULL == n->child) 2040 return(0); 2041 2042 /* XXX: we drop non-initial arguments as per groff. */ 2043 2044 assert(n->child->string); 2045 term_fontpush(p, TERMFONT_BOLD); 2046 term_word(p, n->child->string); 2047 return(0); 2048 } 2049 2050 2051 /* ARGSUSED */ 2052 static void 2053 termp_fo_post(DECL_ARGS) 2054 { 2055 2056 if (MDOC_BODY != n->type) 2057 return; 2058 2059 p->flags |= TERMP_NOSPACE; 2060 term_word(p, ")"); 2061 2062 if (MDOC_SYNPRETTY & n->flags) { 2063 p->flags |= TERMP_NOSPACE; 2064 term_word(p, ";"); 2065 } 2066 } 2067 2068 2069 /* ARGSUSED */ 2070 static int 2071 termp_bf_pre(DECL_ARGS) 2072 { 2073 2074 if (MDOC_HEAD == n->type) 2075 return(0); 2076 else if (MDOC_BODY != n->type) 2077 return(1); 2078 2079 if (FONT_Em == n->norm->Bf.font) 2080 term_fontpush(p, TERMFONT_UNDER); 2081 else if (FONT_Sy == n->norm->Bf.font) 2082 term_fontpush(p, TERMFONT_BOLD); 2083 else 2084 term_fontpush(p, TERMFONT_NONE); 2085 2086 return(1); 2087 } 2088 2089 2090 /* ARGSUSED */ 2091 static int 2092 termp_sm_pre(DECL_ARGS) 2093 { 2094 2095 assert(n->child && MDOC_TEXT == n->child->type); 2096 if (0 == strcmp("on", n->child->string)) { 2097 if (p->col) 2098 p->flags &= ~TERMP_NOSPACE; 2099 p->flags &= ~TERMP_NONOSPACE; 2100 } else 2101 p->flags |= TERMP_NONOSPACE; 2102 2103 return(0); 2104 } 2105 2106 2107 /* ARGSUSED */ 2108 static int 2109 termp_ap_pre(DECL_ARGS) 2110 { 2111 2112 p->flags |= TERMP_NOSPACE; 2113 term_word(p, "'"); 2114 p->flags |= TERMP_NOSPACE; 2115 return(1); 2116 } 2117 2118 2119 /* ARGSUSED */ 2120 static void 2121 termp____post(DECL_ARGS) 2122 { 2123 2124 /* 2125 * Handle lists of authors. In general, print each followed by 2126 * a comma. Don't print the comma if there are only two 2127 * authors. 2128 */ 2129 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 2130 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 2131 if (NULL == n->prev || MDOC__A != n->prev->tok) 2132 return; 2133 2134 /* TODO: %U. */ 2135 2136 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 2137 return; 2138 2139 p->flags |= TERMP_NOSPACE; 2140 if (NULL == n->next) { 2141 term_word(p, "."); 2142 p->flags |= TERMP_SENTENCE; 2143 } else 2144 term_word(p, ","); 2145 } 2146 2147 2148 /* ARGSUSED */ 2149 static int 2150 termp_li_pre(DECL_ARGS) 2151 { 2152 2153 term_fontpush(p, TERMFONT_NONE); 2154 return(1); 2155 } 2156 2157 2158 /* ARGSUSED */ 2159 static int 2160 termp_lk_pre(DECL_ARGS) 2161 { 2162 const struct mdoc_node *link, *descr; 2163 2164 if (NULL == (link = n->child)) 2165 return(0); 2166 2167 if (NULL != (descr = link->next)) { 2168 term_fontpush(p, TERMFONT_UNDER); 2169 while (NULL != descr) { 2170 term_word(p, descr->string); 2171 descr = descr->next; 2172 } 2173 p->flags |= TERMP_NOSPACE; 2174 term_word(p, ":"); 2175 term_fontpop(p); 2176 } 2177 2178 term_fontpush(p, TERMFONT_BOLD); 2179 term_word(p, link->string); 2180 term_fontpop(p); 2181 2182 return(0); 2183 } 2184 2185 2186 /* ARGSUSED */ 2187 static int 2188 termp_bk_pre(DECL_ARGS) 2189 { 2190 2191 switch (n->type) { 2192 case (MDOC_BLOCK): 2193 break; 2194 case (MDOC_HEAD): 2195 return(0); 2196 case (MDOC_BODY): 2197 if (n->parent->args || 0 == n->prev->nchild) 2198 p->flags |= TERMP_PREKEEP; 2199 break; 2200 default: 2201 abort(); 2202 /* NOTREACHED */ 2203 } 2204 2205 return(1); 2206 } 2207 2208 2209 /* ARGSUSED */ 2210 static void 2211 termp_bk_post(DECL_ARGS) 2212 { 2213 2214 if (MDOC_BODY == n->type && ! (MDOC_SYNPRETTY & n->flags)) 2215 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 2216 } 2217 2218 /* ARGSUSED */ 2219 static void 2220 termp__t_post(DECL_ARGS) 2221 { 2222 2223 /* 2224 * If we're in an `Rs' and there's a journal present, then quote 2225 * us instead of underlining us (for disambiguation). 2226 */ 2227 if (n->parent && MDOC_Rs == n->parent->tok && 2228 n->parent->norm->Rs.quote_T) 2229 termp_quote_post(p, pair, meta, n); 2230 2231 termp____post(p, pair, meta, n); 2232 } 2233 2234 /* ARGSUSED */ 2235 static int 2236 termp__t_pre(DECL_ARGS) 2237 { 2238 2239 /* 2240 * If we're in an `Rs' and there's a journal present, then quote 2241 * us instead of underlining us (for disambiguation). 2242 */ 2243 if (n->parent && MDOC_Rs == n->parent->tok && 2244 n->parent->norm->Rs.quote_T) 2245 return(termp_quote_pre(p, pair, meta, n)); 2246 2247 term_fontpush(p, TERMFONT_UNDER); 2248 return(1); 2249 } 2250 2251 /* ARGSUSED */ 2252 static int 2253 termp_under_pre(DECL_ARGS) 2254 { 2255 2256 term_fontpush(p, TERMFONT_UNDER); 2257 return(1); 2258 } 2259