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