1 /* $Vendor-Id: mdoc_html.c,v 1.142 2011/01/07 13:20:58 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010 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 #include <unistd.h> 29 30 #include "mandoc.h" 31 #include "out.h" 32 #include "html.h" 33 #include "mdoc.h" 34 #include "main.h" 35 36 #define INDENT 5 37 #define HALFINDENT 3 38 39 #define MDOC_ARGS const struct mdoc_meta *m, \ 40 const struct mdoc_node *n, \ 41 struct html *h 42 43 #ifndef MIN 44 #define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b)) 45 #endif 46 47 struct htmlmdoc { 48 int (*pre)(MDOC_ARGS); 49 void (*post)(MDOC_ARGS); 50 }; 51 52 static void print_mdoc(MDOC_ARGS); 53 static void print_mdoc_head(MDOC_ARGS); 54 static void print_mdoc_node(MDOC_ARGS); 55 static void print_mdoc_nodelist(MDOC_ARGS); 56 static void synopsis_pre(struct html *, 57 const struct mdoc_node *); 58 59 static void a2width(const char *, struct roffsu *); 60 static void a2offs(const char *, struct roffsu *); 61 62 static void mdoc_root_post(MDOC_ARGS); 63 static int mdoc_root_pre(MDOC_ARGS); 64 65 static void mdoc__x_post(MDOC_ARGS); 66 static int mdoc__x_pre(MDOC_ARGS); 67 static int mdoc_ad_pre(MDOC_ARGS); 68 static int mdoc_an_pre(MDOC_ARGS); 69 static int mdoc_ap_pre(MDOC_ARGS); 70 static int mdoc_ar_pre(MDOC_ARGS); 71 static int mdoc_bd_pre(MDOC_ARGS); 72 static int mdoc_bf_pre(MDOC_ARGS); 73 static void mdoc_bk_post(MDOC_ARGS); 74 static int mdoc_bk_pre(MDOC_ARGS); 75 static int mdoc_bl_pre(MDOC_ARGS); 76 static int mdoc_bt_pre(MDOC_ARGS); 77 static int mdoc_bx_pre(MDOC_ARGS); 78 static int mdoc_cd_pre(MDOC_ARGS); 79 static int mdoc_d1_pre(MDOC_ARGS); 80 static int mdoc_dv_pre(MDOC_ARGS); 81 static int mdoc_fa_pre(MDOC_ARGS); 82 static int mdoc_fd_pre(MDOC_ARGS); 83 static int mdoc_fl_pre(MDOC_ARGS); 84 static int mdoc_fn_pre(MDOC_ARGS); 85 static int mdoc_ft_pre(MDOC_ARGS); 86 static int mdoc_em_pre(MDOC_ARGS); 87 static int mdoc_er_pre(MDOC_ARGS); 88 static int mdoc_ev_pre(MDOC_ARGS); 89 static int mdoc_ex_pre(MDOC_ARGS); 90 static void mdoc_fo_post(MDOC_ARGS); 91 static int mdoc_fo_pre(MDOC_ARGS); 92 static int mdoc_ic_pre(MDOC_ARGS); 93 static int mdoc_igndelim_pre(MDOC_ARGS); 94 static int mdoc_in_pre(MDOC_ARGS); 95 static int mdoc_it_pre(MDOC_ARGS); 96 static int mdoc_lb_pre(MDOC_ARGS); 97 static int mdoc_li_pre(MDOC_ARGS); 98 static int mdoc_lk_pre(MDOC_ARGS); 99 static int mdoc_mt_pre(MDOC_ARGS); 100 static int mdoc_ms_pre(MDOC_ARGS); 101 static int mdoc_nd_pre(MDOC_ARGS); 102 static int mdoc_nm_pre(MDOC_ARGS); 103 static int mdoc_ns_pre(MDOC_ARGS); 104 static int mdoc_pa_pre(MDOC_ARGS); 105 static void mdoc_pf_post(MDOC_ARGS); 106 static int mdoc_pp_pre(MDOC_ARGS); 107 static void mdoc_quote_post(MDOC_ARGS); 108 static int mdoc_quote_pre(MDOC_ARGS); 109 static int mdoc_rs_pre(MDOC_ARGS); 110 static int mdoc_rv_pre(MDOC_ARGS); 111 static int mdoc_sh_pre(MDOC_ARGS); 112 static int mdoc_sm_pre(MDOC_ARGS); 113 static int mdoc_sp_pre(MDOC_ARGS); 114 static int mdoc_ss_pre(MDOC_ARGS); 115 static int mdoc_sx_pre(MDOC_ARGS); 116 static int mdoc_sy_pre(MDOC_ARGS); 117 static int mdoc_ud_pre(MDOC_ARGS); 118 static int mdoc_va_pre(MDOC_ARGS); 119 static int mdoc_vt_pre(MDOC_ARGS); 120 static int mdoc_xr_pre(MDOC_ARGS); 121 static int mdoc_xx_pre(MDOC_ARGS); 122 123 static const struct htmlmdoc mdocs[MDOC_MAX] = { 124 {mdoc_ap_pre, NULL}, /* Ap */ 125 {NULL, NULL}, /* Dd */ 126 {NULL, NULL}, /* Dt */ 127 {NULL, NULL}, /* Os */ 128 {mdoc_sh_pre, NULL }, /* Sh */ 129 {mdoc_ss_pre, NULL }, /* Ss */ 130 {mdoc_pp_pre, NULL}, /* Pp */ 131 {mdoc_d1_pre, NULL}, /* D1 */ 132 {mdoc_d1_pre, NULL}, /* Dl */ 133 {mdoc_bd_pre, NULL}, /* Bd */ 134 {NULL, NULL}, /* Ed */ 135 {mdoc_bl_pre, NULL}, /* Bl */ 136 {NULL, NULL}, /* El */ 137 {mdoc_it_pre, NULL}, /* It */ 138 {mdoc_ad_pre, NULL}, /* Ad */ 139 {mdoc_an_pre, NULL}, /* An */ 140 {mdoc_ar_pre, NULL}, /* Ar */ 141 {mdoc_cd_pre, NULL}, /* Cd */ 142 {mdoc_fl_pre, NULL}, /* Cm */ 143 {mdoc_dv_pre, NULL}, /* Dv */ 144 {mdoc_er_pre, NULL}, /* Er */ 145 {mdoc_ev_pre, NULL}, /* Ev */ 146 {mdoc_ex_pre, NULL}, /* Ex */ 147 {mdoc_fa_pre, NULL}, /* Fa */ 148 {mdoc_fd_pre, NULL}, /* Fd */ 149 {mdoc_fl_pre, NULL}, /* Fl */ 150 {mdoc_fn_pre, NULL}, /* Fn */ 151 {mdoc_ft_pre, NULL}, /* Ft */ 152 {mdoc_ic_pre, NULL}, /* Ic */ 153 {mdoc_in_pre, NULL}, /* In */ 154 {mdoc_li_pre, NULL}, /* Li */ 155 {mdoc_nd_pre, NULL}, /* Nd */ 156 {mdoc_nm_pre, NULL}, /* Nm */ 157 {mdoc_quote_pre, mdoc_quote_post}, /* Op */ 158 {NULL, NULL}, /* Ot */ 159 {mdoc_pa_pre, NULL}, /* Pa */ 160 {mdoc_rv_pre, NULL}, /* Rv */ 161 {NULL, NULL}, /* St */ 162 {mdoc_va_pre, NULL}, /* Va */ 163 {mdoc_vt_pre, NULL}, /* Vt */ 164 {mdoc_xr_pre, NULL}, /* Xr */ 165 {mdoc__x_pre, mdoc__x_post}, /* %A */ 166 {mdoc__x_pre, mdoc__x_post}, /* %B */ 167 {mdoc__x_pre, mdoc__x_post}, /* %D */ 168 {mdoc__x_pre, mdoc__x_post}, /* %I */ 169 {mdoc__x_pre, mdoc__x_post}, /* %J */ 170 {mdoc__x_pre, mdoc__x_post}, /* %N */ 171 {mdoc__x_pre, mdoc__x_post}, /* %O */ 172 {mdoc__x_pre, mdoc__x_post}, /* %P */ 173 {mdoc__x_pre, mdoc__x_post}, /* %R */ 174 {mdoc__x_pre, mdoc__x_post}, /* %T */ 175 {mdoc__x_pre, mdoc__x_post}, /* %V */ 176 {NULL, NULL}, /* Ac */ 177 {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ 178 {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ 179 {NULL, NULL}, /* At */ 180 {NULL, NULL}, /* Bc */ 181 {mdoc_bf_pre, NULL}, /* Bf */ 182 {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ 183 {mdoc_quote_pre, mdoc_quote_post}, /* Bq */ 184 {mdoc_xx_pre, NULL}, /* Bsx */ 185 {mdoc_bx_pre, NULL}, /* Bx */ 186 {NULL, NULL}, /* Db */ 187 {NULL, NULL}, /* Dc */ 188 {mdoc_quote_pre, mdoc_quote_post}, /* Do */ 189 {mdoc_quote_pre, mdoc_quote_post}, /* Dq */ 190 {NULL, NULL}, /* Ec */ /* FIXME: no space */ 191 {NULL, NULL}, /* Ef */ 192 {mdoc_em_pre, NULL}, /* Em */ 193 {NULL, NULL}, /* Eo */ 194 {mdoc_xx_pre, NULL}, /* Fx */ 195 {mdoc_ms_pre, NULL}, /* Ms */ 196 {mdoc_igndelim_pre, NULL}, /* No */ 197 {mdoc_ns_pre, NULL}, /* Ns */ 198 {mdoc_xx_pre, NULL}, /* Nx */ 199 {mdoc_xx_pre, NULL}, /* Ox */ 200 {NULL, NULL}, /* Pc */ 201 {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */ 202 {mdoc_quote_pre, mdoc_quote_post}, /* Po */ 203 {mdoc_quote_pre, mdoc_quote_post}, /* Pq */ 204 {NULL, NULL}, /* Qc */ 205 {mdoc_quote_pre, mdoc_quote_post}, /* Ql */ 206 {mdoc_quote_pre, mdoc_quote_post}, /* Qo */ 207 {mdoc_quote_pre, mdoc_quote_post}, /* Qq */ 208 {NULL, NULL}, /* Re */ 209 {mdoc_rs_pre, NULL}, /* Rs */ 210 {NULL, NULL}, /* Sc */ 211 {mdoc_quote_pre, mdoc_quote_post}, /* So */ 212 {mdoc_quote_pre, mdoc_quote_post}, /* Sq */ 213 {mdoc_sm_pre, NULL}, /* Sm */ 214 {mdoc_sx_pre, NULL}, /* Sx */ 215 {mdoc_sy_pre, NULL}, /* Sy */ 216 {NULL, NULL}, /* Tn */ 217 {mdoc_xx_pre, NULL}, /* Ux */ 218 {NULL, NULL}, /* Xc */ 219 {NULL, NULL}, /* Xo */ 220 {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ 221 {NULL, NULL}, /* Fc */ 222 {mdoc_quote_pre, mdoc_quote_post}, /* Oo */ 223 {NULL, NULL}, /* Oc */ 224 {mdoc_bk_pre, mdoc_bk_post}, /* Bk */ 225 {NULL, NULL}, /* Ek */ 226 {mdoc_bt_pre, NULL}, /* Bt */ 227 {NULL, NULL}, /* Hf */ 228 {NULL, NULL}, /* Fr */ 229 {mdoc_ud_pre, NULL}, /* Ud */ 230 {mdoc_lb_pre, NULL}, /* Lb */ 231 {mdoc_pp_pre, NULL}, /* Lp */ 232 {mdoc_lk_pre, NULL}, /* Lk */ 233 {mdoc_mt_pre, NULL}, /* Mt */ 234 {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ 235 {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ 236 {NULL, NULL}, /* Brc */ 237 {mdoc__x_pre, mdoc__x_post}, /* %C */ 238 {NULL, NULL}, /* Es */ /* TODO */ 239 {NULL, NULL}, /* En */ /* TODO */ 240 {mdoc_xx_pre, NULL}, /* Dx */ 241 {mdoc__x_pre, mdoc__x_post}, /* %Q */ 242 {mdoc_sp_pre, NULL}, /* br */ 243 {mdoc_sp_pre, NULL}, /* sp */ 244 {mdoc__x_pre, mdoc__x_post}, /* %U */ 245 {NULL, NULL}, /* Ta */ 246 }; 247 248 static const char * const lists[LIST_MAX] = { 249 NULL, 250 "list-bul", 251 "list-col", 252 "list-dash", 253 "list-diag", 254 "list-enum", 255 "list-hang", 256 "list-hyph", 257 "list-inset", 258 "list-item", 259 "list-ohang", 260 "list-tag" 261 }; 262 263 void 264 html_mdoc(void *arg, const struct mdoc *m) 265 { 266 struct html *h; 267 struct tag *t; 268 269 h = (struct html *)arg; 270 271 print_gen_decls(h); 272 t = print_otag(h, TAG_HTML, 0, NULL); 273 print_mdoc(mdoc_meta(m), mdoc_node(m), h); 274 print_tagq(h, t); 275 276 printf("\n"); 277 } 278 279 280 /* 281 * Calculate the scaling unit passed in a `-width' argument. This uses 282 * either a native scaling unit (e.g., 1i, 2m) or the string length of 283 * the value. 284 */ 285 static void 286 a2width(const char *p, struct roffsu *su) 287 { 288 289 if ( ! a2roffsu(p, su, SCALE_MAX)) { 290 su->unit = SCALE_BU; 291 su->scale = (int)strlen(p); 292 } 293 } 294 295 296 /* 297 * See the same function in mdoc_term.c for documentation. 298 */ 299 static void 300 synopsis_pre(struct html *h, const struct mdoc_node *n) 301 { 302 303 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 304 return; 305 306 if (n->prev->tok == n->tok && 307 MDOC_Fo != n->tok && 308 MDOC_Ft != n->tok && 309 MDOC_Fn != n->tok) { 310 print_otag(h, TAG_BR, 0, NULL); 311 return; 312 } 313 314 switch (n->prev->tok) { 315 case (MDOC_Fd): 316 /* FALLTHROUGH */ 317 case (MDOC_Fn): 318 /* FALLTHROUGH */ 319 case (MDOC_Fo): 320 /* FALLTHROUGH */ 321 case (MDOC_In): 322 /* FALLTHROUGH */ 323 case (MDOC_Vt): 324 print_otag(h, TAG_P, 0, NULL); 325 break; 326 case (MDOC_Ft): 327 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 328 print_otag(h, TAG_P, 0, NULL); 329 break; 330 } 331 /* FALLTHROUGH */ 332 default: 333 print_otag(h, TAG_BR, 0, NULL); 334 break; 335 } 336 } 337 338 339 /* 340 * Calculate the scaling unit passed in an `-offset' argument. This 341 * uses either a native scaling unit (e.g., 1i, 2m), one of a set of 342 * predefined strings (indent, etc.), or the string length of the value. 343 */ 344 static void 345 a2offs(const char *p, struct roffsu *su) 346 { 347 348 /* FIXME: "right"? */ 349 350 if (0 == strcmp(p, "left")) 351 SCALE_HS_INIT(su, 0); 352 else if (0 == strcmp(p, "indent")) 353 SCALE_HS_INIT(su, INDENT); 354 else if (0 == strcmp(p, "indent-two")) 355 SCALE_HS_INIT(su, INDENT * 2); 356 else if ( ! a2roffsu(p, su, SCALE_MAX)) { 357 su->unit = SCALE_BU; 358 su->scale = (int)strlen(p); 359 } 360 } 361 362 363 static void 364 print_mdoc(MDOC_ARGS) 365 { 366 struct tag *t; 367 368 t = print_otag(h, TAG_HEAD, 0, NULL); 369 print_mdoc_head(m, n, h); 370 print_tagq(h, t); 371 372 t = print_otag(h, TAG_BODY, 0, NULL); 373 print_mdoc_nodelist(m, n, h); 374 print_tagq(h, t); 375 } 376 377 378 /* ARGSUSED */ 379 static void 380 print_mdoc_head(MDOC_ARGS) 381 { 382 383 print_gen_head(h); 384 bufinit(h); 385 buffmt(h, "%s(%s)", m->title, m->msec); 386 387 if (m->arch) { 388 bufcat(h, " ("); 389 bufcat(h, m->arch); 390 bufcat(h, ")"); 391 } 392 393 print_otag(h, TAG_TITLE, 0, NULL); 394 print_text(h, h->buf); 395 } 396 397 398 static void 399 print_mdoc_nodelist(MDOC_ARGS) 400 { 401 402 print_mdoc_node(m, n, h); 403 if (n->next) 404 print_mdoc_nodelist(m, n->next, h); 405 } 406 407 408 static void 409 print_mdoc_node(MDOC_ARGS) 410 { 411 int child; 412 struct tag *t; 413 414 child = 1; 415 t = h->tags.head; 416 417 bufinit(h); 418 switch (n->type) { 419 case (MDOC_ROOT): 420 child = mdoc_root_pre(m, n, h); 421 break; 422 case (MDOC_TEXT): 423 print_text(h, n->string); 424 return; 425 case (MDOC_TBL): 426 print_tbl(h, n->span); 427 break; 428 default: 429 if (mdocs[n->tok].pre && ENDBODY_NOT == n->end) 430 child = (*mdocs[n->tok].pre)(m, n, h); 431 break; 432 } 433 434 if (HTML_KEEP & h->flags) { 435 if (n->prev && n->prev->line != n->line) { 436 h->flags &= ~HTML_KEEP; 437 h->flags |= HTML_PREKEEP; 438 } else if (NULL == n->prev) { 439 if (n->parent && n->parent->line != n->line) { 440 h->flags &= ~HTML_KEEP; 441 h->flags |= HTML_PREKEEP; 442 } 443 } 444 } 445 446 if (child && n->child) 447 print_mdoc_nodelist(m, n->child, h); 448 449 print_stagq(h, t); 450 451 bufinit(h); 452 switch (n->type) { 453 case (MDOC_ROOT): 454 mdoc_root_post(m, n, h); 455 break; 456 case (MDOC_TBL): 457 break; 458 default: 459 if (mdocs[n->tok].post && ENDBODY_NOT == n->end) 460 (*mdocs[n->tok].post)(m, n, h); 461 break; 462 } 463 } 464 465 /* ARGSUSED */ 466 static void 467 mdoc_root_post(MDOC_ARGS) 468 { 469 struct htmlpair tag[3]; 470 struct tag *t, *tt; 471 char b[DATESIZ]; 472 473 time2a(m->date, b, DATESIZ); 474 475 PAIR_SUMMARY_INIT(&tag[0], "Document Footer"); 476 PAIR_CLASS_INIT(&tag[1], "foot"); 477 if (NULL == h->style) { 478 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 479 t = print_otag(h, TAG_TABLE, 3, tag); 480 PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); 481 print_otag(h, TAG_COL, 1, tag); 482 print_otag(h, TAG_COL, 1, tag); 483 } else 484 t = print_otag(h, TAG_TABLE, 2, tag); 485 486 t = print_otag(h, TAG_TBODY, 0, NULL); 487 488 tt = print_otag(h, TAG_TR, 0, NULL); 489 490 PAIR_CLASS_INIT(&tag[0], "foot-date"); 491 print_otag(h, TAG_TD, 1, tag); 492 493 print_text(h, b); 494 print_stagq(h, tt); 495 496 PAIR_CLASS_INIT(&tag[0], "foot-os"); 497 if (NULL == h->style) { 498 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 499 print_otag(h, TAG_TD, 2, tag); 500 } else 501 print_otag(h, TAG_TD, 1, tag); 502 503 print_text(h, m->os); 504 print_tagq(h, t); 505 } 506 507 508 /* ARGSUSED */ 509 static int 510 mdoc_root_pre(MDOC_ARGS) 511 { 512 struct htmlpair tag[3]; 513 struct tag *t, *tt; 514 char b[BUFSIZ], title[BUFSIZ]; 515 516 strlcpy(b, m->vol, BUFSIZ); 517 518 if (m->arch) { 519 strlcat(b, " (", BUFSIZ); 520 strlcat(b, m->arch, BUFSIZ); 521 strlcat(b, ")", BUFSIZ); 522 } 523 524 snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); 525 526 PAIR_SUMMARY_INIT(&tag[0], "Document Header"); 527 PAIR_CLASS_INIT(&tag[1], "head"); 528 if (NULL == h->style) { 529 PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); 530 t = print_otag(h, TAG_TABLE, 3, tag); 531 PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); 532 print_otag(h, TAG_COL, 1, tag); 533 print_otag(h, TAG_COL, 1, tag); 534 print_otag(h, TAG_COL, 1, tag); 535 } else 536 t = print_otag(h, TAG_TABLE, 2, tag); 537 538 print_otag(h, TAG_TBODY, 0, NULL); 539 540 tt = print_otag(h, TAG_TR, 0, NULL); 541 542 PAIR_CLASS_INIT(&tag[0], "head-ltitle"); 543 print_otag(h, TAG_TD, 1, tag); 544 545 print_text(h, title); 546 print_stagq(h, tt); 547 548 PAIR_CLASS_INIT(&tag[0], "head-vol"); 549 if (NULL == h->style) { 550 PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); 551 print_otag(h, TAG_TD, 2, tag); 552 } else 553 print_otag(h, TAG_TD, 1, tag); 554 555 print_text(h, b); 556 print_stagq(h, tt); 557 558 PAIR_CLASS_INIT(&tag[0], "head-rtitle"); 559 if (NULL == h->style) { 560 PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); 561 print_otag(h, TAG_TD, 2, tag); 562 } else 563 print_otag(h, TAG_TD, 1, tag); 564 565 print_text(h, title); 566 print_tagq(h, t); 567 return(1); 568 } 569 570 571 /* ARGSUSED */ 572 static int 573 mdoc_sh_pre(MDOC_ARGS) 574 { 575 struct htmlpair tag; 576 char buf[BUFSIZ]; 577 578 if (MDOC_BLOCK == n->type) { 579 PAIR_CLASS_INIT(&tag, "section"); 580 print_otag(h, TAG_DIV, 1, &tag); 581 return(1); 582 } else if (MDOC_BODY == n->type) 583 return(1); 584 585 buf[0] = '\0'; 586 for (n = n->child; n; n = n->next) { 587 html_idcat(buf, n->string, BUFSIZ); 588 if (n->next) 589 html_idcat(buf, " ", BUFSIZ); 590 } 591 592 PAIR_ID_INIT(&tag, buf); 593 print_otag(h, TAG_H1, 1, &tag); 594 return(1); 595 } 596 597 598 /* ARGSUSED */ 599 static int 600 mdoc_ss_pre(MDOC_ARGS) 601 { 602 struct htmlpair tag; 603 char buf[BUFSIZ]; 604 605 if (MDOC_BLOCK == n->type) { 606 PAIR_CLASS_INIT(&tag, "subsection"); 607 print_otag(h, TAG_DIV, 1, &tag); 608 return(1); 609 } else if (MDOC_BODY == n->type) 610 return(1); 611 612 buf[0] = '\0'; 613 for (n = n->child; n; n = n->next) { 614 html_idcat(buf, n->string, BUFSIZ); 615 if (n->next) 616 html_idcat(buf, " ", BUFSIZ); 617 } 618 619 PAIR_ID_INIT(&tag, buf); 620 print_otag(h, TAG_H2, 1, &tag); 621 return(1); 622 } 623 624 625 /* ARGSUSED */ 626 static int 627 mdoc_fl_pre(MDOC_ARGS) 628 { 629 struct htmlpair tag; 630 631 PAIR_CLASS_INIT(&tag, "flag"); 632 print_otag(h, TAG_B, 1, &tag); 633 634 /* `Cm' has no leading hyphen. */ 635 636 if (MDOC_Cm == n->tok) 637 return(1); 638 639 print_text(h, "\\-"); 640 641 if (n->child) 642 h->flags |= HTML_NOSPACE; 643 else if (n->next && n->next->line == n->line) 644 h->flags |= HTML_NOSPACE; 645 646 return(1); 647 } 648 649 650 /* ARGSUSED */ 651 static int 652 mdoc_nd_pre(MDOC_ARGS) 653 { 654 struct htmlpair tag; 655 656 if (MDOC_BODY != n->type) 657 return(1); 658 659 /* XXX: this tag in theory can contain block elements. */ 660 661 print_text(h, "\\(em"); 662 PAIR_CLASS_INIT(&tag, "desc"); 663 print_otag(h, TAG_SPAN, 1, &tag); 664 return(1); 665 } 666 667 668 static int 669 mdoc_nm_pre(MDOC_ARGS) 670 { 671 struct htmlpair tag; 672 struct roffsu su; 673 size_t len; 674 675 switch (n->type) { 676 case (MDOC_ELEM): 677 synopsis_pre(h, n); 678 PAIR_CLASS_INIT(&tag, "name"); 679 print_otag(h, TAG_B, 1, &tag); 680 if (NULL == n->child && m->name) 681 print_text(h, m->name); 682 return(1); 683 case (MDOC_HEAD): 684 print_otag(h, TAG_TD, 0, NULL); 685 if (NULL == n->child && m->name) 686 print_text(h, m->name); 687 return(1); 688 case (MDOC_BODY): 689 print_otag(h, TAG_TD, 0, NULL); 690 return(1); 691 default: 692 break; 693 } 694 695 synopsis_pre(h, n); 696 PAIR_CLASS_INIT(&tag, "synopsis"); 697 print_otag(h, TAG_TABLE, 1, &tag); 698 699 for (len = 0, n = n->child; n; n = n->next) 700 if (MDOC_TEXT == n->type) 701 len += strlen(n->string); 702 703 if (0 == len && m->name) 704 len = strlen(m->name); 705 706 SCALE_HS_INIT(&su, (double)len); 707 bufcat_su(h, "width", &su); 708 PAIR_STYLE_INIT(&tag, h); 709 print_otag(h, TAG_COL, 1, &tag); 710 print_otag(h, TAG_COL, 0, NULL); 711 print_otag(h, TAG_TBODY, 0, NULL); 712 print_otag(h, TAG_TR, 0, NULL); 713 return(1); 714 } 715 716 717 /* ARGSUSED */ 718 static int 719 mdoc_xr_pre(MDOC_ARGS) 720 { 721 struct htmlpair tag[2]; 722 const struct mdoc_node *nn; 723 724 if (NULL == n->child) 725 return(0); 726 727 PAIR_CLASS_INIT(&tag[0], "link-man"); 728 729 if (h->base_man) { 730 buffmt_man(h, n->child->string, 731 n->child->next ? 732 n->child->next->string : NULL); 733 PAIR_HREF_INIT(&tag[1], h->buf); 734 print_otag(h, TAG_A, 2, tag); 735 } else 736 print_otag(h, TAG_A, 1, tag); 737 738 nn = n->child; 739 print_text(h, nn->string); 740 741 if (NULL == (nn = nn->next)) 742 return(0); 743 744 h->flags |= HTML_NOSPACE; 745 print_text(h, "("); 746 h->flags |= HTML_NOSPACE; 747 print_text(h, nn->string); 748 h->flags |= HTML_NOSPACE; 749 print_text(h, ")"); 750 return(0); 751 } 752 753 754 /* ARGSUSED */ 755 static int 756 mdoc_ns_pre(MDOC_ARGS) 757 { 758 759 h->flags |= HTML_NOSPACE; 760 return(1); 761 } 762 763 764 /* ARGSUSED */ 765 static int 766 mdoc_ar_pre(MDOC_ARGS) 767 { 768 struct htmlpair tag; 769 770 PAIR_CLASS_INIT(&tag, "arg"); 771 print_otag(h, TAG_I, 1, &tag); 772 return(1); 773 } 774 775 776 /* ARGSUSED */ 777 static int 778 mdoc_xx_pre(MDOC_ARGS) 779 { 780 const char *pp; 781 struct htmlpair tag; 782 783 switch (n->tok) { 784 case (MDOC_Bsx): 785 pp = "BSD/OS"; 786 break; 787 case (MDOC_Dx): 788 pp = "DragonFly"; 789 break; 790 case (MDOC_Fx): 791 pp = "FreeBSD"; 792 break; 793 case (MDOC_Nx): 794 pp = "NetBSD"; 795 break; 796 case (MDOC_Ox): 797 pp = "OpenBSD"; 798 break; 799 case (MDOC_Ux): 800 pp = "UNIX"; 801 break; 802 default: 803 return(1); 804 } 805 806 PAIR_CLASS_INIT(&tag, "unix"); 807 print_otag(h, TAG_SPAN, 1, &tag); 808 print_text(h, pp); 809 return(1); 810 } 811 812 813 /* ARGSUSED */ 814 static int 815 mdoc_bx_pre(MDOC_ARGS) 816 { 817 const struct mdoc_node *nn; 818 struct htmlpair tag; 819 820 PAIR_CLASS_INIT(&tag, "unix"); 821 print_otag(h, TAG_SPAN, 1, &tag); 822 823 for (nn = n->child; nn; nn = nn->next) 824 print_mdoc_node(m, nn, h); 825 826 if (n->child) 827 h->flags |= HTML_NOSPACE; 828 829 print_text(h, "BSD"); 830 return(0); 831 } 832 833 /* ARGSUSED */ 834 static int 835 mdoc_it_pre(MDOC_ARGS) 836 { 837 struct roffsu su; 838 enum mdoc_list type; 839 struct htmlpair tag[2]; 840 const struct mdoc_node *bl; 841 842 bl = n->parent; 843 while (bl && MDOC_Bl != bl->tok) 844 bl = bl->parent; 845 846 assert(bl); 847 848 type = bl->norm->Bl.type; 849 850 assert(lists[type]); 851 PAIR_CLASS_INIT(&tag[0], lists[type]); 852 853 if (MDOC_HEAD == n->type) { 854 switch (type) { 855 case(LIST_bullet): 856 /* FALLTHROUGH */ 857 case(LIST_dash): 858 /* FALLTHROUGH */ 859 case(LIST_item): 860 /* FALLTHROUGH */ 861 case(LIST_hyphen): 862 /* FALLTHROUGH */ 863 case(LIST_enum): 864 return(0); 865 case(LIST_diag): 866 /* FALLTHROUGH */ 867 case(LIST_hang): 868 /* FALLTHROUGH */ 869 case(LIST_inset): 870 /* FALLTHROUGH */ 871 case(LIST_ohang): 872 /* FALLTHROUGH */ 873 case(LIST_tag): 874 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 875 bufcat_su(h, "margin-top", &su); 876 PAIR_STYLE_INIT(&tag[1], h); 877 print_otag(h, TAG_DT, 2, tag); 878 if (LIST_diag != type) 879 break; 880 PAIR_CLASS_INIT(&tag[0], "diag"); 881 print_otag(h, TAG_B, 1, tag); 882 break; 883 case(LIST_column): 884 break; 885 default: 886 break; 887 } 888 } else if (MDOC_BODY == n->type) { 889 switch (type) { 890 case(LIST_bullet): 891 /* FALLTHROUGH */ 892 case(LIST_hyphen): 893 /* FALLTHROUGH */ 894 case(LIST_dash): 895 /* FALLTHROUGH */ 896 case(LIST_enum): 897 /* FALLTHROUGH */ 898 case(LIST_item): 899 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 900 bufcat_su(h, "margin-top", &su); 901 PAIR_STYLE_INIT(&tag[1], h); 902 print_otag(h, TAG_LI, 2, tag); 903 break; 904 case(LIST_diag): 905 /* FALLTHROUGH */ 906 case(LIST_hang): 907 /* FALLTHROUGH */ 908 case(LIST_inset): 909 /* FALLTHROUGH */ 910 case(LIST_ohang): 911 /* FALLTHROUGH */ 912 case(LIST_tag): 913 if (NULL == bl->norm->Bl.width) { 914 print_otag(h, TAG_DD, 1, tag); 915 break; 916 } 917 a2width(bl->norm->Bl.width, &su); 918 bufcat_su(h, "margin-left", &su); 919 PAIR_STYLE_INIT(&tag[1], h); 920 print_otag(h, TAG_DD, 2, tag); 921 break; 922 case(LIST_column): 923 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 924 bufcat_su(h, "margin-top", &su); 925 PAIR_STYLE_INIT(&tag[1], h); 926 print_otag(h, TAG_TD, 2, tag); 927 break; 928 default: 929 break; 930 } 931 } else { 932 switch (type) { 933 case (LIST_column): 934 print_otag(h, TAG_TR, 1, tag); 935 break; 936 default: 937 break; 938 } 939 } 940 941 return(1); 942 } 943 944 /* ARGSUSED */ 945 static int 946 mdoc_bl_pre(MDOC_ARGS) 947 { 948 int i; 949 struct htmlpair tag[3]; 950 struct roffsu su; 951 char buf[BUFSIZ]; 952 953 if (MDOC_BODY == n->type) { 954 if (LIST_column == n->norm->Bl.type) 955 print_otag(h, TAG_TBODY, 0, NULL); 956 return(1); 957 } 958 959 if (MDOC_HEAD == n->type) { 960 if (LIST_column != n->norm->Bl.type) 961 return(0); 962 963 /* 964 * For each column, print out the <COL> tag with our 965 * suggested width. The last column gets min-width, as 966 * in terminal mode it auto-sizes to the width of the 967 * screen and we want to preserve that behaviour. 968 */ 969 970 for (i = 0; i < (int)n->norm->Bl.ncols; i++) { 971 a2width(n->norm->Bl.cols[i], &su); 972 bufinit(h); 973 if (i < (int)n->norm->Bl.ncols - 1) 974 bufcat_su(h, "width", &su); 975 else 976 bufcat_su(h, "min-width", &su); 977 PAIR_STYLE_INIT(&tag[0], h); 978 print_otag(h, TAG_COL, 1, tag); 979 } 980 981 return(0); 982 } 983 984 SCALE_VS_INIT(&su, 0); 985 bufcat_su(h, "margin-top", &su); 986 bufcat_su(h, "margin-bottom", &su); 987 PAIR_STYLE_INIT(&tag[0], h); 988 989 assert(lists[n->norm->Bl.type]); 990 strlcpy(buf, "list ", BUFSIZ); 991 strlcat(buf, lists[n->norm->Bl.type], BUFSIZ); 992 PAIR_INIT(&tag[1], ATTR_CLASS, buf); 993 994 /* Set the block's left-hand margin. */ 995 996 if (n->norm->Bl.offs) { 997 a2offs(n->norm->Bl.offs, &su); 998 bufcat_su(h, "margin-left", &su); 999 } 1000 1001 switch (n->norm->Bl.type) { 1002 case(LIST_bullet): 1003 /* FALLTHROUGH */ 1004 case(LIST_dash): 1005 /* FALLTHROUGH */ 1006 case(LIST_hyphen): 1007 /* FALLTHROUGH */ 1008 case(LIST_item): 1009 print_otag(h, TAG_UL, 2, tag); 1010 break; 1011 case(LIST_enum): 1012 print_otag(h, TAG_OL, 2, tag); 1013 break; 1014 case(LIST_diag): 1015 /* FALLTHROUGH */ 1016 case(LIST_hang): 1017 /* FALLTHROUGH */ 1018 case(LIST_inset): 1019 /* FALLTHROUGH */ 1020 case(LIST_ohang): 1021 /* FALLTHROUGH */ 1022 case(LIST_tag): 1023 print_otag(h, TAG_DL, 2, tag); 1024 break; 1025 case(LIST_column): 1026 print_otag(h, TAG_TABLE, 2, tag); 1027 break; 1028 default: 1029 abort(); 1030 /* NOTREACHED */ 1031 } 1032 1033 return(1); 1034 } 1035 1036 /* ARGSUSED */ 1037 static int 1038 mdoc_ex_pre(MDOC_ARGS) 1039 { 1040 const struct mdoc_node *nn; 1041 struct tag *t; 1042 struct htmlpair tag; 1043 1044 if (n->prev) 1045 print_otag(h, TAG_BR, 0, NULL); 1046 1047 PAIR_CLASS_INIT(&tag, "utility"); 1048 1049 print_text(h, "The"); 1050 for (nn = n->child; nn; nn = nn->next) { 1051 t = print_otag(h, TAG_B, 1, &tag); 1052 print_text(h, nn->string); 1053 print_tagq(h, t); 1054 1055 h->flags |= HTML_NOSPACE; 1056 1057 if (nn->next && NULL == nn->next->next) 1058 print_text(h, ", and"); 1059 else if (nn->next) 1060 print_text(h, ","); 1061 else 1062 h->flags &= ~HTML_NOSPACE; 1063 } 1064 1065 if (n->child && n->child->next) 1066 print_text(h, "utilities exit"); 1067 else 1068 print_text(h, "utility exits"); 1069 1070 print_text(h, "0 on success, and >0 if an error occurs."); 1071 return(0); 1072 } 1073 1074 1075 /* ARGSUSED */ 1076 static int 1077 mdoc_em_pre(MDOC_ARGS) 1078 { 1079 struct htmlpair tag; 1080 1081 PAIR_CLASS_INIT(&tag, "emph"); 1082 print_otag(h, TAG_SPAN, 1, &tag); 1083 return(1); 1084 } 1085 1086 1087 /* ARGSUSED */ 1088 static int 1089 mdoc_d1_pre(MDOC_ARGS) 1090 { 1091 struct htmlpair tag[2]; 1092 struct roffsu su; 1093 1094 if (MDOC_BLOCK != n->type) 1095 return(1); 1096 1097 SCALE_VS_INIT(&su, 0); 1098 bufcat_su(h, "margin-top", &su); 1099 bufcat_su(h, "margin-bottom", &su); 1100 PAIR_STYLE_INIT(&tag[0], h); 1101 print_otag(h, TAG_BLOCKQUOTE, 1, tag); 1102 1103 /* BLOCKQUOTE needs a block body. */ 1104 1105 PAIR_CLASS_INIT(&tag[0], "display"); 1106 print_otag(h, TAG_DIV, 1, tag); 1107 1108 if (MDOC_Dl == n->tok) { 1109 PAIR_CLASS_INIT(&tag[0], "lit"); 1110 print_otag(h, TAG_CODE, 1, tag); 1111 } 1112 1113 return(1); 1114 } 1115 1116 1117 /* ARGSUSED */ 1118 static int 1119 mdoc_sx_pre(MDOC_ARGS) 1120 { 1121 struct htmlpair tag[2]; 1122 const struct mdoc_node *nn; 1123 char buf[BUFSIZ]; 1124 1125 strlcpy(buf, "#", BUFSIZ); 1126 for (nn = n->child; nn; nn = nn->next) { 1127 html_idcat(buf, nn->string, BUFSIZ); 1128 if (nn->next) 1129 html_idcat(buf, " ", BUFSIZ); 1130 } 1131 1132 PAIR_CLASS_INIT(&tag[0], "link-sec"); 1133 PAIR_HREF_INIT(&tag[1], buf); 1134 1135 print_otag(h, TAG_I, 1, tag); 1136 print_otag(h, TAG_A, 2, tag); 1137 return(1); 1138 } 1139 1140 1141 /* ARGSUSED */ 1142 static int 1143 mdoc_bd_pre(MDOC_ARGS) 1144 { 1145 struct htmlpair tag[2]; 1146 int comp; 1147 const struct mdoc_node *nn; 1148 struct roffsu su; 1149 1150 if (MDOC_HEAD == n->type) 1151 return(0); 1152 1153 if (MDOC_BLOCK == n->type) { 1154 comp = n->norm->Bd.comp; 1155 for (nn = n; nn && ! comp; nn = nn->parent) { 1156 if (MDOC_BLOCK != nn->type) 1157 continue; 1158 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) 1159 comp = 1; 1160 if (nn->prev) 1161 break; 1162 } 1163 if ( ! comp) 1164 print_otag(h, TAG_P, 0, NULL); 1165 return(1); 1166 } 1167 1168 SCALE_HS_INIT(&su, 0); 1169 if (n->norm->Bd.offs) 1170 a2offs(n->norm->Bd.offs, &su); 1171 1172 bufcat_su(h, "margin-left", &su); 1173 PAIR_STYLE_INIT(&tag[0], h); 1174 1175 if (DISP_unfilled != n->norm->Bd.type && 1176 DISP_literal != n->norm->Bd.type) { 1177 PAIR_CLASS_INIT(&tag[1], "display"); 1178 print_otag(h, TAG_DIV, 2, tag); 1179 return(1); 1180 } 1181 1182 PAIR_CLASS_INIT(&tag[1], "lit display"); 1183 print_otag(h, TAG_PRE, 2, tag); 1184 1185 for (nn = n->child; nn; nn = nn->next) { 1186 print_mdoc_node(m, nn, h); 1187 /* 1188 * If the printed node flushes its own line, then we 1189 * needn't do it here as well. This is hacky, but the 1190 * notion of selective eoln whitespace is pretty dumb 1191 * anyway, so don't sweat it. 1192 */ 1193 switch (nn->tok) { 1194 case (MDOC_Sm): 1195 /* FALLTHROUGH */ 1196 case (MDOC_br): 1197 /* FALLTHROUGH */ 1198 case (MDOC_sp): 1199 /* FALLTHROUGH */ 1200 case (MDOC_Bl): 1201 /* FALLTHROUGH */ 1202 case (MDOC_D1): 1203 /* FALLTHROUGH */ 1204 case (MDOC_Dl): 1205 /* FALLTHROUGH */ 1206 case (MDOC_Lp): 1207 /* FALLTHROUGH */ 1208 case (MDOC_Pp): 1209 continue; 1210 default: 1211 break; 1212 } 1213 if (nn->next && nn->next->line == nn->line) 1214 continue; 1215 else if (nn->next) 1216 print_text(h, "\n"); 1217 1218 h->flags |= HTML_NOSPACE; 1219 } 1220 1221 return(0); 1222 } 1223 1224 1225 /* ARGSUSED */ 1226 static int 1227 mdoc_pa_pre(MDOC_ARGS) 1228 { 1229 struct htmlpair tag; 1230 1231 PAIR_CLASS_INIT(&tag, "file"); 1232 print_otag(h, TAG_I, 1, &tag); 1233 return(1); 1234 } 1235 1236 1237 /* ARGSUSED */ 1238 static int 1239 mdoc_ad_pre(MDOC_ARGS) 1240 { 1241 struct htmlpair tag; 1242 1243 PAIR_CLASS_INIT(&tag, "addr"); 1244 print_otag(h, TAG_I, 1, &tag); 1245 return(1); 1246 } 1247 1248 1249 /* ARGSUSED */ 1250 static int 1251 mdoc_an_pre(MDOC_ARGS) 1252 { 1253 struct htmlpair tag; 1254 1255 /* TODO: -split and -nosplit (see termp_an_pre()). */ 1256 1257 PAIR_CLASS_INIT(&tag, "author"); 1258 print_otag(h, TAG_SPAN, 1, &tag); 1259 return(1); 1260 } 1261 1262 1263 /* ARGSUSED */ 1264 static int 1265 mdoc_cd_pre(MDOC_ARGS) 1266 { 1267 struct htmlpair tag; 1268 1269 synopsis_pre(h, n); 1270 PAIR_CLASS_INIT(&tag, "config"); 1271 print_otag(h, TAG_B, 1, &tag); 1272 return(1); 1273 } 1274 1275 1276 /* ARGSUSED */ 1277 static int 1278 mdoc_dv_pre(MDOC_ARGS) 1279 { 1280 struct htmlpair tag; 1281 1282 PAIR_CLASS_INIT(&tag, "define"); 1283 print_otag(h, TAG_SPAN, 1, &tag); 1284 return(1); 1285 } 1286 1287 1288 /* ARGSUSED */ 1289 static int 1290 mdoc_ev_pre(MDOC_ARGS) 1291 { 1292 struct htmlpair tag; 1293 1294 PAIR_CLASS_INIT(&tag, "env"); 1295 print_otag(h, TAG_SPAN, 1, &tag); 1296 return(1); 1297 } 1298 1299 1300 /* ARGSUSED */ 1301 static int 1302 mdoc_er_pre(MDOC_ARGS) 1303 { 1304 struct htmlpair tag; 1305 1306 PAIR_CLASS_INIT(&tag, "errno"); 1307 print_otag(h, TAG_SPAN, 1, &tag); 1308 return(1); 1309 } 1310 1311 1312 /* ARGSUSED */ 1313 static int 1314 mdoc_fa_pre(MDOC_ARGS) 1315 { 1316 const struct mdoc_node *nn; 1317 struct htmlpair tag; 1318 struct tag *t; 1319 1320 PAIR_CLASS_INIT(&tag, "farg"); 1321 if (n->parent->tok != MDOC_Fo) { 1322 print_otag(h, TAG_I, 1, &tag); 1323 return(1); 1324 } 1325 1326 for (nn = n->child; nn; nn = nn->next) { 1327 t = print_otag(h, TAG_I, 1, &tag); 1328 print_text(h, nn->string); 1329 print_tagq(h, t); 1330 if (nn->next) 1331 print_text(h, ","); 1332 } 1333 1334 if (n->child && n->next && n->next->tok == MDOC_Fa) 1335 print_text(h, ","); 1336 1337 return(0); 1338 } 1339 1340 1341 /* ARGSUSED */ 1342 static int 1343 mdoc_fd_pre(MDOC_ARGS) 1344 { 1345 struct htmlpair tag; 1346 1347 synopsis_pre(h, n); 1348 1349 PAIR_CLASS_INIT(&tag, "macro"); 1350 print_otag(h, TAG_B, 1, &tag); 1351 return(1); 1352 } 1353 1354 1355 /* ARGSUSED */ 1356 static int 1357 mdoc_vt_pre(MDOC_ARGS) 1358 { 1359 struct htmlpair tag; 1360 1361 if (MDOC_BLOCK == n->type) { 1362 synopsis_pre(h, n); 1363 return(1); 1364 } else if (MDOC_ELEM == n->type) { 1365 synopsis_pre(h, n); 1366 } else if (MDOC_HEAD == n->type) 1367 return(0); 1368 1369 PAIR_CLASS_INIT(&tag, "type"); 1370 print_otag(h, TAG_SPAN, 1, &tag); 1371 return(1); 1372 } 1373 1374 1375 /* ARGSUSED */ 1376 static int 1377 mdoc_ft_pre(MDOC_ARGS) 1378 { 1379 struct htmlpair tag; 1380 1381 synopsis_pre(h, n); 1382 PAIR_CLASS_INIT(&tag, "ftype"); 1383 print_otag(h, TAG_I, 1, &tag); 1384 return(1); 1385 } 1386 1387 1388 /* ARGSUSED */ 1389 static int 1390 mdoc_fn_pre(MDOC_ARGS) 1391 { 1392 struct tag *t; 1393 struct htmlpair tag[2]; 1394 const struct mdoc_node *nn; 1395 char nbuf[BUFSIZ]; 1396 const char *sp, *ep; 1397 int sz, i; 1398 1399 synopsis_pre(h, n); 1400 1401 /* Split apart into type and name. */ 1402 assert(n->child->string); 1403 sp = n->child->string; 1404 1405 ep = strchr(sp, ' '); 1406 if (NULL != ep) { 1407 PAIR_CLASS_INIT(&tag[0], "ftype"); 1408 t = print_otag(h, TAG_I, 1, tag); 1409 1410 while (ep) { 1411 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1412 (void)memcpy(nbuf, sp, (size_t)sz); 1413 nbuf[sz] = '\0'; 1414 print_text(h, nbuf); 1415 sp = ++ep; 1416 ep = strchr(sp, ' '); 1417 } 1418 print_tagq(h, t); 1419 } 1420 1421 PAIR_CLASS_INIT(&tag[0], "fname"); 1422 1423 /* 1424 * FIXME: only refer to IDs that we know exist. 1425 */ 1426 1427 #if 0 1428 if (MDOC_SYNPRETTY & n->flags) { 1429 nbuf[0] = '\0'; 1430 html_idcat(nbuf, sp, BUFSIZ); 1431 PAIR_ID_INIT(&tag[1], nbuf); 1432 } else { 1433 strlcpy(nbuf, "#", BUFSIZ); 1434 html_idcat(nbuf, sp, BUFSIZ); 1435 PAIR_HREF_INIT(&tag[1], nbuf); 1436 } 1437 #endif 1438 1439 t = print_otag(h, TAG_B, 1, tag); 1440 1441 if (sp) { 1442 strlcpy(nbuf, sp, BUFSIZ); 1443 print_text(h, nbuf); 1444 } 1445 1446 print_tagq(h, t); 1447 1448 h->flags |= HTML_NOSPACE; 1449 print_text(h, "("); 1450 1451 bufinit(h); 1452 PAIR_CLASS_INIT(&tag[0], "farg"); 1453 bufcat_style(h, "white-space", "nowrap"); 1454 PAIR_STYLE_INIT(&tag[1], h); 1455 1456 for (nn = n->child->next; nn; nn = nn->next) { 1457 i = 1; 1458 if (MDOC_SYNPRETTY & n->flags) 1459 i = 2; 1460 t = print_otag(h, TAG_I, i, tag); 1461 print_text(h, nn->string); 1462 print_tagq(h, t); 1463 if (nn->next) 1464 print_text(h, ","); 1465 } 1466 1467 print_text(h, ")"); 1468 if (MDOC_SYNPRETTY & n->flags) 1469 print_text(h, ";"); 1470 1471 return(0); 1472 } 1473 1474 1475 /* ARGSUSED */ 1476 static int 1477 mdoc_sm_pre(MDOC_ARGS) 1478 { 1479 1480 assert(n->child && MDOC_TEXT == n->child->type); 1481 if (0 == strcmp("on", n->child->string)) { 1482 /* 1483 * FIXME: no p->col to check. Thus, if we have 1484 * .Bd -literal 1485 * .Sm off 1486 * 1 2 1487 * .Sm on 1488 * 3 1489 * .Ed 1490 * the "3" is preceded by a space. 1491 */ 1492 h->flags &= ~HTML_NOSPACE; 1493 h->flags &= ~HTML_NONOSPACE; 1494 } else 1495 h->flags |= HTML_NONOSPACE; 1496 1497 return(0); 1498 } 1499 1500 /* ARGSUSED */ 1501 static int 1502 mdoc_pp_pre(MDOC_ARGS) 1503 { 1504 1505 print_otag(h, TAG_P, 0, NULL); 1506 return(0); 1507 1508 } 1509 1510 /* ARGSUSED */ 1511 static int 1512 mdoc_sp_pre(MDOC_ARGS) 1513 { 1514 struct roffsu su; 1515 struct htmlpair tag; 1516 1517 SCALE_VS_INIT(&su, 1); 1518 1519 if (MDOC_sp == n->tok) { 1520 if (n->child) 1521 a2roffsu(n->child->string, &su, SCALE_VS); 1522 } else 1523 su.scale = 0; 1524 1525 bufcat_su(h, "height", &su); 1526 PAIR_STYLE_INIT(&tag, h); 1527 print_otag(h, TAG_DIV, 1, &tag); 1528 1529 /* So the div isn't empty: */ 1530 print_text(h, "\\~"); 1531 1532 return(0); 1533 1534 } 1535 1536 /* ARGSUSED */ 1537 static int 1538 mdoc_lk_pre(MDOC_ARGS) 1539 { 1540 const struct mdoc_node *nn; 1541 struct htmlpair tag[2]; 1542 1543 nn = n->child; 1544 1545 PAIR_CLASS_INIT(&tag[0], "link-ext"); 1546 PAIR_HREF_INIT(&tag[1], nn->string); 1547 print_otag(h, TAG_A, 2, tag); 1548 1549 if (NULL == nn || NULL == nn->next) 1550 return(1); 1551 1552 for (nn = nn->next; nn; nn = nn->next) 1553 print_text(h, nn->string); 1554 1555 return(0); 1556 } 1557 1558 1559 /* ARGSUSED */ 1560 static int 1561 mdoc_mt_pre(MDOC_ARGS) 1562 { 1563 struct htmlpair tag[2]; 1564 struct tag *t; 1565 const struct mdoc_node *nn; 1566 1567 PAIR_CLASS_INIT(&tag[0], "link-mail"); 1568 1569 for (nn = n->child; nn; nn = nn->next) { 1570 bufinit(h); 1571 bufcat(h, "mailto:"); 1572 bufcat(h, nn->string); 1573 PAIR_HREF_INIT(&tag[1], h->buf); 1574 t = print_otag(h, TAG_A, 2, tag); 1575 print_text(h, nn->string); 1576 print_tagq(h, t); 1577 } 1578 1579 return(0); 1580 } 1581 1582 1583 /* ARGSUSED */ 1584 static int 1585 mdoc_fo_pre(MDOC_ARGS) 1586 { 1587 struct htmlpair tag; 1588 struct tag *t; 1589 1590 if (MDOC_BODY == n->type) { 1591 h->flags |= HTML_NOSPACE; 1592 print_text(h, "("); 1593 h->flags |= HTML_NOSPACE; 1594 return(1); 1595 } else if (MDOC_BLOCK == n->type) { 1596 synopsis_pre(h, n); 1597 return(1); 1598 } 1599 1600 /* XXX: we drop non-initial arguments as per groff. */ 1601 1602 assert(n->child); 1603 assert(n->child->string); 1604 1605 PAIR_CLASS_INIT(&tag, "fname"); 1606 t = print_otag(h, TAG_B, 1, &tag); 1607 print_text(h, n->child->string); 1608 print_tagq(h, t); 1609 return(0); 1610 } 1611 1612 1613 /* ARGSUSED */ 1614 static void 1615 mdoc_fo_post(MDOC_ARGS) 1616 { 1617 1618 if (MDOC_BODY != n->type) 1619 return; 1620 print_text(h, ")"); 1621 print_text(h, ";"); 1622 } 1623 1624 1625 /* ARGSUSED */ 1626 static int 1627 mdoc_in_pre(MDOC_ARGS) 1628 { 1629 const struct mdoc_node *nn; 1630 struct tag *t; 1631 struct htmlpair tag[2]; 1632 int i; 1633 1634 synopsis_pre(h, n); 1635 1636 PAIR_CLASS_INIT(&tag[0], "includes"); 1637 print_otag(h, TAG_B, 1, tag); 1638 1639 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) 1640 print_text(h, "#include"); 1641 1642 print_text(h, "<"); 1643 h->flags |= HTML_NOSPACE; 1644 1645 for (nn = n->child; nn; nn = nn->next) { 1646 PAIR_CLASS_INIT(&tag[0], "link-includes"); 1647 i = 1; 1648 bufinit(h); 1649 if (h->base_includes) { 1650 buffmt_includes(h, nn->string); 1651 PAIR_HREF_INIT(&tag[i], h->buf); 1652 i++; 1653 } 1654 t = print_otag(h, TAG_A, i, tag); 1655 print_mdoc_node(m, nn, h); 1656 print_tagq(h, t); 1657 } 1658 1659 h->flags |= HTML_NOSPACE; 1660 print_text(h, ">"); 1661 1662 return(0); 1663 } 1664 1665 1666 /* ARGSUSED */ 1667 static int 1668 mdoc_ic_pre(MDOC_ARGS) 1669 { 1670 struct htmlpair tag; 1671 1672 PAIR_CLASS_INIT(&tag, "cmd"); 1673 print_otag(h, TAG_B, 1, &tag); 1674 return(1); 1675 } 1676 1677 1678 /* ARGSUSED */ 1679 static int 1680 mdoc_rv_pre(MDOC_ARGS) 1681 { 1682 const struct mdoc_node *nn; 1683 struct htmlpair tag; 1684 struct tag *t; 1685 1686 if (n->prev) 1687 print_otag(h, TAG_BR, 0, NULL); 1688 1689 print_text(h, "The"); 1690 1691 for (nn = n->child; nn; nn = nn->next) { 1692 PAIR_CLASS_INIT(&tag, "fname"); 1693 t = print_otag(h, TAG_B, 1, &tag); 1694 print_text(h, nn->string); 1695 print_tagq(h, t); 1696 1697 h->flags |= HTML_NOSPACE; 1698 if (nn->next && NULL == nn->next->next) 1699 print_text(h, "(), and"); 1700 else if (nn->next) 1701 print_text(h, "(),"); 1702 else 1703 print_text(h, "()"); 1704 } 1705 1706 if (n->child && n->child->next) 1707 print_text(h, "functions return"); 1708 else 1709 print_text(h, "function returns"); 1710 1711 print_text(h, "the value 0 if successful; otherwise the value " 1712 "-1 is returned and the global variable"); 1713 1714 PAIR_CLASS_INIT(&tag, "var"); 1715 t = print_otag(h, TAG_B, 1, &tag); 1716 print_text(h, "errno"); 1717 print_tagq(h, t); 1718 print_text(h, "is set to indicate the error."); 1719 return(0); 1720 } 1721 1722 1723 /* ARGSUSED */ 1724 static int 1725 mdoc_va_pre(MDOC_ARGS) 1726 { 1727 struct htmlpair tag; 1728 1729 PAIR_CLASS_INIT(&tag, "var"); 1730 print_otag(h, TAG_B, 1, &tag); 1731 return(1); 1732 } 1733 1734 1735 /* ARGSUSED */ 1736 static int 1737 mdoc_ap_pre(MDOC_ARGS) 1738 { 1739 1740 h->flags |= HTML_NOSPACE; 1741 print_text(h, "\\(aq"); 1742 h->flags |= HTML_NOSPACE; 1743 return(1); 1744 } 1745 1746 1747 /* ARGSUSED */ 1748 static int 1749 mdoc_bf_pre(MDOC_ARGS) 1750 { 1751 struct htmlpair tag[2]; 1752 struct roffsu su; 1753 1754 if (MDOC_HEAD == n->type) 1755 return(0); 1756 else if (MDOC_BODY != n->type) 1757 return(1); 1758 1759 if (FONT_Em == n->norm->Bf.font) 1760 PAIR_CLASS_INIT(&tag[0], "emph"); 1761 else if (FONT_Sy == n->norm->Bf.font) 1762 PAIR_CLASS_INIT(&tag[0], "symb"); 1763 else if (FONT_Li == n->norm->Bf.font) 1764 PAIR_CLASS_INIT(&tag[0], "lit"); 1765 else 1766 PAIR_CLASS_INIT(&tag[0], "none"); 1767 1768 /* 1769 * We want this to be inline-formatted, but needs to be div to 1770 * accept block children. 1771 */ 1772 bufcat_style(h, "display", "inline"); 1773 SCALE_HS_INIT(&su, 1); 1774 /* Needs a left-margin for spacing. */ 1775 bufcat_su(h, "margin-left", &su); 1776 PAIR_STYLE_INIT(&tag[1], h); 1777 print_otag(h, TAG_DIV, 2, tag); 1778 return(1); 1779 } 1780 1781 1782 /* ARGSUSED */ 1783 static int 1784 mdoc_ms_pre(MDOC_ARGS) 1785 { 1786 struct htmlpair tag; 1787 1788 PAIR_CLASS_INIT(&tag, "symb"); 1789 print_otag(h, TAG_SPAN, 1, &tag); 1790 return(1); 1791 } 1792 1793 1794 /* ARGSUSED */ 1795 static int 1796 mdoc_igndelim_pre(MDOC_ARGS) 1797 { 1798 1799 h->flags |= HTML_IGNDELIM; 1800 return(1); 1801 } 1802 1803 1804 /* ARGSUSED */ 1805 static void 1806 mdoc_pf_post(MDOC_ARGS) 1807 { 1808 1809 h->flags |= HTML_NOSPACE; 1810 } 1811 1812 1813 /* ARGSUSED */ 1814 static int 1815 mdoc_rs_pre(MDOC_ARGS) 1816 { 1817 struct htmlpair tag; 1818 1819 if (MDOC_BLOCK != n->type) 1820 return(1); 1821 1822 if (n->prev && SEC_SEE_ALSO == n->sec) 1823 print_otag(h, TAG_P, 0, NULL); 1824 1825 PAIR_CLASS_INIT(&tag, "ref"); 1826 print_otag(h, TAG_SPAN, 1, &tag); 1827 return(1); 1828 } 1829 1830 1831 1832 /* ARGSUSED */ 1833 static int 1834 mdoc_li_pre(MDOC_ARGS) 1835 { 1836 struct htmlpair tag; 1837 1838 PAIR_CLASS_INIT(&tag, "lit"); 1839 print_otag(h, TAG_SPAN, 1, &tag); 1840 return(1); 1841 } 1842 1843 1844 /* ARGSUSED */ 1845 static int 1846 mdoc_sy_pre(MDOC_ARGS) 1847 { 1848 struct htmlpair tag; 1849 1850 PAIR_CLASS_INIT(&tag, "symb"); 1851 print_otag(h, TAG_SPAN, 1, &tag); 1852 return(1); 1853 } 1854 1855 1856 /* ARGSUSED */ 1857 static int 1858 mdoc_bt_pre(MDOC_ARGS) 1859 { 1860 1861 print_text(h, "is currently in beta test."); 1862 return(0); 1863 } 1864 1865 1866 /* ARGSUSED */ 1867 static int 1868 mdoc_ud_pre(MDOC_ARGS) 1869 { 1870 1871 print_text(h, "currently under development."); 1872 return(0); 1873 } 1874 1875 1876 /* ARGSUSED */ 1877 static int 1878 mdoc_lb_pre(MDOC_ARGS) 1879 { 1880 struct htmlpair tag; 1881 1882 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev) 1883 print_otag(h, TAG_BR, 0, NULL); 1884 1885 PAIR_CLASS_INIT(&tag, "lib"); 1886 print_otag(h, TAG_SPAN, 1, &tag); 1887 return(1); 1888 } 1889 1890 1891 /* ARGSUSED */ 1892 static int 1893 mdoc__x_pre(MDOC_ARGS) 1894 { 1895 struct htmlpair tag[2]; 1896 enum htmltag t; 1897 1898 t = TAG_SPAN; 1899 1900 switch (n->tok) { 1901 case(MDOC__A): 1902 PAIR_CLASS_INIT(&tag[0], "ref-auth"); 1903 if (n->prev && MDOC__A == n->prev->tok) 1904 if (NULL == n->next || MDOC__A != n->next->tok) 1905 print_text(h, "and"); 1906 break; 1907 case(MDOC__B): 1908 PAIR_CLASS_INIT(&tag[0], "ref-book"); 1909 t = TAG_I; 1910 break; 1911 case(MDOC__C): 1912 PAIR_CLASS_INIT(&tag[0], "ref-city"); 1913 break; 1914 case(MDOC__D): 1915 PAIR_CLASS_INIT(&tag[0], "ref-date"); 1916 break; 1917 case(MDOC__I): 1918 PAIR_CLASS_INIT(&tag[0], "ref-issue"); 1919 t = TAG_I; 1920 break; 1921 case(MDOC__J): 1922 PAIR_CLASS_INIT(&tag[0], "ref-jrnl"); 1923 t = TAG_I; 1924 break; 1925 case(MDOC__N): 1926 PAIR_CLASS_INIT(&tag[0], "ref-num"); 1927 break; 1928 case(MDOC__O): 1929 PAIR_CLASS_INIT(&tag[0], "ref-opt"); 1930 break; 1931 case(MDOC__P): 1932 PAIR_CLASS_INIT(&tag[0], "ref-page"); 1933 break; 1934 case(MDOC__Q): 1935 PAIR_CLASS_INIT(&tag[0], "ref-corp"); 1936 break; 1937 case(MDOC__R): 1938 PAIR_CLASS_INIT(&tag[0], "ref-rep"); 1939 break; 1940 case(MDOC__T): 1941 PAIR_CLASS_INIT(&tag[0], "ref-title"); 1942 break; 1943 case(MDOC__U): 1944 PAIR_CLASS_INIT(&tag[0], "link-ref"); 1945 break; 1946 case(MDOC__V): 1947 PAIR_CLASS_INIT(&tag[0], "ref-vol"); 1948 break; 1949 default: 1950 abort(); 1951 /* NOTREACHED */ 1952 } 1953 1954 if (MDOC__U != n->tok) { 1955 print_otag(h, t, 1, tag); 1956 return(1); 1957 } 1958 1959 PAIR_HREF_INIT(&tag[1], n->child->string); 1960 print_otag(h, TAG_A, 2, tag); 1961 1962 return(1); 1963 } 1964 1965 1966 /* ARGSUSED */ 1967 static void 1968 mdoc__x_post(MDOC_ARGS) 1969 { 1970 1971 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1972 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1973 if (NULL == n->prev || MDOC__A != n->prev->tok) 1974 return; 1975 1976 /* TODO: %U */ 1977 1978 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1979 return; 1980 1981 print_text(h, n->next ? "," : "."); 1982 } 1983 1984 1985 /* ARGSUSED */ 1986 static int 1987 mdoc_bk_pre(MDOC_ARGS) 1988 { 1989 1990 switch (n->type) { 1991 case (MDOC_BLOCK): 1992 break; 1993 case (MDOC_HEAD): 1994 return(0); 1995 case (MDOC_BODY): 1996 if (n->parent->args || 0 == n->prev->nchild) 1997 h->flags |= HTML_PREKEEP; 1998 break; 1999 default: 2000 abort(); 2001 /* NOTREACHED */ 2002 } 2003 2004 return(1); 2005 } 2006 2007 2008 /* ARGSUSED */ 2009 static void 2010 mdoc_bk_post(MDOC_ARGS) 2011 { 2012 2013 if (MDOC_BODY == n->type) 2014 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 2015 } 2016 2017 2018 /* ARGSUSED */ 2019 static int 2020 mdoc_quote_pre(MDOC_ARGS) 2021 { 2022 struct htmlpair tag; 2023 2024 if (MDOC_BODY != n->type) 2025 return(1); 2026 2027 switch (n->tok) { 2028 case (MDOC_Ao): 2029 /* FALLTHROUGH */ 2030 case (MDOC_Aq): 2031 print_text(h, "\\(la"); 2032 break; 2033 case (MDOC_Bro): 2034 /* FALLTHROUGH */ 2035 case (MDOC_Brq): 2036 print_text(h, "\\(lC"); 2037 break; 2038 case (MDOC_Bo): 2039 /* FALLTHROUGH */ 2040 case (MDOC_Bq): 2041 print_text(h, "\\(lB"); 2042 break; 2043 case (MDOC_Oo): 2044 /* FALLTHROUGH */ 2045 case (MDOC_Op): 2046 print_text(h, "\\(lB"); 2047 h->flags |= HTML_NOSPACE; 2048 PAIR_CLASS_INIT(&tag, "opt"); 2049 print_otag(h, TAG_SPAN, 1, &tag); 2050 break; 2051 case (MDOC_Do): 2052 /* FALLTHROUGH */ 2053 case (MDOC_Dq): 2054 /* FALLTHROUGH */ 2055 case (MDOC_Qo): 2056 /* FALLTHROUGH */ 2057 case (MDOC_Qq): 2058 print_text(h, "\\(lq"); 2059 break; 2060 case (MDOC_Po): 2061 /* FALLTHROUGH */ 2062 case (MDOC_Pq): 2063 print_text(h, "("); 2064 break; 2065 case (MDOC_Ql): 2066 /* FALLTHROUGH */ 2067 case (MDOC_So): 2068 /* FALLTHROUGH */ 2069 case (MDOC_Sq): 2070 print_text(h, "\\(oq"); 2071 break; 2072 default: 2073 abort(); 2074 /* NOTREACHED */ 2075 } 2076 2077 h->flags |= HTML_NOSPACE; 2078 return(1); 2079 } 2080 2081 2082 /* ARGSUSED */ 2083 static void 2084 mdoc_quote_post(MDOC_ARGS) 2085 { 2086 2087 if (MDOC_BODY != n->type) 2088 return; 2089 2090 h->flags |= HTML_NOSPACE; 2091 2092 switch (n->tok) { 2093 case (MDOC_Ao): 2094 /* FALLTHROUGH */ 2095 case (MDOC_Aq): 2096 print_text(h, "\\(ra"); 2097 break; 2098 case (MDOC_Bro): 2099 /* FALLTHROUGH */ 2100 case (MDOC_Brq): 2101 print_text(h, "\\(rC"); 2102 break; 2103 case (MDOC_Oo): 2104 /* FALLTHROUGH */ 2105 case (MDOC_Op): 2106 /* FALLTHROUGH */ 2107 case (MDOC_Bo): 2108 /* FALLTHROUGH */ 2109 case (MDOC_Bq): 2110 print_text(h, "\\(rB"); 2111 break; 2112 case (MDOC_Qo): 2113 /* FALLTHROUGH */ 2114 case (MDOC_Qq): 2115 /* FALLTHROUGH */ 2116 case (MDOC_Do): 2117 /* FALLTHROUGH */ 2118 case (MDOC_Dq): 2119 print_text(h, "\\(rq"); 2120 break; 2121 case (MDOC_Po): 2122 /* FALLTHROUGH */ 2123 case (MDOC_Pq): 2124 print_text(h, ")"); 2125 break; 2126 case (MDOC_Ql): 2127 /* FALLTHROUGH */ 2128 case (MDOC_So): 2129 /* FALLTHROUGH */ 2130 case (MDOC_Sq): 2131 print_text(h, "\\(aq"); 2132 break; 2133 default: 2134 abort(); 2135 /* NOTREACHED */ 2136 } 2137 } 2138 2139 2140