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