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