1 /* $Id: mdoc_man.c,v 1.56 2013/12/25 00:39: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 #define MMAN_nbrword (1 << 12) /* do not break the next word */ 260 261 #define BL_STACK_MAX 32 262 263 static size_t Bl_stack[BL_STACK_MAX]; /* offsets [chars] */ 264 static int Bl_stack_post[BL_STACK_MAX]; /* add final .RE */ 265 static int Bl_stack_len; /* number of nested Bl blocks */ 266 static int TPremain; /* characters before tag is full */ 267 268 static struct { 269 char *head; 270 char *tail; 271 size_t size; 272 } fontqueue; 273 274 static void 275 font_push(char newfont) 276 { 277 278 if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) { 279 fontqueue.size += 8; 280 fontqueue.head = mandoc_realloc(fontqueue.head, 281 fontqueue.size); 282 } 283 *fontqueue.tail = newfont; 284 print_word(""); 285 printf("\\f"); 286 putchar(newfont); 287 outflags &= ~MMAN_spc; 288 } 289 290 static void 291 font_pop(void) 292 { 293 294 if (fontqueue.tail > fontqueue.head) 295 fontqueue.tail--; 296 outflags &= ~MMAN_spc; 297 print_word(""); 298 printf("\\f"); 299 putchar(*fontqueue.tail); 300 } 301 302 static void 303 print_word(const char *s) 304 { 305 306 if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) { 307 /* 308 * If we need a newline, print it now and start afresh. 309 */ 310 if (MMAN_PP & outflags) { 311 if (MMAN_sp & outflags) { 312 if (MMAN_PD & outflags) { 313 printf("\n.PD"); 314 outflags &= ~MMAN_PD; 315 } 316 } else if ( ! (MMAN_PD & outflags)) { 317 printf("\n.PD 0"); 318 outflags |= MMAN_PD; 319 } 320 printf("\n.PP\n"); 321 } else if (MMAN_sp & outflags) 322 printf("\n.sp\n"); 323 else if (MMAN_br & outflags) 324 printf("\n.br\n"); 325 else if (MMAN_nl & outflags) 326 putchar('\n'); 327 outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc); 328 if (1 == TPremain) 329 printf(".br\n"); 330 TPremain = 0; 331 } else if (MMAN_spc & outflags) { 332 /* 333 * If we need a space, only print it if 334 * (1) it is forced by `No' or 335 * (2) what follows is not terminating punctuation or 336 * (3) what follows is longer than one character. 337 */ 338 if (MMAN_spc_force & outflags || '\0' == s[0] || 339 NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) { 340 if (MMAN_Bk & outflags && 341 ! (MMAN_Bk_susp & outflags)) 342 putchar('\\'); 343 putchar(' '); 344 if (TPremain) 345 TPremain--; 346 } 347 } 348 349 /* 350 * Reassign needing space if we're not following opening 351 * punctuation. 352 */ 353 if (MMAN_Sm & outflags && ('\0' == s[0] || 354 (('(' != s[0] && '[' != s[0]) || '\0' != s[1]))) 355 outflags |= MMAN_spc; 356 else 357 outflags &= ~MMAN_spc; 358 outflags &= ~(MMAN_spc_force | MMAN_Bk_susp); 359 360 for ( ; *s; s++) { 361 switch (*s) { 362 case (ASCII_NBRSP): 363 printf("\\ "); 364 break; 365 case (ASCII_HYPH): 366 putchar('-'); 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 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 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) 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 (cond && act->post) 615 (*act->post)(meta, n); 616 } 617 618 static int 619 cond_head(DECL_ARGS) 620 { 621 622 return(MDOC_HEAD == n->type); 623 } 624 625 static int 626 cond_body(DECL_ARGS) 627 { 628 629 return(MDOC_BODY == n->type); 630 } 631 632 static int 633 pre_enc(DECL_ARGS) 634 { 635 const char *prefix; 636 637 prefix = manacts[n->tok].prefix; 638 if (NULL == prefix) 639 return(1); 640 print_word(prefix); 641 outflags &= ~MMAN_spc; 642 return(1); 643 } 644 645 static void 646 post_enc(DECL_ARGS) 647 { 648 const char *suffix; 649 650 suffix = manacts[n->tok].suffix; 651 if (NULL == suffix) 652 return; 653 outflags &= ~MMAN_spc; 654 print_word(suffix); 655 } 656 657 static void 658 post_font(DECL_ARGS) 659 { 660 661 font_pop(); 662 } 663 664 static void 665 post_percent(DECL_ARGS) 666 { 667 668 if (pre_em == manacts[n->tok].pre) 669 font_pop(); 670 if (n->next) { 671 print_word(","); 672 if (n->prev && n->prev->tok == n->tok && 673 n->next->tok == n->tok) 674 print_word("and"); 675 } else { 676 print_word("."); 677 outflags |= MMAN_nl; 678 } 679 } 680 681 static int 682 pre__t(DECL_ARGS) 683 { 684 685 if (n->parent && MDOC_Rs == n->parent->tok && 686 n->parent->norm->Rs.quote_T) { 687 print_word(""); 688 putchar('\"'); 689 outflags &= ~MMAN_spc; 690 } else 691 font_push('I'); 692 return(1); 693 } 694 695 static void 696 post__t(DECL_ARGS) 697 { 698 699 if (n->parent && MDOC_Rs == n->parent->tok && 700 n->parent->norm->Rs.quote_T) { 701 outflags &= ~MMAN_spc; 702 print_word(""); 703 putchar('\"'); 704 } else 705 font_pop(); 706 post_percent(meta, n); 707 } 708 709 /* 710 * Print before a section header. 711 */ 712 static int 713 pre_sect(DECL_ARGS) 714 { 715 716 if (MDOC_HEAD == n->type) { 717 outflags |= MMAN_sp; 718 print_block(manacts[n->tok].prefix, 0); 719 print_word(""); 720 putchar('\"'); 721 outflags &= ~MMAN_spc; 722 } 723 return(1); 724 } 725 726 /* 727 * Print subsequent a section header. 728 */ 729 static void 730 post_sect(DECL_ARGS) 731 { 732 733 if (MDOC_HEAD != n->type) 734 return; 735 outflags &= ~MMAN_spc; 736 print_word(""); 737 putchar('\"'); 738 outflags |= MMAN_nl; 739 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec) 740 outflags &= ~(MMAN_An_split | MMAN_An_nosplit); 741 } 742 743 /* See mdoc_term.c, synopsis_pre() for comments. */ 744 static void 745 pre_syn(const struct mdoc_node *n) 746 { 747 748 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 749 return; 750 751 if (n->prev->tok == n->tok && 752 MDOC_Ft != n->tok && 753 MDOC_Fo != n->tok && 754 MDOC_Fn != n->tok) { 755 outflags |= MMAN_br; 756 return; 757 } 758 759 switch (n->prev->tok) { 760 case (MDOC_Fd): 761 /* FALLTHROUGH */ 762 case (MDOC_Fn): 763 /* FALLTHROUGH */ 764 case (MDOC_Fo): 765 /* FALLTHROUGH */ 766 case (MDOC_In): 767 /* FALLTHROUGH */ 768 case (MDOC_Vt): 769 outflags |= MMAN_sp; 770 break; 771 case (MDOC_Ft): 772 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 773 outflags |= MMAN_sp; 774 break; 775 } 776 /* FALLTHROUGH */ 777 default: 778 outflags |= MMAN_br; 779 break; 780 } 781 } 782 783 static int 784 pre_an(DECL_ARGS) 785 { 786 787 switch (n->norm->An.auth) { 788 case (AUTH_split): 789 outflags &= ~MMAN_An_nosplit; 790 outflags |= MMAN_An_split; 791 return(0); 792 case (AUTH_nosplit): 793 outflags &= ~MMAN_An_split; 794 outflags |= MMAN_An_nosplit; 795 return(0); 796 default: 797 if (MMAN_An_split & outflags) 798 outflags |= MMAN_br; 799 else if (SEC_AUTHORS == n->sec && 800 ! (MMAN_An_nosplit & outflags)) 801 outflags |= MMAN_An_split; 802 return(1); 803 } 804 } 805 806 static int 807 pre_ap(DECL_ARGS) 808 { 809 810 outflags &= ~MMAN_spc; 811 print_word("'"); 812 outflags &= ~MMAN_spc; 813 return(0); 814 } 815 816 static int 817 pre_bd(DECL_ARGS) 818 { 819 820 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); 821 822 if (DISP_unfilled == n->norm->Bd.type || 823 DISP_literal == n->norm->Bd.type) 824 print_line(".nf", 0); 825 if (0 == n->norm->Bd.comp && NULL != n->parent->prev) 826 outflags |= MMAN_sp; 827 print_offs(n->norm->Bd.offs); 828 return(1); 829 } 830 831 static void 832 post_bd(DECL_ARGS) 833 { 834 835 /* Close out this display. */ 836 print_line(".RE", MMAN_nl); 837 if (DISP_unfilled == n->norm->Bd.type || 838 DISP_literal == n->norm->Bd.type) 839 print_line(".fi", MMAN_nl); 840 841 /* Maybe we are inside an enclosing list? */ 842 if (NULL != n->parent->next) 843 mid_it(); 844 } 845 846 static int 847 pre_bf(DECL_ARGS) 848 { 849 850 switch (n->type) { 851 case (MDOC_BLOCK): 852 return(1); 853 case (MDOC_BODY): 854 break; 855 default: 856 return(0); 857 } 858 switch (n->norm->Bf.font) { 859 case (FONT_Em): 860 font_push('I'); 861 break; 862 case (FONT_Sy): 863 font_push('B'); 864 break; 865 default: 866 font_push('R'); 867 break; 868 } 869 return(1); 870 } 871 872 static void 873 post_bf(DECL_ARGS) 874 { 875 876 if (MDOC_BODY == n->type) 877 font_pop(); 878 } 879 880 static int 881 pre_bk(DECL_ARGS) 882 { 883 884 switch (n->type) { 885 case (MDOC_BLOCK): 886 return(1); 887 case (MDOC_BODY): 888 outflags |= MMAN_Bk; 889 return(1); 890 default: 891 return(0); 892 } 893 } 894 895 static void 896 post_bk(DECL_ARGS) 897 { 898 899 if (MDOC_BODY == n->type) 900 outflags &= ~MMAN_Bk; 901 } 902 903 static int 904 pre_bl(DECL_ARGS) 905 { 906 size_t icol; 907 908 /* 909 * print_offs() will increase the -offset to account for 910 * a possible enclosing .It, but any enclosed .It blocks 911 * just nest and do not add up their indentation. 912 */ 913 if (n->norm->Bl.offs) { 914 print_offs(n->norm->Bl.offs); 915 Bl_stack[Bl_stack_len++] = 0; 916 } 917 918 switch (n->norm->Bl.type) { 919 case (LIST_enum): 920 n->norm->Bl.count = 0; 921 return(1); 922 case (LIST_column): 923 break; 924 default: 925 return(1); 926 } 927 928 print_line(".TS", MMAN_nl); 929 for (icol = 0; icol < n->norm->Bl.ncols; icol++) 930 print_word("l"); 931 print_word("."); 932 outflags |= MMAN_nl; 933 return(1); 934 } 935 936 static void 937 post_bl(DECL_ARGS) 938 { 939 940 switch (n->norm->Bl.type) { 941 case (LIST_column): 942 print_line(".TE", 0); 943 break; 944 case (LIST_enum): 945 n->norm->Bl.count = 0; 946 break; 947 default: 948 break; 949 } 950 951 if (n->norm->Bl.offs) { 952 print_line(".RE", MMAN_nl); 953 assert(Bl_stack_len); 954 Bl_stack_len--; 955 assert(0 == Bl_stack[Bl_stack_len]); 956 } else { 957 outflags |= MMAN_PP | MMAN_nl; 958 outflags &= ~(MMAN_sp | MMAN_br); 959 } 960 961 /* Maybe we are inside an enclosing list? */ 962 if (NULL != n->parent->next) 963 mid_it(); 964 965 } 966 967 static int 968 pre_br(DECL_ARGS) 969 { 970 971 outflags |= MMAN_br; 972 return(0); 973 } 974 975 static int 976 pre_bx(DECL_ARGS) 977 { 978 979 n = n->child; 980 if (n) { 981 print_word(n->string); 982 outflags &= ~MMAN_spc; 983 n = n->next; 984 } 985 print_word("BSD"); 986 if (NULL == n) 987 return(0); 988 outflags &= ~MMAN_spc; 989 print_word("-"); 990 outflags &= ~MMAN_spc; 991 print_word(n->string); 992 return(0); 993 } 994 995 static int 996 pre_dl(DECL_ARGS) 997 { 998 999 print_offs("6n"); 1000 return(1); 1001 } 1002 1003 static void 1004 post_dl(DECL_ARGS) 1005 { 1006 1007 print_line(".RE", MMAN_nl); 1008 1009 /* Maybe we are inside an enclosing list? */ 1010 if (NULL != n->parent->next) 1011 mid_it(); 1012 } 1013 1014 static int 1015 pre_em(DECL_ARGS) 1016 { 1017 1018 font_push('I'); 1019 return(1); 1020 } 1021 1022 static void 1023 post_eo(DECL_ARGS) 1024 { 1025 1026 if (MDOC_HEAD == n->type || MDOC_BODY == n->type) 1027 outflags &= ~MMAN_spc; 1028 } 1029 1030 static int 1031 pre_fa(DECL_ARGS) 1032 { 1033 1034 if (MDOC_Fa == n->tok) 1035 n = n->child; 1036 1037 while (NULL != n) { 1038 font_push('I'); 1039 if (MDOC_SYNPRETTY & n->flags) 1040 outflags |= MMAN_nbrword; 1041 print_node(meta, n); 1042 font_pop(); 1043 if (NULL != (n = n->next)) 1044 print_word(","); 1045 } 1046 return(0); 1047 } 1048 1049 static void 1050 post_fa(DECL_ARGS) 1051 { 1052 1053 if (NULL != n->next && MDOC_Fa == n->next->tok) 1054 print_word(","); 1055 } 1056 1057 static int 1058 pre_fd(DECL_ARGS) 1059 { 1060 1061 pre_syn(n); 1062 font_push('B'); 1063 return(1); 1064 } 1065 1066 static void 1067 post_fd(DECL_ARGS) 1068 { 1069 1070 font_pop(); 1071 outflags |= MMAN_br; 1072 } 1073 1074 static int 1075 pre_fl(DECL_ARGS) 1076 { 1077 1078 font_push('B'); 1079 print_word("\\-"); 1080 outflags &= ~MMAN_spc; 1081 return(1); 1082 } 1083 1084 static void 1085 post_fl(DECL_ARGS) 1086 { 1087 1088 font_pop(); 1089 if (0 == n->nchild && NULL != n->next && 1090 n->next->line == n->line) 1091 outflags &= ~MMAN_spc; 1092 } 1093 1094 static int 1095 pre_fn(DECL_ARGS) 1096 { 1097 1098 pre_syn(n); 1099 1100 n = n->child; 1101 if (NULL == n) 1102 return(0); 1103 1104 if (MDOC_SYNPRETTY & n->flags) 1105 print_block(".HP 4n", MMAN_nl); 1106 1107 font_push('B'); 1108 print_node(meta, n); 1109 font_pop(); 1110 outflags &= ~MMAN_spc; 1111 print_word("("); 1112 outflags &= ~MMAN_spc; 1113 1114 n = n->next; 1115 if (NULL != n) 1116 pre_fa(meta, n); 1117 return(0); 1118 } 1119 1120 static void 1121 post_fn(DECL_ARGS) 1122 { 1123 1124 print_word(")"); 1125 if (MDOC_SYNPRETTY & n->flags) { 1126 print_word(";"); 1127 outflags |= MMAN_PP; 1128 } 1129 } 1130 1131 static int 1132 pre_fo(DECL_ARGS) 1133 { 1134 1135 switch (n->type) { 1136 case (MDOC_BLOCK): 1137 pre_syn(n); 1138 break; 1139 case (MDOC_HEAD): 1140 font_push('B'); 1141 break; 1142 case (MDOC_BODY): 1143 outflags &= ~MMAN_spc; 1144 print_word("("); 1145 outflags &= ~MMAN_spc; 1146 break; 1147 default: 1148 break; 1149 } 1150 return(1); 1151 } 1152 1153 static void 1154 post_fo(DECL_ARGS) 1155 { 1156 1157 switch (n->type) { 1158 case (MDOC_HEAD): 1159 font_pop(); 1160 break; 1161 case (MDOC_BODY): 1162 post_fn(meta, n); 1163 break; 1164 default: 1165 break; 1166 } 1167 } 1168 1169 static int 1170 pre_ft(DECL_ARGS) 1171 { 1172 1173 pre_syn(n); 1174 font_push('I'); 1175 return(1); 1176 } 1177 1178 static int 1179 pre_in(DECL_ARGS) 1180 { 1181 1182 if (MDOC_SYNPRETTY & n->flags) { 1183 pre_syn(n); 1184 font_push('B'); 1185 print_word("#include <"); 1186 outflags &= ~MMAN_spc; 1187 } else { 1188 print_word("<"); 1189 outflags &= ~MMAN_spc; 1190 font_push('I'); 1191 } 1192 return(1); 1193 } 1194 1195 static void 1196 post_in(DECL_ARGS) 1197 { 1198 1199 if (MDOC_SYNPRETTY & n->flags) { 1200 outflags &= ~MMAN_spc; 1201 print_word(">"); 1202 font_pop(); 1203 outflags |= MMAN_br; 1204 } else { 1205 font_pop(); 1206 outflags &= ~MMAN_spc; 1207 print_word(">"); 1208 } 1209 } 1210 1211 static int 1212 pre_it(DECL_ARGS) 1213 { 1214 const struct mdoc_node *bln; 1215 1216 switch (n->type) { 1217 case (MDOC_HEAD): 1218 outflags |= MMAN_PP | MMAN_nl; 1219 bln = n->parent->parent; 1220 if (0 == bln->norm->Bl.comp || 1221 (NULL == n->parent->prev && 1222 NULL == bln->parent->prev)) 1223 outflags |= MMAN_sp; 1224 outflags &= ~MMAN_br; 1225 switch (bln->norm->Bl.type) { 1226 case (LIST_item): 1227 return(0); 1228 case (LIST_inset): 1229 /* FALLTHROUGH */ 1230 case (LIST_diag): 1231 /* FALLTHROUGH */ 1232 case (LIST_ohang): 1233 if (bln->norm->Bl.type == LIST_diag) 1234 print_line(".B \"", 0); 1235 else 1236 print_line(".R \"", 0); 1237 outflags &= ~MMAN_spc; 1238 return(1); 1239 case (LIST_bullet): 1240 /* FALLTHROUGH */ 1241 case (LIST_dash): 1242 /* FALLTHROUGH */ 1243 case (LIST_hyphen): 1244 print_width(bln->norm->Bl.width, NULL, 0); 1245 TPremain = 0; 1246 outflags |= MMAN_nl; 1247 font_push('B'); 1248 if (LIST_bullet == bln->norm->Bl.type) 1249 print_word("o"); 1250 else 1251 print_word("-"); 1252 font_pop(); 1253 break; 1254 case (LIST_enum): 1255 print_width(bln->norm->Bl.width, NULL, 0); 1256 TPremain = 0; 1257 outflags |= MMAN_nl; 1258 print_count(&bln->norm->Bl.count); 1259 break; 1260 case (LIST_hang): 1261 print_width(bln->norm->Bl.width, n->child, 6); 1262 TPremain = 0; 1263 break; 1264 case (LIST_tag): 1265 print_width(bln->norm->Bl.width, n->child, 0); 1266 putchar('\n'); 1267 outflags &= ~MMAN_spc; 1268 return(1); 1269 default: 1270 return(1); 1271 } 1272 outflags |= MMAN_nl; 1273 default: 1274 break; 1275 } 1276 return(1); 1277 } 1278 1279 /* 1280 * This function is called after closing out an indented block. 1281 * If we are inside an enclosing list, restore its indentation. 1282 */ 1283 static void 1284 mid_it(void) 1285 { 1286 char buf[24]; 1287 1288 /* Nothing to do outside a list. */ 1289 if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1]) 1290 return; 1291 1292 /* The indentation has already been set up. */ 1293 if (Bl_stack_post[Bl_stack_len - 1]) 1294 return; 1295 1296 /* Restore the indentation of the enclosing list. */ 1297 print_line(".RS", MMAN_Bk_susp); 1298 snprintf(buf, sizeof(buf), "%zun", Bl_stack[Bl_stack_len - 1]); 1299 print_word(buf); 1300 1301 /* Remeber to close out this .RS block later. */ 1302 Bl_stack_post[Bl_stack_len - 1] = 1; 1303 } 1304 1305 static void 1306 post_it(DECL_ARGS) 1307 { 1308 const struct mdoc_node *bln; 1309 1310 bln = n->parent->parent; 1311 1312 switch (n->type) { 1313 case (MDOC_HEAD): 1314 switch (bln->norm->Bl.type) { 1315 case (LIST_diag): 1316 outflags &= ~MMAN_spc; 1317 print_word("\\ "); 1318 break; 1319 case (LIST_ohang): 1320 outflags |= MMAN_br; 1321 break; 1322 default: 1323 break; 1324 } 1325 break; 1326 case (MDOC_BODY): 1327 switch (bln->norm->Bl.type) { 1328 case (LIST_bullet): 1329 /* FALLTHROUGH */ 1330 case (LIST_dash): 1331 /* FALLTHROUGH */ 1332 case (LIST_hyphen): 1333 /* FALLTHROUGH */ 1334 case (LIST_enum): 1335 /* FALLTHROUGH */ 1336 case (LIST_hang): 1337 /* FALLTHROUGH */ 1338 case (LIST_tag): 1339 assert(Bl_stack_len); 1340 Bl_stack[--Bl_stack_len] = 0; 1341 1342 /* 1343 * Our indentation had to be restored 1344 * after a child display or child list. 1345 * Close out that indentation block now. 1346 */ 1347 if (Bl_stack_post[Bl_stack_len]) { 1348 print_line(".RE", MMAN_nl); 1349 Bl_stack_post[Bl_stack_len] = 0; 1350 } 1351 break; 1352 case (LIST_column): 1353 if (NULL != n->next) { 1354 putchar('\t'); 1355 outflags &= ~MMAN_spc; 1356 } 1357 break; 1358 default: 1359 break; 1360 } 1361 break; 1362 default: 1363 break; 1364 } 1365 } 1366 1367 static void 1368 post_lb(DECL_ARGS) 1369 { 1370 1371 if (SEC_LIBRARY == n->sec) 1372 outflags |= MMAN_br; 1373 } 1374 1375 static int 1376 pre_lk(DECL_ARGS) 1377 { 1378 const struct mdoc_node *link, *descr; 1379 1380 if (NULL == (link = n->child)) 1381 return(0); 1382 1383 if (NULL != (descr = link->next)) { 1384 font_push('I'); 1385 while (NULL != descr) { 1386 print_word(descr->string); 1387 descr = descr->next; 1388 } 1389 print_word(":"); 1390 font_pop(); 1391 } 1392 1393 font_push('B'); 1394 print_word(link->string); 1395 font_pop(); 1396 return(0); 1397 } 1398 1399 static int 1400 pre_li(DECL_ARGS) 1401 { 1402 1403 font_push('R'); 1404 return(1); 1405 } 1406 1407 static int 1408 pre_nm(DECL_ARGS) 1409 { 1410 char *name; 1411 1412 if (MDOC_BLOCK == n->type) { 1413 outflags |= MMAN_Bk; 1414 pre_syn(n); 1415 } 1416 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 1417 return(1); 1418 name = n->child ? n->child->string : meta->name; 1419 if (NULL == name) 1420 return(0); 1421 if (MDOC_HEAD == n->type) { 1422 if (NULL == n->parent->prev) 1423 outflags |= MMAN_sp; 1424 print_block(".HP", 0); 1425 printf(" %zun", strlen(name) + 1); 1426 outflags |= MMAN_nl; 1427 } 1428 font_push('B'); 1429 if (NULL == n->child) 1430 print_word(meta->name); 1431 return(1); 1432 } 1433 1434 static void 1435 post_nm(DECL_ARGS) 1436 { 1437 1438 switch (n->type) { 1439 case (MDOC_BLOCK): 1440 outflags &= ~MMAN_Bk; 1441 break; 1442 case (MDOC_HEAD): 1443 /* FALLTHROUGH */ 1444 case (MDOC_ELEM): 1445 font_pop(); 1446 break; 1447 default: 1448 break; 1449 } 1450 } 1451 1452 static int 1453 pre_no(DECL_ARGS) 1454 { 1455 1456 outflags |= MMAN_spc_force; 1457 return(1); 1458 } 1459 1460 static int 1461 pre_ns(DECL_ARGS) 1462 { 1463 1464 outflags &= ~MMAN_spc; 1465 return(0); 1466 } 1467 1468 static void 1469 post_pf(DECL_ARGS) 1470 { 1471 1472 outflags &= ~MMAN_spc; 1473 } 1474 1475 static int 1476 pre_pp(DECL_ARGS) 1477 { 1478 1479 if (MDOC_It != n->parent->tok) 1480 outflags |= MMAN_PP; 1481 outflags |= MMAN_sp | MMAN_nl; 1482 outflags &= ~MMAN_br; 1483 return(0); 1484 } 1485 1486 static int 1487 pre_rs(DECL_ARGS) 1488 { 1489 1490 if (SEC_SEE_ALSO == n->sec) { 1491 outflags |= MMAN_PP | MMAN_sp | MMAN_nl; 1492 outflags &= ~MMAN_br; 1493 } 1494 return(1); 1495 } 1496 1497 static int 1498 pre_sm(DECL_ARGS) 1499 { 1500 1501 assert(n->child && MDOC_TEXT == n->child->type); 1502 if (0 == strcmp("on", n->child->string)) 1503 outflags |= MMAN_Sm | MMAN_spc; 1504 else 1505 outflags &= ~MMAN_Sm; 1506 return(0); 1507 } 1508 1509 static int 1510 pre_sp(DECL_ARGS) 1511 { 1512 1513 if (MMAN_PP & outflags) { 1514 outflags &= ~MMAN_PP; 1515 print_line(".PP", 0); 1516 } else 1517 print_line(".sp", 0); 1518 return(1); 1519 } 1520 1521 static void 1522 post_sp(DECL_ARGS) 1523 { 1524 1525 outflags |= MMAN_nl; 1526 } 1527 1528 static int 1529 pre_sy(DECL_ARGS) 1530 { 1531 1532 font_push('B'); 1533 return(1); 1534 } 1535 1536 static int 1537 pre_vt(DECL_ARGS) 1538 { 1539 1540 if (MDOC_SYNPRETTY & n->flags) { 1541 switch (n->type) { 1542 case (MDOC_BLOCK): 1543 pre_syn(n); 1544 return(1); 1545 case (MDOC_BODY): 1546 break; 1547 default: 1548 return(0); 1549 } 1550 } 1551 font_push('I'); 1552 return(1); 1553 } 1554 1555 static void 1556 post_vt(DECL_ARGS) 1557 { 1558 1559 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type) 1560 return; 1561 font_pop(); 1562 } 1563 1564 static int 1565 pre_xr(DECL_ARGS) 1566 { 1567 1568 n = n->child; 1569 if (NULL == n) 1570 return(0); 1571 print_node(meta, n); 1572 n = n->next; 1573 if (NULL == n) 1574 return(0); 1575 outflags &= ~MMAN_spc; 1576 print_word("("); 1577 print_node(meta, n); 1578 print_word(")"); 1579 return(0); 1580 } 1581 1582 static int 1583 pre_ux(DECL_ARGS) 1584 { 1585 1586 print_word(manacts[n->tok].prefix); 1587 if (NULL == n->child) 1588 return(0); 1589 outflags &= ~MMAN_spc; 1590 print_word("\\ "); 1591 outflags &= ~MMAN_spc; 1592 return(1); 1593 } 1594