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