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