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