1 /* $Vendor-Id: mdoc_man.c,v 1.7 2011/10/08 12:47:40 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2011 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 #ifdef HAVE_CONFIG_H 18 #include "config.h" 19 #endif 20 21 #include <stdio.h> 22 #include <string.h> 23 24 #include "mandoc.h" 25 #include "man.h" 26 #include "mdoc.h" 27 #include "main.h" 28 29 #define DECL_ARGS const struct mdoc_meta *m, \ 30 const struct mdoc_node *n, \ 31 struct mman *mm 32 33 struct mman { 34 int need_space; /* next word needs prior ws */ 35 int need_nl; /* next word needs prior nl */ 36 }; 37 38 struct manact { 39 int (*cond)(DECL_ARGS); /* DON'T run actions */ 40 int (*pre)(DECL_ARGS); /* pre-node action */ 41 void (*post)(DECL_ARGS); /* post-node action */ 42 const char *prefix; /* pre-node string constant */ 43 const char *suffix; /* post-node string constant */ 44 }; 45 46 static int cond_body(DECL_ARGS); 47 static int cond_head(DECL_ARGS); 48 static void post_bd(DECL_ARGS); 49 static void post_dl(DECL_ARGS); 50 static void post_enc(DECL_ARGS); 51 static void post_nm(DECL_ARGS); 52 static void post_percent(DECL_ARGS); 53 static void post_pf(DECL_ARGS); 54 static void post_sect(DECL_ARGS); 55 static void post_sp(DECL_ARGS); 56 static int pre_ap(DECL_ARGS); 57 static int pre_bd(DECL_ARGS); 58 static int pre_br(DECL_ARGS); 59 static int pre_dl(DECL_ARGS); 60 static int pre_enc(DECL_ARGS); 61 static int pre_it(DECL_ARGS); 62 static int pre_nm(DECL_ARGS); 63 static int pre_ns(DECL_ARGS); 64 static int pre_pp(DECL_ARGS); 65 static int pre_sp(DECL_ARGS); 66 static int pre_sect(DECL_ARGS); 67 static int pre_xr(DECL_ARGS); 68 static void print_word(struct mman *, const char *); 69 static void print_node(DECL_ARGS); 70 71 static const struct manact manacts[MDOC_MAX + 1] = { 72 { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */ 73 { NULL, NULL, NULL, NULL, NULL }, /* Dd */ 74 { NULL, NULL, NULL, NULL, NULL }, /* Dt */ 75 { NULL, NULL, NULL, NULL, NULL }, /* _Os */ 76 { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */ 77 { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */ 78 { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */ 79 { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */ 80 { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */ 81 { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */ 82 { NULL, NULL, NULL, NULL, NULL }, /* Ed */ 83 { NULL, NULL, NULL, NULL, NULL }, /* Bl */ 84 { NULL, NULL, NULL, NULL, NULL }, /* El */ 85 { NULL, pre_it, NULL, NULL, NULL }, /* _It */ 86 { NULL, NULL, NULL, NULL, NULL }, /* _Ad */ 87 { NULL, NULL, NULL, NULL, NULL }, /* _An */ 88 { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ar */ 89 { NULL, NULL, NULL, NULL, NULL }, /* _Cd */ 90 { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cm */ 91 { NULL, NULL, NULL, NULL, NULL }, /* _Dv */ 92 { NULL, NULL, NULL, NULL, NULL }, /* _Er */ 93 { NULL, NULL, NULL, NULL, NULL }, /* _Ev */ 94 { NULL, pre_enc, post_enc, "The \\fB", 95 "\\fP\nutility exits 0 on success, and >0 if an error occurs." 96 }, /* Ex */ 97 { NULL, NULL, NULL, NULL, NULL }, /* _Fa */ 98 { NULL, NULL, NULL, NULL, NULL }, /* _Fd */ 99 { NULL, pre_enc, post_enc, "\\fB-", "\\fP" }, /* Fl */ 100 { NULL, NULL, NULL, NULL, NULL }, /* _Fn */ 101 { NULL, NULL, NULL, NULL, NULL }, /* _Ft */ 102 { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ic */ 103 { NULL, NULL, NULL, NULL, NULL }, /* _In */ 104 { NULL, NULL, NULL, NULL, NULL }, /* _Li */ 105 { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */ 106 { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */ 107 { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */ 108 { NULL, NULL, NULL, NULL, NULL }, /* _Ot */ 109 { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* _Pa */ 110 { NULL, pre_enc, post_enc, "The \\fB", 111 "\\fP\nfunction returns the value 0 if successful;\n" 112 "otherwise the value -1 is returned and the global\n" 113 "variable \\fIerrno\\fP is set to indicate the error." 114 }, /* Rv */ 115 { NULL, NULL, NULL, NULL, NULL }, /* _St */ 116 { NULL, NULL, NULL, NULL, NULL }, /* _Va */ 117 { NULL, NULL, NULL, NULL, NULL }, /* _Vt */ 118 { NULL, pre_xr, NULL, NULL, NULL }, /* _Xr */ 119 { NULL, NULL, post_percent, NULL, NULL }, /* _%A */ 120 { NULL, NULL, NULL, NULL, NULL }, /* _%B */ 121 { NULL, NULL, post_percent, NULL, NULL }, /* _%D */ 122 { NULL, NULL, NULL, NULL, NULL }, /* _%I */ 123 { NULL, pre_enc, post_percent, "\\fI", "\\fP" }, /* %J */ 124 { NULL, NULL, NULL, NULL, NULL }, /* _%N */ 125 { NULL, NULL, NULL, NULL, NULL }, /* _%O */ 126 { NULL, NULL, NULL, NULL, NULL }, /* _%P */ 127 { NULL, NULL, NULL, NULL, NULL }, /* _%R */ 128 { NULL, pre_enc, post_percent, "\"", "\"" }, /* %T */ 129 { NULL, NULL, NULL, NULL, NULL }, /* _%V */ 130 { NULL, NULL, NULL, NULL, NULL }, /* _Ac */ 131 { NULL, NULL, NULL, NULL, NULL }, /* _Ao */ 132 { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */ 133 { NULL, NULL, NULL, NULL, NULL }, /* _At */ 134 { NULL, NULL, NULL, NULL, NULL }, /* Bc */ 135 { NULL, NULL, NULL, NULL, NULL }, /* _Bf */ 136 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */ 137 { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */ 138 { NULL, NULL, NULL, NULL, NULL }, /* _Bsx */ 139 { NULL, NULL, NULL, NULL, NULL }, /* _Bx */ 140 { NULL, NULL, NULL, NULL, NULL }, /* _Db */ 141 { NULL, NULL, NULL, NULL, NULL }, /* _Dc */ 142 { NULL, NULL, NULL, NULL, NULL }, /* _Do */ 143 { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */ 144 { NULL, NULL, NULL, NULL, NULL }, /* _Ec */ 145 { NULL, NULL, NULL, NULL, NULL }, /* _Ef */ 146 { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Em */ 147 { NULL, NULL, NULL, NULL, NULL }, /* _Eo */ 148 { NULL, NULL, NULL, NULL, NULL }, /* _Fx */ 149 { NULL, NULL, NULL, NULL, NULL }, /* _Ms */ 150 { NULL, NULL, NULL, NULL, NULL }, /* _No */ 151 { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */ 152 { NULL, NULL, NULL, NULL, NULL }, /* _Nx */ 153 { NULL, NULL, NULL, NULL, NULL }, /* _Ox */ 154 { NULL, NULL, NULL, NULL, NULL }, /* Pc */ 155 { NULL, NULL, post_pf, NULL, NULL }, /* Pf */ 156 { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */ 157 { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */ 158 { NULL, NULL, NULL, NULL, NULL }, /* _Qc */ 159 { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */ 160 { NULL, NULL, NULL, NULL, NULL }, /* _Qo */ 161 { NULL, NULL, NULL, NULL, NULL }, /* _Qq */ 162 { NULL, NULL, NULL, NULL, NULL }, /* _Re */ 163 { cond_body, pre_pp, NULL, NULL, NULL }, /* Rs */ 164 { NULL, NULL, NULL, NULL, NULL }, /* _Sc */ 165 { NULL, NULL, NULL, NULL, NULL }, /* _So */ 166 { cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */ 167 { NULL, NULL, NULL, NULL, NULL }, /* _Sm */ 168 { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Sx */ 169 { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Sy */ 170 { NULL, NULL, NULL, NULL, NULL }, /* _Tn */ 171 { NULL, NULL, NULL, NULL, NULL }, /* _Ux */ 172 { NULL, NULL, NULL, NULL, NULL }, /* _Xc */ 173 { NULL, NULL, NULL, NULL, NULL }, /* _Xo */ 174 { NULL, NULL, NULL, NULL, NULL }, /* _Fo */ 175 { NULL, NULL, NULL, NULL, NULL }, /* _Fc */ 176 { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */ 177 { NULL, NULL, NULL, NULL, NULL }, /* _Oc */ 178 { NULL, NULL, NULL, NULL, NULL }, /* _Bk */ 179 { NULL, NULL, NULL, NULL, NULL }, /* _Ek */ 180 { NULL, NULL, NULL, NULL, NULL }, /* _Bt */ 181 { NULL, NULL, NULL, NULL, NULL }, /* _Hf */ 182 { NULL, NULL, NULL, NULL, NULL }, /* _Fr */ 183 { NULL, NULL, NULL, NULL, NULL }, /* _Ud */ 184 { NULL, NULL, NULL, NULL, NULL }, /* _Lb */ 185 { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */ 186 { NULL, NULL, NULL, NULL, NULL }, /* _Lk */ 187 { NULL, NULL, NULL, NULL, NULL }, /* _Mt */ 188 { NULL, NULL, NULL, NULL, NULL }, /* _Brq */ 189 { NULL, NULL, NULL, NULL, NULL }, /* _Bro */ 190 { NULL, NULL, NULL, NULL, NULL }, /* _Brc */ 191 { NULL, NULL, NULL, NULL, NULL }, /* _%C */ 192 { NULL, NULL, NULL, NULL, NULL }, /* _Es */ 193 { NULL, NULL, NULL, NULL, NULL }, /* _En */ 194 { NULL, NULL, NULL, NULL, NULL }, /* _Dx */ 195 { NULL, NULL, NULL, NULL, NULL }, /* _%Q */ 196 { NULL, pre_br, NULL, NULL, NULL }, /* br */ 197 { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */ 198 { NULL, NULL, NULL, NULL, NULL }, /* _%U */ 199 { NULL, NULL, NULL, NULL, NULL }, /* _Ta */ 200 { NULL, NULL, NULL, NULL, NULL }, /* ROOT */ 201 }; 202 203 static void 204 print_word(struct mman *mm, const char *s) 205 { 206 207 if (mm->need_nl) { 208 /* 209 * If we need a newline, print it now and start afresh. 210 */ 211 putchar('\n'); 212 mm->need_space = 0; 213 mm->need_nl = 0; 214 } else if (mm->need_space && '\0' != s[0]) 215 /* 216 * If we need a space, only print it before 217 * (1) a nonzero length word; 218 * (2) a word that is non-punctuation; and 219 * (3) if punctuation, non-terminating puncutation. 220 */ 221 if (NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) 222 putchar(' '); 223 224 /* 225 * Reassign needing space if we're not following opening 226 * punctuation. 227 */ 228 mm->need_space = 229 ('(' != s[0] && '[' != s[0]) || '\0' != s[1]; 230 231 for ( ; *s; s++) { 232 switch (*s) { 233 case (ASCII_NBRSP): 234 printf("\\~"); 235 break; 236 case (ASCII_HYPH): 237 putchar('-'); 238 break; 239 default: 240 putchar((unsigned char)*s); 241 break; 242 } 243 } 244 } 245 246 void 247 man_man(void *arg, const struct man *man) 248 { 249 250 /* 251 * Dump the keep buffer. 252 * We're guaranteed by now that this exists (is non-NULL). 253 * Flush stdout afterward, just in case. 254 */ 255 fputs(mparse_getkeep(man_mparse(man)), stdout); 256 fflush(stdout); 257 } 258 259 void 260 man_mdoc(void *arg, const struct mdoc *mdoc) 261 { 262 const struct mdoc_meta *m; 263 const struct mdoc_node *n; 264 struct mman mm; 265 266 m = mdoc_meta(mdoc); 267 n = mdoc_node(mdoc); 268 269 printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"", 270 m->title, m->msec, m->date, m->os, m->vol); 271 272 memset(&mm, 0, sizeof(struct mman)); 273 274 mm.need_nl = 1; 275 print_node(m, n, &mm); 276 putchar('\n'); 277 } 278 279 static void 280 print_node(DECL_ARGS) 281 { 282 const struct mdoc_node *prev, *sub; 283 const struct manact *act; 284 int cond, do_sub; 285 286 /* 287 * Break the line if we were parsed subsequent the current node. 288 * This makes the page structure be more consistent. 289 */ 290 prev = n->prev ? n->prev : n->parent; 291 if (prev && prev->line < n->line) 292 mm->need_nl = 1; 293 294 act = NULL; 295 cond = 0; 296 do_sub = 1; 297 298 if (MDOC_TEXT == n->type) { 299 /* 300 * Make sure that we don't happen to start with a 301 * control character at the start of a line. 302 */ 303 if (mm->need_nl && ('.' == *n->string || 304 '\'' == *n->string)) { 305 print_word(mm, "\\&"); 306 mm->need_space = 0; 307 } 308 print_word(mm, n->string); 309 } else { 310 /* 311 * Conditionally run the pre-node action handler for a 312 * node. 313 */ 314 act = manacts + n->tok; 315 cond = NULL == act->cond || (*act->cond)(m, n, mm); 316 if (cond && act->pre) 317 do_sub = (*act->pre)(m, n, mm); 318 } 319 320 /* 321 * Conditionally run all child nodes. 322 * Note that this iterates over children instead of using 323 * recursion. This prevents unnecessary depth in the stack. 324 */ 325 if (do_sub) 326 for (sub = n->child; sub; sub = sub->next) 327 print_node(m, sub, mm); 328 329 /* 330 * Lastly, conditionally run the post-node handler. 331 */ 332 if (cond && act->post) 333 (*act->post)(m, n, mm); 334 } 335 336 static int 337 cond_head(DECL_ARGS) 338 { 339 340 return(MDOC_HEAD == n->type); 341 } 342 343 static int 344 cond_body(DECL_ARGS) 345 { 346 347 return(MDOC_BODY == n->type); 348 } 349 350 /* 351 * Output a font encoding before a node, e.g., \fR. 352 * This obviously has no trailing space. 353 */ 354 static int 355 pre_enc(DECL_ARGS) 356 { 357 const char *prefix; 358 359 prefix = manacts[n->tok].prefix; 360 if (NULL == prefix) 361 return(1); 362 print_word(mm, prefix); 363 mm->need_space = 0; 364 return(1); 365 } 366 367 /* 368 * Output a font encoding subsequent a node, e.g., \fP. 369 */ 370 static void 371 post_enc(DECL_ARGS) 372 { 373 const char *suffix; 374 375 suffix = manacts[n->tok].suffix; 376 if (NULL == suffix) 377 return; 378 mm->need_space = 0; 379 print_word(mm, suffix); 380 } 381 382 /* 383 * Used in listings (percent = %A, e.g.). 384 * FIXME: this is incomplete. 385 * It doesn't print a nice ", and" for lists. 386 */ 387 static void 388 post_percent(DECL_ARGS) 389 { 390 391 post_enc(m, n, mm); 392 if (n->next) 393 print_word(mm, ","); 394 else { 395 print_word(mm, "."); 396 mm->need_nl = 1; 397 } 398 } 399 400 /* 401 * Print before a section header. 402 */ 403 static int 404 pre_sect(DECL_ARGS) 405 { 406 407 if (MDOC_HEAD != n->type) 408 return(1); 409 mm->need_nl = 1; 410 print_word(mm, manacts[n->tok].prefix); 411 print_word(mm, "\""); 412 mm->need_space = 0; 413 return(1); 414 } 415 416 /* 417 * Print subsequent a section header. 418 */ 419 static void 420 post_sect(DECL_ARGS) 421 { 422 423 if (MDOC_HEAD != n->type) 424 return; 425 mm->need_space = 0; 426 print_word(mm, "\""); 427 mm->need_nl = 1; 428 } 429 430 static int 431 pre_ap(DECL_ARGS) 432 { 433 434 mm->need_space = 0; 435 print_word(mm, "'"); 436 mm->need_space = 0; 437 return(0); 438 } 439 440 static int 441 pre_bd(DECL_ARGS) 442 { 443 444 if (DISP_unfilled == n->norm->Bd.type || 445 DISP_literal == n->norm->Bd.type) { 446 mm->need_nl = 1; 447 print_word(mm, ".nf"); 448 } 449 mm->need_nl = 1; 450 return(1); 451 } 452 453 static void 454 post_bd(DECL_ARGS) 455 { 456 457 if (DISP_unfilled == n->norm->Bd.type || 458 DISP_literal == n->norm->Bd.type) { 459 mm->need_nl = 1; 460 print_word(mm, ".fi"); 461 } 462 mm->need_nl = 1; 463 } 464 465 static int 466 pre_br(DECL_ARGS) 467 { 468 469 mm->need_nl = 1; 470 print_word(mm, ".br"); 471 mm->need_nl = 1; 472 return(0); 473 } 474 475 static int 476 pre_dl(DECL_ARGS) 477 { 478 479 mm->need_nl = 1; 480 print_word(mm, ".RS 6n"); 481 mm->need_nl = 1; 482 return(1); 483 } 484 485 static void 486 post_dl(DECL_ARGS) 487 { 488 489 mm->need_nl = 1; 490 print_word(mm, ".RE"); 491 mm->need_nl = 1; 492 } 493 494 static int 495 pre_it(DECL_ARGS) 496 { 497 const struct mdoc_node *bln; 498 499 if (MDOC_HEAD == n->type) { 500 mm->need_nl = 1; 501 print_word(mm, ".TP"); 502 bln = n->parent->parent->prev; 503 switch (bln->norm->Bl.type) { 504 case (LIST_bullet): 505 print_word(mm, "4n"); 506 mm->need_nl = 1; 507 print_word(mm, "\\fBo\\fP"); 508 break; 509 default: 510 if (bln->norm->Bl.width) 511 print_word(mm, bln->norm->Bl.width); 512 break; 513 } 514 mm->need_nl = 1; 515 } 516 return(1); 517 } 518 519 static int 520 pre_nm(DECL_ARGS) 521 { 522 523 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 524 return(1); 525 print_word(mm, "\\fB"); 526 mm->need_space = 0; 527 if (NULL == n->child) 528 print_word(mm, m->name); 529 return(1); 530 } 531 532 static void 533 post_nm(DECL_ARGS) 534 { 535 536 if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) 537 return; 538 mm->need_space = 0; 539 print_word(mm, "\\fP"); 540 } 541 542 static int 543 pre_ns(DECL_ARGS) 544 { 545 546 mm->need_space = 0; 547 return(0); 548 } 549 550 static void 551 post_pf(DECL_ARGS) 552 { 553 554 mm->need_space = 0; 555 } 556 557 static int 558 pre_pp(DECL_ARGS) 559 { 560 561 mm->need_nl = 1; 562 if (MDOC_It == n->parent->tok) 563 print_word(mm, ".sp"); 564 else 565 print_word(mm, ".PP"); 566 mm->need_nl = 1; 567 return(1); 568 } 569 570 static int 571 pre_sp(DECL_ARGS) 572 { 573 574 mm->need_nl = 1; 575 print_word(mm, ".sp"); 576 return(1); 577 } 578 579 static void 580 post_sp(DECL_ARGS) 581 { 582 583 mm->need_nl = 1; 584 } 585 586 static int 587 pre_xr(DECL_ARGS) 588 { 589 590 n = n->child; 591 if (NULL == n) 592 return(0); 593 print_node(m, n, mm); 594 n = n->next; 595 if (NULL == n) 596 return(0); 597 mm->need_space = 0; 598 print_word(mm, "("); 599 print_node(m, n, mm); 600 print_word(mm, ")"); 601 return(0); 602 } 603