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