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