1 /* $OpenBSD: mdoc_html.c,v 1.169 2017/07/15 17:57:46 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 print_otag(h, TAG_LI, "c", cattr); 717 break; 718 default: 719 break; 720 } 721 break; 722 case LIST_diag: 723 case LIST_hang: 724 case LIST_inset: 725 case LIST_ohang: 726 switch (n->type) { 727 case ROFFT_HEAD: 728 print_otag(h, TAG_DT, "c", cattr); 729 if (type == LIST_diag) 730 print_otag(h, TAG_B, "c", cattr); 731 break; 732 case ROFFT_BODY: 733 print_otag(h, TAG_DD, "csw*+l", cattr, 734 bl->norm->Bl.width); 735 break; 736 default: 737 break; 738 } 739 break; 740 case LIST_tag: 741 switch (n->type) { 742 case ROFFT_HEAD: 743 if (h->style != NULL && !bl->norm->Bl.comp && 744 (n->parent->prev == NULL || 745 n->parent->prev->body == NULL || 746 n->parent->prev->body->child != NULL)) { 747 t = print_otag(h, TAG_DT, "csw*+-l", 748 cattr, bl->norm->Bl.width); 749 print_text(h, "\\ "); 750 print_tagq(h, t); 751 t = print_otag(h, TAG_DD, "c", cattr); 752 print_text(h, "\\ "); 753 print_tagq(h, t); 754 } 755 print_otag(h, TAG_DT, "csw*+-l", cattr, 756 bl->norm->Bl.width); 757 break; 758 case ROFFT_BODY: 759 if (n->child == NULL) { 760 print_otag(h, TAG_DD, "css?", cattr, 761 "width", "auto"); 762 print_text(h, "\\ "); 763 } else 764 print_otag(h, TAG_DD, "c", cattr); 765 break; 766 default: 767 break; 768 } 769 break; 770 case LIST_column: 771 switch (n->type) { 772 case ROFFT_HEAD: 773 break; 774 case ROFFT_BODY: 775 print_otag(h, TAG_TD, "c", cattr); 776 break; 777 default: 778 print_otag(h, TAG_TR, "c", cattr); 779 } 780 default: 781 break; 782 } 783 784 return 1; 785 } 786 787 static int 788 mdoc_bl_pre(MDOC_ARGS) 789 { 790 char cattr[21]; 791 struct tag *t; 792 struct mdoc_bl *bl; 793 size_t i; 794 enum htmltag elemtype; 795 796 bl = &n->norm->Bl; 797 798 switch (n->type) { 799 case ROFFT_BODY: 800 return 1; 801 802 case ROFFT_HEAD: 803 if (bl->type != LIST_column || bl->ncols == 0) 804 return 0; 805 806 /* 807 * For each column, print out the <COL> tag with our 808 * suggested width. The last column gets min-width, as 809 * in terminal mode it auto-sizes to the width of the 810 * screen and we want to preserve that behaviour. 811 */ 812 813 t = print_otag(h, TAG_COLGROUP, ""); 814 for (i = 0; i < bl->ncols - 1; i++) 815 print_otag(h, TAG_COL, "sw+w", bl->cols[i]); 816 print_otag(h, TAG_COL, "swW", bl->cols[i]); 817 print_tagq(h, t); 818 return 0; 819 820 default: 821 break; 822 } 823 824 switch (bl->type) { 825 case LIST_bullet: 826 elemtype = TAG_UL; 827 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 828 break; 829 case LIST_dash: 830 case LIST_hyphen: 831 elemtype = TAG_UL; 832 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 833 break; 834 case LIST_item: 835 elemtype = TAG_UL; 836 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 837 break; 838 case LIST_enum: 839 elemtype = TAG_OL; 840 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 841 break; 842 case LIST_diag: 843 elemtype = TAG_DL; 844 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 845 break; 846 case LIST_hang: 847 elemtype = TAG_DL; 848 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 849 break; 850 case LIST_inset: 851 elemtype = TAG_DL; 852 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 853 break; 854 case LIST_ohang: 855 elemtype = TAG_DL; 856 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 857 break; 858 case LIST_tag: 859 if (bl->offs) 860 print_otag(h, TAG_DIV, "cswl", "Bl-tag", bl->offs); 861 print_otag(h, TAG_DL, "csw*+l", bl->comp ? 862 "Bl-tag Bl-compact" : "Bl-tag", bl->width); 863 return 1; 864 case LIST_column: 865 elemtype = TAG_TABLE; 866 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 867 break; 868 default: 869 abort(); 870 } 871 if (bl->comp) 872 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 873 print_otag(h, elemtype, "cswl", cattr, bl->offs); 874 return 1; 875 } 876 877 static int 878 mdoc_ex_pre(MDOC_ARGS) 879 { 880 if (n->prev) 881 print_otag(h, TAG_BR, ""); 882 return 1; 883 } 884 885 static int 886 mdoc_st_pre(MDOC_ARGS) 887 { 888 print_otag(h, TAG_SPAN, "cT", "St"); 889 return 1; 890 } 891 892 static int 893 mdoc_em_pre(MDOC_ARGS) 894 { 895 print_otag(h, TAG_I, "cT", "Em"); 896 return 1; 897 } 898 899 static int 900 mdoc_d1_pre(MDOC_ARGS) 901 { 902 if (n->type != ROFFT_BLOCK) 903 return 1; 904 905 print_otag(h, TAG_DIV, "c", "D1"); 906 907 if (n->tok == MDOC_Dl) 908 print_otag(h, TAG_CODE, "c", "Li"); 909 910 return 1; 911 } 912 913 static int 914 mdoc_sx_pre(MDOC_ARGS) 915 { 916 char *id; 917 918 id = html_make_id(n); 919 print_otag(h, TAG_A, "cThR", "Sx", id); 920 free(id); 921 return 1; 922 } 923 924 static int 925 mdoc_bd_pre(MDOC_ARGS) 926 { 927 int comp, offs, sv; 928 struct roff_node *nn; 929 930 if (n->type == ROFFT_HEAD) 931 return 0; 932 933 if (n->type == ROFFT_BLOCK) { 934 comp = n->norm->Bd.comp; 935 for (nn = n; nn && ! comp; nn = nn->parent) { 936 if (nn->type != ROFFT_BLOCK) 937 continue; 938 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) 939 comp = 1; 940 if (nn->prev) 941 break; 942 } 943 if ( ! comp) 944 print_paragraph(h); 945 return 1; 946 } 947 948 /* Handle the -offset argument. */ 949 950 if (n->norm->Bd.offs == NULL || 951 ! strcmp(n->norm->Bd.offs, "left")) 952 offs = 0; 953 else if ( ! strcmp(n->norm->Bd.offs, "indent")) 954 offs = INDENT; 955 else if ( ! strcmp(n->norm->Bd.offs, "indent-two")) 956 offs = INDENT * 2; 957 else 958 offs = -1; 959 960 if (offs == -1) 961 print_otag(h, TAG_DIV, "cswl", "Bd", n->norm->Bd.offs); 962 else 963 print_otag(h, TAG_DIV, "cshl", "Bd", offs); 964 965 if (n->norm->Bd.type != DISP_unfilled && 966 n->norm->Bd.type != DISP_literal) 967 return 1; 968 969 print_otag(h, TAG_PRE, "c", "Li"); 970 971 /* This can be recursive: save & set our literal state. */ 972 973 sv = h->flags & HTML_LITERAL; 974 h->flags |= HTML_LITERAL; 975 976 for (nn = n->child; nn; nn = nn->next) { 977 print_mdoc_node(meta, nn, h); 978 /* 979 * If the printed node flushes its own line, then we 980 * needn't do it here as well. This is hacky, but the 981 * notion of selective eoln whitespace is pretty dumb 982 * anyway, so don't sweat it. 983 */ 984 switch (nn->tok) { 985 case ROFF_br: 986 case ROFF_sp: 987 case MDOC_Sm: 988 case MDOC_Bl: 989 case MDOC_D1: 990 case MDOC_Dl: 991 case MDOC_Lp: 992 case MDOC_Pp: 993 continue; 994 default: 995 break; 996 } 997 if (h->flags & HTML_NONEWLINE || 998 (nn->next && ! (nn->next->flags & NODE_LINE))) 999 continue; 1000 else if (nn->next) 1001 print_text(h, "\n"); 1002 1003 h->flags |= HTML_NOSPACE; 1004 } 1005 1006 if (0 == sv) 1007 h->flags &= ~HTML_LITERAL; 1008 1009 return 0; 1010 } 1011 1012 static int 1013 mdoc_pa_pre(MDOC_ARGS) 1014 { 1015 print_otag(h, TAG_I, "cT", "Pa"); 1016 return 1; 1017 } 1018 1019 static int 1020 mdoc_ad_pre(MDOC_ARGS) 1021 { 1022 print_otag(h, TAG_I, "c", "Ad"); 1023 return 1; 1024 } 1025 1026 static int 1027 mdoc_an_pre(MDOC_ARGS) 1028 { 1029 if (n->norm->An.auth == AUTH_split) { 1030 h->flags &= ~HTML_NOSPLIT; 1031 h->flags |= HTML_SPLIT; 1032 return 0; 1033 } 1034 if (n->norm->An.auth == AUTH_nosplit) { 1035 h->flags &= ~HTML_SPLIT; 1036 h->flags |= HTML_NOSPLIT; 1037 return 0; 1038 } 1039 1040 if (h->flags & HTML_SPLIT) 1041 print_otag(h, TAG_BR, ""); 1042 1043 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 1044 h->flags |= HTML_SPLIT; 1045 1046 print_otag(h, TAG_SPAN, "cT", "An"); 1047 return 1; 1048 } 1049 1050 static int 1051 mdoc_cd_pre(MDOC_ARGS) 1052 { 1053 synopsis_pre(h, n); 1054 print_otag(h, TAG_B, "cT", "Cd"); 1055 return 1; 1056 } 1057 1058 static int 1059 mdoc_dv_pre(MDOC_ARGS) 1060 { 1061 char *id; 1062 1063 if ((id = cond_id(n)) != NULL) 1064 print_otag(h, TAG_A, "chR", "selflink", id); 1065 print_otag(h, TAG_CODE, "cTi", "Dv", id); 1066 free(id); 1067 return 1; 1068 } 1069 1070 static int 1071 mdoc_ev_pre(MDOC_ARGS) 1072 { 1073 char *id; 1074 1075 if ((id = cond_id(n)) != NULL) 1076 print_otag(h, TAG_A, "chR", "selflink", id); 1077 print_otag(h, TAG_CODE, "cTi", "Ev", id); 1078 free(id); 1079 return 1; 1080 } 1081 1082 static int 1083 mdoc_er_pre(MDOC_ARGS) 1084 { 1085 char *id; 1086 1087 id = n->sec == SEC_ERRORS && 1088 (n->parent->tok == MDOC_It || 1089 (n->parent->tok == MDOC_Bq && 1090 n->parent->parent->parent->tok == MDOC_It)) ? 1091 html_make_id(n) : NULL; 1092 1093 if (id != NULL) 1094 print_otag(h, TAG_A, "chR", "selflink", id); 1095 print_otag(h, TAG_CODE, "cTi", "Er", id); 1096 free(id); 1097 return 1; 1098 } 1099 1100 static int 1101 mdoc_fa_pre(MDOC_ARGS) 1102 { 1103 const struct roff_node *nn; 1104 struct tag *t; 1105 1106 if (n->parent->tok != MDOC_Fo) { 1107 print_otag(h, TAG_VAR, "cT", "Fa"); 1108 return 1; 1109 } 1110 1111 for (nn = n->child; nn; nn = nn->next) { 1112 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1113 print_text(h, nn->string); 1114 print_tagq(h, t); 1115 if (nn->next) { 1116 h->flags |= HTML_NOSPACE; 1117 print_text(h, ","); 1118 } 1119 } 1120 1121 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1122 h->flags |= HTML_NOSPACE; 1123 print_text(h, ","); 1124 } 1125 1126 return 0; 1127 } 1128 1129 static int 1130 mdoc_fd_pre(MDOC_ARGS) 1131 { 1132 struct tag *t; 1133 char *buf, *cp; 1134 1135 synopsis_pre(h, n); 1136 1137 if (NULL == (n = n->child)) 1138 return 0; 1139 1140 assert(n->type == ROFFT_TEXT); 1141 1142 if (strcmp(n->string, "#include")) { 1143 print_otag(h, TAG_B, "cT", "Fd"); 1144 return 1; 1145 } 1146 1147 print_otag(h, TAG_B, "cT", "In"); 1148 print_text(h, n->string); 1149 1150 if (NULL != (n = n->next)) { 1151 assert(n->type == ROFFT_TEXT); 1152 1153 if (h->base_includes) { 1154 cp = n->string; 1155 if (*cp == '<' || *cp == '"') 1156 cp++; 1157 buf = mandoc_strdup(cp); 1158 cp = strchr(buf, '\0') - 1; 1159 if (cp >= buf && (*cp == '>' || *cp == '"')) 1160 *cp = '\0'; 1161 t = print_otag(h, TAG_A, "cThI", "In", buf); 1162 free(buf); 1163 } else 1164 t = print_otag(h, TAG_A, "cT", "In"); 1165 1166 print_text(h, n->string); 1167 print_tagq(h, t); 1168 1169 n = n->next; 1170 } 1171 1172 for ( ; n; n = n->next) { 1173 assert(n->type == ROFFT_TEXT); 1174 print_text(h, n->string); 1175 } 1176 1177 return 0; 1178 } 1179 1180 static int 1181 mdoc_vt_pre(MDOC_ARGS) 1182 { 1183 if (n->type == ROFFT_BLOCK) { 1184 synopsis_pre(h, n); 1185 return 1; 1186 } else if (n->type == ROFFT_ELEM) { 1187 synopsis_pre(h, n); 1188 } else if (n->type == ROFFT_HEAD) 1189 return 0; 1190 1191 print_otag(h, TAG_VAR, "cT", "Vt"); 1192 return 1; 1193 } 1194 1195 static int 1196 mdoc_ft_pre(MDOC_ARGS) 1197 { 1198 synopsis_pre(h, n); 1199 print_otag(h, TAG_VAR, "cT", "Ft"); 1200 return 1; 1201 } 1202 1203 static int 1204 mdoc_fn_pre(MDOC_ARGS) 1205 { 1206 struct tag *t; 1207 char nbuf[BUFSIZ]; 1208 const char *sp, *ep; 1209 int sz, pretty; 1210 1211 pretty = NODE_SYNPRETTY & n->flags; 1212 synopsis_pre(h, n); 1213 1214 /* Split apart into type and name. */ 1215 assert(n->child->string); 1216 sp = n->child->string; 1217 1218 ep = strchr(sp, ' '); 1219 if (NULL != ep) { 1220 t = print_otag(h, TAG_VAR, "cT", "Ft"); 1221 1222 while (ep) { 1223 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1224 (void)memcpy(nbuf, sp, (size_t)sz); 1225 nbuf[sz] = '\0'; 1226 print_text(h, nbuf); 1227 sp = ++ep; 1228 ep = strchr(sp, ' '); 1229 } 1230 print_tagq(h, t); 1231 } 1232 1233 t = print_otag(h, TAG_B, "cT", "Fn"); 1234 1235 if (sp) 1236 print_text(h, sp); 1237 1238 print_tagq(h, t); 1239 1240 h->flags |= HTML_NOSPACE; 1241 print_text(h, "("); 1242 h->flags |= HTML_NOSPACE; 1243 1244 for (n = n->child->next; n; n = n->next) { 1245 if (NODE_SYNPRETTY & n->flags) 1246 t = print_otag(h, TAG_VAR, "cTss?", "Fa", 1247 "white-space", "nowrap"); 1248 else 1249 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1250 print_text(h, n->string); 1251 print_tagq(h, t); 1252 if (n->next) { 1253 h->flags |= HTML_NOSPACE; 1254 print_text(h, ","); 1255 } 1256 } 1257 1258 h->flags |= HTML_NOSPACE; 1259 print_text(h, ")"); 1260 1261 if (pretty) { 1262 h->flags |= HTML_NOSPACE; 1263 print_text(h, ";"); 1264 } 1265 1266 return 0; 1267 } 1268 1269 static int 1270 mdoc_sm_pre(MDOC_ARGS) 1271 { 1272 1273 if (NULL == n->child) 1274 h->flags ^= HTML_NONOSPACE; 1275 else if (0 == strcmp("on", n->child->string)) 1276 h->flags &= ~HTML_NONOSPACE; 1277 else 1278 h->flags |= HTML_NONOSPACE; 1279 1280 if ( ! (HTML_NONOSPACE & h->flags)) 1281 h->flags &= ~HTML_NOSPACE; 1282 1283 return 0; 1284 } 1285 1286 static int 1287 mdoc_skip_pre(MDOC_ARGS) 1288 { 1289 1290 return 0; 1291 } 1292 1293 static int 1294 mdoc_pp_pre(MDOC_ARGS) 1295 { 1296 1297 print_paragraph(h); 1298 return 0; 1299 } 1300 1301 static int 1302 mdoc_lk_pre(MDOC_ARGS) 1303 { 1304 const struct roff_node *link, *descr, *punct; 1305 struct tag *t; 1306 1307 if ((link = n->child) == NULL) 1308 return 0; 1309 1310 /* Find beginning of trailing punctuation. */ 1311 punct = n->last; 1312 while (punct != link && punct->flags & NODE_DELIMC) 1313 punct = punct->prev; 1314 punct = punct->next; 1315 1316 /* Link target and link text. */ 1317 descr = link->next; 1318 if (descr == punct) 1319 descr = link; /* no text */ 1320 t = print_otag(h, TAG_A, "cTh", "Lk", link->string); 1321 do { 1322 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1323 h->flags |= HTML_NOSPACE; 1324 print_text(h, descr->string); 1325 descr = descr->next; 1326 } while (descr != punct); 1327 print_tagq(h, t); 1328 1329 /* Trailing punctuation. */ 1330 while (punct != NULL) { 1331 h->flags |= HTML_NOSPACE; 1332 print_text(h, punct->string); 1333 punct = punct->next; 1334 } 1335 return 0; 1336 } 1337 1338 static int 1339 mdoc_mt_pre(MDOC_ARGS) 1340 { 1341 struct tag *t; 1342 char *cp; 1343 1344 for (n = n->child; n; n = n->next) { 1345 assert(n->type == ROFFT_TEXT); 1346 1347 mandoc_asprintf(&cp, "mailto:%s", n->string); 1348 t = print_otag(h, TAG_A, "cTh", "Mt", cp); 1349 print_text(h, n->string); 1350 print_tagq(h, t); 1351 free(cp); 1352 } 1353 1354 return 0; 1355 } 1356 1357 static int 1358 mdoc_fo_pre(MDOC_ARGS) 1359 { 1360 struct tag *t; 1361 1362 if (n->type == ROFFT_BODY) { 1363 h->flags |= HTML_NOSPACE; 1364 print_text(h, "("); 1365 h->flags |= HTML_NOSPACE; 1366 return 1; 1367 } else if (n->type == ROFFT_BLOCK) { 1368 synopsis_pre(h, n); 1369 return 1; 1370 } 1371 1372 if (n->child == NULL) 1373 return 0; 1374 1375 assert(n->child->string); 1376 t = print_otag(h, TAG_B, "cT", "Fn"); 1377 print_text(h, n->child->string); 1378 print_tagq(h, t); 1379 return 0; 1380 } 1381 1382 static void 1383 mdoc_fo_post(MDOC_ARGS) 1384 { 1385 1386 if (n->type != ROFFT_BODY) 1387 return; 1388 h->flags |= HTML_NOSPACE; 1389 print_text(h, ")"); 1390 h->flags |= HTML_NOSPACE; 1391 print_text(h, ";"); 1392 } 1393 1394 static int 1395 mdoc_in_pre(MDOC_ARGS) 1396 { 1397 struct tag *t; 1398 1399 synopsis_pre(h, n); 1400 print_otag(h, TAG_B, "cT", "In"); 1401 1402 /* 1403 * The first argument of the `In' gets special treatment as 1404 * being a linked value. Subsequent values are printed 1405 * afterward. groff does similarly. This also handles the case 1406 * of no children. 1407 */ 1408 1409 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) 1410 print_text(h, "#include"); 1411 1412 print_text(h, "<"); 1413 h->flags |= HTML_NOSPACE; 1414 1415 if (NULL != (n = n->child)) { 1416 assert(n->type == ROFFT_TEXT); 1417 1418 if (h->base_includes) 1419 t = print_otag(h, TAG_A, "cThI", "In", n->string); 1420 else 1421 t = print_otag(h, TAG_A, "cT", "In"); 1422 print_text(h, n->string); 1423 print_tagq(h, t); 1424 1425 n = n->next; 1426 } 1427 1428 h->flags |= HTML_NOSPACE; 1429 print_text(h, ">"); 1430 1431 for ( ; n; n = n->next) { 1432 assert(n->type == ROFFT_TEXT); 1433 print_text(h, n->string); 1434 } 1435 1436 return 0; 1437 } 1438 1439 static int 1440 mdoc_ic_pre(MDOC_ARGS) 1441 { 1442 char *id; 1443 1444 if ((id = cond_id(n)) != NULL) 1445 print_otag(h, TAG_A, "chR", "selflink", id); 1446 print_otag(h, TAG_B, "cTi", "Ic", id); 1447 free(id); 1448 return 1; 1449 } 1450 1451 static int 1452 mdoc_va_pre(MDOC_ARGS) 1453 { 1454 print_otag(h, TAG_VAR, "cT", "Va"); 1455 return 1; 1456 } 1457 1458 static int 1459 mdoc_ap_pre(MDOC_ARGS) 1460 { 1461 1462 h->flags |= HTML_NOSPACE; 1463 print_text(h, "\\(aq"); 1464 h->flags |= HTML_NOSPACE; 1465 return 1; 1466 } 1467 1468 static int 1469 mdoc_bf_pre(MDOC_ARGS) 1470 { 1471 const char *cattr; 1472 1473 if (n->type == ROFFT_HEAD) 1474 return 0; 1475 else if (n->type != ROFFT_BODY) 1476 return 1; 1477 1478 if (FONT_Em == n->norm->Bf.font) 1479 cattr = "Em"; 1480 else if (FONT_Sy == n->norm->Bf.font) 1481 cattr = "Sy"; 1482 else if (FONT_Li == n->norm->Bf.font) 1483 cattr = "Li"; 1484 else 1485 cattr = "No"; 1486 1487 /* 1488 * We want this to be inline-formatted, but needs to be div to 1489 * accept block children. 1490 */ 1491 1492 print_otag(h, TAG_DIV, "css?hl", cattr, "display", "inline", 1); 1493 return 1; 1494 } 1495 1496 static int 1497 mdoc_ms_pre(MDOC_ARGS) 1498 { 1499 char *id; 1500 1501 if ((id = cond_id(n)) != NULL) 1502 print_otag(h, TAG_A, "chR", "selflink", id); 1503 print_otag(h, TAG_B, "cTi", "Ms", id); 1504 free(id); 1505 return 1; 1506 } 1507 1508 static int 1509 mdoc_igndelim_pre(MDOC_ARGS) 1510 { 1511 1512 h->flags |= HTML_IGNDELIM; 1513 return 1; 1514 } 1515 1516 static void 1517 mdoc_pf_post(MDOC_ARGS) 1518 { 1519 1520 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1521 h->flags |= HTML_NOSPACE; 1522 } 1523 1524 static int 1525 mdoc_rs_pre(MDOC_ARGS) 1526 { 1527 if (n->type != ROFFT_BLOCK) 1528 return 1; 1529 1530 if (n->prev && SEC_SEE_ALSO == n->sec) 1531 print_paragraph(h); 1532 1533 print_otag(h, TAG_CITE, "cT", "Rs"); 1534 return 1; 1535 } 1536 1537 static int 1538 mdoc_no_pre(MDOC_ARGS) 1539 { 1540 char *id; 1541 1542 if ((id = cond_id(n)) != NULL) 1543 print_otag(h, TAG_A, "chR", "selflink", id); 1544 print_otag(h, TAG_SPAN, "ci", "No", id); 1545 free(id); 1546 return 1; 1547 } 1548 1549 static int 1550 mdoc_li_pre(MDOC_ARGS) 1551 { 1552 char *id; 1553 1554 if ((id = cond_id(n)) != NULL) 1555 print_otag(h, TAG_A, "chR", "selflink", id); 1556 print_otag(h, TAG_CODE, "ci", "Li", id); 1557 free(id); 1558 return 1; 1559 } 1560 1561 static int 1562 mdoc_sy_pre(MDOC_ARGS) 1563 { 1564 print_otag(h, TAG_B, "cT", "Sy"); 1565 return 1; 1566 } 1567 1568 static int 1569 mdoc_lb_pre(MDOC_ARGS) 1570 { 1571 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) 1572 print_otag(h, TAG_BR, ""); 1573 1574 print_otag(h, TAG_SPAN, "cT", "Lb"); 1575 return 1; 1576 } 1577 1578 static int 1579 mdoc__x_pre(MDOC_ARGS) 1580 { 1581 const char *cattr; 1582 enum htmltag t; 1583 1584 t = TAG_SPAN; 1585 1586 switch (n->tok) { 1587 case MDOC__A: 1588 cattr = "RsA"; 1589 if (n->prev && MDOC__A == n->prev->tok) 1590 if (NULL == n->next || MDOC__A != n->next->tok) 1591 print_text(h, "and"); 1592 break; 1593 case MDOC__B: 1594 t = TAG_I; 1595 cattr = "RsB"; 1596 break; 1597 case MDOC__C: 1598 cattr = "RsC"; 1599 break; 1600 case MDOC__D: 1601 cattr = "RsD"; 1602 break; 1603 case MDOC__I: 1604 t = TAG_I; 1605 cattr = "RsI"; 1606 break; 1607 case MDOC__J: 1608 t = TAG_I; 1609 cattr = "RsJ"; 1610 break; 1611 case MDOC__N: 1612 cattr = "RsN"; 1613 break; 1614 case MDOC__O: 1615 cattr = "RsO"; 1616 break; 1617 case MDOC__P: 1618 cattr = "RsP"; 1619 break; 1620 case MDOC__Q: 1621 cattr = "RsQ"; 1622 break; 1623 case MDOC__R: 1624 cattr = "RsR"; 1625 break; 1626 case MDOC__T: 1627 cattr = "RsT"; 1628 break; 1629 case MDOC__U: 1630 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1631 return 1; 1632 case MDOC__V: 1633 cattr = "RsV"; 1634 break; 1635 default: 1636 abort(); 1637 } 1638 1639 print_otag(h, t, "c", cattr); 1640 return 1; 1641 } 1642 1643 static void 1644 mdoc__x_post(MDOC_ARGS) 1645 { 1646 1647 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1648 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1649 if (NULL == n->prev || MDOC__A != n->prev->tok) 1650 return; 1651 1652 /* TODO: %U */ 1653 1654 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1655 return; 1656 1657 h->flags |= HTML_NOSPACE; 1658 print_text(h, n->next ? "," : "."); 1659 } 1660 1661 static int 1662 mdoc_bk_pre(MDOC_ARGS) 1663 { 1664 1665 switch (n->type) { 1666 case ROFFT_BLOCK: 1667 break; 1668 case ROFFT_HEAD: 1669 return 0; 1670 case ROFFT_BODY: 1671 if (n->parent->args != NULL || n->prev->child == NULL) 1672 h->flags |= HTML_PREKEEP; 1673 break; 1674 default: 1675 abort(); 1676 } 1677 1678 return 1; 1679 } 1680 1681 static void 1682 mdoc_bk_post(MDOC_ARGS) 1683 { 1684 1685 if (n->type == ROFFT_BODY) 1686 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1687 } 1688 1689 static int 1690 mdoc_quote_pre(MDOC_ARGS) 1691 { 1692 if (n->type != ROFFT_BODY) 1693 return 1; 1694 1695 switch (n->tok) { 1696 case MDOC_Ao: 1697 case MDOC_Aq: 1698 print_text(h, n->child != NULL && n->child->next == NULL && 1699 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1700 break; 1701 case MDOC_Bro: 1702 case MDOC_Brq: 1703 print_text(h, "\\(lC"); 1704 break; 1705 case MDOC_Bo: 1706 case MDOC_Bq: 1707 print_text(h, "\\(lB"); 1708 break; 1709 case MDOC_Oo: 1710 case MDOC_Op: 1711 print_text(h, "\\(lB"); 1712 h->flags |= HTML_NOSPACE; 1713 print_otag(h, TAG_SPAN, "c", "Op"); 1714 break; 1715 case MDOC_En: 1716 if (NULL == n->norm->Es || 1717 NULL == n->norm->Es->child) 1718 return 1; 1719 print_text(h, n->norm->Es->child->string); 1720 break; 1721 case MDOC_Do: 1722 case MDOC_Dq: 1723 case MDOC_Qo: 1724 case MDOC_Qq: 1725 print_text(h, "\\(lq"); 1726 break; 1727 case MDOC_Po: 1728 case MDOC_Pq: 1729 print_text(h, "("); 1730 break; 1731 case MDOC_Ql: 1732 print_text(h, "\\(oq"); 1733 h->flags |= HTML_NOSPACE; 1734 print_otag(h, TAG_CODE, "c", "Li"); 1735 break; 1736 case MDOC_So: 1737 case MDOC_Sq: 1738 print_text(h, "\\(oq"); 1739 break; 1740 default: 1741 abort(); 1742 } 1743 1744 h->flags |= HTML_NOSPACE; 1745 return 1; 1746 } 1747 1748 static void 1749 mdoc_quote_post(MDOC_ARGS) 1750 { 1751 1752 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1753 return; 1754 1755 h->flags |= HTML_NOSPACE; 1756 1757 switch (n->tok) { 1758 case MDOC_Ao: 1759 case MDOC_Aq: 1760 print_text(h, n->child != NULL && n->child->next == NULL && 1761 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1762 break; 1763 case MDOC_Bro: 1764 case MDOC_Brq: 1765 print_text(h, "\\(rC"); 1766 break; 1767 case MDOC_Oo: 1768 case MDOC_Op: 1769 case MDOC_Bo: 1770 case MDOC_Bq: 1771 print_text(h, "\\(rB"); 1772 break; 1773 case MDOC_En: 1774 if (n->norm->Es == NULL || 1775 n->norm->Es->child == NULL || 1776 n->norm->Es->child->next == NULL) 1777 h->flags &= ~HTML_NOSPACE; 1778 else 1779 print_text(h, n->norm->Es->child->next->string); 1780 break; 1781 case MDOC_Qo: 1782 case MDOC_Qq: 1783 case MDOC_Do: 1784 case MDOC_Dq: 1785 print_text(h, "\\(rq"); 1786 break; 1787 case MDOC_Po: 1788 case MDOC_Pq: 1789 print_text(h, ")"); 1790 break; 1791 case MDOC_Ql: 1792 case MDOC_So: 1793 case MDOC_Sq: 1794 print_text(h, "\\(cq"); 1795 break; 1796 default: 1797 abort(); 1798 } 1799 } 1800 1801 static int 1802 mdoc_eo_pre(MDOC_ARGS) 1803 { 1804 1805 if (n->type != ROFFT_BODY) 1806 return 1; 1807 1808 if (n->end == ENDBODY_NOT && 1809 n->parent->head->child == NULL && 1810 n->child != NULL && 1811 n->child->end != ENDBODY_NOT) 1812 print_text(h, "\\&"); 1813 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1814 n->parent->head->child != NULL && (n->child != NULL || 1815 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1816 h->flags |= HTML_NOSPACE; 1817 return 1; 1818 } 1819 1820 static void 1821 mdoc_eo_post(MDOC_ARGS) 1822 { 1823 int body, tail; 1824 1825 if (n->type != ROFFT_BODY) 1826 return; 1827 1828 if (n->end != ENDBODY_NOT) { 1829 h->flags &= ~HTML_NOSPACE; 1830 return; 1831 } 1832 1833 body = n->child != NULL || n->parent->head->child != NULL; 1834 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1835 1836 if (body && tail) 1837 h->flags |= HTML_NOSPACE; 1838 else if ( ! tail) 1839 h->flags &= ~HTML_NOSPACE; 1840 } 1841