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