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