1 /* $Id: man_html.c,v 1.70 2011/03/07 01:35:51 schwarze 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 bufinit(h); 161 buffmt(h, "%s(%s)", m->title, m->msec); 162 163 print_otag(h, TAG_TITLE, 0, NULL); 164 print_text(h, h->buf); 165 } 166 167 168 static void 169 print_man_nodelist(MAN_ARGS) 170 { 171 172 print_man_node(m, n, mh, h); 173 if (n->next) 174 print_man_nodelist(m, n->next, mh, h); 175 } 176 177 178 static void 179 print_man_node(MAN_ARGS) 180 { 181 int child; 182 struct tag *t; 183 struct htmlpair tag; 184 185 child = 1; 186 t = h->tags.head; 187 bufinit(h); 188 189 switch (n->type) { 190 case (MAN_ROOT): 191 man_root_pre(m, n, mh, h); 192 break; 193 case (MAN_TEXT): 194 /* 195 * If we have a blank line, output a vertical space. 196 * If we have a space as the first character, break 197 * before printing the line's data. 198 */ 199 if ('\0' == *n->string) { 200 print_otag(h, TAG_P, 0, NULL); 201 return; 202 } else if (' ' == *n->string && MAN_LINE & n->flags) 203 print_otag(h, TAG_BR, 0, NULL); 204 205 print_text(h, n->string); 206 207 /* 208 * If we're in a literal context, make sure that words 209 * togehter on the same line stay together. This is a 210 * POST-printing call, so we check the NEXT word. Since 211 * -man doesn't have nested macros, we don't need to be 212 * more specific than this. 213 */ 214 if (MANH_LITERAL & mh->fl && 215 (NULL == n->next || 216 n->next->line > n->line)) 217 print_otag(h, TAG_BR, 0, NULL); 218 return; 219 case (MAN_EQN): 220 PAIR_CLASS_INIT(&tag, "eqn"); 221 print_otag(h, TAG_SPAN, 1, &tag); 222 print_text(h, n->eqn->data); 223 break; 224 case (MAN_TBL): 225 /* 226 * This will take care of initialising all of the table 227 * state data for the first table, then tearing it down 228 * for the last one. 229 */ 230 print_tbl(h, n->span); 231 return; 232 default: 233 /* 234 * Close out scope of font prior to opening a macro 235 * scope. 236 */ 237 if (HTMLFONT_NONE != h->metac) { 238 h->metal = h->metac; 239 h->metac = HTMLFONT_NONE; 240 } 241 242 /* 243 * Close out the current table, if it's open, and unset 244 * the "meta" table state. This will be reopened on the 245 * next table element. 246 */ 247 if (h->tblt) { 248 print_tblclose(h); 249 t = h->tags.head; 250 } 251 if (mans[n->tok].pre) 252 child = (*mans[n->tok].pre)(m, n, mh, h); 253 break; 254 } 255 256 if (child && n->child) 257 print_man_nodelist(m, n->child, mh, h); 258 259 /* This will automatically close out any font scope. */ 260 print_stagq(h, t); 261 262 bufinit(h); 263 264 switch (n->type) { 265 case (MAN_ROOT): 266 man_root_post(m, n, mh, h); 267 break; 268 case (MAN_EQN): 269 break; 270 default: 271 if (mans[n->tok].post) 272 (*mans[n->tok].post)(m, n, mh, h); 273 break; 274 } 275 } 276 277 278 static int 279 a2width(const struct man_node *n, struct roffsu *su) 280 { 281 282 if (MAN_TEXT != n->type) 283 return(0); 284 if (a2roffsu(n->string, su, SCALE_BU)) 285 return(1); 286 287 return(0); 288 } 289 290 291 /* ARGSUSED */ 292 static void 293 man_root_pre(MAN_ARGS) 294 { 295 struct htmlpair tag[3]; 296 struct tag *t, *tt; 297 char b[BUFSIZ], title[BUFSIZ]; 298 299 b[0] = 0; 300 if (m->vol) 301 (void)strlcat(b, m->vol, BUFSIZ); 302 303 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); 304 305 PAIR_SUMMARY_INIT(&tag[0], "Document Header"); 306 PAIR_CLASS_INIT(&tag[1], "head"); 307 if (NULL == h->style) { 308 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 309 t = print_otag(h, TAG_TABLE, 3, tag); 310 PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); 311 print_otag(h, TAG_COL, 1, tag); 312 print_otag(h, TAG_COL, 1, tag); 313 print_otag(h, TAG_COL, 1, tag); 314 } else 315 t = print_otag(h, TAG_TABLE, 2, tag); 316 317 print_otag(h, TAG_TBODY, 0, NULL); 318 319 tt = print_otag(h, TAG_TR, 0, NULL); 320 321 PAIR_CLASS_INIT(&tag[0], "head-ltitle"); 322 print_otag(h, TAG_TD, 1, tag); 323 324 print_text(h, title); 325 print_stagq(h, tt); 326 327 PAIR_CLASS_INIT(&tag[0], "head-vol"); 328 if (NULL == h->style) { 329 PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); 330 print_otag(h, TAG_TD, 2, tag); 331 } else 332 print_otag(h, TAG_TD, 1, tag); 333 334 print_text(h, b); 335 print_stagq(h, tt); 336 337 PAIR_CLASS_INIT(&tag[0], "head-rtitle"); 338 if (NULL == h->style) { 339 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 340 print_otag(h, TAG_TD, 2, tag); 341 } else 342 print_otag(h, TAG_TD, 1, tag); 343 344 print_text(h, title); 345 print_tagq(h, t); 346 } 347 348 349 /* ARGSUSED */ 350 static void 351 man_root_post(MAN_ARGS) 352 { 353 struct htmlpair tag[3]; 354 struct tag *t, *tt; 355 356 PAIR_SUMMARY_INIT(&tag[0], "Document Footer"); 357 PAIR_CLASS_INIT(&tag[1], "foot"); 358 if (NULL == h->style) { 359 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 360 t = print_otag(h, TAG_TABLE, 3, tag); 361 PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); 362 print_otag(h, TAG_COL, 1, tag); 363 print_otag(h, TAG_COL, 1, tag); 364 } else 365 t = print_otag(h, TAG_TABLE, 2, tag); 366 367 tt = print_otag(h, TAG_TR, 0, NULL); 368 369 PAIR_CLASS_INIT(&tag[0], "foot-date"); 370 print_otag(h, TAG_TD, 1, tag); 371 372 print_text(h, m->date); 373 print_stagq(h, tt); 374 375 PAIR_CLASS_INIT(&tag[0], "foot-os"); 376 if (NULL == h->style) { 377 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 378 print_otag(h, TAG_TD, 2, tag); 379 } else 380 print_otag(h, TAG_TD, 1, tag); 381 382 if (m->source) 383 print_text(h, m->source); 384 print_tagq(h, t); 385 } 386 387 388 389 /* ARGSUSED */ 390 static int 391 man_br_pre(MAN_ARGS) 392 { 393 struct roffsu su; 394 struct htmlpair tag; 395 396 SCALE_VS_INIT(&su, 1); 397 398 if (MAN_sp == n->tok) { 399 if (n->child) 400 a2roffsu(n->child->string, &su, SCALE_VS); 401 } else 402 su.scale = 0; 403 404 bufcat_su(h, "height", &su); 405 PAIR_STYLE_INIT(&tag, h); 406 print_otag(h, TAG_DIV, 1, &tag); 407 408 /* So the div isn't empty: */ 409 print_text(h, "\\~"); 410 411 return(0); 412 } 413 414 415 /* ARGSUSED */ 416 static int 417 man_SH_pre(MAN_ARGS) 418 { 419 struct htmlpair tag; 420 421 if (MAN_BLOCK == n->type) { 422 PAIR_CLASS_INIT(&tag, "section"); 423 print_otag(h, TAG_DIV, 1, &tag); 424 return(1); 425 } else if (MAN_BODY == n->type) 426 return(1); 427 428 print_otag(h, TAG_H1, 0, NULL); 429 return(1); 430 } 431 432 433 /* ARGSUSED */ 434 static int 435 man_alt_pre(MAN_ARGS) 436 { 437 const struct man_node *nn; 438 int i; 439 enum htmltag fp; 440 struct tag *t; 441 442 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 443 t = NULL; 444 switch (n->tok) { 445 case (MAN_BI): 446 fp = i % 2 ? TAG_I : TAG_B; 447 break; 448 case (MAN_IB): 449 fp = i % 2 ? TAG_B : TAG_I; 450 break; 451 case (MAN_RI): 452 fp = i % 2 ? TAG_I : TAG_MAX; 453 break; 454 case (MAN_IR): 455 fp = i % 2 ? TAG_MAX : TAG_I; 456 break; 457 case (MAN_BR): 458 fp = i % 2 ? TAG_MAX : TAG_B; 459 break; 460 case (MAN_RB): 461 fp = i % 2 ? TAG_B : TAG_MAX; 462 break; 463 default: 464 abort(); 465 /* NOTREACHED */ 466 } 467 468 if (i) 469 h->flags |= HTML_NOSPACE; 470 471 if (TAG_MAX != fp) 472 t = print_otag(h, fp, 0, NULL); 473 474 print_man_node(m, nn, mh, h); 475 476 if (t) 477 print_tagq(h, t); 478 } 479 480 return(0); 481 } 482 483 484 /* ARGSUSED */ 485 static int 486 man_SM_pre(MAN_ARGS) 487 { 488 489 print_otag(h, TAG_SMALL, 0, NULL); 490 if (MAN_SB == n->tok) 491 print_otag(h, TAG_B, 0, NULL); 492 return(1); 493 } 494 495 496 /* ARGSUSED */ 497 static int 498 man_SS_pre(MAN_ARGS) 499 { 500 struct htmlpair tag; 501 502 if (MAN_BLOCK == n->type) { 503 PAIR_CLASS_INIT(&tag, "subsection"); 504 print_otag(h, TAG_DIV, 1, &tag); 505 return(1); 506 } else if (MAN_BODY == n->type) 507 return(1); 508 509 print_otag(h, TAG_H2, 0, NULL); 510 return(1); 511 } 512 513 514 /* ARGSUSED */ 515 static int 516 man_PP_pre(MAN_ARGS) 517 { 518 519 if (MAN_HEAD == n->type) 520 return(0); 521 else if (MAN_BODY == n->type && n->prev) 522 print_otag(h, TAG_P, 0, NULL); 523 524 return(1); 525 } 526 527 528 /* ARGSUSED */ 529 static int 530 man_IP_pre(MAN_ARGS) 531 { 532 struct roffsu su; 533 struct htmlpair tag; 534 const struct man_node *nn; 535 536 /* 537 * This scattering of 1-BU margins and pads is to make sure that 538 * when text overruns its box, the subsequent text isn't flush 539 * up against it. However, the rest of the right-hand box must 540 * also be adjusted in consideration of this 1-BU space. 541 */ 542 543 if (MAN_BODY == n->type) { 544 print_otag(h, TAG_TD, 0, NULL); 545 return(1); 546 } 547 548 nn = MAN_BLOCK == n->type ? 549 n->head->child : n->parent->head->child; 550 551 SCALE_HS_INIT(&su, INDENT); 552 553 /* Width is the second token. */ 554 555 if (MAN_IP == n->tok && NULL != nn) 556 if (NULL != (nn = nn->next)) 557 a2width(nn, &su); 558 559 /* Width is the first token. */ 560 561 if (MAN_TP == n->tok && NULL != nn) { 562 /* Skip past non-text children. */ 563 while (nn && MAN_TEXT != nn->type) 564 nn = nn->next; 565 if (nn) 566 a2width(nn, &su); 567 } 568 569 if (MAN_BLOCK == n->type) { 570 print_otag(h, TAG_P, 0, NULL); 571 print_otag(h, TAG_TABLE, 0, NULL); 572 bufcat_su(h, "width", &su); 573 PAIR_STYLE_INIT(&tag, h); 574 print_otag(h, TAG_COL, 1, &tag); 575 print_otag(h, TAG_COL, 0, NULL); 576 print_otag(h, TAG_TBODY, 0, NULL); 577 print_otag(h, TAG_TR, 0, NULL); 578 return(1); 579 } 580 581 print_otag(h, TAG_TD, 0, NULL); 582 583 /* For IP, only print the first header element. */ 584 585 if (MAN_IP == n->tok && n->child) 586 print_man_node(m, n->child, mh, h); 587 588 /* For TP, only print next-line header elements. */ 589 590 if (MAN_TP == n->tok) 591 for (nn = n->child; nn; nn = nn->next) 592 if (nn->line > n->line) 593 print_man_node(m, nn, mh, h); 594 595 return(0); 596 } 597 598 599 /* ARGSUSED */ 600 static int 601 man_HP_pre(MAN_ARGS) 602 { 603 struct htmlpair tag; 604 struct roffsu su; 605 const struct man_node *np; 606 607 np = MAN_BLOCK == n->type ? 608 n->head->child : 609 n->parent->head->child; 610 611 if (NULL == np || ! a2width(np, &su)) 612 SCALE_HS_INIT(&su, INDENT); 613 614 if (MAN_HEAD == n->type) { 615 print_otag(h, TAG_TD, 0, NULL); 616 return(0); 617 } else if (MAN_BLOCK == n->type) { 618 print_otag(h, TAG_P, 0, NULL); 619 print_otag(h, TAG_TABLE, 0, NULL); 620 bufcat_su(h, "width", &su); 621 PAIR_STYLE_INIT(&tag, h); 622 print_otag(h, TAG_COL, 1, &tag); 623 print_otag(h, TAG_COL, 0, NULL); 624 print_otag(h, TAG_TBODY, 0, NULL); 625 print_otag(h, TAG_TR, 0, NULL); 626 return(1); 627 } 628 629 su.scale = -su.scale; 630 bufcat_su(h, "text-indent", &su); 631 PAIR_STYLE_INIT(&tag, h); 632 print_otag(h, TAG_TD, 1, &tag); 633 return(1); 634 } 635 636 637 /* ARGSUSED */ 638 static int 639 man_B_pre(MAN_ARGS) 640 { 641 642 print_otag(h, TAG_B, 0, NULL); 643 return(1); 644 } 645 646 647 /* ARGSUSED */ 648 static int 649 man_I_pre(MAN_ARGS) 650 { 651 652 print_otag(h, TAG_I, 0, NULL); 653 return(1); 654 } 655 656 657 /* ARGSUSED */ 658 static int 659 man_literal_pre(MAN_ARGS) 660 { 661 662 if (MAN_nf == n->tok) { 663 print_otag(h, TAG_BR, 0, NULL); 664 mh->fl |= MANH_LITERAL; 665 } else 666 mh->fl &= ~MANH_LITERAL; 667 668 return(0); 669 } 670 671 672 /* ARGSUSED */ 673 static int 674 man_in_pre(MAN_ARGS) 675 { 676 677 print_otag(h, TAG_BR, 0, NULL); 678 return(0); 679 } 680 681 682 /* ARGSUSED */ 683 static int 684 man_ign_pre(MAN_ARGS) 685 { 686 687 return(0); 688 } 689 690 691 /* ARGSUSED */ 692 static int 693 man_RS_pre(MAN_ARGS) 694 { 695 struct htmlpair tag; 696 struct roffsu su; 697 698 if (MAN_HEAD == n->type) 699 return(0); 700 else if (MAN_BODY == n->type) 701 return(1); 702 703 SCALE_HS_INIT(&su, INDENT); 704 if (n->head->child) 705 a2width(n->head->child, &su); 706 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