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