1 /* $OpenBSD: mdoc_html.c,v 1.171 2018/04/13 16:27:14 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(const struct roff_meta *, 52 struct html *); 53 static void print_mdoc_node(MDOC_ARGS); 54 static void print_mdoc_nodelist(MDOC_ARGS); 55 static void synopsis_pre(struct html *, 56 const struct roff_node *); 57 58 static void mdoc_root_post(const struct roff_meta *, 59 struct html *); 60 static int mdoc_root_pre(const struct roff_meta *, 61 struct html *); 62 63 static void mdoc__x_post(MDOC_ARGS); 64 static int mdoc__x_pre(MDOC_ARGS); 65 static int mdoc_ad_pre(MDOC_ARGS); 66 static int mdoc_an_pre(MDOC_ARGS); 67 static int mdoc_ap_pre(MDOC_ARGS); 68 static int mdoc_ar_pre(MDOC_ARGS); 69 static int mdoc_bd_pre(MDOC_ARGS); 70 static int mdoc_bf_pre(MDOC_ARGS); 71 static void mdoc_bk_post(MDOC_ARGS); 72 static int mdoc_bk_pre(MDOC_ARGS); 73 static int mdoc_bl_pre(MDOC_ARGS); 74 static int mdoc_cd_pre(MDOC_ARGS); 75 static int mdoc_cm_pre(MDOC_ARGS); 76 static int mdoc_d1_pre(MDOC_ARGS); 77 static int mdoc_dv_pre(MDOC_ARGS); 78 static int mdoc_fa_pre(MDOC_ARGS); 79 static int mdoc_fd_pre(MDOC_ARGS); 80 static int mdoc_fl_pre(MDOC_ARGS); 81 static int mdoc_fn_pre(MDOC_ARGS); 82 static int mdoc_ft_pre(MDOC_ARGS); 83 static int mdoc_em_pre(MDOC_ARGS); 84 static void mdoc_eo_post(MDOC_ARGS); 85 static int mdoc_eo_pre(MDOC_ARGS); 86 static int mdoc_er_pre(MDOC_ARGS); 87 static int mdoc_ev_pre(MDOC_ARGS); 88 static int mdoc_ex_pre(MDOC_ARGS); 89 static void mdoc_fo_post(MDOC_ARGS); 90 static int mdoc_fo_pre(MDOC_ARGS); 91 static int mdoc_ic_pre(MDOC_ARGS); 92 static int mdoc_igndelim_pre(MDOC_ARGS); 93 static int mdoc_in_pre(MDOC_ARGS); 94 static int mdoc_it_pre(MDOC_ARGS); 95 static int mdoc_lb_pre(MDOC_ARGS); 96 static int mdoc_li_pre(MDOC_ARGS); 97 static int mdoc_lk_pre(MDOC_ARGS); 98 static int mdoc_mt_pre(MDOC_ARGS); 99 static int mdoc_ms_pre(MDOC_ARGS); 100 static int mdoc_nd_pre(MDOC_ARGS); 101 static int mdoc_nm_pre(MDOC_ARGS); 102 static int mdoc_no_pre(MDOC_ARGS); 103 static int mdoc_ns_pre(MDOC_ARGS); 104 static int mdoc_pa_pre(MDOC_ARGS); 105 static void mdoc_pf_post(MDOC_ARGS); 106 static int mdoc_pp_pre(MDOC_ARGS); 107 static void mdoc_quote_post(MDOC_ARGS); 108 static int mdoc_quote_pre(MDOC_ARGS); 109 static int mdoc_rs_pre(MDOC_ARGS); 110 static int mdoc_sh_pre(MDOC_ARGS); 111 static int mdoc_skip_pre(MDOC_ARGS); 112 static int mdoc_sm_pre(MDOC_ARGS); 113 static int mdoc_ss_pre(MDOC_ARGS); 114 static int mdoc_st_pre(MDOC_ARGS); 115 static int mdoc_sx_pre(MDOC_ARGS); 116 static int mdoc_sy_pre(MDOC_ARGS); 117 static int mdoc_va_pre(MDOC_ARGS); 118 static int mdoc_vt_pre(MDOC_ARGS); 119 static int mdoc_xr_pre(MDOC_ARGS); 120 static int mdoc_xx_pre(MDOC_ARGS); 121 122 static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { 123 {NULL, NULL}, /* Dd */ 124 {NULL, NULL}, /* Dt */ 125 {NULL, NULL}, /* Os */ 126 {mdoc_sh_pre, NULL }, /* Sh */ 127 {mdoc_ss_pre, NULL }, /* Ss */ 128 {mdoc_pp_pre, NULL}, /* Pp */ 129 {mdoc_d1_pre, NULL}, /* D1 */ 130 {mdoc_d1_pre, NULL}, /* Dl */ 131 {mdoc_bd_pre, NULL}, /* Bd */ 132 {NULL, NULL}, /* Ed */ 133 {mdoc_bl_pre, NULL}, /* Bl */ 134 {NULL, NULL}, /* El */ 135 {mdoc_it_pre, NULL}, /* It */ 136 {mdoc_ad_pre, NULL}, /* Ad */ 137 {mdoc_an_pre, NULL}, /* An */ 138 {mdoc_ap_pre, NULL}, /* Ap */ 139 {mdoc_ar_pre, NULL}, /* Ar */ 140 {mdoc_cd_pre, NULL}, /* Cd */ 141 {mdoc_cm_pre, NULL}, /* Cm */ 142 {mdoc_dv_pre, NULL}, /* Dv */ 143 {mdoc_er_pre, NULL}, /* Er */ 144 {mdoc_ev_pre, NULL}, /* Ev */ 145 {mdoc_ex_pre, NULL}, /* Ex */ 146 {mdoc_fa_pre, NULL}, /* Fa */ 147 {mdoc_fd_pre, NULL}, /* Fd */ 148 {mdoc_fl_pre, NULL}, /* Fl */ 149 {mdoc_fn_pre, NULL}, /* Fn */ 150 {mdoc_ft_pre, NULL}, /* Ft */ 151 {mdoc_ic_pre, NULL}, /* Ic */ 152 {mdoc_in_pre, NULL}, /* In */ 153 {mdoc_li_pre, NULL}, /* Li */ 154 {mdoc_nd_pre, NULL}, /* Nd */ 155 {mdoc_nm_pre, NULL}, /* Nm */ 156 {mdoc_quote_pre, mdoc_quote_post}, /* Op */ 157 {mdoc_ft_pre, NULL}, /* Ot */ 158 {mdoc_pa_pre, NULL}, /* Pa */ 159 {mdoc_ex_pre, NULL}, /* Rv */ 160 {mdoc_st_pre, NULL}, /* St */ 161 {mdoc_va_pre, NULL}, /* Va */ 162 {mdoc_vt_pre, NULL}, /* Vt */ 163 {mdoc_xr_pre, NULL}, /* Xr */ 164 {mdoc__x_pre, mdoc__x_post}, /* %A */ 165 {mdoc__x_pre, mdoc__x_post}, /* %B */ 166 {mdoc__x_pre, mdoc__x_post}, /* %D */ 167 {mdoc__x_pre, mdoc__x_post}, /* %I */ 168 {mdoc__x_pre, mdoc__x_post}, /* %J */ 169 {mdoc__x_pre, mdoc__x_post}, /* %N */ 170 {mdoc__x_pre, mdoc__x_post}, /* %O */ 171 {mdoc__x_pre, mdoc__x_post}, /* %P */ 172 {mdoc__x_pre, mdoc__x_post}, /* %R */ 173 {mdoc__x_pre, mdoc__x_post}, /* %T */ 174 {mdoc__x_pre, mdoc__x_post}, /* %V */ 175 {NULL, NULL}, /* Ac */ 176 {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ 177 {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ 178 {mdoc_xx_pre, NULL}, /* At */ 179 {NULL, NULL}, /* Bc */ 180 {mdoc_bf_pre, NULL}, /* Bf */ 181 {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ 182 {mdoc_quote_pre, mdoc_quote_post}, /* Bq */ 183 {mdoc_xx_pre, NULL}, /* Bsx */ 184 {mdoc_xx_pre, NULL}, /* Bx */ 185 {mdoc_skip_pre, NULL}, /* Db */ 186 {NULL, NULL}, /* Dc */ 187 {mdoc_quote_pre, mdoc_quote_post}, /* Do */ 188 {mdoc_quote_pre, mdoc_quote_post}, /* Dq */ 189 {NULL, NULL}, /* Ec */ /* FIXME: no space */ 190 {NULL, NULL}, /* Ef */ 191 {mdoc_em_pre, NULL}, /* Em */ 192 {mdoc_eo_pre, mdoc_eo_post}, /* Eo */ 193 {mdoc_xx_pre, NULL}, /* Fx */ 194 {mdoc_ms_pre, NULL}, /* Ms */ 195 {mdoc_no_pre, NULL}, /* No */ 196 {mdoc_ns_pre, NULL}, /* Ns */ 197 {mdoc_xx_pre, NULL}, /* Nx */ 198 {mdoc_xx_pre, NULL}, /* Ox */ 199 {NULL, NULL}, /* Pc */ 200 {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */ 201 {mdoc_quote_pre, mdoc_quote_post}, /* Po */ 202 {mdoc_quote_pre, mdoc_quote_post}, /* Pq */ 203 {NULL, NULL}, /* Qc */ 204 {mdoc_quote_pre, mdoc_quote_post}, /* Ql */ 205 {mdoc_quote_pre, mdoc_quote_post}, /* Qo */ 206 {mdoc_quote_pre, mdoc_quote_post}, /* Qq */ 207 {NULL, NULL}, /* Re */ 208 {mdoc_rs_pre, NULL}, /* Rs */ 209 {NULL, NULL}, /* Sc */ 210 {mdoc_quote_pre, mdoc_quote_post}, /* So */ 211 {mdoc_quote_pre, mdoc_quote_post}, /* Sq */ 212 {mdoc_sm_pre, NULL}, /* Sm */ 213 {mdoc_sx_pre, NULL}, /* Sx */ 214 {mdoc_sy_pre, NULL}, /* Sy */ 215 {NULL, NULL}, /* Tn */ 216 {mdoc_xx_pre, NULL}, /* Ux */ 217 {NULL, NULL}, /* Xc */ 218 {NULL, NULL}, /* Xo */ 219 {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ 220 {NULL, NULL}, /* Fc */ 221 {mdoc_quote_pre, mdoc_quote_post}, /* Oo */ 222 {NULL, NULL}, /* Oc */ 223 {mdoc_bk_pre, mdoc_bk_post}, /* Bk */ 224 {NULL, NULL}, /* Ek */ 225 {NULL, NULL}, /* Bt */ 226 {NULL, NULL}, /* Hf */ 227 {mdoc_em_pre, NULL}, /* Fr */ 228 {NULL, NULL}, /* Ud */ 229 {mdoc_lb_pre, NULL}, /* Lb */ 230 {mdoc_pp_pre, NULL}, /* Lp */ 231 {mdoc_lk_pre, NULL}, /* Lk */ 232 {mdoc_mt_pre, NULL}, /* Mt */ 233 {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ 234 {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ 235 {NULL, NULL}, /* Brc */ 236 {mdoc__x_pre, mdoc__x_post}, /* %C */ 237 {mdoc_skip_pre, NULL}, /* Es */ 238 {mdoc_quote_pre, mdoc_quote_post}, /* En */ 239 {mdoc_xx_pre, NULL}, /* Dx */ 240 {mdoc__x_pre, mdoc__x_post}, /* %Q */ 241 {mdoc__x_pre, mdoc__x_post}, /* %U */ 242 {NULL, NULL}, /* Ta */ 243 }; 244 static const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd; 245 246 247 /* 248 * See the same function in mdoc_term.c for documentation. 249 */ 250 static void 251 synopsis_pre(struct html *h, const struct roff_node *n) 252 { 253 254 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) 255 return; 256 257 if (n->prev->tok == n->tok && 258 MDOC_Fo != n->tok && 259 MDOC_Ft != n->tok && 260 MDOC_Fn != n->tok) { 261 print_otag(h, TAG_BR, ""); 262 return; 263 } 264 265 switch (n->prev->tok) { 266 case MDOC_Fd: 267 case MDOC_Fn: 268 case MDOC_Fo: 269 case MDOC_In: 270 case MDOC_Vt: 271 print_paragraph(h); 272 break; 273 case MDOC_Ft: 274 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 275 print_paragraph(h); 276 break; 277 } 278 /* FALLTHROUGH */ 279 default: 280 print_otag(h, TAG_BR, ""); 281 break; 282 } 283 } 284 285 void 286 html_mdoc(void *arg, const struct roff_man *mdoc) 287 { 288 struct html *h; 289 struct roff_node *n; 290 struct tag *t; 291 292 h = (struct html *)arg; 293 n = mdoc->first->child; 294 295 if ((h->oflags & HTML_FRAGMENT) == 0) { 296 print_gen_decls(h); 297 print_otag(h, TAG_HTML, ""); 298 if (n->type == ROFFT_COMMENT) 299 print_gen_comment(h, n); 300 t = print_otag(h, TAG_HEAD, ""); 301 print_mdoc_head(&mdoc->meta, h); 302 print_tagq(h, t); 303 print_otag(h, TAG_BODY, ""); 304 } 305 306 mdoc_root_pre(&mdoc->meta, h); 307 t = print_otag(h, TAG_DIV, "c", "manual-text"); 308 print_mdoc_nodelist(&mdoc->meta, n, h); 309 print_tagq(h, t); 310 mdoc_root_post(&mdoc->meta, h); 311 print_tagq(h, NULL); 312 } 313 314 static void 315 print_mdoc_head(const struct roff_meta *meta, struct html *h) 316 { 317 char *cp; 318 319 print_gen_head(h); 320 321 if (meta->arch != NULL && meta->msec != NULL) 322 mandoc_asprintf(&cp, "%s(%s) (%s)", meta->title, 323 meta->msec, meta->arch); 324 else if (meta->msec != NULL) 325 mandoc_asprintf(&cp, "%s(%s)", meta->title, meta->msec); 326 else if (meta->arch != NULL) 327 mandoc_asprintf(&cp, "%s (%s)", meta->title, meta->arch); 328 else 329 cp = mandoc_strdup(meta->title); 330 331 print_otag(h, TAG_TITLE, ""); 332 print_text(h, cp); 333 free(cp); 334 } 335 336 static void 337 print_mdoc_nodelist(MDOC_ARGS) 338 { 339 340 while (n != NULL) { 341 print_mdoc_node(meta, n, h); 342 n = n->next; 343 } 344 } 345 346 static void 347 print_mdoc_node(MDOC_ARGS) 348 { 349 int child; 350 struct tag *t; 351 352 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 353 return; 354 355 child = 1; 356 t = h->tag; 357 n->flags &= ~NODE_ENDED; 358 359 switch (n->type) { 360 case ROFFT_TEXT: 361 /* No tables in this mode... */ 362 assert(NULL == h->tblt); 363 364 /* 365 * Make sure that if we're in a literal mode already 366 * (i.e., within a <PRE>) don't print the newline. 367 */ 368 if (*n->string == ' ' && n->flags & NODE_LINE && 369 (h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0) 370 print_otag(h, TAG_BR, ""); 371 if (NODE_DELIMC & n->flags) 372 h->flags |= HTML_NOSPACE; 373 print_text(h, n->string); 374 if (NODE_DELIMO & n->flags) 375 h->flags |= HTML_NOSPACE; 376 return; 377 case ROFFT_EQN: 378 print_eqn(h, n->eqn); 379 break; 380 case ROFFT_TBL: 381 /* 382 * This will take care of initialising all of the table 383 * state data for the first table, then tearing it down 384 * for the last one. 385 */ 386 print_tbl(h, n->span); 387 return; 388 default: 389 /* 390 * Close out the current table, if it's open, and unset 391 * the "meta" table state. This will be reopened on the 392 * next table element. 393 */ 394 if (h->tblt != NULL) { 395 print_tblclose(h); 396 t = h->tag; 397 } 398 assert(h->tblt == NULL); 399 if (n->tok < ROFF_MAX) { 400 roff_html_pre(h, n); 401 child = 0; 402 break; 403 } 404 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 405 if (mdocs[n->tok].pre != NULL && 406 (n->end == ENDBODY_NOT || n->child != NULL)) 407 child = (*mdocs[n->tok].pre)(meta, n, h); 408 break; 409 } 410 411 if (h->flags & HTML_KEEP && n->flags & NODE_LINE) { 412 h->flags &= ~HTML_KEEP; 413 h->flags |= HTML_PREKEEP; 414 } 415 416 if (child && n->child) 417 print_mdoc_nodelist(meta, n->child, h); 418 419 print_stagq(h, t); 420 421 switch (n->type) { 422 case ROFFT_EQN: 423 break; 424 default: 425 if (n->tok < ROFF_MAX || 426 mdocs[n->tok].post == NULL || 427 n->flags & NODE_ENDED) 428 break; 429 (*mdocs[n->tok].post)(meta, n, h); 430 if (n->end != ENDBODY_NOT) 431 n->body->flags |= NODE_ENDED; 432 break; 433 } 434 } 435 436 static void 437 mdoc_root_post(const struct roff_meta *meta, struct html *h) 438 { 439 struct tag *t, *tt; 440 441 t = print_otag(h, TAG_TABLE, "c", "foot"); 442 tt = print_otag(h, TAG_TR, ""); 443 444 print_otag(h, TAG_TD, "c", "foot-date"); 445 print_text(h, meta->date); 446 print_stagq(h, tt); 447 448 print_otag(h, TAG_TD, "c", "foot-os"); 449 print_text(h, meta->os); 450 print_tagq(h, t); 451 } 452 453 static int 454 mdoc_root_pre(const struct roff_meta *meta, struct html *h) 455 { 456 struct tag *t, *tt; 457 char *volume, *title; 458 459 if (NULL == meta->arch) 460 volume = mandoc_strdup(meta->vol); 461 else 462 mandoc_asprintf(&volume, "%s (%s)", 463 meta->vol, meta->arch); 464 465 if (NULL == meta->msec) 466 title = mandoc_strdup(meta->title); 467 else 468 mandoc_asprintf(&title, "%s(%s)", 469 meta->title, meta->msec); 470 471 t = print_otag(h, TAG_TABLE, "c", "head"); 472 tt = print_otag(h, TAG_TR, ""); 473 474 print_otag(h, TAG_TD, "c", "head-ltitle"); 475 print_text(h, title); 476 print_stagq(h, tt); 477 478 print_otag(h, TAG_TD, "c", "head-vol"); 479 print_text(h, volume); 480 print_stagq(h, tt); 481 482 print_otag(h, TAG_TD, "c", "head-rtitle"); 483 print_text(h, title); 484 print_tagq(h, t); 485 486 free(title); 487 free(volume); 488 return 1; 489 } 490 491 static char * 492 cond_id(const struct roff_node *n) 493 { 494 if (n->child != NULL && 495 n->child->type == ROFFT_TEXT && 496 (n->prev == NULL || 497 (n->prev->type == ROFFT_TEXT && 498 strcmp(n->prev->string, "|") == 0)) && 499 (n->parent->tok == MDOC_It || 500 (n->parent->tok == MDOC_Xo && 501 n->parent->parent->prev == NULL && 502 n->parent->parent->parent->tok == MDOC_It))) 503 return html_make_id(n); 504 return NULL; 505 } 506 507 static int 508 mdoc_sh_pre(MDOC_ARGS) 509 { 510 char *id; 511 512 switch (n->type) { 513 case ROFFT_HEAD: 514 id = html_make_id(n); 515 print_otag(h, TAG_H1, "cTi", "Sh", id); 516 if (id != NULL) 517 print_otag(h, TAG_A, "chR", "selflink", id); 518 free(id); 519 break; 520 case ROFFT_BODY: 521 if (n->sec == SEC_AUTHORS) 522 h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); 523 break; 524 default: 525 break; 526 } 527 return 1; 528 } 529 530 static int 531 mdoc_ss_pre(MDOC_ARGS) 532 { 533 char *id; 534 535 if (n->type != ROFFT_HEAD) 536 return 1; 537 538 id = html_make_id(n); 539 print_otag(h, TAG_H2, "cTi", "Ss", id); 540 if (id != NULL) 541 print_otag(h, TAG_A, "chR", "selflink", id); 542 free(id); 543 return 1; 544 } 545 546 static int 547 mdoc_fl_pre(MDOC_ARGS) 548 { 549 char *id; 550 551 if ((id = cond_id(n)) != NULL) 552 print_otag(h, TAG_A, "chR", "selflink", id); 553 print_otag(h, TAG_B, "cTi", "Fl", id); 554 free(id); 555 556 print_text(h, "\\-"); 557 if (!(n->child == NULL && 558 (n->next == NULL || 559 n->next->type == ROFFT_TEXT || 560 n->next->flags & NODE_LINE))) 561 h->flags |= HTML_NOSPACE; 562 563 return 1; 564 } 565 566 static int 567 mdoc_cm_pre(MDOC_ARGS) 568 { 569 char *id; 570 571 if ((id = cond_id(n)) != NULL) 572 print_otag(h, TAG_A, "chR", "selflink", id); 573 print_otag(h, TAG_B, "cTi", "Cm", id); 574 free(id); 575 return 1; 576 } 577 578 static int 579 mdoc_nd_pre(MDOC_ARGS) 580 { 581 if (n->type != ROFFT_BODY) 582 return 1; 583 584 /* XXX: this tag in theory can contain block elements. */ 585 586 print_text(h, "\\(em"); 587 print_otag(h, TAG_SPAN, "cT", "Nd"); 588 return 1; 589 } 590 591 static int 592 mdoc_nm_pre(MDOC_ARGS) 593 { 594 switch (n->type) { 595 case ROFFT_HEAD: 596 print_otag(h, TAG_TD, ""); 597 /* FALLTHROUGH */ 598 case ROFFT_ELEM: 599 print_otag(h, TAG_B, "cT", "Nm"); 600 return 1; 601 case ROFFT_BODY: 602 print_otag(h, TAG_TD, ""); 603 return 1; 604 default: 605 break; 606 } 607 synopsis_pre(h, n); 608 print_otag(h, TAG_TABLE, "c", "Nm"); 609 print_otag(h, TAG_TR, ""); 610 return 1; 611 } 612 613 static int 614 mdoc_xr_pre(MDOC_ARGS) 615 { 616 if (NULL == n->child) 617 return 0; 618 619 if (h->base_man) 620 print_otag(h, TAG_A, "cThM", "Xr", 621 n->child->string, n->child->next == NULL ? 622 NULL : n->child->next->string); 623 else 624 print_otag(h, TAG_A, "cT", "Xr"); 625 626 n = n->child; 627 print_text(h, n->string); 628 629 if (NULL == (n = n->next)) 630 return 0; 631 632 h->flags |= HTML_NOSPACE; 633 print_text(h, "("); 634 h->flags |= HTML_NOSPACE; 635 print_text(h, n->string); 636 h->flags |= HTML_NOSPACE; 637 print_text(h, ")"); 638 return 0; 639 } 640 641 static int 642 mdoc_ns_pre(MDOC_ARGS) 643 { 644 645 if ( ! (NODE_LINE & n->flags)) 646 h->flags |= HTML_NOSPACE; 647 return 1; 648 } 649 650 static int 651 mdoc_ar_pre(MDOC_ARGS) 652 { 653 print_otag(h, TAG_VAR, "cT", "Ar"); 654 return 1; 655 } 656 657 static int 658 mdoc_xx_pre(MDOC_ARGS) 659 { 660 print_otag(h, TAG_SPAN, "c", "Ux"); 661 return 1; 662 } 663 664 static int 665 mdoc_it_pre(MDOC_ARGS) 666 { 667 const struct roff_node *bl; 668 struct tag *t; 669 const char *cattr; 670 enum mdoc_list type; 671 672 bl = n->parent; 673 while (bl->tok != MDOC_Bl) 674 bl = bl->parent; 675 type = bl->norm->Bl.type; 676 677 switch (type) { 678 case LIST_bullet: 679 cattr = "It-bullet"; 680 break; 681 case LIST_dash: 682 case LIST_hyphen: 683 cattr = "It-dash"; 684 break; 685 case LIST_item: 686 cattr = "It-item"; 687 break; 688 case LIST_enum: 689 cattr = "It-enum"; 690 break; 691 case LIST_diag: 692 cattr = "It-diag"; 693 break; 694 case LIST_hang: 695 cattr = "It-hang"; 696 break; 697 case LIST_inset: 698 cattr = "It-inset"; 699 break; 700 case LIST_ohang: 701 cattr = "It-ohang"; 702 break; 703 case LIST_tag: 704 cattr = "It-tag"; 705 break; 706 case LIST_column: 707 cattr = "It-column"; 708 break; 709 default: 710 break; 711 } 712 713 switch (type) { 714 case LIST_bullet: 715 case LIST_dash: 716 case LIST_hyphen: 717 case LIST_item: 718 case LIST_enum: 719 switch (n->type) { 720 case ROFFT_HEAD: 721 return 0; 722 case ROFFT_BODY: 723 print_otag(h, TAG_LI, "c", cattr); 724 break; 725 default: 726 break; 727 } 728 break; 729 case LIST_diag: 730 case LIST_hang: 731 case LIST_inset: 732 case LIST_ohang: 733 switch (n->type) { 734 case ROFFT_HEAD: 735 print_otag(h, TAG_DT, "c", cattr); 736 if (type == LIST_diag) 737 print_otag(h, TAG_B, "c", cattr); 738 break; 739 case ROFFT_BODY: 740 print_otag(h, TAG_DD, "csw*+l", cattr, 741 bl->norm->Bl.width); 742 break; 743 default: 744 break; 745 } 746 break; 747 case LIST_tag: 748 switch (n->type) { 749 case ROFFT_HEAD: 750 if (h->style != NULL && !bl->norm->Bl.comp && 751 (n->parent->prev == NULL || 752 n->parent->prev->body == NULL || 753 n->parent->prev->body->child != NULL)) { 754 t = print_otag(h, TAG_DT, "csw*+-l", 755 cattr, bl->norm->Bl.width); 756 print_text(h, "\\ "); 757 print_tagq(h, t); 758 t = print_otag(h, TAG_DD, "c", cattr); 759 print_text(h, "\\ "); 760 print_tagq(h, t); 761 } 762 print_otag(h, TAG_DT, "csw*+-l", cattr, 763 bl->norm->Bl.width); 764 break; 765 case ROFFT_BODY: 766 if (n->child == NULL) { 767 print_otag(h, TAG_DD, "css?", cattr, 768 "width", "auto"); 769 print_text(h, "\\ "); 770 } else 771 print_otag(h, TAG_DD, "c", cattr); 772 break; 773 default: 774 break; 775 } 776 break; 777 case LIST_column: 778 switch (n->type) { 779 case ROFFT_HEAD: 780 break; 781 case ROFFT_BODY: 782 print_otag(h, TAG_TD, "c", cattr); 783 break; 784 default: 785 print_otag(h, TAG_TR, "c", cattr); 786 } 787 default: 788 break; 789 } 790 791 return 1; 792 } 793 794 static int 795 mdoc_bl_pre(MDOC_ARGS) 796 { 797 char cattr[21]; 798 struct tag *t; 799 struct mdoc_bl *bl; 800 size_t i; 801 enum htmltag elemtype; 802 803 bl = &n->norm->Bl; 804 805 switch (n->type) { 806 case ROFFT_BODY: 807 return 1; 808 809 case ROFFT_HEAD: 810 if (bl->type != LIST_column || bl->ncols == 0) 811 return 0; 812 813 /* 814 * For each column, print out the <COL> tag with our 815 * suggested width. The last column gets min-width, as 816 * in terminal mode it auto-sizes to the width of the 817 * screen and we want to preserve that behaviour. 818 */ 819 820 t = print_otag(h, TAG_COLGROUP, ""); 821 for (i = 0; i < bl->ncols - 1; i++) 822 print_otag(h, TAG_COL, "sw+w", bl->cols[i]); 823 print_otag(h, TAG_COL, "swW", bl->cols[i]); 824 print_tagq(h, t); 825 return 0; 826 827 default: 828 break; 829 } 830 831 switch (bl->type) { 832 case LIST_bullet: 833 elemtype = TAG_UL; 834 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 835 break; 836 case LIST_dash: 837 case LIST_hyphen: 838 elemtype = TAG_UL; 839 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 840 break; 841 case LIST_item: 842 elemtype = TAG_UL; 843 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 844 break; 845 case LIST_enum: 846 elemtype = TAG_OL; 847 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 848 break; 849 case LIST_diag: 850 elemtype = TAG_DL; 851 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 852 break; 853 case LIST_hang: 854 elemtype = TAG_DL; 855 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 856 break; 857 case LIST_inset: 858 elemtype = TAG_DL; 859 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 860 break; 861 case LIST_ohang: 862 elemtype = TAG_DL; 863 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 864 break; 865 case LIST_tag: 866 if (bl->offs) 867 print_otag(h, TAG_DIV, "cswl", "Bl-tag", bl->offs); 868 print_otag(h, TAG_DL, "csw*+l", bl->comp ? 869 "Bl-tag Bl-compact" : "Bl-tag", bl->width); 870 return 1; 871 case LIST_column: 872 elemtype = TAG_TABLE; 873 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 874 break; 875 default: 876 abort(); 877 } 878 if (bl->comp) 879 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 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