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