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