1 /* $Id: mdoc_term.c,v 1.373 2019/06/03 19:50:33 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2012-2019 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 AUTHORS DISCLAIM ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 #include "config.h" 20 21 #include <sys/types.h> 22 23 #include <assert.h> 24 #include <ctype.h> 25 #include <limits.h> 26 #include <stdint.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include "mandoc_aux.h" 32 #include "roff.h" 33 #include "mdoc.h" 34 #include "out.h" 35 #include "term.h" 36 #include "tag.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 roff_meta *meta, \ 47 struct roff_node *n 48 49 struct mdoc_term_act { 50 int (*pre)(DECL_ARGS); 51 void (*post)(DECL_ARGS); 52 }; 53 54 static int a2width(const struct termp *, const char *); 55 56 static void print_bvspace(struct termp *, 57 const struct roff_node *, 58 const struct roff_node *); 59 static void print_mdoc_node(DECL_ARGS); 60 static void print_mdoc_nodelist(DECL_ARGS); 61 static void print_mdoc_head(struct termp *, const struct roff_meta *); 62 static void print_mdoc_foot(struct termp *, const struct roff_meta *); 63 static void synopsis_pre(struct termp *, 64 const struct roff_node *); 65 66 static void termp____post(DECL_ARGS); 67 static void termp__t_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_eo_post(DECL_ARGS); 72 static void termp_fd_post(DECL_ARGS); 73 static void termp_fo_post(DECL_ARGS); 74 static void termp_in_post(DECL_ARGS); 75 static void termp_it_post(DECL_ARGS); 76 static void termp_lb_post(DECL_ARGS); 77 static void termp_nm_post(DECL_ARGS); 78 static void termp_pf_post(DECL_ARGS); 79 static void termp_quote_post(DECL_ARGS); 80 static void termp_sh_post(DECL_ARGS); 81 static void termp_ss_post(DECL_ARGS); 82 static void termp_xx_post(DECL_ARGS); 83 84 static int termp__a_pre(DECL_ARGS); 85 static int termp__t_pre(DECL_ARGS); 86 static int termp_abort_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_cd_pre(DECL_ARGS); 95 static int termp_d1_pre(DECL_ARGS); 96 static int termp_eo_pre(DECL_ARGS); 97 static int termp_em_pre(DECL_ARGS); 98 static int termp_er_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_lk_pre(DECL_ARGS); 110 static int termp_nd_pre(DECL_ARGS); 111 static int termp_nm_pre(DECL_ARGS); 112 static int termp_ns_pre(DECL_ARGS); 113 static int termp_quote_pre(DECL_ARGS); 114 static int termp_rs_pre(DECL_ARGS); 115 static int termp_sh_pre(DECL_ARGS); 116 static int termp_skip_pre(DECL_ARGS); 117 static int termp_sm_pre(DECL_ARGS); 118 static int termp_pp_pre(DECL_ARGS); 119 static int termp_ss_pre(DECL_ARGS); 120 static int termp_sy_pre(DECL_ARGS); 121 static int termp_tag_pre(DECL_ARGS); 122 static int termp_under_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 mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { 128 { NULL, NULL }, /* Dd */ 129 { NULL, NULL }, /* Dt */ 130 { NULL, NULL }, /* Os */ 131 { termp_sh_pre, termp_sh_post }, /* Sh */ 132 { termp_ss_pre, termp_ss_post }, /* Ss */ 133 { termp_pp_pre, NULL }, /* Pp */ 134 { termp_d1_pre, termp_bl_post }, /* D1 */ 135 { termp_d1_pre, termp_bl_post }, /* Dl */ 136 { termp_bd_pre, termp_bd_post }, /* Bd */ 137 { NULL, NULL }, /* Ed */ 138 { termp_bl_pre, termp_bl_post }, /* Bl */ 139 { NULL, NULL }, /* El */ 140 { termp_it_pre, termp_it_post }, /* It */ 141 { termp_under_pre, NULL }, /* Ad */ 142 { termp_an_pre, NULL }, /* An */ 143 { termp_ap_pre, NULL }, /* Ap */ 144 { termp_under_pre, NULL }, /* Ar */ 145 { termp_cd_pre, NULL }, /* Cd */ 146 { termp_bold_pre, NULL }, /* Cm */ 147 { termp_li_pre, NULL }, /* Dv */ 148 { termp_er_pre, NULL }, /* Er */ 149 { termp_tag_pre, 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_abort_pre, NULL }, /* Ot */ 163 { termp_under_pre, NULL }, /* Pa */ 164 { termp_ex_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, termp_xx_post }, /* Bsx */ 189 { NULL, NULL }, /* Bx */ 190 { termp_skip_pre, 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_em_pre, NULL }, /* Em */ 197 { termp_eo_pre, termp_eo_post }, /* Eo */ 198 { termp_xx_pre, termp_xx_post }, /* Fx */ 199 { termp_bold_pre, NULL }, /* Ms */ 200 { termp_li_pre, NULL }, /* No */ 201 { termp_ns_pre, NULL }, /* Ns */ 202 { termp_xx_pre, termp_xx_post }, /* Nx */ 203 { termp_xx_pre, termp_xx_post }, /* 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_sy_pre, NULL }, /* Sy */ 220 { NULL, NULL }, /* Tn */ 221 { termp_xx_pre, termp_xx_post }, /* 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 { NULL, NULL }, /* Bt */ 231 { NULL, NULL }, /* Hf */ 232 { termp_under_pre, NULL }, /* Fr */ 233 { NULL, NULL }, /* Ud */ 234 { NULL, termp_lb_post }, /* Lb */ 235 { termp_abort_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_skip_pre, NULL }, /* Es */ 243 { termp_quote_pre, termp_quote_post }, /* En */ 244 { termp_xx_pre, termp_xx_post }, /* Dx */ 245 { NULL, termp____post }, /* %Q */ 246 { NULL, termp____post }, /* %U */ 247 { NULL, NULL }, /* Ta */ 248 }; 249 250 static int fn_prio; 251 252 253 void 254 terminal_mdoc(void *arg, const struct roff_meta *mdoc) 255 { 256 struct roff_node *n; 257 struct termp *p; 258 size_t save_defindent; 259 260 p = (struct termp *)arg; 261 p->tcol->rmargin = p->maxrmargin = p->defrmargin; 262 term_tab_set(p, NULL); 263 term_tab_set(p, "T"); 264 term_tab_set(p, ".5i"); 265 266 n = mdoc->first->child; 267 if (p->synopsisonly) { 268 while (n != NULL) { 269 if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) { 270 if (n->child->next->child != NULL) 271 print_mdoc_nodelist(p, NULL, 272 mdoc, n->child->next->child); 273 term_newln(p); 274 break; 275 } 276 n = n->next; 277 } 278 } else { 279 save_defindent = p->defindent; 280 if (p->defindent == 0) 281 p->defindent = 5; 282 term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc); 283 while (n != NULL && 284 (n->type == ROFFT_COMMENT || 285 n->flags & NODE_NOPRT)) 286 n = n->next; 287 if (n != NULL) { 288 if (n->tok != MDOC_Sh) 289 term_vspace(p); 290 print_mdoc_nodelist(p, NULL, mdoc, n); 291 } 292 term_end(p); 293 p->defindent = save_defindent; 294 } 295 } 296 297 static void 298 print_mdoc_nodelist(DECL_ARGS) 299 { 300 301 while (n != NULL) { 302 print_mdoc_node(p, pair, meta, n); 303 n = n->next; 304 } 305 } 306 307 static void 308 print_mdoc_node(DECL_ARGS) 309 { 310 const struct mdoc_term_act *act; 311 struct termpair npair; 312 size_t offset, rmargin; 313 int chld; 314 315 /* 316 * In no-fill mode, break the output line at the beginning 317 * of new input lines except after \c, and nowhere else. 318 */ 319 320 if (n->flags & NODE_NOFILL) { 321 if (n->flags & NODE_LINE && 322 (p->flags & TERMP_NONEWLINE) == 0) 323 term_newln(p); 324 p->flags |= TERMP_BRNEVER; 325 } else 326 p->flags &= ~TERMP_BRNEVER; 327 328 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 329 return; 330 331 chld = 1; 332 offset = p->tcol->offset; 333 rmargin = p->tcol->rmargin; 334 n->flags &= ~NODE_ENDED; 335 n->prev_font = p->fonti; 336 337 memset(&npair, 0, sizeof(struct termpair)); 338 npair.ppair = pair; 339 340 /* 341 * Keeps only work until the end of a line. If a keep was 342 * invoked in a prior line, revert it to PREKEEP. 343 */ 344 345 if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) { 346 p->flags &= ~TERMP_KEEP; 347 p->flags |= TERMP_PREKEEP; 348 } 349 350 /* 351 * After the keep flags have been set up, we may now 352 * produce output. Note that some pre-handlers do so. 353 */ 354 355 act = NULL; 356 switch (n->type) { 357 case ROFFT_TEXT: 358 if (n->flags & NODE_LINE) { 359 switch (*n->string) { 360 case '\0': 361 if (p->flags & TERMP_NONEWLINE) 362 term_newln(p); 363 else 364 term_vspace(p); 365 return; 366 case ' ': 367 if ((p->flags & TERMP_NONEWLINE) == 0) 368 term_newln(p); 369 break; 370 default: 371 break; 372 } 373 } 374 if (NODE_DELIMC & n->flags) 375 p->flags |= TERMP_NOSPACE; 376 term_word(p, n->string); 377 if (NODE_DELIMO & n->flags) 378 p->flags |= TERMP_NOSPACE; 379 break; 380 case ROFFT_EQN: 381 if ( ! (n->flags & NODE_LINE)) 382 p->flags |= TERMP_NOSPACE; 383 term_eqn(p, n->eqn); 384 if (n->next != NULL && ! (n->next->flags & NODE_LINE)) 385 p->flags |= TERMP_NOSPACE; 386 break; 387 case ROFFT_TBL: 388 if (p->tbl.cols == NULL) 389 term_newln(p); 390 term_tbl(p, n->span); 391 break; 392 default: 393 if (n->tok < ROFF_MAX) { 394 roff_term_pre(p, n); 395 return; 396 } 397 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 398 act = mdoc_term_acts + (n->tok - MDOC_Dd); 399 if (act->pre != NULL && 400 (n->end == ENDBODY_NOT || n->child != NULL)) 401 chld = (*act->pre)(p, &npair, meta, n); 402 break; 403 } 404 405 if (chld && n->child) 406 print_mdoc_nodelist(p, &npair, meta, n->child); 407 408 term_fontpopq(p, 409 (ENDBODY_NOT == n->end ? n : n->body)->prev_font); 410 411 switch (n->type) { 412 case ROFFT_TEXT: 413 break; 414 case ROFFT_TBL: 415 break; 416 case ROFFT_EQN: 417 break; 418 default: 419 if (act->post == NULL || n->flags & NODE_ENDED) 420 break; 421 (void)(*act->post)(p, &npair, meta, n); 422 423 /* 424 * Explicit end tokens not only call the post 425 * handler, but also tell the respective block 426 * that it must not call the post handler again. 427 */ 428 if (ENDBODY_NOT != n->end) 429 n->body->flags |= NODE_ENDED; 430 break; 431 } 432 433 if (NODE_EOS & n->flags) 434 p->flags |= TERMP_SENTENCE; 435 436 if (n->type != ROFFT_TEXT) 437 p->tcol->offset = offset; 438 p->tcol->rmargin = rmargin; 439 } 440 441 static void 442 print_mdoc_foot(struct termp *p, const struct roff_meta *meta) 443 { 444 size_t sz; 445 446 term_fontrepl(p, TERMFONT_NONE); 447 448 /* 449 * Output the footer in new-groff style, that is, three columns 450 * with the middle being the manual date and flanking columns 451 * being the operating system: 452 * 453 * SYSTEM DATE SYSTEM 454 */ 455 456 term_vspace(p); 457 458 p->tcol->offset = 0; 459 sz = term_strlen(p, meta->date); 460 p->tcol->rmargin = p->maxrmargin > sz ? 461 (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0; 462 p->trailspace = 1; 463 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 464 465 term_word(p, meta->os); 466 term_flushln(p); 467 468 p->tcol->offset = p->tcol->rmargin; 469 sz = term_strlen(p, meta->os); 470 p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0; 471 p->flags |= TERMP_NOSPACE; 472 473 term_word(p, meta->date); 474 term_flushln(p); 475 476 p->tcol->offset = p->tcol->rmargin; 477 p->tcol->rmargin = p->maxrmargin; 478 p->trailspace = 0; 479 p->flags &= ~TERMP_NOBREAK; 480 p->flags |= TERMP_NOSPACE; 481 482 term_word(p, meta->os); 483 term_flushln(p); 484 485 p->tcol->offset = 0; 486 p->tcol->rmargin = p->maxrmargin; 487 p->flags = 0; 488 } 489 490 static void 491 print_mdoc_head(struct termp *p, const struct roff_meta *meta) 492 { 493 char *volume, *title; 494 size_t vollen, titlen; 495 496 /* 497 * The header is strange. It has three components, which are 498 * really two with the first duplicated. It goes like this: 499 * 500 * IDENTIFIER TITLE IDENTIFIER 501 * 502 * The IDENTIFIER is NAME(SECTION), which is the command-name 503 * (if given, or "unknown" if not) followed by the manual page 504 * section. These are given in `Dt'. The TITLE is a free-form 505 * string depending on the manual volume. If not specified, it 506 * switches on the manual section. 507 */ 508 509 assert(meta->vol); 510 if (NULL == meta->arch) 511 volume = mandoc_strdup(meta->vol); 512 else 513 mandoc_asprintf(&volume, "%s (%s)", 514 meta->vol, meta->arch); 515 vollen = term_strlen(p, volume); 516 517 if (NULL == meta->msec) 518 title = mandoc_strdup(meta->title); 519 else 520 mandoc_asprintf(&title, "%s(%s)", 521 meta->title, meta->msec); 522 titlen = term_strlen(p, title); 523 524 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 525 p->trailspace = 1; 526 p->tcol->offset = 0; 527 p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? 528 (p->maxrmargin - vollen + term_len(p, 1)) / 2 : 529 vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; 530 531 term_word(p, title); 532 term_flushln(p); 533 534 p->flags |= TERMP_NOSPACE; 535 p->tcol->offset = p->tcol->rmargin; 536 p->tcol->rmargin = p->tcol->offset + vollen + titlen < 537 p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; 538 539 term_word(p, volume); 540 term_flushln(p); 541 542 p->flags &= ~TERMP_NOBREAK; 543 p->trailspace = 0; 544 if (p->tcol->rmargin + titlen <= p->maxrmargin) { 545 p->flags |= TERMP_NOSPACE; 546 p->tcol->offset = p->tcol->rmargin; 547 p->tcol->rmargin = p->maxrmargin; 548 term_word(p, title); 549 term_flushln(p); 550 } 551 552 p->flags &= ~TERMP_NOSPACE; 553 p->tcol->offset = 0; 554 p->tcol->rmargin = p->maxrmargin; 555 free(title); 556 free(volume); 557 } 558 559 static int 560 a2width(const struct termp *p, const char *v) 561 { 562 struct roffsu su; 563 const char *end; 564 565 end = a2roffsu(v, &su, SCALE_MAX); 566 if (end == NULL || *end != '\0') { 567 SCALE_HS_INIT(&su, term_strlen(p, v)); 568 su.scale /= term_strlen(p, "0"); 569 } 570 return term_hen(p, &su); 571 } 572 573 /* 574 * Determine how much space to print out before block elements of `It' 575 * (and thus `Bl') and `Bd'. And then go ahead and print that space, 576 * too. 577 */ 578 static void 579 print_bvspace(struct termp *p, 580 const struct roff_node *bl, 581 const struct roff_node *n) 582 { 583 const struct roff_node *nn; 584 585 assert(n); 586 587 term_newln(p); 588 589 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) 590 return; 591 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) 592 return; 593 594 /* Do not vspace directly after Ss/Sh. */ 595 596 nn = n; 597 while (nn->prev != NULL && 598 (nn->prev->type == ROFFT_COMMENT || 599 nn->prev->flags & NODE_NOPRT)) 600 nn = nn->prev; 601 while (nn->prev == NULL) { 602 do { 603 nn = nn->parent; 604 if (nn->type == ROFFT_ROOT) 605 return; 606 } while (nn->type != ROFFT_BLOCK); 607 if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) 608 return; 609 if (nn->tok == MDOC_It && 610 nn->parent->parent->norm->Bl.type != LIST_item) 611 break; 612 } 613 614 /* A `-column' does not assert vspace within the list. */ 615 616 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) 617 if (n->prev && MDOC_It == n->prev->tok) 618 return; 619 620 /* A `-diag' without body does not vspace. */ 621 622 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) 623 if (n->prev && MDOC_It == n->prev->tok) { 624 assert(n->prev->body); 625 if (NULL == n->prev->body->child) 626 return; 627 } 628 629 term_vspace(p); 630 } 631 632 633 static int 634 termp_it_pre(DECL_ARGS) 635 { 636 struct roffsu su; 637 char buf[24]; 638 const struct roff_node *bl, *nn; 639 size_t ncols, dcol; 640 int i, offset, width; 641 enum mdoc_list type; 642 643 if (n->type == ROFFT_BLOCK) { 644 print_bvspace(p, n->parent->parent, n); 645 return 1; 646 } 647 648 bl = n->parent->parent->parent; 649 type = bl->norm->Bl.type; 650 651 /* 652 * Defaults for specific list types. 653 */ 654 655 switch (type) { 656 case LIST_bullet: 657 case LIST_dash: 658 case LIST_hyphen: 659 case LIST_enum: 660 width = term_len(p, 2); 661 break; 662 case LIST_hang: 663 case LIST_tag: 664 width = term_len(p, 8); 665 break; 666 case LIST_column: 667 width = term_len(p, 10); 668 break; 669 default: 670 width = 0; 671 break; 672 } 673 offset = 0; 674 675 /* 676 * First calculate width and offset. This is pretty easy unless 677 * we're a -column list, in which case all prior columns must 678 * be accounted for. 679 */ 680 681 if (bl->norm->Bl.offs != NULL) { 682 offset = a2width(p, bl->norm->Bl.offs); 683 if (offset < 0 && (size_t)(-offset) > p->tcol->offset) 684 offset = -p->tcol->offset; 685 else if (offset > SHRT_MAX) 686 offset = 0; 687 } 688 689 switch (type) { 690 case LIST_column: 691 if (n->type == ROFFT_HEAD) 692 break; 693 694 /* 695 * Imitate groff's column handling: 696 * - For each earlier column, add its width. 697 * - For less than 5 columns, add four more blanks per 698 * column. 699 * - For exactly 5 columns, add three more blank per 700 * column. 701 * - For more than 5 columns, add only one column. 702 */ 703 ncols = bl->norm->Bl.ncols; 704 dcol = ncols < 5 ? term_len(p, 4) : 705 ncols == 5 ? term_len(p, 3) : term_len(p, 1); 706 707 /* 708 * Calculate the offset by applying all prior ROFFT_BODY, 709 * so we stop at the ROFFT_HEAD (nn->prev == NULL). 710 */ 711 712 for (i = 0, nn = n->prev; 713 nn->prev && i < (int)ncols; 714 nn = nn->prev, i++) { 715 SCALE_HS_INIT(&su, 716 term_strlen(p, bl->norm->Bl.cols[i])); 717 su.scale /= term_strlen(p, "0"); 718 offset += term_hen(p, &su) + dcol; 719 } 720 721 /* 722 * When exceeding the declared number of columns, leave 723 * the remaining widths at 0. This will later be 724 * adjusted to the default width of 10, or, for the last 725 * column, stretched to the right margin. 726 */ 727 if (i >= (int)ncols) 728 break; 729 730 /* 731 * Use the declared column widths, extended as explained 732 * in the preceding paragraph. 733 */ 734 SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i])); 735 su.scale /= term_strlen(p, "0"); 736 width = term_hen(p, &su) + dcol; 737 break; 738 default: 739 if (NULL == bl->norm->Bl.width) 740 break; 741 742 /* 743 * Note: buffer the width by 2, which is groff's magic 744 * number for buffering single arguments. See the above 745 * handling for column for how this changes. 746 */ 747 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); 748 if (width < 0 && (size_t)(-width) > p->tcol->offset) 749 width = -p->tcol->offset; 750 else if (width > SHRT_MAX) 751 width = 0; 752 break; 753 } 754 755 /* 756 * Whitespace control. Inset bodies need an initial space, 757 * while diagonal bodies need two. 758 */ 759 760 p->flags |= TERMP_NOSPACE; 761 762 switch (type) { 763 case LIST_diag: 764 if (n->type == ROFFT_BODY) 765 term_word(p, "\\ \\ "); 766 break; 767 case LIST_inset: 768 if (n->type == ROFFT_BODY && n->parent->head->child != NULL) 769 term_word(p, "\\ "); 770 break; 771 default: 772 break; 773 } 774 775 p->flags |= TERMP_NOSPACE; 776 777 switch (type) { 778 case LIST_diag: 779 if (n->type == ROFFT_HEAD) 780 term_fontpush(p, TERMFONT_BOLD); 781 break; 782 default: 783 break; 784 } 785 786 /* 787 * Pad and break control. This is the tricky part. These flags 788 * are documented in term_flushln() in term.c. Note that we're 789 * going to unset all of these flags in termp_it_post() when we 790 * exit. 791 */ 792 793 switch (type) { 794 case LIST_enum: 795 case LIST_bullet: 796 case LIST_dash: 797 case LIST_hyphen: 798 if (n->type == ROFFT_HEAD) { 799 p->flags |= TERMP_NOBREAK | TERMP_HANG; 800 p->trailspace = 1; 801 } else if (width <= (int)term_len(p, 2)) 802 p->flags |= TERMP_NOPAD; 803 break; 804 case LIST_hang: 805 if (n->type != ROFFT_HEAD) 806 break; 807 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; 808 p->trailspace = 1; 809 break; 810 case LIST_tag: 811 if (n->type != ROFFT_HEAD) 812 break; 813 814 p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND; 815 p->trailspace = 2; 816 817 if (NULL == n->next || NULL == n->next->child) 818 p->flags |= TERMP_HANG; 819 break; 820 case LIST_column: 821 if (n->type == ROFFT_HEAD) 822 break; 823 824 if (NULL == n->next) { 825 p->flags &= ~TERMP_NOBREAK; 826 p->trailspace = 0; 827 } else { 828 p->flags |= TERMP_NOBREAK; 829 p->trailspace = 1; 830 } 831 832 break; 833 case LIST_diag: 834 if (n->type != ROFFT_HEAD) 835 break; 836 p->flags |= TERMP_NOBREAK | TERMP_BRIND; 837 p->trailspace = 1; 838 break; 839 default: 840 break; 841 } 842 843 /* 844 * Margin control. Set-head-width lists have their right 845 * margins shortened. The body for these lists has the offset 846 * necessarily lengthened. Everybody gets the offset. 847 */ 848 849 p->tcol->offset += offset; 850 851 switch (type) { 852 case LIST_bullet: 853 case LIST_dash: 854 case LIST_enum: 855 case LIST_hyphen: 856 case LIST_hang: 857 case LIST_tag: 858 if (n->type == ROFFT_HEAD) 859 p->tcol->rmargin = p->tcol->offset + width; 860 else 861 p->tcol->offset += width; 862 break; 863 case LIST_column: 864 assert(width); 865 p->tcol->rmargin = p->tcol->offset + width; 866 /* 867 * XXX - this behaviour is not documented: the 868 * right-most column is filled to the right margin. 869 */ 870 if (n->type == ROFFT_HEAD) 871 break; 872 if (n->next == NULL && p->tcol->rmargin < p->maxrmargin) 873 p->tcol->rmargin = p->maxrmargin; 874 break; 875 default: 876 break; 877 } 878 879 /* 880 * The dash, hyphen, bullet and enum lists all have a special 881 * HEAD character (temporarily bold, in some cases). 882 */ 883 884 if (n->type == ROFFT_HEAD) 885 switch (type) { 886 case LIST_bullet: 887 term_fontpush(p, TERMFONT_BOLD); 888 term_word(p, "\\[bu]"); 889 term_fontpop(p); 890 break; 891 case LIST_dash: 892 case LIST_hyphen: 893 term_fontpush(p, TERMFONT_BOLD); 894 term_word(p, "-"); 895 term_fontpop(p); 896 break; 897 case LIST_enum: 898 (pair->ppair->ppair->count)++; 899 (void)snprintf(buf, sizeof(buf), "%d.", 900 pair->ppair->ppair->count); 901 term_word(p, buf); 902 break; 903 default: 904 break; 905 } 906 907 /* 908 * If we're not going to process our children, indicate so here. 909 */ 910 911 switch (type) { 912 case LIST_bullet: 913 case LIST_item: 914 case LIST_dash: 915 case LIST_hyphen: 916 case LIST_enum: 917 if (n->type == ROFFT_HEAD) 918 return 0; 919 break; 920 case LIST_column: 921 if (n->type == ROFFT_HEAD) 922 return 0; 923 p->minbl = 0; 924 break; 925 default: 926 break; 927 } 928 929 return 1; 930 } 931 932 static void 933 termp_it_post(DECL_ARGS) 934 { 935 enum mdoc_list type; 936 937 if (n->type == ROFFT_BLOCK) 938 return; 939 940 type = n->parent->parent->parent->norm->Bl.type; 941 942 switch (type) { 943 case LIST_item: 944 case LIST_diag: 945 case LIST_inset: 946 if (n->type == ROFFT_BODY) 947 term_newln(p); 948 break; 949 case LIST_column: 950 if (n->type == ROFFT_BODY) 951 term_flushln(p); 952 break; 953 default: 954 term_newln(p); 955 break; 956 } 957 958 /* 959 * Now that our output is flushed, we can reset our tags. Since 960 * only `It' sets these flags, we're free to assume that nobody 961 * has munged them in the meanwhile. 962 */ 963 964 p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG); 965 p->trailspace = 0; 966 } 967 968 static int 969 termp_nm_pre(DECL_ARGS) 970 { 971 const char *cp; 972 973 if (n->type == ROFFT_BLOCK) { 974 p->flags |= TERMP_PREKEEP; 975 return 1; 976 } 977 978 if (n->type == ROFFT_BODY) { 979 if (n->child == NULL) 980 return 0; 981 p->flags |= TERMP_NOSPACE; 982 cp = NULL; 983 if (n->prev->child != NULL) 984 cp = n->prev->child->string; 985 if (cp == NULL) 986 cp = meta->name; 987 if (cp == NULL) 988 p->tcol->offset += term_len(p, 6); 989 else 990 p->tcol->offset += term_len(p, 1) + 991 term_strlen(p, cp); 992 return 1; 993 } 994 995 if (n->child == NULL) 996 return 0; 997 998 if (n->type == ROFFT_HEAD) 999 synopsis_pre(p, n->parent); 1000 1001 if (n->type == ROFFT_HEAD && 1002 n->next != NULL && n->next->child != NULL) { 1003 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND; 1004 p->trailspace = 1; 1005 p->tcol->rmargin = p->tcol->offset + term_len(p, 1); 1006 if (n->child == NULL) 1007 p->tcol->rmargin += term_strlen(p, meta->name); 1008 else if (n->child->type == ROFFT_TEXT) { 1009 p->tcol->rmargin += term_strlen(p, n->child->string); 1010 if (n->child->next != NULL) 1011 p->flags |= TERMP_HANG; 1012 } else { 1013 p->tcol->rmargin += term_len(p, 5); 1014 p->flags |= TERMP_HANG; 1015 } 1016 } 1017 1018 term_fontpush(p, TERMFONT_BOLD); 1019 return 1; 1020 } 1021 1022 static void 1023 termp_nm_post(DECL_ARGS) 1024 { 1025 1026 if (n->type == ROFFT_BLOCK) { 1027 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 1028 } else if (n->type == ROFFT_HEAD && 1029 NULL != n->next && NULL != n->next->child) { 1030 term_flushln(p); 1031 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); 1032 p->trailspace = 0; 1033 } else if (n->type == ROFFT_BODY && n->child != NULL) 1034 term_flushln(p); 1035 } 1036 1037 static int 1038 termp_fl_pre(DECL_ARGS) 1039 { 1040 1041 termp_tag_pre(p, pair, meta, n); 1042 term_fontpush(p, TERMFONT_BOLD); 1043 term_word(p, "\\-"); 1044 1045 if (!(n->child == NULL && 1046 (n->next == NULL || 1047 n->next->type == ROFFT_TEXT || 1048 n->next->flags & NODE_LINE))) 1049 p->flags |= TERMP_NOSPACE; 1050 1051 return 1; 1052 } 1053 1054 static int 1055 termp__a_pre(DECL_ARGS) 1056 { 1057 1058 if (n->prev && MDOC__A == n->prev->tok) 1059 if (NULL == n->next || MDOC__A != n->next->tok) 1060 term_word(p, "and"); 1061 1062 return 1; 1063 } 1064 1065 static int 1066 termp_an_pre(DECL_ARGS) 1067 { 1068 1069 if (n->norm->An.auth == AUTH_split) { 1070 p->flags &= ~TERMP_NOSPLIT; 1071 p->flags |= TERMP_SPLIT; 1072 return 0; 1073 } 1074 if (n->norm->An.auth == AUTH_nosplit) { 1075 p->flags &= ~TERMP_SPLIT; 1076 p->flags |= TERMP_NOSPLIT; 1077 return 0; 1078 } 1079 1080 if (p->flags & TERMP_SPLIT) 1081 term_newln(p); 1082 1083 if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT)) 1084 p->flags |= TERMP_SPLIT; 1085 1086 return 1; 1087 } 1088 1089 static int 1090 termp_ns_pre(DECL_ARGS) 1091 { 1092 1093 if ( ! (NODE_LINE & n->flags)) 1094 p->flags |= TERMP_NOSPACE; 1095 return 1; 1096 } 1097 1098 static int 1099 termp_rs_pre(DECL_ARGS) 1100 { 1101 1102 if (SEC_SEE_ALSO != n->sec) 1103 return 1; 1104 if (n->type == ROFFT_BLOCK && n->prev != NULL) 1105 term_vspace(p); 1106 return 1; 1107 } 1108 1109 static int 1110 termp_ex_pre(DECL_ARGS) 1111 { 1112 term_newln(p); 1113 return 1; 1114 } 1115 1116 static int 1117 termp_nd_pre(DECL_ARGS) 1118 { 1119 1120 if (n->type == ROFFT_BODY) 1121 term_word(p, "\\(en"); 1122 return 1; 1123 } 1124 1125 static int 1126 termp_bl_pre(DECL_ARGS) 1127 { 1128 1129 return n->type != ROFFT_HEAD; 1130 } 1131 1132 static void 1133 termp_bl_post(DECL_ARGS) 1134 { 1135 1136 if (n->type != ROFFT_BLOCK) 1137 return; 1138 term_newln(p); 1139 if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column) 1140 return; 1141 term_tab_set(p, NULL); 1142 term_tab_set(p, "T"); 1143 term_tab_set(p, ".5i"); 1144 } 1145 1146 static int 1147 termp_xr_pre(DECL_ARGS) 1148 { 1149 1150 if (NULL == (n = n->child)) 1151 return 0; 1152 1153 assert(n->type == ROFFT_TEXT); 1154 term_word(p, n->string); 1155 1156 if (NULL == (n = n->next)) 1157 return 0; 1158 1159 p->flags |= TERMP_NOSPACE; 1160 term_word(p, "("); 1161 p->flags |= TERMP_NOSPACE; 1162 1163 assert(n->type == ROFFT_TEXT); 1164 term_word(p, n->string); 1165 1166 p->flags |= TERMP_NOSPACE; 1167 term_word(p, ")"); 1168 1169 return 0; 1170 } 1171 1172 /* 1173 * This decides how to assert whitespace before any of the SYNOPSIS set 1174 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain 1175 * macro combos). 1176 */ 1177 static void 1178 synopsis_pre(struct termp *p, const struct roff_node *n) 1179 { 1180 /* 1181 * Obviously, if we're not in a SYNOPSIS or no prior macros 1182 * exist, do nothing. 1183 */ 1184 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) 1185 return; 1186 1187 /* 1188 * If we're the second in a pair of like elements, emit our 1189 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which 1190 * case we soldier on. 1191 */ 1192 if (n->prev->tok == n->tok && 1193 MDOC_Ft != n->tok && 1194 MDOC_Fo != n->tok && 1195 MDOC_Fn != n->tok) { 1196 term_newln(p); 1197 return; 1198 } 1199 1200 /* 1201 * If we're one of the SYNOPSIS set and non-like pair-wise after 1202 * another (or Fn/Fo, which we've let slip through) then assert 1203 * vertical space, else only newline and move on. 1204 */ 1205 switch (n->prev->tok) { 1206 case MDOC_Fd: 1207 case MDOC_Fn: 1208 case MDOC_Fo: 1209 case MDOC_In: 1210 case MDOC_Vt: 1211 term_vspace(p); 1212 break; 1213 case MDOC_Ft: 1214 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 1215 term_vspace(p); 1216 break; 1217 } 1218 /* FALLTHROUGH */ 1219 default: 1220 term_newln(p); 1221 break; 1222 } 1223 } 1224 1225 static int 1226 termp_vt_pre(DECL_ARGS) 1227 { 1228 1229 if (n->type == ROFFT_ELEM) { 1230 synopsis_pre(p, n); 1231 return termp_under_pre(p, pair, meta, n); 1232 } else if (n->type == ROFFT_BLOCK) { 1233 synopsis_pre(p, n); 1234 return 1; 1235 } else if (n->type == ROFFT_HEAD) 1236 return 0; 1237 1238 return termp_under_pre(p, pair, meta, n); 1239 } 1240 1241 static int 1242 termp_bold_pre(DECL_ARGS) 1243 { 1244 1245 termp_tag_pre(p, pair, meta, n); 1246 term_fontpush(p, TERMFONT_BOLD); 1247 return 1; 1248 } 1249 1250 static int 1251 termp_fd_pre(DECL_ARGS) 1252 { 1253 1254 synopsis_pre(p, n); 1255 return termp_bold_pre(p, pair, meta, n); 1256 } 1257 1258 static void 1259 termp_fd_post(DECL_ARGS) 1260 { 1261 1262 term_newln(p); 1263 } 1264 1265 static int 1266 termp_sh_pre(DECL_ARGS) 1267 { 1268 1269 switch (n->type) { 1270 case ROFFT_BLOCK: 1271 /* 1272 * Vertical space before sections, except 1273 * when the previous section was empty. 1274 */ 1275 if (n->prev == NULL || 1276 n->prev->tok != MDOC_Sh || 1277 (n->prev->body != NULL && 1278 n->prev->body->child != NULL)) 1279 term_vspace(p); 1280 break; 1281 case ROFFT_HEAD: 1282 term_fontpush(p, TERMFONT_BOLD); 1283 break; 1284 case ROFFT_BODY: 1285 p->tcol->offset = term_len(p, p->defindent); 1286 term_tab_set(p, NULL); 1287 term_tab_set(p, "T"); 1288 term_tab_set(p, ".5i"); 1289 switch (n->sec) { 1290 case SEC_DESCRIPTION: 1291 fn_prio = 0; 1292 break; 1293 case SEC_AUTHORS: 1294 p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); 1295 break; 1296 default: 1297 break; 1298 } 1299 break; 1300 default: 1301 break; 1302 } 1303 return 1; 1304 } 1305 1306 static void 1307 termp_sh_post(DECL_ARGS) 1308 { 1309 1310 switch (n->type) { 1311 case ROFFT_HEAD: 1312 term_newln(p); 1313 break; 1314 case ROFFT_BODY: 1315 term_newln(p); 1316 p->tcol->offset = 0; 1317 break; 1318 default: 1319 break; 1320 } 1321 } 1322 1323 static void 1324 termp_lb_post(DECL_ARGS) 1325 { 1326 1327 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags) 1328 term_newln(p); 1329 } 1330 1331 static int 1332 termp_d1_pre(DECL_ARGS) 1333 { 1334 1335 if (n->type != ROFFT_BLOCK) 1336 return 1; 1337 term_newln(p); 1338 p->tcol->offset += term_len(p, p->defindent + 1); 1339 term_tab_set(p, NULL); 1340 term_tab_set(p, "T"); 1341 term_tab_set(p, ".5i"); 1342 return 1; 1343 } 1344 1345 static int 1346 termp_ft_pre(DECL_ARGS) 1347 { 1348 1349 /* NB: NODE_LINE does not effect this! */ 1350 synopsis_pre(p, n); 1351 term_fontpush(p, TERMFONT_UNDER); 1352 return 1; 1353 } 1354 1355 static int 1356 termp_fn_pre(DECL_ARGS) 1357 { 1358 size_t rmargin = 0; 1359 int pretty; 1360 1361 pretty = NODE_SYNPRETTY & n->flags; 1362 1363 synopsis_pre(p, n); 1364 1365 if (NULL == (n = n->child)) 1366 return 0; 1367 1368 if (pretty) { 1369 rmargin = p->tcol->rmargin; 1370 p->tcol->rmargin = p->tcol->offset + term_len(p, 4); 1371 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; 1372 } 1373 1374 assert(n->type == ROFFT_TEXT); 1375 term_fontpush(p, TERMFONT_BOLD); 1376 term_word(p, n->string); 1377 term_fontpop(p); 1378 1379 if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM) 1380 tag_put(n->string, ++fn_prio, p->line); 1381 1382 if (pretty) { 1383 term_flushln(p); 1384 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); 1385 p->flags |= TERMP_NOPAD; 1386 p->tcol->offset = p->tcol->rmargin; 1387 p->tcol->rmargin = rmargin; 1388 } 1389 1390 p->flags |= TERMP_NOSPACE; 1391 term_word(p, "("); 1392 p->flags |= TERMP_NOSPACE; 1393 1394 for (n = n->next; n; n = n->next) { 1395 assert(n->type == ROFFT_TEXT); 1396 term_fontpush(p, TERMFONT_UNDER); 1397 if (pretty) 1398 p->flags |= TERMP_NBRWORD; 1399 term_word(p, n->string); 1400 term_fontpop(p); 1401 1402 if (n->next) { 1403 p->flags |= TERMP_NOSPACE; 1404 term_word(p, ","); 1405 } 1406 } 1407 1408 p->flags |= TERMP_NOSPACE; 1409 term_word(p, ")"); 1410 1411 if (pretty) { 1412 p->flags |= TERMP_NOSPACE; 1413 term_word(p, ";"); 1414 term_flushln(p); 1415 } 1416 1417 return 0; 1418 } 1419 1420 static int 1421 termp_fa_pre(DECL_ARGS) 1422 { 1423 const struct roff_node *nn; 1424 1425 if (n->parent->tok != MDOC_Fo) { 1426 term_fontpush(p, TERMFONT_UNDER); 1427 return 1; 1428 } 1429 1430 for (nn = n->child; nn; nn = nn->next) { 1431 term_fontpush(p, TERMFONT_UNDER); 1432 p->flags |= TERMP_NBRWORD; 1433 term_word(p, nn->string); 1434 term_fontpop(p); 1435 1436 if (nn->next || (n->next && n->next->tok == MDOC_Fa)) { 1437 p->flags |= TERMP_NOSPACE; 1438 term_word(p, ","); 1439 } 1440 } 1441 1442 return 0; 1443 } 1444 1445 static int 1446 termp_bd_pre(DECL_ARGS) 1447 { 1448 int offset; 1449 1450 if (n->type == ROFFT_BLOCK) { 1451 print_bvspace(p, n, n); 1452 return 1; 1453 } else if (n->type == ROFFT_HEAD) 1454 return 0; 1455 1456 /* Handle the -offset argument. */ 1457 1458 if (n->norm->Bd.offs == NULL || 1459 ! strcmp(n->norm->Bd.offs, "left")) 1460 /* nothing */; 1461 else if ( ! strcmp(n->norm->Bd.offs, "indent")) 1462 p->tcol->offset += term_len(p, p->defindent + 1); 1463 else if ( ! strcmp(n->norm->Bd.offs, "indent-two")) 1464 p->tcol->offset += term_len(p, (p->defindent + 1) * 2); 1465 else { 1466 offset = a2width(p, n->norm->Bd.offs); 1467 if (offset < 0 && (size_t)(-offset) > p->tcol->offset) 1468 p->tcol->offset = 0; 1469 else if (offset < SHRT_MAX) 1470 p->tcol->offset += offset; 1471 } 1472 1473 switch (n->norm->Bd.type) { 1474 case DISP_literal: 1475 term_tab_set(p, NULL); 1476 term_tab_set(p, "T"); 1477 term_tab_set(p, "8n"); 1478 break; 1479 case DISP_centered: 1480 p->flags |= TERMP_CENTER; 1481 break; 1482 default: 1483 break; 1484 } 1485 return 1; 1486 } 1487 1488 static void 1489 termp_bd_post(DECL_ARGS) 1490 { 1491 if (n->type != ROFFT_BODY) 1492 return; 1493 if (n->norm->Bd.type == DISP_unfilled || 1494 n->norm->Bd.type == DISP_literal) 1495 p->flags |= TERMP_BRNEVER; 1496 p->flags |= TERMP_NOSPACE; 1497 term_newln(p); 1498 p->flags &= ~TERMP_BRNEVER; 1499 if (n->norm->Bd.type == DISP_centered) 1500 p->flags &= ~TERMP_CENTER; 1501 } 1502 1503 static int 1504 termp_xx_pre(DECL_ARGS) 1505 { 1506 if ((n->aux = p->flags & TERMP_PREKEEP) == 0) 1507 p->flags |= TERMP_PREKEEP; 1508 return 1; 1509 } 1510 1511 static void 1512 termp_xx_post(DECL_ARGS) 1513 { 1514 if (n->aux == 0) 1515 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 1516 } 1517 1518 static void 1519 termp_pf_post(DECL_ARGS) 1520 { 1521 1522 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1523 p->flags |= TERMP_NOSPACE; 1524 } 1525 1526 static int 1527 termp_ss_pre(DECL_ARGS) 1528 { 1529 struct roff_node *nn; 1530 1531 switch (n->type) { 1532 case ROFFT_BLOCK: 1533 term_newln(p); 1534 for (nn = n->prev; nn != NULL; nn = nn->prev) 1535 if (nn->type != ROFFT_COMMENT && 1536 (nn->flags & NODE_NOPRT) == 0) 1537 break; 1538 if (nn != NULL) 1539 term_vspace(p); 1540 break; 1541 case ROFFT_HEAD: 1542 term_fontpush(p, TERMFONT_BOLD); 1543 p->tcol->offset = term_len(p, (p->defindent+1)/2); 1544 break; 1545 case ROFFT_BODY: 1546 p->tcol->offset = term_len(p, p->defindent); 1547 term_tab_set(p, NULL); 1548 term_tab_set(p, "T"); 1549 term_tab_set(p, ".5i"); 1550 break; 1551 default: 1552 break; 1553 } 1554 1555 return 1; 1556 } 1557 1558 static void 1559 termp_ss_post(DECL_ARGS) 1560 { 1561 1562 if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY) 1563 term_newln(p); 1564 } 1565 1566 static int 1567 termp_cd_pre(DECL_ARGS) 1568 { 1569 1570 synopsis_pre(p, n); 1571 term_fontpush(p, TERMFONT_BOLD); 1572 return 1; 1573 } 1574 1575 static int 1576 termp_in_pre(DECL_ARGS) 1577 { 1578 1579 synopsis_pre(p, n); 1580 1581 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) { 1582 term_fontpush(p, TERMFONT_BOLD); 1583 term_word(p, "#include"); 1584 term_word(p, "<"); 1585 } else { 1586 term_word(p, "<"); 1587 term_fontpush(p, TERMFONT_UNDER); 1588 } 1589 1590 p->flags |= TERMP_NOSPACE; 1591 return 1; 1592 } 1593 1594 static void 1595 termp_in_post(DECL_ARGS) 1596 { 1597 1598 if (NODE_SYNPRETTY & n->flags) 1599 term_fontpush(p, TERMFONT_BOLD); 1600 1601 p->flags |= TERMP_NOSPACE; 1602 term_word(p, ">"); 1603 1604 if (NODE_SYNPRETTY & n->flags) 1605 term_fontpop(p); 1606 } 1607 1608 static int 1609 termp_pp_pre(DECL_ARGS) 1610 { 1611 fn_prio = 0; 1612 term_vspace(p); 1613 return 0; 1614 } 1615 1616 static int 1617 termp_skip_pre(DECL_ARGS) 1618 { 1619 1620 return 0; 1621 } 1622 1623 static int 1624 termp_quote_pre(DECL_ARGS) 1625 { 1626 1627 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1628 return 1; 1629 1630 switch (n->tok) { 1631 case MDOC_Ao: 1632 case MDOC_Aq: 1633 term_word(p, n->child != NULL && n->child->next == NULL && 1634 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1635 break; 1636 case MDOC_Bro: 1637 case MDOC_Brq: 1638 term_word(p, "{"); 1639 break; 1640 case MDOC_Oo: 1641 case MDOC_Op: 1642 case MDOC_Bo: 1643 case MDOC_Bq: 1644 term_word(p, "["); 1645 break; 1646 case MDOC__T: 1647 /* FALLTHROUGH */ 1648 case MDOC_Do: 1649 case MDOC_Dq: 1650 term_word(p, "\\(lq"); 1651 break; 1652 case MDOC_En: 1653 if (NULL == n->norm->Es || 1654 NULL == n->norm->Es->child) 1655 return 1; 1656 term_word(p, n->norm->Es->child->string); 1657 break; 1658 case MDOC_Po: 1659 case MDOC_Pq: 1660 term_word(p, "("); 1661 break; 1662 case MDOC_Qo: 1663 case MDOC_Qq: 1664 term_word(p, "\""); 1665 break; 1666 case MDOC_Ql: 1667 case MDOC_So: 1668 case MDOC_Sq: 1669 term_word(p, "\\(oq"); 1670 break; 1671 default: 1672 abort(); 1673 } 1674 1675 p->flags |= TERMP_NOSPACE; 1676 return 1; 1677 } 1678 1679 static void 1680 termp_quote_post(DECL_ARGS) 1681 { 1682 1683 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1684 return; 1685 1686 p->flags |= TERMP_NOSPACE; 1687 1688 switch (n->tok) { 1689 case MDOC_Ao: 1690 case MDOC_Aq: 1691 term_word(p, n->child != NULL && n->child->next == NULL && 1692 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1693 break; 1694 case MDOC_Bro: 1695 case MDOC_Brq: 1696 term_word(p, "}"); 1697 break; 1698 case MDOC_Oo: 1699 case MDOC_Op: 1700 case MDOC_Bo: 1701 case MDOC_Bq: 1702 term_word(p, "]"); 1703 break; 1704 case MDOC__T: 1705 /* FALLTHROUGH */ 1706 case MDOC_Do: 1707 case MDOC_Dq: 1708 term_word(p, "\\(rq"); 1709 break; 1710 case MDOC_En: 1711 if (n->norm->Es == NULL || 1712 n->norm->Es->child == NULL || 1713 n->norm->Es->child->next == NULL) 1714 p->flags &= ~TERMP_NOSPACE; 1715 else 1716 term_word(p, n->norm->Es->child->next->string); 1717 break; 1718 case MDOC_Po: 1719 case MDOC_Pq: 1720 term_word(p, ")"); 1721 break; 1722 case MDOC_Qo: 1723 case MDOC_Qq: 1724 term_word(p, "\""); 1725 break; 1726 case MDOC_Ql: 1727 case MDOC_So: 1728 case MDOC_Sq: 1729 term_word(p, "\\(cq"); 1730 break; 1731 default: 1732 abort(); 1733 } 1734 } 1735 1736 static int 1737 termp_eo_pre(DECL_ARGS) 1738 { 1739 1740 if (n->type != ROFFT_BODY) 1741 return 1; 1742 1743 if (n->end == ENDBODY_NOT && 1744 n->parent->head->child == NULL && 1745 n->child != NULL && 1746 n->child->end != ENDBODY_NOT) 1747 term_word(p, "\\&"); 1748 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1749 n->parent->head->child != NULL && (n->child != NULL || 1750 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1751 p->flags |= TERMP_NOSPACE; 1752 1753 return 1; 1754 } 1755 1756 static void 1757 termp_eo_post(DECL_ARGS) 1758 { 1759 int body, tail; 1760 1761 if (n->type != ROFFT_BODY) 1762 return; 1763 1764 if (n->end != ENDBODY_NOT) { 1765 p->flags &= ~TERMP_NOSPACE; 1766 return; 1767 } 1768 1769 body = n->child != NULL || n->parent->head->child != NULL; 1770 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1771 1772 if (body && tail) 1773 p->flags |= TERMP_NOSPACE; 1774 else if ( ! (body || tail)) 1775 term_word(p, "\\&"); 1776 else if ( ! tail) 1777 p->flags &= ~TERMP_NOSPACE; 1778 } 1779 1780 static int 1781 termp_fo_pre(DECL_ARGS) 1782 { 1783 size_t rmargin = 0; 1784 int pretty; 1785 1786 pretty = NODE_SYNPRETTY & n->flags; 1787 1788 if (n->type == ROFFT_BLOCK) { 1789 synopsis_pre(p, n); 1790 return 1; 1791 } else if (n->type == ROFFT_BODY) { 1792 if (pretty) { 1793 rmargin = p->tcol->rmargin; 1794 p->tcol->rmargin = p->tcol->offset + term_len(p, 4); 1795 p->flags |= TERMP_NOBREAK | TERMP_BRIND | 1796 TERMP_HANG; 1797 } 1798 p->flags |= TERMP_NOSPACE; 1799 term_word(p, "("); 1800 p->flags |= TERMP_NOSPACE; 1801 if (pretty) { 1802 term_flushln(p); 1803 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | 1804 TERMP_HANG); 1805 p->flags |= TERMP_NOPAD; 1806 p->tcol->offset = p->tcol->rmargin; 1807 p->tcol->rmargin = rmargin; 1808 } 1809 return 1; 1810 } 1811 1812 if (NULL == n->child) 1813 return 0; 1814 1815 /* XXX: we drop non-initial arguments as per groff. */ 1816 1817 assert(n->child->string); 1818 term_fontpush(p, TERMFONT_BOLD); 1819 term_word(p, n->child->string); 1820 return 0; 1821 } 1822 1823 static void 1824 termp_fo_post(DECL_ARGS) 1825 { 1826 1827 if (n->type != ROFFT_BODY) 1828 return; 1829 1830 p->flags |= TERMP_NOSPACE; 1831 term_word(p, ")"); 1832 1833 if (NODE_SYNPRETTY & n->flags) { 1834 p->flags |= TERMP_NOSPACE; 1835 term_word(p, ";"); 1836 term_flushln(p); 1837 } 1838 } 1839 1840 static int 1841 termp_bf_pre(DECL_ARGS) 1842 { 1843 1844 if (n->type == ROFFT_HEAD) 1845 return 0; 1846 else if (n->type != ROFFT_BODY) 1847 return 1; 1848 1849 if (FONT_Em == n->norm->Bf.font) 1850 term_fontpush(p, TERMFONT_UNDER); 1851 else if (FONT_Sy == n->norm->Bf.font) 1852 term_fontpush(p, TERMFONT_BOLD); 1853 else 1854 term_fontpush(p, TERMFONT_NONE); 1855 1856 return 1; 1857 } 1858 1859 static int 1860 termp_sm_pre(DECL_ARGS) 1861 { 1862 1863 if (NULL == n->child) 1864 p->flags ^= TERMP_NONOSPACE; 1865 else if (0 == strcmp("on", n->child->string)) 1866 p->flags &= ~TERMP_NONOSPACE; 1867 else 1868 p->flags |= TERMP_NONOSPACE; 1869 1870 if (p->col && ! (TERMP_NONOSPACE & p->flags)) 1871 p->flags &= ~TERMP_NOSPACE; 1872 1873 return 0; 1874 } 1875 1876 static int 1877 termp_ap_pre(DECL_ARGS) 1878 { 1879 1880 p->flags |= TERMP_NOSPACE; 1881 term_word(p, "'"); 1882 p->flags |= TERMP_NOSPACE; 1883 return 1; 1884 } 1885 1886 static void 1887 termp____post(DECL_ARGS) 1888 { 1889 1890 /* 1891 * Handle lists of authors. In general, print each followed by 1892 * a comma. Don't print the comma if there are only two 1893 * authors. 1894 */ 1895 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1896 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1897 if (NULL == n->prev || MDOC__A != n->prev->tok) 1898 return; 1899 1900 /* TODO: %U. */ 1901 1902 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1903 return; 1904 1905 p->flags |= TERMP_NOSPACE; 1906 if (NULL == n->next) { 1907 term_word(p, "."); 1908 p->flags |= TERMP_SENTENCE; 1909 } else 1910 term_word(p, ","); 1911 } 1912 1913 static int 1914 termp_li_pre(DECL_ARGS) 1915 { 1916 1917 termp_tag_pre(p, pair, meta, n); 1918 term_fontpush(p, TERMFONT_NONE); 1919 return 1; 1920 } 1921 1922 static int 1923 termp_lk_pre(DECL_ARGS) 1924 { 1925 const struct roff_node *link, *descr, *punct; 1926 1927 if ((link = n->child) == NULL) 1928 return 0; 1929 1930 /* Find beginning of trailing punctuation. */ 1931 punct = n->last; 1932 while (punct != link && punct->flags & NODE_DELIMC) 1933 punct = punct->prev; 1934 punct = punct->next; 1935 1936 /* Link text. */ 1937 if ((descr = link->next) != NULL && descr != punct) { 1938 term_fontpush(p, TERMFONT_UNDER); 1939 while (descr != punct) { 1940 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1941 p->flags |= TERMP_NOSPACE; 1942 term_word(p, descr->string); 1943 descr = descr->next; 1944 } 1945 term_fontpop(p); 1946 p->flags |= TERMP_NOSPACE; 1947 term_word(p, ":"); 1948 } 1949 1950 /* Link target. */ 1951 term_fontpush(p, TERMFONT_BOLD); 1952 term_word(p, link->string); 1953 term_fontpop(p); 1954 1955 /* Trailing punctuation. */ 1956 while (punct != NULL) { 1957 p->flags |= TERMP_NOSPACE; 1958 term_word(p, punct->string); 1959 punct = punct->next; 1960 } 1961 return 0; 1962 } 1963 1964 static int 1965 termp_bk_pre(DECL_ARGS) 1966 { 1967 1968 switch (n->type) { 1969 case ROFFT_BLOCK: 1970 break; 1971 case ROFFT_HEAD: 1972 return 0; 1973 case ROFFT_BODY: 1974 if (n->parent->args != NULL || n->prev->child == NULL) 1975 p->flags |= TERMP_PREKEEP; 1976 break; 1977 default: 1978 abort(); 1979 } 1980 1981 return 1; 1982 } 1983 1984 static void 1985 termp_bk_post(DECL_ARGS) 1986 { 1987 1988 if (n->type == ROFFT_BODY) 1989 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); 1990 } 1991 1992 static void 1993 termp__t_post(DECL_ARGS) 1994 { 1995 1996 /* 1997 * If we're in an `Rs' and there's a journal present, then quote 1998 * us instead of underlining us (for disambiguation). 1999 */ 2000 if (n->parent && MDOC_Rs == n->parent->tok && 2001 n->parent->norm->Rs.quote_T) 2002 termp_quote_post(p, pair, meta, n); 2003 2004 termp____post(p, pair, meta, n); 2005 } 2006 2007 static int 2008 termp__t_pre(DECL_ARGS) 2009 { 2010 2011 /* 2012 * If we're in an `Rs' and there's a journal present, then quote 2013 * us instead of underlining us (for disambiguation). 2014 */ 2015 if (n->parent && MDOC_Rs == n->parent->tok && 2016 n->parent->norm->Rs.quote_T) 2017 return termp_quote_pre(p, pair, meta, n); 2018 2019 term_fontpush(p, TERMFONT_UNDER); 2020 return 1; 2021 } 2022 2023 static int 2024 termp_under_pre(DECL_ARGS) 2025 { 2026 2027 term_fontpush(p, TERMFONT_UNDER); 2028 return 1; 2029 } 2030 2031 static int 2032 termp_em_pre(DECL_ARGS) 2033 { 2034 if (n->child != NULL && 2035 n->child->type == ROFFT_TEXT) 2036 tag_put(n->child->string, 0, p->line); 2037 term_fontpush(p, TERMFONT_UNDER); 2038 return 1; 2039 } 2040 2041 static int 2042 termp_sy_pre(DECL_ARGS) 2043 { 2044 if (n->child != NULL && 2045 n->child->type == ROFFT_TEXT) 2046 tag_put(n->child->string, 0, p->line); 2047 term_fontpush(p, TERMFONT_BOLD); 2048 return 1; 2049 } 2050 2051 static int 2052 termp_er_pre(DECL_ARGS) 2053 { 2054 2055 if (n->sec == SEC_ERRORS && 2056 (n->parent->tok == MDOC_It || 2057 (n->parent->tok == MDOC_Bq && 2058 n->parent->parent->parent->tok == MDOC_It))) 2059 tag_put(n->child->string, 1, p->line); 2060 return 1; 2061 } 2062 2063 static int 2064 termp_tag_pre(DECL_ARGS) 2065 { 2066 2067 if (n->child != NULL && 2068 n->child->type == ROFFT_TEXT && 2069 (n->prev == NULL || 2070 (n->prev->type == ROFFT_TEXT && 2071 strcmp(n->prev->string, "|") == 0)) && 2072 (n->parent->tok == MDOC_It || 2073 (n->parent->tok == MDOC_Xo && 2074 n->parent->parent->prev == NULL && 2075 n->parent->parent->parent->tok == MDOC_It))) 2076 tag_put(n->child->string, 1, p->line); 2077 return 1; 2078 } 2079 2080 static int 2081 termp_abort_pre(DECL_ARGS) 2082 { 2083 abort(); 2084 } 2085