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