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