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