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