1 /* $OpenBSD: mdoc_html.c,v 1.216 2020/10/16 17:22:39 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2014-2020 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[16]; 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 print_otag_id(h, TAG_DIV, buf, n); 969 return 1; 970 } 971 972 static int 973 mdoc_pa_pre(MDOC_ARGS) 974 { 975 print_otag(h, TAG_SPAN, "c", "Pa"); 976 return 1; 977 } 978 979 static int 980 mdoc_ad_pre(MDOC_ARGS) 981 { 982 print_otag(h, TAG_SPAN, "c", "Ad"); 983 return 1; 984 } 985 986 static int 987 mdoc_an_pre(MDOC_ARGS) 988 { 989 if (n->norm->An.auth == AUTH_split) { 990 h->flags &= ~HTML_NOSPLIT; 991 h->flags |= HTML_SPLIT; 992 return 0; 993 } 994 if (n->norm->An.auth == AUTH_nosplit) { 995 h->flags &= ~HTML_SPLIT; 996 h->flags |= HTML_NOSPLIT; 997 return 0; 998 } 999 1000 if (h->flags & HTML_SPLIT) 1001 print_otag(h, TAG_BR, ""); 1002 1003 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 1004 h->flags |= HTML_SPLIT; 1005 1006 print_otag(h, TAG_SPAN, "c", "An"); 1007 return 1; 1008 } 1009 1010 static int 1011 mdoc_cd_pre(MDOC_ARGS) 1012 { 1013 synopsis_pre(h, n); 1014 print_otag(h, TAG_CODE, "c", "Cd"); 1015 return 1; 1016 } 1017 1018 static int 1019 mdoc_fa_pre(MDOC_ARGS) 1020 { 1021 const struct roff_node *nn; 1022 struct tag *t; 1023 1024 if (n->parent->tok != MDOC_Fo) { 1025 print_otag(h, TAG_VAR, "c", "Fa"); 1026 return 1; 1027 } 1028 for (nn = n->child; nn != NULL; nn = nn->next) { 1029 t = print_otag(h, TAG_VAR, "c", "Fa"); 1030 print_text(h, nn->string); 1031 print_tagq(h, t); 1032 if (nn->next != NULL) { 1033 h->flags |= HTML_NOSPACE; 1034 print_text(h, ","); 1035 } 1036 } 1037 if (n->child != NULL && 1038 (nn = roff_node_next(n)) != NULL && 1039 nn->tok == MDOC_Fa) { 1040 h->flags |= HTML_NOSPACE; 1041 print_text(h, ","); 1042 } 1043 return 0; 1044 } 1045 1046 static int 1047 mdoc_fd_pre(MDOC_ARGS) 1048 { 1049 struct tag *t; 1050 char *buf, *cp; 1051 1052 synopsis_pre(h, n); 1053 1054 if (NULL == (n = n->child)) 1055 return 0; 1056 1057 assert(n->type == ROFFT_TEXT); 1058 1059 if (strcmp(n->string, "#include")) { 1060 print_otag(h, TAG_CODE, "c", "Fd"); 1061 return 1; 1062 } 1063 1064 print_otag(h, TAG_CODE, "c", "In"); 1065 print_text(h, n->string); 1066 1067 if (NULL != (n = n->next)) { 1068 assert(n->type == ROFFT_TEXT); 1069 1070 if (h->base_includes) { 1071 cp = n->string; 1072 if (*cp == '<' || *cp == '"') 1073 cp++; 1074 buf = mandoc_strdup(cp); 1075 cp = strchr(buf, '\0') - 1; 1076 if (cp >= buf && (*cp == '>' || *cp == '"')) 1077 *cp = '\0'; 1078 t = print_otag(h, TAG_A, "chI", "In", buf); 1079 free(buf); 1080 } else 1081 t = print_otag(h, TAG_A, "c", "In"); 1082 1083 print_text(h, n->string); 1084 print_tagq(h, t); 1085 1086 n = n->next; 1087 } 1088 1089 for ( ; n; n = n->next) { 1090 assert(n->type == ROFFT_TEXT); 1091 print_text(h, n->string); 1092 } 1093 1094 return 0; 1095 } 1096 1097 static int 1098 mdoc_vt_pre(MDOC_ARGS) 1099 { 1100 if (n->type == ROFFT_BLOCK) { 1101 synopsis_pre(h, n); 1102 return 1; 1103 } else if (n->type == ROFFT_ELEM) { 1104 synopsis_pre(h, n); 1105 } else if (n->type == ROFFT_HEAD) 1106 return 0; 1107 1108 print_otag(h, TAG_VAR, "c", "Vt"); 1109 return 1; 1110 } 1111 1112 static int 1113 mdoc_ft_pre(MDOC_ARGS) 1114 { 1115 synopsis_pre(h, n); 1116 print_otag(h, TAG_VAR, "c", "Ft"); 1117 return 1; 1118 } 1119 1120 static int 1121 mdoc_fn_pre(MDOC_ARGS) 1122 { 1123 struct tag *t; 1124 char nbuf[BUFSIZ]; 1125 const char *sp, *ep; 1126 int sz, pretty; 1127 1128 pretty = NODE_SYNPRETTY & n->flags; 1129 synopsis_pre(h, n); 1130 1131 /* Split apart into type and name. */ 1132 assert(n->child->string); 1133 sp = n->child->string; 1134 1135 ep = strchr(sp, ' '); 1136 if (NULL != ep) { 1137 t = print_otag(h, TAG_VAR, "c", "Ft"); 1138 1139 while (ep) { 1140 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1141 (void)memcpy(nbuf, sp, (size_t)sz); 1142 nbuf[sz] = '\0'; 1143 print_text(h, nbuf); 1144 sp = ++ep; 1145 ep = strchr(sp, ' '); 1146 } 1147 print_tagq(h, t); 1148 } 1149 1150 t = print_otag_id(h, TAG_CODE, "Fn", n); 1151 1152 if (sp) 1153 print_text(h, sp); 1154 1155 print_tagq(h, t); 1156 1157 h->flags |= HTML_NOSPACE; 1158 print_text(h, "("); 1159 h->flags |= HTML_NOSPACE; 1160 1161 for (n = n->child->next; n; n = n->next) { 1162 if (NODE_SYNPRETTY & n->flags) 1163 t = print_otag(h, TAG_VAR, "cs", "Fa", 1164 "white-space", "nowrap"); 1165 else 1166 t = print_otag(h, TAG_VAR, "c", "Fa"); 1167 print_text(h, n->string); 1168 print_tagq(h, t); 1169 if (n->next) { 1170 h->flags |= HTML_NOSPACE; 1171 print_text(h, ","); 1172 } 1173 } 1174 1175 h->flags |= HTML_NOSPACE; 1176 print_text(h, ")"); 1177 1178 if (pretty) { 1179 h->flags |= HTML_NOSPACE; 1180 print_text(h, ";"); 1181 } 1182 1183 return 0; 1184 } 1185 1186 static int 1187 mdoc_sm_pre(MDOC_ARGS) 1188 { 1189 1190 if (NULL == n->child) 1191 h->flags ^= HTML_NONOSPACE; 1192 else if (0 == strcmp("on", n->child->string)) 1193 h->flags &= ~HTML_NONOSPACE; 1194 else 1195 h->flags |= HTML_NONOSPACE; 1196 1197 if ( ! (HTML_NONOSPACE & h->flags)) 1198 h->flags &= ~HTML_NOSPACE; 1199 1200 return 0; 1201 } 1202 1203 static int 1204 mdoc_skip_pre(MDOC_ARGS) 1205 { 1206 1207 return 0; 1208 } 1209 1210 static int 1211 mdoc_pp_pre(MDOC_ARGS) 1212 { 1213 char *id; 1214 1215 if (n->flags & NODE_NOFILL) { 1216 print_endline(h); 1217 if (n->flags & NODE_ID) 1218 mdoc_tg_pre(meta, n, h); 1219 else { 1220 h->col = 1; 1221 print_endline(h); 1222 } 1223 } else { 1224 html_close_paragraph(h); 1225 id = n->flags & NODE_ID ? html_make_id(n, 1) : NULL; 1226 print_otag(h, TAG_P, "ci", "Pp", id); 1227 free(id); 1228 } 1229 return 0; 1230 } 1231 1232 static int 1233 mdoc_lk_pre(MDOC_ARGS) 1234 { 1235 const struct roff_node *link, *descr, *punct; 1236 struct tag *t; 1237 1238 if ((link = n->child) == NULL) 1239 return 0; 1240 1241 /* Find beginning of trailing punctuation. */ 1242 punct = n->last; 1243 while (punct != link && punct->flags & NODE_DELIMC) 1244 punct = punct->prev; 1245 punct = punct->next; 1246 1247 /* Link target and link text. */ 1248 descr = link->next; 1249 if (descr == punct) 1250 descr = link; /* no text */ 1251 t = print_otag(h, TAG_A, "ch", "Lk", link->string); 1252 do { 1253 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1254 h->flags |= HTML_NOSPACE; 1255 print_text(h, descr->string); 1256 descr = descr->next; 1257 } while (descr != punct); 1258 print_tagq(h, t); 1259 1260 /* Trailing punctuation. */ 1261 while (punct != NULL) { 1262 h->flags |= HTML_NOSPACE; 1263 print_text(h, punct->string); 1264 punct = punct->next; 1265 } 1266 return 0; 1267 } 1268 1269 static int 1270 mdoc_mt_pre(MDOC_ARGS) 1271 { 1272 struct tag *t; 1273 char *cp; 1274 1275 for (n = n->child; n; n = n->next) { 1276 assert(n->type == ROFFT_TEXT); 1277 mandoc_asprintf(&cp, "mailto:%s", n->string); 1278 t = print_otag(h, TAG_A, "ch", "Mt", cp); 1279 print_text(h, n->string); 1280 print_tagq(h, t); 1281 free(cp); 1282 } 1283 return 0; 1284 } 1285 1286 static int 1287 mdoc_fo_pre(MDOC_ARGS) 1288 { 1289 struct tag *t; 1290 1291 switch (n->type) { 1292 case ROFFT_BLOCK: 1293 synopsis_pre(h, n); 1294 return 1; 1295 case ROFFT_HEAD: 1296 if (n->child != NULL) { 1297 t = print_otag_id(h, TAG_CODE, "Fn", n); 1298 print_text(h, n->child->string); 1299 print_tagq(h, t); 1300 } 1301 return 0; 1302 case ROFFT_BODY: 1303 h->flags |= HTML_NOSPACE; 1304 print_text(h, "("); 1305 h->flags |= HTML_NOSPACE; 1306 return 1; 1307 default: 1308 abort(); 1309 } 1310 } 1311 1312 static void 1313 mdoc_fo_post(MDOC_ARGS) 1314 { 1315 if (n->type != ROFFT_BODY) 1316 return; 1317 h->flags |= HTML_NOSPACE; 1318 print_text(h, ")"); 1319 h->flags |= HTML_NOSPACE; 1320 print_text(h, ";"); 1321 } 1322 1323 static int 1324 mdoc_in_pre(MDOC_ARGS) 1325 { 1326 struct tag *t; 1327 1328 synopsis_pre(h, n); 1329 print_otag(h, TAG_CODE, "c", "In"); 1330 1331 /* 1332 * The first argument of the `In' gets special treatment as 1333 * being a linked value. Subsequent values are printed 1334 * afterward. groff does similarly. This also handles the case 1335 * of no children. 1336 */ 1337 1338 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) 1339 print_text(h, "#include"); 1340 1341 print_text(h, "<"); 1342 h->flags |= HTML_NOSPACE; 1343 1344 if (NULL != (n = n->child)) { 1345 assert(n->type == ROFFT_TEXT); 1346 1347 if (h->base_includes) 1348 t = print_otag(h, TAG_A, "chI", "In", n->string); 1349 else 1350 t = print_otag(h, TAG_A, "c", "In"); 1351 print_text(h, n->string); 1352 print_tagq(h, t); 1353 1354 n = n->next; 1355 } 1356 1357 h->flags |= HTML_NOSPACE; 1358 print_text(h, ">"); 1359 1360 for ( ; n; n = n->next) { 1361 assert(n->type == ROFFT_TEXT); 1362 print_text(h, n->string); 1363 } 1364 return 0; 1365 } 1366 1367 static int 1368 mdoc_va_pre(MDOC_ARGS) 1369 { 1370 print_otag(h, TAG_VAR, "c", "Va"); 1371 return 1; 1372 } 1373 1374 static int 1375 mdoc_ap_pre(MDOC_ARGS) 1376 { 1377 h->flags |= HTML_NOSPACE; 1378 print_text(h, "\\(aq"); 1379 h->flags |= HTML_NOSPACE; 1380 return 1; 1381 } 1382 1383 static int 1384 mdoc_bf_pre(MDOC_ARGS) 1385 { 1386 const char *cattr; 1387 1388 switch (n->type) { 1389 case ROFFT_BLOCK: 1390 html_close_paragraph(h); 1391 return 1; 1392 case ROFFT_HEAD: 1393 return 0; 1394 case ROFFT_BODY: 1395 break; 1396 default: 1397 abort(); 1398 } 1399 1400 if (FONT_Em == n->norm->Bf.font) 1401 cattr = "Bf Em"; 1402 else if (FONT_Sy == n->norm->Bf.font) 1403 cattr = "Bf Sy"; 1404 else if (FONT_Li == n->norm->Bf.font) 1405 cattr = "Bf Li"; 1406 else 1407 cattr = "Bf No"; 1408 1409 /* Cannot use TAG_SPAN because it may contain blocks. */ 1410 print_otag(h, TAG_DIV, "c", cattr); 1411 return 1; 1412 } 1413 1414 static int 1415 mdoc_igndelim_pre(MDOC_ARGS) 1416 { 1417 h->flags |= HTML_IGNDELIM; 1418 return 1; 1419 } 1420 1421 static void 1422 mdoc_pf_post(MDOC_ARGS) 1423 { 1424 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1425 h->flags |= HTML_NOSPACE; 1426 } 1427 1428 static int 1429 mdoc_rs_pre(MDOC_ARGS) 1430 { 1431 switch (n->type) { 1432 case ROFFT_BLOCK: 1433 if (n->sec == SEC_SEE_ALSO) 1434 html_close_paragraph(h); 1435 break; 1436 case ROFFT_HEAD: 1437 return 0; 1438 case ROFFT_BODY: 1439 if (n->sec == SEC_SEE_ALSO) 1440 print_otag(h, TAG_P, "c", "Pp"); 1441 print_otag(h, TAG_CITE, "c", "Rs"); 1442 break; 1443 default: 1444 abort(); 1445 } 1446 return 1; 1447 } 1448 1449 static int 1450 mdoc_no_pre(MDOC_ARGS) 1451 { 1452 print_otag_id(h, TAG_SPAN, roff_name[n->tok], n); 1453 return 1; 1454 } 1455 1456 static int 1457 mdoc_sy_pre(MDOC_ARGS) 1458 { 1459 print_otag_id(h, TAG_B, "Sy", n); 1460 return 1; 1461 } 1462 1463 static int 1464 mdoc_lb_pre(MDOC_ARGS) 1465 { 1466 if (n->sec == SEC_LIBRARY && 1467 n->flags & NODE_LINE && 1468 roff_node_prev(n) != NULL) 1469 print_otag(h, TAG_BR, ""); 1470 1471 print_otag(h, TAG_SPAN, "c", "Lb"); 1472 return 1; 1473 } 1474 1475 static int 1476 mdoc__x_pre(MDOC_ARGS) 1477 { 1478 struct roff_node *nn; 1479 const char *cattr; 1480 enum htmltag t; 1481 1482 t = TAG_SPAN; 1483 1484 switch (n->tok) { 1485 case MDOC__A: 1486 cattr = "RsA"; 1487 if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && 1488 ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) 1489 print_text(h, "and"); 1490 break; 1491 case MDOC__B: 1492 t = TAG_I; 1493 cattr = "RsB"; 1494 break; 1495 case MDOC__C: 1496 cattr = "RsC"; 1497 break; 1498 case MDOC__D: 1499 cattr = "RsD"; 1500 break; 1501 case MDOC__I: 1502 t = TAG_I; 1503 cattr = "RsI"; 1504 break; 1505 case MDOC__J: 1506 t = TAG_I; 1507 cattr = "RsJ"; 1508 break; 1509 case MDOC__N: 1510 cattr = "RsN"; 1511 break; 1512 case MDOC__O: 1513 cattr = "RsO"; 1514 break; 1515 case MDOC__P: 1516 cattr = "RsP"; 1517 break; 1518 case MDOC__Q: 1519 cattr = "RsQ"; 1520 break; 1521 case MDOC__R: 1522 cattr = "RsR"; 1523 break; 1524 case MDOC__T: 1525 cattr = "RsT"; 1526 break; 1527 case MDOC__U: 1528 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1529 return 1; 1530 case MDOC__V: 1531 cattr = "RsV"; 1532 break; 1533 default: 1534 abort(); 1535 } 1536 1537 print_otag(h, t, "c", cattr); 1538 return 1; 1539 } 1540 1541 static void 1542 mdoc__x_post(MDOC_ARGS) 1543 { 1544 struct roff_node *nn; 1545 1546 if (n->tok == MDOC__A && 1547 (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && 1548 ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && 1549 ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) 1550 return; 1551 1552 /* TODO: %U */ 1553 1554 if (n->parent == NULL || n->parent->tok != MDOC_Rs) 1555 return; 1556 1557 h->flags |= HTML_NOSPACE; 1558 print_text(h, roff_node_next(n) ? "," : "."); 1559 } 1560 1561 static int 1562 mdoc_bk_pre(MDOC_ARGS) 1563 { 1564 1565 switch (n->type) { 1566 case ROFFT_BLOCK: 1567 break; 1568 case ROFFT_HEAD: 1569 return 0; 1570 case ROFFT_BODY: 1571 if (n->parent->args != NULL || n->prev->child == NULL) 1572 h->flags |= HTML_PREKEEP; 1573 break; 1574 default: 1575 abort(); 1576 } 1577 1578 return 1; 1579 } 1580 1581 static void 1582 mdoc_bk_post(MDOC_ARGS) 1583 { 1584 1585 if (n->type == ROFFT_BODY) 1586 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1587 } 1588 1589 static int 1590 mdoc_quote_pre(MDOC_ARGS) 1591 { 1592 if (n->type != ROFFT_BODY) 1593 return 1; 1594 1595 switch (n->tok) { 1596 case MDOC_Ao: 1597 case MDOC_Aq: 1598 print_text(h, n->child != NULL && n->child->next == NULL && 1599 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1600 break; 1601 case MDOC_Bro: 1602 case MDOC_Brq: 1603 print_text(h, "\\(lC"); 1604 break; 1605 case MDOC_Bo: 1606 case MDOC_Bq: 1607 print_text(h, "\\(lB"); 1608 break; 1609 case MDOC_Oo: 1610 case MDOC_Op: 1611 print_text(h, "\\(lB"); 1612 /* 1613 * Give up on semantic markup for now. 1614 * We cannot use TAG_SPAN because .Oo may contain blocks. 1615 * We cannot use TAG_DIV because we might be in a 1616 * phrasing context (like .Dl or .Pp); we cannot 1617 * close out a .Pp at this point either because 1618 * that would break the line. 1619 */ 1620 /* XXX print_otag(h, TAG_???, "c", "Op"); */ 1621 break; 1622 case MDOC_En: 1623 if (NULL == n->norm->Es || 1624 NULL == n->norm->Es->child) 1625 return 1; 1626 print_text(h, n->norm->Es->child->string); 1627 break; 1628 case MDOC_Do: 1629 case MDOC_Dq: 1630 print_text(h, "\\(lq"); 1631 break; 1632 case MDOC_Qo: 1633 case MDOC_Qq: 1634 print_text(h, "\""); 1635 break; 1636 case MDOC_Po: 1637 case MDOC_Pq: 1638 print_text(h, "("); 1639 break; 1640 case MDOC_Ql: 1641 print_text(h, "\\(oq"); 1642 h->flags |= HTML_NOSPACE; 1643 print_otag(h, TAG_CODE, "c", "Li"); 1644 break; 1645 case MDOC_So: 1646 case MDOC_Sq: 1647 print_text(h, "\\(oq"); 1648 break; 1649 default: 1650 abort(); 1651 } 1652 1653 h->flags |= HTML_NOSPACE; 1654 return 1; 1655 } 1656 1657 static void 1658 mdoc_quote_post(MDOC_ARGS) 1659 { 1660 1661 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1662 return; 1663 1664 h->flags |= HTML_NOSPACE; 1665 1666 switch (n->tok) { 1667 case MDOC_Ao: 1668 case MDOC_Aq: 1669 print_text(h, n->child != NULL && n->child->next == NULL && 1670 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1671 break; 1672 case MDOC_Bro: 1673 case MDOC_Brq: 1674 print_text(h, "\\(rC"); 1675 break; 1676 case MDOC_Oo: 1677 case MDOC_Op: 1678 case MDOC_Bo: 1679 case MDOC_Bq: 1680 print_text(h, "\\(rB"); 1681 break; 1682 case MDOC_En: 1683 if (n->norm->Es == NULL || 1684 n->norm->Es->child == NULL || 1685 n->norm->Es->child->next == NULL) 1686 h->flags &= ~HTML_NOSPACE; 1687 else 1688 print_text(h, n->norm->Es->child->next->string); 1689 break; 1690 case MDOC_Do: 1691 case MDOC_Dq: 1692 print_text(h, "\\(rq"); 1693 break; 1694 case MDOC_Qo: 1695 case MDOC_Qq: 1696 print_text(h, "\""); 1697 break; 1698 case MDOC_Po: 1699 case MDOC_Pq: 1700 print_text(h, ")"); 1701 break; 1702 case MDOC_Ql: 1703 case MDOC_So: 1704 case MDOC_Sq: 1705 print_text(h, "\\(cq"); 1706 break; 1707 default: 1708 abort(); 1709 } 1710 } 1711 1712 static int 1713 mdoc_eo_pre(MDOC_ARGS) 1714 { 1715 1716 if (n->type != ROFFT_BODY) 1717 return 1; 1718 1719 if (n->end == ENDBODY_NOT && 1720 n->parent->head->child == NULL && 1721 n->child != NULL && 1722 n->child->end != ENDBODY_NOT) 1723 print_text(h, "\\&"); 1724 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1725 n->parent->head->child != NULL && (n->child != NULL || 1726 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1727 h->flags |= HTML_NOSPACE; 1728 return 1; 1729 } 1730 1731 static void 1732 mdoc_eo_post(MDOC_ARGS) 1733 { 1734 int body, tail; 1735 1736 if (n->type != ROFFT_BODY) 1737 return; 1738 1739 if (n->end != ENDBODY_NOT) { 1740 h->flags &= ~HTML_NOSPACE; 1741 return; 1742 } 1743 1744 body = n->child != NULL || n->parent->head->child != NULL; 1745 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1746 1747 if (body && tail) 1748 h->flags |= HTML_NOSPACE; 1749 else if ( ! tail) 1750 h->flags &= ~HTML_NOSPACE; 1751 } 1752 1753 static int 1754 mdoc_abort_pre(MDOC_ARGS) 1755 { 1756 abort(); 1757 } 1758