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