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