1 /* $OpenBSD: mdoc_html.c,v 1.107 2015/04/18 17:50:02 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 /* XXX: we drop non-initial arguments as per groff. */ 1644 1645 assert(n->child); 1646 assert(n->child->string); 1647 1648 PAIR_CLASS_INIT(&tag, "fname"); 1649 t = print_otag(h, TAG_B, 1, &tag); 1650 print_text(h, n->child->string); 1651 print_tagq(h, t); 1652 return(0); 1653 } 1654 1655 static void 1656 mdoc_fo_post(MDOC_ARGS) 1657 { 1658 1659 if (n->type != ROFFT_BODY) 1660 return; 1661 h->flags |= HTML_NOSPACE; 1662 print_text(h, ")"); 1663 h->flags |= HTML_NOSPACE; 1664 print_text(h, ";"); 1665 } 1666 1667 static int 1668 mdoc_in_pre(MDOC_ARGS) 1669 { 1670 struct tag *t; 1671 struct htmlpair tag[2]; 1672 int i; 1673 1674 synopsis_pre(h, n); 1675 1676 PAIR_CLASS_INIT(&tag[0], "includes"); 1677 print_otag(h, TAG_B, 1, tag); 1678 1679 /* 1680 * The first argument of the `In' gets special treatment as 1681 * being a linked value. Subsequent values are printed 1682 * afterward. groff does similarly. This also handles the case 1683 * of no children. 1684 */ 1685 1686 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) 1687 print_text(h, "#include"); 1688 1689 print_text(h, "<"); 1690 h->flags |= HTML_NOSPACE; 1691 1692 if (NULL != (n = n->child)) { 1693 assert(n->type == ROFFT_TEXT); 1694 1695 PAIR_CLASS_INIT(&tag[0], "link-includes"); 1696 1697 i = 1; 1698 if (h->base_includes) { 1699 buffmt_includes(h, n->string); 1700 PAIR_HREF_INIT(&tag[i], h->buf); 1701 i++; 1702 } 1703 1704 t = print_otag(h, TAG_A, i, tag); 1705 print_text(h, n->string); 1706 print_tagq(h, t); 1707 1708 n = n->next; 1709 } 1710 1711 h->flags |= HTML_NOSPACE; 1712 print_text(h, ">"); 1713 1714 for ( ; n; n = n->next) { 1715 assert(n->type == ROFFT_TEXT); 1716 print_text(h, n->string); 1717 } 1718 1719 return(0); 1720 } 1721 1722 static int 1723 mdoc_ic_pre(MDOC_ARGS) 1724 { 1725 struct htmlpair tag; 1726 1727 PAIR_CLASS_INIT(&tag, "cmd"); 1728 print_otag(h, TAG_B, 1, &tag); 1729 return(1); 1730 } 1731 1732 static int 1733 mdoc_rv_pre(MDOC_ARGS) 1734 { 1735 struct htmlpair tag; 1736 struct tag *t; 1737 int nchild; 1738 1739 if (n->prev) 1740 print_otag(h, TAG_BR, 0, NULL); 1741 1742 PAIR_CLASS_INIT(&tag, "fname"); 1743 1744 nchild = n->nchild; 1745 if (nchild > 0) { 1746 print_text(h, "The"); 1747 1748 for (n = n->child; n; n = n->next) { 1749 t = print_otag(h, TAG_B, 1, &tag); 1750 print_text(h, n->string); 1751 print_tagq(h, t); 1752 1753 h->flags |= HTML_NOSPACE; 1754 print_text(h, "()"); 1755 1756 if (n->next == NULL) 1757 continue; 1758 1759 if (nchild > 2) { 1760 h->flags |= HTML_NOSPACE; 1761 print_text(h, ","); 1762 } 1763 if (n->next->next == NULL) 1764 print_text(h, "and"); 1765 } 1766 1767 if (nchild > 1) 1768 print_text(h, "functions return"); 1769 else 1770 print_text(h, "function returns"); 1771 1772 print_text(h, "the value\\~0 if successful;"); 1773 } else 1774 print_text(h, "Upon successful completion," 1775 " the value\\~0 is returned;"); 1776 1777 print_text(h, "otherwise the value\\~\\-1 is returned" 1778 " and the global variable"); 1779 1780 PAIR_CLASS_INIT(&tag, "var"); 1781 t = print_otag(h, TAG_B, 1, &tag); 1782 print_text(h, "errno"); 1783 print_tagq(h, t); 1784 print_text(h, "is set to indicate the error."); 1785 return(0); 1786 } 1787 1788 static int 1789 mdoc_va_pre(MDOC_ARGS) 1790 { 1791 struct htmlpair tag; 1792 1793 PAIR_CLASS_INIT(&tag, "var"); 1794 print_otag(h, TAG_B, 1, &tag); 1795 return(1); 1796 } 1797 1798 static int 1799 mdoc_ap_pre(MDOC_ARGS) 1800 { 1801 1802 h->flags |= HTML_NOSPACE; 1803 print_text(h, "\\(aq"); 1804 h->flags |= HTML_NOSPACE; 1805 return(1); 1806 } 1807 1808 static int 1809 mdoc_bf_pre(MDOC_ARGS) 1810 { 1811 struct htmlpair tag[2]; 1812 struct roffsu su; 1813 1814 if (n->type == ROFFT_HEAD) 1815 return(0); 1816 else if (n->type != ROFFT_BODY) 1817 return(1); 1818 1819 if (FONT_Em == n->norm->Bf.font) 1820 PAIR_CLASS_INIT(&tag[0], "emph"); 1821 else if (FONT_Sy == n->norm->Bf.font) 1822 PAIR_CLASS_INIT(&tag[0], "symb"); 1823 else if (FONT_Li == n->norm->Bf.font) 1824 PAIR_CLASS_INIT(&tag[0], "lit"); 1825 else 1826 PAIR_CLASS_INIT(&tag[0], "none"); 1827 1828 /* 1829 * We want this to be inline-formatted, but needs to be div to 1830 * accept block children. 1831 */ 1832 bufinit(h); 1833 bufcat_style(h, "display", "inline"); 1834 SCALE_HS_INIT(&su, 1); 1835 /* Needs a left-margin for spacing. */ 1836 bufcat_su(h, "margin-left", &su); 1837 PAIR_STYLE_INIT(&tag[1], h); 1838 print_otag(h, TAG_DIV, 2, tag); 1839 return(1); 1840 } 1841 1842 static int 1843 mdoc_ms_pre(MDOC_ARGS) 1844 { 1845 struct htmlpair tag; 1846 1847 PAIR_CLASS_INIT(&tag, "symb"); 1848 print_otag(h, TAG_SPAN, 1, &tag); 1849 return(1); 1850 } 1851 1852 static int 1853 mdoc_igndelim_pre(MDOC_ARGS) 1854 { 1855 1856 h->flags |= HTML_IGNDELIM; 1857 return(1); 1858 } 1859 1860 static void 1861 mdoc_pf_post(MDOC_ARGS) 1862 { 1863 1864 if ( ! (n->next == NULL || n->next->flags & MDOC_LINE)) 1865 h->flags |= HTML_NOSPACE; 1866 } 1867 1868 static int 1869 mdoc_rs_pre(MDOC_ARGS) 1870 { 1871 struct htmlpair tag; 1872 1873 if (n->type != ROFFT_BLOCK) 1874 return(1); 1875 1876 if (n->prev && SEC_SEE_ALSO == n->sec) 1877 print_paragraph(h); 1878 1879 PAIR_CLASS_INIT(&tag, "ref"); 1880 print_otag(h, TAG_SPAN, 1, &tag); 1881 return(1); 1882 } 1883 1884 static int 1885 mdoc_no_pre(MDOC_ARGS) 1886 { 1887 struct htmlpair tag; 1888 1889 PAIR_CLASS_INIT(&tag, "none"); 1890 print_otag(h, TAG_CODE, 1, &tag); 1891 return(1); 1892 } 1893 1894 static int 1895 mdoc_li_pre(MDOC_ARGS) 1896 { 1897 struct htmlpair tag; 1898 1899 PAIR_CLASS_INIT(&tag, "lit"); 1900 print_otag(h, TAG_CODE, 1, &tag); 1901 return(1); 1902 } 1903 1904 static int 1905 mdoc_sy_pre(MDOC_ARGS) 1906 { 1907 struct htmlpair tag; 1908 1909 PAIR_CLASS_INIT(&tag, "symb"); 1910 print_otag(h, TAG_SPAN, 1, &tag); 1911 return(1); 1912 } 1913 1914 static int 1915 mdoc_bt_pre(MDOC_ARGS) 1916 { 1917 1918 print_text(h, "is currently in beta test."); 1919 return(0); 1920 } 1921 1922 static int 1923 mdoc_ud_pre(MDOC_ARGS) 1924 { 1925 1926 print_text(h, "currently under development."); 1927 return(0); 1928 } 1929 1930 static int 1931 mdoc_lb_pre(MDOC_ARGS) 1932 { 1933 struct htmlpair tag; 1934 1935 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev) 1936 print_otag(h, TAG_BR, 0, NULL); 1937 1938 PAIR_CLASS_INIT(&tag, "lib"); 1939 print_otag(h, TAG_SPAN, 1, &tag); 1940 return(1); 1941 } 1942 1943 static int 1944 mdoc__x_pre(MDOC_ARGS) 1945 { 1946 struct htmlpair tag[2]; 1947 enum htmltag t; 1948 1949 t = TAG_SPAN; 1950 1951 switch (n->tok) { 1952 case MDOC__A: 1953 PAIR_CLASS_INIT(&tag[0], "ref-auth"); 1954 if (n->prev && MDOC__A == n->prev->tok) 1955 if (NULL == n->next || MDOC__A != n->next->tok) 1956 print_text(h, "and"); 1957 break; 1958 case MDOC__B: 1959 PAIR_CLASS_INIT(&tag[0], "ref-book"); 1960 t = TAG_I; 1961 break; 1962 case MDOC__C: 1963 PAIR_CLASS_INIT(&tag[0], "ref-city"); 1964 break; 1965 case MDOC__D: 1966 PAIR_CLASS_INIT(&tag[0], "ref-date"); 1967 break; 1968 case MDOC__I: 1969 PAIR_CLASS_INIT(&tag[0], "ref-issue"); 1970 t = TAG_I; 1971 break; 1972 case MDOC__J: 1973 PAIR_CLASS_INIT(&tag[0], "ref-jrnl"); 1974 t = TAG_I; 1975 break; 1976 case MDOC__N: 1977 PAIR_CLASS_INIT(&tag[0], "ref-num"); 1978 break; 1979 case MDOC__O: 1980 PAIR_CLASS_INIT(&tag[0], "ref-opt"); 1981 break; 1982 case MDOC__P: 1983 PAIR_CLASS_INIT(&tag[0], "ref-page"); 1984 break; 1985 case MDOC__Q: 1986 PAIR_CLASS_INIT(&tag[0], "ref-corp"); 1987 break; 1988 case MDOC__R: 1989 PAIR_CLASS_INIT(&tag[0], "ref-rep"); 1990 break; 1991 case MDOC__T: 1992 PAIR_CLASS_INIT(&tag[0], "ref-title"); 1993 break; 1994 case MDOC__U: 1995 PAIR_CLASS_INIT(&tag[0], "link-ref"); 1996 break; 1997 case MDOC__V: 1998 PAIR_CLASS_INIT(&tag[0], "ref-vol"); 1999 break; 2000 default: 2001 abort(); 2002 /* NOTREACHED */ 2003 } 2004 2005 if (MDOC__U != n->tok) { 2006 print_otag(h, t, 1, tag); 2007 return(1); 2008 } 2009 2010 PAIR_HREF_INIT(&tag[1], n->child->string); 2011 print_otag(h, TAG_A, 2, tag); 2012 2013 return(1); 2014 } 2015 2016 static void 2017 mdoc__x_post(MDOC_ARGS) 2018 { 2019 2020 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 2021 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 2022 if (NULL == n->prev || MDOC__A != n->prev->tok) 2023 return; 2024 2025 /* TODO: %U */ 2026 2027 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 2028 return; 2029 2030 h->flags |= HTML_NOSPACE; 2031 print_text(h, n->next ? "," : "."); 2032 } 2033 2034 static int 2035 mdoc_bk_pre(MDOC_ARGS) 2036 { 2037 2038 switch (n->type) { 2039 case ROFFT_BLOCK: 2040 break; 2041 case ROFFT_HEAD: 2042 return(0); 2043 case ROFFT_BODY: 2044 if (n->parent->args || 0 == n->prev->nchild) 2045 h->flags |= HTML_PREKEEP; 2046 break; 2047 default: 2048 abort(); 2049 /* NOTREACHED */ 2050 } 2051 2052 return(1); 2053 } 2054 2055 static void 2056 mdoc_bk_post(MDOC_ARGS) 2057 { 2058 2059 if (n->type == ROFFT_BODY) 2060 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 2061 } 2062 2063 static int 2064 mdoc_quote_pre(MDOC_ARGS) 2065 { 2066 struct htmlpair tag; 2067 2068 if (n->type != ROFFT_BODY) 2069 return(1); 2070 2071 switch (n->tok) { 2072 case MDOC_Ao: 2073 /* FALLTHROUGH */ 2074 case MDOC_Aq: 2075 print_text(h, n->nchild == 1 && 2076 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 2077 break; 2078 case MDOC_Bro: 2079 /* FALLTHROUGH */ 2080 case MDOC_Brq: 2081 print_text(h, "\\(lC"); 2082 break; 2083 case MDOC_Bo: 2084 /* FALLTHROUGH */ 2085 case MDOC_Bq: 2086 print_text(h, "\\(lB"); 2087 break; 2088 case MDOC_Oo: 2089 /* FALLTHROUGH */ 2090 case MDOC_Op: 2091 print_text(h, "\\(lB"); 2092 h->flags |= HTML_NOSPACE; 2093 PAIR_CLASS_INIT(&tag, "opt"); 2094 print_otag(h, TAG_SPAN, 1, &tag); 2095 break; 2096 case MDOC_En: 2097 if (NULL == n->norm->Es || 2098 NULL == n->norm->Es->child) 2099 return(1); 2100 print_text(h, n->norm->Es->child->string); 2101 break; 2102 case MDOC_Do: 2103 /* FALLTHROUGH */ 2104 case MDOC_Dq: 2105 /* FALLTHROUGH */ 2106 case MDOC_Qo: 2107 /* FALLTHROUGH */ 2108 case MDOC_Qq: 2109 print_text(h, "\\(lq"); 2110 break; 2111 case MDOC_Po: 2112 /* FALLTHROUGH */ 2113 case MDOC_Pq: 2114 print_text(h, "("); 2115 break; 2116 case MDOC_Ql: 2117 print_text(h, "\\(oq"); 2118 h->flags |= HTML_NOSPACE; 2119 PAIR_CLASS_INIT(&tag, "lit"); 2120 print_otag(h, TAG_CODE, 1, &tag); 2121 break; 2122 case MDOC_So: 2123 /* FALLTHROUGH */ 2124 case MDOC_Sq: 2125 print_text(h, "\\(oq"); 2126 break; 2127 default: 2128 abort(); 2129 /* NOTREACHED */ 2130 } 2131 2132 h->flags |= HTML_NOSPACE; 2133 return(1); 2134 } 2135 2136 static void 2137 mdoc_quote_post(MDOC_ARGS) 2138 { 2139 2140 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 2141 return; 2142 2143 h->flags |= HTML_NOSPACE; 2144 2145 switch (n->tok) { 2146 case MDOC_Ao: 2147 /* FALLTHROUGH */ 2148 case MDOC_Aq: 2149 print_text(h, n->nchild == 1 && 2150 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 2151 break; 2152 case MDOC_Bro: 2153 /* FALLTHROUGH */ 2154 case MDOC_Brq: 2155 print_text(h, "\\(rC"); 2156 break; 2157 case MDOC_Oo: 2158 /* FALLTHROUGH */ 2159 case MDOC_Op: 2160 /* FALLTHROUGH */ 2161 case MDOC_Bo: 2162 /* FALLTHROUGH */ 2163 case MDOC_Bq: 2164 print_text(h, "\\(rB"); 2165 break; 2166 case MDOC_En: 2167 if (n->norm->Es == NULL || 2168 n->norm->Es->child == NULL || 2169 n->norm->Es->child->next == NULL) 2170 h->flags &= ~HTML_NOSPACE; 2171 else 2172 print_text(h, n->norm->Es->child->next->string); 2173 break; 2174 case MDOC_Qo: 2175 /* FALLTHROUGH */ 2176 case MDOC_Qq: 2177 /* FALLTHROUGH */ 2178 case MDOC_Do: 2179 /* FALLTHROUGH */ 2180 case MDOC_Dq: 2181 print_text(h, "\\(rq"); 2182 break; 2183 case MDOC_Po: 2184 /* FALLTHROUGH */ 2185 case MDOC_Pq: 2186 print_text(h, ")"); 2187 break; 2188 case MDOC_Ql: 2189 /* FALLTHROUGH */ 2190 case MDOC_So: 2191 /* FALLTHROUGH */ 2192 case MDOC_Sq: 2193 print_text(h, "\\(cq"); 2194 break; 2195 default: 2196 abort(); 2197 /* NOTREACHED */ 2198 } 2199 } 2200 2201 static int 2202 mdoc_eo_pre(MDOC_ARGS) 2203 { 2204 2205 if (n->type != ROFFT_BODY) 2206 return(1); 2207 2208 if (n->end == ENDBODY_NOT && 2209 n->parent->head->child == NULL && 2210 n->child != NULL && 2211 n->child->end != ENDBODY_NOT) 2212 print_text(h, "\\&"); 2213 else if (n->end != ENDBODY_NOT ? n->child != NULL : 2214 n->parent->head->child != NULL && (n->child != NULL || 2215 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 2216 h->flags |= HTML_NOSPACE; 2217 return(1); 2218 } 2219 2220 static void 2221 mdoc_eo_post(MDOC_ARGS) 2222 { 2223 int body, tail; 2224 2225 if (n->type != ROFFT_BODY) 2226 return; 2227 2228 if (n->end != ENDBODY_NOT) { 2229 h->flags &= ~HTML_NOSPACE; 2230 return; 2231 } 2232 2233 body = n->child != NULL || n->parent->head->child != NULL; 2234 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 2235 2236 if (body && tail) 2237 h->flags |= HTML_NOSPACE; 2238 else if ( ! tail) 2239 h->flags &= ~HTML_NOSPACE; 2240 } 2241