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