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