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