1 /* $Id: man_html.c,v 1.72 2011/05/17 11:34:31 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 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 "mandoc.h" 30 #include "out.h" 31 #include "html.h" 32 #include "man.h" 33 #include "main.h" 34 35 /* TODO: preserve ident widths. */ 36 /* FIXME: have PD set the default vspace width. */ 37 38 #define INDENT 5 39 #define HALFINDENT 3 40 41 #define MAN_ARGS const struct man_meta *m, \ 42 const struct man_node *n, \ 43 struct mhtml *mh, \ 44 struct html *h 45 46 struct mhtml { 47 int fl; 48 #define MANH_LITERAL (1 << 0) /* literal context */ 49 }; 50 51 struct htmlman { 52 int (*pre)(MAN_ARGS); 53 int (*post)(MAN_ARGS); 54 }; 55 56 static void print_man(MAN_ARGS); 57 static void print_man_head(MAN_ARGS); 58 static void print_man_nodelist(MAN_ARGS); 59 static void print_man_node(MAN_ARGS); 60 61 static int a2width(const struct man_node *, 62 struct roffsu *); 63 64 static int man_alt_pre(MAN_ARGS); 65 static int man_br_pre(MAN_ARGS); 66 static int man_ign_pre(MAN_ARGS); 67 static int man_in_pre(MAN_ARGS); 68 static int man_literal_pre(MAN_ARGS); 69 static void man_root_post(MAN_ARGS); 70 static void man_root_pre(MAN_ARGS); 71 static int man_B_pre(MAN_ARGS); 72 static int man_HP_pre(MAN_ARGS); 73 static int man_I_pre(MAN_ARGS); 74 static int man_IP_pre(MAN_ARGS); 75 static int man_PP_pre(MAN_ARGS); 76 static int man_RS_pre(MAN_ARGS); 77 static int man_SH_pre(MAN_ARGS); 78 static int man_SM_pre(MAN_ARGS); 79 static int man_SS_pre(MAN_ARGS); 80 81 static const struct htmlman mans[MAN_MAX] = { 82 { man_br_pre, NULL }, /* br */ 83 { NULL, NULL }, /* TH */ 84 { man_SH_pre, NULL }, /* SH */ 85 { man_SS_pre, NULL }, /* SS */ 86 { man_IP_pre, NULL }, /* TP */ 87 { man_PP_pre, NULL }, /* LP */ 88 { man_PP_pre, NULL }, /* PP */ 89 { man_PP_pre, NULL }, /* P */ 90 { man_IP_pre, NULL }, /* IP */ 91 { man_HP_pre, NULL }, /* HP */ 92 { man_SM_pre, NULL }, /* SM */ 93 { man_SM_pre, NULL }, /* SB */ 94 { man_alt_pre, NULL }, /* BI */ 95 { man_alt_pre, NULL }, /* IB */ 96 { man_alt_pre, NULL }, /* BR */ 97 { man_alt_pre, NULL }, /* RB */ 98 { NULL, NULL }, /* R */ 99 { man_B_pre, NULL }, /* B */ 100 { man_I_pre, NULL }, /* I */ 101 { man_alt_pre, NULL }, /* IR */ 102 { man_alt_pre, NULL }, /* RI */ 103 { man_ign_pre, NULL }, /* na */ 104 { man_br_pre, NULL }, /* sp */ 105 { man_literal_pre, NULL }, /* nf */ 106 { man_literal_pre, NULL }, /* fi */ 107 { NULL, NULL }, /* RE */ 108 { man_RS_pre, NULL }, /* RS */ 109 { man_ign_pre, NULL }, /* DT */ 110 { man_ign_pre, NULL }, /* UC */ 111 { man_ign_pre, NULL }, /* PD */ 112 { man_ign_pre, NULL }, /* AT */ 113 { man_in_pre, NULL }, /* in */ 114 { man_ign_pre, NULL }, /* ft */ 115 }; 116 117 118 void 119 html_man(void *arg, const struct man *m) 120 { 121 struct html *h; 122 struct tag *t; 123 struct mhtml mh; 124 125 h = (struct html *)arg; 126 127 print_gen_decls(h); 128 129 memset(&mh, 0, sizeof(struct mhtml)); 130 131 t = print_otag(h, TAG_HTML, 0, NULL); 132 print_man(man_meta(m), man_node(m), &mh, h); 133 print_tagq(h, t); 134 135 printf("\n"); 136 } 137 138 139 static void 140 print_man(MAN_ARGS) 141 { 142 struct tag *t; 143 144 t = print_otag(h, TAG_HEAD, 0, NULL); 145 print_man_head(m, n, mh, h); 146 print_tagq(h, t); 147 148 t = print_otag(h, TAG_BODY, 0, NULL); 149 print_man_nodelist(m, n, mh, h); 150 print_tagq(h, t); 151 } 152 153 154 /* ARGSUSED */ 155 static void 156 print_man_head(MAN_ARGS) 157 { 158 159 print_gen_head(h); 160 bufcat_fmt(h, "%s(%s)", m->title, m->msec); 161 print_otag(h, TAG_TITLE, 0, NULL); 162 print_text(h, h->buf); 163 } 164 165 166 static void 167 print_man_nodelist(MAN_ARGS) 168 { 169 170 print_man_node(m, n, mh, h); 171 if (n->next) 172 print_man_nodelist(m, n->next, mh, h); 173 } 174 175 176 static void 177 print_man_node(MAN_ARGS) 178 { 179 int child; 180 struct tag *t; 181 struct htmlpair tag; 182 183 child = 1; 184 t = h->tags.head; 185 186 switch (n->type) { 187 case (MAN_ROOT): 188 man_root_pre(m, n, mh, h); 189 break; 190 case (MAN_TEXT): 191 /* 192 * If we have a blank line, output a vertical space. 193 * If we have a space as the first character, break 194 * before printing the line's data. 195 */ 196 if ('\0' == *n->string) { 197 print_otag(h, TAG_P, 0, NULL); 198 return; 199 } else if (' ' == *n->string && MAN_LINE & n->flags) 200 print_otag(h, TAG_BR, 0, NULL); 201 202 print_text(h, n->string); 203 204 /* 205 * If we're in a literal context, make sure that words 206 * togehter on the same line stay together. This is a 207 * POST-printing call, so we check the NEXT word. Since 208 * -man doesn't have nested macros, we don't need to be 209 * more specific than this. 210 */ 211 if (MANH_LITERAL & mh->fl && 212 (NULL == n->next || 213 n->next->line > n->line)) 214 print_otag(h, TAG_BR, 0, NULL); 215 return; 216 case (MAN_EQN): 217 PAIR_CLASS_INIT(&tag, "eqn"); 218 print_otag(h, TAG_SPAN, 1, &tag); 219 print_text(h, n->eqn->data); 220 break; 221 case (MAN_TBL): 222 /* 223 * This will take care of initialising all of the table 224 * state data for the first table, then tearing it down 225 * for the last one. 226 */ 227 print_tbl(h, n->span); 228 return; 229 default: 230 /* 231 * Close out scope of font prior to opening a macro 232 * scope. 233 */ 234 if (HTMLFONT_NONE != h->metac) { 235 h->metal = h->metac; 236 h->metac = HTMLFONT_NONE; 237 } 238 239 /* 240 * Close out the current table, if it's open, and unset 241 * the "meta" table state. This will be reopened on the 242 * next table element. 243 */ 244 if (h->tblt) { 245 print_tblclose(h); 246 t = h->tags.head; 247 } 248 if (mans[n->tok].pre) 249 child = (*mans[n->tok].pre)(m, n, mh, h); 250 break; 251 } 252 253 if (child && n->child) 254 print_man_nodelist(m, n->child, mh, h); 255 256 /* This will automatically close out any font scope. */ 257 print_stagq(h, t); 258 259 switch (n->type) { 260 case (MAN_ROOT): 261 man_root_post(m, n, mh, h); 262 break; 263 case (MAN_EQN): 264 break; 265 default: 266 if (mans[n->tok].post) 267 (*mans[n->tok].post)(m, n, mh, h); 268 break; 269 } 270 } 271 272 273 static int 274 a2width(const struct man_node *n, struct roffsu *su) 275 { 276 277 if (MAN_TEXT != n->type) 278 return(0); 279 if (a2roffsu(n->string, su, SCALE_BU)) 280 return(1); 281 282 return(0); 283 } 284 285 286 /* ARGSUSED */ 287 static void 288 man_root_pre(MAN_ARGS) 289 { 290 struct htmlpair tag[3]; 291 struct tag *t, *tt; 292 char b[BUFSIZ], title[BUFSIZ]; 293 294 b[0] = 0; 295 if (m->vol) 296 (void)strlcat(b, m->vol, BUFSIZ); 297 298 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); 299 300 PAIR_SUMMARY_INIT(&tag[0], "Document Header"); 301 PAIR_CLASS_INIT(&tag[1], "head"); 302 if (NULL == h->style) { 303 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 304 t = print_otag(h, TAG_TABLE, 3, tag); 305 PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); 306 print_otag(h, TAG_COL, 1, tag); 307 print_otag(h, TAG_COL, 1, tag); 308 print_otag(h, TAG_COL, 1, tag); 309 } else 310 t = print_otag(h, TAG_TABLE, 2, tag); 311 312 print_otag(h, TAG_TBODY, 0, NULL); 313 314 tt = print_otag(h, TAG_TR, 0, NULL); 315 316 PAIR_CLASS_INIT(&tag[0], "head-ltitle"); 317 print_otag(h, TAG_TD, 1, tag); 318 319 print_text(h, title); 320 print_stagq(h, tt); 321 322 PAIR_CLASS_INIT(&tag[0], "head-vol"); 323 if (NULL == h->style) { 324 PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); 325 print_otag(h, TAG_TD, 2, tag); 326 } else 327 print_otag(h, TAG_TD, 1, tag); 328 329 print_text(h, b); 330 print_stagq(h, tt); 331 332 PAIR_CLASS_INIT(&tag[0], "head-rtitle"); 333 if (NULL == h->style) { 334 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 335 print_otag(h, TAG_TD, 2, tag); 336 } else 337 print_otag(h, TAG_TD, 1, tag); 338 339 print_text(h, title); 340 print_tagq(h, t); 341 } 342 343 344 /* ARGSUSED */ 345 static void 346 man_root_post(MAN_ARGS) 347 { 348 struct htmlpair tag[3]; 349 struct tag *t, *tt; 350 351 PAIR_SUMMARY_INIT(&tag[0], "Document Footer"); 352 PAIR_CLASS_INIT(&tag[1], "foot"); 353 if (NULL == h->style) { 354 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 355 t = print_otag(h, TAG_TABLE, 3, tag); 356 PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); 357 print_otag(h, TAG_COL, 1, tag); 358 print_otag(h, TAG_COL, 1, tag); 359 } else 360 t = print_otag(h, TAG_TABLE, 2, tag); 361 362 tt = print_otag(h, TAG_TR, 0, NULL); 363 364 PAIR_CLASS_INIT(&tag[0], "foot-date"); 365 print_otag(h, TAG_TD, 1, tag); 366 367 print_text(h, m->date); 368 print_stagq(h, tt); 369 370 PAIR_CLASS_INIT(&tag[0], "foot-os"); 371 if (NULL == h->style) { 372 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 373 print_otag(h, TAG_TD, 2, tag); 374 } else 375 print_otag(h, TAG_TD, 1, tag); 376 377 if (m->source) 378 print_text(h, m->source); 379 print_tagq(h, t); 380 } 381 382 383 384 /* ARGSUSED */ 385 static int 386 man_br_pre(MAN_ARGS) 387 { 388 struct roffsu su; 389 struct htmlpair tag; 390 391 SCALE_VS_INIT(&su, 1); 392 393 if (MAN_sp == n->tok) { 394 if (n->child) 395 a2roffsu(n->child->string, &su, SCALE_VS); 396 } else 397 su.scale = 0; 398 399 bufinit(h); 400 bufcat_su(h, "height", &su); 401 PAIR_STYLE_INIT(&tag, h); 402 print_otag(h, TAG_DIV, 1, &tag); 403 404 /* So the div isn't empty: */ 405 print_text(h, "\\~"); 406 407 return(0); 408 } 409 410 411 /* ARGSUSED */ 412 static int 413 man_SH_pre(MAN_ARGS) 414 { 415 struct htmlpair tag; 416 417 if (MAN_BLOCK == n->type) { 418 PAIR_CLASS_INIT(&tag, "section"); 419 print_otag(h, TAG_DIV, 1, &tag); 420 return(1); 421 } else if (MAN_BODY == n->type) 422 return(1); 423 424 print_otag(h, TAG_H1, 0, NULL); 425 return(1); 426 } 427 428 429 /* ARGSUSED */ 430 static int 431 man_alt_pre(MAN_ARGS) 432 { 433 const struct man_node *nn; 434 int i; 435 enum htmltag fp; 436 struct tag *t; 437 438 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 439 t = NULL; 440 switch (n->tok) { 441 case (MAN_BI): 442 fp = i % 2 ? TAG_I : TAG_B; 443 break; 444 case (MAN_IB): 445 fp = i % 2 ? TAG_B : TAG_I; 446 break; 447 case (MAN_RI): 448 fp = i % 2 ? TAG_I : TAG_MAX; 449 break; 450 case (MAN_IR): 451 fp = i % 2 ? TAG_MAX : TAG_I; 452 break; 453 case (MAN_BR): 454 fp = i % 2 ? TAG_MAX : TAG_B; 455 break; 456 case (MAN_RB): 457 fp = i % 2 ? TAG_B : TAG_MAX; 458 break; 459 default: 460 abort(); 461 /* NOTREACHED */ 462 } 463 464 if (i) 465 h->flags |= HTML_NOSPACE; 466 467 if (TAG_MAX != fp) 468 t = print_otag(h, fp, 0, NULL); 469 470 print_man_node(m, nn, mh, h); 471 472 if (t) 473 print_tagq(h, t); 474 } 475 476 return(0); 477 } 478 479 480 /* ARGSUSED */ 481 static int 482 man_SM_pre(MAN_ARGS) 483 { 484 485 print_otag(h, TAG_SMALL, 0, NULL); 486 if (MAN_SB == n->tok) 487 print_otag(h, TAG_B, 0, NULL); 488 return(1); 489 } 490 491 492 /* ARGSUSED */ 493 static int 494 man_SS_pre(MAN_ARGS) 495 { 496 struct htmlpair tag; 497 498 if (MAN_BLOCK == n->type) { 499 PAIR_CLASS_INIT(&tag, "subsection"); 500 print_otag(h, TAG_DIV, 1, &tag); 501 return(1); 502 } else if (MAN_BODY == n->type) 503 return(1); 504 505 print_otag(h, TAG_H2, 0, NULL); 506 return(1); 507 } 508 509 510 /* ARGSUSED */ 511 static int 512 man_PP_pre(MAN_ARGS) 513 { 514 515 if (MAN_HEAD == n->type) 516 return(0); 517 else if (MAN_BODY == n->type && n->prev) 518 print_otag(h, TAG_P, 0, NULL); 519 520 return(1); 521 } 522 523 524 /* ARGSUSED */ 525 static int 526 man_IP_pre(MAN_ARGS) 527 { 528 struct roffsu su; 529 struct htmlpair tag; 530 const struct man_node *nn; 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 print_otag(h, TAG_TD, 0, NULL); 541 return(1); 542 } 543 544 nn = MAN_BLOCK == n->type ? 545 n->head->child : n->parent->head->child; 546 547 SCALE_HS_INIT(&su, INDENT); 548 549 /* Width is the second token. */ 550 551 if (MAN_IP == n->tok && NULL != nn) 552 if (NULL != (nn = nn->next)) 553 a2width(nn, &su); 554 555 /* Width is the first token. */ 556 557 if (MAN_TP == n->tok && NULL != nn) { 558 /* Skip past non-text children. */ 559 while (nn && MAN_TEXT != nn->type) 560 nn = nn->next; 561 if (nn) 562 a2width(nn, &su); 563 } 564 565 if (MAN_BLOCK == n->type) { 566 print_otag(h, TAG_P, 0, NULL); 567 print_otag(h, TAG_TABLE, 0, NULL); 568 bufinit(h); 569 bufcat_su(h, "width", &su); 570 PAIR_STYLE_INIT(&tag, h); 571 print_otag(h, TAG_COL, 1, &tag); 572 print_otag(h, TAG_COL, 0, NULL); 573 print_otag(h, TAG_TBODY, 0, NULL); 574 print_otag(h, TAG_TR, 0, NULL); 575 return(1); 576 } 577 578 print_otag(h, TAG_TD, 0, NULL); 579 580 /* For IP, only print the first header element. */ 581 582 if (MAN_IP == n->tok && n->child) 583 print_man_node(m, n->child, mh, h); 584 585 /* For TP, only print next-line header elements. */ 586 587 if (MAN_TP == n->tok) 588 for (nn = n->child; nn; nn = nn->next) 589 if (nn->line > n->line) 590 print_man_node(m, nn, mh, h); 591 592 return(0); 593 } 594 595 596 /* ARGSUSED */ 597 static int 598 man_HP_pre(MAN_ARGS) 599 { 600 struct htmlpair tag; 601 struct roffsu su; 602 const struct man_node *np; 603 604 bufinit(h); 605 606 np = MAN_BLOCK == n->type ? 607 n->head->child : 608 n->parent->head->child; 609 610 if (NULL == np || ! a2width(np, &su)) 611 SCALE_HS_INIT(&su, INDENT); 612 613 if (MAN_HEAD == n->type) { 614 print_otag(h, TAG_TD, 0, NULL); 615 return(0); 616 } else if (MAN_BLOCK == n->type) { 617 print_otag(h, TAG_P, 0, NULL); 618 print_otag(h, TAG_TABLE, 0, NULL); 619 bufcat_su(h, "width", &su); 620 PAIR_STYLE_INIT(&tag, h); 621 print_otag(h, TAG_COL, 1, &tag); 622 print_otag(h, TAG_COL, 0, NULL); 623 print_otag(h, TAG_TBODY, 0, NULL); 624 print_otag(h, TAG_TR, 0, NULL); 625 return(1); 626 } 627 628 su.scale = -su.scale; 629 bufcat_su(h, "text-indent", &su); 630 PAIR_STYLE_INIT(&tag, h); 631 print_otag(h, TAG_TD, 1, &tag); 632 return(1); 633 } 634 635 636 /* ARGSUSED */ 637 static int 638 man_B_pre(MAN_ARGS) 639 { 640 641 print_otag(h, TAG_B, 0, NULL); 642 return(1); 643 } 644 645 646 /* ARGSUSED */ 647 static int 648 man_I_pre(MAN_ARGS) 649 { 650 651 print_otag(h, TAG_I, 0, NULL); 652 return(1); 653 } 654 655 656 /* ARGSUSED */ 657 static int 658 man_literal_pre(MAN_ARGS) 659 { 660 661 if (MAN_nf == n->tok) { 662 print_otag(h, TAG_BR, 0, NULL); 663 mh->fl |= MANH_LITERAL; 664 } else 665 mh->fl &= ~MANH_LITERAL; 666 667 return(0); 668 } 669 670 671 /* ARGSUSED */ 672 static int 673 man_in_pre(MAN_ARGS) 674 { 675 676 print_otag(h, TAG_BR, 0, NULL); 677 return(0); 678 } 679 680 681 /* ARGSUSED */ 682 static int 683 man_ign_pre(MAN_ARGS) 684 { 685 686 return(0); 687 } 688 689 690 /* ARGSUSED */ 691 static int 692 man_RS_pre(MAN_ARGS) 693 { 694 struct htmlpair tag; 695 struct roffsu su; 696 697 if (MAN_HEAD == n->type) 698 return(0); 699 else if (MAN_BODY == n->type) 700 return(1); 701 702 SCALE_HS_INIT(&su, INDENT); 703 if (n->head->child) 704 a2width(n->head->child, &su); 705 706 bufinit(h); 707 bufcat_su(h, "margin-left", &su); 708 PAIR_STYLE_INIT(&tag, h); 709 print_otag(h, TAG_DIV, 1, &tag); 710 return(1); 711 } 712