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