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