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