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