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