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