1 /* $Id: mdoc_term.c,v 1.134 2011/07/05 04:12:41 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #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 #define INDENT 5 34 #define HALFINDENT 3 35 36 struct termpair { 37 struct termpair *ppair; 38 int count; 39 }; 40 41 #define DECL_ARGS struct termp *p, \ 42 struct termpair *pair, \ 43 const struct mdoc_meta *m, \ 44 const struct mdoc_node *n 45 46 struct termact { 47 int (*pre)(DECL_ARGS); 48 void (*post)(DECL_ARGS); 49 }; 50 51 static size_t a2width(const struct termp *, const char *); 52 static size_t a2height(const struct termp *, const char *); 53 static size_t a2offs(const struct termp *, const char *); 54 55 static void print_bvspace(struct termp *, 56 const struct mdoc_node *, 57 const struct mdoc_node *); 58 static void print_mdoc_node(DECL_ARGS); 59 static void print_mdoc_nodelist(DECL_ARGS); 60 static void print_mdoc_head(struct termp *, const void *); 61 static void print_mdoc_foot(struct termp *, const void *); 62 static void synopsis_pre(struct termp *, 63 const struct mdoc_node *); 64 65 static void termp____post(DECL_ARGS); 66 static void termp__t_post(DECL_ARGS); 67 static void termp_an_post(DECL_ARGS); 68 static void termp_bd_post(DECL_ARGS); 69 static void termp_bk_post(DECL_ARGS); 70 static void termp_bl_post(DECL_ARGS); 71 static void termp_d1_post(DECL_ARGS); 72 static void termp_fo_post(DECL_ARGS); 73 static void termp_in_post(DECL_ARGS); 74 static void termp_it_post(DECL_ARGS); 75 static void termp_lb_post(DECL_ARGS); 76 static void termp_nm_post(DECL_ARGS); 77 static void termp_pf_post(DECL_ARGS); 78 static void termp_quote_post(DECL_ARGS); 79 static void termp_sh_post(DECL_ARGS); 80 static void termp_ss_post(DECL_ARGS); 81 82 static int termp__a_pre(DECL_ARGS); 83 static int termp__t_pre(DECL_ARGS); 84 static int termp_an_pre(DECL_ARGS); 85 static int termp_ap_pre(DECL_ARGS); 86 static int termp_bd_pre(DECL_ARGS); 87 static int termp_bf_pre(DECL_ARGS); 88 static int termp_bk_pre(DECL_ARGS); 89 static int termp_bl_pre(DECL_ARGS); 90 static int termp_bold_pre(DECL_ARGS); 91 static int termp_bt_pre(DECL_ARGS); 92 static int termp_bx_pre(DECL_ARGS); 93 static int termp_cd_pre(DECL_ARGS); 94 static int termp_d1_pre(DECL_ARGS); 95 static int termp_ex_pre(DECL_ARGS); 96 static int termp_fa_pre(DECL_ARGS); 97 static int termp_fd_pre(DECL_ARGS); 98 static int termp_fl_pre(DECL_ARGS); 99 static int termp_fn_pre(DECL_ARGS); 100 static int termp_fo_pre(DECL_ARGS); 101 static int termp_ft_pre(DECL_ARGS); 102 static int termp_igndelim_pre(DECL_ARGS); 103 static int termp_in_pre(DECL_ARGS); 104 static int termp_it_pre(DECL_ARGS); 105 static int termp_li_pre(DECL_ARGS); 106 static int termp_lk_pre(DECL_ARGS); 107 static int termp_nd_pre(DECL_ARGS); 108 static int termp_nm_pre(DECL_ARGS); 109 static int termp_ns_pre(DECL_ARGS); 110 static int termp_quote_pre(DECL_ARGS); 111 static int termp_rs_pre(DECL_ARGS); 112 static int termp_rv_pre(DECL_ARGS); 113 static int termp_sh_pre(DECL_ARGS); 114 static int termp_sm_pre(DECL_ARGS); 115 static int termp_sp_pre(DECL_ARGS); 116 static int termp_ss_pre(DECL_ARGS); 117 static int termp_under_pre(DECL_ARGS); 118 static int termp_ud_pre(DECL_ARGS); 119 static int termp_vt_pre(DECL_ARGS); 120 static int termp_xr_pre(DECL_ARGS); 121 static int termp_xx_pre(DECL_ARGS); 122 123 static const struct termact termacts[MDOC_MAX] = { 124 { termp_ap_pre, NULL }, /* Ap */ 125 { NULL, NULL }, /* Dd */ 126 { NULL, NULL }, /* Dt */ 127 { NULL, NULL }, /* Os */ 128 { termp_sh_pre, termp_sh_post }, /* Sh */ 129 { termp_ss_pre, termp_ss_post }, /* Ss */ 130 { termp_sp_pre, NULL }, /* Pp */ 131 { termp_d1_pre, termp_d1_post }, /* D1 */ 132 { termp_d1_pre, termp_d1_post }, /* Dl */ 133 { termp_bd_pre, termp_bd_post }, /* Bd */ 134 { NULL, NULL }, /* Ed */ 135 { termp_bl_pre, termp_bl_post }, /* Bl */ 136 { NULL, NULL }, /* El */ 137 { termp_it_pre, termp_it_post }, /* It */ 138 { termp_under_pre, NULL }, /* Ad */ 139 { termp_an_pre, termp_an_post }, /* An */ 140 { termp_under_pre, NULL }, /* Ar */ 141 { termp_cd_pre, NULL }, /* Cd */ 142 { termp_bold_pre, NULL }, /* Cm */ 143 { NULL, NULL }, /* Dv */ 144 { NULL, NULL }, /* Er */ 145 { NULL, NULL }, /* Ev */ 146 { termp_ex_pre, NULL }, /* Ex */ 147 { termp_fa_pre, NULL }, /* Fa */ 148 { termp_fd_pre, NULL }, /* Fd */ 149 { termp_fl_pre, NULL }, /* Fl */ 150 { termp_fn_pre, NULL }, /* Fn */ 151 { termp_ft_pre, NULL }, /* Ft */ 152 { termp_bold_pre, NULL }, /* Ic */ 153 { termp_in_pre, termp_in_post }, /* In */ 154 { termp_li_pre, NULL }, /* Li */ 155 { termp_nd_pre, NULL }, /* Nd */ 156 { termp_nm_pre, termp_nm_post }, /* Nm */ 157 { termp_quote_pre, termp_quote_post }, /* Op */ 158 { NULL, NULL }, /* Ot */ 159 { termp_under_pre, NULL }, /* Pa */ 160 { termp_rv_pre, NULL }, /* Rv */ 161 { NULL, NULL }, /* St */ 162 { termp_under_pre, NULL }, /* Va */ 163 { termp_vt_pre, NULL }, /* Vt */ 164 { termp_xr_pre, NULL }, /* Xr */ 165 { termp__a_pre, termp____post }, /* %A */ 166 { termp_under_pre, termp____post }, /* %B */ 167 { NULL, termp____post }, /* %D */ 168 { termp_under_pre, termp____post }, /* %I */ 169 { termp_under_pre, termp____post }, /* %J */ 170 { NULL, termp____post }, /* %N */ 171 { NULL, termp____post }, /* %O */ 172 { NULL, termp____post }, /* %P */ 173 { NULL, termp____post }, /* %R */ 174 { termp__t_pre, termp__t_post }, /* %T */ 175 { NULL, termp____post }, /* %V */ 176 { NULL, NULL }, /* Ac */ 177 { termp_quote_pre, termp_quote_post }, /* Ao */ 178 { termp_quote_pre, termp_quote_post }, /* Aq */ 179 { NULL, NULL }, /* At */ 180 { NULL, NULL }, /* Bc */ 181 { termp_bf_pre, NULL }, /* Bf */ 182 { termp_quote_pre, termp_quote_post }, /* Bo */ 183 { termp_quote_pre, termp_quote_post }, /* Bq */ 184 { termp_xx_pre, NULL }, /* Bsx */ 185 { termp_bx_pre, NULL }, /* Bx */ 186 { NULL, NULL }, /* Db */ 187 { NULL, NULL }, /* Dc */ 188 { termp_quote_pre, termp_quote_post }, /* Do */ 189 { termp_quote_pre, termp_quote_post }, /* Dq */ 190 { NULL, NULL }, /* Ec */ /* FIXME: no space */ 191 { NULL, NULL }, /* Ef */ 192 { termp_under_pre, NULL }, /* Em */ 193 { NULL, NULL }, /* Eo */ 194 { termp_xx_pre, NULL }, /* Fx */ 195 { termp_bold_pre, NULL }, /* Ms */ 196 { termp_igndelim_pre, NULL }, /* No */ 197 { termp_ns_pre, NULL }, /* Ns */ 198 { termp_xx_pre, NULL }, /* Nx */ 199 { termp_xx_pre, NULL }, /* Ox */ 200 { NULL, NULL }, /* Pc */ 201 { termp_igndelim_pre, termp_pf_post }, /* Pf */ 202 { termp_quote_pre, termp_quote_post }, /* Po */ 203 { termp_quote_pre, termp_quote_post }, /* Pq */ 204 { NULL, NULL }, /* Qc */ 205 { termp_quote_pre, termp_quote_post }, /* Ql */ 206 { termp_quote_pre, termp_quote_post }, /* Qo */ 207 { termp_quote_pre, termp_quote_post }, /* Qq */ 208 { NULL, NULL }, /* Re */ 209 { termp_rs_pre, NULL }, /* Rs */ 210 { NULL, NULL }, /* Sc */ 211 { termp_quote_pre, termp_quote_post }, /* So */ 212 { termp_quote_pre, termp_quote_post }, /* Sq */ 213 { termp_sm_pre, NULL }, /* Sm */ 214 { termp_under_pre, NULL }, /* Sx */ 215 { termp_bold_pre, NULL }, /* Sy */ 216 { NULL, NULL }, /* Tn */ 217 { termp_xx_pre, NULL }, /* Ux */ 218 { NULL, NULL }, /* Xc */ 219 { NULL, NULL }, /* Xo */ 220 { termp_fo_pre, termp_fo_post }, /* Fo */ 221 { NULL, NULL }, /* Fc */ 222 { termp_quote_pre, termp_quote_post }, /* Oo */ 223 { NULL, NULL }, /* Oc */ 224 { termp_bk_pre, termp_bk_post }, /* Bk */ 225 { NULL, NULL }, /* Ek */ 226 { termp_bt_pre, NULL }, /* Bt */ 227 { NULL, NULL }, /* Hf */ 228 { NULL, NULL }, /* Fr */ 229 { termp_ud_pre, NULL }, /* Ud */ 230 { NULL, termp_lb_post }, /* Lb */ 231 { termp_sp_pre, NULL }, /* Lp */ 232 { termp_lk_pre, NULL }, /* Lk */ 233 { termp_under_pre, NULL }, /* Mt */ 234 { termp_quote_pre, termp_quote_post }, /* Brq */ 235 { termp_quote_pre, termp_quote_post }, /* Bro */ 236 { NULL, NULL }, /* Brc */ 237 { NULL, termp____post }, /* %C */ 238 { NULL, NULL }, /* Es */ /* TODO */ 239 { NULL, NULL }, /* En */ /* TODO */ 240 { termp_xx_pre, NULL }, /* Dx */ 241 { NULL, termp____post }, /* %Q */ 242 { termp_sp_pre, NULL }, /* br */ 243 { termp_sp_pre, NULL }, /* sp */ 244 { termp_under_pre, termp____post }, /* %U */ 245 { NULL, NULL }, /* Ta */ 246 }; 247 248 249 void 250 terminal_mdoc(void *arg, const struct mdoc *mdoc) 251 { 252 const struct mdoc_node *n; 253 const struct mdoc_meta *m; 254 struct termp *p; 255 256 p = (struct termp *)arg; 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_word(p, n->eqn->data); 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_NOLPAD | 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_NOLPAD | 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 const struct mdoc_meta *m; 458 459 m = (const struct mdoc_meta *)arg; 460 461 p->rmargin = p->maxrmargin; 462 p->offset = 0; 463 464 /* 465 * The header is strange. It has three components, which are 466 * really two with the first duplicated. It goes like this: 467 * 468 * IDENTIFIER TITLE IDENTIFIER 469 * 470 * The IDENTIFIER is NAME(SECTION), which is the command-name 471 * (if given, or "unknown" if not) followed by the manual page 472 * section. These are given in `Dt'. The TITLE is a free-form 473 * string depending on the manual volume. If not specified, it 474 * switches on the manual section. 475 */ 476 477 assert(m->vol); 478 strlcpy(buf, m->vol, BUFSIZ); 479 480 if (m->arch) { 481 strlcat(buf, " (", BUFSIZ); 482 strlcat(buf, m->arch, BUFSIZ); 483 strlcat(buf, ")", BUFSIZ); 484 } 485 486 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); 487 488 p->offset = 0; 489 p->rmargin = (p->maxrmargin - 490 term_strlen(p, buf) + term_len(p, 1)) / 2; 491 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 492 493 term_word(p, title); 494 term_flushln(p); 495 496 p->offset = p->rmargin; 497 p->rmargin = p->maxrmargin - term_strlen(p, title); 498 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 499 500 term_word(p, buf); 501 term_flushln(p); 502 503 p->offset = p->rmargin; 504 p->rmargin = p->maxrmargin; 505 p->flags &= ~TERMP_NOBREAK; 506 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 507 508 term_word(p, title); 509 term_flushln(p); 510 511 p->offset = 0; 512 p->rmargin = p->maxrmargin; 513 p->flags &= ~TERMP_NOSPACE; 514 } 515 516 517 static size_t 518 a2height(const struct termp *p, const char *v) 519 { 520 struct roffsu su; 521 522 523 assert(v); 524 if ( ! a2roffsu(v, &su, SCALE_VS)) 525 SCALE_VS_INIT(&su, atoi(v)); 526 527 return(term_vspan(p, &su)); 528 } 529 530 531 static size_t 532 a2width(const struct termp *p, const char *v) 533 { 534 struct roffsu su; 535 536 assert(v); 537 if ( ! a2roffsu(v, &su, SCALE_MAX)) 538 SCALE_HS_INIT(&su, term_strlen(p, v)); 539 540 return(term_hspan(p, &su)); 541 } 542 543 544 static size_t 545 a2offs(const struct termp *p, const char *v) 546 { 547 struct roffsu su; 548 549 if ('\0' == *v) 550 return(0); 551 else if (0 == strcmp(v, "left")) 552 return(0); 553 else if (0 == strcmp(v, "indent")) 554 return(term_len(p, INDENT + 1)); 555 else if (0 == strcmp(v, "indent-two")) 556 return(term_len(p, (INDENT + 1) * 2)); 557 else if ( ! a2roffsu(v, &su, SCALE_MAX)) 558 SCALE_HS_INIT(&su, term_strlen(p, v)); 559 560 return(term_hspan(p, &su)); 561 } 562 563 564 /* 565 * Determine how much space to print out before block elements of `It' 566 * (and thus `Bl') and `Bd'. And then go ahead and print that space, 567 * too. 568 */ 569 static void 570 print_bvspace(struct termp *p, 571 const struct mdoc_node *bl, 572 const struct mdoc_node *n) 573 { 574 const struct mdoc_node *nn; 575 576 term_newln(p); 577 578 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) 579 return; 580 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) 581 return; 582 583 /* Do not vspace directly after Ss/Sh. */ 584 585 for (nn = n; nn; nn = nn->parent) { 586 if (MDOC_BLOCK != nn->type) 587 continue; 588 if (MDOC_Ss == nn->tok) 589 return; 590 if (MDOC_Sh == nn->tok) 591 return; 592 if (NULL == nn->prev) 593 continue; 594 break; 595 } 596 597 /* A `-column' does not assert vspace within the list. */ 598 599 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) 600 if (n->prev && MDOC_It == n->prev->tok) 601 return; 602 603 /* A `-diag' without body does not vspace. */ 604 605 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) 606 if (n->prev && MDOC_It == n->prev->tok) { 607 assert(n->prev->body); 608 if (NULL == n->prev->body->child) 609 return; 610 } 611 612 term_vspace(p); 613 } 614 615 616 /* ARGSUSED */ 617 static int 618 termp_it_pre(DECL_ARGS) 619 { 620 const struct mdoc_node *bl, *nn; 621 char buf[7]; 622 int i; 623 size_t width, offset, ncols, dcol; 624 enum mdoc_list type; 625 626 if (MDOC_BLOCK == n->type) { 627 print_bvspace(p, n->parent->parent, n); 628 return(1); 629 } 630 631 bl = n->parent->parent->parent; 632 type = bl->norm->Bl.type; 633 634 /* 635 * First calculate width and offset. This is pretty easy unless 636 * we're a -column list, in which case all prior columns must 637 * be accounted for. 638 */ 639 640 width = offset = 0; 641 642 if (bl->norm->Bl.offs) 643 offset = a2offs(p, bl->norm->Bl.offs); 644 645 switch (type) { 646 case (LIST_column): 647 if (MDOC_HEAD == n->type) 648 break; 649 650 /* 651 * Imitate groff's column handling: 652 * - For each earlier column, add its width. 653 * - For less than 5 columns, add four more blanks per 654 * column. 655 * - For exactly 5 columns, add three more blank per 656 * column. 657 * - For more than 5 columns, add only one column. 658 */ 659 ncols = bl->norm->Bl.ncols; 660 661 /* LINTED */ 662 dcol = ncols < 5 ? term_len(p, 4) : 663 ncols == 5 ? term_len(p, 3) : term_len(p, 1); 664 665 /* 666 * Calculate the offset by applying all prior MDOC_BODY, 667 * so we stop at the MDOC_HEAD (NULL == nn->prev). 668 */ 669 670 for (i = 0, nn = n->prev; 671 nn->prev && i < (int)ncols; 672 nn = nn->prev, i++) 673 offset += dcol + a2width 674 (p, bl->norm->Bl.cols[i]); 675 676 /* 677 * When exceeding the declared number of columns, leave 678 * the remaining widths at 0. This will later be 679 * adjusted to the default width of 10, or, for the last 680 * column, stretched to the right margin. 681 */ 682 if (i >= (int)ncols) 683 break; 684 685 /* 686 * Use the declared column widths, extended as explained 687 * in the preceding paragraph. 688 */ 689 width = a2width(p, bl->norm->Bl.cols[i]) + dcol; 690 break; 691 default: 692 if (NULL == bl->norm->Bl.width) 693 break; 694 695 /* 696 * Note: buffer the width by 2, which is groff's magic 697 * number for buffering single arguments. See the above 698 * handling for column for how this changes. 699 */ 700 assert(bl->norm->Bl.width); 701 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); 702 break; 703 } 704 705 /* 706 * List-type can override the width in the case of fixed-head 707 * values (bullet, dash/hyphen, enum). Tags need a non-zero 708 * offset. 709 */ 710 711 switch (type) { 712 case (LIST_bullet): 713 /* FALLTHROUGH */ 714 case (LIST_dash): 715 /* FALLTHROUGH */ 716 case (LIST_hyphen): 717 if (width < term_len(p, 4)) 718 width = term_len(p, 4); 719 break; 720 case (LIST_enum): 721 if (width < term_len(p, 5)) 722 width = term_len(p, 5); 723 break; 724 case (LIST_hang): 725 if (0 == width) 726 width = term_len(p, 8); 727 break; 728 case (LIST_column): 729 /* FALLTHROUGH */ 730 case (LIST_tag): 731 if (0 == width) 732 width = term_len(p, 10); 733 break; 734 default: 735 break; 736 } 737 738 /* 739 * Whitespace control. Inset bodies need an initial space, 740 * while diagonal bodies need two. 741 */ 742 743 p->flags |= TERMP_NOSPACE; 744 745 switch (type) { 746 case (LIST_diag): 747 if (MDOC_BODY == n->type) 748 term_word(p, "\\ \\ "); 749 break; 750 case (LIST_inset): 751 if (MDOC_BODY == n->type) 752 term_word(p, "\\ "); 753 break; 754 default: 755 break; 756 } 757 758 p->flags |= TERMP_NOSPACE; 759 760 switch (type) { 761 case (LIST_diag): 762 if (MDOC_HEAD == n->type) 763 term_fontpush(p, TERMFONT_BOLD); 764 break; 765 default: 766 break; 767 } 768 769 /* 770 * Pad and break control. This is the tricky part. These flags 771 * are documented in term_flushln() in term.c. Note that we're 772 * going to unset all of these flags in termp_it_post() when we 773 * exit. 774 */ 775 776 switch (type) { 777 case (LIST_bullet): 778 /* FALLTHROUGH */ 779 case (LIST_dash): 780 /* FALLTHROUGH */ 781 case (LIST_enum): 782 /* FALLTHROUGH */ 783 case (LIST_hyphen): 784 if (MDOC_HEAD == n->type) 785 p->flags |= TERMP_NOBREAK; 786 else 787 p->flags |= TERMP_NOLPAD; 788 break; 789 case (LIST_hang): 790 if (MDOC_HEAD == n->type) 791 p->flags |= TERMP_NOBREAK; 792 else 793 p->flags |= TERMP_NOLPAD; 794 795 if (MDOC_HEAD != n->type) 796 break; 797 798 /* 799 * This is ugly. If `-hang' is specified and the body 800 * is a `Bl' or `Bd', then we want basically to nullify 801 * the "overstep" effect in term_flushln() and treat 802 * this as a `-ohang' list instead. 803 */ 804 if (n->next->child && 805 (MDOC_Bl == n->next->child->tok || 806 MDOC_Bd == n->next->child->tok)) { 807 p->flags &= ~TERMP_NOBREAK; 808 p->flags &= ~TERMP_NOLPAD; 809 } else 810 p->flags |= TERMP_HANG; 811 break; 812 case (LIST_tag): 813 if (MDOC_HEAD == n->type) 814 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE; 815 else 816 p->flags |= TERMP_NOLPAD; 817 818 if (MDOC_HEAD != n->type) 819 break; 820 if (NULL == n->next || NULL == n->next->child) 821 p->flags |= TERMP_DANGLE; 822 break; 823 case (LIST_column): 824 if (MDOC_HEAD == n->type) 825 break; 826 827 if (NULL == n->next) 828 p->flags &= ~TERMP_NOBREAK; 829 else 830 p->flags |= TERMP_NOBREAK; 831 832 assert(n->prev); 833 if (MDOC_BODY == n->prev->type) 834 p->flags |= TERMP_NOLPAD; 835 836 break; 837 case (LIST_diag): 838 if (MDOC_HEAD == n->type) 839 p->flags |= TERMP_NOBREAK; 840 break; 841 default: 842 break; 843 } 844 845 /* 846 * Margin control. Set-head-width lists have their right 847 * margins shortened. The body for these lists has the offset 848 * necessarily lengthened. Everybody gets the offset. 849 */ 850 851 p->offset += offset; 852 853 switch (type) { 854 case (LIST_hang): 855 /* 856 * Same stipulation as above, regarding `-hang'. We 857 * don't want to recalculate rmargin and offsets when 858 * using `Bd' or `Bl' within `-hang' overstep lists. 859 */ 860 if (MDOC_HEAD == n->type && n->next->child && 861 (MDOC_Bl == n->next->child->tok || 862 MDOC_Bd == n->next->child->tok)) 863 break; 864 /* FALLTHROUGH */ 865 case (LIST_bullet): 866 /* FALLTHROUGH */ 867 case (LIST_dash): 868 /* FALLTHROUGH */ 869 case (LIST_enum): 870 /* FALLTHROUGH */ 871 case (LIST_hyphen): 872 /* FALLTHROUGH */ 873 case (LIST_tag): 874 assert(width); 875 if (MDOC_HEAD == n->type) 876 p->rmargin = p->offset + width; 877 else 878 p->offset += width; 879 break; 880 case (LIST_column): 881 assert(width); 882 p->rmargin = p->offset + width; 883 /* 884 * XXX - this behaviour is not documented: the 885 * right-most column is filled to the right margin. 886 */ 887 if (MDOC_HEAD == n->type) 888 break; 889 if (NULL == n->next && p->rmargin < p->maxrmargin) 890 p->rmargin = p->maxrmargin; 891 break; 892 default: 893 break; 894 } 895 896 /* 897 * The dash, hyphen, bullet and enum lists all have a special 898 * HEAD character (temporarily bold, in some cases). 899 */ 900 901 if (MDOC_HEAD == n->type) 902 switch (type) { 903 case (LIST_bullet): 904 term_fontpush(p, TERMFONT_BOLD); 905 term_word(p, "\\[bu]"); 906 term_fontpop(p); 907 break; 908 case (LIST_dash): 909 /* FALLTHROUGH */ 910 case (LIST_hyphen): 911 term_fontpush(p, TERMFONT_BOLD); 912 term_word(p, "\\(hy"); 913 term_fontpop(p); 914 break; 915 case (LIST_enum): 916 (pair->ppair->ppair->count)++; 917 snprintf(buf, sizeof(buf), "%d.", 918 pair->ppair->ppair->count); 919 term_word(p, buf); 920 break; 921 default: 922 break; 923 } 924 925 /* 926 * If we're not going to process our children, indicate so here. 927 */ 928 929 switch (type) { 930 case (LIST_bullet): 931 /* FALLTHROUGH */ 932 case (LIST_item): 933 /* FALLTHROUGH */ 934 case (LIST_dash): 935 /* FALLTHROUGH */ 936 case (LIST_hyphen): 937 /* FALLTHROUGH */ 938 case (LIST_enum): 939 if (MDOC_HEAD == n->type) 940 return(0); 941 break; 942 case (LIST_column): 943 if (MDOC_HEAD == n->type) 944 return(0); 945 break; 946 default: 947 break; 948 } 949 950 return(1); 951 } 952 953 954 /* ARGSUSED */ 955 static void 956 termp_it_post(DECL_ARGS) 957 { 958 enum mdoc_list type; 959 960 if (MDOC_BLOCK == n->type) 961 return; 962 963 type = n->parent->parent->parent->norm->Bl.type; 964 965 switch (type) { 966 case (LIST_item): 967 /* FALLTHROUGH */ 968 case (LIST_diag): 969 /* FALLTHROUGH */ 970 case (LIST_inset): 971 if (MDOC_BODY == n->type) 972 term_newln(p); 973 break; 974 case (LIST_column): 975 if (MDOC_BODY == n->type) 976 term_flushln(p); 977 break; 978 default: 979 term_newln(p); 980 break; 981 } 982 983 /* 984 * Now that our output is flushed, we can reset our tags. Since 985 * only `It' sets these flags, we're free to assume that nobody 986 * has munged them in the meanwhile. 987 */ 988 989 p->flags &= ~TERMP_DANGLE; 990 p->flags &= ~TERMP_NOBREAK; 991 p->flags &= ~TERMP_TWOSPACE; 992 p->flags &= ~TERMP_NOLPAD; 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_NOLPAD | 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 p->flags &= ~TERMP_NOLPAD; 1056 } 1057 } 1058 1059 1060 /* ARGSUSED */ 1061 static int 1062 termp_fl_pre(DECL_ARGS) 1063 { 1064 1065 term_fontpush(p, TERMFONT_BOLD); 1066 term_word(p, "\\-"); 1067 1068 if (n->child) 1069 p->flags |= TERMP_NOSPACE; 1070 else if (n->next && n->next->line == n->line) 1071 p->flags |= TERMP_NOSPACE; 1072 1073 return(1); 1074 } 1075 1076 1077 /* ARGSUSED */ 1078 static int 1079 termp__a_pre(DECL_ARGS) 1080 { 1081 1082 if (n->prev && MDOC__A == n->prev->tok) 1083 if (NULL == n->next || MDOC__A != n->next->tok) 1084 term_word(p, "and"); 1085 1086 return(1); 1087 } 1088 1089 1090 /* ARGSUSED */ 1091 static int 1092 termp_an_pre(DECL_ARGS) 1093 { 1094 1095 if (NULL == n->child) 1096 return(1); 1097 1098 /* 1099 * If not in the AUTHORS section, `An -split' will cause 1100 * newlines to occur before the author name. If in the AUTHORS 1101 * section, by default, the first `An' invocation is nosplit, 1102 * then all subsequent ones, regardless of whether interspersed 1103 * with other macros/text, are split. -split, in this case, 1104 * will override the condition of the implied first -nosplit. 1105 */ 1106 1107 if (n->sec == SEC_AUTHORS) { 1108 if ( ! (TERMP_ANPREC & p->flags)) { 1109 if (TERMP_SPLIT & p->flags) 1110 term_newln(p); 1111 return(1); 1112 } 1113 if (TERMP_NOSPLIT & p->flags) 1114 return(1); 1115 term_newln(p); 1116 return(1); 1117 } 1118 1119 if (TERMP_SPLIT & p->flags) 1120 term_newln(p); 1121 1122 return(1); 1123 } 1124 1125 1126 /* ARGSUSED */ 1127 static void 1128 termp_an_post(DECL_ARGS) 1129 { 1130 1131 if (n->child) { 1132 if (SEC_AUTHORS == n->sec) 1133 p->flags |= TERMP_ANPREC; 1134 return; 1135 } 1136 1137 if (AUTH_split == n->norm->An.auth) { 1138 p->flags &= ~TERMP_NOSPLIT; 1139 p->flags |= TERMP_SPLIT; 1140 } else if (AUTH_nosplit == n->norm->An.auth) { 1141 p->flags &= ~TERMP_SPLIT; 1142 p->flags |= TERMP_NOSPLIT; 1143 } 1144 1145 } 1146 1147 1148 /* ARGSUSED */ 1149 static int 1150 termp_ns_pre(DECL_ARGS) 1151 { 1152 1153 if ( ! (MDOC_LINE & n->flags)) 1154 p->flags |= TERMP_NOSPACE; 1155 return(1); 1156 } 1157 1158 1159 /* ARGSUSED */ 1160 static int 1161 termp_rs_pre(DECL_ARGS) 1162 { 1163 1164 if (SEC_SEE_ALSO != n->sec) 1165 return(1); 1166 if (MDOC_BLOCK == n->type && n->prev) 1167 term_vspace(p); 1168 return(1); 1169 } 1170 1171 1172 /* ARGSUSED */ 1173 static int 1174 termp_rv_pre(DECL_ARGS) 1175 { 1176 int nchild; 1177 1178 term_newln(p); 1179 term_word(p, "The"); 1180 1181 nchild = n->nchild; 1182 for (n = n->child; n; n = n->next) { 1183 term_fontpush(p, TERMFONT_BOLD); 1184 term_word(p, n->string); 1185 term_fontpop(p); 1186 1187 p->flags |= TERMP_NOSPACE; 1188 term_word(p, "()"); 1189 1190 if (nchild > 2 && n->next) { 1191 p->flags |= TERMP_NOSPACE; 1192 term_word(p, ","); 1193 } 1194 1195 if (n->next && NULL == n->next->next) 1196 term_word(p, "and"); 1197 } 1198 1199 if (nchild > 1) 1200 term_word(p, "functions return"); 1201 else 1202 term_word(p, "function returns"); 1203 1204 term_word(p, "the value 0 if successful; otherwise the value " 1205 "-1 is returned and the global variable"); 1206 1207 term_fontpush(p, TERMFONT_UNDER); 1208 term_word(p, "errno"); 1209 term_fontpop(p); 1210 1211 term_word(p, "is set to indicate the error."); 1212 p->flags |= TERMP_SENTENCE; 1213 1214 return(0); 1215 } 1216 1217 1218 /* ARGSUSED */ 1219 static int 1220 termp_ex_pre(DECL_ARGS) 1221 { 1222 int nchild; 1223 1224 term_newln(p); 1225 term_word(p, "The"); 1226 1227 nchild = n->nchild; 1228 for (n = n->child; n; n = n->next) { 1229 term_fontpush(p, TERMFONT_BOLD); 1230 term_word(p, n->string); 1231 term_fontpop(p); 1232 1233 if (nchild > 2 && n->next) { 1234 p->flags |= TERMP_NOSPACE; 1235 term_word(p, ","); 1236 } 1237 1238 if (n->next && NULL == n->next->next) 1239 term_word(p, "and"); 1240 } 1241 1242 if (nchild > 1) 1243 term_word(p, "utilities exit"); 1244 else 1245 term_word(p, "utility exits"); 1246 1247 term_word(p, "0 on success, and >0 if an error occurs."); 1248 1249 p->flags |= TERMP_SENTENCE; 1250 return(0); 1251 } 1252 1253 1254 /* ARGSUSED */ 1255 static int 1256 termp_nd_pre(DECL_ARGS) 1257 { 1258 1259 if (MDOC_BODY != n->type) 1260 return(1); 1261 1262 #if defined(__OpenBSD__) || defined(__linux__) 1263 term_word(p, "\\(en"); 1264 #else 1265 term_word(p, "\\(em"); 1266 #endif 1267 return(1); 1268 } 1269 1270 1271 /* ARGSUSED */ 1272 static int 1273 termp_bl_pre(DECL_ARGS) 1274 { 1275 1276 return(MDOC_HEAD != n->type); 1277 } 1278 1279 1280 /* ARGSUSED */ 1281 static void 1282 termp_bl_post(DECL_ARGS) 1283 { 1284 1285 if (MDOC_BLOCK == n->type) 1286 term_newln(p); 1287 } 1288 1289 /* ARGSUSED */ 1290 static int 1291 termp_xr_pre(DECL_ARGS) 1292 { 1293 1294 if (NULL == (n = n->child)) 1295 return(0); 1296 1297 assert(MDOC_TEXT == n->type); 1298 term_word(p, n->string); 1299 1300 if (NULL == (n = n->next)) 1301 return(0); 1302 1303 p->flags |= TERMP_NOSPACE; 1304 term_word(p, "("); 1305 p->flags |= TERMP_NOSPACE; 1306 1307 assert(MDOC_TEXT == n->type); 1308 term_word(p, n->string); 1309 1310 p->flags |= TERMP_NOSPACE; 1311 term_word(p, ")"); 1312 1313 return(0); 1314 } 1315 1316 /* 1317 * This decides how to assert whitespace before any of the SYNOPSIS set 1318 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain 1319 * macro combos). 1320 */ 1321 static void 1322 synopsis_pre(struct termp *p, const struct mdoc_node *n) 1323 { 1324 /* 1325 * Obviously, if we're not in a SYNOPSIS or no prior macros 1326 * exist, do nothing. 1327 */ 1328 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 1329 return; 1330 1331 /* 1332 * If we're the second in a pair of like elements, emit our 1333 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which 1334 * case we soldier on. 1335 */ 1336 if (n->prev->tok == n->tok && 1337 MDOC_Ft != n->tok && 1338 MDOC_Fo != n->tok && 1339 MDOC_Fn != n->tok) { 1340 term_newln(p); 1341 return; 1342 } 1343 1344 /* 1345 * If we're one of the SYNOPSIS set and non-like pair-wise after 1346 * another (or Fn/Fo, which we've let slip through) then assert 1347 * vertical space, else only newline and move on. 1348 */ 1349 switch (n->prev->tok) { 1350 case (MDOC_Fd): 1351 /* FALLTHROUGH */ 1352 case (MDOC_Fn): 1353 /* FALLTHROUGH */ 1354 case (MDOC_Fo): 1355 /* FALLTHROUGH */ 1356 case (MDOC_In): 1357 /* FALLTHROUGH */ 1358 case (MDOC_Vt): 1359 term_vspace(p); 1360 break; 1361 case (MDOC_Ft): 1362 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 1363 term_vspace(p); 1364 break; 1365 } 1366 /* FALLTHROUGH */ 1367 default: 1368 term_newln(p); 1369 break; 1370 } 1371 } 1372 1373 1374 static int 1375 termp_vt_pre(DECL_ARGS) 1376 { 1377 1378 if (MDOC_ELEM == n->type) { 1379 synopsis_pre(p, n); 1380 return(termp_under_pre(p, pair, m, n)); 1381 } else if (MDOC_BLOCK == n->type) { 1382 synopsis_pre(p, n); 1383 return(1); 1384 } else if (MDOC_HEAD == n->type) 1385 return(0); 1386 1387 return(termp_under_pre(p, pair, m, n)); 1388 } 1389 1390 1391 /* ARGSUSED */ 1392 static int 1393 termp_bold_pre(DECL_ARGS) 1394 { 1395 1396 term_fontpush(p, TERMFONT_BOLD); 1397 return(1); 1398 } 1399 1400 1401 /* ARGSUSED */ 1402 static int 1403 termp_fd_pre(DECL_ARGS) 1404 { 1405 1406 synopsis_pre(p, n); 1407 return(termp_bold_pre(p, pair, m, n)); 1408 } 1409 1410 1411 /* ARGSUSED */ 1412 static int 1413 termp_sh_pre(DECL_ARGS) 1414 { 1415 1416 /* No vspace between consecutive `Sh' calls. */ 1417 1418 switch (n->type) { 1419 case (MDOC_BLOCK): 1420 if (n->prev && MDOC_Sh == n->prev->tok) 1421 if (NULL == n->prev->body->child) 1422 break; 1423 term_vspace(p); 1424 break; 1425 case (MDOC_HEAD): 1426 term_fontpush(p, TERMFONT_BOLD); 1427 break; 1428 case (MDOC_BODY): 1429 p->offset = term_len(p, INDENT); 1430 break; 1431 default: 1432 break; 1433 } 1434 return(1); 1435 } 1436 1437 1438 /* ARGSUSED */ 1439 static void 1440 termp_sh_post(DECL_ARGS) 1441 { 1442 1443 switch (n->type) { 1444 case (MDOC_HEAD): 1445 term_newln(p); 1446 break; 1447 case (MDOC_BODY): 1448 term_newln(p); 1449 p->offset = 0; 1450 break; 1451 default: 1452 break; 1453 } 1454 } 1455 1456 1457 /* ARGSUSED */ 1458 static int 1459 termp_bt_pre(DECL_ARGS) 1460 { 1461 1462 term_word(p, "is currently in beta test."); 1463 p->flags |= TERMP_SENTENCE; 1464 return(0); 1465 } 1466 1467 1468 /* ARGSUSED */ 1469 static void 1470 termp_lb_post(DECL_ARGS) 1471 { 1472 1473 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags) 1474 term_newln(p); 1475 } 1476 1477 1478 /* ARGSUSED */ 1479 static int 1480 termp_ud_pre(DECL_ARGS) 1481 { 1482 1483 term_word(p, "currently under development."); 1484 p->flags |= TERMP_SENTENCE; 1485 return(0); 1486 } 1487 1488 1489 /* ARGSUSED */ 1490 static int 1491 termp_d1_pre(DECL_ARGS) 1492 { 1493 1494 if (MDOC_BLOCK != n->type) 1495 return(1); 1496 term_newln(p); 1497 p->offset += term_len(p, (INDENT + 1)); 1498 return(1); 1499 } 1500 1501 1502 /* ARGSUSED */ 1503 static void 1504 termp_d1_post(DECL_ARGS) 1505 { 1506 1507 if (MDOC_BLOCK != n->type) 1508 return; 1509 term_newln(p); 1510 } 1511 1512 1513 /* ARGSUSED */ 1514 static int 1515 termp_ft_pre(DECL_ARGS) 1516 { 1517 1518 /* NB: MDOC_LINE does not effect this! */ 1519 synopsis_pre(p, n); 1520 term_fontpush(p, TERMFONT_UNDER); 1521 return(1); 1522 } 1523 1524 1525 /* ARGSUSED */ 1526 static int 1527 termp_fn_pre(DECL_ARGS) 1528 { 1529 int pretty; 1530 1531 pretty = MDOC_SYNPRETTY & n->flags; 1532 1533 synopsis_pre(p, n); 1534 1535 if (NULL == (n = n->child)) 1536 return(0); 1537 1538 assert(MDOC_TEXT == n->type); 1539 term_fontpush(p, TERMFONT_BOLD); 1540 term_word(p, n->string); 1541 term_fontpop(p); 1542 1543 p->flags |= TERMP_NOSPACE; 1544 term_word(p, "("); 1545 p->flags |= TERMP_NOSPACE; 1546 1547 for (n = n->next; n; n = n->next) { 1548 assert(MDOC_TEXT == n->type); 1549 term_fontpush(p, TERMFONT_UNDER); 1550 term_word(p, n->string); 1551 term_fontpop(p); 1552 1553 if (n->next) { 1554 p->flags |= TERMP_NOSPACE; 1555 term_word(p, ","); 1556 } 1557 } 1558 1559 p->flags |= TERMP_NOSPACE; 1560 term_word(p, ")"); 1561 1562 if (pretty) { 1563 p->flags |= TERMP_NOSPACE; 1564 term_word(p, ";"); 1565 } 1566 1567 return(0); 1568 } 1569 1570 1571 /* ARGSUSED */ 1572 static int 1573 termp_fa_pre(DECL_ARGS) 1574 { 1575 const struct mdoc_node *nn; 1576 1577 if (n->parent->tok != MDOC_Fo) { 1578 term_fontpush(p, TERMFONT_UNDER); 1579 return(1); 1580 } 1581 1582 for (nn = n->child; nn; nn = nn->next) { 1583 term_fontpush(p, TERMFONT_UNDER); 1584 term_word(p, nn->string); 1585 term_fontpop(p); 1586 1587 if (nn->next) { 1588 p->flags |= TERMP_NOSPACE; 1589 term_word(p, ","); 1590 } 1591 } 1592 1593 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1594 p->flags |= TERMP_NOSPACE; 1595 term_word(p, ","); 1596 } 1597 1598 return(0); 1599 } 1600 1601 1602 /* ARGSUSED */ 1603 static int 1604 termp_bd_pre(DECL_ARGS) 1605 { 1606 size_t tabwidth, rm, rmax; 1607 const struct mdoc_node *nn; 1608 1609 if (MDOC_BLOCK == n->type) { 1610 print_bvspace(p, n, n); 1611 return(1); 1612 } else if (MDOC_HEAD == n->type) 1613 return(0); 1614 1615 if (n->norm->Bd.offs) 1616 p->offset += a2offs(p, n->norm->Bd.offs); 1617 1618 /* 1619 * If -ragged or -filled are specified, the block does nothing 1620 * but change the indentation. If -unfilled or -literal are 1621 * specified, text is printed exactly as entered in the display: 1622 * for macro lines, a newline is appended to the line. Blank 1623 * lines are allowed. 1624 */ 1625 1626 if (DISP_literal != n->norm->Bd.type && 1627 DISP_unfilled != n->norm->Bd.type) 1628 return(1); 1629 1630 tabwidth = p->tabwidth; 1631 if (DISP_literal == n->norm->Bd.type) 1632 p->tabwidth = term_len(p, 8); 1633 1634 rm = p->rmargin; 1635 rmax = p->maxrmargin; 1636 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1637 1638 for (nn = n->child; nn; nn = nn->next) { 1639 print_mdoc_node(p, pair, m, nn); 1640 /* 1641 * If the printed node flushes its own line, then we 1642 * needn't do it here as well. This is hacky, but the 1643 * notion of selective eoln whitespace is pretty dumb 1644 * anyway, so don't sweat it. 1645 */ 1646 switch (nn->tok) { 1647 case (MDOC_Sm): 1648 /* FALLTHROUGH */ 1649 case (MDOC_br): 1650 /* FALLTHROUGH */ 1651 case (MDOC_sp): 1652 /* FALLTHROUGH */ 1653 case (MDOC_Bl): 1654 /* FALLTHROUGH */ 1655 case (MDOC_D1): 1656 /* FALLTHROUGH */ 1657 case (MDOC_Dl): 1658 /* FALLTHROUGH */ 1659 case (MDOC_Lp): 1660 /* FALLTHROUGH */ 1661 case (MDOC_Pp): 1662 continue; 1663 default: 1664 break; 1665 } 1666 if (nn->next && nn->next->line == nn->line) 1667 continue; 1668 term_flushln(p); 1669 p->flags |= TERMP_NOSPACE; 1670 } 1671 1672 p->tabwidth = tabwidth; 1673 p->rmargin = rm; 1674 p->maxrmargin = rmax; 1675 return(0); 1676 } 1677 1678 1679 /* ARGSUSED */ 1680 static void 1681 termp_bd_post(DECL_ARGS) 1682 { 1683 size_t rm, rmax; 1684 1685 if (MDOC_BODY != n->type) 1686 return; 1687 1688 rm = p->rmargin; 1689 rmax = p->maxrmargin; 1690 1691 if (DISP_literal == n->norm->Bd.type || 1692 DISP_unfilled == n->norm->Bd.type) 1693 p->rmargin = p->maxrmargin = TERM_MAXMARGIN; 1694 1695 p->flags |= TERMP_NOSPACE; 1696 term_newln(p); 1697 1698 p->rmargin = rm; 1699 p->maxrmargin = rmax; 1700 } 1701 1702 1703 /* ARGSUSED */ 1704 static int 1705 termp_bx_pre(DECL_ARGS) 1706 { 1707 1708 if (NULL != (n = n->child)) { 1709 term_word(p, n->string); 1710 p->flags |= TERMP_NOSPACE; 1711 term_word(p, "BSD"); 1712 } else { 1713 term_word(p, "BSD"); 1714 return(0); 1715 } 1716 1717 if (NULL != (n = n->next)) { 1718 p->flags |= TERMP_NOSPACE; 1719 term_word(p, "-"); 1720 p->flags |= TERMP_NOSPACE; 1721 term_word(p, n->string); 1722 } 1723 1724 return(0); 1725 } 1726 1727 1728 /* ARGSUSED */ 1729 static int 1730 termp_xx_pre(DECL_ARGS) 1731 { 1732 const char *pp; 1733 int flags; 1734 1735 pp = NULL; 1736 switch (n->tok) { 1737 case (MDOC_Bsx): 1738 pp = "BSD/OS"; 1739 break; 1740 case (MDOC_Dx): 1741 pp = "DragonFly"; 1742 break; 1743 case (MDOC_Fx): 1744 pp = "FreeBSD"; 1745 break; 1746 case (MDOC_Nx): 1747 pp = "NetBSD"; 1748 break; 1749 case (MDOC_Ox): 1750 pp = "OpenBSD"; 1751 break; 1752 case (MDOC_Ux): 1753 pp = "UNIX"; 1754 break; 1755 default: 1756 break; 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, HALFINDENT); 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, "``"); 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_Po): 1995 /* FALLTHROUGH */ 1996 case (MDOC_Pq): 1997 term_word(p, ")"); 1998 break; 1999 case (MDOC__T): 2000 /* FALLTHROUGH */ 2001 case (MDOC_Qo): 2002 /* FALLTHROUGH */ 2003 case (MDOC_Qq): 2004 term_word(p, "\""); 2005 break; 2006 case (MDOC_Ql): 2007 /* FALLTHROUGH */ 2008 case (MDOC_So): 2009 /* FALLTHROUGH */ 2010 case (MDOC_Sq): 2011 term_word(p, "'"); 2012 break; 2013 default: 2014 abort(); 2015 /* NOTREACHED */ 2016 } 2017 } 2018 2019 2020 /* ARGSUSED */ 2021 static int 2022 termp_fo_pre(DECL_ARGS) 2023 { 2024 2025 if (MDOC_BLOCK == n->type) { 2026 synopsis_pre(p, n); 2027 return(1); 2028 } else if (MDOC_BODY == n->type) { 2029 p->flags |= TERMP_NOSPACE; 2030 term_word(p, "("); 2031 p->flags |= TERMP_NOSPACE; 2032 return(1); 2033 } 2034 2035 if (NULL == n->child) 2036 return(0); 2037 2038 /* XXX: we drop non-initial arguments as per groff. */ 2039 2040 assert(n->child->string); 2041 term_fontpush(p, TERMFONT_BOLD); 2042 term_word(p, n->child->string); 2043 return(0); 2044 } 2045 2046 2047 /* ARGSUSED */ 2048 static void 2049 termp_fo_post(DECL_ARGS) 2050 { 2051 2052 if (MDOC_BODY != n->type) 2053 return; 2054 2055 p->flags |= TERMP_NOSPACE; 2056 term_word(p, ")"); 2057 2058 if (MDOC_SYNPRETTY & n->flags) { 2059 p->flags |= TERMP_NOSPACE; 2060 term_word(p, ";"); 2061 } 2062 } 2063 2064 2065 /* ARGSUSED */ 2066 static int 2067 termp_bf_pre(DECL_ARGS) 2068 { 2069 2070 if (MDOC_HEAD == n->type) 2071 return(0); 2072 else if (MDOC_BLOCK != n->type) 2073 return(1); 2074 2075 if (FONT_Em == n->norm->Bf.font) 2076 term_fontpush(p, TERMFONT_UNDER); 2077 else if (FONT_Sy == n->norm->Bf.font) 2078 term_fontpush(p, TERMFONT_BOLD); 2079 else 2080 term_fontpush(p, TERMFONT_NONE); 2081 2082 return(1); 2083 } 2084 2085 2086 /* ARGSUSED */ 2087 static int 2088 termp_sm_pre(DECL_ARGS) 2089 { 2090 2091 assert(n->child && MDOC_TEXT == n->child->type); 2092 if (0 == strcmp("on", n->child->string)) { 2093 if (p->col) 2094 p->flags &= ~TERMP_NOSPACE; 2095 p->flags &= ~TERMP_NONOSPACE; 2096 } else 2097 p->flags |= TERMP_NONOSPACE; 2098 2099 return(0); 2100 } 2101 2102 2103 /* ARGSUSED */ 2104 static int 2105 termp_ap_pre(DECL_ARGS) 2106 { 2107 2108 p->flags |= TERMP_NOSPACE; 2109 term_word(p, "'"); 2110 p->flags |= TERMP_NOSPACE; 2111 return(1); 2112 } 2113 2114 2115 /* ARGSUSED */ 2116 static void 2117 termp____post(DECL_ARGS) 2118 { 2119 2120 /* 2121 * Handle lists of authors. In general, print each followed by 2122 * a comma. Don't print the comma if there are only two 2123 * authors. 2124 */ 2125 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 2126 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 2127 if (NULL == n->prev || MDOC__A != n->prev->tok) 2128 return; 2129 2130 /* TODO: %U. */ 2131 2132 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 2133 return; 2134 2135 p->flags |= TERMP_NOSPACE; 2136 if (NULL == n->next) { 2137 term_word(p, "."); 2138 p->flags |= TERMP_SENTENCE; 2139 } else 2140 term_word(p, ","); 2141 } 2142 2143 2144 /* ARGSUSED */ 2145 static int 2146 termp_li_pre(DECL_ARGS) 2147 { 2148 2149 term_fontpush(p, TERMFONT_NONE); 2150 return(1); 2151 } 2152 2153 2154 /* ARGSUSED */ 2155 static int 2156 termp_lk_pre(DECL_ARGS) 2157 { 2158 const struct mdoc_node *nn, *sv; 2159 2160 term_fontpush(p, TERMFONT_UNDER); 2161 2162 nn = sv = n->child; 2163 2164 if (NULL == nn || NULL == nn->next) 2165 return(1); 2166 2167 for (nn = nn->next; nn; nn = nn->next) 2168 term_word(p, nn->string); 2169 2170 term_fontpop(p); 2171 2172 p->flags |= TERMP_NOSPACE; 2173 term_word(p, ":"); 2174 2175 term_fontpush(p, TERMFONT_BOLD); 2176 term_word(p, sv->string); 2177 term_fontpop(p); 2178 2179 return(0); 2180 } 2181 2182 2183 /* ARGSUSED */ 2184 static int 2185 termp_bk_pre(DECL_ARGS) 2186 { 2187 2188 switch (n->type) { 2189 case (MDOC_BLOCK): 2190 break; 2191 case (MDOC_HEAD): 2192 return(0); 2193 case (MDOC_BODY): 2194 if (n->parent->args || 0 == n->prev->nchild) 2195 p->flags |= TERMP_PREKEEP; 2196 break; 2197 default: 2198 abort(); 2199 /* NOTREACHED */ 2200 } 2201 2202 return(1); 2203 } 2204 2205 2206 /* ARGSUSED */ 2207 static void 2208 termp_bk_post(DECL_ARGS) 2209 { 2210 2211 if (MDOC_BODY == n->type) 2212 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 2213 } 2214 2215 /* ARGSUSED */ 2216 static void 2217 termp__t_post(DECL_ARGS) 2218 { 2219 2220 /* 2221 * If we're in an `Rs' and there's a journal present, then quote 2222 * us instead of underlining us (for disambiguation). 2223 */ 2224 if (n->parent && MDOC_Rs == n->parent->tok && 2225 n->parent->norm->Rs.quote_T) 2226 termp_quote_post(p, pair, m, n); 2227 2228 termp____post(p, pair, m, n); 2229 } 2230 2231 /* ARGSUSED */ 2232 static int 2233 termp__t_pre(DECL_ARGS) 2234 { 2235 2236 /* 2237 * If we're in an `Rs' and there's a journal present, then quote 2238 * us instead of underlining us (for disambiguation). 2239 */ 2240 if (n->parent && MDOC_Rs == n->parent->tok && 2241 n->parent->norm->Rs.quote_T) 2242 return(termp_quote_pre(p, pair, m, n)); 2243 2244 term_fontpush(p, TERMFONT_UNDER); 2245 return(1); 2246 } 2247 2248 /* ARGSUSED */ 2249 static int 2250 termp_under_pre(DECL_ARGS) 2251 { 2252 2253 term_fontpush(p, TERMFONT_UNDER); 2254 return(1); 2255 } 2256