1 /* $Id: mdoc_man.c,v 1.34 2012/07/13 14:15:50 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@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 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 <assert.h> 18 #include <stdio.h> 19 #include <string.h> 20 21 #include "mandoc.h" 22 #include "out.h" 23 #include "man.h" 24 #include "mdoc.h" 25 #include "main.h" 26 27 #define DECL_ARGS const struct mdoc_meta *m, \ 28 const struct mdoc_node *n 29 30 struct manact { 31 int (*cond)(DECL_ARGS); /* DON'T run actions */ 32 int (*pre)(DECL_ARGS); /* pre-node action */ 33 void (*post)(DECL_ARGS); /* post-node action */ 34 const char *prefix; /* pre-node string constant */ 35 const char *suffix; /* post-node string constant */ 36 }; 37 38 static int cond_body(DECL_ARGS); 39 static int cond_head(DECL_ARGS); 40 static void font_push(char); 41 static void font_pop(void); 42 static void post__t(DECL_ARGS); 43 static void post_bd(DECL_ARGS); 44 static void post_bf(DECL_ARGS); 45 static void post_bk(DECL_ARGS); 46 static void post_bl(DECL_ARGS); 47 static void post_dl(DECL_ARGS); 48 static void post_enc(DECL_ARGS); 49 static void post_eo(DECL_ARGS); 50 static void post_fa(DECL_ARGS); 51 static void post_fd(DECL_ARGS); 52 static void post_fl(DECL_ARGS); 53 static void post_fn(DECL_ARGS); 54 static void post_fo(DECL_ARGS); 55 static void post_font(DECL_ARGS); 56 static void post_in(DECL_ARGS); 57 static void post_it(DECL_ARGS); 58 static void post_lb(DECL_ARGS); 59 static void post_nm(DECL_ARGS); 60 static void post_percent(DECL_ARGS); 61 static void post_pf(DECL_ARGS); 62 static void post_sect(DECL_ARGS); 63 static void post_sp(DECL_ARGS); 64 static void post_vt(DECL_ARGS); 65 static int pre__t(DECL_ARGS); 66 static int pre_an(DECL_ARGS); 67 static int pre_ap(DECL_ARGS); 68 static int pre_bd(DECL_ARGS); 69 static int pre_bf(DECL_ARGS); 70 static int pre_bk(DECL_ARGS); 71 static int pre_bl(DECL_ARGS); 72 static int pre_br(DECL_ARGS); 73 static int pre_bx(DECL_ARGS); 74 static int pre_dl(DECL_ARGS); 75 static int pre_enc(DECL_ARGS); 76 static int pre_em(DECL_ARGS); 77 static int pre_fa(DECL_ARGS); 78 static int pre_fd(DECL_ARGS); 79 static int pre_fl(DECL_ARGS); 80 static int pre_fn(DECL_ARGS); 81 static int pre_fo(DECL_ARGS); 82 static int pre_ft(DECL_ARGS); 83 static int pre_in(DECL_ARGS); 84 static int pre_it(DECL_ARGS); 85 static int pre_lk(DECL_ARGS); 86 static int pre_li(DECL_ARGS); 87 static int pre_nm(DECL_ARGS); 88 static int pre_no(DECL_ARGS); 89 static int pre_ns(DECL_ARGS); 90 static int pre_pp(DECL_ARGS); 91 static int pre_rs(DECL_ARGS); 92 static int pre_sm(DECL_ARGS); 93 static int pre_sp(DECL_ARGS); 94 static int pre_sect(DECL_ARGS); 95 static int pre_sy(DECL_ARGS); 96 static void pre_syn(const struct mdoc_node *); 97 static int pre_vt(DECL_ARGS); 98 static int pre_ux(DECL_ARGS); 99 static int pre_xr(DECL_ARGS); 100 static void print_word(const char *); 101 static void print_line(const char *, int); 102 static void print_block(const char *, int); 103 static void print_offs(const char *); 104 static void print_width(const char *, 105 const struct mdoc_node *, size_t); 106 static void print_count(int *); 107 static void print_node(DECL_ARGS); 108 109 static const struct manact manacts[MDOC_MAX + 1] = { 110 { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */ 111 { NULL, NULL, NULL, NULL, NULL }, /* Dd */ 112 { NULL, NULL, NULL, NULL, NULL }, /* Dt */ 113 { NULL, NULL, NULL, NULL, NULL }, /* Os */ 114 { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */ 115 { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */ 116 { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */ 117 { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */ 118 { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */ 119 { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */ 120 { NULL, NULL, NULL, NULL, NULL }, /* Ed */ 121 { cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */ 122 { NULL, NULL, NULL, NULL, NULL }, /* El */ 123 { NULL, pre_it, post_it, NULL, NULL }, /* It */ 124 { NULL, pre_em, post_font, NULL, NULL }, /* Ad */ 125 { NULL, pre_an, NULL, NULL, NULL }, /* An */ 126 { NULL, pre_em, post_font, NULL, NULL }, /* Ar */ 127 { NULL, pre_sy, post_font, NULL, NULL }, /* Cd */ 128 { NULL, pre_sy, post_font, NULL, NULL }, /* Cm */ 129 { NULL, pre_li, post_font, NULL, NULL }, /* Dv */ 130 { NULL, pre_li, post_font, NULL, NULL }, /* Er */ 131 { NULL, pre_li, post_font, NULL, NULL }, /* Ev */ 132 { NULL, pre_enc, post_enc, "The \\fB", 133 "\\fP\nutility exits 0 on success, and >0 if an error occurs." 134 }, /* Ex */ 135 { NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */ 136 { NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */ 137 { NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */ 138 { NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */ 139 { NULL, pre_ft, post_font, NULL, NULL }, /* Ft */ 140 { NULL, pre_sy, post_font, NULL, NULL }, /* Ic */ 141 { NULL, pre_in, post_in, NULL, NULL }, /* In */ 142 { NULL, pre_li, post_font, NULL, NULL }, /* Li */ 143 { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */ 144 { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */ 145 { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */ 146 { NULL, NULL, NULL, NULL, NULL }, /* Ot */ 147 { NULL, pre_em, post_font, NULL, NULL }, /* Pa */ 148 { NULL, pre_enc, post_enc, "The \\fB", 149 "\\fP\nfunction returns the value 0 if successful;\n" 150 "otherwise the value -1 is returned and the global\n" 151 "variable \\fIerrno\\fP is set to indicate the error." 152 }, /* Rv */ 153 { NULL, NULL, NULL, NULL, NULL }, /* St */ 154 { NULL, pre_em, post_font, NULL, NULL }, /* Va */ 155 { NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */ 156 { NULL, pre_xr, NULL, NULL, NULL }, /* Xr */ 157 { NULL, NULL, post_percent, NULL, NULL }, /* %A */ 158 { NULL, pre_em, post_percent, NULL, NULL }, /* %B */ 159 { NULL, NULL, post_percent, NULL, NULL }, /* %D */ 160 { NULL, pre_em, post_percent, NULL, NULL }, /* %I */ 161 { NULL, pre_em, post_percent, NULL, NULL }, /* %J */ 162 { NULL, NULL, post_percent, NULL, NULL }, /* %N */ 163 { NULL, NULL, post_percent, NULL, NULL }, /* %O */ 164 { NULL, NULL, post_percent, NULL, NULL }, /* %P */ 165 { NULL, NULL, post_percent, NULL, NULL }, /* %R */ 166 { NULL, pre__t, post__t, NULL, NULL }, /* %T */ 167 { NULL, NULL, post_percent, NULL, NULL }, /* %V */ 168 { NULL, NULL, NULL, NULL, NULL }, /* Ac */ 169 { cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */ 170 { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */ 171 { NULL, NULL, NULL, NULL, NULL }, /* At */ 172 { NULL, NULL, NULL, NULL, NULL }, /* Bc */ 173 { NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */ 174 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */ 175 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */ 176 { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */ 177 { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */ 178 { NULL, NULL, NULL, NULL, NULL }, /* Db */ 179 { NULL, NULL, NULL, NULL, NULL }, /* Dc */ 180 { cond_body, pre_enc, post_enc, "``", "''" }, /* Do */ 181 { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */ 182 { NULL, NULL, NULL, NULL, NULL }, /* Ec */ 183 { NULL, NULL, NULL, NULL, NULL }, /* Ef */ 184 { NULL, pre_em, post_font, NULL, NULL }, /* Em */ 185 { NULL, NULL, post_eo, NULL, NULL }, /* Eo */ 186 { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */ 187 { NULL, pre_sy, post_font, NULL, NULL }, /* Ms */ 188 { NULL, pre_no, NULL, NULL, NULL }, /* No */ 189 { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */ 190 { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */ 191 { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */ 192 { NULL, NULL, NULL, NULL, NULL }, /* Pc */ 193 { NULL, NULL, post_pf, NULL, NULL }, /* Pf */ 194 { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */ 195 { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */ 196 { NULL, NULL, NULL, NULL, NULL }, /* Qc */ 197 { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */ 198 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */ 199 { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */ 200 { NULL, NULL, NULL, NULL, NULL }, /* Re */ 201 { cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */ 202 { NULL, NULL, NULL, NULL, NULL }, /* Sc */ 203 { cond_body, pre_enc, post_enc, "`", "'" }, /* So */ 204 { cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */ 205 { NULL, pre_sm, NULL, NULL, NULL }, /* Sm */ 206 { NULL, pre_em, post_font, NULL, NULL }, /* Sx */ 207 { NULL, pre_sy, post_font, NULL, NULL }, /* Sy */ 208 { NULL, pre_li, post_font, NULL, NULL }, /* Tn */ 209 { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */ 210 { NULL, NULL, NULL, NULL, NULL }, /* Xc */ 211 { NULL, NULL, NULL, NULL, NULL }, /* Xo */ 212 { NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */ 213 { NULL, NULL, NULL, NULL, NULL }, /* Fc */ 214 { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */ 215 { NULL, NULL, NULL, NULL, NULL }, /* Oc */ 216 { NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */ 217 { NULL, NULL, NULL, NULL, NULL }, /* Ek */ 218 { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */ 219 { NULL, NULL, NULL, NULL, NULL }, /* Hf */ 220 { NULL, NULL, NULL, NULL, NULL }, /* Fr */ 221 { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */ 222 { NULL, NULL, post_lb, NULL, NULL }, /* Lb */ 223 { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */ 224 { NULL, pre_lk, NULL, NULL, NULL }, /* Lk */ 225 { NULL, pre_em, post_font, NULL, NULL }, /* Mt */ 226 { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */ 227 { cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */ 228 { NULL, NULL, NULL, NULL, NULL }, /* Brc */ 229 { NULL, NULL, post_percent, NULL, NULL }, /* %C */ 230 { NULL, NULL, NULL, NULL, NULL }, /* Es */ 231 { NULL, NULL, NULL, NULL, NULL }, /* En */ 232 { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */ 233 { NULL, NULL, post_percent, NULL, NULL }, /* %Q */ 234 { NULL, pre_br, NULL, NULL, NULL }, /* br */ 235 { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */ 236 { NULL, NULL, post_percent, NULL, NULL }, /* %U */ 237 { NULL, NULL, NULL, NULL, NULL }, /* Ta */ 238 { NULL, NULL, NULL, NULL, NULL }, /* ROOT */ 239 }; 240 241 static int outflags; 242 #define MMAN_spc (1 << 0) /* blank character before next word */ 243 #define MMAN_spc_force (1 << 1) /* even before trailing punctuation */ 244 #define MMAN_nl (1 << 2) /* break man(7) code line */ 245 #define MMAN_br (1 << 3) /* break output line */ 246 #define MMAN_sp (1 << 4) /* insert a blank output line */ 247 #define MMAN_PP (1 << 5) /* reset indentation etc. */ 248 #define MMAN_Sm (1 << 6) /* horizontal spacing mode */ 249 #define MMAN_Bk (1 << 7) /* word keep mode */ 250 #define MMAN_An_split (1 << 8) /* author mode is "split" */ 251 #define MMAN_An_nosplit (1 << 9) /* author mode is "nosplit" */ 252 253 static struct { 254 char *head; 255 char *tail; 256 size_t size; 257 } fontqueue; 258 259 static void 260 font_push(char newfont) 261 { 262 263 if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) { 264 fontqueue.size += 8; 265 fontqueue.head = mandoc_realloc(fontqueue.head, 266 fontqueue.size); 267 } 268 *fontqueue.tail = newfont; 269 print_word("\\f"); 270 putchar(newfont); 271 outflags &= ~MMAN_spc; 272 } 273 274 static void 275 font_pop(void) 276 { 277 278 if (fontqueue.tail > fontqueue.head) 279 fontqueue.tail--; 280 outflags &= ~MMAN_spc; 281 print_word("\\f"); 282 putchar(*fontqueue.tail); 283 } 284 285 static void 286 print_word(const char *s) 287 { 288 289 if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) { 290 /* 291 * If we need a newline, print it now and start afresh. 292 */ 293 if (MMAN_PP & outflags) { 294 if ( ! (MMAN_sp & outflags)) 295 printf("\n.sp -1v"); 296 printf("\n.PP\n"); 297 } else if (MMAN_sp & outflags) 298 printf("\n.sp\n"); 299 else if (MMAN_br & outflags) 300 printf("\n.br\n"); 301 else if (MMAN_nl & outflags) 302 putchar('\n'); 303 outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc); 304 } else if (MMAN_spc & outflags && '\0' != s[0]) 305 /* 306 * If we need a space, only print it if 307 * (1) it is forced by `No' or 308 * (2) what follows is not terminating punctuation or 309 * (3) what follows is longer than one character. 310 */ 311 if (MMAN_spc_force & outflags || 312 NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) { 313 if (MMAN_Bk & outflags) { 314 putchar('\\'); 315 putchar('~'); 316 } else 317 putchar(' '); 318 } 319 320 /* 321 * Reassign needing space if we're not following opening 322 * punctuation. 323 */ 324 if (MMAN_Sm & outflags && 325 (('(' != s[0] && '[' != s[0]) || '\0' != s[1])) 326 outflags |= MMAN_spc; 327 else 328 outflags &= ~MMAN_spc; 329 outflags &= ~MMAN_spc_force; 330 331 for ( ; *s; s++) { 332 switch (*s) { 333 case (ASCII_NBRSP): 334 printf("\\~"); 335 break; 336 case (ASCII_HYPH): 337 putchar('-'); 338 break; 339 default: 340 putchar((unsigned char)*s); 341 break; 342 } 343 } 344 } 345 346 static void 347 print_line(const char *s, int newflags) 348 { 349 350 outflags &= ~MMAN_br; 351 outflags |= MMAN_nl; 352 print_word(s); 353 outflags |= newflags; 354 } 355 356 static void 357 print_block(const char *s, int newflags) 358 { 359 360 outflags &= ~MMAN_PP; 361 if (MMAN_sp & outflags) 362 outflags &= ~(MMAN_sp | MMAN_br); 363 else 364 print_line(".sp -1v", 0); 365 outflags |= MMAN_nl; 366 print_word(s); 367 outflags |= newflags; 368 } 369 370 static void 371 print_offs(const char *v) 372 { 373 char buf[24]; 374 struct roffsu su; 375 size_t sz; 376 377 if (NULL == v || '\0' == *v || 0 == strcmp(v, "left")) 378 sz = 0; 379 else if (0 == strcmp(v, "indent")) 380 sz = 6; 381 else if (0 == strcmp(v, "indent-two")) 382 sz = 12; 383 else if (a2roffsu(v, &su, SCALE_MAX)) { 384 print_word(v); 385 return; 386 } else 387 sz = strlen(v); 388 389 snprintf(buf, sizeof(buf), "%ldn", sz); 390 print_word(buf); 391 } 392 393 void 394 print_width(const char *v, const struct mdoc_node *child, size_t defsz) 395 { 396 char buf[24]; 397 struct roffsu su; 398 size_t sz, chsz; 399 400 /* XXX Rough estimation, might have multiple parts. */ 401 chsz = (NULL != child && MDOC_TEXT == child->type) ? 402 strlen(child->string) : 0; 403 404 if (NULL == v) 405 sz = defsz; 406 else if (a2roffsu(v, &su, SCALE_MAX)) { 407 if (SCALE_EN == su.unit) 408 sz = su.scale; 409 else { 410 if (chsz) 411 print_block(".HP", 0); 412 else 413 print_block(".TP", 0); 414 print_word(v); 415 return; 416 } 417 } else 418 sz = strlen(v); 419 420 if (chsz > sz) 421 print_block(".HP", 0); 422 else 423 print_block(".TP", 0); 424 snprintf(buf, sizeof(buf), "%ldn", sz + 2); 425 print_word(buf); 426 } 427 428 void 429 print_count(int *count) 430 { 431 char buf[12]; 432 433 snprintf(buf, sizeof(buf), "%d.", ++*count); 434 print_word(buf); 435 } 436 437 void 438 man_man(void *arg, const struct man *man) 439 { 440 441 /* 442 * Dump the keep buffer. 443 * We're guaranteed by now that this exists (is non-NULL). 444 * Flush stdout afterward, just in case. 445 */ 446 fputs(mparse_getkeep(man_mparse(man)), stdout); 447 fflush(stdout); 448 } 449 450 void 451 man_mdoc(void *arg, const struct mdoc *mdoc) 452 { 453 const struct mdoc_meta *m; 454 const struct mdoc_node *n; 455 456 m = mdoc_meta(mdoc); 457 n = mdoc_node(mdoc); 458 459 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"", 460 m->title, m->msec, m->date, m->os, m->vol); 461 462 outflags = MMAN_nl | MMAN_Sm; 463 if (0 == fontqueue.size) { 464 fontqueue.size = 8; 465 fontqueue.head = fontqueue.tail = mandoc_malloc(8); 466 *fontqueue.tail = 'R'; 467 } 468 print_node(m, n); 469 putchar('\n'); 470 } 471 472 static void 473 print_node(DECL_ARGS) 474 { 475 const struct mdoc_node *prev, *sub; 476 const struct manact *act; 477 int cond, do_sub; 478 479 /* 480 * Break the line if we were parsed subsequent the current node. 481 * This makes the page structure be more consistent. 482 */ 483 prev = n->prev ? n->prev : n->parent; 484 if (MMAN_spc & outflags && prev && prev->line < n->line) 485 outflags |= MMAN_nl; 486 487 act = NULL; 488 cond = 0; 489 do_sub = 1; 490 491 if (MDOC_TEXT == n->type) { 492 /* 493 * Make sure that we don't happen to start with a 494 * control character at the start of a line. 495 */ 496 if (MMAN_nl & outflags && ('.' == *n->string || 497 '\'' == *n->string)) { 498 print_word("\\&"); 499 outflags &= ~MMAN_spc; 500 } 501 print_word(n->string); 502 } else { 503 /* 504 * Conditionally run the pre-node action handler for a 505 * node. 506 */ 507 act = manacts + n->tok; 508 cond = NULL == act->cond || (*act->cond)(m, n); 509 if (cond && act->pre) 510 do_sub = (*act->pre)(m, n); 511 } 512 513 /* 514 * Conditionally run all child nodes. 515 * Note that this iterates over children instead of using 516 * recursion. This prevents unnecessary depth in the stack. 517 */ 518 if (do_sub) 519 for (sub = n->child; sub; sub = sub->next) 520 print_node(m, sub); 521 522 /* 523 * Lastly, conditionally run the post-node handler. 524 */ 525 if (cond && act->post) 526 (*act->post)(m, n); 527 } 528 529 static int 530 cond_head(DECL_ARGS) 531 { 532 533 return(MDOC_HEAD == n->type); 534 } 535 536 static int 537 cond_body(DECL_ARGS) 538 { 539 540 return(MDOC_BODY == n->type); 541 } 542 543 static int 544 pre_enc(DECL_ARGS) 545 { 546 const char *prefix; 547 548 prefix = manacts[n->tok].prefix; 549 if (NULL == prefix) 550 return(1); 551 print_word(prefix); 552 outflags &= ~MMAN_spc; 553 return(1); 554 } 555 556 static void 557 post_enc(DECL_ARGS) 558 { 559 const char *suffix; 560 561 suffix = manacts[n->tok].suffix; 562 if (NULL == suffix) 563 return; 564 outflags &= ~MMAN_spc; 565 print_word(suffix); 566 } 567 568 static void 569 post_font(DECL_ARGS) 570 { 571 572 font_pop(); 573 } 574 575 static void 576 post_percent(DECL_ARGS) 577 { 578 579 if (pre_em == manacts[n->tok].pre) 580 font_pop(); 581 if (n->next) { 582 print_word(","); 583 if (n->prev && n->prev->tok == n->tok && 584 n->next->tok == n->tok) 585 print_word("and"); 586 } else { 587 print_word("."); 588 outflags |= MMAN_nl; 589 } 590 } 591 592 static int 593 pre__t(DECL_ARGS) 594 { 595 596 if (n->parent && MDOC_Rs == n->parent->tok && 597 n->parent->norm->Rs.quote_T) { 598 print_word("\""); 599 outflags &= ~MMAN_spc; 600 } else 601 font_push('I'); 602 return(1); 603 } 604 605 static void 606 post__t(DECL_ARGS) 607 { 608 609 if (n->parent && MDOC_Rs == n->parent->tok && 610 n->parent->norm->Rs.quote_T) { 611 outflags &= ~MMAN_spc; 612 print_word("\""); 613 } else 614 font_pop(); 615 post_percent(m, n); 616 } 617 618 /* 619 * Print before a section header. 620 */ 621 static int 622 pre_sect(DECL_ARGS) 623 { 624 625 if (MDOC_HEAD != n->type) 626 return(1); 627 outflags |= MMAN_sp; 628 print_block(manacts[n->tok].prefix, 0); 629 print_word("\""); 630 outflags &= ~MMAN_spc; 631 return(1); 632 } 633 634 /* 635 * Print subsequent a section header. 636 */ 637 static void 638 post_sect(DECL_ARGS) 639 { 640 641 if (MDOC_HEAD != n->type) 642 return; 643 outflags &= ~MMAN_spc; 644 print_word("\""); 645 outflags |= MMAN_nl; 646 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec) 647 outflags &= ~(MMAN_An_split | MMAN_An_nosplit); 648 } 649 650 /* See mdoc_term.c, synopsis_pre() for comments. */ 651 static void 652 pre_syn(const struct mdoc_node *n) 653 { 654 655 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 656 return; 657 658 if (n->prev->tok == n->tok && 659 MDOC_Ft != n->tok && 660 MDOC_Fo != n->tok && 661 MDOC_Fn != n->tok) { 662 outflags |= MMAN_br; 663 return; 664 } 665 666 switch (n->prev->tok) { 667 case (MDOC_Fd): 668 /* FALLTHROUGH */ 669 case (MDOC_Fn): 670 /* FALLTHROUGH */ 671 case (MDOC_Fo): 672 /* FALLTHROUGH */ 673 case (MDOC_In): 674 /* FALLTHROUGH */ 675 case (MDOC_Vt): 676 outflags |= MMAN_sp; 677 break; 678 case (MDOC_Ft): 679 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 680 outflags |= MMAN_sp; 681 break; 682 } 683 /* FALLTHROUGH */ 684 default: 685 outflags |= MMAN_br; 686 break; 687 } 688 } 689 690 static int 691 pre_an(DECL_ARGS) 692 { 693 694 switch (n->norm->An.auth) { 695 case (AUTH_split): 696 outflags &= ~MMAN_An_nosplit; 697 outflags |= MMAN_An_split; 698 return(0); 699 case (AUTH_nosplit): 700 outflags &= ~MMAN_An_split; 701 outflags |= MMAN_An_nosplit; 702 return(0); 703 default: 704 if (MMAN_An_split & outflags) 705 outflags |= MMAN_br; 706 else if (SEC_AUTHORS == n->sec && 707 ! (MMAN_An_nosplit & outflags)) 708 outflags |= MMAN_An_split; 709 return(1); 710 } 711 } 712 713 static int 714 pre_ap(DECL_ARGS) 715 { 716 717 outflags &= ~MMAN_spc; 718 print_word("'"); 719 outflags &= ~MMAN_spc; 720 return(0); 721 } 722 723 static int 724 pre_bd(DECL_ARGS) 725 { 726 727 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); 728 729 if (DISP_unfilled == n->norm->Bd.type || 730 DISP_literal == n->norm->Bd.type) 731 print_line(".nf", 0); 732 if (0 == n->norm->Bd.comp && NULL != n->parent->prev) 733 outflags |= MMAN_sp; 734 print_line(".RS", 0); 735 print_offs(n->norm->Bd.offs); 736 outflags |= MMAN_nl; 737 return(1); 738 } 739 740 static void 741 post_bd(DECL_ARGS) 742 { 743 744 print_line(".RE", MMAN_nl); 745 if (DISP_unfilled == n->norm->Bd.type || 746 DISP_literal == n->norm->Bd.type) 747 print_line(".fi", MMAN_nl); 748 } 749 750 static int 751 pre_bf(DECL_ARGS) 752 { 753 754 switch (n->type) { 755 case (MDOC_BLOCK): 756 return(1); 757 case (MDOC_BODY): 758 break; 759 default: 760 return(0); 761 } 762 switch (n->norm->Bf.font) { 763 case (FONT_Em): 764 font_push('I'); 765 break; 766 case (FONT_Sy): 767 font_push('B'); 768 break; 769 default: 770 font_push('R'); 771 break; 772 } 773 return(1); 774 } 775 776 static void 777 post_bf(DECL_ARGS) 778 { 779 780 if (MDOC_BODY == n->type) 781 font_pop(); 782 } 783 784 static int 785 pre_bk(DECL_ARGS) 786 { 787 788 switch (n->type) { 789 case (MDOC_BLOCK): 790 return(1); 791 case (MDOC_BODY): 792 outflags |= MMAN_Bk; 793 return(1); 794 default: 795 return(0); 796 } 797 } 798 799 static void 800 post_bk(DECL_ARGS) 801 { 802 803 if (MDOC_BODY == n->type) 804 outflags &= ~MMAN_Bk; 805 } 806 807 static int 808 pre_bl(DECL_ARGS) 809 { 810 size_t icol; 811 812 switch (n->norm->Bl.type) { 813 case (LIST_enum): 814 n->norm->Bl.count = 0; 815 return(1); 816 case (LIST_column): 817 break; 818 default: 819 return(1); 820 } 821 822 print_line(".TS", MMAN_nl); 823 for (icol = 0; icol < n->norm->Bl.ncols; icol++) 824 print_word("l"); 825 print_word("."); 826 outflags |= MMAN_nl; 827 return(1); 828 } 829 830 static void 831 post_bl(DECL_ARGS) 832 { 833 834 switch (n->norm->Bl.type) { 835 case (LIST_column): 836 print_line(".TE", 0); 837 break; 838 case (LIST_enum): 839 n->norm->Bl.count = 0; 840 break; 841 default: 842 break; 843 } 844 outflags |= MMAN_PP | MMAN_nl; 845 outflags &= ~(MMAN_sp | MMAN_br); 846 } 847 848 static int 849 pre_br(DECL_ARGS) 850 { 851 852 outflags |= MMAN_br; 853 return(0); 854 } 855 856 static int 857 pre_bx(DECL_ARGS) 858 { 859 860 n = n->child; 861 if (n) { 862 print_word(n->string); 863 outflags &= ~MMAN_spc; 864 n = n->next; 865 } 866 print_word("BSD"); 867 if (NULL == n) 868 return(0); 869 outflags &= ~MMAN_spc; 870 print_word("-"); 871 outflags &= ~MMAN_spc; 872 print_word(n->string); 873 return(0); 874 } 875 876 static int 877 pre_dl(DECL_ARGS) 878 { 879 880 print_line(".RS 6n", MMAN_nl); 881 return(1); 882 } 883 884 static void 885 post_dl(DECL_ARGS) 886 { 887 888 print_line(".RE", MMAN_nl); 889 } 890 891 static int 892 pre_em(DECL_ARGS) 893 { 894 895 font_push('I'); 896 return(1); 897 } 898 899 static void 900 post_eo(DECL_ARGS) 901 { 902 903 if (MDOC_HEAD == n->type || MDOC_BODY == n->type) 904 outflags &= ~MMAN_spc; 905 } 906 907 static int 908 pre_fa(DECL_ARGS) 909 { 910 911 if (MDOC_Fa == n->tok) 912 n = n->child; 913 914 while (NULL != n) { 915 font_push('I'); 916 print_node(m, n); 917 font_pop(); 918 if (NULL != (n = n->next)) 919 print_word(","); 920 } 921 return(0); 922 } 923 924 static void 925 post_fa(DECL_ARGS) 926 { 927 928 if (NULL != n->next && MDOC_Fa == n->next->tok) 929 print_word(","); 930 } 931 932 static int 933 pre_fd(DECL_ARGS) 934 { 935 936 pre_syn(n); 937 font_push('B'); 938 return(1); 939 } 940 941 static void 942 post_fd(DECL_ARGS) 943 { 944 945 font_pop(); 946 outflags |= MMAN_br; 947 } 948 949 static int 950 pre_fl(DECL_ARGS) 951 { 952 953 font_push('B'); 954 print_word("-"); 955 outflags &= ~MMAN_spc; 956 return(1); 957 } 958 959 static void 960 post_fl(DECL_ARGS) 961 { 962 963 font_pop(); 964 if (0 == n->nchild && NULL != n->next && 965 n->next->line == n->line) 966 outflags &= ~MMAN_spc; 967 } 968 969 static int 970 pre_fn(DECL_ARGS) 971 { 972 973 pre_syn(n); 974 975 n = n->child; 976 if (NULL == n) 977 return(0); 978 979 font_push('B'); 980 print_node(m, n); 981 font_pop(); 982 outflags &= ~MMAN_spc; 983 print_word("("); 984 outflags &= ~MMAN_spc; 985 986 n = n->next; 987 if (NULL != n) 988 pre_fa(m, n); 989 return(0); 990 } 991 992 static void 993 post_fn(DECL_ARGS) 994 { 995 996 print_word(")"); 997 if (MDOC_SYNPRETTY & n->flags) { 998 print_word(";"); 999 outflags |= MMAN_br; 1000 } 1001 } 1002 1003 static int 1004 pre_fo(DECL_ARGS) 1005 { 1006 1007 switch (n->type) { 1008 case (MDOC_BLOCK): 1009 pre_syn(n); 1010 break; 1011 case (MDOC_HEAD): 1012 font_push('B'); 1013 break; 1014 case (MDOC_BODY): 1015 outflags &= ~MMAN_spc; 1016 print_word("("); 1017 outflags &= ~MMAN_spc; 1018 break; 1019 default: 1020 break; 1021 } 1022 return(1); 1023 } 1024 1025 static void 1026 post_fo(DECL_ARGS) 1027 { 1028 1029 switch (n->type) { 1030 case (MDOC_HEAD): 1031 font_pop(); 1032 break; 1033 case (MDOC_BODY): 1034 post_fn(m, n); 1035 break; 1036 default: 1037 break; 1038 } 1039 } 1040 1041 static int 1042 pre_ft(DECL_ARGS) 1043 { 1044 1045 pre_syn(n); 1046 font_push('I'); 1047 return(1); 1048 } 1049 1050 static int 1051 pre_in(DECL_ARGS) 1052 { 1053 1054 if (MDOC_SYNPRETTY & n->flags) { 1055 pre_syn(n); 1056 font_push('B'); 1057 print_word("#include <"); 1058 outflags &= ~MMAN_spc; 1059 } else { 1060 print_word("<"); 1061 outflags &= ~MMAN_spc; 1062 font_push('I'); 1063 } 1064 return(1); 1065 } 1066 1067 static void 1068 post_in(DECL_ARGS) 1069 { 1070 1071 if (MDOC_SYNPRETTY & n->flags) { 1072 outflags &= ~MMAN_spc; 1073 print_word(">"); 1074 font_pop(); 1075 outflags |= MMAN_br; 1076 } else { 1077 font_pop(); 1078 outflags &= ~MMAN_spc; 1079 print_word(">"); 1080 } 1081 } 1082 1083 static int 1084 pre_it(DECL_ARGS) 1085 { 1086 const struct mdoc_node *bln; 1087 1088 switch (n->type) { 1089 case (MDOC_HEAD): 1090 outflags |= MMAN_PP | MMAN_nl; 1091 bln = n->parent->parent; 1092 if (0 == bln->norm->Bl.comp || 1093 NULL == bln->parent->prev) 1094 outflags |= MMAN_sp; 1095 outflags &= ~MMAN_br; 1096 switch (bln->norm->Bl.type) { 1097 case (LIST_item): 1098 return(0); 1099 case (LIST_inset): 1100 /* FALLTHROUGH */ 1101 case (LIST_diag): 1102 /* FALLTHROUGH */ 1103 case (LIST_ohang): 1104 if (bln->norm->Bl.type == LIST_diag) 1105 print_line(".B \"", 0); 1106 else 1107 print_line(".R \"", 0); 1108 outflags &= ~MMAN_spc; 1109 return(1); 1110 case (LIST_bullet): 1111 /* FALLTHROUGH */ 1112 case (LIST_dash): 1113 /* FALLTHROUGH */ 1114 case (LIST_hyphen): 1115 print_width(bln->norm->Bl.width, NULL, 0); 1116 outflags |= MMAN_nl; 1117 font_push('B'); 1118 if (LIST_bullet == bln->norm->Bl.type) 1119 print_word("o"); 1120 else 1121 print_word("-"); 1122 font_pop(); 1123 break; 1124 case (LIST_enum): 1125 print_width(bln->norm->Bl.width, NULL, 0); 1126 outflags |= MMAN_nl; 1127 print_count(&bln->norm->Bl.count); 1128 break; 1129 case (LIST_hang): 1130 print_width(bln->norm->Bl.width, n->child, 6); 1131 break; 1132 case (LIST_tag): 1133 print_width(bln->norm->Bl.width, NULL, 8); 1134 break; 1135 default: 1136 return(1); 1137 } 1138 outflags |= MMAN_nl; 1139 default: 1140 break; 1141 } 1142 return(1); 1143 } 1144 1145 static void 1146 post_it(DECL_ARGS) 1147 { 1148 const struct mdoc_node *bln; 1149 1150 bln = n->parent->parent; 1151 1152 switch (n->type) { 1153 case (MDOC_HEAD): 1154 switch (bln->norm->Bl.type) { 1155 case (LIST_diag): 1156 outflags &= ~MMAN_spc; 1157 print_word("\\ "); 1158 break; 1159 case (LIST_ohang): 1160 outflags |= MMAN_br; 1161 break; 1162 default: 1163 break; 1164 } 1165 break; 1166 case (MDOC_BODY): 1167 if (LIST_column == bln->norm->Bl.type && 1168 NULL != n->next) { 1169 putchar('\t'); 1170 outflags &= ~MMAN_spc; 1171 } 1172 break; 1173 default: 1174 break; 1175 } 1176 } 1177 1178 static void 1179 post_lb(DECL_ARGS) 1180 { 1181 1182 if (SEC_LIBRARY == n->sec) 1183 outflags |= MMAN_br; 1184 } 1185 1186 static int 1187 pre_lk(DECL_ARGS) 1188 { 1189 const struct mdoc_node *link, *descr; 1190 1191 if (NULL == (link = n->child)) 1192 return(0); 1193 1194 if (NULL != (descr = link->next)) { 1195 font_push('I'); 1196 while (NULL != descr) { 1197 print_word(descr->string); 1198 descr = descr->next; 1199 } 1200 print_word(":"); 1201 font_pop(); 1202 } 1203 1204 font_push('B'); 1205 print_word(link->string); 1206 font_pop(); 1207 return(0); 1208 } 1209 1210 static int 1211 pre_li(DECL_ARGS) 1212 { 1213 1214 font_push('R'); 1215 return(1); 1216 } 1217 1218 static int 1219 pre_nm(DECL_ARGS) 1220 { 1221 1222 if (MDOC_BLOCK == n->type) 1223 pre_syn(n); 1224 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 1225 return(1); 1226 if (NULL == n->child && NULL == m->name) 1227 return(0); 1228 font_push('B'); 1229 if (NULL == n->child) 1230 print_word(m->name); 1231 return(1); 1232 } 1233 1234 static void 1235 post_nm(DECL_ARGS) 1236 { 1237 1238 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 1239 return; 1240 font_pop(); 1241 } 1242 1243 static int 1244 pre_no(DECL_ARGS) 1245 { 1246 1247 outflags |= MMAN_spc_force; 1248 return(1); 1249 } 1250 1251 static int 1252 pre_ns(DECL_ARGS) 1253 { 1254 1255 outflags &= ~MMAN_spc; 1256 return(0); 1257 } 1258 1259 static void 1260 post_pf(DECL_ARGS) 1261 { 1262 1263 outflags &= ~MMAN_spc; 1264 } 1265 1266 static int 1267 pre_pp(DECL_ARGS) 1268 { 1269 1270 if (MDOC_It != n->parent->tok) 1271 outflags |= MMAN_PP; 1272 outflags |= MMAN_sp | MMAN_nl; 1273 outflags &= ~MMAN_br; 1274 return(0); 1275 } 1276 1277 static int 1278 pre_rs(DECL_ARGS) 1279 { 1280 1281 if (SEC_SEE_ALSO == n->sec) { 1282 outflags |= MMAN_PP | MMAN_sp | MMAN_nl; 1283 outflags &= ~MMAN_br; 1284 } 1285 return(1); 1286 } 1287 1288 static int 1289 pre_sm(DECL_ARGS) 1290 { 1291 1292 assert(n->child && MDOC_TEXT == n->child->type); 1293 if (0 == strcmp("on", n->child->string)) 1294 outflags |= MMAN_Sm | MMAN_spc; 1295 else 1296 outflags &= ~MMAN_Sm; 1297 return(0); 1298 } 1299 1300 static int 1301 pre_sp(DECL_ARGS) 1302 { 1303 1304 print_line(".sp", MMAN_nl); 1305 return(1); 1306 } 1307 1308 static void 1309 post_sp(DECL_ARGS) 1310 { 1311 1312 outflags |= MMAN_nl; 1313 } 1314 1315 static int 1316 pre_sy(DECL_ARGS) 1317 { 1318 1319 font_push('B'); 1320 return(1); 1321 } 1322 1323 static int 1324 pre_vt(DECL_ARGS) 1325 { 1326 1327 if (MDOC_SYNPRETTY & n->flags) { 1328 switch (n->type) { 1329 case (MDOC_BLOCK): 1330 pre_syn(n); 1331 return(1); 1332 case (MDOC_BODY): 1333 break; 1334 default: 1335 return(0); 1336 } 1337 } 1338 font_push('I'); 1339 return(1); 1340 } 1341 1342 static void 1343 post_vt(DECL_ARGS) 1344 { 1345 1346 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type) 1347 return; 1348 font_pop(); 1349 } 1350 1351 static int 1352 pre_xr(DECL_ARGS) 1353 { 1354 1355 n = n->child; 1356 if (NULL == n) 1357 return(0); 1358 print_node(m, n); 1359 n = n->next; 1360 if (NULL == n) 1361 return(0); 1362 outflags &= ~MMAN_spc; 1363 print_word("("); 1364 print_node(m, n); 1365 print_word(")"); 1366 return(0); 1367 } 1368 1369 static int 1370 pre_ux(DECL_ARGS) 1371 { 1372 1373 print_word(manacts[n->tok].prefix); 1374 if (NULL == n->child) 1375 return(0); 1376 outflags &= ~MMAN_spc; 1377 print_word("\\~"); 1378 outflags &= ~MMAN_spc; 1379 return(1); 1380 } 1381