1 /* $Id: mdoc_man.c,v 1.122 2017/06/14 22:51:25 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include "config.h" 18 19 #include <sys/types.h> 20 21 #include <assert.h> 22 #include <stdio.h> 23 #include <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(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", 614 mdoc->meta.title, 615 (mdoc->meta.msec == NULL ? "" : mdoc->meta.msec), 616 mdoc->meta.date, mdoc->meta.os, mdoc->meta.vol); 617 618 /* Disable hyphenation and if nroff, disable justification. */ 619 printf(".nh\n.if n .ad l"); 620 621 outflags = MMAN_nl | MMAN_Sm; 622 if (0 == fontqueue.size) { 623 fontqueue.size = 8; 624 fontqueue.head = fontqueue.tail = mandoc_malloc(8); 625 *fontqueue.tail = 'R'; 626 } 627 for (n = mdoc->first->child; n != NULL; n = n->next) 628 print_node(&mdoc->meta, n); 629 putchar('\n'); 630 } 631 632 static void 633 print_node(DECL_ARGS) 634 { 635 const struct manact *act; 636 struct roff_node *sub; 637 int cond, do_sub; 638 639 if (n->flags & NODE_NOPRT) 640 return; 641 642 /* 643 * Break the line if we were parsed subsequent the current node. 644 * This makes the page structure be more consistent. 645 */ 646 if (MMAN_spc & outflags && NODE_LINE & n->flags) 647 outflags |= MMAN_nl; 648 649 act = NULL; 650 cond = 0; 651 do_sub = 1; 652 n->flags &= ~NODE_ENDED; 653 654 if (n->type == ROFFT_TEXT) { 655 /* 656 * Make sure that we don't happen to start with a 657 * control character at the start of a line. 658 */ 659 if (MMAN_nl & outflags && 660 ('.' == *n->string || '\'' == *n->string)) { 661 print_word(""); 662 printf("\\&"); 663 outflags &= ~MMAN_spc; 664 } 665 if (n->flags & NODE_DELIMC) 666 outflags &= ~(MMAN_spc | MMAN_spc_force); 667 else if (outflags & MMAN_Sm) 668 outflags |= MMAN_spc_force; 669 print_word(n->string); 670 if (n->flags & NODE_DELIMO) 671 outflags &= ~(MMAN_spc | MMAN_spc_force); 672 else if (outflags & MMAN_Sm) 673 outflags |= MMAN_spc; 674 } else if (n->tok < ROFF_MAX) { 675 (*roff_manacts[n->tok])(meta, n); 676 return; 677 } else { 678 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 679 /* 680 * Conditionally run the pre-node action handler for a 681 * node. 682 */ 683 act = manacts + n->tok; 684 cond = act->cond == NULL || (*act->cond)(meta, n); 685 if (cond && act->pre != NULL && 686 (n->end == ENDBODY_NOT || n->child != NULL)) 687 do_sub = (*act->pre)(meta, n); 688 } 689 690 /* 691 * Conditionally run all child nodes. 692 * Note that this iterates over children instead of using 693 * recursion. This prevents unnecessary depth in the stack. 694 */ 695 if (do_sub) 696 for (sub = n->child; sub; sub = sub->next) 697 print_node(meta, sub); 698 699 /* 700 * Lastly, conditionally run the post-node handler. 701 */ 702 if (NODE_ENDED & n->flags) 703 return; 704 705 if (cond && act->post) 706 (*act->post)(meta, n); 707 708 if (ENDBODY_NOT != n->end) 709 n->body->flags |= NODE_ENDED; 710 } 711 712 static int 713 cond_head(DECL_ARGS) 714 { 715 716 return n->type == ROFFT_HEAD; 717 } 718 719 static int 720 cond_body(DECL_ARGS) 721 { 722 723 return n->type == ROFFT_BODY; 724 } 725 726 static int 727 pre_enc(DECL_ARGS) 728 { 729 const char *prefix; 730 731 prefix = manacts[n->tok].prefix; 732 if (NULL == prefix) 733 return 1; 734 print_word(prefix); 735 outflags &= ~MMAN_spc; 736 return 1; 737 } 738 739 static void 740 post_enc(DECL_ARGS) 741 { 742 const char *suffix; 743 744 suffix = manacts[n->tok].suffix; 745 if (NULL == suffix) 746 return; 747 outflags &= ~(MMAN_spc | MMAN_nl); 748 print_word(suffix); 749 } 750 751 static int 752 pre_ex(DECL_ARGS) 753 { 754 outflags |= MMAN_br | MMAN_nl; 755 return 1; 756 } 757 758 static void 759 post_font(DECL_ARGS) 760 { 761 762 font_pop(); 763 } 764 765 static void 766 post_percent(DECL_ARGS) 767 { 768 769 if (pre_em == manacts[n->tok].pre) 770 font_pop(); 771 if (n->next) { 772 print_word(","); 773 if (n->prev && n->prev->tok == n->tok && 774 n->next->tok == n->tok) 775 print_word("and"); 776 } else { 777 print_word("."); 778 outflags |= MMAN_nl; 779 } 780 } 781 782 static int 783 pre__t(DECL_ARGS) 784 { 785 786 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) { 787 print_word("\\(lq"); 788 outflags &= ~MMAN_spc; 789 } else 790 font_push('I'); 791 return 1; 792 } 793 794 static void 795 post__t(DECL_ARGS) 796 { 797 798 if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) { 799 outflags &= ~MMAN_spc; 800 print_word("\\(rq"); 801 } else 802 font_pop(); 803 post_percent(meta, n); 804 } 805 806 /* 807 * Print before a section header. 808 */ 809 static int 810 pre_sect(DECL_ARGS) 811 { 812 813 if (n->type == ROFFT_HEAD) { 814 outflags |= MMAN_sp; 815 print_block(manacts[n->tok].prefix, 0); 816 print_word(""); 817 putchar('\"'); 818 outflags &= ~MMAN_spc; 819 } 820 return 1; 821 } 822 823 /* 824 * Print subsequent a section header. 825 */ 826 static void 827 post_sect(DECL_ARGS) 828 { 829 830 if (n->type != ROFFT_HEAD) 831 return; 832 outflags &= ~MMAN_spc; 833 print_word(""); 834 putchar('\"'); 835 outflags |= MMAN_nl; 836 if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec) 837 outflags &= ~(MMAN_An_split | MMAN_An_nosplit); 838 } 839 840 /* See mdoc_term.c, synopsis_pre() for comments. */ 841 static void 842 pre_syn(const struct roff_node *n) 843 { 844 845 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) 846 return; 847 848 if (n->prev->tok == n->tok && 849 MDOC_Ft != n->tok && 850 MDOC_Fo != n->tok && 851 MDOC_Fn != n->tok) { 852 outflags |= MMAN_br; 853 return; 854 } 855 856 switch (n->prev->tok) { 857 case MDOC_Fd: 858 case MDOC_Fn: 859 case MDOC_Fo: 860 case MDOC_In: 861 case MDOC_Vt: 862 outflags |= MMAN_sp; 863 break; 864 case MDOC_Ft: 865 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 866 outflags |= MMAN_sp; 867 break; 868 } 869 /* FALLTHROUGH */ 870 default: 871 outflags |= MMAN_br; 872 break; 873 } 874 } 875 876 static int 877 pre_an(DECL_ARGS) 878 { 879 880 switch (n->norm->An.auth) { 881 case AUTH_split: 882 outflags &= ~MMAN_An_nosplit; 883 outflags |= MMAN_An_split; 884 return 0; 885 case AUTH_nosplit: 886 outflags &= ~MMAN_An_split; 887 outflags |= MMAN_An_nosplit; 888 return 0; 889 default: 890 if (MMAN_An_split & outflags) 891 outflags |= MMAN_br; 892 else if (SEC_AUTHORS == n->sec && 893 ! (MMAN_An_nosplit & outflags)) 894 outflags |= MMAN_An_split; 895 return 1; 896 } 897 } 898 899 static int 900 pre_ap(DECL_ARGS) 901 { 902 903 outflags &= ~MMAN_spc; 904 print_word("'"); 905 outflags &= ~MMAN_spc; 906 return 0; 907 } 908 909 static int 910 pre_aq(DECL_ARGS) 911 { 912 913 print_word(n->child != NULL && n->child->next == NULL && 914 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 915 outflags &= ~MMAN_spc; 916 return 1; 917 } 918 919 static void 920 post_aq(DECL_ARGS) 921 { 922 923 outflags &= ~(MMAN_spc | MMAN_nl); 924 print_word(n->child != NULL && n->child->next == NULL && 925 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 926 } 927 928 static int 929 pre_bd(DECL_ARGS) 930 { 931 932 outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); 933 934 if (DISP_unfilled == n->norm->Bd.type || 935 DISP_literal == n->norm->Bd.type) 936 print_line(".nf", 0); 937 if (0 == n->norm->Bd.comp && NULL != n->parent->prev) 938 outflags |= MMAN_sp; 939 print_offs(n->norm->Bd.offs, 1); 940 return 1; 941 } 942 943 static void 944 post_bd(DECL_ARGS) 945 { 946 947 /* Close out this display. */ 948 print_line(".RE", MMAN_nl); 949 if (DISP_unfilled == n->norm->Bd.type || 950 DISP_literal == n->norm->Bd.type) 951 print_line(".fi", MMAN_nl); 952 953 /* Maybe we are inside an enclosing list? */ 954 if (NULL != n->parent->next) 955 mid_it(); 956 } 957 958 static int 959 pre_bf(DECL_ARGS) 960 { 961 962 switch (n->type) { 963 case ROFFT_BLOCK: 964 return 1; 965 case ROFFT_BODY: 966 break; 967 default: 968 return 0; 969 } 970 switch (n->norm->Bf.font) { 971 case FONT_Em: 972 font_push('I'); 973 break; 974 case FONT_Sy: 975 font_push('B'); 976 break; 977 default: 978 font_push('R'); 979 break; 980 } 981 return 1; 982 } 983 984 static void 985 post_bf(DECL_ARGS) 986 { 987 988 if (n->type == ROFFT_BODY) 989 font_pop(); 990 } 991 992 static int 993 pre_bk(DECL_ARGS) 994 { 995 switch (n->type) { 996 case ROFFT_BLOCK: 997 return 1; 998 case ROFFT_BODY: 999 case ROFFT_ELEM: 1000 outflags |= MMAN_Bk; 1001 return 1; 1002 default: 1003 return 0; 1004 } 1005 } 1006 1007 static void 1008 post_bk(DECL_ARGS) 1009 { 1010 switch (n->type) { 1011 case ROFFT_ELEM: 1012 while ((n = n->parent) != NULL) 1013 if (n->tok == MDOC_Bk) 1014 return; 1015 /* FALLTHROUGH */ 1016 case ROFFT_BODY: 1017 outflags &= ~MMAN_Bk; 1018 break; 1019 default: 1020 break; 1021 } 1022 } 1023 1024 static int 1025 pre_bl(DECL_ARGS) 1026 { 1027 size_t icol; 1028 1029 /* 1030 * print_offs() will increase the -offset to account for 1031 * a possible enclosing .It, but any enclosed .It blocks 1032 * just nest and do not add up their indentation. 1033 */ 1034 if (n->norm->Bl.offs) { 1035 print_offs(n->norm->Bl.offs, 0); 1036 Bl_stack[Bl_stack_len++] = 0; 1037 } 1038 1039 switch (n->norm->Bl.type) { 1040 case LIST_enum: 1041 n->norm->Bl.count = 0; 1042 return 1; 1043 case LIST_column: 1044 break; 1045 default: 1046 return 1; 1047 } 1048 1049 if (n->child != NULL) { 1050 print_line(".TS", MMAN_nl); 1051 for (icol = 0; icol < n->norm->Bl.ncols; icol++) 1052 print_word("l"); 1053 print_word("."); 1054 } 1055 outflags |= MMAN_nl; 1056 return 1; 1057 } 1058 1059 static void 1060 post_bl(DECL_ARGS) 1061 { 1062 1063 switch (n->norm->Bl.type) { 1064 case LIST_column: 1065 if (n->child != NULL) 1066 print_line(".TE", 0); 1067 break; 1068 case LIST_enum: 1069 n->norm->Bl.count = 0; 1070 break; 1071 default: 1072 break; 1073 } 1074 1075 if (n->norm->Bl.offs) { 1076 print_line(".RE", MMAN_nl); 1077 assert(Bl_stack_len); 1078 Bl_stack_len--; 1079 assert(0 == Bl_stack[Bl_stack_len]); 1080 } else { 1081 outflags |= MMAN_PP | MMAN_nl; 1082 outflags &= ~(MMAN_sp | MMAN_br); 1083 } 1084 1085 /* Maybe we are inside an enclosing list? */ 1086 if (NULL != n->parent->next) 1087 mid_it(); 1088 1089 } 1090 1091 static void 1092 pre_br(DECL_ARGS) 1093 { 1094 outflags |= MMAN_br; 1095 } 1096 1097 static int 1098 pre_dl(DECL_ARGS) 1099 { 1100 1101 print_offs("6n", 0); 1102 return 1; 1103 } 1104 1105 static void 1106 post_dl(DECL_ARGS) 1107 { 1108 1109 print_line(".RE", MMAN_nl); 1110 1111 /* Maybe we are inside an enclosing list? */ 1112 if (NULL != n->parent->next) 1113 mid_it(); 1114 } 1115 1116 static int 1117 pre_em(DECL_ARGS) 1118 { 1119 1120 font_push('I'); 1121 return 1; 1122 } 1123 1124 static int 1125 pre_en(DECL_ARGS) 1126 { 1127 1128 if (NULL == n->norm->Es || 1129 NULL == n->norm->Es->child) 1130 return 1; 1131 1132 print_word(n->norm->Es->child->string); 1133 outflags &= ~MMAN_spc; 1134 return 1; 1135 } 1136 1137 static void 1138 post_en(DECL_ARGS) 1139 { 1140 1141 if (NULL == n->norm->Es || 1142 NULL == n->norm->Es->child || 1143 NULL == n->norm->Es->child->next) 1144 return; 1145 1146 outflags &= ~MMAN_spc; 1147 print_word(n->norm->Es->child->next->string); 1148 return; 1149 } 1150 1151 static int 1152 pre_eo(DECL_ARGS) 1153 { 1154 1155 if (n->end == ENDBODY_NOT && 1156 n->parent->head->child == NULL && 1157 n->child != NULL && 1158 n->child->end != ENDBODY_NOT) 1159 print_word("\\&"); 1160 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1161 n->parent->head->child != NULL && (n->child != NULL || 1162 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1163 outflags &= ~(MMAN_spc | MMAN_nl); 1164 return 1; 1165 } 1166 1167 static void 1168 post_eo(DECL_ARGS) 1169 { 1170 int body, tail; 1171 1172 if (n->end != ENDBODY_NOT) { 1173 outflags |= MMAN_spc; 1174 return; 1175 } 1176 1177 body = n->child != NULL || n->parent->head->child != NULL; 1178 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1179 1180 if (body && tail) 1181 outflags &= ~MMAN_spc; 1182 else if ( ! (body || tail)) 1183 print_word("\\&"); 1184 else if ( ! tail) 1185 outflags |= MMAN_spc; 1186 } 1187 1188 static int 1189 pre_fa(DECL_ARGS) 1190 { 1191 int am_Fa; 1192 1193 am_Fa = MDOC_Fa == n->tok; 1194 1195 if (am_Fa) 1196 n = n->child; 1197 1198 while (NULL != n) { 1199 font_push('I'); 1200 if (am_Fa || NODE_SYNPRETTY & n->flags) 1201 outflags |= MMAN_nbrword; 1202 print_node(meta, n); 1203 font_pop(); 1204 if (NULL != (n = n->next)) 1205 print_word(","); 1206 } 1207 return 0; 1208 } 1209 1210 static void 1211 post_fa(DECL_ARGS) 1212 { 1213 1214 if (NULL != n->next && MDOC_Fa == n->next->tok) 1215 print_word(","); 1216 } 1217 1218 static int 1219 pre_fd(DECL_ARGS) 1220 { 1221 1222 pre_syn(n); 1223 font_push('B'); 1224 return 1; 1225 } 1226 1227 static void 1228 post_fd(DECL_ARGS) 1229 { 1230 1231 font_pop(); 1232 outflags |= MMAN_br; 1233 } 1234 1235 static int 1236 pre_fl(DECL_ARGS) 1237 { 1238 1239 font_push('B'); 1240 print_word("\\-"); 1241 if (n->child != NULL) 1242 outflags &= ~MMAN_spc; 1243 return 1; 1244 } 1245 1246 static void 1247 post_fl(DECL_ARGS) 1248 { 1249 1250 font_pop(); 1251 if (!(n->child != NULL || 1252 n->next == NULL || 1253 n->next->type == ROFFT_TEXT || 1254 n->next->flags & NODE_LINE)) 1255 outflags &= ~MMAN_spc; 1256 } 1257 1258 static int 1259 pre_fn(DECL_ARGS) 1260 { 1261 1262 pre_syn(n); 1263 1264 n = n->child; 1265 if (NULL == n) 1266 return 0; 1267 1268 if (NODE_SYNPRETTY & n->flags) 1269 print_block(".HP 4n", MMAN_nl); 1270 1271 font_push('B'); 1272 print_node(meta, n); 1273 font_pop(); 1274 outflags &= ~MMAN_spc; 1275 print_word("("); 1276 outflags &= ~MMAN_spc; 1277 1278 n = n->next; 1279 if (NULL != n) 1280 pre_fa(meta, n); 1281 return 0; 1282 } 1283 1284 static void 1285 post_fn(DECL_ARGS) 1286 { 1287 1288 print_word(")"); 1289 if (NODE_SYNPRETTY & n->flags) { 1290 print_word(";"); 1291 outflags |= MMAN_PP; 1292 } 1293 } 1294 1295 static int 1296 pre_fo(DECL_ARGS) 1297 { 1298 1299 switch (n->type) { 1300 case ROFFT_BLOCK: 1301 pre_syn(n); 1302 break; 1303 case ROFFT_HEAD: 1304 if (n->child == NULL) 1305 return 0; 1306 if (NODE_SYNPRETTY & n->flags) 1307 print_block(".HP 4n", MMAN_nl); 1308 font_push('B'); 1309 break; 1310 case ROFFT_BODY: 1311 outflags &= ~(MMAN_spc | MMAN_nl); 1312 print_word("("); 1313 outflags &= ~MMAN_spc; 1314 break; 1315 default: 1316 break; 1317 } 1318 return 1; 1319 } 1320 1321 static void 1322 post_fo(DECL_ARGS) 1323 { 1324 1325 switch (n->type) { 1326 case ROFFT_HEAD: 1327 if (n->child != NULL) 1328 font_pop(); 1329 break; 1330 case ROFFT_BODY: 1331 post_fn(meta, n); 1332 break; 1333 default: 1334 break; 1335 } 1336 } 1337 1338 static int 1339 pre_Ft(DECL_ARGS) 1340 { 1341 1342 pre_syn(n); 1343 font_push('I'); 1344 return 1; 1345 } 1346 1347 static void 1348 pre_ft(DECL_ARGS) 1349 { 1350 print_line(".ft", 0); 1351 print_word(n->child->string); 1352 outflags |= MMAN_nl; 1353 } 1354 1355 static int 1356 pre_in(DECL_ARGS) 1357 { 1358 1359 if (NODE_SYNPRETTY & n->flags) { 1360 pre_syn(n); 1361 font_push('B'); 1362 print_word("#include <"); 1363 outflags &= ~MMAN_spc; 1364 } else { 1365 print_word("<"); 1366 outflags &= ~MMAN_spc; 1367 font_push('I'); 1368 } 1369 return 1; 1370 } 1371 1372 static void 1373 post_in(DECL_ARGS) 1374 { 1375 1376 if (NODE_SYNPRETTY & n->flags) { 1377 outflags &= ~MMAN_spc; 1378 print_word(">"); 1379 font_pop(); 1380 outflags |= MMAN_br; 1381 } else { 1382 font_pop(); 1383 outflags &= ~MMAN_spc; 1384 print_word(">"); 1385 } 1386 } 1387 1388 static int 1389 pre_it(DECL_ARGS) 1390 { 1391 const struct roff_node *bln; 1392 1393 switch (n->type) { 1394 case ROFFT_HEAD: 1395 outflags |= MMAN_PP | MMAN_nl; 1396 bln = n->parent->parent; 1397 if (0 == bln->norm->Bl.comp || 1398 (NULL == n->parent->prev && 1399 NULL == bln->parent->prev)) 1400 outflags |= MMAN_sp; 1401 outflags &= ~MMAN_br; 1402 switch (bln->norm->Bl.type) { 1403 case LIST_item: 1404 return 0; 1405 case LIST_inset: 1406 case LIST_diag: 1407 case LIST_ohang: 1408 if (bln->norm->Bl.type == LIST_diag) 1409 print_line(".B \"", 0); 1410 else 1411 print_line(".R \"", 0); 1412 outflags &= ~MMAN_spc; 1413 return 1; 1414 case LIST_bullet: 1415 case LIST_dash: 1416 case LIST_hyphen: 1417 print_width(&bln->norm->Bl, NULL); 1418 TPremain = 0; 1419 outflags |= MMAN_nl; 1420 font_push('B'); 1421 if (LIST_bullet == bln->norm->Bl.type) 1422 print_word("\\(bu"); 1423 else 1424 print_word("-"); 1425 font_pop(); 1426 outflags |= MMAN_nl; 1427 return 0; 1428 case LIST_enum: 1429 print_width(&bln->norm->Bl, NULL); 1430 TPremain = 0; 1431 outflags |= MMAN_nl; 1432 print_count(&bln->norm->Bl.count); 1433 outflags |= MMAN_nl; 1434 return 0; 1435 case LIST_hang: 1436 print_width(&bln->norm->Bl, n->child); 1437 TPremain = 0; 1438 outflags |= MMAN_nl; 1439 return 1; 1440 case LIST_tag: 1441 print_width(&bln->norm->Bl, n->child); 1442 putchar('\n'); 1443 outflags &= ~MMAN_spc; 1444 return 1; 1445 default: 1446 return 1; 1447 } 1448 default: 1449 break; 1450 } 1451 return 1; 1452 } 1453 1454 /* 1455 * This function is called after closing out an indented block. 1456 * If we are inside an enclosing list, restore its indentation. 1457 */ 1458 static void 1459 mid_it(void) 1460 { 1461 char buf[24]; 1462 1463 /* Nothing to do outside a list. */ 1464 if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1]) 1465 return; 1466 1467 /* The indentation has already been set up. */ 1468 if (Bl_stack_post[Bl_stack_len - 1]) 1469 return; 1470 1471 /* Restore the indentation of the enclosing list. */ 1472 print_line(".RS", MMAN_Bk_susp); 1473 (void)snprintf(buf, sizeof(buf), "%dn", 1474 Bl_stack[Bl_stack_len - 1]); 1475 print_word(buf); 1476 1477 /* Remeber to close out this .RS block later. */ 1478 Bl_stack_post[Bl_stack_len - 1] = 1; 1479 } 1480 1481 static void 1482 post_it(DECL_ARGS) 1483 { 1484 const struct roff_node *bln; 1485 1486 bln = n->parent->parent; 1487 1488 switch (n->type) { 1489 case ROFFT_HEAD: 1490 switch (bln->norm->Bl.type) { 1491 case LIST_diag: 1492 outflags &= ~MMAN_spc; 1493 print_word("\\ "); 1494 break; 1495 case LIST_ohang: 1496 outflags |= MMAN_br; 1497 break; 1498 default: 1499 break; 1500 } 1501 break; 1502 case ROFFT_BODY: 1503 switch (bln->norm->Bl.type) { 1504 case LIST_bullet: 1505 case LIST_dash: 1506 case LIST_hyphen: 1507 case LIST_enum: 1508 case LIST_hang: 1509 case LIST_tag: 1510 assert(Bl_stack_len); 1511 Bl_stack[--Bl_stack_len] = 0; 1512 1513 /* 1514 * Our indentation had to be restored 1515 * after a child display or child list. 1516 * Close out that indentation block now. 1517 */ 1518 if (Bl_stack_post[Bl_stack_len]) { 1519 print_line(".RE", MMAN_nl); 1520 Bl_stack_post[Bl_stack_len] = 0; 1521 } 1522 break; 1523 case LIST_column: 1524 if (NULL != n->next) { 1525 putchar('\t'); 1526 outflags &= ~MMAN_spc; 1527 } 1528 break; 1529 default: 1530 break; 1531 } 1532 break; 1533 default: 1534 break; 1535 } 1536 } 1537 1538 static void 1539 post_lb(DECL_ARGS) 1540 { 1541 1542 if (SEC_LIBRARY == n->sec) 1543 outflags |= MMAN_br; 1544 } 1545 1546 static int 1547 pre_lk(DECL_ARGS) 1548 { 1549 const struct roff_node *link, *descr, *punct; 1550 int display; 1551 1552 if ((link = n->child) == NULL) 1553 return 0; 1554 1555 /* Find beginning of trailing punctuation. */ 1556 punct = n->last; 1557 while (punct != link && punct->flags & NODE_DELIMC) 1558 punct = punct->prev; 1559 punct = punct->next; 1560 1561 /* Link text. */ 1562 if ((descr = link->next) != NULL && descr != punct) { 1563 font_push('I'); 1564 while (descr != punct) { 1565 print_word(descr->string); 1566 descr = descr->next; 1567 } 1568 font_pop(); 1569 print_word(":"); 1570 } 1571 1572 /* Link target. */ 1573 display = man_strlen(link->string) >= 26; 1574 if (display) { 1575 print_line(".RS", MMAN_Bk_susp); 1576 print_word("6n"); 1577 outflags |= MMAN_nl; 1578 } 1579 font_push('B'); 1580 print_word(link->string); 1581 font_pop(); 1582 1583 /* Trailing punctuation. */ 1584 while (punct != NULL) { 1585 print_word(punct->string); 1586 punct = punct->next; 1587 } 1588 if (display) 1589 print_line(".RE", MMAN_nl); 1590 return 0; 1591 } 1592 1593 static void 1594 pre_onearg(DECL_ARGS) 1595 { 1596 outflags |= MMAN_nl; 1597 print_word("."); 1598 outflags &= ~MMAN_spc; 1599 print_word(roff_name[n->tok]); 1600 if (n->child != NULL) 1601 print_word(n->child->string); 1602 outflags |= MMAN_nl; 1603 if (n->tok == ROFF_ce) 1604 for (n = n->child->next; n != NULL; n = n->next) 1605 print_node(meta, n); 1606 } 1607 1608 static int 1609 pre_li(DECL_ARGS) 1610 { 1611 1612 font_push('R'); 1613 return 1; 1614 } 1615 1616 static int 1617 pre_nm(DECL_ARGS) 1618 { 1619 char *name; 1620 1621 if (n->type == ROFFT_BLOCK) { 1622 outflags |= MMAN_Bk; 1623 pre_syn(n); 1624 } 1625 if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) 1626 return 1; 1627 name = n->child == NULL ? NULL : n->child->string; 1628 if (NULL == name) 1629 return 0; 1630 if (n->type == ROFFT_HEAD) { 1631 if (NULL == n->parent->prev) 1632 outflags |= MMAN_sp; 1633 print_block(".HP", 0); 1634 printf(" %dn", man_strlen(name) + 1); 1635 outflags |= MMAN_nl; 1636 } 1637 font_push('B'); 1638 return 1; 1639 } 1640 1641 static void 1642 post_nm(DECL_ARGS) 1643 { 1644 1645 switch (n->type) { 1646 case ROFFT_BLOCK: 1647 outflags &= ~MMAN_Bk; 1648 break; 1649 case ROFFT_HEAD: 1650 case ROFFT_ELEM: 1651 if (n->child != NULL && n->child->string != NULL) 1652 font_pop(); 1653 break; 1654 default: 1655 break; 1656 } 1657 } 1658 1659 static int 1660 pre_no(DECL_ARGS) 1661 { 1662 1663 outflags |= MMAN_spc_force; 1664 return 1; 1665 } 1666 1667 static int 1668 pre_ns(DECL_ARGS) 1669 { 1670 1671 outflags &= ~MMAN_spc; 1672 return 0; 1673 } 1674 1675 static void 1676 post_pf(DECL_ARGS) 1677 { 1678 1679 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1680 outflags &= ~MMAN_spc; 1681 } 1682 1683 static int 1684 pre_pp(DECL_ARGS) 1685 { 1686 1687 if (MDOC_It != n->parent->tok) 1688 outflags |= MMAN_PP; 1689 outflags |= MMAN_sp | MMAN_nl; 1690 outflags &= ~MMAN_br; 1691 return 0; 1692 } 1693 1694 static int 1695 pre_rs(DECL_ARGS) 1696 { 1697 1698 if (SEC_SEE_ALSO == n->sec) { 1699 outflags |= MMAN_PP | MMAN_sp | MMAN_nl; 1700 outflags &= ~MMAN_br; 1701 } 1702 return 1; 1703 } 1704 1705 static int 1706 pre_skip(DECL_ARGS) 1707 { 1708 1709 return 0; 1710 } 1711 1712 static int 1713 pre_sm(DECL_ARGS) 1714 { 1715 1716 if (NULL == n->child) 1717 outflags ^= MMAN_Sm; 1718 else if (0 == strcmp("on", n->child->string)) 1719 outflags |= MMAN_Sm; 1720 else 1721 outflags &= ~MMAN_Sm; 1722 1723 if (MMAN_Sm & outflags) 1724 outflags |= MMAN_spc; 1725 1726 return 0; 1727 } 1728 1729 static void 1730 pre_sp(DECL_ARGS) 1731 { 1732 if (outflags & MMAN_PP) { 1733 outflags &= ~MMAN_PP; 1734 print_line(".PP", 0); 1735 } else { 1736 print_line(".sp", 0); 1737 if (n->child != NULL) 1738 print_word(n->child->string); 1739 } 1740 outflags |= MMAN_nl; 1741 } 1742 1743 static int 1744 pre_sy(DECL_ARGS) 1745 { 1746 1747 font_push('B'); 1748 return 1; 1749 } 1750 1751 static void 1752 pre_ta(DECL_ARGS) 1753 { 1754 print_line(".ta", 0); 1755 for (n = n->child; n != NULL; n = n->next) 1756 print_word(n->string); 1757 outflags |= MMAN_nl; 1758 } 1759 1760 static int 1761 pre_vt(DECL_ARGS) 1762 { 1763 1764 if (NODE_SYNPRETTY & n->flags) { 1765 switch (n->type) { 1766 case ROFFT_BLOCK: 1767 pre_syn(n); 1768 return 1; 1769 case ROFFT_BODY: 1770 break; 1771 default: 1772 return 0; 1773 } 1774 } 1775 font_push('I'); 1776 return 1; 1777 } 1778 1779 static void 1780 post_vt(DECL_ARGS) 1781 { 1782 1783 if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY) 1784 return; 1785 font_pop(); 1786 } 1787 1788 static int 1789 pre_xr(DECL_ARGS) 1790 { 1791 1792 n = n->child; 1793 if (NULL == n) 1794 return 0; 1795 print_node(meta, n); 1796 n = n->next; 1797 if (NULL == n) 1798 return 0; 1799 outflags &= ~MMAN_spc; 1800 print_word("("); 1801 print_node(meta, n); 1802 print_word(")"); 1803 return 0; 1804 }