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