1 /* $Vendor-Id: man_html.c,v 1.30 2010/03/24 20:10:53 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 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 <sys/types.h> 22 23 #include <assert.h> 24 #include <ctype.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 29 #include "out.h" 30 #include "html.h" 31 #include "man.h" 32 #include "main.h" 33 34 /* TODO: preserve ident widths. */ 35 /* FIXME: have PD set the default vspace width. */ 36 37 #define INDENT 5 38 #define HALFINDENT 3 39 40 #define MAN_ARGS const struct man_meta *m, \ 41 const struct man_node *n, \ 42 struct html *h 43 44 struct htmlman { 45 int (*pre)(MAN_ARGS); 46 int (*post)(MAN_ARGS); 47 }; 48 49 static void print_man(MAN_ARGS); 50 static void print_man_head(MAN_ARGS); 51 static void print_man_nodelist(MAN_ARGS); 52 static void print_man_node(MAN_ARGS); 53 54 static int a2width(const struct man_node *, 55 struct roffsu *); 56 57 static int man_alt_pre(MAN_ARGS); 58 static int man_br_pre(MAN_ARGS); 59 static int man_ign_pre(MAN_ARGS); 60 static void man_root_post(MAN_ARGS); 61 static int man_root_pre(MAN_ARGS); 62 static int man_B_pre(MAN_ARGS); 63 static int man_HP_pre(MAN_ARGS); 64 static int man_I_pre(MAN_ARGS); 65 static int man_IP_pre(MAN_ARGS); 66 static int man_PP_pre(MAN_ARGS); 67 static int man_RS_pre(MAN_ARGS); 68 static int man_SB_pre(MAN_ARGS); 69 static int man_SH_pre(MAN_ARGS); 70 static int man_SM_pre(MAN_ARGS); 71 static int man_SS_pre(MAN_ARGS); 72 73 static const struct htmlman mans[MAN_MAX] = { 74 { man_br_pre, NULL }, /* br */ 75 { NULL, NULL }, /* TH */ 76 { man_SH_pre, NULL }, /* SH */ 77 { man_SS_pre, NULL }, /* SS */ 78 { man_IP_pre, NULL }, /* TP */ 79 { man_PP_pre, NULL }, /* LP */ 80 { man_PP_pre, NULL }, /* PP */ 81 { man_PP_pre, NULL }, /* P */ 82 { man_IP_pre, NULL }, /* IP */ 83 { man_HP_pre, NULL }, /* HP */ 84 { man_SM_pre, NULL }, /* SM */ 85 { man_SB_pre, NULL }, /* SB */ 86 { man_alt_pre, NULL }, /* BI */ 87 { man_alt_pre, NULL }, /* IB */ 88 { man_alt_pre, NULL }, /* BR */ 89 { man_alt_pre, NULL }, /* RB */ 90 { NULL, NULL }, /* R */ 91 { man_B_pre, NULL }, /* B */ 92 { man_I_pre, NULL }, /* I */ 93 { man_alt_pre, NULL }, /* IR */ 94 { man_alt_pre, NULL }, /* RI */ 95 { NULL, NULL }, /* na */ 96 { NULL, NULL }, /* i */ 97 { man_br_pre, NULL }, /* sp */ 98 { NULL, NULL }, /* nf */ 99 { NULL, NULL }, /* fi */ 100 { NULL, NULL }, /* r */ 101 { NULL, NULL }, /* RE */ 102 { man_RS_pre, NULL }, /* RS */ 103 { man_ign_pre, NULL }, /* DT */ 104 { man_ign_pre, NULL }, /* UC */ 105 { man_ign_pre, NULL }, /* PD */ 106 { man_br_pre, NULL }, /* Sp */ 107 { man_ign_pre, NULL }, /* Vb */ 108 { NULL, NULL }, /* Ve */ 109 { man_ign_pre, NULL }, /* de */ 110 { man_ign_pre, NULL }, /* dei */ 111 { man_ign_pre, NULL }, /* am */ 112 { man_ign_pre, NULL }, /* ami */ 113 { man_ign_pre, NULL }, /* ig */ 114 { NULL, NULL }, /* . */ 115 }; 116 117 118 void 119 html_man(void *arg, const struct man *m) 120 { 121 struct html *h; 122 struct tag *t; 123 124 h = (struct html *)arg; 125 126 print_gen_decls(h); 127 128 t = print_otag(h, TAG_HTML, 0, NULL); 129 print_man(man_meta(m), man_node(m), h); 130 print_tagq(h, t); 131 132 printf("\n"); 133 } 134 135 136 static void 137 print_man(MAN_ARGS) 138 { 139 struct tag *t; 140 struct htmlpair tag; 141 142 t = print_otag(h, TAG_HEAD, 0, NULL); 143 144 print_man_head(m, n, h); 145 print_tagq(h, t); 146 t = print_otag(h, TAG_BODY, 0, NULL); 147 148 tag.key = ATTR_CLASS; 149 tag.val = "body"; 150 print_otag(h, TAG_DIV, 1, &tag); 151 152 print_man_nodelist(m, n, h); 153 154 print_tagq(h, t); 155 } 156 157 158 /* ARGSUSED */ 159 static void 160 print_man_head(MAN_ARGS) 161 { 162 163 print_gen_head(h); 164 bufinit(h); 165 buffmt(h, "%s(%d)", m->title, m->msec); 166 167 print_otag(h, TAG_TITLE, 0, NULL); 168 print_text(h, h->buf); 169 } 170 171 172 static void 173 print_man_nodelist(MAN_ARGS) 174 { 175 176 print_man_node(m, n, h); 177 if (n->next) 178 print_man_nodelist(m, n->next, h); 179 } 180 181 182 static void 183 print_man_node(MAN_ARGS) 184 { 185 int child; 186 struct tag *t; 187 188 child = 1; 189 t = h->tags.head; 190 191 bufinit(h); 192 193 /* 194 * FIXME: embedded elements within next-line scopes (e.g., `br' 195 * within an empty `B') will cause formatting to be forgotten 196 * due to scope closing out. 197 */ 198 199 switch (n->type) { 200 case (MAN_ROOT): 201 child = man_root_pre(m, n, h); 202 break; 203 case (MAN_TEXT): 204 print_text(h, n->string); 205 return; 206 default: 207 /* 208 * Close out scope of font prior to opening a macro 209 * scope. Assert that the metafont is on the top of the 210 * stack (it's never nested). 211 */ 212 if (h->metaf) { 213 assert(h->metaf == t); 214 print_tagq(h, h->metaf); 215 assert(NULL == h->metaf); 216 t = h->tags.head; 217 } 218 if (mans[n->tok].pre) 219 child = (*mans[n->tok].pre)(m, n, h); 220 break; 221 } 222 223 if (child && n->child) 224 print_man_nodelist(m, n->child, h); 225 226 /* This will automatically close out any font scope. */ 227 print_stagq(h, t); 228 229 bufinit(h); 230 231 switch (n->type) { 232 case (MAN_ROOT): 233 man_root_post(m, n, h); 234 break; 235 case (MAN_TEXT): 236 break; 237 default: 238 if (mans[n->tok].post) 239 (*mans[n->tok].post)(m, n, h); 240 break; 241 } 242 } 243 244 245 static int 246 a2width(const struct man_node *n, struct roffsu *su) 247 { 248 249 if (MAN_TEXT != n->type) 250 return(0); 251 if (a2roffsu(n->string, su, SCALE_BU)) 252 return(1); 253 254 return(0); 255 } 256 257 258 /* ARGSUSED */ 259 static int 260 man_root_pre(MAN_ARGS) 261 { 262 struct htmlpair tag[3]; 263 struct tag *t, *tt; 264 char b[BUFSIZ], title[BUFSIZ]; 265 266 b[0] = 0; 267 if (m->vol) 268 (void)strlcat(b, m->vol, BUFSIZ); 269 270 snprintf(title, BUFSIZ - 1, "%s(%d)", m->title, m->msec); 271 272 PAIR_CLASS_INIT(&tag[0], "header"); 273 bufcat_style(h, "width", "100%"); 274 PAIR_STYLE_INIT(&tag[1], h); 275 PAIR_SUMMARY_INIT(&tag[2], "header"); 276 277 t = print_otag(h, TAG_TABLE, 3, tag); 278 tt = print_otag(h, TAG_TR, 0, NULL); 279 280 bufinit(h); 281 bufcat_style(h, "width", "10%"); 282 PAIR_STYLE_INIT(&tag[0], h); 283 print_otag(h, TAG_TD, 1, tag); 284 print_text(h, title); 285 print_stagq(h, tt); 286 287 bufinit(h); 288 bufcat_style(h, "width", "80%"); 289 bufcat_style(h, "white-space", "nowrap"); 290 bufcat_style(h, "text-align", "center"); 291 PAIR_STYLE_INIT(&tag[0], h); 292 print_otag(h, TAG_TD, 1, tag); 293 print_text(h, b); 294 print_stagq(h, tt); 295 296 bufinit(h); 297 bufcat_style(h, "width", "10%"); 298 bufcat_style(h, "text-align", "right"); 299 PAIR_STYLE_INIT(&tag[0], h); 300 print_otag(h, TAG_TD, 1, tag); 301 print_text(h, title); 302 print_tagq(h, t); 303 return(1); 304 } 305 306 307 /* ARGSUSED */ 308 static void 309 man_root_post(MAN_ARGS) 310 { 311 struct htmlpair tag[3]; 312 struct tag *t, *tt; 313 char b[DATESIZ]; 314 315 time2a(m->date, b, DATESIZ); 316 317 PAIR_CLASS_INIT(&tag[0], "footer"); 318 bufcat_style(h, "width", "100%"); 319 PAIR_STYLE_INIT(&tag[1], h); 320 PAIR_SUMMARY_INIT(&tag[2], "footer"); 321 322 t = print_otag(h, TAG_TABLE, 3, tag); 323 tt = print_otag(h, TAG_TR, 0, NULL); 324 325 bufinit(h); 326 bufcat_style(h, "width", "50%"); 327 PAIR_STYLE_INIT(&tag[0], h); 328 print_otag(h, TAG_TD, 1, tag); 329 print_text(h, b); 330 print_stagq(h, tt); 331 332 bufinit(h); 333 bufcat_style(h, "width", "50%"); 334 bufcat_style(h, "text-align", "right"); 335 PAIR_STYLE_INIT(&tag[0], h); 336 print_otag(h, TAG_TD, 1, tag); 337 if (m->source) 338 print_text(h, m->source); 339 print_tagq(h, t); 340 } 341 342 343 344 /* ARGSUSED */ 345 static int 346 man_br_pre(MAN_ARGS) 347 { 348 struct roffsu su; 349 struct htmlpair tag; 350 351 SCALE_VS_INIT(&su, 1); 352 353 switch (n->tok) { 354 case (MAN_Sp): 355 SCALE_VS_INIT(&su, 0.5); 356 break; 357 case (MAN_sp): 358 if (n->child) 359 a2roffsu(n->child->string, &su, SCALE_VS); 360 break; 361 default: 362 su.scale = 0; 363 break; 364 } 365 366 bufcat_su(h, "height", &su); 367 PAIR_STYLE_INIT(&tag, h); 368 print_otag(h, TAG_DIV, 1, &tag); 369 370 /* So the div isn't empty: */ 371 print_text(h, "\\~"); 372 373 return(0); 374 } 375 376 377 /* ARGSUSED */ 378 static int 379 man_SH_pre(MAN_ARGS) 380 { 381 struct htmlpair tag[2]; 382 struct roffsu su; 383 384 if (MAN_BODY == n->type) { 385 SCALE_HS_INIT(&su, INDENT); 386 bufcat_su(h, "margin-left", &su); 387 PAIR_CLASS_INIT(&tag[0], "sec-body"); 388 PAIR_STYLE_INIT(&tag[1], h); 389 print_otag(h, TAG_DIV, 2, tag); 390 return(1); 391 } else if (MAN_BLOCK == n->type) { 392 PAIR_CLASS_INIT(&tag[0], "sec-block"); 393 if (n->prev && MAN_SH == n->prev->tok) 394 if (NULL == n->prev->body->child) { 395 print_otag(h, TAG_DIV, 1, tag); 396 return(1); 397 } 398 399 SCALE_VS_INIT(&su, 1); 400 bufcat_su(h, "margin-top", &su); 401 if (NULL == n->next) 402 bufcat_su(h, "margin-bottom", &su); 403 PAIR_STYLE_INIT(&tag[1], h); 404 print_otag(h, TAG_DIV, 2, tag); 405 return(1); 406 } 407 408 PAIR_CLASS_INIT(&tag[0], "sec-head"); 409 print_otag(h, TAG_DIV, 1, tag); 410 return(1); 411 } 412 413 414 /* ARGSUSED */ 415 static int 416 man_alt_pre(MAN_ARGS) 417 { 418 const struct man_node *nn; 419 struct tag *t; 420 int i; 421 enum htmlfont fp; 422 423 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 424 switch (n->tok) { 425 case (MAN_BI): 426 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD; 427 break; 428 case (MAN_IB): 429 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC; 430 break; 431 case (MAN_RI): 432 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE; 433 break; 434 case (MAN_IR): 435 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC; 436 break; 437 case (MAN_BR): 438 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD; 439 break; 440 case (MAN_RB): 441 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE; 442 break; 443 default: 444 abort(); 445 /* NOTREACHED */ 446 } 447 448 if (i) 449 h->flags |= HTML_NOSPACE; 450 451 /* 452 * Open and close the scope with each argument, so that 453 * internal \f escapes, which are common, are also 454 * closed out with the scope. 455 */ 456 t = print_ofont(h, fp); 457 print_man_node(m, nn, h); 458 print_tagq(h, t); 459 } 460 461 return(0); 462 } 463 464 465 /* ARGSUSED */ 466 static int 467 man_SB_pre(MAN_ARGS) 468 { 469 struct htmlpair tag; 470 471 /* FIXME: print_ofont(). */ 472 PAIR_CLASS_INIT(&tag, "small bold"); 473 print_otag(h, TAG_SPAN, 1, &tag); 474 return(1); 475 } 476 477 478 /* ARGSUSED */ 479 static int 480 man_SM_pre(MAN_ARGS) 481 { 482 struct htmlpair tag; 483 484 PAIR_CLASS_INIT(&tag, "small"); 485 print_otag(h, TAG_SPAN, 1, &tag); 486 return(1); 487 } 488 489 490 /* ARGSUSED */ 491 static int 492 man_SS_pre(MAN_ARGS) 493 { 494 struct htmlpair tag[3]; 495 struct roffsu su; 496 497 SCALE_VS_INIT(&su, 1); 498 499 if (MAN_BODY == n->type) { 500 PAIR_CLASS_INIT(&tag[0], "ssec-body"); 501 if (n->parent->next && n->child) { 502 bufcat_su(h, "margin-bottom", &su); 503 PAIR_STYLE_INIT(&tag[1], h); 504 print_otag(h, TAG_DIV, 2, tag); 505 return(1); 506 } 507 508 print_otag(h, TAG_DIV, 1, tag); 509 return(1); 510 } else if (MAN_BLOCK == n->type) { 511 PAIR_CLASS_INIT(&tag[0], "ssec-block"); 512 if (n->prev && MAN_SS == n->prev->tok) 513 if (n->prev->body->child) { 514 bufcat_su(h, "margin-top", &su); 515 PAIR_STYLE_INIT(&tag[1], h); 516 print_otag(h, TAG_DIV, 2, tag); 517 return(1); 518 } 519 520 print_otag(h, TAG_DIV, 1, tag); 521 return(1); 522 } 523 524 SCALE_HS_INIT(&su, INDENT - HALFINDENT); 525 bufcat_su(h, "margin-left", &su); 526 PAIR_CLASS_INIT(&tag[0], "ssec-head"); 527 PAIR_STYLE_INIT(&tag[1], h); 528 print_otag(h, TAG_DIV, 2, tag); 529 return(1); 530 } 531 532 533 /* ARGSUSED */ 534 static int 535 man_PP_pre(MAN_ARGS) 536 { 537 struct htmlpair tag; 538 struct roffsu su; 539 int i; 540 541 if (MAN_BLOCK != n->type) 542 return(1); 543 544 i = 0; 545 546 if (MAN_ROOT == n->parent->type) { 547 SCALE_HS_INIT(&su, INDENT); 548 bufcat_su(h, "margin-left", &su); 549 i = 1; 550 } 551 if (n->prev) { 552 SCALE_VS_INIT(&su, 1); 553 bufcat_su(h, "margin-top", &su); 554 i = 1; 555 } 556 557 PAIR_STYLE_INIT(&tag, h); 558 print_otag(h, TAG_DIV, i, &tag); 559 return(1); 560 } 561 562 563 /* ARGSUSED */ 564 static int 565 man_IP_pre(MAN_ARGS) 566 { 567 struct roffsu su; 568 struct htmlpair tag; 569 const struct man_node *nn; 570 int width; 571 572 /* 573 * This scattering of 1-BU margins and pads is to make sure that 574 * when text overruns its box, the subsequent text isn't flush 575 * up against it. However, the rest of the right-hand box must 576 * also be adjusted in consideration of this 1-BU space. 577 */ 578 579 if (MAN_BODY == n->type) { 580 SCALE_HS_INIT(&su, INDENT); 581 bufcat_su(h, "margin-left", &su); 582 PAIR_STYLE_INIT(&tag, h); 583 print_otag(h, TAG_DIV, 1, &tag); 584 return(1); 585 } 586 587 nn = MAN_BLOCK == n->type ? 588 n->head->child : n->parent->head->child; 589 590 SCALE_HS_INIT(&su, INDENT); 591 width = 0; 592 593 /* Width is the last token. */ 594 595 if (MAN_IP == n->tok && NULL != nn) 596 if (NULL != (nn = nn->next)) { 597 for ( ; nn->next; nn = nn->next) 598 /* Do nothing. */ ; 599 width = a2width(nn, &su); 600 } 601 602 /* Width is the first token. */ 603 604 if (MAN_TP == n->tok && NULL != nn) { 605 /* Skip past non-text children. */ 606 while (nn && MAN_TEXT != nn->type) 607 nn = nn->next; 608 if (nn) 609 width = a2width(nn, &su); 610 } 611 612 if (MAN_BLOCK == n->type) { 613 bufcat_su(h, "margin-left", &su); 614 SCALE_VS_INIT(&su, 1); 615 bufcat_su(h, "margin-top", &su); 616 bufcat_style(h, "clear", "both"); 617 PAIR_STYLE_INIT(&tag, h); 618 print_otag(h, TAG_DIV, 1, &tag); 619 return(1); 620 } 621 622 bufcat_su(h, "min-width", &su); 623 SCALE_INVERT(&su); 624 bufcat_su(h, "margin-left", &su); 625 SCALE_HS_INIT(&su, 1); 626 bufcat_su(h, "margin-right", &su); 627 bufcat_style(h, "clear", "left"); 628 629 if (n->next && n->next->child) 630 bufcat_style(h, "float", "left"); 631 632 PAIR_STYLE_INIT(&tag, h); 633 print_otag(h, TAG_DIV, 1, &tag); 634 635 /* 636 * Without a length string, we can print all of our children. 637 */ 638 639 if ( ! width) 640 return(1); 641 642 /* 643 * When a length has been specified, we need to carefully print 644 * our child context: IP gets all children printed but the last 645 * (the width), while TP gets all children printed but the first 646 * (the width). 647 */ 648 649 if (MAN_IP == n->tok) 650 for (nn = n->child; nn->next; nn = nn->next) 651 print_man_node(m, nn, h); 652 if (MAN_TP == n->tok) 653 for (nn = n->child->next; nn; nn = nn->next) 654 print_man_node(m, nn, h); 655 656 return(0); 657 } 658 659 660 /* ARGSUSED */ 661 static int 662 man_HP_pre(MAN_ARGS) 663 { 664 const struct man_node *nn; 665 struct htmlpair tag; 666 struct roffsu su; 667 668 if (MAN_HEAD == n->type) 669 return(0); 670 671 nn = MAN_BLOCK == n->type ? 672 n->head->child : n->parent->head->child; 673 674 SCALE_HS_INIT(&su, INDENT); 675 676 if (NULL != nn) 677 (void)a2width(nn, &su); 678 679 if (MAN_BLOCK == n->type) { 680 bufcat_su(h, "margin-left", &su); 681 SCALE_VS_INIT(&su, 1); 682 bufcat_su(h, "margin-top", &su); 683 bufcat_style(h, "clear", "both"); 684 PAIR_STYLE_INIT(&tag, h); 685 print_otag(h, TAG_DIV, 1, &tag); 686 return(1); 687 } 688 689 bufcat_su(h, "margin-left", &su); 690 SCALE_INVERT(&su); 691 bufcat_su(h, "text-indent", &su); 692 693 PAIR_STYLE_INIT(&tag, h); 694 print_otag(h, TAG_DIV, 1, &tag); 695 return(1); 696 } 697 698 699 /* ARGSUSED */ 700 static int 701 man_B_pre(MAN_ARGS) 702 { 703 704 print_ofont(h, HTMLFONT_BOLD); 705 return(1); 706 } 707 708 709 /* ARGSUSED */ 710 static int 711 man_I_pre(MAN_ARGS) 712 { 713 714 print_ofont(h, HTMLFONT_ITALIC); 715 return(1); 716 } 717 718 719 /* ARGSUSED */ 720 static int 721 man_ign_pre(MAN_ARGS) 722 { 723 724 return(0); 725 } 726 727 728 /* ARGSUSED */ 729 static int 730 man_RS_pre(MAN_ARGS) 731 { 732 struct htmlpair tag; 733 struct roffsu su; 734 735 if (MAN_HEAD == n->type) 736 return(0); 737 else if (MAN_BODY == n->type) 738 return(1); 739 740 SCALE_HS_INIT(&su, INDENT); 741 bufcat_su(h, "margin-left", &su); 742 743 if (n->head->child) { 744 SCALE_VS_INIT(&su, 1); 745 a2width(n->head->child, &su); 746 bufcat_su(h, "margin-top", &su); 747 } 748 749 PAIR_STYLE_INIT(&tag, h); 750 print_otag(h, TAG_DIV, 1, &tag); 751 return(1); 752 } 753