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