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