1 /* $Vendor-Id: man_html.c,v 1.26 2010/01/29 14:39:38 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 }; 107 108 109 void 110 html_man(void *arg, const struct man *m) 111 { 112 struct html *h; 113 struct tag *t; 114 115 h = (struct html *)arg; 116 117 print_gen_decls(h); 118 119 t = print_otag(h, TAG_HTML, 0, NULL); 120 print_man(man_meta(m), man_node(m), h); 121 print_tagq(h, t); 122 123 printf("\n"); 124 } 125 126 127 static void 128 print_man(MAN_ARGS) 129 { 130 struct tag *t; 131 struct htmlpair tag; 132 133 t = print_otag(h, TAG_HEAD, 0, NULL); 134 135 print_man_head(m, n, h); 136 print_tagq(h, t); 137 t = print_otag(h, TAG_BODY, 0, NULL); 138 139 tag.key = ATTR_CLASS; 140 tag.val = "body"; 141 print_otag(h, TAG_DIV, 1, &tag); 142 143 print_man_nodelist(m, n, h); 144 145 print_tagq(h, t); 146 } 147 148 149 /* ARGSUSED */ 150 static void 151 print_man_head(MAN_ARGS) 152 { 153 154 print_gen_head(h); 155 bufinit(h); 156 buffmt(h, "%s(%d)", m->title, m->msec); 157 158 print_otag(h, TAG_TITLE, 0, NULL); 159 print_text(h, h->buf); 160 } 161 162 163 static void 164 print_man_nodelist(MAN_ARGS) 165 { 166 167 print_man_node(m, n, h); 168 if (n->next) 169 print_man_nodelist(m, n->next, h); 170 } 171 172 173 static void 174 print_man_node(MAN_ARGS) 175 { 176 int child; 177 struct tag *t; 178 179 child = 1; 180 t = h->tags.head; 181 182 bufinit(h); 183 184 switch (n->type) { 185 case (MAN_ROOT): 186 child = man_root_pre(m, n, h); 187 break; 188 case (MAN_TEXT): 189 print_text(h, n->string); 190 return; 191 default: 192 /* 193 * Close out scope of font prior to opening a macro 194 * scope. Assert that the metafont is on the top of the 195 * stack (it's never nested). 196 */ 197 if (h->metaf) { 198 assert(h->metaf == t); 199 print_tagq(h, h->metaf); 200 assert(NULL == h->metaf); 201 t = h->tags.head; 202 } 203 if (mans[n->tok].pre) 204 child = (*mans[n->tok].pre)(m, n, h); 205 break; 206 } 207 208 if (child && n->child) 209 print_man_nodelist(m, n->child, h); 210 211 /* This will automatically close out any font scope. */ 212 print_stagq(h, t); 213 214 bufinit(h); 215 216 switch (n->type) { 217 case (MAN_ROOT): 218 man_root_post(m, n, h); 219 break; 220 case (MAN_TEXT): 221 break; 222 default: 223 if (mans[n->tok].post) 224 (*mans[n->tok].post)(m, n, h); 225 break; 226 } 227 } 228 229 230 static int 231 a2width(const struct man_node *n, struct roffsu *su) 232 { 233 234 if (MAN_TEXT != n->type) 235 return(0); 236 if (a2roffsu(n->string, su, SCALE_BU)) 237 return(1); 238 239 return(0); 240 } 241 242 243 /* ARGSUSED */ 244 static int 245 man_root_pre(MAN_ARGS) 246 { 247 struct htmlpair tag[3]; 248 struct tag *t, *tt; 249 char b[BUFSIZ], title[BUFSIZ]; 250 251 b[0] = 0; 252 if (m->vol) 253 (void)strlcat(b, m->vol, BUFSIZ); 254 255 snprintf(title, BUFSIZ - 1, "%s(%d)", m->title, m->msec); 256 257 PAIR_CLASS_INIT(&tag[0], "header"); 258 bufcat_style(h, "width", "100%"); 259 PAIR_STYLE_INIT(&tag[1], h); 260 PAIR_SUMMARY_INIT(&tag[2], "header"); 261 262 t = print_otag(h, TAG_TABLE, 3, tag); 263 tt = print_otag(h, TAG_TR, 0, NULL); 264 265 bufinit(h); 266 bufcat_style(h, "width", "10%"); 267 PAIR_STYLE_INIT(&tag[0], h); 268 print_otag(h, TAG_TD, 1, tag); 269 print_text(h, title); 270 print_stagq(h, tt); 271 272 bufinit(h); 273 bufcat_style(h, "width", "80%"); 274 bufcat_style(h, "white-space", "nowrap"); 275 bufcat_style(h, "text-align", "center"); 276 PAIR_STYLE_INIT(&tag[0], h); 277 print_otag(h, TAG_TD, 1, tag); 278 print_text(h, b); 279 print_stagq(h, tt); 280 281 bufinit(h); 282 bufcat_style(h, "width", "10%"); 283 bufcat_style(h, "text-align", "right"); 284 PAIR_STYLE_INIT(&tag[0], h); 285 print_otag(h, TAG_TD, 1, tag); 286 print_text(h, title); 287 print_tagq(h, t); 288 return(1); 289 } 290 291 292 /* ARGSUSED */ 293 static void 294 man_root_post(MAN_ARGS) 295 { 296 struct htmlpair tag[3]; 297 struct tag *t, *tt; 298 char b[DATESIZ]; 299 300 time2a(m->date, b, DATESIZ); 301 302 PAIR_CLASS_INIT(&tag[0], "footer"); 303 bufcat_style(h, "width", "100%"); 304 PAIR_STYLE_INIT(&tag[1], h); 305 PAIR_SUMMARY_INIT(&tag[2], "footer"); 306 307 t = print_otag(h, TAG_TABLE, 3, tag); 308 tt = print_otag(h, TAG_TR, 0, NULL); 309 310 bufinit(h); 311 bufcat_style(h, "width", "50%"); 312 PAIR_STYLE_INIT(&tag[0], h); 313 print_otag(h, TAG_TD, 1, tag); 314 print_text(h, b); 315 print_stagq(h, tt); 316 317 bufinit(h); 318 bufcat_style(h, "width", "50%"); 319 bufcat_style(h, "text-align", "right"); 320 PAIR_STYLE_INIT(&tag[0], h); 321 print_otag(h, TAG_TD, 1, tag); 322 if (m->source) 323 print_text(h, m->source); 324 print_tagq(h, t); 325 } 326 327 328 329 /* ARGSUSED */ 330 static int 331 man_br_pre(MAN_ARGS) 332 { 333 struct roffsu su; 334 struct htmlpair tag; 335 336 SCALE_VS_INIT(&su, 1); 337 338 if (MAN_sp == n->tok && n->child) 339 a2roffsu(n->child->string, &su, SCALE_VS); 340 else if (MAN_br == n->tok) 341 su.scale = 0; 342 343 bufcat_su(h, "height", &su); 344 PAIR_STYLE_INIT(&tag, h); 345 print_otag(h, TAG_DIV, 1, &tag); 346 347 /* So the div isn't empty: */ 348 print_text(h, "\\~"); 349 350 return(0); 351 } 352 353 354 /* ARGSUSED */ 355 static int 356 man_SH_pre(MAN_ARGS) 357 { 358 struct htmlpair tag[2]; 359 struct roffsu su; 360 361 if (MAN_BODY == n->type) { 362 SCALE_HS_INIT(&su, INDENT); 363 bufcat_su(h, "margin-left", &su); 364 PAIR_CLASS_INIT(&tag[0], "sec-body"); 365 PAIR_STYLE_INIT(&tag[1], h); 366 print_otag(h, TAG_DIV, 2, tag); 367 return(1); 368 } else if (MAN_BLOCK == n->type) { 369 PAIR_CLASS_INIT(&tag[0], "sec-block"); 370 if (n->prev && MAN_SH == n->prev->tok) 371 if (NULL == n->prev->body->child) { 372 print_otag(h, TAG_DIV, 1, tag); 373 return(1); 374 } 375 376 SCALE_VS_INIT(&su, 1); 377 bufcat_su(h, "margin-top", &su); 378 if (NULL == n->next) 379 bufcat_su(h, "margin-bottom", &su); 380 PAIR_STYLE_INIT(&tag[1], h); 381 print_otag(h, TAG_DIV, 2, tag); 382 return(1); 383 } 384 385 PAIR_CLASS_INIT(&tag[0], "sec-head"); 386 print_otag(h, TAG_DIV, 1, tag); 387 return(1); 388 } 389 390 391 /* ARGSUSED */ 392 static int 393 man_alt_pre(MAN_ARGS) 394 { 395 const struct man_node *nn; 396 struct tag *t; 397 int i; 398 enum htmlfont fp; 399 400 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 401 switch (n->tok) { 402 case (MAN_BI): 403 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD; 404 break; 405 case (MAN_IB): 406 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC; 407 break; 408 case (MAN_RI): 409 fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE; 410 break; 411 case (MAN_IR): 412 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC; 413 break; 414 case (MAN_BR): 415 fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD; 416 break; 417 case (MAN_RB): 418 fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE; 419 break; 420 default: 421 abort(); 422 /* NOTREACHED */ 423 } 424 425 if (i) 426 h->flags |= HTML_NOSPACE; 427 428 /* 429 * Open and close the scope with each argument, so that 430 * internal \f escapes, which are common, are also 431 * closed out with the scope. 432 */ 433 t = print_ofont(h, fp); 434 print_man_node(m, nn, h); 435 print_tagq(h, t); 436 } 437 438 return(0); 439 } 440 441 442 /* ARGSUSED */ 443 static int 444 man_SB_pre(MAN_ARGS) 445 { 446 struct htmlpair tag; 447 448 /* FIXME: print_ofont(). */ 449 PAIR_CLASS_INIT(&tag, "small bold"); 450 print_otag(h, TAG_SPAN, 1, &tag); 451 return(1); 452 } 453 454 455 /* ARGSUSED */ 456 static int 457 man_SM_pre(MAN_ARGS) 458 { 459 struct htmlpair tag; 460 461 PAIR_CLASS_INIT(&tag, "small"); 462 print_otag(h, TAG_SPAN, 1, &tag); 463 return(1); 464 } 465 466 467 /* ARGSUSED */ 468 static int 469 man_SS_pre(MAN_ARGS) 470 { 471 struct htmlpair tag[3]; 472 struct roffsu su; 473 474 SCALE_VS_INIT(&su, 1); 475 476 if (MAN_BODY == n->type) { 477 PAIR_CLASS_INIT(&tag[0], "ssec-body"); 478 if (n->parent->next && n->child) { 479 bufcat_su(h, "margin-bottom", &su); 480 PAIR_STYLE_INIT(&tag[1], h); 481 print_otag(h, TAG_DIV, 2, tag); 482 return(1); 483 } 484 485 print_otag(h, TAG_DIV, 1, tag); 486 return(1); 487 } else if (MAN_BLOCK == n->type) { 488 PAIR_CLASS_INIT(&tag[0], "ssec-block"); 489 if (n->prev && MAN_SS == n->prev->tok) 490 if (n->prev->body->child) { 491 bufcat_su(h, "margin-top", &su); 492 PAIR_STYLE_INIT(&tag[1], h); 493 print_otag(h, TAG_DIV, 2, tag); 494 return(1); 495 } 496 497 print_otag(h, TAG_DIV, 1, tag); 498 return(1); 499 } 500 501 SCALE_HS_INIT(&su, INDENT - HALFINDENT); 502 bufcat_su(h, "margin-left", &su); 503 PAIR_CLASS_INIT(&tag[0], "ssec-head"); 504 PAIR_STYLE_INIT(&tag[1], h); 505 print_otag(h, TAG_DIV, 2, tag); 506 return(1); 507 } 508 509 510 /* ARGSUSED */ 511 static int 512 man_PP_pre(MAN_ARGS) 513 { 514 struct htmlpair tag; 515 struct roffsu su; 516 int i; 517 518 if (MAN_BLOCK != n->type) 519 return(1); 520 521 i = 0; 522 523 if (MAN_ROOT == n->parent->type) { 524 SCALE_HS_INIT(&su, INDENT); 525 bufcat_su(h, "margin-left", &su); 526 i = 1; 527 } 528 if (n->prev) { 529 SCALE_VS_INIT(&su, 1); 530 bufcat_su(h, "margin-top", &su); 531 i = 1; 532 } 533 534 PAIR_STYLE_INIT(&tag, h); 535 print_otag(h, TAG_DIV, i, &tag); 536 return(1); 537 } 538 539 540 /* ARGSUSED */ 541 static int 542 man_IP_pre(MAN_ARGS) 543 { 544 struct roffsu su; 545 struct htmlpair tag; 546 const struct man_node *nn; 547 int width; 548 549 /* 550 * This scattering of 1-BU margins and pads is to make sure that 551 * when text overruns its box, the subsequent text isn't flush 552 * up against it. However, the rest of the right-hand box must 553 * also be adjusted in consideration of this 1-BU space. 554 */ 555 556 if (MAN_BODY == n->type) { 557 SCALE_HS_INIT(&su, INDENT); 558 bufcat_su(h, "margin-left", &su); 559 PAIR_STYLE_INIT(&tag, h); 560 print_otag(h, TAG_DIV, 1, &tag); 561 return(1); 562 } 563 564 nn = MAN_BLOCK == n->type ? 565 n->head->child : n->parent->head->child; 566 567 SCALE_HS_INIT(&su, INDENT); 568 width = 0; 569 570 if (MAN_IP == n->tok && NULL != nn) 571 if (NULL != (nn = nn->next)) { 572 for ( ; nn->next; nn = nn->next) 573 /* Do nothing. */ ; 574 width = a2width(nn, &su); 575 } 576 577 if (MAN_TP == n->tok && NULL != nn) 578 width = a2width(nn, &su); 579 580 if (MAN_BLOCK == n->type) { 581 bufcat_su(h, "margin-left", &su); 582 SCALE_VS_INIT(&su, 1); 583 bufcat_su(h, "margin-top", &su); 584 bufcat_style(h, "clear", "both"); 585 PAIR_STYLE_INIT(&tag, h); 586 print_otag(h, TAG_DIV, 1, &tag); 587 return(1); 588 } 589 590 bufcat_su(h, "min-width", &su); 591 SCALE_INVERT(&su); 592 bufcat_su(h, "margin-left", &su); 593 SCALE_HS_INIT(&su, 1); 594 bufcat_su(h, "margin-right", &su); 595 bufcat_style(h, "clear", "left"); 596 597 if (n->next && n->next->child) 598 bufcat_style(h, "float", "left"); 599 600 PAIR_STYLE_INIT(&tag, h); 601 print_otag(h, TAG_DIV, 1, &tag); 602 603 /* With a length string, manually omit the last child. */ 604 605 if ( ! width) 606 return(1); 607 608 if (MAN_IP == n->tok) 609 for (nn = n->child; nn->next; nn = nn->next) 610 print_man_node(m, nn, h); 611 if (MAN_TP == n->tok) 612 for (nn = n->child->next; nn; nn = nn->next) 613 print_man_node(m, nn, h); 614 615 return(0); 616 } 617 618 619 /* ARGSUSED */ 620 static int 621 man_HP_pre(MAN_ARGS) 622 { 623 const struct man_node *nn; 624 struct htmlpair tag; 625 struct roffsu su; 626 627 if (MAN_HEAD == n->type) 628 return(0); 629 630 nn = MAN_BLOCK == n->type ? 631 n->head->child : n->parent->head->child; 632 633 SCALE_HS_INIT(&su, INDENT); 634 635 if (NULL != nn) 636 (void)a2width(nn, &su); 637 638 if (MAN_BLOCK == n->type) { 639 bufcat_su(h, "margin-left", &su); 640 SCALE_VS_INIT(&su, 1); 641 bufcat_su(h, "margin-top", &su); 642 bufcat_style(h, "clear", "both"); 643 PAIR_STYLE_INIT(&tag, h); 644 print_otag(h, TAG_DIV, 1, &tag); 645 return(1); 646 } 647 648 bufcat_su(h, "margin-left", &su); 649 SCALE_INVERT(&su); 650 bufcat_su(h, "text-indent", &su); 651 652 PAIR_STYLE_INIT(&tag, h); 653 print_otag(h, TAG_DIV, 1, &tag); 654 return(1); 655 } 656 657 658 /* ARGSUSED */ 659 static int 660 man_B_pre(MAN_ARGS) 661 { 662 663 print_ofont(h, HTMLFONT_BOLD); 664 return(1); 665 } 666 667 668 /* ARGSUSED */ 669 static int 670 man_I_pre(MAN_ARGS) 671 { 672 673 print_ofont(h, HTMLFONT_ITALIC); 674 return(1); 675 } 676 677 678 /* ARGSUSED */ 679 static int 680 man_ign_pre(MAN_ARGS) 681 { 682 683 return(0); 684 } 685 686 687 /* ARGSUSED */ 688 static int 689 man_RS_pre(MAN_ARGS) 690 { 691 struct htmlpair tag; 692 struct roffsu su; 693 694 if (MAN_HEAD == n->type) 695 return(0); 696 else if (MAN_BODY == n->type) 697 return(1); 698 699 SCALE_HS_INIT(&su, INDENT); 700 bufcat_su(h, "margin-left", &su); 701 702 if (n->head->child) { 703 SCALE_VS_INIT(&su, 1); 704 a2width(n->head->child, &su); 705 bufcat_su(h, "margin-top", &su); 706 } 707 708 PAIR_STYLE_INIT(&tag, h); 709 print_otag(h, TAG_DIV, 1, &tag); 710 return(1); 711 } 712