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