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