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