1 /* $OpenBSD: man_html.c,v 1.82 2017/01/21 01:20:29 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2013, 2014, 2015, 2017 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 AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 #include <sys/types.h> 19 20 #include <assert.h> 21 #include <ctype.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "mandoc_aux.h" 27 #include "roff.h" 28 #include "man.h" 29 #include "out.h" 30 #include "html.h" 31 #include "main.h" 32 33 /* TODO: preserve ident widths. */ 34 /* FIXME: have PD set the default vspace width. */ 35 36 #define INDENT 5 37 38 #define MAN_ARGS const struct roff_meta *man, \ 39 const struct roff_node *n, \ 40 struct mhtml *mh, \ 41 struct html *h 42 43 struct mhtml { 44 int fl; 45 #define MANH_LITERAL (1 << 0) /* literal context */ 46 }; 47 48 struct htmlman { 49 int (*pre)(MAN_ARGS); 50 int (*post)(MAN_ARGS); 51 }; 52 53 static void print_bvspace(struct html *, 54 const struct roff_node *); 55 static void print_man_head(MAN_ARGS); 56 static void print_man_nodelist(MAN_ARGS); 57 static void print_man_node(MAN_ARGS); 58 static int a2width(const struct roff_node *, 59 struct roffsu *); 60 static int man_B_pre(MAN_ARGS); 61 static int man_HP_pre(MAN_ARGS); 62 static int man_IP_pre(MAN_ARGS); 63 static int man_I_pre(MAN_ARGS); 64 static int man_OP_pre(MAN_ARGS); 65 static int man_PP_pre(MAN_ARGS); 66 static int man_RS_pre(MAN_ARGS); 67 static int man_SH_pre(MAN_ARGS); 68 static int man_SM_pre(MAN_ARGS); 69 static int man_SS_pre(MAN_ARGS); 70 static int man_UR_pre(MAN_ARGS); 71 static int man_alt_pre(MAN_ARGS); 72 static int man_br_pre(MAN_ARGS); 73 static int man_ign_pre(MAN_ARGS); 74 static int man_in_pre(MAN_ARGS); 75 static int man_literal_pre(MAN_ARGS); 76 static void man_root_post(MAN_ARGS); 77 static void man_root_pre(MAN_ARGS); 78 79 static const struct htmlman mans[MAN_MAX] = { 80 { man_br_pre, NULL }, /* br */ 81 { NULL, NULL }, /* TH */ 82 { man_SH_pre, NULL }, /* SH */ 83 { man_SS_pre, NULL }, /* SS */ 84 { man_IP_pre, NULL }, /* TP */ 85 { man_PP_pre, NULL }, /* LP */ 86 { man_PP_pre, NULL }, /* PP */ 87 { man_PP_pre, NULL }, /* P */ 88 { man_IP_pre, NULL }, /* IP */ 89 { man_HP_pre, NULL }, /* HP */ 90 { man_SM_pre, NULL }, /* SM */ 91 { man_SM_pre, NULL }, /* SB */ 92 { man_alt_pre, NULL }, /* BI */ 93 { man_alt_pre, NULL }, /* IB */ 94 { man_alt_pre, NULL }, /* BR */ 95 { man_alt_pre, NULL }, /* RB */ 96 { NULL, NULL }, /* R */ 97 { man_B_pre, NULL }, /* B */ 98 { man_I_pre, NULL }, /* I */ 99 { man_alt_pre, NULL }, /* IR */ 100 { man_alt_pre, NULL }, /* RI */ 101 { man_br_pre, NULL }, /* sp */ 102 { man_literal_pre, NULL }, /* nf */ 103 { man_literal_pre, NULL }, /* fi */ 104 { NULL, NULL }, /* RE */ 105 { man_RS_pre, NULL }, /* RS */ 106 { man_ign_pre, NULL }, /* DT */ 107 { man_ign_pre, NULL }, /* UC */ 108 { man_ign_pre, NULL }, /* PD */ 109 { man_ign_pre, NULL }, /* AT */ 110 { man_in_pre, NULL }, /* in */ 111 { man_ign_pre, NULL }, /* ft */ 112 { man_OP_pre, NULL }, /* OP */ 113 { man_literal_pre, NULL }, /* EX */ 114 { man_literal_pre, NULL }, /* EE */ 115 { man_UR_pre, NULL }, /* UR */ 116 { NULL, NULL }, /* UE */ 117 { man_ign_pre, NULL }, /* ll */ 118 }; 119 120 121 /* 122 * Printing leading vertical space before a block. 123 * This is used for the paragraph macros. 124 * The rules are pretty simple, since there's very little nesting going 125 * on here. Basically, if we're the first within another block (SS/SH), 126 * then don't emit vertical space. If we are (RS), then do. If not the 127 * first, print it. 128 */ 129 static void 130 print_bvspace(struct html *h, const struct roff_node *n) 131 { 132 133 if (n->body && n->body->child) 134 if (n->body->child->type == ROFFT_TBL) 135 return; 136 137 if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) 138 if (NULL == n->prev) 139 return; 140 141 print_paragraph(h); 142 } 143 144 void 145 html_man(void *arg, const struct roff_man *man) 146 { 147 struct mhtml mh; 148 struct html *h; 149 struct tag *t; 150 151 memset(&mh, 0, sizeof(mh)); 152 h = (struct html *)arg; 153 154 if ((h->oflags & HTML_FRAGMENT) == 0) { 155 print_gen_decls(h); 156 print_otag(h, TAG_HTML, ""); 157 t = print_otag(h, TAG_HEAD, ""); 158 print_man_head(&man->meta, man->first, &mh, h); 159 print_tagq(h, t); 160 print_otag(h, TAG_BODY, ""); 161 } 162 163 man_root_pre(&man->meta, man->first, &mh, h); 164 t = print_otag(h, TAG_DIV, "c", "manual-text"); 165 print_man_nodelist(&man->meta, man->first->child, &mh, h); 166 print_tagq(h, t); 167 man_root_post(&man->meta, man->first, &mh, h); 168 print_tagq(h, NULL); 169 } 170 171 static void 172 print_man_head(MAN_ARGS) 173 { 174 char *cp; 175 176 print_gen_head(h); 177 mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec); 178 print_otag(h, TAG_TITLE, ""); 179 print_text(h, cp); 180 free(cp); 181 } 182 183 static void 184 print_man_nodelist(MAN_ARGS) 185 { 186 187 while (n != NULL) { 188 print_man_node(man, n, mh, h); 189 n = n->next; 190 } 191 } 192 193 static void 194 print_man_node(MAN_ARGS) 195 { 196 int child; 197 struct tag *t; 198 199 child = 1; 200 t = h->tags.head; 201 202 switch (n->type) { 203 case ROFFT_TEXT: 204 if ('\0' == *n->string) { 205 print_paragraph(h); 206 return; 207 } 208 if (n->flags & NODE_LINE && (*n->string == ' ' || 209 (n->prev != NULL && mh->fl & MANH_LITERAL && 210 ! (h->flags & HTML_NONEWLINE)))) 211 print_otag(h, TAG_BR, ""); 212 print_text(h, n->string); 213 return; 214 case ROFFT_EQN: 215 print_eqn(h, n->eqn); 216 break; 217 case ROFFT_TBL: 218 /* 219 * This will take care of initialising all of the table 220 * state data for the first table, then tearing it down 221 * for the last one. 222 */ 223 print_tbl(h, n->span); 224 return; 225 default: 226 /* 227 * Close out scope of font prior to opening a macro 228 * scope. 229 */ 230 if (HTMLFONT_NONE != h->metac) { 231 h->metal = h->metac; 232 h->metac = HTMLFONT_NONE; 233 } 234 235 /* 236 * Close out the current table, if it's open, and unset 237 * the "meta" table state. This will be reopened on the 238 * next table element. 239 */ 240 if (h->tblt) { 241 print_tblclose(h); 242 t = h->tags.head; 243 } 244 if (mans[n->tok].pre) 245 child = (*mans[n->tok].pre)(man, n, mh, h); 246 break; 247 } 248 249 if (child && n->child) 250 print_man_nodelist(man, n->child, mh, h); 251 252 /* This will automatically close out any font scope. */ 253 print_stagq(h, t); 254 255 switch (n->type) { 256 case ROFFT_EQN: 257 break; 258 default: 259 if (mans[n->tok].post) 260 (*mans[n->tok].post)(man, n, mh, h); 261 break; 262 } 263 } 264 265 static int 266 a2width(const struct roff_node *n, struct roffsu *su) 267 { 268 269 if (n->type != ROFFT_TEXT) 270 return 0; 271 if (a2roffsu(n->string, su, SCALE_EN)) 272 return 1; 273 274 return 0; 275 } 276 277 static void 278 man_root_pre(MAN_ARGS) 279 { 280 struct tag *t, *tt; 281 char *title; 282 283 assert(man->title); 284 assert(man->msec); 285 mandoc_asprintf(&title, "%s(%s)", man->title, man->msec); 286 287 t = print_otag(h, TAG_TABLE, "c", "head"); 288 print_otag(h, TAG_TBODY, ""); 289 tt = print_otag(h, TAG_TR, ""); 290 291 print_otag(h, TAG_TD, "c", "head-ltitle"); 292 print_text(h, title); 293 print_stagq(h, tt); 294 295 print_otag(h, TAG_TD, "c", "head-vol"); 296 if (NULL != man->vol) 297 print_text(h, man->vol); 298 print_stagq(h, tt); 299 300 print_otag(h, TAG_TD, "c", "head-rtitle"); 301 print_text(h, title); 302 print_tagq(h, t); 303 free(title); 304 } 305 306 static void 307 man_root_post(MAN_ARGS) 308 { 309 struct tag *t, *tt; 310 311 t = print_otag(h, TAG_TABLE, "c", "foot"); 312 tt = print_otag(h, TAG_TR, ""); 313 314 print_otag(h, TAG_TD, "c", "foot-date"); 315 print_text(h, man->date); 316 print_stagq(h, tt); 317 318 print_otag(h, TAG_TD, "c", "foot-os"); 319 if (man->os) 320 print_text(h, man->os); 321 print_tagq(h, t); 322 } 323 324 325 static int 326 man_br_pre(MAN_ARGS) 327 { 328 struct roffsu su; 329 330 SCALE_VS_INIT(&su, 1); 331 332 if (MAN_sp == n->tok) { 333 if (NULL != (n = n->child)) 334 if ( ! a2roffsu(n->string, &su, SCALE_VS)) 335 su.scale = 1.0; 336 } else 337 su.scale = 0.0; 338 339 print_otag(h, TAG_DIV, "suh", &su); 340 341 /* So the div isn't empty: */ 342 print_text(h, "\\~"); 343 344 return 0; 345 } 346 347 static int 348 man_SH_pre(MAN_ARGS) 349 { 350 if (n->type == ROFFT_BLOCK) { 351 mh->fl &= ~MANH_LITERAL; 352 return 1; 353 } else if (n->type == ROFFT_BODY) 354 return 1; 355 356 print_otag(h, TAG_H1, "c", "Sh"); 357 return 1; 358 } 359 360 static int 361 man_alt_pre(MAN_ARGS) 362 { 363 const struct roff_node *nn; 364 int i, savelit; 365 enum htmltag fp; 366 struct tag *t; 367 368 if ((savelit = mh->fl & MANH_LITERAL)) 369 print_otag(h, TAG_BR, ""); 370 371 mh->fl &= ~MANH_LITERAL; 372 373 for (i = 0, nn = n->child; nn; nn = nn->next, i++) { 374 t = NULL; 375 switch (n->tok) { 376 case MAN_BI: 377 fp = i % 2 ? TAG_I : TAG_B; 378 break; 379 case MAN_IB: 380 fp = i % 2 ? TAG_B : TAG_I; 381 break; 382 case MAN_RI: 383 fp = i % 2 ? TAG_I : TAG_MAX; 384 break; 385 case MAN_IR: 386 fp = i % 2 ? TAG_MAX : TAG_I; 387 break; 388 case MAN_BR: 389 fp = i % 2 ? TAG_MAX : TAG_B; 390 break; 391 case MAN_RB: 392 fp = i % 2 ? TAG_B : TAG_MAX; 393 break; 394 default: 395 abort(); 396 } 397 398 if (i) 399 h->flags |= HTML_NOSPACE; 400 401 if (TAG_MAX != fp) 402 t = print_otag(h, fp, ""); 403 404 print_man_node(man, nn, mh, h); 405 406 if (t) 407 print_tagq(h, t); 408 } 409 410 if (savelit) 411 mh->fl |= MANH_LITERAL; 412 413 return 0; 414 } 415 416 static int 417 man_SM_pre(MAN_ARGS) 418 { 419 print_otag(h, TAG_SMALL, ""); 420 if (MAN_SB == n->tok) 421 print_otag(h, TAG_B, ""); 422 return 1; 423 } 424 425 static int 426 man_SS_pre(MAN_ARGS) 427 { 428 if (n->type == ROFFT_BLOCK) { 429 mh->fl &= ~MANH_LITERAL; 430 return 1; 431 } else if (n->type == ROFFT_BODY) 432 return 1; 433 434 print_otag(h, TAG_H2, "c", "Ss"); 435 return 1; 436 } 437 438 static int 439 man_PP_pre(MAN_ARGS) 440 { 441 442 if (n->type == ROFFT_HEAD) 443 return 0; 444 else if (n->type == ROFFT_BLOCK) 445 print_bvspace(h, n); 446 447 return 1; 448 } 449 450 static int 451 man_IP_pre(MAN_ARGS) 452 { 453 const struct roff_node *nn; 454 455 if (n->type == ROFFT_BODY) { 456 print_otag(h, TAG_DD, "c", "It-tag"); 457 return 1; 458 } else if (n->type != ROFFT_HEAD) { 459 print_otag(h, TAG_DL, "c", "Bl-tag"); 460 return 1; 461 } 462 463 /* FIXME: width specification. */ 464 465 print_otag(h, TAG_DT, "c", "It-tag"); 466 467 /* For IP, only print the first header element. */ 468 469 if (MAN_IP == n->tok && n->child) 470 print_man_node(man, n->child, mh, h); 471 472 /* For TP, only print next-line header elements. */ 473 474 if (MAN_TP == n->tok) { 475 nn = n->child; 476 while (NULL != nn && 0 == (NODE_LINE & nn->flags)) 477 nn = nn->next; 478 while (NULL != nn) { 479 print_man_node(man, nn, mh, h); 480 nn = nn->next; 481 } 482 } 483 484 return 0; 485 } 486 487 static int 488 man_HP_pre(MAN_ARGS) 489 { 490 struct roffsu sum, sui; 491 const struct roff_node *np; 492 493 if (n->type == ROFFT_HEAD) 494 return 0; 495 else if (n->type != ROFFT_BLOCK) 496 return 1; 497 498 np = n->head->child; 499 500 if (np == NULL || !a2width(np, &sum)) 501 SCALE_HS_INIT(&sum, INDENT); 502 503 sui.unit = sum.unit; 504 sui.scale = -sum.scale; 505 506 print_bvspace(h, n); 507 print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui); 508 return 1; 509 } 510 511 static int 512 man_OP_pre(MAN_ARGS) 513 { 514 struct tag *tt; 515 516 print_text(h, "["); 517 h->flags |= HTML_NOSPACE; 518 tt = print_otag(h, TAG_SPAN, "c", "Op"); 519 520 if (NULL != (n = n->child)) { 521 print_otag(h, TAG_B, ""); 522 print_text(h, n->string); 523 } 524 525 print_stagq(h, tt); 526 527 if (NULL != n && NULL != n->next) { 528 print_otag(h, TAG_I, ""); 529 print_text(h, n->next->string); 530 } 531 532 print_stagq(h, tt); 533 h->flags |= HTML_NOSPACE; 534 print_text(h, "]"); 535 return 0; 536 } 537 538 static int 539 man_B_pre(MAN_ARGS) 540 { 541 print_otag(h, TAG_B, ""); 542 return 1; 543 } 544 545 static int 546 man_I_pre(MAN_ARGS) 547 { 548 print_otag(h, TAG_I, ""); 549 return 1; 550 } 551 552 static int 553 man_literal_pre(MAN_ARGS) 554 { 555 556 if (MAN_fi == n->tok || MAN_EE == n->tok) { 557 print_otag(h, TAG_BR, ""); 558 mh->fl &= ~MANH_LITERAL; 559 } else 560 mh->fl |= MANH_LITERAL; 561 562 return 0; 563 } 564 565 static int 566 man_in_pre(MAN_ARGS) 567 { 568 print_otag(h, TAG_BR, ""); 569 return 0; 570 } 571 572 static int 573 man_ign_pre(MAN_ARGS) 574 { 575 576 return 0; 577 } 578 579 static int 580 man_RS_pre(MAN_ARGS) 581 { 582 struct roffsu su; 583 584 if (n->type == ROFFT_HEAD) 585 return 0; 586 else if (n->type == ROFFT_BODY) 587 return 1; 588 589 SCALE_HS_INIT(&su, INDENT); 590 if (n->head->child) 591 a2width(n->head->child, &su); 592 593 print_otag(h, TAG_DIV, "sul", &su); 594 return 1; 595 } 596 597 static int 598 man_UR_pre(MAN_ARGS) 599 { 600 n = n->child; 601 assert(n->type == ROFFT_HEAD); 602 if (n->child != NULL) { 603 assert(n->child->type == ROFFT_TEXT); 604 print_otag(h, TAG_A, "ch", "Lk", n->child->string); 605 } 606 607 assert(n->next->type == ROFFT_BODY); 608 if (n->next->child != NULL) 609 n = n->next; 610 611 print_man_nodelist(man, n->child, mh, h); 612 613 return 0; 614 } 615