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