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