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