1 /* $Id: mdoc_man.c,v 1.57 2013/12/25 22:00:45 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 int am_Fa; 1034 1035 am_Fa = MDOC_Fa == n->tok; 1036 1037 if (am_Fa) 1038 n = n->child; 1039 1040 while (NULL != n) { 1041 font_push('I'); 1042 if (am_Fa || MDOC_SYNPRETTY & n->flags) 1043 outflags |= MMAN_nbrword; 1044 print_node(meta, n); 1045 font_pop(); 1046 if (NULL != (n = n->next)) 1047 print_word(","); 1048 } 1049 return(0); 1050 } 1051 1052 static void 1053 post_fa(DECL_ARGS) 1054 { 1055 1056 if (NULL != n->next && MDOC_Fa == n->next->tok) 1057 print_word(","); 1058 } 1059 1060 static int 1061 pre_fd(DECL_ARGS) 1062 { 1063 1064 pre_syn(n); 1065 font_push('B'); 1066 return(1); 1067 } 1068 1069 static void 1070 post_fd(DECL_ARGS) 1071 { 1072 1073 font_pop(); 1074 outflags |= MMAN_br; 1075 } 1076 1077 static int 1078 pre_fl(DECL_ARGS) 1079 { 1080 1081 font_push('B'); 1082 print_word("\\-"); 1083 outflags &= ~MMAN_spc; 1084 return(1); 1085 } 1086 1087 static void 1088 post_fl(DECL_ARGS) 1089 { 1090 1091 font_pop(); 1092 if (0 == n->nchild && NULL != n->next && 1093 n->next->line == n->line) 1094 outflags &= ~MMAN_spc; 1095 } 1096 1097 static int 1098 pre_fn(DECL_ARGS) 1099 { 1100 1101 pre_syn(n); 1102 1103 n = n->child; 1104 if (NULL == n) 1105 return(0); 1106 1107 if (MDOC_SYNPRETTY & n->flags) 1108 print_block(".HP 4n", MMAN_nl); 1109 1110 font_push('B'); 1111 print_node(meta, n); 1112 font_pop(); 1113 outflags &= ~MMAN_spc; 1114 print_word("("); 1115 outflags &= ~MMAN_spc; 1116 1117 n = n->next; 1118 if (NULL != n) 1119 pre_fa(meta, n); 1120 return(0); 1121 } 1122 1123 static void 1124 post_fn(DECL_ARGS) 1125 { 1126 1127 print_word(")"); 1128 if (MDOC_SYNPRETTY & n->flags) { 1129 print_word(";"); 1130 outflags |= MMAN_PP; 1131 } 1132 } 1133 1134 static int 1135 pre_fo(DECL_ARGS) 1136 { 1137 1138 switch (n->type) { 1139 case (MDOC_BLOCK): 1140 pre_syn(n); 1141 break; 1142 case (MDOC_HEAD): 1143 if (MDOC_SYNPRETTY & n->flags) 1144 print_block(".HP 4n", MMAN_nl); 1145 font_push('B'); 1146 break; 1147 case (MDOC_BODY): 1148 outflags &= ~MMAN_spc; 1149 print_word("("); 1150 outflags &= ~MMAN_spc; 1151 break; 1152 default: 1153 break; 1154 } 1155 return(1); 1156 } 1157 1158 static void 1159 post_fo(DECL_ARGS) 1160 { 1161 1162 switch (n->type) { 1163 case (MDOC_HEAD): 1164 font_pop(); 1165 break; 1166 case (MDOC_BODY): 1167 post_fn(meta, n); 1168 break; 1169 default: 1170 break; 1171 } 1172 } 1173 1174 static int 1175 pre_ft(DECL_ARGS) 1176 { 1177 1178 pre_syn(n); 1179 font_push('I'); 1180 return(1); 1181 } 1182 1183 static int 1184 pre_in(DECL_ARGS) 1185 { 1186 1187 if (MDOC_SYNPRETTY & n->flags) { 1188 pre_syn(n); 1189 font_push('B'); 1190 print_word("#include <"); 1191 outflags &= ~MMAN_spc; 1192 } else { 1193 print_word("<"); 1194 outflags &= ~MMAN_spc; 1195 font_push('I'); 1196 } 1197 return(1); 1198 } 1199 1200 static void 1201 post_in(DECL_ARGS) 1202 { 1203 1204 if (MDOC_SYNPRETTY & n->flags) { 1205 outflags &= ~MMAN_spc; 1206 print_word(">"); 1207 font_pop(); 1208 outflags |= MMAN_br; 1209 } else { 1210 font_pop(); 1211 outflags &= ~MMAN_spc; 1212 print_word(">"); 1213 } 1214 } 1215 1216 static int 1217 pre_it(DECL_ARGS) 1218 { 1219 const struct mdoc_node *bln; 1220 1221 switch (n->type) { 1222 case (MDOC_HEAD): 1223 outflags |= MMAN_PP | MMAN_nl; 1224 bln = n->parent->parent; 1225 if (0 == bln->norm->Bl.comp || 1226 (NULL == n->parent->prev && 1227 NULL == bln->parent->prev)) 1228 outflags |= MMAN_sp; 1229 outflags &= ~MMAN_br; 1230 switch (bln->norm->Bl.type) { 1231 case (LIST_item): 1232 return(0); 1233 case (LIST_inset): 1234 /* FALLTHROUGH */ 1235 case (LIST_diag): 1236 /* FALLTHROUGH */ 1237 case (LIST_ohang): 1238 if (bln->norm->Bl.type == LIST_diag) 1239 print_line(".B \"", 0); 1240 else 1241 print_line(".R \"", 0); 1242 outflags &= ~MMAN_spc; 1243 return(1); 1244 case (LIST_bullet): 1245 /* FALLTHROUGH */ 1246 case (LIST_dash): 1247 /* FALLTHROUGH */ 1248 case (LIST_hyphen): 1249 print_width(bln->norm->Bl.width, NULL, 0); 1250 TPremain = 0; 1251 outflags |= MMAN_nl; 1252 font_push('B'); 1253 if (LIST_bullet == bln->norm->Bl.type) 1254 print_word("o"); 1255 else 1256 print_word("-"); 1257 font_pop(); 1258 break; 1259 case (LIST_enum): 1260 print_width(bln->norm->Bl.width, NULL, 0); 1261 TPremain = 0; 1262 outflags |= MMAN_nl; 1263 print_count(&bln->norm->Bl.count); 1264 break; 1265 case (LIST_hang): 1266 print_width(bln->norm->Bl.width, n->child, 6); 1267 TPremain = 0; 1268 break; 1269 case (LIST_tag): 1270 print_width(bln->norm->Bl.width, n->child, 0); 1271 putchar('\n'); 1272 outflags &= ~MMAN_spc; 1273 return(1); 1274 default: 1275 return(1); 1276 } 1277 outflags |= MMAN_nl; 1278 default: 1279 break; 1280 } 1281 return(1); 1282 } 1283 1284 /* 1285 * This function is called after closing out an indented block. 1286 * If we are inside an enclosing list, restore its indentation. 1287 */ 1288 static void 1289 mid_it(void) 1290 { 1291 char buf[24]; 1292 1293 /* Nothing to do outside a list. */ 1294 if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1]) 1295 return; 1296 1297 /* The indentation has already been set up. */ 1298 if (Bl_stack_post[Bl_stack_len - 1]) 1299 return; 1300 1301 /* Restore the indentation of the enclosing list. */ 1302 print_line(".RS", MMAN_Bk_susp); 1303 snprintf(buf, sizeof(buf), "%zun", Bl_stack[Bl_stack_len - 1]); 1304 print_word(buf); 1305 1306 /* Remeber to close out this .RS block later. */ 1307 Bl_stack_post[Bl_stack_len - 1] = 1; 1308 } 1309 1310 static void 1311 post_it(DECL_ARGS) 1312 { 1313 const struct mdoc_node *bln; 1314 1315 bln = n->parent->parent; 1316 1317 switch (n->type) { 1318 case (MDOC_HEAD): 1319 switch (bln->norm->Bl.type) { 1320 case (LIST_diag): 1321 outflags &= ~MMAN_spc; 1322 print_word("\\ "); 1323 break; 1324 case (LIST_ohang): 1325 outflags |= MMAN_br; 1326 break; 1327 default: 1328 break; 1329 } 1330 break; 1331 case (MDOC_BODY): 1332 switch (bln->norm->Bl.type) { 1333 case (LIST_bullet): 1334 /* FALLTHROUGH */ 1335 case (LIST_dash): 1336 /* FALLTHROUGH */ 1337 case (LIST_hyphen): 1338 /* FALLTHROUGH */ 1339 case (LIST_enum): 1340 /* FALLTHROUGH */ 1341 case (LIST_hang): 1342 /* FALLTHROUGH */ 1343 case (LIST_tag): 1344 assert(Bl_stack_len); 1345 Bl_stack[--Bl_stack_len] = 0; 1346 1347 /* 1348 * Our indentation had to be restored 1349 * after a child display or child list. 1350 * Close out that indentation block now. 1351 */ 1352 if (Bl_stack_post[Bl_stack_len]) { 1353 print_line(".RE", MMAN_nl); 1354 Bl_stack_post[Bl_stack_len] = 0; 1355 } 1356 break; 1357 case (LIST_column): 1358 if (NULL != n->next) { 1359 putchar('\t'); 1360 outflags &= ~MMAN_spc; 1361 } 1362 break; 1363 default: 1364 break; 1365 } 1366 break; 1367 default: 1368 break; 1369 } 1370 } 1371 1372 static void 1373 post_lb(DECL_ARGS) 1374 { 1375 1376 if (SEC_LIBRARY == n->sec) 1377 outflags |= MMAN_br; 1378 } 1379 1380 static int 1381 pre_lk(DECL_ARGS) 1382 { 1383 const struct mdoc_node *link, *descr; 1384 1385 if (NULL == (link = n->child)) 1386 return(0); 1387 1388 if (NULL != (descr = link->next)) { 1389 font_push('I'); 1390 while (NULL != descr) { 1391 print_word(descr->string); 1392 descr = descr->next; 1393 } 1394 print_word(":"); 1395 font_pop(); 1396 } 1397 1398 font_push('B'); 1399 print_word(link->string); 1400 font_pop(); 1401 return(0); 1402 } 1403 1404 static int 1405 pre_li(DECL_ARGS) 1406 { 1407 1408 font_push('R'); 1409 return(1); 1410 } 1411 1412 static int 1413 pre_nm(DECL_ARGS) 1414 { 1415 char *name; 1416 1417 if (MDOC_BLOCK == n->type) { 1418 outflags |= MMAN_Bk; 1419 pre_syn(n); 1420 } 1421 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 1422 return(1); 1423 name = n->child ? n->child->string : meta->name; 1424 if (NULL == name) 1425 return(0); 1426 if (MDOC_HEAD == n->type) { 1427 if (NULL == n->parent->prev) 1428 outflags |= MMAN_sp; 1429 print_block(".HP", 0); 1430 printf(" %zun", strlen(name) + 1); 1431 outflags |= MMAN_nl; 1432 } 1433 font_push('B'); 1434 if (NULL == n->child) 1435 print_word(meta->name); 1436 return(1); 1437 } 1438 1439 static void 1440 post_nm(DECL_ARGS) 1441 { 1442 1443 switch (n->type) { 1444 case (MDOC_BLOCK): 1445 outflags &= ~MMAN_Bk; 1446 break; 1447 case (MDOC_HEAD): 1448 /* FALLTHROUGH */ 1449 case (MDOC_ELEM): 1450 font_pop(); 1451 break; 1452 default: 1453 break; 1454 } 1455 } 1456 1457 static int 1458 pre_no(DECL_ARGS) 1459 { 1460 1461 outflags |= MMAN_spc_force; 1462 return(1); 1463 } 1464 1465 static int 1466 pre_ns(DECL_ARGS) 1467 { 1468 1469 outflags &= ~MMAN_spc; 1470 return(0); 1471 } 1472 1473 static void 1474 post_pf(DECL_ARGS) 1475 { 1476 1477 outflags &= ~MMAN_spc; 1478 } 1479 1480 static int 1481 pre_pp(DECL_ARGS) 1482 { 1483 1484 if (MDOC_It != n->parent->tok) 1485 outflags |= MMAN_PP; 1486 outflags |= MMAN_sp | MMAN_nl; 1487 outflags &= ~MMAN_br; 1488 return(0); 1489 } 1490 1491 static int 1492 pre_rs(DECL_ARGS) 1493 { 1494 1495 if (SEC_SEE_ALSO == n->sec) { 1496 outflags |= MMAN_PP | MMAN_sp | MMAN_nl; 1497 outflags &= ~MMAN_br; 1498 } 1499 return(1); 1500 } 1501 1502 static int 1503 pre_sm(DECL_ARGS) 1504 { 1505 1506 assert(n->child && MDOC_TEXT == n->child->type); 1507 if (0 == strcmp("on", n->child->string)) 1508 outflags |= MMAN_Sm | MMAN_spc; 1509 else 1510 outflags &= ~MMAN_Sm; 1511 return(0); 1512 } 1513 1514 static int 1515 pre_sp(DECL_ARGS) 1516 { 1517 1518 if (MMAN_PP & outflags) { 1519 outflags &= ~MMAN_PP; 1520 print_line(".PP", 0); 1521 } else 1522 print_line(".sp", 0); 1523 return(1); 1524 } 1525 1526 static void 1527 post_sp(DECL_ARGS) 1528 { 1529 1530 outflags |= MMAN_nl; 1531 } 1532 1533 static int 1534 pre_sy(DECL_ARGS) 1535 { 1536 1537 font_push('B'); 1538 return(1); 1539 } 1540 1541 static int 1542 pre_vt(DECL_ARGS) 1543 { 1544 1545 if (MDOC_SYNPRETTY & n->flags) { 1546 switch (n->type) { 1547 case (MDOC_BLOCK): 1548 pre_syn(n); 1549 return(1); 1550 case (MDOC_BODY): 1551 break; 1552 default: 1553 return(0); 1554 } 1555 } 1556 font_push('I'); 1557 return(1); 1558 } 1559 1560 static void 1561 post_vt(DECL_ARGS) 1562 { 1563 1564 if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type) 1565 return; 1566 font_pop(); 1567 } 1568 1569 static int 1570 pre_xr(DECL_ARGS) 1571 { 1572 1573 n = n->child; 1574 if (NULL == n) 1575 return(0); 1576 print_node(meta, n); 1577 n = n->next; 1578 if (NULL == n) 1579 return(0); 1580 outflags &= ~MMAN_spc; 1581 print_word("("); 1582 print_node(meta, n); 1583 print_word(")"); 1584 return(0); 1585 } 1586 1587 static int 1588 pre_ux(DECL_ARGS) 1589 { 1590 1591 print_word(manacts[n->tok].prefix); 1592 if (NULL == n->child) 1593 return(0); 1594 outflags &= ~MMAN_spc; 1595 print_word("\\ "); 1596 outflags &= ~MMAN_spc; 1597 return(1); 1598 }