1 /* $OpenBSD: mdoc_html.c,v 1.217 2021/03/30 19:23:50 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> 4 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> 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 * HTML formatter for mdoc(7) used by mandoc(1). 19 */ 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "mandoc_aux.h" 30 #include "mandoc.h" 31 #include "roff.h" 32 #include "mdoc.h" 33 #include "out.h" 34 #include "html.h" 35 #include "main.h" 36 37 #define MDOC_ARGS const struct roff_meta *meta, \ 38 struct roff_node *n, \ 39 struct html *h 40 41 #ifndef MIN 42 #define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b)) 43 #endif 44 45 struct mdoc_html_act { 46 int (*pre)(MDOC_ARGS); 47 void (*post)(MDOC_ARGS); 48 }; 49 50 static void print_mdoc_head(const struct roff_meta *, 51 struct html *); 52 static void print_mdoc_node(MDOC_ARGS); 53 static void print_mdoc_nodelist(MDOC_ARGS); 54 static void synopsis_pre(struct html *, struct roff_node *); 55 56 static void mdoc_root_post(const struct roff_meta *, 57 struct html *); 58 static int mdoc_root_pre(const struct roff_meta *, 59 struct html *); 60 61 static void mdoc__x_post(MDOC_ARGS); 62 static int mdoc__x_pre(MDOC_ARGS); 63 static int mdoc_abort_pre(MDOC_ARGS); 64 static int mdoc_ad_pre(MDOC_ARGS); 65 static int mdoc_an_pre(MDOC_ARGS); 66 static int mdoc_ap_pre(MDOC_ARGS); 67 static int mdoc_ar_pre(MDOC_ARGS); 68 static int mdoc_bd_pre(MDOC_ARGS); 69 static int mdoc_bf_pre(MDOC_ARGS); 70 static void mdoc_bk_post(MDOC_ARGS); 71 static int mdoc_bk_pre(MDOC_ARGS); 72 static int mdoc_bl_pre(MDOC_ARGS); 73 static int mdoc_cd_pre(MDOC_ARGS); 74 static int mdoc_code_pre(MDOC_ARGS); 75 static int mdoc_d1_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_ex_pre(MDOC_ARGS); 85 static void mdoc_fo_post(MDOC_ARGS); 86 static int mdoc_fo_pre(MDOC_ARGS); 87 static int mdoc_igndelim_pre(MDOC_ARGS); 88 static int mdoc_in_pre(MDOC_ARGS); 89 static int mdoc_it_pre(MDOC_ARGS); 90 static int mdoc_lb_pre(MDOC_ARGS); 91 static int mdoc_lk_pre(MDOC_ARGS); 92 static int mdoc_mt_pre(MDOC_ARGS); 93 static int mdoc_nd_pre(MDOC_ARGS); 94 static int mdoc_nm_pre(MDOC_ARGS); 95 static int mdoc_no_pre(MDOC_ARGS); 96 static int mdoc_ns_pre(MDOC_ARGS); 97 static int mdoc_pa_pre(MDOC_ARGS); 98 static void mdoc_pf_post(MDOC_ARGS); 99 static int mdoc_pp_pre(MDOC_ARGS); 100 static void mdoc_quote_post(MDOC_ARGS); 101 static int mdoc_quote_pre(MDOC_ARGS); 102 static int mdoc_rs_pre(MDOC_ARGS); 103 static int mdoc_sh_pre(MDOC_ARGS); 104 static int mdoc_skip_pre(MDOC_ARGS); 105 static int mdoc_sm_pre(MDOC_ARGS); 106 static int mdoc_ss_pre(MDOC_ARGS); 107 static int mdoc_st_pre(MDOC_ARGS); 108 static int mdoc_sx_pre(MDOC_ARGS); 109 static int mdoc_sy_pre(MDOC_ARGS); 110 static int mdoc_tg_pre(MDOC_ARGS); 111 static int mdoc_va_pre(MDOC_ARGS); 112 static int mdoc_vt_pre(MDOC_ARGS); 113 static int mdoc_xr_pre(MDOC_ARGS); 114 static int mdoc_xx_pre(MDOC_ARGS); 115 116 static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { 117 {NULL, NULL}, /* Dd */ 118 {NULL, NULL}, /* Dt */ 119 {NULL, NULL}, /* Os */ 120 {mdoc_sh_pre, NULL }, /* Sh */ 121 {mdoc_ss_pre, NULL }, /* Ss */ 122 {mdoc_pp_pre, NULL}, /* Pp */ 123 {mdoc_d1_pre, NULL}, /* D1 */ 124 {mdoc_d1_pre, NULL}, /* Dl */ 125 {mdoc_bd_pre, NULL}, /* Bd */ 126 {NULL, NULL}, /* Ed */ 127 {mdoc_bl_pre, NULL}, /* Bl */ 128 {NULL, NULL}, /* El */ 129 {mdoc_it_pre, NULL}, /* It */ 130 {mdoc_ad_pre, NULL}, /* Ad */ 131 {mdoc_an_pre, NULL}, /* An */ 132 {mdoc_ap_pre, NULL}, /* Ap */ 133 {mdoc_ar_pre, NULL}, /* Ar */ 134 {mdoc_cd_pre, NULL}, /* Cd */ 135 {mdoc_code_pre, NULL}, /* Cm */ 136 {mdoc_code_pre, NULL}, /* Dv */ 137 {mdoc_code_pre, NULL}, /* Er */ 138 {mdoc_code_pre, NULL}, /* Ev */ 139 {mdoc_ex_pre, NULL}, /* Ex */ 140 {mdoc_fa_pre, NULL}, /* Fa */ 141 {mdoc_fd_pre, NULL}, /* Fd */ 142 {mdoc_fl_pre, NULL}, /* Fl */ 143 {mdoc_fn_pre, NULL}, /* Fn */ 144 {mdoc_ft_pre, NULL}, /* Ft */ 145 {mdoc_code_pre, NULL}, /* Ic */ 146 {mdoc_in_pre, NULL}, /* In */ 147 {mdoc_code_pre, NULL}, /* Li */ 148 {mdoc_nd_pre, NULL}, /* Nd */ 149 {mdoc_nm_pre, NULL}, /* Nm */ 150 {mdoc_quote_pre, mdoc_quote_post}, /* Op */ 151 {mdoc_abort_pre, NULL}, /* Ot */ 152 {mdoc_pa_pre, NULL}, /* Pa */ 153 {mdoc_ex_pre, NULL}, /* Rv */ 154 {mdoc_st_pre, NULL}, /* St */ 155 {mdoc_va_pre, NULL}, /* Va */ 156 {mdoc_vt_pre, NULL}, /* Vt */ 157 {mdoc_xr_pre, NULL}, /* Xr */ 158 {mdoc__x_pre, mdoc__x_post}, /* %A */ 159 {mdoc__x_pre, mdoc__x_post}, /* %B */ 160 {mdoc__x_pre, mdoc__x_post}, /* %D */ 161 {mdoc__x_pre, mdoc__x_post}, /* %I */ 162 {mdoc__x_pre, mdoc__x_post}, /* %J */ 163 {mdoc__x_pre, mdoc__x_post}, /* %N */ 164 {mdoc__x_pre, mdoc__x_post}, /* %O */ 165 {mdoc__x_pre, mdoc__x_post}, /* %P */ 166 {mdoc__x_pre, mdoc__x_post}, /* %R */ 167 {mdoc__x_pre, mdoc__x_post}, /* %T */ 168 {mdoc__x_pre, mdoc__x_post}, /* %V */ 169 {NULL, NULL}, /* Ac */ 170 {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ 171 {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ 172 {mdoc_xx_pre, NULL}, /* At */ 173 {NULL, NULL}, /* Bc */ 174 {mdoc_bf_pre, NULL}, /* Bf */ 175 {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ 176 {mdoc_quote_pre, mdoc_quote_post}, /* Bq */ 177 {mdoc_xx_pre, NULL}, /* Bsx */ 178 {mdoc_xx_pre, NULL}, /* Bx */ 179 {mdoc_skip_pre, NULL}, /* Db */ 180 {NULL, NULL}, /* Dc */ 181 {mdoc_quote_pre, mdoc_quote_post}, /* Do */ 182 {mdoc_quote_pre, mdoc_quote_post}, /* Dq */ 183 {NULL, NULL}, /* Ec */ /* FIXME: no space */ 184 {NULL, NULL}, /* Ef */ 185 {mdoc_em_pre, NULL}, /* Em */ 186 {mdoc_eo_pre, mdoc_eo_post}, /* Eo */ 187 {mdoc_xx_pre, NULL}, /* Fx */ 188 {mdoc_no_pre, NULL}, /* Ms */ 189 {mdoc_no_pre, NULL}, /* No */ 190 {mdoc_ns_pre, NULL}, /* Ns */ 191 {mdoc_xx_pre, NULL}, /* Nx */ 192 {mdoc_xx_pre, NULL}, /* Ox */ 193 {NULL, NULL}, /* Pc */ 194 {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */ 195 {mdoc_quote_pre, mdoc_quote_post}, /* Po */ 196 {mdoc_quote_pre, mdoc_quote_post}, /* Pq */ 197 {NULL, NULL}, /* Qc */ 198 {mdoc_quote_pre, mdoc_quote_post}, /* Ql */ 199 {mdoc_quote_pre, mdoc_quote_post}, /* Qo */ 200 {mdoc_quote_pre, mdoc_quote_post}, /* Qq */ 201 {NULL, NULL}, /* Re */ 202 {mdoc_rs_pre, NULL}, /* Rs */ 203 {NULL, NULL}, /* Sc */ 204 {mdoc_quote_pre, mdoc_quote_post}, /* So */ 205 {mdoc_quote_pre, mdoc_quote_post}, /* Sq */ 206 {mdoc_sm_pre, NULL}, /* Sm */ 207 {mdoc_sx_pre, NULL}, /* Sx */ 208 {mdoc_sy_pre, NULL}, /* Sy */ 209 {NULL, NULL}, /* Tn */ 210 {mdoc_xx_pre, NULL}, /* Ux */ 211 {NULL, NULL}, /* Xc */ 212 {NULL, NULL}, /* Xo */ 213 {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ 214 {NULL, NULL}, /* Fc */ 215 {mdoc_quote_pre, mdoc_quote_post}, /* Oo */ 216 {NULL, NULL}, /* Oc */ 217 {mdoc_bk_pre, mdoc_bk_post}, /* Bk */ 218 {NULL, NULL}, /* Ek */ 219 {NULL, NULL}, /* Bt */ 220 {NULL, NULL}, /* Hf */ 221 {mdoc_em_pre, NULL}, /* Fr */ 222 {NULL, NULL}, /* Ud */ 223 {mdoc_lb_pre, NULL}, /* Lb */ 224 {mdoc_abort_pre, NULL}, /* Lp */ 225 {mdoc_lk_pre, NULL}, /* Lk */ 226 {mdoc_mt_pre, NULL}, /* Mt */ 227 {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ 228 {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ 229 {NULL, NULL}, /* Brc */ 230 {mdoc__x_pre, mdoc__x_post}, /* %C */ 231 {mdoc_skip_pre, NULL}, /* Es */ 232 {mdoc_quote_pre, mdoc_quote_post}, /* En */ 233 {mdoc_xx_pre, NULL}, /* Dx */ 234 {mdoc__x_pre, mdoc__x_post}, /* %Q */ 235 {mdoc__x_pre, mdoc__x_post}, /* %U */ 236 {NULL, NULL}, /* Ta */ 237 {mdoc_tg_pre, NULL}, /* Tg */ 238 }; 239 240 241 /* 242 * See the same function in mdoc_term.c for documentation. 243 */ 244 static void 245 synopsis_pre(struct html *h, struct roff_node *n) 246 { 247 struct roff_node *np; 248 249 if ((n->flags & NODE_SYNPRETTY) == 0 || 250 (np = roff_node_prev(n)) == NULL) 251 return; 252 253 if (np->tok == n->tok && 254 MDOC_Fo != n->tok && 255 MDOC_Ft != n->tok && 256 MDOC_Fn != n->tok) { 257 print_otag(h, TAG_BR, ""); 258 return; 259 } 260 261 switch (np->tok) { 262 case MDOC_Fd: 263 case MDOC_Fn: 264 case MDOC_Fo: 265 case MDOC_In: 266 case MDOC_Vt: 267 break; 268 case MDOC_Ft: 269 if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) 270 break; 271 /* FALLTHROUGH */ 272 default: 273 print_otag(h, TAG_BR, ""); 274 return; 275 } 276 html_close_paragraph(h); 277 print_otag(h, TAG_P, "c", "Pp"); 278 } 279 280 void 281 html_mdoc(void *arg, const struct roff_meta *mdoc) 282 { 283 struct html *h; 284 struct roff_node *n; 285 struct tag *t; 286 287 h = (struct html *)arg; 288 n = mdoc->first->child; 289 290 if ((h->oflags & HTML_FRAGMENT) == 0) { 291 print_gen_decls(h); 292 print_otag(h, TAG_HTML, ""); 293 if (n != NULL && n->type == ROFFT_COMMENT) 294 print_gen_comment(h, n); 295 t = print_otag(h, TAG_HEAD, ""); 296 print_mdoc_head(mdoc, h); 297 print_tagq(h, t); 298 print_otag(h, TAG_BODY, ""); 299 } 300 301 mdoc_root_pre(mdoc, h); 302 t = print_otag(h, TAG_DIV, "c", "manual-text"); 303 print_mdoc_nodelist(mdoc, n, h); 304 print_tagq(h, t); 305 mdoc_root_post(mdoc, h); 306 print_tagq(h, NULL); 307 } 308 309 static void 310 print_mdoc_head(const struct roff_meta *meta, struct html *h) 311 { 312 char *cp; 313 314 print_gen_head(h); 315 316 if (meta->arch != NULL && meta->msec != NULL) 317 mandoc_asprintf(&cp, "%s(%s) (%s)", meta->title, 318 meta->msec, meta->arch); 319 else if (meta->msec != NULL) 320 mandoc_asprintf(&cp, "%s(%s)", meta->title, meta->msec); 321 else if (meta->arch != NULL) 322 mandoc_asprintf(&cp, "%s (%s)", meta->title, meta->arch); 323 else 324 cp = mandoc_strdup(meta->title); 325 326 print_otag(h, TAG_TITLE, ""); 327 print_text(h, cp); 328 free(cp); 329 } 330 331 static void 332 print_mdoc_nodelist(MDOC_ARGS) 333 { 334 335 while (n != NULL) { 336 print_mdoc_node(meta, n, h); 337 n = n->next; 338 } 339 } 340 341 static void 342 print_mdoc_node(MDOC_ARGS) 343 { 344 struct tag *t; 345 int child; 346 347 if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 348 return; 349 350 if ((n->flags & NODE_NOFILL) == 0) 351 html_fillmode(h, ROFF_fi); 352 else if (html_fillmode(h, ROFF_nf) == ROFF_nf && 353 n->tok != ROFF_fi && n->flags & NODE_LINE) 354 print_endline(h); 355 356 child = 1; 357 n->flags &= ~NODE_ENDED; 358 switch (n->type) { 359 case ROFFT_TEXT: 360 if (n->flags & NODE_LINE) { 361 switch (*n->string) { 362 case '\0': 363 h->col = 1; 364 print_endline(h); 365 return; 366 case ' ': 367 if ((h->flags & HTML_NONEWLINE) == 0 && 368 (n->flags & NODE_NOFILL) == 0) 369 print_otag(h, TAG_BR, ""); 370 break; 371 default: 372 break; 373 } 374 } 375 t = h->tag; 376 t->refcnt++; 377 if (n->flags & NODE_DELIMC) 378 h->flags |= HTML_NOSPACE; 379 if (n->flags & NODE_HREF) 380 print_tagged_text(h, n->string, n); 381 else 382 print_text(h, n->string); 383 if (n->flags & NODE_DELIMO) 384 h->flags |= HTML_NOSPACE; 385 break; 386 case ROFFT_EQN: 387 t = h->tag; 388 t->refcnt++; 389 print_eqn(h, n->eqn); 390 break; 391 case ROFFT_TBL: 392 /* 393 * This will take care of initialising all of the table 394 * state data for the first table, then tearing it down 395 * for the last one. 396 */ 397 print_tbl(h, n->span); 398 return; 399 default: 400 /* 401 * Close out the current table, if it's open, and unset 402 * the "meta" table state. This will be reopened on the 403 * next table element. 404 */ 405 if (h->tblt != NULL) 406 print_tblclose(h); 407 assert(h->tblt == NULL); 408 t = h->tag; 409 t->refcnt++; 410 if (n->tok < ROFF_MAX) { 411 roff_html_pre(h, n); 412 t->refcnt--; 413 print_stagq(h, t); 414 return; 415 } 416 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 417 if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL && 418 (n->end == ENDBODY_NOT || n->child != NULL)) 419 child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta, 420 n, h); 421 break; 422 } 423 424 if (h->flags & HTML_KEEP && n->flags & NODE_LINE) { 425 h->flags &= ~HTML_KEEP; 426 h->flags |= HTML_PREKEEP; 427 } 428 429 if (child && n->child != NULL) 430 print_mdoc_nodelist(meta, n->child, h); 431 432 t->refcnt--; 433 print_stagq(h, t); 434 435 switch (n->type) { 436 case ROFFT_TEXT: 437 case ROFFT_EQN: 438 break; 439 default: 440 if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL || 441 n->flags & NODE_ENDED) 442 break; 443 (*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h); 444 if (n->end != ENDBODY_NOT) 445 n->body->flags |= NODE_ENDED; 446 break; 447 } 448 } 449 450 static void 451 mdoc_root_post(const struct roff_meta *meta, struct html *h) 452 { 453 struct tag *t, *tt; 454 455 t = print_otag(h, TAG_TABLE, "c", "foot"); 456 tt = print_otag(h, TAG_TR, ""); 457 458 print_otag(h, TAG_TD, "c", "foot-date"); 459 print_text(h, meta->date); 460 print_stagq(h, tt); 461 462 print_otag(h, TAG_TD, "c", "foot-os"); 463 print_text(h, meta->os); 464 print_tagq(h, t); 465 } 466 467 static int 468 mdoc_root_pre(const struct roff_meta *meta, struct html *h) 469 { 470 struct tag *t, *tt; 471 char *volume, *title; 472 473 if (NULL == meta->arch) 474 volume = mandoc_strdup(meta->vol); 475 else 476 mandoc_asprintf(&volume, "%s (%s)", 477 meta->vol, meta->arch); 478 479 if (NULL == meta->msec) 480 title = mandoc_strdup(meta->title); 481 else 482 mandoc_asprintf(&title, "%s(%s)", 483 meta->title, meta->msec); 484 485 t = print_otag(h, TAG_TABLE, "c", "head"); 486 tt = print_otag(h, TAG_TR, ""); 487 488 print_otag(h, TAG_TD, "c", "head-ltitle"); 489 print_text(h, title); 490 print_stagq(h, tt); 491 492 print_otag(h, TAG_TD, "c", "head-vol"); 493 print_text(h, volume); 494 print_stagq(h, tt); 495 496 print_otag(h, TAG_TD, "c", "head-rtitle"); 497 print_text(h, title); 498 print_tagq(h, t); 499 500 free(title); 501 free(volume); 502 return 1; 503 } 504 505 static int 506 mdoc_code_pre(MDOC_ARGS) 507 { 508 print_otag_id(h, TAG_CODE, roff_name[n->tok], n); 509 return 1; 510 } 511 512 static int 513 mdoc_sh_pre(MDOC_ARGS) 514 { 515 struct roff_node *sn, *subn; 516 struct tag *t, *tsec, *tsub; 517 char *id; 518 int sc; 519 520 switch (n->type) { 521 case ROFFT_BLOCK: 522 html_close_paragraph(h); 523 if ((h->oflags & HTML_TOC) == 0 || 524 h->flags & HTML_TOCDONE || 525 n->sec <= SEC_SYNOPSIS) { 526 print_otag(h, TAG_SECTION, "c", "Sh"); 527 break; 528 } 529 h->flags |= HTML_TOCDONE; 530 sc = 0; 531 for (sn = n->next; sn != NULL; sn = sn->next) 532 if (sn->sec == SEC_CUSTOM) 533 if (++sc == 2) 534 break; 535 if (sc < 2) 536 break; 537 t = print_otag(h, TAG_H1, "c", "Sh"); 538 print_text(h, "TABLE OF CONTENTS"); 539 print_tagq(h, t); 540 t = print_otag(h, TAG_UL, "c", "Bl-compact"); 541 for (sn = n; sn != NULL; sn = sn->next) { 542 tsec = print_otag(h, TAG_LI, ""); 543 id = html_make_id(sn->head, 0); 544 tsub = print_otag(h, TAG_A, "hR", id); 545 free(id); 546 print_mdoc_nodelist(meta, sn->head->child, h); 547 print_tagq(h, tsub); 548 tsub = NULL; 549 for (subn = sn->body->child; subn != NULL; 550 subn = subn->next) { 551 if (subn->tok != MDOC_Ss) 552 continue; 553 id = html_make_id(subn->head, 0); 554 if (id == NULL) 555 continue; 556 if (tsub == NULL) 557 print_otag(h, TAG_UL, 558 "c", "Bl-compact"); 559 tsub = print_otag(h, TAG_LI, ""); 560 print_otag(h, TAG_A, "hR", id); 561 free(id); 562 print_mdoc_nodelist(meta, 563 subn->head->child, h); 564 print_tagq(h, tsub); 565 } 566 print_tagq(h, tsec); 567 } 568 print_tagq(h, t); 569 print_otag(h, TAG_SECTION, "c", "Sh"); 570 break; 571 case ROFFT_HEAD: 572 print_otag_id(h, TAG_H1, "Sh", n); 573 break; 574 case ROFFT_BODY: 575 if (n->sec == SEC_AUTHORS) 576 h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); 577 break; 578 default: 579 break; 580 } 581 return 1; 582 } 583 584 static int 585 mdoc_ss_pre(MDOC_ARGS) 586 { 587 switch (n->type) { 588 case ROFFT_BLOCK: 589 html_close_paragraph(h); 590 print_otag(h, TAG_SECTION, "c", "Ss"); 591 break; 592 case ROFFT_HEAD: 593 print_otag_id(h, TAG_H2, "Ss", n); 594 break; 595 case ROFFT_BODY: 596 break; 597 default: 598 abort(); 599 } 600 return 1; 601 } 602 603 static int 604 mdoc_fl_pre(MDOC_ARGS) 605 { 606 struct roff_node *nn; 607 608 print_otag_id(h, TAG_CODE, "Fl", n); 609 print_text(h, "\\-"); 610 if (n->child != NULL || 611 ((nn = roff_node_next(n)) != NULL && 612 nn->type != ROFFT_TEXT && 613 (nn->flags & NODE_LINE) == 0)) 614 h->flags |= HTML_NOSPACE; 615 616 return 1; 617 } 618 619 static int 620 mdoc_nd_pre(MDOC_ARGS) 621 { 622 switch (n->type) { 623 case ROFFT_BLOCK: 624 return 1; 625 case ROFFT_HEAD: 626 return 0; 627 case ROFFT_BODY: 628 break; 629 default: 630 abort(); 631 } 632 print_text(h, "\\(em"); 633 print_otag(h, TAG_SPAN, "c", "Nd"); 634 return 1; 635 } 636 637 static int 638 mdoc_nm_pre(MDOC_ARGS) 639 { 640 switch (n->type) { 641 case ROFFT_BLOCK: 642 break; 643 case ROFFT_HEAD: 644 print_otag(h, TAG_TD, ""); 645 /* FALLTHROUGH */ 646 case ROFFT_ELEM: 647 print_otag(h, TAG_CODE, "c", "Nm"); 648 return 1; 649 case ROFFT_BODY: 650 print_otag(h, TAG_TD, ""); 651 return 1; 652 default: 653 abort(); 654 } 655 html_close_paragraph(h); 656 synopsis_pre(h, n); 657 print_otag(h, TAG_TABLE, "c", "Nm"); 658 print_otag(h, TAG_TR, ""); 659 return 1; 660 } 661 662 static int 663 mdoc_xr_pre(MDOC_ARGS) 664 { 665 if (NULL == n->child) 666 return 0; 667 668 if (h->base_man1) 669 print_otag(h, TAG_A, "chM", "Xr", 670 n->child->string, n->child->next == NULL ? 671 NULL : n->child->next->string); 672 else 673 print_otag(h, TAG_A, "c", "Xr"); 674 675 n = n->child; 676 print_text(h, n->string); 677 678 if (NULL == (n = n->next)) 679 return 0; 680 681 h->flags |= HTML_NOSPACE; 682 print_text(h, "("); 683 h->flags |= HTML_NOSPACE; 684 print_text(h, n->string); 685 h->flags |= HTML_NOSPACE; 686 print_text(h, ")"); 687 return 0; 688 } 689 690 static int 691 mdoc_tg_pre(MDOC_ARGS) 692 { 693 char *id; 694 695 if ((id = html_make_id(n, 1)) != NULL) { 696 print_tagq(h, print_otag(h, TAG_MARK, "i", id)); 697 free(id); 698 } 699 return 0; 700 } 701 702 static int 703 mdoc_ns_pre(MDOC_ARGS) 704 { 705 706 if ( ! (NODE_LINE & n->flags)) 707 h->flags |= HTML_NOSPACE; 708 return 1; 709 } 710 711 static int 712 mdoc_ar_pre(MDOC_ARGS) 713 { 714 print_otag(h, TAG_VAR, "c", "Ar"); 715 return 1; 716 } 717 718 static int 719 mdoc_xx_pre(MDOC_ARGS) 720 { 721 print_otag(h, TAG_SPAN, "c", "Ux"); 722 return 1; 723 } 724 725 static int 726 mdoc_it_pre(MDOC_ARGS) 727 { 728 const struct roff_node *bl; 729 enum mdoc_list type; 730 731 bl = n->parent; 732 while (bl->tok != MDOC_Bl) 733 bl = bl->parent; 734 type = bl->norm->Bl.type; 735 736 switch (type) { 737 case LIST_bullet: 738 case LIST_dash: 739 case LIST_hyphen: 740 case LIST_item: 741 case LIST_enum: 742 switch (n->type) { 743 case ROFFT_HEAD: 744 return 0; 745 case ROFFT_BODY: 746 print_otag_id(h, TAG_LI, NULL, n); 747 break; 748 default: 749 break; 750 } 751 break; 752 case LIST_diag: 753 case LIST_hang: 754 case LIST_inset: 755 case LIST_ohang: 756 switch (n->type) { 757 case ROFFT_HEAD: 758 print_otag_id(h, TAG_DT, NULL, n); 759 break; 760 case ROFFT_BODY: 761 print_otag(h, TAG_DD, ""); 762 break; 763 default: 764 break; 765 } 766 break; 767 case LIST_tag: 768 switch (n->type) { 769 case ROFFT_HEAD: 770 print_otag_id(h, TAG_DT, NULL, n); 771 break; 772 case ROFFT_BODY: 773 if (n->child == NULL) { 774 print_otag(h, TAG_DD, "s", "width", "auto"); 775 print_text(h, "\\ "); 776 } else 777 print_otag(h, TAG_DD, ""); 778 break; 779 default: 780 break; 781 } 782 break; 783 case LIST_column: 784 switch (n->type) { 785 case ROFFT_HEAD: 786 break; 787 case ROFFT_BODY: 788 print_otag(h, TAG_TD, ""); 789 break; 790 default: 791 print_otag_id(h, TAG_TR, NULL, n); 792 } 793 default: 794 break; 795 } 796 797 return 1; 798 } 799 800 static int 801 mdoc_bl_pre(MDOC_ARGS) 802 { 803 char cattr[32]; 804 struct mdoc_bl *bl; 805 enum htmltag elemtype; 806 807 switch (n->type) { 808 case ROFFT_BLOCK: 809 html_close_paragraph(h); 810 break; 811 case ROFFT_HEAD: 812 return 0; 813 case ROFFT_BODY: 814 return 1; 815 default: 816 abort(); 817 } 818 819 bl = &n->norm->Bl; 820 switch (bl->type) { 821 case LIST_bullet: 822 elemtype = TAG_UL; 823 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 824 break; 825 case LIST_dash: 826 case LIST_hyphen: 827 elemtype = TAG_UL; 828 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 829 break; 830 case LIST_item: 831 elemtype = TAG_UL; 832 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 833 break; 834 case LIST_enum: 835 elemtype = TAG_OL; 836 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 837 break; 838 case LIST_diag: 839 elemtype = TAG_DL; 840 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 841 break; 842 case LIST_hang: 843 elemtype = TAG_DL; 844 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 845 break; 846 case LIST_inset: 847 elemtype = TAG_DL; 848 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 849 break; 850 case LIST_ohang: 851 elemtype = TAG_DL; 852 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 853 break; 854 case LIST_tag: 855 if (bl->offs) 856 print_otag(h, TAG_DIV, "c", "Bd-indent"); 857 print_otag_id(h, TAG_DL, 858 bl->comp ? "Bl-tag Bl-compact" : "Bl-tag", n->body); 859 return 1; 860 case LIST_column: 861 elemtype = TAG_TABLE; 862 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 863 break; 864 default: 865 abort(); 866 } 867 if (bl->offs != NULL) 868 (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); 869 if (bl->comp) 870 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 871 print_otag_id(h, elemtype, cattr, n->body); 872 return 1; 873 } 874 875 static int 876 mdoc_ex_pre(MDOC_ARGS) 877 { 878 if (roff_node_prev(n) != NULL) 879 print_otag(h, TAG_BR, ""); 880 return 1; 881 } 882 883 static int 884 mdoc_st_pre(MDOC_ARGS) 885 { 886 print_otag(h, TAG_SPAN, "c", "St"); 887 return 1; 888 } 889 890 static int 891 mdoc_em_pre(MDOC_ARGS) 892 { 893 print_otag_id(h, TAG_I, "Em", n); 894 return 1; 895 } 896 897 static int 898 mdoc_d1_pre(MDOC_ARGS) 899 { 900 switch (n->type) { 901 case ROFFT_BLOCK: 902 html_close_paragraph(h); 903 return 1; 904 case ROFFT_HEAD: 905 return 0; 906 case ROFFT_BODY: 907 break; 908 default: 909 abort(); 910 } 911 print_otag_id(h, TAG_DIV, "Bd Bd-indent", n); 912 if (n->tok == MDOC_Dl) 913 print_otag(h, TAG_CODE, "c", "Li"); 914 return 1; 915 } 916 917 static int 918 mdoc_sx_pre(MDOC_ARGS) 919 { 920 char *id; 921 922 id = html_make_id(n, 0); 923 print_otag(h, TAG_A, "chR", "Sx", id); 924 free(id); 925 return 1; 926 } 927 928 static int 929 mdoc_bd_pre(MDOC_ARGS) 930 { 931 char buf[20]; 932 struct roff_node *nn; 933 int comp; 934 935 switch (n->type) { 936 case ROFFT_BLOCK: 937 html_close_paragraph(h); 938 return 1; 939 case ROFFT_HEAD: 940 return 0; 941 case ROFFT_BODY: 942 break; 943 default: 944 abort(); 945 } 946 947 /* Handle preceding whitespace. */ 948 949 comp = n->norm->Bd.comp; 950 for (nn = n; nn != NULL && comp == 0; nn = nn->parent) { 951 if (nn->type != ROFFT_BLOCK) 952 continue; 953 if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) 954 comp = 1; 955 if (roff_node_prev(nn) != NULL) 956 break; 957 } 958 (void)strlcpy(buf, "Bd", sizeof(buf)); 959 if (comp == 0) 960 (void)strlcat(buf, " Pp", sizeof(buf)); 961 962 /* Handle the -offset argument. */ 963 964 if (n->norm->Bd.offs != NULL && 965 strcmp(n->norm->Bd.offs, "left") != 0) 966 (void)strlcat(buf, " Bd-indent", sizeof(buf)); 967 968 if (n->norm->Bd.type == DISP_literal) 969 (void)strlcat(buf, " Li", sizeof(buf)); 970 971 print_otag_id(h, TAG_DIV, buf, n); 972 return 1; 973 } 974 975 static int 976 mdoc_pa_pre(MDOC_ARGS) 977 { 978 print_otag(h, TAG_SPAN, "c", "Pa"); 979 return 1; 980 } 981 982 static int 983 mdoc_ad_pre(MDOC_ARGS) 984 { 985 print_otag(h, TAG_SPAN, "c", "Ad"); 986 return 1; 987 } 988 989 static int 990 mdoc_an_pre(MDOC_ARGS) 991 { 992 if (n->norm->An.auth == AUTH_split) { 993 h->flags &= ~HTML_NOSPLIT; 994 h->flags |= HTML_SPLIT; 995 return 0; 996 } 997 if (n->norm->An.auth == AUTH_nosplit) { 998 h->flags &= ~HTML_SPLIT; 999 h->flags |= HTML_NOSPLIT; 1000 return 0; 1001 } 1002 1003 if (h->flags & HTML_SPLIT) 1004 print_otag(h, TAG_BR, ""); 1005 1006 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 1007 h->flags |= HTML_SPLIT; 1008 1009 print_otag(h, TAG_SPAN, "c", "An"); 1010 return 1; 1011 } 1012 1013 static int 1014 mdoc_cd_pre(MDOC_ARGS) 1015 { 1016 synopsis_pre(h, n); 1017 print_otag(h, TAG_CODE, "c", "Cd"); 1018 return 1; 1019 } 1020 1021 static int 1022 mdoc_fa_pre(MDOC_ARGS) 1023 { 1024 const struct roff_node *nn; 1025 struct tag *t; 1026 1027 if (n->parent->tok != MDOC_Fo) { 1028 print_otag(h, TAG_VAR, "c", "Fa"); 1029 return 1; 1030 } 1031 for (nn = n->child; nn != NULL; nn = nn->next) { 1032 t = print_otag(h, TAG_VAR, "c", "Fa"); 1033 print_text(h, nn->string); 1034 print_tagq(h, t); 1035 if (nn->next != NULL) { 1036 h->flags |= HTML_NOSPACE; 1037 print_text(h, ","); 1038 } 1039 } 1040 if (n->child != NULL && 1041 (nn = roff_node_next(n)) != NULL && 1042 nn->tok == MDOC_Fa) { 1043 h->flags |= HTML_NOSPACE; 1044 print_text(h, ","); 1045 } 1046 return 0; 1047 } 1048 1049 static int 1050 mdoc_fd_pre(MDOC_ARGS) 1051 { 1052 struct tag *t; 1053 char *buf, *cp; 1054 1055 synopsis_pre(h, n); 1056 1057 if (NULL == (n = n->child)) 1058 return 0; 1059 1060 assert(n->type == ROFFT_TEXT); 1061 1062 if (strcmp(n->string, "#include")) { 1063 print_otag(h, TAG_CODE, "c", "Fd"); 1064 return 1; 1065 } 1066 1067 print_otag(h, TAG_CODE, "c", "In"); 1068 print_text(h, n->string); 1069 1070 if (NULL != (n = n->next)) { 1071 assert(n->type == ROFFT_TEXT); 1072 1073 if (h->base_includes) { 1074 cp = n->string; 1075 if (*cp == '<' || *cp == '"') 1076 cp++; 1077 buf = mandoc_strdup(cp); 1078 cp = strchr(buf, '\0') - 1; 1079 if (cp >= buf && (*cp == '>' || *cp == '"')) 1080 *cp = '\0'; 1081 t = print_otag(h, TAG_A, "chI", "In", buf); 1082 free(buf); 1083 } else 1084 t = print_otag(h, TAG_A, "c", "In"); 1085 1086 print_text(h, n->string); 1087 print_tagq(h, t); 1088 1089 n = n->next; 1090 } 1091 1092 for ( ; n; n = n->next) { 1093 assert(n->type == ROFFT_TEXT); 1094 print_text(h, n->string); 1095 } 1096 1097 return 0; 1098 } 1099 1100 static int 1101 mdoc_vt_pre(MDOC_ARGS) 1102 { 1103 if (n->type == ROFFT_BLOCK) { 1104 synopsis_pre(h, n); 1105 return 1; 1106 } else if (n->type == ROFFT_ELEM) { 1107 synopsis_pre(h, n); 1108 } else if (n->type == ROFFT_HEAD) 1109 return 0; 1110 1111 print_otag(h, TAG_VAR, "c", "Vt"); 1112 return 1; 1113 } 1114 1115 static int 1116 mdoc_ft_pre(MDOC_ARGS) 1117 { 1118 synopsis_pre(h, n); 1119 print_otag(h, TAG_VAR, "c", "Ft"); 1120 return 1; 1121 } 1122 1123 static int 1124 mdoc_fn_pre(MDOC_ARGS) 1125 { 1126 struct tag *t; 1127 char nbuf[BUFSIZ]; 1128 const char *sp, *ep; 1129 int sz, pretty; 1130 1131 pretty = NODE_SYNPRETTY & n->flags; 1132 synopsis_pre(h, n); 1133 1134 /* Split apart into type and name. */ 1135 assert(n->child->string); 1136 sp = n->child->string; 1137 1138 ep = strchr(sp, ' '); 1139 if (NULL != ep) { 1140 t = print_otag(h, TAG_VAR, "c", "Ft"); 1141 1142 while (ep) { 1143 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1144 (void)memcpy(nbuf, sp, (size_t)sz); 1145 nbuf[sz] = '\0'; 1146 print_text(h, nbuf); 1147 sp = ++ep; 1148 ep = strchr(sp, ' '); 1149 } 1150 print_tagq(h, t); 1151 } 1152 1153 t = print_otag_id(h, TAG_CODE, "Fn", n); 1154 1155 if (sp) 1156 print_text(h, sp); 1157 1158 print_tagq(h, t); 1159 1160 h->flags |= HTML_NOSPACE; 1161 print_text(h, "("); 1162 h->flags |= HTML_NOSPACE; 1163 1164 for (n = n->child->next; n; n = n->next) { 1165 if (NODE_SYNPRETTY & n->flags) 1166 t = print_otag(h, TAG_VAR, "cs", "Fa", 1167 "white-space", "nowrap"); 1168 else 1169 t = print_otag(h, TAG_VAR, "c", "Fa"); 1170 print_text(h, n->string); 1171 print_tagq(h, t); 1172 if (n->next) { 1173 h->flags |= HTML_NOSPACE; 1174 print_text(h, ","); 1175 } 1176 } 1177 1178 h->flags |= HTML_NOSPACE; 1179 print_text(h, ")"); 1180 1181 if (pretty) { 1182 h->flags |= HTML_NOSPACE; 1183 print_text(h, ";"); 1184 } 1185 1186 return 0; 1187 } 1188 1189 static int 1190 mdoc_sm_pre(MDOC_ARGS) 1191 { 1192 1193 if (NULL == n->child) 1194 h->flags ^= HTML_NONOSPACE; 1195 else if (0 == strcmp("on", n->child->string)) 1196 h->flags &= ~HTML_NONOSPACE; 1197 else 1198 h->flags |= HTML_NONOSPACE; 1199 1200 if ( ! (HTML_NONOSPACE & h->flags)) 1201 h->flags &= ~HTML_NOSPACE; 1202 1203 return 0; 1204 } 1205 1206 static int 1207 mdoc_skip_pre(MDOC_ARGS) 1208 { 1209 1210 return 0; 1211 } 1212 1213 static int 1214 mdoc_pp_pre(MDOC_ARGS) 1215 { 1216 char *id; 1217 1218 if (n->flags & NODE_NOFILL) { 1219 print_endline(h); 1220 if (n->flags & NODE_ID) 1221 mdoc_tg_pre(meta, n, h); 1222 else { 1223 h->col = 1; 1224 print_endline(h); 1225 } 1226 } else { 1227 html_close_paragraph(h); 1228 id = n->flags & NODE_ID ? html_make_id(n, 1) : NULL; 1229 print_otag(h, TAG_P, "ci", "Pp", id); 1230 free(id); 1231 } 1232 return 0; 1233 } 1234 1235 static int 1236 mdoc_lk_pre(MDOC_ARGS) 1237 { 1238 const struct roff_node *link, *descr, *punct; 1239 struct tag *t; 1240 1241 if ((link = n->child) == NULL) 1242 return 0; 1243 1244 /* Find beginning of trailing punctuation. */ 1245 punct = n->last; 1246 while (punct != link && punct->flags & NODE_DELIMC) 1247 punct = punct->prev; 1248 punct = punct->next; 1249 1250 /* Link target and link text. */ 1251 descr = link->next; 1252 if (descr == punct) 1253 descr = link; /* no text */ 1254 t = print_otag(h, TAG_A, "ch", "Lk", link->string); 1255 do { 1256 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1257 h->flags |= HTML_NOSPACE; 1258 print_text(h, descr->string); 1259 descr = descr->next; 1260 } while (descr != punct); 1261 print_tagq(h, t); 1262 1263 /* Trailing punctuation. */ 1264 while (punct != NULL) { 1265 h->flags |= HTML_NOSPACE; 1266 print_text(h, punct->string); 1267 punct = punct->next; 1268 } 1269 return 0; 1270 } 1271 1272 static int 1273 mdoc_mt_pre(MDOC_ARGS) 1274 { 1275 struct tag *t; 1276 char *cp; 1277 1278 for (n = n->child; n; n = n->next) { 1279 assert(n->type == ROFFT_TEXT); 1280 mandoc_asprintf(&cp, "mailto:%s", n->string); 1281 t = print_otag(h, TAG_A, "ch", "Mt", cp); 1282 print_text(h, n->string); 1283 print_tagq(h, t); 1284 free(cp); 1285 } 1286 return 0; 1287 } 1288 1289 static int 1290 mdoc_fo_pre(MDOC_ARGS) 1291 { 1292 struct tag *t; 1293 1294 switch (n->type) { 1295 case ROFFT_BLOCK: 1296 synopsis_pre(h, n); 1297 return 1; 1298 case ROFFT_HEAD: 1299 if (n->child != NULL) { 1300 t = print_otag_id(h, TAG_CODE, "Fn", n); 1301 print_text(h, n->child->string); 1302 print_tagq(h, t); 1303 } 1304 return 0; 1305 case ROFFT_BODY: 1306 h->flags |= HTML_NOSPACE; 1307 print_text(h, "("); 1308 h->flags |= HTML_NOSPACE; 1309 return 1; 1310 default: 1311 abort(); 1312 } 1313 } 1314 1315 static void 1316 mdoc_fo_post(MDOC_ARGS) 1317 { 1318 if (n->type != ROFFT_BODY) 1319 return; 1320 h->flags |= HTML_NOSPACE; 1321 print_text(h, ")"); 1322 h->flags |= HTML_NOSPACE; 1323 print_text(h, ";"); 1324 } 1325 1326 static int 1327 mdoc_in_pre(MDOC_ARGS) 1328 { 1329 struct tag *t; 1330 1331 synopsis_pre(h, n); 1332 print_otag(h, TAG_CODE, "c", "In"); 1333 1334 /* 1335 * The first argument of the `In' gets special treatment as 1336 * being a linked value. Subsequent values are printed 1337 * afterward. groff does similarly. This also handles the case 1338 * of no children. 1339 */ 1340 1341 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) 1342 print_text(h, "#include"); 1343 1344 print_text(h, "<"); 1345 h->flags |= HTML_NOSPACE; 1346 1347 if (NULL != (n = n->child)) { 1348 assert(n->type == ROFFT_TEXT); 1349 1350 if (h->base_includes) 1351 t = print_otag(h, TAG_A, "chI", "In", n->string); 1352 else 1353 t = print_otag(h, TAG_A, "c", "In"); 1354 print_text(h, n->string); 1355 print_tagq(h, t); 1356 1357 n = n->next; 1358 } 1359 1360 h->flags |= HTML_NOSPACE; 1361 print_text(h, ">"); 1362 1363 for ( ; n; n = n->next) { 1364 assert(n->type == ROFFT_TEXT); 1365 print_text(h, n->string); 1366 } 1367 return 0; 1368 } 1369 1370 static int 1371 mdoc_va_pre(MDOC_ARGS) 1372 { 1373 print_otag(h, TAG_VAR, "c", "Va"); 1374 return 1; 1375 } 1376 1377 static int 1378 mdoc_ap_pre(MDOC_ARGS) 1379 { 1380 h->flags |= HTML_NOSPACE; 1381 print_text(h, "\\(aq"); 1382 h->flags |= HTML_NOSPACE; 1383 return 1; 1384 } 1385 1386 static int 1387 mdoc_bf_pre(MDOC_ARGS) 1388 { 1389 const char *cattr; 1390 1391 switch (n->type) { 1392 case ROFFT_BLOCK: 1393 html_close_paragraph(h); 1394 return 1; 1395 case ROFFT_HEAD: 1396 return 0; 1397 case ROFFT_BODY: 1398 break; 1399 default: 1400 abort(); 1401 } 1402 1403 if (FONT_Em == n->norm->Bf.font) 1404 cattr = "Bf Em"; 1405 else if (FONT_Sy == n->norm->Bf.font) 1406 cattr = "Bf Sy"; 1407 else if (FONT_Li == n->norm->Bf.font) 1408 cattr = "Bf Li"; 1409 else 1410 cattr = "Bf No"; 1411 1412 /* Cannot use TAG_SPAN because it may contain blocks. */ 1413 print_otag(h, TAG_DIV, "c", cattr); 1414 return 1; 1415 } 1416 1417 static int 1418 mdoc_igndelim_pre(MDOC_ARGS) 1419 { 1420 h->flags |= HTML_IGNDELIM; 1421 return 1; 1422 } 1423 1424 static void 1425 mdoc_pf_post(MDOC_ARGS) 1426 { 1427 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1428 h->flags |= HTML_NOSPACE; 1429 } 1430 1431 static int 1432 mdoc_rs_pre(MDOC_ARGS) 1433 { 1434 switch (n->type) { 1435 case ROFFT_BLOCK: 1436 if (n->sec == SEC_SEE_ALSO) 1437 html_close_paragraph(h); 1438 break; 1439 case ROFFT_HEAD: 1440 return 0; 1441 case ROFFT_BODY: 1442 if (n->sec == SEC_SEE_ALSO) 1443 print_otag(h, TAG_P, "c", "Pp"); 1444 print_otag(h, TAG_CITE, "c", "Rs"); 1445 break; 1446 default: 1447 abort(); 1448 } 1449 return 1; 1450 } 1451 1452 static int 1453 mdoc_no_pre(MDOC_ARGS) 1454 { 1455 print_otag_id(h, TAG_SPAN, roff_name[n->tok], n); 1456 return 1; 1457 } 1458 1459 static int 1460 mdoc_sy_pre(MDOC_ARGS) 1461 { 1462 print_otag_id(h, TAG_B, "Sy", n); 1463 return 1; 1464 } 1465 1466 static int 1467 mdoc_lb_pre(MDOC_ARGS) 1468 { 1469 if (n->sec == SEC_LIBRARY && 1470 n->flags & NODE_LINE && 1471 roff_node_prev(n) != NULL) 1472 print_otag(h, TAG_BR, ""); 1473 1474 print_otag(h, TAG_SPAN, "c", "Lb"); 1475 return 1; 1476 } 1477 1478 static int 1479 mdoc__x_pre(MDOC_ARGS) 1480 { 1481 struct roff_node *nn; 1482 const char *cattr; 1483 enum htmltag t; 1484 1485 t = TAG_SPAN; 1486 1487 switch (n->tok) { 1488 case MDOC__A: 1489 cattr = "RsA"; 1490 if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && 1491 ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) 1492 print_text(h, "and"); 1493 break; 1494 case MDOC__B: 1495 t = TAG_I; 1496 cattr = "RsB"; 1497 break; 1498 case MDOC__C: 1499 cattr = "RsC"; 1500 break; 1501 case MDOC__D: 1502 cattr = "RsD"; 1503 break; 1504 case MDOC__I: 1505 t = TAG_I; 1506 cattr = "RsI"; 1507 break; 1508 case MDOC__J: 1509 t = TAG_I; 1510 cattr = "RsJ"; 1511 break; 1512 case MDOC__N: 1513 cattr = "RsN"; 1514 break; 1515 case MDOC__O: 1516 cattr = "RsO"; 1517 break; 1518 case MDOC__P: 1519 cattr = "RsP"; 1520 break; 1521 case MDOC__Q: 1522 cattr = "RsQ"; 1523 break; 1524 case MDOC__R: 1525 cattr = "RsR"; 1526 break; 1527 case MDOC__T: 1528 cattr = "RsT"; 1529 break; 1530 case MDOC__U: 1531 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1532 return 1; 1533 case MDOC__V: 1534 cattr = "RsV"; 1535 break; 1536 default: 1537 abort(); 1538 } 1539 1540 print_otag(h, t, "c", cattr); 1541 return 1; 1542 } 1543 1544 static void 1545 mdoc__x_post(MDOC_ARGS) 1546 { 1547 struct roff_node *nn; 1548 1549 if (n->tok == MDOC__A && 1550 (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && 1551 ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && 1552 ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) 1553 return; 1554 1555 /* TODO: %U */ 1556 1557 if (n->parent == NULL || n->parent->tok != MDOC_Rs) 1558 return; 1559 1560 h->flags |= HTML_NOSPACE; 1561 print_text(h, roff_node_next(n) ? "," : "."); 1562 } 1563 1564 static int 1565 mdoc_bk_pre(MDOC_ARGS) 1566 { 1567 1568 switch (n->type) { 1569 case ROFFT_BLOCK: 1570 break; 1571 case ROFFT_HEAD: 1572 return 0; 1573 case ROFFT_BODY: 1574 if (n->parent->args != NULL || n->prev->child == NULL) 1575 h->flags |= HTML_PREKEEP; 1576 break; 1577 default: 1578 abort(); 1579 } 1580 1581 return 1; 1582 } 1583 1584 static void 1585 mdoc_bk_post(MDOC_ARGS) 1586 { 1587 1588 if (n->type == ROFFT_BODY) 1589 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1590 } 1591 1592 static int 1593 mdoc_quote_pre(MDOC_ARGS) 1594 { 1595 if (n->type != ROFFT_BODY) 1596 return 1; 1597 1598 switch (n->tok) { 1599 case MDOC_Ao: 1600 case MDOC_Aq: 1601 print_text(h, n->child != NULL && n->child->next == NULL && 1602 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1603 break; 1604 case MDOC_Bro: 1605 case MDOC_Brq: 1606 print_text(h, "\\(lC"); 1607 break; 1608 case MDOC_Bo: 1609 case MDOC_Bq: 1610 print_text(h, "\\(lB"); 1611 break; 1612 case MDOC_Oo: 1613 case MDOC_Op: 1614 print_text(h, "\\(lB"); 1615 /* 1616 * Give up on semantic markup for now. 1617 * We cannot use TAG_SPAN because .Oo may contain blocks. 1618 * We cannot use TAG_DIV because we might be in a 1619 * phrasing context (like .Dl or .Pp); we cannot 1620 * close out a .Pp at this point either because 1621 * that would break the line. 1622 */ 1623 /* XXX print_otag(h, TAG_???, "c", "Op"); */ 1624 break; 1625 case MDOC_En: 1626 if (NULL == n->norm->Es || 1627 NULL == n->norm->Es->child) 1628 return 1; 1629 print_text(h, n->norm->Es->child->string); 1630 break; 1631 case MDOC_Do: 1632 case MDOC_Dq: 1633 print_text(h, "\\(lq"); 1634 break; 1635 case MDOC_Qo: 1636 case MDOC_Qq: 1637 print_text(h, "\""); 1638 break; 1639 case MDOC_Po: 1640 case MDOC_Pq: 1641 print_text(h, "("); 1642 break; 1643 case MDOC_Ql: 1644 print_text(h, "\\(oq"); 1645 h->flags |= HTML_NOSPACE; 1646 print_otag(h, TAG_CODE, "c", "Li"); 1647 break; 1648 case MDOC_So: 1649 case MDOC_Sq: 1650 print_text(h, "\\(oq"); 1651 break; 1652 default: 1653 abort(); 1654 } 1655 1656 h->flags |= HTML_NOSPACE; 1657 return 1; 1658 } 1659 1660 static void 1661 mdoc_quote_post(MDOC_ARGS) 1662 { 1663 1664 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1665 return; 1666 1667 h->flags |= HTML_NOSPACE; 1668 1669 switch (n->tok) { 1670 case MDOC_Ao: 1671 case MDOC_Aq: 1672 print_text(h, n->child != NULL && n->child->next == NULL && 1673 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1674 break; 1675 case MDOC_Bro: 1676 case MDOC_Brq: 1677 print_text(h, "\\(rC"); 1678 break; 1679 case MDOC_Oo: 1680 case MDOC_Op: 1681 case MDOC_Bo: 1682 case MDOC_Bq: 1683 print_text(h, "\\(rB"); 1684 break; 1685 case MDOC_En: 1686 if (n->norm->Es == NULL || 1687 n->norm->Es->child == NULL || 1688 n->norm->Es->child->next == NULL) 1689 h->flags &= ~HTML_NOSPACE; 1690 else 1691 print_text(h, n->norm->Es->child->next->string); 1692 break; 1693 case MDOC_Do: 1694 case MDOC_Dq: 1695 print_text(h, "\\(rq"); 1696 break; 1697 case MDOC_Qo: 1698 case MDOC_Qq: 1699 print_text(h, "\""); 1700 break; 1701 case MDOC_Po: 1702 case MDOC_Pq: 1703 print_text(h, ")"); 1704 break; 1705 case MDOC_Ql: 1706 case MDOC_So: 1707 case MDOC_Sq: 1708 print_text(h, "\\(cq"); 1709 break; 1710 default: 1711 abort(); 1712 } 1713 } 1714 1715 static int 1716 mdoc_eo_pre(MDOC_ARGS) 1717 { 1718 1719 if (n->type != ROFFT_BODY) 1720 return 1; 1721 1722 if (n->end == ENDBODY_NOT && 1723 n->parent->head->child == NULL && 1724 n->child != NULL && 1725 n->child->end != ENDBODY_NOT) 1726 print_text(h, "\\&"); 1727 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1728 n->parent->head->child != NULL && (n->child != NULL || 1729 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1730 h->flags |= HTML_NOSPACE; 1731 return 1; 1732 } 1733 1734 static void 1735 mdoc_eo_post(MDOC_ARGS) 1736 { 1737 int body, tail; 1738 1739 if (n->type != ROFFT_BODY) 1740 return; 1741 1742 if (n->end != ENDBODY_NOT) { 1743 h->flags &= ~HTML_NOSPACE; 1744 return; 1745 } 1746 1747 body = n->child != NULL || n->parent->head->child != NULL; 1748 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1749 1750 if (body && tail) 1751 h->flags |= HTML_NOSPACE; 1752 else if ( ! tail) 1753 h->flags &= ~HTML_NOSPACE; 1754 } 1755 1756 static int 1757 mdoc_abort_pre(MDOC_ARGS) 1758 { 1759 abort(); 1760 } 1761