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