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