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