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