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