1 /* $Id: mdoc_term.c,v 1.55 2009/09/21 20:57:57 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 19 #include <assert.h> 20 #include <ctype.h> 21 #include <err.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "term.h" 27 #include "mdoc.h" 28 29 /* FIXME: check HANG lists: they seem to be broken... : 30 * .Bl -hang -width Ds 31 * .It a 32 * b 33 * .It Fl f Ns Ar option... 34 * Override default compiler behaviour. See 35 * .Sx Compiler Options 36 * for details. 37 * Override default compiler behaviour. See 38 * .Sx Compiler Options 39 * for details. 40 * Override default compiler behaviour. See 41 * .Sx Compiler Options 42 * for details. 43 * Override default compiler behaviour. See 44 * .Sx Compiler Options 45 * for details. 46 * . 47 * .It a sasd fasd as afsd sfad sfds sadfs sd sfd ssfad asfd 48 * Override default compiler behaviour. See 49 * .Sx Compiler Options 50 * for details. 51 * Override default compiler behaviour. See 52 * .Sx Compiler Options 53 * for details. 54 * Override default compiler behaviour. See 55 * .Sx Compiler Options 56 * for details. 57 * Override default compiler behaviour. See 58 * .Sx Compiler Options 59 * for details. 60 * .El 61 * 62 */ 63 64 #define INDENT 5 65 #define HALFINDENT 3 66 67 struct termpair { 68 struct termpair *ppair; 69 int flag; 70 int count; 71 }; 72 73 #define DECL_ARGS struct termp *p, \ 74 struct termpair *pair, \ 75 const struct mdoc_meta *meta, \ 76 const struct mdoc_node *node 77 78 struct termact { 79 int (*pre)(DECL_ARGS); 80 void (*post)(DECL_ARGS); 81 }; 82 83 static void termp____post(DECL_ARGS); 84 static void termp_an_post(DECL_ARGS); 85 static void termp_aq_post(DECL_ARGS); 86 static void termp_bd_post(DECL_ARGS); 87 static void termp_bl_post(DECL_ARGS); 88 static void termp_bq_post(DECL_ARGS); 89 static void termp_brq_post(DECL_ARGS); 90 static void termp_bx_post(DECL_ARGS); 91 static void termp_d1_post(DECL_ARGS); 92 static void termp_dq_post(DECL_ARGS); 93 static void termp_fd_post(DECL_ARGS); 94 static void termp_fn_post(DECL_ARGS); 95 static void termp_fo_post(DECL_ARGS); 96 static void termp_ft_post(DECL_ARGS); 97 static void termp_in_post(DECL_ARGS); 98 static void termp_it_post(DECL_ARGS); 99 static void termp_lb_post(DECL_ARGS); 100 static void termp_op_post(DECL_ARGS); 101 static void termp_pf_post(DECL_ARGS); 102 static void termp_pq_post(DECL_ARGS); 103 static void termp_qq_post(DECL_ARGS); 104 static void termp_sh_post(DECL_ARGS); 105 static void termp_sq_post(DECL_ARGS); 106 static void termp_ss_post(DECL_ARGS); 107 static void termp_vt_post(DECL_ARGS); 108 109 static int termp_an_pre(DECL_ARGS); 110 static int termp_ap_pre(DECL_ARGS); 111 static int termp_aq_pre(DECL_ARGS); 112 static int termp_bd_pre(DECL_ARGS); 113 static int termp_bf_pre(DECL_ARGS); 114 static int termp_bold_pre(DECL_ARGS); 115 static int termp_bq_pre(DECL_ARGS); 116 static int termp_brq_pre(DECL_ARGS); 117 static int termp_bt_pre(DECL_ARGS); 118 static int termp_cd_pre(DECL_ARGS); 119 static int termp_d1_pre(DECL_ARGS); 120 static int termp_dq_pre(DECL_ARGS); 121 static int termp_ex_pre(DECL_ARGS); 122 static int termp_fa_pre(DECL_ARGS); 123 static int termp_fl_pre(DECL_ARGS); 124 static int termp_fn_pre(DECL_ARGS); 125 static int termp_fo_pre(DECL_ARGS); 126 static int termp_ft_pre(DECL_ARGS); 127 static int termp_in_pre(DECL_ARGS); 128 static int termp_it_pre(DECL_ARGS); 129 static int termp_lk_pre(DECL_ARGS); 130 static int termp_nd_pre(DECL_ARGS); 131 static int termp_nm_pre(DECL_ARGS); 132 static int termp_ns_pre(DECL_ARGS); 133 static int termp_op_pre(DECL_ARGS); 134 static int termp_pf_pre(DECL_ARGS); 135 static int termp_pq_pre(DECL_ARGS); 136 static int termp_qq_pre(DECL_ARGS); 137 static int termp_rs_pre(DECL_ARGS); 138 static int termp_rv_pre(DECL_ARGS); 139 static int termp_sh_pre(DECL_ARGS); 140 static int termp_sm_pre(DECL_ARGS); 141 static int termp_sp_pre(DECL_ARGS); 142 static int termp_sq_pre(DECL_ARGS); 143 static int termp_ss_pre(DECL_ARGS); 144 static int termp_under_pre(DECL_ARGS); 145 static int termp_ud_pre(DECL_ARGS); 146 static int termp_xr_pre(DECL_ARGS); 147 static int termp_xx_pre(DECL_ARGS); 148 149 static const struct termact termacts[MDOC_MAX] = { 150 { termp_ap_pre, NULL }, /* Ap */ 151 { NULL, NULL }, /* Dd */ 152 { NULL, NULL }, /* Dt */ 153 { NULL, NULL }, /* Os */ 154 { termp_sh_pre, termp_sh_post }, /* Sh */ 155 { termp_ss_pre, termp_ss_post }, /* Ss */ 156 { termp_sp_pre, NULL }, /* Pp */ 157 { termp_d1_pre, termp_d1_post }, /* D1 */ 158 { termp_d1_pre, termp_d1_post }, /* Dl */ 159 { termp_bd_pre, termp_bd_post }, /* Bd */ 160 { NULL, NULL }, /* Ed */ 161 { NULL, termp_bl_post }, /* Bl */ 162 { NULL, NULL }, /* El */ 163 { termp_it_pre, termp_it_post }, /* It */ 164 { NULL, NULL }, /* Ad */ 165 { termp_an_pre, termp_an_post }, /* An */ 166 { termp_under_pre, NULL }, /* Ar */ 167 { termp_cd_pre, NULL }, /* Cd */ 168 { termp_bold_pre, NULL }, /* Cm */ 169 { NULL, NULL }, /* Dv */ 170 { NULL, NULL }, /* Er */ 171 { NULL, NULL }, /* Ev */ 172 { termp_ex_pre, NULL }, /* Ex */ 173 { termp_fa_pre, NULL }, /* Fa */ 174 { termp_bold_pre, termp_fd_post }, /* Fd */ 175 { termp_fl_pre, NULL }, /* Fl */ 176 { termp_fn_pre, termp_fn_post }, /* Fn */ 177 { termp_ft_pre, termp_ft_post }, /* Ft */ 178 { termp_bold_pre, NULL }, /* Ic */ 179 { termp_in_pre, termp_in_post }, /* In */ 180 { NULL, NULL }, /* Li */ 181 { termp_nd_pre, NULL }, /* Nd */ 182 { termp_nm_pre, NULL }, /* Nm */ 183 { termp_op_pre, termp_op_post }, /* Op */ 184 { NULL, NULL }, /* Ot */ 185 { termp_under_pre, NULL }, /* Pa */ 186 { termp_rv_pre, NULL }, /* Rv */ 187 { NULL, NULL }, /* St */ 188 { termp_under_pre, NULL }, /* Va */ 189 { termp_under_pre, termp_vt_post }, /* Vt */ 190 { termp_xr_pre, NULL }, /* Xr */ 191 { NULL, termp____post }, /* %A */ 192 { NULL, termp____post }, /* %B */ 193 { NULL, termp____post }, /* %D */ 194 { NULL, termp____post }, /* %I */ 195 { termp_under_pre, termp____post }, /* %J */ 196 { NULL, termp____post }, /* %N */ 197 { NULL, termp____post }, /* %O */ 198 { NULL, termp____post }, /* %P */ 199 { NULL, termp____post }, /* %R */ 200 { termp_under_pre, termp____post }, /* %T */ 201 { NULL, termp____post }, /* %V */ 202 { NULL, NULL }, /* Ac */ 203 { termp_aq_pre, termp_aq_post }, /* Ao */ 204 { termp_aq_pre, termp_aq_post }, /* Aq */ 205 { NULL, NULL }, /* At */ 206 { NULL, NULL }, /* Bc */ 207 { termp_bf_pre, NULL }, /* Bf */ 208 { termp_bq_pre, termp_bq_post }, /* Bo */ 209 { termp_bq_pre, termp_bq_post }, /* Bq */ 210 { termp_xx_pre, NULL }, /* Bsx */ 211 { NULL, termp_bx_post }, /* Bx */ 212 { NULL, NULL }, /* Db */ 213 { NULL, NULL }, /* Dc */ 214 { termp_dq_pre, termp_dq_post }, /* Do */ 215 { termp_dq_pre, termp_dq_post }, /* Dq */ 216 { NULL, NULL }, /* Ec */ 217 { NULL, NULL }, /* Ef */ 218 { termp_under_pre, NULL }, /* Em */ 219 { NULL, NULL }, /* Eo */ 220 { termp_xx_pre, NULL }, /* Fx */ 221 { termp_bold_pre, NULL }, /* Ms */ 222 { NULL, NULL }, /* No */ 223 { termp_ns_pre, NULL }, /* Ns */ 224 { termp_xx_pre, NULL }, /* Nx */ 225 { termp_xx_pre, NULL }, /* Ox */ 226 { NULL, NULL }, /* Pc */ 227 { termp_pf_pre, termp_pf_post }, /* Pf */ 228 { termp_pq_pre, termp_pq_post }, /* Po */ 229 { termp_pq_pre, termp_pq_post }, /* Pq */ 230 { NULL, NULL }, /* Qc */ 231 { termp_sq_pre, termp_sq_post }, /* Ql */ 232 { termp_qq_pre, termp_qq_post }, /* Qo */ 233 { termp_qq_pre, termp_qq_post }, /* Qq */ 234 { NULL, NULL }, /* Re */ 235 { termp_rs_pre, NULL }, /* Rs */ 236 { NULL, NULL }, /* Sc */ 237 { termp_sq_pre, termp_sq_post }, /* So */ 238 { termp_sq_pre, termp_sq_post }, /* Sq */ 239 { termp_sm_pre, NULL }, /* Sm */ 240 { termp_under_pre, NULL }, /* Sx */ 241 { termp_bold_pre, NULL }, /* Sy */ 242 { NULL, NULL }, /* Tn */ 243 { termp_xx_pre, NULL }, /* Ux */ 244 { NULL, NULL }, /* Xc */ 245 { NULL, NULL }, /* Xo */ 246 { termp_fo_pre, termp_fo_post }, /* Fo */ 247 { NULL, NULL }, /* Fc */ 248 { termp_op_pre, termp_op_post }, /* Oo */ 249 { NULL, NULL }, /* Oc */ 250 { NULL, NULL }, /* Bk */ 251 { NULL, NULL }, /* Ek */ 252 { termp_bt_pre, NULL }, /* Bt */ 253 { NULL, NULL }, /* Hf */ 254 { NULL, NULL }, /* Fr */ 255 { termp_ud_pre, NULL }, /* Ud */ 256 { NULL, termp_lb_post }, /* Lb */ 257 { termp_sp_pre, NULL }, /* Lp */ 258 { termp_lk_pre, NULL }, /* Lk */ 259 { termp_under_pre, NULL }, /* Mt */ 260 { termp_brq_pre, termp_brq_post }, /* Brq */ 261 { termp_brq_pre, termp_brq_post }, /* Bro */ 262 { NULL, NULL }, /* Brc */ 263 { NULL, NULL }, /* %C */ 264 { NULL, NULL }, /* Es */ 265 { NULL, NULL }, /* En */ 266 { termp_xx_pre, NULL }, /* Dx */ 267 { NULL, NULL }, /* %Q */ 268 { termp_sp_pre, NULL }, /* br */ 269 { termp_sp_pre, NULL }, /* sp */ 270 }; 271 272 static int arg_hasattr(int, const struct mdoc_node *); 273 static int arg_getattrs(const int *, int *, size_t, 274 const struct mdoc_node *); 275 static int arg_getattr(int, const struct mdoc_node *); 276 static size_t arg_offset(const struct mdoc_argv *); 277 static size_t arg_width(const struct mdoc_argv *, int); 278 static int arg_listtype(const struct mdoc_node *); 279 static void fmt_block_vspace(struct termp *, 280 const struct mdoc_node *, 281 const struct mdoc_node *); 282 static void print_node(DECL_ARGS); 283 static void print_head(DECL_ARGS); 284 static void print_body(DECL_ARGS); 285 static void print_foot(DECL_ARGS); 286 287 288 void 289 mdoc_run(struct termp *p, const struct mdoc *mdoc) 290 { 291 const struct mdoc_node *n; 292 const struct mdoc_meta *m; 293 294 n = mdoc_node(mdoc); 295 m = mdoc_meta(mdoc); 296 297 print_head(p, NULL, m, n); 298 if (n->child) 299 print_body(p, NULL, m, n->child); 300 print_foot(p, NULL, m, n); 301 } 302 303 304 static void 305 print_body(DECL_ARGS) 306 { 307 308 print_node(p, pair, meta, node); 309 if (node->next) 310 print_body(p, pair, meta, node->next); 311 } 312 313 314 /* ARGSUSED */ 315 static void 316 print_node(DECL_ARGS) 317 { 318 int chld, bold, under; 319 struct termpair npair; 320 size_t offset, rmargin; 321 322 chld = 1; 323 offset = p->offset; 324 rmargin = p->rmargin; 325 bold = p->bold; 326 under = p->under; 327 328 bzero(&npair, sizeof(struct termpair)); 329 npair.ppair = pair; 330 331 if (MDOC_TEXT != node->type) { 332 if (termacts[node->tok].pre) 333 chld = (*termacts[node->tok].pre) 334 (p, &npair, meta, node); 335 } else 336 term_word(p, node->string); 337 338 if (chld && node->child) 339 print_body(p, &npair, meta, node->child); 340 341 /* 342 * XXX - if bold/under were to span scopes, this wouldn't be 343 * possible, but because decoration is always in-scope, we can 344 * get away with this. 345 */ 346 347 p->bold = bold; 348 p->under = under; 349 350 if (MDOC_TEXT != node->type) 351 if (termacts[node->tok].post) 352 (*termacts[node->tok].post) 353 (p, &npair, meta, node); 354 355 p->offset = offset; 356 p->rmargin = rmargin; 357 } 358 359 360 /* ARGSUSED */ 361 static void 362 print_foot(DECL_ARGS) 363 { 364 struct tm *tm; 365 char *buf, *os; 366 367 /* 368 * Output the footer in new-groff style, that is, three columns 369 * with the middle being the manual date and flanking columns 370 * being the operating system: 371 * 372 * SYSTEM DATE SYSTEM 373 */ 374 375 if (NULL == (buf = malloc(p->rmargin))) 376 err(EXIT_FAILURE, "malloc"); 377 if (NULL == (os = malloc(p->rmargin))) 378 err(EXIT_FAILURE, "malloc"); 379 380 tm = localtime(&meta->date); 381 382 if (0 == strftime(buf, p->rmargin, "%B %e, %Y", tm)) 383 err(EXIT_FAILURE, "strftime"); 384 385 (void)strlcpy(os, meta->os, p->rmargin); 386 387 term_vspace(p); 388 389 p->offset = 0; 390 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; 391 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; 392 393 term_word(p, os); 394 term_flushln(p); 395 396 p->offset = p->rmargin; 397 p->rmargin = p->maxrmargin - strlen(os); 398 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 399 400 term_word(p, buf); 401 term_flushln(p); 402 403 p->offset = p->rmargin; 404 p->rmargin = p->maxrmargin; 405 p->flags &= ~TERMP_NOBREAK; 406 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 407 408 term_word(p, os); 409 term_flushln(p); 410 411 p->offset = 0; 412 p->rmargin = p->maxrmargin; 413 p->flags = 0; 414 415 free(buf); 416 free(os); 417 } 418 419 420 /* FIXME: put in utility library. */ 421 /* ARGSUSED */ 422 static void 423 print_head(DECL_ARGS) 424 { 425 char *buf, *title; 426 427 p->rmargin = p->maxrmargin; 428 p->offset = 0; 429 430 if (NULL == (buf = malloc(p->rmargin))) 431 err(EXIT_FAILURE, "malloc"); 432 if (NULL == (title = malloc(p->rmargin))) 433 err(EXIT_FAILURE, "malloc"); 434 435 /* 436 * The header is strange. It has three components, which are 437 * really two with the first duplicated. It goes like this: 438 * 439 * IDENTIFIER TITLE IDENTIFIER 440 * 441 * The IDENTIFIER is NAME(SECTION), which is the command-name 442 * (if given, or "unknown" if not) followed by the manual page 443 * section. These are given in `Dt'. The TITLE is a free-form 444 * string depending on the manual volume. If not specified, it 445 * switches on the manual section. 446 */ 447 448 assert(meta->vol); 449 (void)strlcpy(buf, meta->vol, p->rmargin); 450 451 if (meta->arch) { 452 (void)strlcat(buf, " (", p->rmargin); 453 (void)strlcat(buf, meta->arch, p->rmargin); 454 (void)strlcat(buf, ")", p->rmargin); 455 } 456 457 snprintf(title, p->rmargin, "%s(%d)", meta->title, meta->msec); 458 459 p->offset = 0; 460 p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; 461 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; 462 463 term_word(p, title); 464 term_flushln(p); 465 466 p->offset = p->rmargin; 467 p->rmargin = p->maxrmargin - strlen(title); 468 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 469 470 term_word(p, buf); 471 term_flushln(p); 472 473 p->offset = p->rmargin; 474 p->rmargin = p->maxrmargin; 475 p->flags &= ~TERMP_NOBREAK; 476 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; 477 478 term_word(p, title); 479 term_flushln(p); 480 481 p->offset = 0; 482 p->rmargin = p->maxrmargin; 483 p->flags &= ~TERMP_NOSPACE; 484 485 free(title); 486 free(buf); 487 } 488 489 490 /* FIXME: put in utility file for front-ends. */ 491 static size_t 492 arg_width(const struct mdoc_argv *arg, int pos) 493 { 494 int i, len; 495 const char *p; 496 497 assert(pos < (int)arg->sz && pos >= 0); 498 assert(arg->value[pos]); 499 500 p = arg->value[pos]; 501 502 if (0 == (len = (int)strlen(p))) 503 return(0); 504 505 for (i = 0; i < len - 1; i++) 506 if ( ! isdigit((u_char)p[i])) 507 break; 508 509 if (i == len - 1) 510 if ('n' == p[len - 1] || 'm' == p[len - 1]) 511 return((size_t)atoi(p) + 2); 512 513 return((size_t)len + 2); 514 } 515 516 517 /* FIXME: put in utility file for front-ends. */ 518 static int 519 arg_listtype(const struct mdoc_node *n) 520 { 521 int i, len; 522 523 assert(MDOC_BLOCK == n->type); 524 525 len = (int)(n->args ? n->args->argc : 0); 526 527 for (i = 0; i < len; i++) 528 switch (n->args->argv[i].arg) { 529 case (MDOC_Bullet): 530 /* FALLTHROUGH */ 531 case (MDOC_Dash): 532 /* FALLTHROUGH */ 533 case (MDOC_Enum): 534 /* FALLTHROUGH */ 535 case (MDOC_Hyphen): 536 /* FALLTHROUGH */ 537 case (MDOC_Tag): 538 /* FALLTHROUGH */ 539 case (MDOC_Inset): 540 /* FALLTHROUGH */ 541 case (MDOC_Diag): 542 /* FALLTHROUGH */ 543 case (MDOC_Item): 544 /* FALLTHROUGH */ 545 case (MDOC_Column): 546 /* FALLTHROUGH */ 547 case (MDOC_Hang): 548 /* FALLTHROUGH */ 549 case (MDOC_Ohang): 550 return(n->args->argv[i].arg); 551 default: 552 break; 553 } 554 555 return(-1); 556 } 557 558 559 /* FIXME: put in utility file for front-ends. */ 560 static size_t 561 arg_offset(const struct mdoc_argv *arg) 562 { 563 int len, i; 564 const char *p; 565 566 assert(*arg->value); 567 p = *arg->value; 568 569 if (0 == strcmp(p, "left")) 570 return(0); 571 if (0 == strcmp(p, "indent")) 572 return(INDENT + 1); 573 if (0 == strcmp(p, "indent-two")) 574 return((INDENT + 1) * 2); 575 576 if (0 == (len = (int)strlen(p))) 577 return(0); 578 579 for (i = 0; i < len - 1; i++) 580 if ( ! isdigit((u_char)p[i])) 581 break; 582 583 if (i == len - 1) 584 if ('n' == p[len - 1] || 'm' == p[len - 1]) 585 return((size_t)atoi(p)); 586 587 return((size_t)len); 588 } 589 590 591 static int 592 arg_hasattr(int arg, const struct mdoc_node *n) 593 { 594 595 return(-1 != arg_getattr(arg, n)); 596 } 597 598 599 static int 600 arg_getattr(int v, const struct mdoc_node *n) 601 { 602 int val; 603 604 return(arg_getattrs(&v, &val, 1, n) ? val : -1); 605 } 606 607 608 static int 609 arg_getattrs(const int *keys, int *vals, 610 size_t sz, const struct mdoc_node *n) 611 { 612 int i, j, k; 613 614 if (NULL == n->args) 615 return(0); 616 617 for (k = i = 0; i < (int)n->args->argc; i++) 618 for (j = 0; j < (int)sz; j++) 619 if (n->args->argv[i].arg == keys[j]) { 620 vals[j] = i; 621 k++; 622 } 623 return(k); 624 } 625 626 627 /* ARGSUSED */ 628 static void 629 fmt_block_vspace(struct termp *p, 630 const struct mdoc_node *bl, 631 const struct mdoc_node *node) 632 { 633 const struct mdoc_node *n; 634 635 term_newln(p); 636 637 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Compact, bl)) 638 return; 639 assert(node); 640 641 /* 642 * Search through our prior nodes. If we follow a `Ss' or `Sh', 643 * then don't vspace. 644 */ 645 646 for (n = node; n; n = n->parent) { 647 if (MDOC_BLOCK != n->type) 648 continue; 649 if (MDOC_Ss == n->tok) 650 return; 651 if (MDOC_Sh == n->tok) 652 return; 653 if (NULL == n->prev) 654 continue; 655 break; 656 } 657 658 /* 659 * XXX - not documented: a `-column' does not ever assert vspace 660 * within the list. 661 */ 662 663 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Column, bl)) 664 if (node->prev && MDOC_It == node->prev->tok) 665 return; 666 667 /* 668 * XXX - not documented: a `-diag' without a body does not 669 * assert a vspace prior to the next element. 670 */ 671 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Diag, bl)) 672 if (node->prev && MDOC_It == node->prev->tok) { 673 assert(node->prev->body); 674 if (NULL == node->prev->body->child) 675 return; 676 } 677 678 term_vspace(p); 679 } 680 681 682 /* ARGSUSED */ 683 static int 684 termp_dq_pre(DECL_ARGS) 685 { 686 687 if (MDOC_BODY != node->type) 688 return(1); 689 690 term_word(p, "\\(lq"); 691 p->flags |= TERMP_NOSPACE; 692 return(1); 693 } 694 695 696 /* ARGSUSED */ 697 static void 698 termp_dq_post(DECL_ARGS) 699 { 700 701 if (MDOC_BODY != node->type) 702 return; 703 704 p->flags |= TERMP_NOSPACE; 705 term_word(p, "\\(rq"); 706 } 707 708 709 /* ARGSUSED */ 710 static int 711 termp_it_pre(DECL_ARGS) 712 { 713 const struct mdoc_node *bl, *n; 714 char buf[7]; 715 int i, type, keys[3], vals[3]; 716 size_t width, offset; 717 718 if (MDOC_BLOCK == node->type) { 719 fmt_block_vspace(p, node->parent->parent, node); 720 return(1); 721 } 722 723 bl = node->parent->parent->parent; 724 725 /* Save parent attributes. */ 726 727 pair->flag = p->flags; 728 729 /* Get list width and offset. */ 730 731 keys[0] = MDOC_Width; 732 keys[1] = MDOC_Offset; 733 keys[2] = MDOC_Column; 734 735 vals[0] = vals[1] = vals[2] = -1; 736 737 width = offset = 0; 738 739 (void)arg_getattrs(keys, vals, 3, bl); 740 741 type = arg_listtype(bl); 742 assert(-1 != type); 743 744 /* Calculate real width and offset. */ 745 746 switch (type) { 747 case (MDOC_Column): 748 if (MDOC_BODY == node->type) 749 break; 750 /* 751 * Work around groff's column handling. The offset is 752 * equal to the sum of all widths leading to the current 753 * column (plus the -offset value). If this column 754 * exceeds the stated number of columns, the width is 755 * set as 0, else it's the stated column width (later 756 * the 0 will be adjusted to default 10 or, if in the 757 * last column case, set to stretch to the margin). 758 */ 759 for (i = 0, n = node->prev; n && 760 i < (int)bl->args[vals[2]].argv->sz; 761 n = n->prev, i++) 762 offset += arg_width 763 (&bl->args->argv[vals[2]], i); 764 765 /* Whether exceeds maximum column. */ 766 if (i < (int)bl->args[vals[2]].argv->sz) 767 width = arg_width(&bl->args->argv[vals[2]], i); 768 else 769 width = 0; 770 771 if (vals[1] >= 0) 772 offset += arg_offset(&bl->args->argv[vals[1]]); 773 break; 774 default: 775 if (vals[0] >= 0) 776 width = arg_width(&bl->args->argv[vals[0]], 0); 777 if (vals[1] >= 0) 778 offset += arg_offset(&bl->args->argv[vals[1]]); 779 break; 780 } 781 782 /* 783 * List-type can override the width in the case of fixed-head 784 * values (bullet, dash/hyphen, enum). Tags need a non-zero 785 * offset. 786 */ 787 788 switch (type) { 789 case (MDOC_Bullet): 790 /* FALLTHROUGH */ 791 case (MDOC_Dash): 792 /* FALLTHROUGH */ 793 case (MDOC_Hyphen): 794 if (width < 4) 795 width = 4; 796 break; 797 case (MDOC_Enum): 798 if (width < 5) 799 width = 5; 800 break; 801 case (MDOC_Hang): 802 if (0 == width) 803 width = 8; 804 break; 805 case (MDOC_Column): 806 /* FALLTHROUGH */ 807 case (MDOC_Tag): 808 if (0 == width) 809 width = 10; 810 break; 811 default: 812 break; 813 } 814 815 /* 816 * Whitespace control. Inset bodies need an initial space, 817 * while diagonal bodies need two. 818 */ 819 820 p->flags |= TERMP_NOSPACE; 821 822 switch (type) { 823 case (MDOC_Diag): 824 if (MDOC_BODY == node->type) 825 term_word(p, "\\ \\ "); 826 break; 827 case (MDOC_Inset): 828 if (MDOC_BODY == node->type) 829 term_word(p, "\\ "); 830 break; 831 default: 832 break; 833 } 834 835 p->flags |= TERMP_NOSPACE; 836 837 switch (type) { 838 case (MDOC_Diag): 839 if (MDOC_HEAD == node->type) 840 p->bold++; 841 break; 842 default: 843 break; 844 } 845 846 /* 847 * Pad and break control. This is the tricker part. Lists with 848 * set right-margins for the head get TERMP_NOBREAK because, if 849 * they overrun the margin, they wrap to the new margin. 850 * Correspondingly, the body for these types don't left-pad, as 851 * the head will pad out to to the right. 852 */ 853 854 switch (type) { 855 case (MDOC_Bullet): 856 /* FALLTHROUGH */ 857 case (MDOC_Dash): 858 /* FALLTHROUGH */ 859 case (MDOC_Enum): 860 /* FALLTHROUGH */ 861 case (MDOC_Hyphen): 862 if (MDOC_HEAD == node->type) 863 p->flags |= TERMP_NOBREAK; 864 else 865 p->flags |= TERMP_NOLPAD; 866 break; 867 case (MDOC_Hang): 868 if (MDOC_HEAD == node->type) 869 p->flags |= TERMP_NOBREAK; 870 else 871 p->flags |= TERMP_NOLPAD; 872 873 if (MDOC_HEAD != node->type) 874 break; 875 876 /* 877 * This is ugly. If `-hang' is specified and the body 878 * is a `Bl' or `Bd', then we want basically to nullify 879 * the "overstep" effect in term_flushln() and treat 880 * this as a `-ohang' list instead. 881 */ 882 if (node->next->child && 883 (MDOC_Bl == node->next->child->tok || 884 MDOC_Bd == node->next->child->tok)) { 885 p->flags &= ~TERMP_NOBREAK; 886 p->flags &= ~TERMP_NOLPAD; 887 } else 888 p->flags |= TERMP_HANG; 889 break; 890 case (MDOC_Tag): 891 if (MDOC_HEAD == node->type) 892 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE; 893 else 894 p->flags |= TERMP_NOLPAD; 895 896 if (MDOC_HEAD != node->type) 897 break; 898 if (NULL == node->next || NULL == node->next->child) 899 p->flags |= TERMP_DANGLE; 900 break; 901 case (MDOC_Column): 902 if (MDOC_HEAD == node->type) { 903 assert(node->next); 904 if (MDOC_BODY == node->next->type) 905 p->flags &= ~TERMP_NOBREAK; 906 else 907 p->flags |= TERMP_NOBREAK; 908 if (node->prev) 909 p->flags |= TERMP_NOLPAD; 910 } 911 break; 912 case (MDOC_Diag): 913 if (MDOC_HEAD == node->type) 914 p->flags |= TERMP_NOBREAK; 915 break; 916 default: 917 break; 918 } 919 920 /* 921 * Margin control. Set-head-width lists have their right 922 * margins shortened. The body for these lists has the offset 923 * necessarily lengthened. Everybody gets the offset. 924 */ 925 926 p->offset += offset; 927 928 switch (type) { 929 case (MDOC_Hang): 930 /* 931 * Same stipulation as above, regarding `-hang'. We 932 * don't want to recalculate rmargin and offsets when 933 * using `Bd' or `Bl' within `-hang' overstep lists. 934 */ 935 if (MDOC_HEAD == node->type && node->next->child && 936 (MDOC_Bl == node->next->child->tok || 937 MDOC_Bd == node->next->child->tok)) 938 break; 939 /* FALLTHROUGH */ 940 case (MDOC_Bullet): 941 /* FALLTHROUGH */ 942 case (MDOC_Dash): 943 /* FALLTHROUGH */ 944 case (MDOC_Enum): 945 /* FALLTHROUGH */ 946 case (MDOC_Hyphen): 947 /* FALLTHROUGH */ 948 case (MDOC_Tag): 949 assert(width); 950 if (MDOC_HEAD == node->type) 951 p->rmargin = p->offset + width; 952 else 953 p->offset += width; 954 break; 955 case (MDOC_Column): 956 assert(width); 957 p->rmargin = p->offset + width; 958 /* 959 * XXX - this behaviour is not documented: the 960 * right-most column is filled to the right margin. 961 */ 962 if (MDOC_HEAD == node->type && 963 MDOC_BODY == node->next->type) 964 p->rmargin = p->maxrmargin; 965 break; 966 default: 967 break; 968 } 969 970 /* 971 * The dash, hyphen, bullet and enum lists all have a special 972 * HEAD character (temporarily bold, in some cases). 973 */ 974 975 if (MDOC_HEAD == node->type) 976 switch (type) { 977 case (MDOC_Bullet): 978 p->bold++; 979 term_word(p, "\\[bu]"); 980 p->bold--; 981 break; 982 case (MDOC_Dash): 983 /* FALLTHROUGH */ 984 case (MDOC_Hyphen): 985 p->bold++; 986 term_word(p, "\\(hy"); 987 p->bold--; 988 break; 989 case (MDOC_Enum): 990 (pair->ppair->ppair->count)++; 991 (void)snprintf(buf, sizeof(buf), "%d.", 992 pair->ppair->ppair->count); 993 term_word(p, buf); 994 break; 995 default: 996 break; 997 } 998 999 /* 1000 * If we're not going to process our children, indicate so here. 1001 */ 1002 1003 switch (type) { 1004 case (MDOC_Bullet): 1005 /* FALLTHROUGH */ 1006 case (MDOC_Item): 1007 /* FALLTHROUGH */ 1008 case (MDOC_Dash): 1009 /* FALLTHROUGH */ 1010 case (MDOC_Hyphen): 1011 /* FALLTHROUGH */ 1012 case (MDOC_Enum): 1013 if (MDOC_HEAD == node->type) 1014 return(0); 1015 break; 1016 case (MDOC_Column): 1017 if (MDOC_BODY == node->type) 1018 return(0); 1019 break; 1020 default: 1021 break; 1022 } 1023 1024 return(1); 1025 } 1026 1027 1028 /* ARGSUSED */ 1029 static void 1030 termp_it_post(DECL_ARGS) 1031 { 1032 int type; 1033 1034 if (MDOC_BODY != node->type && MDOC_HEAD != node->type) 1035 return; 1036 1037 type = arg_listtype(node->parent->parent->parent); 1038 assert(-1 != type); 1039 1040 switch (type) { 1041 case (MDOC_Item): 1042 /* FALLTHROUGH */ 1043 case (MDOC_Diag): 1044 /* FALLTHROUGH */ 1045 case (MDOC_Inset): 1046 if (MDOC_BODY == node->type) 1047 term_flushln(p); 1048 break; 1049 case (MDOC_Column): 1050 if (MDOC_HEAD == node->type) 1051 term_flushln(p); 1052 break; 1053 default: 1054 term_flushln(p); 1055 break; 1056 } 1057 1058 p->flags = pair->flag; 1059 } 1060 1061 1062 /* ARGSUSED */ 1063 static int 1064 termp_nm_pre(DECL_ARGS) 1065 { 1066 1067 if (SEC_SYNOPSIS == node->sec) 1068 term_newln(p); 1069 p->bold++; 1070 if (NULL == node->child) 1071 term_word(p, meta->name); 1072 return(1); 1073 } 1074 1075 1076 /* ARGSUSED */ 1077 static int 1078 termp_fl_pre(DECL_ARGS) 1079 { 1080 1081 p->bold++; 1082 term_word(p, "\\-"); 1083 p->flags |= TERMP_NOSPACE; 1084 return(1); 1085 } 1086 1087 1088 /* ARGSUSED */ 1089 static int 1090 termp_an_pre(DECL_ARGS) 1091 { 1092 1093 if (NULL == node->child) 1094 return(1); 1095 1096 /* 1097 * XXX: this is poorly documented. If not in the AUTHORS 1098 * section, `An -split' will cause newlines to occur before the 1099 * author name. If in the AUTHORS section, by default, the 1100 * first `An' invocation is nosplit, then all subsequent ones, 1101 * regardless of whether interspersed with other macros/text, 1102 * are split. -split, in this case, will override the condition 1103 * of the implied first -nosplit. 1104 */ 1105 1106 if (node->sec == SEC_AUTHORS) { 1107 if ( ! (TERMP_ANPREC & p->flags)) { 1108 if (TERMP_SPLIT & p->flags) 1109 term_newln(p); 1110 return(1); 1111 } 1112 if (TERMP_NOSPLIT & p->flags) 1113 return(1); 1114 term_newln(p); 1115 return(1); 1116 } 1117 1118 if (TERMP_SPLIT & p->flags) 1119 term_newln(p); 1120 1121 return(1); 1122 } 1123 1124 1125 /* ARGSUSED */ 1126 static void 1127 termp_an_post(DECL_ARGS) 1128 { 1129 1130 if (node->child) { 1131 if (SEC_AUTHORS == node->sec) 1132 p->flags |= TERMP_ANPREC; 1133 return; 1134 } 1135 1136 if (arg_getattr(MDOC_Split, node) > -1) { 1137 p->flags &= ~TERMP_NOSPLIT; 1138 p->flags |= TERMP_SPLIT; 1139 } else { 1140 p->flags &= ~TERMP_SPLIT; 1141 p->flags |= TERMP_NOSPLIT; 1142 } 1143 1144 } 1145 1146 1147 /* ARGSUSED */ 1148 static int 1149 termp_ns_pre(DECL_ARGS) 1150 { 1151 1152 p->flags |= TERMP_NOSPACE; 1153 return(1); 1154 } 1155 1156 1157 /* ARGSUSED */ 1158 static int 1159 termp_rs_pre(DECL_ARGS) 1160 { 1161 1162 if (MDOC_BLOCK == node->type && node->prev) 1163 term_vspace(p); 1164 return(1); 1165 } 1166 1167 1168 /* ARGSUSED */ 1169 static int 1170 termp_rv_pre(DECL_ARGS) 1171 { 1172 const struct mdoc_node *nn; 1173 1174 term_newln(p); 1175 term_word(p, "The"); 1176 1177 nn = node->child; 1178 assert(nn); 1179 for ( ; nn; nn = nn->next) { 1180 p->bold++; 1181 term_word(p, nn->string); 1182 p->bold--; 1183 p->flags |= TERMP_NOSPACE; 1184 if (nn->next && NULL == nn->next->next) 1185 term_word(p, "(), and"); 1186 else if (nn->next) 1187 term_word(p, "(),"); 1188 else 1189 term_word(p, "()"); 1190 } 1191 1192 if (node->child->next) 1193 term_word(p, "functions return"); 1194 else 1195 term_word(p, "function returns"); 1196 1197 term_word(p, "the value 0 if successful; otherwise the value " 1198 "-1 is returned and the global variable"); 1199 1200 p->under++; 1201 term_word(p, "errno"); 1202 p->under--; 1203 1204 term_word(p, "is set to indicate the error."); 1205 1206 return(0); 1207 } 1208 1209 1210 /* ARGSUSED */ 1211 static int 1212 termp_ex_pre(DECL_ARGS) 1213 { 1214 const struct mdoc_node *nn; 1215 1216 term_word(p, "The"); 1217 1218 nn = node->child; 1219 assert(nn); 1220 for ( ; nn; nn = nn->next) { 1221 p->bold++; 1222 term_word(p, nn->string); 1223 p->bold--; 1224 p->flags |= TERMP_NOSPACE; 1225 if (nn->next && NULL == nn->next->next) 1226 term_word(p, ", and"); 1227 else if (nn->next) 1228 term_word(p, ","); 1229 else 1230 p->flags &= ~TERMP_NOSPACE; 1231 } 1232 1233 if (node->child->next) 1234 term_word(p, "utilities exit"); 1235 else 1236 term_word(p, "utility exits"); 1237 1238 term_word(p, "0 on success, and >0 if an error occurs."); 1239 1240 return(0); 1241 } 1242 1243 1244 /* ARGSUSED */ 1245 static int 1246 termp_nd_pre(DECL_ARGS) 1247 { 1248 1249 if (MDOC_BODY != node->type) 1250 return(1); 1251 1252 #if defined(__OpenBSD__) || defined(__linux__) 1253 term_word(p, "\\(en"); 1254 #else 1255 term_word(p, "\\(em"); 1256 #endif 1257 return(1); 1258 } 1259 1260 1261 /* ARGSUSED */ 1262 static void 1263 termp_bl_post(DECL_ARGS) 1264 { 1265 1266 if (MDOC_BLOCK == node->type) 1267 term_newln(p); 1268 } 1269 1270 1271 /* ARGSUSED */ 1272 static void 1273 termp_op_post(DECL_ARGS) 1274 { 1275 1276 if (MDOC_BODY != node->type) 1277 return; 1278 p->flags |= TERMP_NOSPACE; 1279 term_word(p, "\\(rB"); 1280 } 1281 1282 1283 /* ARGSUSED */ 1284 static int 1285 termp_xr_pre(DECL_ARGS) 1286 { 1287 const struct mdoc_node *n; 1288 1289 assert(node->child && MDOC_TEXT == node->child->type); 1290 n = node->child; 1291 1292 term_word(p, n->string); 1293 if (NULL == (n = n->next)) 1294 return(0); 1295 p->flags |= TERMP_NOSPACE; 1296 term_word(p, "("); 1297 p->flags |= TERMP_NOSPACE; 1298 term_word(p, n->string); 1299 p->flags |= TERMP_NOSPACE; 1300 term_word(p, ")"); 1301 return(0); 1302 } 1303 1304 1305 /* ARGSUSED */ 1306 static void 1307 termp_vt_post(DECL_ARGS) 1308 { 1309 1310 if (node->sec != SEC_SYNOPSIS) 1311 return; 1312 if (node->next && MDOC_Vt == node->next->tok) 1313 term_newln(p); 1314 else if (node->next) 1315 term_vspace(p); 1316 } 1317 1318 1319 /* ARGSUSED */ 1320 static int 1321 termp_bold_pre(DECL_ARGS) 1322 { 1323 1324 p->bold++; 1325 return(1); 1326 } 1327 1328 1329 /* ARGSUSED */ 1330 static void 1331 termp_fd_post(DECL_ARGS) 1332 { 1333 1334 if (node->sec != SEC_SYNOPSIS) 1335 return; 1336 1337 term_newln(p); 1338 if (node->next && MDOC_Fd != node->next->tok) 1339 term_vspace(p); 1340 } 1341 1342 1343 /* ARGSUSED */ 1344 static int 1345 termp_sh_pre(DECL_ARGS) 1346 { 1347 /* 1348 * XXX: undocumented: using two `Sh' macros in sequence has no 1349 * vspace between calls, only a newline. 1350 */ 1351 switch (node->type) { 1352 case (MDOC_BLOCK): 1353 if (node->prev && MDOC_Sh == node->prev->tok) 1354 if (NULL == node->prev->body->child) 1355 break; 1356 term_vspace(p); 1357 break; 1358 case (MDOC_HEAD): 1359 p->bold++; 1360 break; 1361 case (MDOC_BODY): 1362 p->offset = INDENT; 1363 break; 1364 default: 1365 break; 1366 } 1367 return(1); 1368 } 1369 1370 1371 /* ARGSUSED */ 1372 static void 1373 termp_sh_post(DECL_ARGS) 1374 { 1375 1376 switch (node->type) { 1377 case (MDOC_HEAD): 1378 term_newln(p); 1379 break; 1380 case (MDOC_BODY): 1381 term_newln(p); 1382 p->offset = 0; 1383 break; 1384 default: 1385 break; 1386 } 1387 } 1388 1389 1390 /* ARGSUSED */ 1391 static int 1392 termp_op_pre(DECL_ARGS) 1393 { 1394 1395 switch (node->type) { 1396 case (MDOC_BODY): 1397 term_word(p, "\\(lB"); 1398 p->flags |= TERMP_NOSPACE; 1399 break; 1400 default: 1401 break; 1402 } 1403 return(1); 1404 } 1405 1406 1407 /* ARGSUSED */ 1408 static int 1409 termp_bt_pre(DECL_ARGS) 1410 { 1411 1412 term_word(p, "is currently in beta test."); 1413 return(1); 1414 } 1415 1416 1417 /* ARGSUSED */ 1418 static void 1419 termp_lb_post(DECL_ARGS) 1420 { 1421 1422 term_newln(p); 1423 } 1424 1425 1426 /* ARGSUSED */ 1427 static int 1428 termp_ud_pre(DECL_ARGS) 1429 { 1430 1431 term_word(p, "currently under development."); 1432 return(1); 1433 } 1434 1435 1436 /* ARGSUSED */ 1437 static int 1438 termp_d1_pre(DECL_ARGS) 1439 { 1440 1441 if (MDOC_BLOCK != node->type) 1442 return(1); 1443 term_newln(p); 1444 p->offset += (INDENT + 1); 1445 return(1); 1446 } 1447 1448 1449 /* ARGSUSED */ 1450 static void 1451 termp_d1_post(DECL_ARGS) 1452 { 1453 1454 if (MDOC_BLOCK != node->type) 1455 return; 1456 term_newln(p); 1457 } 1458 1459 1460 /* ARGSUSED */ 1461 static int 1462 termp_aq_pre(DECL_ARGS) 1463 { 1464 1465 if (MDOC_BODY != node->type) 1466 return(1); 1467 term_word(p, "\\(la"); 1468 p->flags |= TERMP_NOSPACE; 1469 return(1); 1470 } 1471 1472 1473 /* ARGSUSED */ 1474 static void 1475 termp_aq_post(DECL_ARGS) 1476 { 1477 1478 if (MDOC_BODY != node->type) 1479 return; 1480 p->flags |= TERMP_NOSPACE; 1481 term_word(p, "\\(ra"); 1482 } 1483 1484 1485 /* ARGSUSED */ 1486 static int 1487 termp_ft_pre(DECL_ARGS) 1488 { 1489 1490 if (SEC_SYNOPSIS == node->sec) 1491 if (node->prev && MDOC_Fo == node->prev->tok) 1492 term_vspace(p); 1493 p->under++; 1494 return(1); 1495 } 1496 1497 1498 /* ARGSUSED */ 1499 static void 1500 termp_ft_post(DECL_ARGS) 1501 { 1502 1503 if (SEC_SYNOPSIS == node->sec) 1504 term_newln(p); 1505 } 1506 1507 1508 /* ARGSUSED */ 1509 static int 1510 termp_fn_pre(DECL_ARGS) 1511 { 1512 const struct mdoc_node *n; 1513 1514 assert(node->child && MDOC_TEXT == node->child->type); 1515 1516 /* FIXME: can be "type funcname" "type varname"... */ 1517 1518 p->bold++; 1519 term_word(p, node->child->string); 1520 p->bold--; 1521 1522 p->flags |= TERMP_NOSPACE; 1523 term_word(p, "("); 1524 1525 for (n = node->child->next; n; n = n->next) { 1526 p->under++; 1527 term_word(p, n->string); 1528 p->under--; 1529 if (n->next) 1530 term_word(p, ","); 1531 } 1532 1533 term_word(p, ")"); 1534 1535 if (SEC_SYNOPSIS == node->sec) 1536 term_word(p, ";"); 1537 1538 return(0); 1539 } 1540 1541 1542 /* ARGSUSED */ 1543 static void 1544 termp_fn_post(DECL_ARGS) 1545 { 1546 1547 if (node->sec == SEC_SYNOPSIS && node->next) 1548 term_vspace(p); 1549 } 1550 1551 1552 /* ARGSUSED */ 1553 static int 1554 termp_fa_pre(DECL_ARGS) 1555 { 1556 struct mdoc_node *n; 1557 1558 if (node->parent->tok != MDOC_Fo) { 1559 p->under++; 1560 return(1); 1561 } 1562 1563 for (n = node->child; n; n = n->next) { 1564 p->under++; 1565 term_word(p, n->string); 1566 p->under--; 1567 if (n->next) 1568 term_word(p, ","); 1569 } 1570 1571 if (node->child && node->next && node->next->tok == MDOC_Fa) 1572 term_word(p, ","); 1573 1574 return(0); 1575 } 1576 1577 1578 /* ARGSUSED */ 1579 static int 1580 termp_bd_pre(DECL_ARGS) 1581 { 1582 int i, type; 1583 1584 /* 1585 * This is fairly tricky due primarily to crappy documentation. 1586 * If -ragged or -filled are specified, the block does nothing 1587 * but change the indentation. 1588 * 1589 * If, on the other hand, -unfilled or -literal are specified, 1590 * then the game changes. Text is printed exactly as entered in 1591 * the display: if a macro line, a newline is appended to the 1592 * line. Blank lines are allowed. 1593 */ 1594 1595 if (MDOC_BLOCK == node->type) { 1596 fmt_block_vspace(p, node, node); 1597 return(1); 1598 } else if (MDOC_BODY != node->type) 1599 return(1); 1600 1601 assert(node->parent->args); 1602 1603 for (type = -1, i = 0; -1 == type && 1604 i < (int)node->parent->args->argc; i++) { 1605 switch (node->parent->args->argv[i].arg) { 1606 case (MDOC_Ragged): 1607 /* FALLTHROUGH */ 1608 case (MDOC_Filled): 1609 /* FALLTHROUGH */ 1610 case (MDOC_Unfilled): 1611 /* FALLTHROUGH */ 1612 case (MDOC_Literal): 1613 type = node->parent->args->argv[i].arg; 1614 break; 1615 default: 1616 break; 1617 } 1618 } 1619 1620 assert(type > -1); 1621 1622 i = arg_getattr(MDOC_Offset, node->parent); 1623 if (-1 != i) 1624 p->offset += arg_offset(&node->parent->args->argv[i]); 1625 1626 switch (type) { 1627 case (MDOC_Literal): 1628 /* FALLTHROUGH */ 1629 case (MDOC_Unfilled): 1630 break; 1631 default: 1632 return(1); 1633 } 1634 1635 for (node = node->child; node; node = node->next) { 1636 p->flags |= TERMP_NOSPACE; 1637 print_node(p, pair, meta, node); 1638 if (node->next) 1639 term_flushln(p); 1640 } 1641 1642 return(0); 1643 } 1644 1645 1646 /* ARGSUSED */ 1647 static void 1648 termp_bd_post(DECL_ARGS) 1649 { 1650 1651 if (MDOC_BODY != node->type) 1652 return; 1653 p->flags |= TERMP_NOSPACE; 1654 term_flushln(p); 1655 } 1656 1657 1658 /* ARGSUSED */ 1659 static int 1660 termp_qq_pre(DECL_ARGS) 1661 { 1662 1663 if (MDOC_BODY != node->type) 1664 return(1); 1665 term_word(p, "\""); 1666 p->flags |= TERMP_NOSPACE; 1667 return(1); 1668 } 1669 1670 1671 /* ARGSUSED */ 1672 static void 1673 termp_qq_post(DECL_ARGS) 1674 { 1675 1676 if (MDOC_BODY != node->type) 1677 return; 1678 p->flags |= TERMP_NOSPACE; 1679 term_word(p, "\""); 1680 } 1681 1682 1683 /* ARGSUSED */ 1684 static void 1685 termp_bx_post(DECL_ARGS) 1686 { 1687 1688 if (node->child) 1689 p->flags |= TERMP_NOSPACE; 1690 term_word(p, "BSD"); 1691 } 1692 1693 1694 /* ARGSUSED */ 1695 static int 1696 termp_xx_pre(DECL_ARGS) 1697 { 1698 const char *pp; 1699 1700 pp = NULL; 1701 switch (node->tok) { 1702 case (MDOC_Bsx): 1703 pp = "BSDI BSD/OS"; 1704 break; 1705 case (MDOC_Dx): 1706 pp = "DragonFlyBSD"; 1707 break; 1708 case (MDOC_Fx): 1709 pp = "FreeBSD"; 1710 break; 1711 case (MDOC_Nx): 1712 pp = "NetBSD"; 1713 break; 1714 case (MDOC_Ox): 1715 pp = "OpenBSD"; 1716 break; 1717 case (MDOC_Ux): 1718 pp = "UNIX"; 1719 break; 1720 default: 1721 break; 1722 } 1723 1724 assert(pp); 1725 term_word(p, pp); 1726 return(1); 1727 } 1728 1729 1730 /* ARGSUSED */ 1731 static int 1732 termp_sq_pre(DECL_ARGS) 1733 { 1734 1735 if (MDOC_BODY != node->type) 1736 return(1); 1737 term_word(p, "\\(oq"); 1738 p->flags |= TERMP_NOSPACE; 1739 return(1); 1740 } 1741 1742 1743 /* ARGSUSED */ 1744 static void 1745 termp_sq_post(DECL_ARGS) 1746 { 1747 1748 if (MDOC_BODY != node->type) 1749 return; 1750 p->flags |= TERMP_NOSPACE; 1751 term_word(p, "\\(aq"); 1752 } 1753 1754 1755 /* ARGSUSED */ 1756 static int 1757 termp_pf_pre(DECL_ARGS) 1758 { 1759 1760 p->flags |= TERMP_IGNDELIM; 1761 return(1); 1762 } 1763 1764 1765 /* ARGSUSED */ 1766 static void 1767 termp_pf_post(DECL_ARGS) 1768 { 1769 1770 p->flags &= ~TERMP_IGNDELIM; 1771 p->flags |= TERMP_NOSPACE; 1772 } 1773 1774 1775 /* ARGSUSED */ 1776 static int 1777 termp_ss_pre(DECL_ARGS) 1778 { 1779 1780 switch (node->type) { 1781 case (MDOC_BLOCK): 1782 term_newln(p); 1783 if (node->prev) 1784 term_vspace(p); 1785 break; 1786 case (MDOC_HEAD): 1787 p->bold++; 1788 p->offset = HALFINDENT; 1789 break; 1790 default: 1791 break; 1792 } 1793 1794 return(1); 1795 } 1796 1797 1798 /* ARGSUSED */ 1799 static void 1800 termp_ss_post(DECL_ARGS) 1801 { 1802 1803 if (MDOC_HEAD == node->type) 1804 term_newln(p); 1805 } 1806 1807 1808 /* ARGSUSED */ 1809 static int 1810 termp_cd_pre(DECL_ARGS) 1811 { 1812 1813 p->bold++; 1814 term_newln(p); 1815 return(1); 1816 } 1817 1818 1819 /* ARGSUSED */ 1820 static int 1821 termp_in_pre(DECL_ARGS) 1822 { 1823 1824 p->bold++; 1825 if (SEC_SYNOPSIS == node->sec) 1826 term_word(p, "#include"); 1827 1828 term_word(p, "<"); 1829 p->flags |= TERMP_NOSPACE; 1830 return(1); 1831 } 1832 1833 1834 /* ARGSUSED */ 1835 static void 1836 termp_in_post(DECL_ARGS) 1837 { 1838 1839 p->bold++; 1840 term_word(p, ">"); 1841 p->bold--; 1842 1843 if (SEC_SYNOPSIS != node->sec) 1844 return; 1845 1846 term_newln(p); 1847 /* 1848 * XXX Not entirely correct. If `.In foo bar' is specified in 1849 * the SYNOPSIS section, then it produces a single break after 1850 * the <foo>; mandoc asserts a vertical space. Since this 1851 * construction is rarely used, I think it's fine. 1852 */ 1853 if (node->next && MDOC_In != node->next->tok) 1854 term_vspace(p); 1855 } 1856 1857 1858 /* ARGSUSED */ 1859 static int 1860 termp_sp_pre(DECL_ARGS) 1861 { 1862 int i, len; 1863 1864 switch (node->tok) { 1865 case (MDOC_sp): 1866 len = node->child ? atoi(node->child->string) : 1; 1867 break; 1868 case (MDOC_br): 1869 len = 0; 1870 break; 1871 default: 1872 len = 1; 1873 break; 1874 } 1875 1876 if (0 == len) 1877 term_newln(p); 1878 for (i = 0; i < len; i++) 1879 term_vspace(p); 1880 1881 return(0); 1882 } 1883 1884 1885 /* ARGSUSED */ 1886 static int 1887 termp_brq_pre(DECL_ARGS) 1888 { 1889 1890 if (MDOC_BODY != node->type) 1891 return(1); 1892 term_word(p, "\\(lC"); 1893 p->flags |= TERMP_NOSPACE; 1894 return(1); 1895 } 1896 1897 1898 /* ARGSUSED */ 1899 static void 1900 termp_brq_post(DECL_ARGS) 1901 { 1902 1903 if (MDOC_BODY != node->type) 1904 return; 1905 p->flags |= TERMP_NOSPACE; 1906 term_word(p, "\\(rC"); 1907 } 1908 1909 1910 /* ARGSUSED */ 1911 static int 1912 termp_bq_pre(DECL_ARGS) 1913 { 1914 1915 if (MDOC_BODY != node->type) 1916 return(1); 1917 term_word(p, "\\(lB"); 1918 p->flags |= TERMP_NOSPACE; 1919 return(1); 1920 } 1921 1922 1923 /* ARGSUSED */ 1924 static void 1925 termp_bq_post(DECL_ARGS) 1926 { 1927 1928 if (MDOC_BODY != node->type) 1929 return; 1930 p->flags |= TERMP_NOSPACE; 1931 term_word(p, "\\(rB"); 1932 } 1933 1934 1935 /* ARGSUSED */ 1936 static int 1937 termp_pq_pre(DECL_ARGS) 1938 { 1939 1940 if (MDOC_BODY != node->type) 1941 return(1); 1942 term_word(p, "\\&("); 1943 p->flags |= TERMP_NOSPACE; 1944 return(1); 1945 } 1946 1947 1948 /* ARGSUSED */ 1949 static void 1950 termp_pq_post(DECL_ARGS) 1951 { 1952 1953 if (MDOC_BODY != node->type) 1954 return; 1955 term_word(p, ")"); 1956 } 1957 1958 1959 /* ARGSUSED */ 1960 static int 1961 termp_fo_pre(DECL_ARGS) 1962 { 1963 const struct mdoc_node *n; 1964 1965 if (MDOC_BODY == node->type) { 1966 p->flags |= TERMP_NOSPACE; 1967 term_word(p, "("); 1968 p->flags |= TERMP_NOSPACE; 1969 return(1); 1970 } else if (MDOC_HEAD != node->type) 1971 return(1); 1972 1973 p->bold++; 1974 for (n = node->child; n; n = n->next) { 1975 assert(MDOC_TEXT == n->type); 1976 term_word(p, n->string); 1977 } 1978 p->bold--; 1979 1980 return(0); 1981 } 1982 1983 1984 /* ARGSUSED */ 1985 static void 1986 termp_fo_post(DECL_ARGS) 1987 { 1988 1989 if (MDOC_BODY != node->type) 1990 return; 1991 p->flags |= TERMP_NOSPACE; 1992 term_word(p, ")"); 1993 p->flags |= TERMP_NOSPACE; 1994 term_word(p, ";"); 1995 term_newln(p); 1996 } 1997 1998 1999 /* ARGSUSED */ 2000 static int 2001 termp_bf_pre(DECL_ARGS) 2002 { 2003 const struct mdoc_node *n; 2004 2005 if (MDOC_HEAD == node->type) 2006 return(0); 2007 else if (MDOC_BLOCK != node->type) 2008 return(1); 2009 2010 if (NULL == (n = node->head->child)) { 2011 if (arg_hasattr(MDOC_Emphasis, node)) 2012 p->under++; 2013 else if (arg_hasattr(MDOC_Symbolic, node)) 2014 p->bold++; 2015 2016 return(1); 2017 } 2018 2019 assert(MDOC_TEXT == n->type); 2020 if (0 == strcmp("Em", n->string)) 2021 p->under++; 2022 else if (0 == strcmp("Sy", n->string)) 2023 p->bold++; 2024 2025 return(1); 2026 } 2027 2028 2029 /* ARGSUSED */ 2030 static int 2031 termp_sm_pre(DECL_ARGS) 2032 { 2033 2034 assert(node->child && MDOC_TEXT == node->child->type); 2035 if (0 == strcmp("on", node->child->string)) { 2036 p->flags &= ~TERMP_NONOSPACE; 2037 p->flags &= ~TERMP_NOSPACE; 2038 } else 2039 p->flags |= TERMP_NONOSPACE; 2040 2041 return(0); 2042 } 2043 2044 2045 /* ARGSUSED */ 2046 static int 2047 termp_ap_pre(DECL_ARGS) 2048 { 2049 2050 p->flags |= TERMP_NOSPACE; 2051 term_word(p, "\\(aq"); 2052 p->flags |= TERMP_NOSPACE; 2053 return(1); 2054 } 2055 2056 2057 /* ARGSUSED */ 2058 static void 2059 termp____post(DECL_ARGS) 2060 { 2061 2062 p->flags |= TERMP_NOSPACE; 2063 term_word(p, node->next ? "," : "."); 2064 } 2065 2066 2067 /* ARGSUSED */ 2068 static int 2069 termp_lk_pre(DECL_ARGS) 2070 { 2071 const struct mdoc_node *n; 2072 2073 assert(node->child); 2074 n = node->child; 2075 2076 if (NULL == n->next) { 2077 p->under++; 2078 return(1); 2079 } 2080 2081 p->under++; 2082 term_word(p, n->string); 2083 p->flags |= TERMP_NOSPACE; 2084 term_word(p, ":"); 2085 p->under--; 2086 2087 p->bold++; 2088 for (n = n->next; n; n = n->next) 2089 term_word(p, n->string); 2090 p->bold--; 2091 2092 return(0); 2093 } 2094 2095 2096 /* ARGSUSED */ 2097 static int 2098 termp_under_pre(DECL_ARGS) 2099 { 2100 2101 p->under++; 2102 return(1); 2103 } 2104