1 /* $OpenBSD: mdoc_html.c,v 1.89 2014/11/30 05:28:00 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2014 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 AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.h" 28 #include "mandoc_aux.h" 29 #include "out.h" 30 #include "html.h" 31 #include "mdoc.h" 32 #include "main.h" 33 34 #define INDENT 5 35 36 #define MDOC_ARGS const struct mdoc_meta *meta, \ 37 const struct mdoc_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 void print_mdoc(MDOC_ARGS); 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 mdoc_node *); 55 56 static void a2width(const char *, struct roffsu *); 57 58 static void mdoc_root_post(MDOC_ARGS); 59 static int mdoc_root_pre(MDOC_ARGS); 60 61 static void mdoc__x_post(MDOC_ARGS); 62 static int mdoc__x_pre(MDOC_ARGS); 63 static int mdoc_ad_pre(MDOC_ARGS); 64 static int mdoc_an_pre(MDOC_ARGS); 65 static int mdoc_ap_pre(MDOC_ARGS); 66 static int mdoc_ar_pre(MDOC_ARGS); 67 static int mdoc_bd_pre(MDOC_ARGS); 68 static int mdoc_bf_pre(MDOC_ARGS); 69 static void mdoc_bk_post(MDOC_ARGS); 70 static int mdoc_bk_pre(MDOC_ARGS); 71 static int mdoc_bl_pre(MDOC_ARGS); 72 static int mdoc_bt_pre(MDOC_ARGS); 73 static int mdoc_bx_pre(MDOC_ARGS); 74 static int mdoc_cd_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 int mdoc_er_pre(MDOC_ARGS); 84 static int mdoc_ev_pre(MDOC_ARGS); 85 static int mdoc_ex_pre(MDOC_ARGS); 86 static void mdoc_fo_post(MDOC_ARGS); 87 static int mdoc_fo_pre(MDOC_ARGS); 88 static int mdoc_ic_pre(MDOC_ARGS); 89 static int mdoc_igndelim_pre(MDOC_ARGS); 90 static int mdoc_in_pre(MDOC_ARGS); 91 static int mdoc_it_pre(MDOC_ARGS); 92 static int mdoc_lb_pre(MDOC_ARGS); 93 static int mdoc_li_pre(MDOC_ARGS); 94 static int mdoc_lk_pre(MDOC_ARGS); 95 static int mdoc_mt_pre(MDOC_ARGS); 96 static int mdoc_ms_pre(MDOC_ARGS); 97 static int mdoc_nd_pre(MDOC_ARGS); 98 static int mdoc_nm_pre(MDOC_ARGS); 99 static int mdoc_no_pre(MDOC_ARGS); 100 static int mdoc_ns_pre(MDOC_ARGS); 101 static int mdoc_pa_pre(MDOC_ARGS); 102 static void mdoc_pf_post(MDOC_ARGS); 103 static int mdoc_pp_pre(MDOC_ARGS); 104 static void mdoc_quote_post(MDOC_ARGS); 105 static int mdoc_quote_pre(MDOC_ARGS); 106 static int mdoc_rs_pre(MDOC_ARGS); 107 static int mdoc_rv_pre(MDOC_ARGS); 108 static int mdoc_sh_pre(MDOC_ARGS); 109 static int mdoc_skip_pre(MDOC_ARGS); 110 static int mdoc_sm_pre(MDOC_ARGS); 111 static int mdoc_sp_pre(MDOC_ARGS); 112 static int mdoc_ss_pre(MDOC_ARGS); 113 static int mdoc_sx_pre(MDOC_ARGS); 114 static int mdoc_sy_pre(MDOC_ARGS); 115 static int mdoc_ud_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] = { 122 {mdoc_ap_pre, NULL}, /* Ap */ 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_ar_pre, NULL}, /* Ar */ 139 {mdoc_cd_pre, NULL}, /* Cd */ 140 {mdoc_fl_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_rv_pre, NULL}, /* Rv */ 159 {NULL, 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 {NULL, 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_bx_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_quote_pre, mdoc_quote_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 {mdoc_bt_pre, NULL}, /* Bt */ 225 {NULL, NULL}, /* Hf */ 226 {mdoc_em_pre, NULL}, /* Fr */ 227 {mdoc_ud_pre, 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_sp_pre, NULL}, /* br */ 241 {mdoc_sp_pre, NULL}, /* sp */ 242 {mdoc__x_pre, mdoc__x_post}, /* %U */ 243 {NULL, NULL}, /* Ta */ 244 {mdoc_skip_pre, NULL}, /* ll */ 245 }; 246 247 static const char * const lists[LIST_MAX] = { 248 NULL, 249 "list-bul", 250 "list-col", 251 "list-dash", 252 "list-diag", 253 "list-enum", 254 "list-hang", 255 "list-hyph", 256 "list-inset", 257 "list-item", 258 "list-ohang", 259 "list-tag" 260 }; 261 262 263 void 264 html_mdoc(void *arg, const struct mdoc *mdoc) 265 { 266 267 print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc), 268 (struct html *)arg); 269 putchar('\n'); 270 } 271 272 /* 273 * Calculate the scaling unit passed in a `-width' argument. This uses 274 * either a native scaling unit (e.g., 1i, 2m) or the string length of 275 * the value. 276 */ 277 static void 278 a2width(const char *p, struct roffsu *su) 279 { 280 281 if ( ! a2roffsu(p, su, SCALE_MAX)) { 282 su->unit = SCALE_EN; 283 su->scale = html_strlen(p); 284 } 285 } 286 287 /* 288 * See the same function in mdoc_term.c for documentation. 289 */ 290 static void 291 synopsis_pre(struct html *h, const struct mdoc_node *n) 292 { 293 294 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) 295 return; 296 297 if (n->prev->tok == n->tok && 298 MDOC_Fo != n->tok && 299 MDOC_Ft != n->tok && 300 MDOC_Fn != n->tok) { 301 print_otag(h, TAG_BR, 0, NULL); 302 return; 303 } 304 305 switch (n->prev->tok) { 306 case MDOC_Fd: 307 /* FALLTHROUGH */ 308 case MDOC_Fn: 309 /* FALLTHROUGH */ 310 case MDOC_Fo: 311 /* FALLTHROUGH */ 312 case MDOC_In: 313 /* FALLTHROUGH */ 314 case MDOC_Vt: 315 print_paragraph(h); 316 break; 317 case MDOC_Ft: 318 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { 319 print_paragraph(h); 320 break; 321 } 322 /* FALLTHROUGH */ 323 default: 324 print_otag(h, TAG_BR, 0, NULL); 325 break; 326 } 327 } 328 329 static void 330 print_mdoc(MDOC_ARGS) 331 { 332 struct tag *t, *tt; 333 struct htmlpair tag; 334 335 PAIR_CLASS_INIT(&tag, "mandoc"); 336 337 if ( ! (HTML_FRAGMENT & h->oflags)) { 338 print_gen_decls(h); 339 t = print_otag(h, TAG_HTML, 0, NULL); 340 tt = print_otag(h, TAG_HEAD, 0, NULL); 341 print_mdoc_head(meta, n, h); 342 print_tagq(h, tt); 343 print_otag(h, TAG_BODY, 0, NULL); 344 print_otag(h, TAG_DIV, 1, &tag); 345 } else 346 t = print_otag(h, TAG_DIV, 1, &tag); 347 348 print_mdoc_nodelist(meta, n, h); 349 print_tagq(h, t); 350 } 351 352 static void 353 print_mdoc_head(MDOC_ARGS) 354 { 355 356 print_gen_head(h); 357 bufinit(h); 358 bufcat(h, meta->title); 359 if (meta->msec) 360 bufcat_fmt(h, "(%s)", meta->msec); 361 if (meta->arch) 362 bufcat_fmt(h, " (%s)", meta->arch); 363 364 print_otag(h, TAG_TITLE, 0, NULL); 365 print_text(h, h->buf); 366 } 367 368 static void 369 print_mdoc_nodelist(MDOC_ARGS) 370 { 371 372 print_mdoc_node(meta, n, h); 373 if (n->next) 374 print_mdoc_nodelist(meta, n->next, h); 375 } 376 377 static void 378 print_mdoc_node(MDOC_ARGS) 379 { 380 int child; 381 struct tag *t; 382 383 child = 1; 384 t = h->tags.head; 385 386 switch (n->type) { 387 case MDOC_ROOT: 388 child = mdoc_root_pre(meta, n, h); 389 break; 390 case MDOC_TEXT: 391 /* No tables in this mode... */ 392 assert(NULL == h->tblt); 393 394 /* 395 * Make sure that if we're in a literal mode already 396 * (i.e., within a <PRE>) don't print the newline. 397 */ 398 if (' ' == *n->string && MDOC_LINE & n->flags) 399 if ( ! (HTML_LITERAL & h->flags)) 400 print_otag(h, TAG_BR, 0, NULL); 401 if (MDOC_DELIMC & n->flags) 402 h->flags |= HTML_NOSPACE; 403 print_text(h, n->string); 404 if (MDOC_DELIMO & n->flags) 405 h->flags |= HTML_NOSPACE; 406 return; 407 case MDOC_EQN: 408 print_eqn(h, n->eqn); 409 break; 410 case MDOC_TBL: 411 /* 412 * This will take care of initialising all of the table 413 * state data for the first table, then tearing it down 414 * for the last one. 415 */ 416 print_tbl(h, n->span); 417 return; 418 default: 419 /* 420 * Close out the current table, if it's open, and unset 421 * the "meta" table state. This will be reopened on the 422 * next table element. 423 */ 424 if (h->tblt != NULL) { 425 print_tblclose(h); 426 t = h->tags.head; 427 } 428 assert(h->tblt == NULL); 429 if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child)) 430 child = (*mdocs[n->tok].pre)(meta, n, h); 431 break; 432 } 433 434 if (HTML_KEEP & h->flags) { 435 if (n->prev ? (n->prev->lastline != n->line) : 436 (n->parent && n->parent->line != n->line)) { 437 h->flags &= ~HTML_KEEP; 438 h->flags |= HTML_PREKEEP; 439 } 440 } 441 442 if (child && n->child) 443 print_mdoc_nodelist(meta, n->child, h); 444 445 print_stagq(h, t); 446 447 switch (n->type) { 448 case MDOC_ROOT: 449 mdoc_root_post(meta, n, h); 450 break; 451 case MDOC_EQN: 452 break; 453 default: 454 if ( ! mdocs[n->tok].post || n->flags & MDOC_ENDED) 455 break; 456 (*mdocs[n->tok].post)(meta, n, h); 457 if (n->end != ENDBODY_NOT) 458 n->pending->flags |= MDOC_ENDED; 459 if (n->end == ENDBODY_NOSPACE) 460 h->flags |= HTML_NOSPACE; 461 break; 462 } 463 } 464 465 static void 466 mdoc_root_post(MDOC_ARGS) 467 { 468 struct htmlpair tag; 469 struct tag *t, *tt; 470 471 PAIR_CLASS_INIT(&tag, "foot"); 472 t = print_otag(h, TAG_TABLE, 1, &tag); 473 474 print_otag(h, TAG_TBODY, 0, NULL); 475 476 tt = print_otag(h, TAG_TR, 0, NULL); 477 478 PAIR_CLASS_INIT(&tag, "foot-date"); 479 print_otag(h, TAG_TD, 1, &tag); 480 print_text(h, meta->date); 481 print_stagq(h, tt); 482 483 PAIR_CLASS_INIT(&tag, "foot-os"); 484 print_otag(h, TAG_TD, 1, &tag); 485 print_text(h, meta->os); 486 print_tagq(h, t); 487 } 488 489 static int 490 mdoc_root_pre(MDOC_ARGS) 491 { 492 struct htmlpair tag; 493 struct tag *t, *tt; 494 char *volume, *title; 495 496 if (NULL == meta->arch) 497 volume = mandoc_strdup(meta->vol); 498 else 499 mandoc_asprintf(&volume, "%s (%s)", 500 meta->vol, meta->arch); 501 502 if (NULL == meta->msec) 503 title = mandoc_strdup(meta->title); 504 else 505 mandoc_asprintf(&title, "%s(%s)", 506 meta->title, meta->msec); 507 508 PAIR_CLASS_INIT(&tag, "head"); 509 t = print_otag(h, TAG_TABLE, 1, &tag); 510 511 print_otag(h, TAG_TBODY, 0, NULL); 512 513 tt = print_otag(h, TAG_TR, 0, NULL); 514 515 PAIR_CLASS_INIT(&tag, "head-ltitle"); 516 print_otag(h, TAG_TD, 1, &tag); 517 print_text(h, title); 518 print_stagq(h, tt); 519 520 PAIR_CLASS_INIT(&tag, "head-vol"); 521 print_otag(h, TAG_TD, 1, &tag); 522 print_text(h, volume); 523 print_stagq(h, tt); 524 525 PAIR_CLASS_INIT(&tag, "head-rtitle"); 526 print_otag(h, TAG_TD, 1, &tag); 527 print_text(h, title); 528 print_tagq(h, t); 529 530 free(title); 531 free(volume); 532 return(1); 533 } 534 535 static int 536 mdoc_sh_pre(MDOC_ARGS) 537 { 538 struct htmlpair tag; 539 540 switch (n->type) { 541 case MDOC_BLOCK: 542 PAIR_CLASS_INIT(&tag, "section"); 543 print_otag(h, TAG_DIV, 1, &tag); 544 return(1); 545 case MDOC_BODY: 546 if (n->sec == SEC_AUTHORS) 547 h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); 548 return(1); 549 default: 550 break; 551 } 552 553 bufinit(h); 554 bufcat(h, "x"); 555 556 for (n = n->child; n && MDOC_TEXT == n->type; ) { 557 bufcat_id(h, n->string); 558 if (NULL != (n = n->next)) 559 bufcat_id(h, " "); 560 } 561 562 if (NULL == n) { 563 PAIR_ID_INIT(&tag, h->buf); 564 print_otag(h, TAG_H1, 1, &tag); 565 } else 566 print_otag(h, TAG_H1, 0, NULL); 567 568 return(1); 569 } 570 571 static int 572 mdoc_ss_pre(MDOC_ARGS) 573 { 574 struct htmlpair tag; 575 576 if (MDOC_BLOCK == n->type) { 577 PAIR_CLASS_INIT(&tag, "subsection"); 578 print_otag(h, TAG_DIV, 1, &tag); 579 return(1); 580 } else if (MDOC_BODY == n->type) 581 return(1); 582 583 bufinit(h); 584 bufcat(h, "x"); 585 586 for (n = n->child; n && MDOC_TEXT == n->type; ) { 587 bufcat_id(h, n->string); 588 if (NULL != (n = n->next)) 589 bufcat_id(h, " "); 590 } 591 592 if (NULL == n) { 593 PAIR_ID_INIT(&tag, h->buf); 594 print_otag(h, TAG_H2, 1, &tag); 595 } else 596 print_otag(h, TAG_H2, 0, NULL); 597 598 return(1); 599 } 600 601 static int 602 mdoc_fl_pre(MDOC_ARGS) 603 { 604 struct htmlpair tag; 605 606 PAIR_CLASS_INIT(&tag, "flag"); 607 print_otag(h, TAG_B, 1, &tag); 608 609 /* `Cm' has no leading hyphen. */ 610 611 if (MDOC_Cm == n->tok) 612 return(1); 613 614 print_text(h, "\\-"); 615 616 if ( ! (n->nchild == 0 && 617 (n->next == NULL || 618 n->next->type == MDOC_TEXT || 619 n->next->flags & MDOC_LINE))) 620 h->flags |= HTML_NOSPACE; 621 622 return(1); 623 } 624 625 static int 626 mdoc_nd_pre(MDOC_ARGS) 627 { 628 struct htmlpair tag; 629 630 if (MDOC_BODY != n->type) 631 return(1); 632 633 /* XXX: this tag in theory can contain block elements. */ 634 635 print_text(h, "\\(em"); 636 PAIR_CLASS_INIT(&tag, "desc"); 637 print_otag(h, TAG_SPAN, 1, &tag); 638 return(1); 639 } 640 641 static int 642 mdoc_nm_pre(MDOC_ARGS) 643 { 644 struct htmlpair tag; 645 struct roffsu su; 646 int len; 647 648 switch (n->type) { 649 case MDOC_ELEM: 650 synopsis_pre(h, n); 651 PAIR_CLASS_INIT(&tag, "name"); 652 print_otag(h, TAG_B, 1, &tag); 653 if (NULL == n->child && meta->name) 654 print_text(h, meta->name); 655 return(1); 656 case MDOC_HEAD: 657 print_otag(h, TAG_TD, 0, NULL); 658 if (NULL == n->child && meta->name) 659 print_text(h, meta->name); 660 return(1); 661 case MDOC_BODY: 662 print_otag(h, TAG_TD, 0, NULL); 663 return(1); 664 default: 665 break; 666 } 667 668 synopsis_pre(h, n); 669 PAIR_CLASS_INIT(&tag, "synopsis"); 670 print_otag(h, TAG_TABLE, 1, &tag); 671 672 for (len = 0, n = n->child; n; n = n->next) 673 if (MDOC_TEXT == n->type) 674 len += html_strlen(n->string); 675 676 if (0 == len && meta->name) 677 len = html_strlen(meta->name); 678 679 SCALE_HS_INIT(&su, len); 680 bufinit(h); 681 bufcat_su(h, "width", &su); 682 PAIR_STYLE_INIT(&tag, h); 683 print_otag(h, TAG_COL, 1, &tag); 684 print_otag(h, TAG_COL, 0, NULL); 685 print_otag(h, TAG_TBODY, 0, NULL); 686 print_otag(h, TAG_TR, 0, NULL); 687 return(1); 688 } 689 690 static int 691 mdoc_xr_pre(MDOC_ARGS) 692 { 693 struct htmlpair tag[2]; 694 695 if (NULL == n->child) 696 return(0); 697 698 PAIR_CLASS_INIT(&tag[0], "link-man"); 699 700 if (h->base_man) { 701 buffmt_man(h, n->child->string, 702 n->child->next ? 703 n->child->next->string : NULL); 704 PAIR_HREF_INIT(&tag[1], h->buf); 705 print_otag(h, TAG_A, 2, tag); 706 } else 707 print_otag(h, TAG_A, 1, tag); 708 709 n = n->child; 710 print_text(h, n->string); 711 712 if (NULL == (n = n->next)) 713 return(0); 714 715 h->flags |= HTML_NOSPACE; 716 print_text(h, "("); 717 h->flags |= HTML_NOSPACE; 718 print_text(h, n->string); 719 h->flags |= HTML_NOSPACE; 720 print_text(h, ")"); 721 return(0); 722 } 723 724 static int 725 mdoc_ns_pre(MDOC_ARGS) 726 { 727 728 if ( ! (MDOC_LINE & n->flags)) 729 h->flags |= HTML_NOSPACE; 730 return(1); 731 } 732 733 static int 734 mdoc_ar_pre(MDOC_ARGS) 735 { 736 struct htmlpair tag; 737 738 PAIR_CLASS_INIT(&tag, "arg"); 739 print_otag(h, TAG_I, 1, &tag); 740 return(1); 741 } 742 743 static int 744 mdoc_xx_pre(MDOC_ARGS) 745 { 746 const char *pp; 747 struct htmlpair tag; 748 int flags; 749 750 switch (n->tok) { 751 case MDOC_Bsx: 752 pp = "BSD/OS"; 753 break; 754 case MDOC_Dx: 755 pp = "DragonFly"; 756 break; 757 case MDOC_Fx: 758 pp = "FreeBSD"; 759 break; 760 case MDOC_Nx: 761 pp = "NetBSD"; 762 break; 763 case MDOC_Ox: 764 pp = "OpenBSD"; 765 break; 766 case MDOC_Ux: 767 pp = "UNIX"; 768 break; 769 default: 770 return(1); 771 } 772 773 PAIR_CLASS_INIT(&tag, "unix"); 774 print_otag(h, TAG_SPAN, 1, &tag); 775 776 print_text(h, pp); 777 if (n->child) { 778 flags = h->flags; 779 h->flags |= HTML_KEEP; 780 print_text(h, n->child->string); 781 h->flags = flags; 782 } 783 return(0); 784 } 785 786 static int 787 mdoc_bx_pre(MDOC_ARGS) 788 { 789 struct htmlpair tag; 790 791 PAIR_CLASS_INIT(&tag, "unix"); 792 print_otag(h, TAG_SPAN, 1, &tag); 793 794 if (NULL != (n = n->child)) { 795 print_text(h, n->string); 796 h->flags |= HTML_NOSPACE; 797 print_text(h, "BSD"); 798 } else { 799 print_text(h, "BSD"); 800 return(0); 801 } 802 803 if (NULL != (n = n->next)) { 804 h->flags |= HTML_NOSPACE; 805 print_text(h, "-"); 806 h->flags |= HTML_NOSPACE; 807 print_text(h, n->string); 808 } 809 810 return(0); 811 } 812 813 static int 814 mdoc_it_pre(MDOC_ARGS) 815 { 816 struct roffsu su; 817 enum mdoc_list type; 818 struct htmlpair tag[2]; 819 const struct mdoc_node *bl; 820 821 bl = n->parent; 822 while (bl && MDOC_Bl != bl->tok) 823 bl = bl->parent; 824 825 assert(bl); 826 827 type = bl->norm->Bl.type; 828 829 assert(lists[type]); 830 PAIR_CLASS_INIT(&tag[0], lists[type]); 831 832 bufinit(h); 833 834 if (MDOC_HEAD == n->type) { 835 switch (type) { 836 case LIST_bullet: 837 /* FALLTHROUGH */ 838 case LIST_dash: 839 /* FALLTHROUGH */ 840 case LIST_item: 841 /* FALLTHROUGH */ 842 case LIST_hyphen: 843 /* FALLTHROUGH */ 844 case LIST_enum: 845 return(0); 846 case LIST_diag: 847 /* FALLTHROUGH */ 848 case LIST_hang: 849 /* FALLTHROUGH */ 850 case LIST_inset: 851 /* FALLTHROUGH */ 852 case LIST_ohang: 853 /* FALLTHROUGH */ 854 case LIST_tag: 855 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 856 bufcat_su(h, "margin-top", &su); 857 PAIR_STYLE_INIT(&tag[1], h); 858 print_otag(h, TAG_DT, 2, tag); 859 if (LIST_diag != type) 860 break; 861 PAIR_CLASS_INIT(&tag[0], "diag"); 862 print_otag(h, TAG_B, 1, tag); 863 break; 864 case LIST_column: 865 break; 866 default: 867 break; 868 } 869 } else if (MDOC_BODY == n->type) { 870 switch (type) { 871 case LIST_bullet: 872 /* FALLTHROUGH */ 873 case LIST_hyphen: 874 /* FALLTHROUGH */ 875 case LIST_dash: 876 /* FALLTHROUGH */ 877 case LIST_enum: 878 /* FALLTHROUGH */ 879 case LIST_item: 880 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 881 bufcat_su(h, "margin-top", &su); 882 PAIR_STYLE_INIT(&tag[1], h); 883 print_otag(h, TAG_LI, 2, tag); 884 break; 885 case LIST_diag: 886 /* FALLTHROUGH */ 887 case LIST_hang: 888 /* FALLTHROUGH */ 889 case LIST_inset: 890 /* FALLTHROUGH */ 891 case LIST_ohang: 892 /* FALLTHROUGH */ 893 case LIST_tag: 894 if (NULL == bl->norm->Bl.width) { 895 print_otag(h, TAG_DD, 1, tag); 896 break; 897 } 898 a2width(bl->norm->Bl.width, &su); 899 bufcat_su(h, "margin-left", &su); 900 PAIR_STYLE_INIT(&tag[1], h); 901 print_otag(h, TAG_DD, 2, tag); 902 break; 903 case LIST_column: 904 SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); 905 bufcat_su(h, "margin-top", &su); 906 PAIR_STYLE_INIT(&tag[1], h); 907 print_otag(h, TAG_TD, 2, tag); 908 break; 909 default: 910 break; 911 } 912 } else { 913 switch (type) { 914 case LIST_column: 915 print_otag(h, TAG_TR, 1, tag); 916 break; 917 default: 918 break; 919 } 920 } 921 922 return(1); 923 } 924 925 static int 926 mdoc_bl_pre(MDOC_ARGS) 927 { 928 int i; 929 struct htmlpair tag[3]; 930 struct roffsu su; 931 char buf[BUFSIZ]; 932 933 if (MDOC_BODY == n->type) { 934 if (LIST_column == n->norm->Bl.type) 935 print_otag(h, TAG_TBODY, 0, NULL); 936 return(1); 937 } 938 939 if (MDOC_HEAD == n->type) { 940 if (LIST_column != n->norm->Bl.type) 941 return(0); 942 943 /* 944 * For each column, print out the <COL> tag with our 945 * suggested width. The last column gets min-width, as 946 * in terminal mode it auto-sizes to the width of the 947 * screen and we want to preserve that behaviour. 948 */ 949 950 for (i = 0; i < (int)n->norm->Bl.ncols; i++) { 951 bufinit(h); 952 a2width(n->norm->Bl.cols[i], &su); 953 if (i < (int)n->norm->Bl.ncols - 1) 954 bufcat_su(h, "width", &su); 955 else 956 bufcat_su(h, "min-width", &su); 957 PAIR_STYLE_INIT(&tag[0], h); 958 print_otag(h, TAG_COL, 1, tag); 959 } 960 961 return(0); 962 } 963 964 SCALE_VS_INIT(&su, 0); 965 bufinit(h); 966 bufcat_su(h, "margin-top", &su); 967 bufcat_su(h, "margin-bottom", &su); 968 PAIR_STYLE_INIT(&tag[0], h); 969 970 assert(lists[n->norm->Bl.type]); 971 (void)strlcpy(buf, "list ", BUFSIZ); 972 (void)strlcat(buf, lists[n->norm->Bl.type], BUFSIZ); 973 PAIR_INIT(&tag[1], ATTR_CLASS, buf); 974 975 /* Set the block's left-hand margin. */ 976 977 if (n->norm->Bl.offs) { 978 a2width(n->norm->Bl.offs, &su); 979 bufcat_su(h, "margin-left", &su); 980 } 981 982 switch (n->norm->Bl.type) { 983 case LIST_bullet: 984 /* FALLTHROUGH */ 985 case LIST_dash: 986 /* FALLTHROUGH */ 987 case LIST_hyphen: 988 /* FALLTHROUGH */ 989 case LIST_item: 990 print_otag(h, TAG_UL, 2, tag); 991 break; 992 case LIST_enum: 993 print_otag(h, TAG_OL, 2, tag); 994 break; 995 case LIST_diag: 996 /* FALLTHROUGH */ 997 case LIST_hang: 998 /* FALLTHROUGH */ 999 case LIST_inset: 1000 /* FALLTHROUGH */ 1001 case LIST_ohang: 1002 /* FALLTHROUGH */ 1003 case LIST_tag: 1004 print_otag(h, TAG_DL, 2, tag); 1005 break; 1006 case LIST_column: 1007 print_otag(h, TAG_TABLE, 2, tag); 1008 break; 1009 default: 1010 abort(); 1011 /* NOTREACHED */ 1012 } 1013 1014 return(1); 1015 } 1016 1017 static int 1018 mdoc_ex_pre(MDOC_ARGS) 1019 { 1020 struct tag *t; 1021 struct htmlpair tag; 1022 int nchild; 1023 1024 if (n->prev) 1025 print_otag(h, TAG_BR, 0, NULL); 1026 1027 PAIR_CLASS_INIT(&tag, "utility"); 1028 1029 print_text(h, "The"); 1030 1031 nchild = n->nchild; 1032 for (n = n->child; n; n = n->next) { 1033 assert(MDOC_TEXT == n->type); 1034 1035 t = print_otag(h, TAG_B, 1, &tag); 1036 print_text(h, n->string); 1037 print_tagq(h, t); 1038 1039 if (nchild > 2 && n->next) { 1040 h->flags |= HTML_NOSPACE; 1041 print_text(h, ","); 1042 } 1043 1044 if (n->next && NULL == n->next->next) 1045 print_text(h, "and"); 1046 } 1047 1048 if (nchild > 1) 1049 print_text(h, "utilities exit\\~0"); 1050 else 1051 print_text(h, "utility exits\\~0"); 1052 1053 print_text(h, "on success, and\\~>0 if an error occurs."); 1054 return(0); 1055 } 1056 1057 static int 1058 mdoc_em_pre(MDOC_ARGS) 1059 { 1060 struct htmlpair tag; 1061 1062 PAIR_CLASS_INIT(&tag, "emph"); 1063 print_otag(h, TAG_SPAN, 1, &tag); 1064 return(1); 1065 } 1066 1067 static int 1068 mdoc_d1_pre(MDOC_ARGS) 1069 { 1070 struct htmlpair tag[2]; 1071 struct roffsu su; 1072 1073 if (MDOC_BLOCK != n->type) 1074 return(1); 1075 1076 SCALE_VS_INIT(&su, 0); 1077 bufinit(h); 1078 bufcat_su(h, "margin-top", &su); 1079 bufcat_su(h, "margin-bottom", &su); 1080 PAIR_STYLE_INIT(&tag[0], h); 1081 print_otag(h, TAG_BLOCKQUOTE, 1, tag); 1082 1083 /* BLOCKQUOTE needs a block body. */ 1084 1085 PAIR_CLASS_INIT(&tag[0], "display"); 1086 print_otag(h, TAG_DIV, 1, tag); 1087 1088 if (MDOC_Dl == n->tok) { 1089 PAIR_CLASS_INIT(&tag[0], "lit"); 1090 print_otag(h, TAG_CODE, 1, tag); 1091 } 1092 1093 return(1); 1094 } 1095 1096 static int 1097 mdoc_sx_pre(MDOC_ARGS) 1098 { 1099 struct htmlpair tag[2]; 1100 1101 bufinit(h); 1102 bufcat(h, "#x"); 1103 1104 for (n = n->child; n; ) { 1105 bufcat_id(h, n->string); 1106 if (NULL != (n = n->next)) 1107 bufcat_id(h, " "); 1108 } 1109 1110 PAIR_CLASS_INIT(&tag[0], "link-sec"); 1111 PAIR_HREF_INIT(&tag[1], h->buf); 1112 1113 print_otag(h, TAG_I, 1, tag); 1114 print_otag(h, TAG_A, 2, tag); 1115 return(1); 1116 } 1117 1118 static int 1119 mdoc_bd_pre(MDOC_ARGS) 1120 { 1121 struct htmlpair tag[2]; 1122 int comp, sv; 1123 const struct mdoc_node *nn; 1124 struct roffsu su; 1125 1126 if (MDOC_HEAD == n->type) 1127 return(0); 1128 1129 if (MDOC_BLOCK == n->type) { 1130 comp = n->norm->Bd.comp; 1131 for (nn = n; nn && ! comp; nn = nn->parent) { 1132 if (MDOC_BLOCK != nn->type) 1133 continue; 1134 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) 1135 comp = 1; 1136 if (nn->prev) 1137 break; 1138 } 1139 if ( ! comp) 1140 print_paragraph(h); 1141 return(1); 1142 } 1143 1144 /* Handle the -offset argument. */ 1145 1146 if (n->norm->Bd.offs == NULL || 1147 ! strcmp(n->norm->Bd.offs, "left")) 1148 SCALE_HS_INIT(&su, 0); 1149 else if ( ! strcmp(n->norm->Bd.offs, "indent")) 1150 SCALE_HS_INIT(&su, INDENT); 1151 else if ( ! strcmp(n->norm->Bd.offs, "indent-two")) 1152 SCALE_HS_INIT(&su, INDENT * 2); 1153 else 1154 a2width(n->norm->Bd.offs, &su); 1155 1156 bufinit(h); 1157 bufcat_su(h, "margin-left", &su); 1158 PAIR_STYLE_INIT(&tag[0], h); 1159 1160 if (DISP_unfilled != n->norm->Bd.type && 1161 DISP_literal != n->norm->Bd.type) { 1162 PAIR_CLASS_INIT(&tag[1], "display"); 1163 print_otag(h, TAG_DIV, 2, tag); 1164 return(1); 1165 } 1166 1167 PAIR_CLASS_INIT(&tag[1], "lit display"); 1168 print_otag(h, TAG_PRE, 2, tag); 1169 1170 /* This can be recursive: save & set our literal state. */ 1171 1172 sv = h->flags & HTML_LITERAL; 1173 h->flags |= HTML_LITERAL; 1174 1175 for (nn = n->child; nn; nn = nn->next) { 1176 print_mdoc_node(meta, nn, h); 1177 /* 1178 * If the printed node flushes its own line, then we 1179 * needn't do it here as well. This is hacky, but the 1180 * notion of selective eoln whitespace is pretty dumb 1181 * anyway, so don't sweat it. 1182 */ 1183 switch (nn->tok) { 1184 case MDOC_Sm: 1185 /* FALLTHROUGH */ 1186 case MDOC_br: 1187 /* FALLTHROUGH */ 1188 case MDOC_sp: 1189 /* FALLTHROUGH */ 1190 case MDOC_Bl: 1191 /* FALLTHROUGH */ 1192 case MDOC_D1: 1193 /* FALLTHROUGH */ 1194 case MDOC_Dl: 1195 /* FALLTHROUGH */ 1196 case MDOC_Lp: 1197 /* FALLTHROUGH */ 1198 case MDOC_Pp: 1199 continue; 1200 default: 1201 break; 1202 } 1203 if (nn->next && nn->next->line == nn->line) 1204 continue; 1205 else if (nn->next) 1206 print_text(h, "\n"); 1207 1208 h->flags |= HTML_NOSPACE; 1209 } 1210 1211 if (0 == sv) 1212 h->flags &= ~HTML_LITERAL; 1213 1214 return(0); 1215 } 1216 1217 static int 1218 mdoc_pa_pre(MDOC_ARGS) 1219 { 1220 struct htmlpair tag; 1221 1222 PAIR_CLASS_INIT(&tag, "file"); 1223 print_otag(h, TAG_I, 1, &tag); 1224 return(1); 1225 } 1226 1227 static int 1228 mdoc_ad_pre(MDOC_ARGS) 1229 { 1230 struct htmlpair tag; 1231 1232 PAIR_CLASS_INIT(&tag, "addr"); 1233 print_otag(h, TAG_I, 1, &tag); 1234 return(1); 1235 } 1236 1237 static int 1238 mdoc_an_pre(MDOC_ARGS) 1239 { 1240 struct htmlpair tag; 1241 1242 if (n->norm->An.auth == AUTH_split) { 1243 h->flags &= ~HTML_NOSPLIT; 1244 h->flags |= HTML_SPLIT; 1245 return(0); 1246 } 1247 if (n->norm->An.auth == AUTH_nosplit) { 1248 h->flags &= ~HTML_SPLIT; 1249 h->flags |= HTML_NOSPLIT; 1250 return(0); 1251 } 1252 1253 if (n->child == NULL) 1254 return(0); 1255 1256 if (h->flags & HTML_SPLIT) 1257 print_otag(h, TAG_BR, 0, NULL); 1258 1259 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 1260 h->flags |= HTML_SPLIT; 1261 1262 PAIR_CLASS_INIT(&tag, "author"); 1263 print_otag(h, TAG_SPAN, 1, &tag); 1264 return(1); 1265 } 1266 1267 static int 1268 mdoc_cd_pre(MDOC_ARGS) 1269 { 1270 struct htmlpair tag; 1271 1272 synopsis_pre(h, n); 1273 PAIR_CLASS_INIT(&tag, "config"); 1274 print_otag(h, TAG_B, 1, &tag); 1275 return(1); 1276 } 1277 1278 static int 1279 mdoc_dv_pre(MDOC_ARGS) 1280 { 1281 struct htmlpair tag; 1282 1283 PAIR_CLASS_INIT(&tag, "define"); 1284 print_otag(h, TAG_SPAN, 1, &tag); 1285 return(1); 1286 } 1287 1288 static int 1289 mdoc_ev_pre(MDOC_ARGS) 1290 { 1291 struct htmlpair tag; 1292 1293 PAIR_CLASS_INIT(&tag, "env"); 1294 print_otag(h, TAG_SPAN, 1, &tag); 1295 return(1); 1296 } 1297 1298 static int 1299 mdoc_er_pre(MDOC_ARGS) 1300 { 1301 struct htmlpair tag; 1302 1303 PAIR_CLASS_INIT(&tag, "errno"); 1304 print_otag(h, TAG_SPAN, 1, &tag); 1305 return(1); 1306 } 1307 1308 static int 1309 mdoc_fa_pre(MDOC_ARGS) 1310 { 1311 const struct mdoc_node *nn; 1312 struct htmlpair tag; 1313 struct tag *t; 1314 1315 PAIR_CLASS_INIT(&tag, "farg"); 1316 if (n->parent->tok != MDOC_Fo) { 1317 print_otag(h, TAG_I, 1, &tag); 1318 return(1); 1319 } 1320 1321 for (nn = n->child; nn; nn = nn->next) { 1322 t = print_otag(h, TAG_I, 1, &tag); 1323 print_text(h, nn->string); 1324 print_tagq(h, t); 1325 if (nn->next) { 1326 h->flags |= HTML_NOSPACE; 1327 print_text(h, ","); 1328 } 1329 } 1330 1331 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1332 h->flags |= HTML_NOSPACE; 1333 print_text(h, ","); 1334 } 1335 1336 return(0); 1337 } 1338 1339 static int 1340 mdoc_fd_pre(MDOC_ARGS) 1341 { 1342 struct htmlpair tag[2]; 1343 char buf[BUFSIZ]; 1344 size_t sz; 1345 int i; 1346 struct tag *t; 1347 1348 synopsis_pre(h, n); 1349 1350 if (NULL == (n = n->child)) 1351 return(0); 1352 1353 assert(MDOC_TEXT == n->type); 1354 1355 if (strcmp(n->string, "#include")) { 1356 PAIR_CLASS_INIT(&tag[0], "macro"); 1357 print_otag(h, TAG_B, 1, tag); 1358 return(1); 1359 } 1360 1361 PAIR_CLASS_INIT(&tag[0], "includes"); 1362 print_otag(h, TAG_B, 1, tag); 1363 print_text(h, n->string); 1364 1365 if (NULL != (n = n->next)) { 1366 assert(MDOC_TEXT == n->type); 1367 1368 /* 1369 * XXX This is broken and not easy to fix. 1370 * When using -Oincludes, truncation may occur. 1371 * Dynamic allocation wouldn't help because 1372 * passing long strings to buffmt_includes() 1373 * does not work either. 1374 */ 1375 1376 strlcpy(buf, '<' == *n->string || '"' == *n->string ? 1377 n->string + 1 : n->string, BUFSIZ); 1378 1379 sz = strlen(buf); 1380 if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1])) 1381 buf[sz - 1] = '\0'; 1382 1383 PAIR_CLASS_INIT(&tag[0], "link-includes"); 1384 1385 i = 1; 1386 if (h->base_includes) { 1387 buffmt_includes(h, buf); 1388 PAIR_HREF_INIT(&tag[i], h->buf); 1389 i++; 1390 } 1391 1392 t = print_otag(h, TAG_A, i, tag); 1393 print_text(h, n->string); 1394 print_tagq(h, t); 1395 1396 n = n->next; 1397 } 1398 1399 for ( ; n; n = n->next) { 1400 assert(MDOC_TEXT == n->type); 1401 print_text(h, n->string); 1402 } 1403 1404 return(0); 1405 } 1406 1407 static int 1408 mdoc_vt_pre(MDOC_ARGS) 1409 { 1410 struct htmlpair tag; 1411 1412 if (MDOC_BLOCK == n->type) { 1413 synopsis_pre(h, n); 1414 return(1); 1415 } else if (MDOC_ELEM == n->type) { 1416 synopsis_pre(h, n); 1417 } else if (MDOC_HEAD == n->type) 1418 return(0); 1419 1420 PAIR_CLASS_INIT(&tag, "type"); 1421 print_otag(h, TAG_SPAN, 1, &tag); 1422 return(1); 1423 } 1424 1425 static int 1426 mdoc_ft_pre(MDOC_ARGS) 1427 { 1428 struct htmlpair tag; 1429 1430 synopsis_pre(h, n); 1431 PAIR_CLASS_INIT(&tag, "ftype"); 1432 print_otag(h, TAG_I, 1, &tag); 1433 return(1); 1434 } 1435 1436 static int 1437 mdoc_fn_pre(MDOC_ARGS) 1438 { 1439 struct tag *t; 1440 struct htmlpair tag[2]; 1441 char nbuf[BUFSIZ]; 1442 const char *sp, *ep; 1443 int sz, i, pretty; 1444 1445 pretty = MDOC_SYNPRETTY & n->flags; 1446 synopsis_pre(h, n); 1447 1448 /* Split apart into type and name. */ 1449 assert(n->child->string); 1450 sp = n->child->string; 1451 1452 ep = strchr(sp, ' '); 1453 if (NULL != ep) { 1454 PAIR_CLASS_INIT(&tag[0], "ftype"); 1455 t = print_otag(h, TAG_I, 1, tag); 1456 1457 while (ep) { 1458 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1459 (void)memcpy(nbuf, sp, (size_t)sz); 1460 nbuf[sz] = '\0'; 1461 print_text(h, nbuf); 1462 sp = ++ep; 1463 ep = strchr(sp, ' '); 1464 } 1465 print_tagq(h, t); 1466 } 1467 1468 PAIR_CLASS_INIT(&tag[0], "fname"); 1469 1470 /* 1471 * FIXME: only refer to IDs that we know exist. 1472 */ 1473 1474 #if 0 1475 if (MDOC_SYNPRETTY & n->flags) { 1476 nbuf[0] = '\0'; 1477 html_idcat(nbuf, sp, BUFSIZ); 1478 PAIR_ID_INIT(&tag[1], nbuf); 1479 } else { 1480 strlcpy(nbuf, "#", BUFSIZ); 1481 html_idcat(nbuf, sp, BUFSIZ); 1482 PAIR_HREF_INIT(&tag[1], nbuf); 1483 } 1484 #endif 1485 1486 t = print_otag(h, TAG_B, 1, tag); 1487 1488 if (sp) 1489 print_text(h, sp); 1490 1491 print_tagq(h, t); 1492 1493 h->flags |= HTML_NOSPACE; 1494 print_text(h, "("); 1495 h->flags |= HTML_NOSPACE; 1496 1497 PAIR_CLASS_INIT(&tag[0], "farg"); 1498 bufinit(h); 1499 bufcat_style(h, "white-space", "nowrap"); 1500 PAIR_STYLE_INIT(&tag[1], h); 1501 1502 for (n = n->child->next; n; n = n->next) { 1503 i = 1; 1504 if (MDOC_SYNPRETTY & n->flags) 1505 i = 2; 1506 t = print_otag(h, TAG_I, i, tag); 1507 print_text(h, n->string); 1508 print_tagq(h, t); 1509 if (n->next) { 1510 h->flags |= HTML_NOSPACE; 1511 print_text(h, ","); 1512 } 1513 } 1514 1515 h->flags |= HTML_NOSPACE; 1516 print_text(h, ")"); 1517 1518 if (pretty) { 1519 h->flags |= HTML_NOSPACE; 1520 print_text(h, ";"); 1521 } 1522 1523 return(0); 1524 } 1525 1526 static int 1527 mdoc_sm_pre(MDOC_ARGS) 1528 { 1529 1530 if (NULL == n->child) 1531 h->flags ^= HTML_NONOSPACE; 1532 else if (0 == strcmp("on", n->child->string)) 1533 h->flags &= ~HTML_NONOSPACE; 1534 else 1535 h->flags |= HTML_NONOSPACE; 1536 1537 if ( ! (HTML_NONOSPACE & h->flags)) 1538 h->flags &= ~HTML_NOSPACE; 1539 1540 return(0); 1541 } 1542 1543 static int 1544 mdoc_skip_pre(MDOC_ARGS) 1545 { 1546 1547 return(0); 1548 } 1549 1550 static int 1551 mdoc_pp_pre(MDOC_ARGS) 1552 { 1553 1554 print_paragraph(h); 1555 return(0); 1556 } 1557 1558 static int 1559 mdoc_sp_pre(MDOC_ARGS) 1560 { 1561 struct roffsu su; 1562 struct htmlpair tag; 1563 1564 SCALE_VS_INIT(&su, 1); 1565 1566 if (MDOC_sp == n->tok) { 1567 if (NULL != (n = n->child)) 1568 if ( ! a2roffsu(n->string, &su, SCALE_VS)) 1569 SCALE_VS_INIT(&su, atoi(n->string)); 1570 } else 1571 su.scale = 0.0; 1572 1573 bufinit(h); 1574 bufcat_su(h, "height", &su); 1575 PAIR_STYLE_INIT(&tag, h); 1576 print_otag(h, TAG_DIV, 1, &tag); 1577 1578 /* So the div isn't empty: */ 1579 print_text(h, "\\~"); 1580 1581 return(0); 1582 1583 } 1584 1585 static int 1586 mdoc_lk_pre(MDOC_ARGS) 1587 { 1588 struct htmlpair tag[2]; 1589 1590 if (NULL == (n = n->child)) 1591 return(0); 1592 1593 assert(MDOC_TEXT == n->type); 1594 1595 PAIR_CLASS_INIT(&tag[0], "link-ext"); 1596 PAIR_HREF_INIT(&tag[1], n->string); 1597 1598 print_otag(h, TAG_A, 2, tag); 1599 1600 if (NULL == n->next) 1601 print_text(h, n->string); 1602 1603 for (n = n->next; n; n = n->next) 1604 print_text(h, n->string); 1605 1606 return(0); 1607 } 1608 1609 static int 1610 mdoc_mt_pre(MDOC_ARGS) 1611 { 1612 struct htmlpair tag[2]; 1613 struct tag *t; 1614 1615 PAIR_CLASS_INIT(&tag[0], "link-mail"); 1616 1617 for (n = n->child; n; n = n->next) { 1618 assert(MDOC_TEXT == n->type); 1619 1620 bufinit(h); 1621 bufcat(h, "mailto:"); 1622 bufcat(h, n->string); 1623 1624 PAIR_HREF_INIT(&tag[1], h->buf); 1625 t = print_otag(h, TAG_A, 2, tag); 1626 print_text(h, n->string); 1627 print_tagq(h, t); 1628 } 1629 1630 return(0); 1631 } 1632 1633 static int 1634 mdoc_fo_pre(MDOC_ARGS) 1635 { 1636 struct htmlpair tag; 1637 struct tag *t; 1638 1639 if (MDOC_BODY == n->type) { 1640 h->flags |= HTML_NOSPACE; 1641 print_text(h, "("); 1642 h->flags |= HTML_NOSPACE; 1643 return(1); 1644 } else if (MDOC_BLOCK == n->type) { 1645 synopsis_pre(h, n); 1646 return(1); 1647 } 1648 1649 /* XXX: we drop non-initial arguments as per groff. */ 1650 1651 assert(n->child); 1652 assert(n->child->string); 1653 1654 PAIR_CLASS_INIT(&tag, "fname"); 1655 t = print_otag(h, TAG_B, 1, &tag); 1656 print_text(h, n->child->string); 1657 print_tagq(h, t); 1658 return(0); 1659 } 1660 1661 static void 1662 mdoc_fo_post(MDOC_ARGS) 1663 { 1664 1665 if (MDOC_BODY != n->type) 1666 return; 1667 h->flags |= HTML_NOSPACE; 1668 print_text(h, ")"); 1669 h->flags |= HTML_NOSPACE; 1670 print_text(h, ";"); 1671 } 1672 1673 static int 1674 mdoc_in_pre(MDOC_ARGS) 1675 { 1676 struct tag *t; 1677 struct htmlpair tag[2]; 1678 int i; 1679 1680 synopsis_pre(h, n); 1681 1682 PAIR_CLASS_INIT(&tag[0], "includes"); 1683 print_otag(h, TAG_B, 1, tag); 1684 1685 /* 1686 * The first argument of the `In' gets special treatment as 1687 * being a linked value. Subsequent values are printed 1688 * afterward. groff does similarly. This also handles the case 1689 * of no children. 1690 */ 1691 1692 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) 1693 print_text(h, "#include"); 1694 1695 print_text(h, "<"); 1696 h->flags |= HTML_NOSPACE; 1697 1698 if (NULL != (n = n->child)) { 1699 assert(MDOC_TEXT == n->type); 1700 1701 PAIR_CLASS_INIT(&tag[0], "link-includes"); 1702 1703 i = 1; 1704 if (h->base_includes) { 1705 buffmt_includes(h, n->string); 1706 PAIR_HREF_INIT(&tag[i], h->buf); 1707 i++; 1708 } 1709 1710 t = print_otag(h, TAG_A, i, tag); 1711 print_text(h, n->string); 1712 print_tagq(h, t); 1713 1714 n = n->next; 1715 } 1716 1717 h->flags |= HTML_NOSPACE; 1718 print_text(h, ">"); 1719 1720 for ( ; n; n = n->next) { 1721 assert(MDOC_TEXT == n->type); 1722 print_text(h, n->string); 1723 } 1724 1725 return(0); 1726 } 1727 1728 static int 1729 mdoc_ic_pre(MDOC_ARGS) 1730 { 1731 struct htmlpair tag; 1732 1733 PAIR_CLASS_INIT(&tag, "cmd"); 1734 print_otag(h, TAG_B, 1, &tag); 1735 return(1); 1736 } 1737 1738 static int 1739 mdoc_rv_pre(MDOC_ARGS) 1740 { 1741 struct htmlpair tag; 1742 struct tag *t; 1743 int nchild; 1744 1745 if (n->prev) 1746 print_otag(h, TAG_BR, 0, NULL); 1747 1748 PAIR_CLASS_INIT(&tag, "fname"); 1749 1750 nchild = n->nchild; 1751 if (nchild > 0) { 1752 print_text(h, "The"); 1753 1754 for (n = n->child; n; n = n->next) { 1755 t = print_otag(h, TAG_B, 1, &tag); 1756 print_text(h, n->string); 1757 print_tagq(h, t); 1758 1759 h->flags |= HTML_NOSPACE; 1760 print_text(h, "()"); 1761 1762 if (n->next == NULL) 1763 continue; 1764 1765 if (nchild > 2) { 1766 h->flags |= HTML_NOSPACE; 1767 print_text(h, ","); 1768 } 1769 if (n->next->next == NULL) 1770 print_text(h, "and"); 1771 } 1772 1773 if (nchild > 1) 1774 print_text(h, "functions return"); 1775 else 1776 print_text(h, "function returns"); 1777 1778 print_text(h, "the value\\~0 if successful;"); 1779 } else 1780 print_text(h, "Upon successful completion," 1781 " the value\\~0 is returned;"); 1782 1783 print_text(h, "otherwise the value\\~\\-1 is returned" 1784 " and the global variable"); 1785 1786 PAIR_CLASS_INIT(&tag, "var"); 1787 t = print_otag(h, TAG_B, 1, &tag); 1788 print_text(h, "errno"); 1789 print_tagq(h, t); 1790 print_text(h, "is set to indicate the error."); 1791 return(0); 1792 } 1793 1794 static int 1795 mdoc_va_pre(MDOC_ARGS) 1796 { 1797 struct htmlpair tag; 1798 1799 PAIR_CLASS_INIT(&tag, "var"); 1800 print_otag(h, TAG_B, 1, &tag); 1801 return(1); 1802 } 1803 1804 static int 1805 mdoc_ap_pre(MDOC_ARGS) 1806 { 1807 1808 h->flags |= HTML_NOSPACE; 1809 print_text(h, "\\(aq"); 1810 h->flags |= HTML_NOSPACE; 1811 return(1); 1812 } 1813 1814 static int 1815 mdoc_bf_pre(MDOC_ARGS) 1816 { 1817 struct htmlpair tag[2]; 1818 struct roffsu su; 1819 1820 if (MDOC_HEAD == n->type) 1821 return(0); 1822 else if (MDOC_BODY != n->type) 1823 return(1); 1824 1825 if (FONT_Em == n->norm->Bf.font) 1826 PAIR_CLASS_INIT(&tag[0], "emph"); 1827 else if (FONT_Sy == n->norm->Bf.font) 1828 PAIR_CLASS_INIT(&tag[0], "symb"); 1829 else if (FONT_Li == n->norm->Bf.font) 1830 PAIR_CLASS_INIT(&tag[0], "lit"); 1831 else 1832 PAIR_CLASS_INIT(&tag[0], "none"); 1833 1834 /* 1835 * We want this to be inline-formatted, but needs to be div to 1836 * accept block children. 1837 */ 1838 bufinit(h); 1839 bufcat_style(h, "display", "inline"); 1840 SCALE_HS_INIT(&su, 1); 1841 /* Needs a left-margin for spacing. */ 1842 bufcat_su(h, "margin-left", &su); 1843 PAIR_STYLE_INIT(&tag[1], h); 1844 print_otag(h, TAG_DIV, 2, tag); 1845 return(1); 1846 } 1847 1848 static int 1849 mdoc_ms_pre(MDOC_ARGS) 1850 { 1851 struct htmlpair tag; 1852 1853 PAIR_CLASS_INIT(&tag, "symb"); 1854 print_otag(h, TAG_SPAN, 1, &tag); 1855 return(1); 1856 } 1857 1858 static int 1859 mdoc_igndelim_pre(MDOC_ARGS) 1860 { 1861 1862 h->flags |= HTML_IGNDELIM; 1863 return(1); 1864 } 1865 1866 static void 1867 mdoc_pf_post(MDOC_ARGS) 1868 { 1869 1870 if ( ! (n->next == NULL || n->next->flags & MDOC_LINE)) 1871 h->flags |= HTML_NOSPACE; 1872 } 1873 1874 static int 1875 mdoc_rs_pre(MDOC_ARGS) 1876 { 1877 struct htmlpair tag; 1878 1879 if (MDOC_BLOCK != n->type) 1880 return(1); 1881 1882 if (n->prev && SEC_SEE_ALSO == n->sec) 1883 print_paragraph(h); 1884 1885 PAIR_CLASS_INIT(&tag, "ref"); 1886 print_otag(h, TAG_SPAN, 1, &tag); 1887 return(1); 1888 } 1889 1890 static int 1891 mdoc_no_pre(MDOC_ARGS) 1892 { 1893 struct htmlpair tag; 1894 1895 PAIR_CLASS_INIT(&tag, "none"); 1896 print_otag(h, TAG_CODE, 1, &tag); 1897 return(1); 1898 } 1899 1900 static int 1901 mdoc_li_pre(MDOC_ARGS) 1902 { 1903 struct htmlpair tag; 1904 1905 PAIR_CLASS_INIT(&tag, "lit"); 1906 print_otag(h, TAG_CODE, 1, &tag); 1907 return(1); 1908 } 1909 1910 static int 1911 mdoc_sy_pre(MDOC_ARGS) 1912 { 1913 struct htmlpair tag; 1914 1915 PAIR_CLASS_INIT(&tag, "symb"); 1916 print_otag(h, TAG_SPAN, 1, &tag); 1917 return(1); 1918 } 1919 1920 static int 1921 mdoc_bt_pre(MDOC_ARGS) 1922 { 1923 1924 print_text(h, "is currently in beta test."); 1925 return(0); 1926 } 1927 1928 static int 1929 mdoc_ud_pre(MDOC_ARGS) 1930 { 1931 1932 print_text(h, "currently under development."); 1933 return(0); 1934 } 1935 1936 static int 1937 mdoc_lb_pre(MDOC_ARGS) 1938 { 1939 struct htmlpair tag; 1940 1941 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev) 1942 print_otag(h, TAG_BR, 0, NULL); 1943 1944 PAIR_CLASS_INIT(&tag, "lib"); 1945 print_otag(h, TAG_SPAN, 1, &tag); 1946 return(1); 1947 } 1948 1949 static int 1950 mdoc__x_pre(MDOC_ARGS) 1951 { 1952 struct htmlpair tag[2]; 1953 enum htmltag t; 1954 1955 t = TAG_SPAN; 1956 1957 switch (n->tok) { 1958 case MDOC__A: 1959 PAIR_CLASS_INIT(&tag[0], "ref-auth"); 1960 if (n->prev && MDOC__A == n->prev->tok) 1961 if (NULL == n->next || MDOC__A != n->next->tok) 1962 print_text(h, "and"); 1963 break; 1964 case MDOC__B: 1965 PAIR_CLASS_INIT(&tag[0], "ref-book"); 1966 t = TAG_I; 1967 break; 1968 case MDOC__C: 1969 PAIR_CLASS_INIT(&tag[0], "ref-city"); 1970 break; 1971 case MDOC__D: 1972 PAIR_CLASS_INIT(&tag[0], "ref-date"); 1973 break; 1974 case MDOC__I: 1975 PAIR_CLASS_INIT(&tag[0], "ref-issue"); 1976 t = TAG_I; 1977 break; 1978 case MDOC__J: 1979 PAIR_CLASS_INIT(&tag[0], "ref-jrnl"); 1980 t = TAG_I; 1981 break; 1982 case MDOC__N: 1983 PAIR_CLASS_INIT(&tag[0], "ref-num"); 1984 break; 1985 case MDOC__O: 1986 PAIR_CLASS_INIT(&tag[0], "ref-opt"); 1987 break; 1988 case MDOC__P: 1989 PAIR_CLASS_INIT(&tag[0], "ref-page"); 1990 break; 1991 case MDOC__Q: 1992 PAIR_CLASS_INIT(&tag[0], "ref-corp"); 1993 break; 1994 case MDOC__R: 1995 PAIR_CLASS_INIT(&tag[0], "ref-rep"); 1996 break; 1997 case MDOC__T: 1998 PAIR_CLASS_INIT(&tag[0], "ref-title"); 1999 break; 2000 case MDOC__U: 2001 PAIR_CLASS_INIT(&tag[0], "link-ref"); 2002 break; 2003 case MDOC__V: 2004 PAIR_CLASS_INIT(&tag[0], "ref-vol"); 2005 break; 2006 default: 2007 abort(); 2008 /* NOTREACHED */ 2009 } 2010 2011 if (MDOC__U != n->tok) { 2012 print_otag(h, t, 1, tag); 2013 return(1); 2014 } 2015 2016 PAIR_HREF_INIT(&tag[1], n->child->string); 2017 print_otag(h, TAG_A, 2, tag); 2018 2019 return(1); 2020 } 2021 2022 static void 2023 mdoc__x_post(MDOC_ARGS) 2024 { 2025 2026 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 2027 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 2028 if (NULL == n->prev || MDOC__A != n->prev->tok) 2029 return; 2030 2031 /* TODO: %U */ 2032 2033 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 2034 return; 2035 2036 h->flags |= HTML_NOSPACE; 2037 print_text(h, n->next ? "," : "."); 2038 } 2039 2040 static int 2041 mdoc_bk_pre(MDOC_ARGS) 2042 { 2043 2044 switch (n->type) { 2045 case MDOC_BLOCK: 2046 break; 2047 case MDOC_HEAD: 2048 return(0); 2049 case MDOC_BODY: 2050 if (n->parent->args || 0 == n->prev->nchild) 2051 h->flags |= HTML_PREKEEP; 2052 break; 2053 default: 2054 abort(); 2055 /* NOTREACHED */ 2056 } 2057 2058 return(1); 2059 } 2060 2061 static void 2062 mdoc_bk_post(MDOC_ARGS) 2063 { 2064 2065 if (MDOC_BODY == n->type) 2066 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 2067 } 2068 2069 static int 2070 mdoc_quote_pre(MDOC_ARGS) 2071 { 2072 struct htmlpair tag; 2073 2074 if (MDOC_BODY != n->type) 2075 return(1); 2076 2077 switch (n->tok) { 2078 case MDOC_Ao: 2079 /* FALLTHROUGH */ 2080 case MDOC_Aq: 2081 print_text(h, n->parent->prev != NULL && 2082 n->parent->prev->tok == MDOC_An ? "<" : "\\(la"); 2083 break; 2084 case MDOC_Bro: 2085 /* FALLTHROUGH */ 2086 case MDOC_Brq: 2087 print_text(h, "\\(lC"); 2088 break; 2089 case MDOC_Bo: 2090 /* FALLTHROUGH */ 2091 case MDOC_Bq: 2092 print_text(h, "\\(lB"); 2093 break; 2094 case MDOC_Oo: 2095 /* FALLTHROUGH */ 2096 case MDOC_Op: 2097 print_text(h, "\\(lB"); 2098 h->flags |= HTML_NOSPACE; 2099 PAIR_CLASS_INIT(&tag, "opt"); 2100 print_otag(h, TAG_SPAN, 1, &tag); 2101 break; 2102 case MDOC_En: 2103 if (NULL == n->norm->Es || 2104 NULL == n->norm->Es->child) 2105 return(1); 2106 print_text(h, n->norm->Es->child->string); 2107 break; 2108 case MDOC_Eo: 2109 break; 2110 case MDOC_Do: 2111 /* FALLTHROUGH */ 2112 case MDOC_Dq: 2113 /* FALLTHROUGH */ 2114 case MDOC_Qo: 2115 /* FALLTHROUGH */ 2116 case MDOC_Qq: 2117 print_text(h, "\\(lq"); 2118 break; 2119 case MDOC_Po: 2120 /* FALLTHROUGH */ 2121 case MDOC_Pq: 2122 print_text(h, "("); 2123 break; 2124 case MDOC_Ql: 2125 print_text(h, "\\(oq"); 2126 h->flags |= HTML_NOSPACE; 2127 PAIR_CLASS_INIT(&tag, "lit"); 2128 print_otag(h, TAG_CODE, 1, &tag); 2129 break; 2130 case MDOC_So: 2131 /* FALLTHROUGH */ 2132 case MDOC_Sq: 2133 print_text(h, "\\(oq"); 2134 break; 2135 default: 2136 abort(); 2137 /* NOTREACHED */ 2138 } 2139 2140 h->flags |= HTML_NOSPACE; 2141 return(1); 2142 } 2143 2144 static void 2145 mdoc_quote_post(MDOC_ARGS) 2146 { 2147 2148 if (n->type != MDOC_BODY && n->type != MDOC_ELEM) 2149 return; 2150 2151 if ( ! (n->tok == MDOC_En || 2152 (n->tok == MDOC_Eo && n->end == ENDBODY_SPACE))) 2153 h->flags |= HTML_NOSPACE; 2154 2155 switch (n->tok) { 2156 case MDOC_Ao: 2157 /* FALLTHROUGH */ 2158 case MDOC_Aq: 2159 print_text(h, n->parent->prev != NULL && 2160 n->parent->prev->tok == MDOC_An ? ">" : "\\(ra"); 2161 break; 2162 case MDOC_Bro: 2163 /* FALLTHROUGH */ 2164 case MDOC_Brq: 2165 print_text(h, "\\(rC"); 2166 break; 2167 case MDOC_Oo: 2168 /* FALLTHROUGH */ 2169 case MDOC_Op: 2170 /* FALLTHROUGH */ 2171 case MDOC_Bo: 2172 /* FALLTHROUGH */ 2173 case MDOC_Bq: 2174 print_text(h, "\\(rB"); 2175 break; 2176 case MDOC_En: 2177 if (NULL != n->norm->Es && 2178 NULL != n->norm->Es->child && 2179 NULL != n->norm->Es->child->next) { 2180 h->flags |= HTML_NOSPACE; 2181 print_text(h, n->norm->Es->child->next->string); 2182 } 2183 break; 2184 case MDOC_Eo: 2185 break; 2186 case MDOC_Qo: 2187 /* FALLTHROUGH */ 2188 case MDOC_Qq: 2189 /* FALLTHROUGH */ 2190 case MDOC_Do: 2191 /* FALLTHROUGH */ 2192 case MDOC_Dq: 2193 print_text(h, "\\(rq"); 2194 break; 2195 case MDOC_Po: 2196 /* FALLTHROUGH */ 2197 case MDOC_Pq: 2198 print_text(h, ")"); 2199 break; 2200 case MDOC_Ql: 2201 /* FALLTHROUGH */ 2202 case MDOC_So: 2203 /* FALLTHROUGH */ 2204 case MDOC_Sq: 2205 print_text(h, "\\(cq"); 2206 break; 2207 default: 2208 abort(); 2209 /* NOTREACHED */ 2210 } 2211 } 2212