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