1 /* $OpenBSD: mdoc_html.c,v 1.189 2018/10/02 19:43:27 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 if (tsub == NULL) 543 print_otag(h, TAG_UL, 544 "c", "Bl-compact"); 545 tsub = print_otag(h, TAG_LI, ""); 546 id = html_make_id(subn->head, 0); 547 print_otag(h, TAG_A, "hR", id); 548 free(id); 549 print_mdoc_nodelist(meta, 550 subn->head->child, h); 551 print_tagq(h, tsub); 552 } 553 print_tagq(h, tsec); 554 } 555 print_tagq(h, t); 556 break; 557 case ROFFT_HEAD: 558 id = html_make_id(n, 1); 559 print_otag(h, TAG_H1, "cTi", "Sh", id); 560 if (id != NULL) 561 print_otag(h, TAG_A, "chR", "permalink", id); 562 break; 563 case ROFFT_BODY: 564 if (n->sec == SEC_AUTHORS) 565 h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); 566 break; 567 default: 568 break; 569 } 570 return 1; 571 } 572 573 static int 574 mdoc_ss_pre(MDOC_ARGS) 575 { 576 char *id; 577 578 if (n->type != ROFFT_HEAD) 579 return 1; 580 581 id = html_make_id(n, 1); 582 print_otag(h, TAG_H2, "cTi", "Ss", id); 583 if (id != NULL) 584 print_otag(h, TAG_A, "chR", "permalink", id); 585 return 1; 586 } 587 588 static int 589 mdoc_fl_pre(MDOC_ARGS) 590 { 591 char *id; 592 593 if ((id = cond_id(n)) != NULL) 594 print_otag(h, TAG_A, "chR", "permalink", id); 595 print_otag(h, TAG_CODE, "cTi", "Fl", id); 596 597 print_text(h, "\\-"); 598 if (!(n->child == NULL && 599 (n->next == NULL || 600 n->next->type == ROFFT_TEXT || 601 n->next->flags & NODE_LINE))) 602 h->flags |= HTML_NOSPACE; 603 604 return 1; 605 } 606 607 static int 608 mdoc_cm_pre(MDOC_ARGS) 609 { 610 char *id; 611 612 if ((id = cond_id(n)) != NULL) 613 print_otag(h, TAG_A, "chR", "permalink", id); 614 print_otag(h, TAG_CODE, "cTi", "Cm", id); 615 return 1; 616 } 617 618 static int 619 mdoc_nd_pre(MDOC_ARGS) 620 { 621 if (n->type != ROFFT_BODY) 622 return 1; 623 624 print_text(h, "\\(em"); 625 /* Cannot use TAG_SPAN because it may contain blocks. */ 626 print_otag(h, TAG_DIV, "cT", "Nd"); 627 return 1; 628 } 629 630 static int 631 mdoc_nm_pre(MDOC_ARGS) 632 { 633 switch (n->type) { 634 case ROFFT_HEAD: 635 print_otag(h, TAG_TD, ""); 636 /* FALLTHROUGH */ 637 case ROFFT_ELEM: 638 print_otag(h, TAG_CODE, "cT", "Nm"); 639 return 1; 640 case ROFFT_BODY: 641 print_otag(h, TAG_TD, ""); 642 return 1; 643 default: 644 break; 645 } 646 synopsis_pre(h, n); 647 print_otag(h, TAG_TABLE, "c", "Nm"); 648 print_otag(h, TAG_TR, ""); 649 return 1; 650 } 651 652 static int 653 mdoc_xr_pre(MDOC_ARGS) 654 { 655 if (NULL == n->child) 656 return 0; 657 658 if (h->base_man1) 659 print_otag(h, TAG_A, "cThM", "Xr", 660 n->child->string, n->child->next == NULL ? 661 NULL : n->child->next->string); 662 else 663 print_otag(h, TAG_A, "cT", "Xr"); 664 665 n = n->child; 666 print_text(h, n->string); 667 668 if (NULL == (n = n->next)) 669 return 0; 670 671 h->flags |= HTML_NOSPACE; 672 print_text(h, "("); 673 h->flags |= HTML_NOSPACE; 674 print_text(h, n->string); 675 h->flags |= HTML_NOSPACE; 676 print_text(h, ")"); 677 return 0; 678 } 679 680 static int 681 mdoc_ns_pre(MDOC_ARGS) 682 { 683 684 if ( ! (NODE_LINE & n->flags)) 685 h->flags |= HTML_NOSPACE; 686 return 1; 687 } 688 689 static int 690 mdoc_ar_pre(MDOC_ARGS) 691 { 692 print_otag(h, TAG_VAR, "cT", "Ar"); 693 return 1; 694 } 695 696 static int 697 mdoc_xx_pre(MDOC_ARGS) 698 { 699 print_otag(h, TAG_SPAN, "c", "Ux"); 700 return 1; 701 } 702 703 static int 704 mdoc_it_pre(MDOC_ARGS) 705 { 706 const struct roff_node *bl; 707 struct tag *t; 708 enum mdoc_list type; 709 710 bl = n->parent; 711 while (bl->tok != MDOC_Bl) 712 bl = bl->parent; 713 type = bl->norm->Bl.type; 714 715 switch (type) { 716 case LIST_bullet: 717 case LIST_dash: 718 case LIST_hyphen: 719 case LIST_item: 720 case LIST_enum: 721 switch (n->type) { 722 case ROFFT_HEAD: 723 return 0; 724 case ROFFT_BODY: 725 print_otag(h, TAG_LI, ""); 726 break; 727 default: 728 break; 729 } 730 break; 731 case LIST_diag: 732 case LIST_hang: 733 case LIST_inset: 734 case LIST_ohang: 735 switch (n->type) { 736 case ROFFT_HEAD: 737 print_otag(h, TAG_DT, ""); 738 break; 739 case ROFFT_BODY: 740 print_otag(h, TAG_DD, ""); 741 break; 742 default: 743 break; 744 } 745 break; 746 case LIST_tag: 747 switch (n->type) { 748 case ROFFT_HEAD: 749 if (h->style != NULL && !bl->norm->Bl.comp && 750 (n->parent->prev == NULL || 751 n->parent->prev->body == NULL || 752 n->parent->prev->body->child != NULL)) { 753 t = print_otag(h, TAG_DT, ""); 754 print_text(h, "\\ "); 755 print_tagq(h, t); 756 t = print_otag(h, TAG_DD, ""); 757 print_text(h, "\\ "); 758 print_tagq(h, t); 759 } 760 print_otag(h, TAG_DT, ""); 761 break; 762 case ROFFT_BODY: 763 if (n->child == NULL) { 764 print_otag(h, TAG_DD, "s", "width", "auto"); 765 print_text(h, "\\ "); 766 } else 767 print_otag(h, TAG_DD, ""); 768 break; 769 default: 770 break; 771 } 772 break; 773 case LIST_column: 774 switch (n->type) { 775 case ROFFT_HEAD: 776 break; 777 case ROFFT_BODY: 778 print_otag(h, TAG_TD, ""); 779 break; 780 default: 781 print_otag(h, TAG_TR, ""); 782 } 783 default: 784 break; 785 } 786 787 return 1; 788 } 789 790 static int 791 mdoc_bl_pre(MDOC_ARGS) 792 { 793 char cattr[28]; 794 struct mdoc_bl *bl; 795 enum htmltag elemtype; 796 797 switch (n->type) { 798 case ROFFT_BODY: 799 return 1; 800 case ROFFT_HEAD: 801 return 0; 802 default: 803 break; 804 } 805 806 bl = &n->norm->Bl; 807 switch (bl->type) { 808 case LIST_bullet: 809 elemtype = TAG_UL; 810 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 811 break; 812 case LIST_dash: 813 case LIST_hyphen: 814 elemtype = TAG_UL; 815 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 816 break; 817 case LIST_item: 818 elemtype = TAG_UL; 819 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 820 break; 821 case LIST_enum: 822 elemtype = TAG_OL; 823 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 824 break; 825 case LIST_diag: 826 elemtype = TAG_DL; 827 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 828 break; 829 case LIST_hang: 830 elemtype = TAG_DL; 831 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 832 break; 833 case LIST_inset: 834 elemtype = TAG_DL; 835 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 836 break; 837 case LIST_ohang: 838 elemtype = TAG_DL; 839 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 840 break; 841 case LIST_tag: 842 if (bl->offs) 843 print_otag(h, TAG_DIV, "c", "Bd-indent"); 844 print_otag(h, TAG_DL, "c", bl->comp ? 845 "Bl-tag Bl-compact" : "Bl-tag"); 846 return 1; 847 case LIST_column: 848 elemtype = TAG_TABLE; 849 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 850 break; 851 default: 852 abort(); 853 } 854 if (bl->offs != NULL) 855 (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); 856 if (bl->comp) 857 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 858 print_otag(h, elemtype, "c", cattr); 859 return 1; 860 } 861 862 static int 863 mdoc_ex_pre(MDOC_ARGS) 864 { 865 if (n->prev) 866 print_otag(h, TAG_BR, ""); 867 return 1; 868 } 869 870 static int 871 mdoc_st_pre(MDOC_ARGS) 872 { 873 print_otag(h, TAG_SPAN, "cT", "St"); 874 return 1; 875 } 876 877 static int 878 mdoc_em_pre(MDOC_ARGS) 879 { 880 print_otag(h, TAG_I, "cT", "Em"); 881 return 1; 882 } 883 884 static int 885 mdoc_d1_pre(MDOC_ARGS) 886 { 887 if (n->type != ROFFT_BLOCK) 888 return 1; 889 890 print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); 891 892 if (n->tok == MDOC_Dl) 893 print_otag(h, TAG_CODE, "c", "Li"); 894 895 return 1; 896 } 897 898 static int 899 mdoc_sx_pre(MDOC_ARGS) 900 { 901 char *id; 902 903 id = html_make_id(n, 0); 904 print_otag(h, TAG_A, "cThR", "Sx", id); 905 free(id); 906 return 1; 907 } 908 909 static int 910 mdoc_bd_pre(MDOC_ARGS) 911 { 912 int comp, sv; 913 struct roff_node *nn; 914 915 if (n->type == ROFFT_HEAD) 916 return 0; 917 918 if (n->type == ROFFT_BLOCK) { 919 comp = n->norm->Bd.comp; 920 for (nn = n; nn && ! comp; nn = nn->parent) { 921 if (nn->type != ROFFT_BLOCK) 922 continue; 923 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) 924 comp = 1; 925 if (nn->prev) 926 break; 927 } 928 if ( ! comp) 929 print_paragraph(h); 930 return 1; 931 } 932 933 /* Handle the -offset argument. */ 934 935 if (n->norm->Bd.offs == NULL || 936 ! strcmp(n->norm->Bd.offs, "left")) 937 print_otag(h, TAG_DIV, "c", "Bd"); 938 else 939 print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); 940 941 if (n->norm->Bd.type != DISP_unfilled && 942 n->norm->Bd.type != DISP_literal) 943 return 1; 944 945 print_otag(h, TAG_PRE, "c", "Li"); 946 947 /* This can be recursive: save & set our literal state. */ 948 949 sv = h->flags & HTML_LITERAL; 950 h->flags |= HTML_LITERAL; 951 952 for (nn = n->child; nn; nn = nn->next) { 953 print_mdoc_node(meta, nn, h); 954 /* 955 * If the printed node flushes its own line, then we 956 * needn't do it here as well. This is hacky, but the 957 * notion of selective eoln whitespace is pretty dumb 958 * anyway, so don't sweat it. 959 */ 960 switch (nn->tok) { 961 case ROFF_br: 962 case ROFF_sp: 963 case MDOC_Sm: 964 case MDOC_Bl: 965 case MDOC_D1: 966 case MDOC_Dl: 967 case MDOC_Lp: 968 case MDOC_Pp: 969 continue; 970 default: 971 break; 972 } 973 if (h->flags & HTML_NONEWLINE || 974 (nn->next && ! (nn->next->flags & NODE_LINE))) 975 continue; 976 else if (nn->next) 977 print_text(h, "\n"); 978 979 h->flags |= HTML_NOSPACE; 980 } 981 982 if (0 == sv) 983 h->flags &= ~HTML_LITERAL; 984 985 return 0; 986 } 987 988 static int 989 mdoc_pa_pre(MDOC_ARGS) 990 { 991 print_otag(h, TAG_SPAN, "cT", "Pa"); 992 return 1; 993 } 994 995 static int 996 mdoc_ad_pre(MDOC_ARGS) 997 { 998 print_otag(h, TAG_SPAN, "c", "Ad"); 999 return 1; 1000 } 1001 1002 static int 1003 mdoc_an_pre(MDOC_ARGS) 1004 { 1005 if (n->norm->An.auth == AUTH_split) { 1006 h->flags &= ~HTML_NOSPLIT; 1007 h->flags |= HTML_SPLIT; 1008 return 0; 1009 } 1010 if (n->norm->An.auth == AUTH_nosplit) { 1011 h->flags &= ~HTML_SPLIT; 1012 h->flags |= HTML_NOSPLIT; 1013 return 0; 1014 } 1015 1016 if (h->flags & HTML_SPLIT) 1017 print_otag(h, TAG_BR, ""); 1018 1019 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 1020 h->flags |= HTML_SPLIT; 1021 1022 print_otag(h, TAG_SPAN, "cT", "An"); 1023 return 1; 1024 } 1025 1026 static int 1027 mdoc_cd_pre(MDOC_ARGS) 1028 { 1029 synopsis_pre(h, n); 1030 print_otag(h, TAG_CODE, "cT", "Cd"); 1031 return 1; 1032 } 1033 1034 static int 1035 mdoc_dv_pre(MDOC_ARGS) 1036 { 1037 char *id; 1038 1039 if ((id = cond_id(n)) != NULL) 1040 print_otag(h, TAG_A, "chR", "permalink", id); 1041 print_otag(h, TAG_CODE, "cTi", "Dv", id); 1042 return 1; 1043 } 1044 1045 static int 1046 mdoc_ev_pre(MDOC_ARGS) 1047 { 1048 char *id; 1049 1050 if ((id = cond_id(n)) != NULL) 1051 print_otag(h, TAG_A, "chR", "permalink", id); 1052 print_otag(h, TAG_CODE, "cTi", "Ev", id); 1053 return 1; 1054 } 1055 1056 static int 1057 mdoc_er_pre(MDOC_ARGS) 1058 { 1059 char *id; 1060 1061 id = n->sec == SEC_ERRORS && 1062 (n->parent->tok == MDOC_It || 1063 (n->parent->tok == MDOC_Bq && 1064 n->parent->parent->parent->tok == MDOC_It)) ? 1065 html_make_id(n, 1) : NULL; 1066 1067 if (id != NULL) 1068 print_otag(h, TAG_A, "chR", "permalink", id); 1069 print_otag(h, TAG_CODE, "cTi", "Er", id); 1070 return 1; 1071 } 1072 1073 static int 1074 mdoc_fa_pre(MDOC_ARGS) 1075 { 1076 const struct roff_node *nn; 1077 struct tag *t; 1078 1079 if (n->parent->tok != MDOC_Fo) { 1080 print_otag(h, TAG_VAR, "cT", "Fa"); 1081 return 1; 1082 } 1083 1084 for (nn = n->child; nn; nn = nn->next) { 1085 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1086 print_text(h, nn->string); 1087 print_tagq(h, t); 1088 if (nn->next) { 1089 h->flags |= HTML_NOSPACE; 1090 print_text(h, ","); 1091 } 1092 } 1093 1094 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1095 h->flags |= HTML_NOSPACE; 1096 print_text(h, ","); 1097 } 1098 1099 return 0; 1100 } 1101 1102 static int 1103 mdoc_fd_pre(MDOC_ARGS) 1104 { 1105 struct tag *t; 1106 char *buf, *cp; 1107 1108 synopsis_pre(h, n); 1109 1110 if (NULL == (n = n->child)) 1111 return 0; 1112 1113 assert(n->type == ROFFT_TEXT); 1114 1115 if (strcmp(n->string, "#include")) { 1116 print_otag(h, TAG_CODE, "cT", "Fd"); 1117 return 1; 1118 } 1119 1120 print_otag(h, TAG_CODE, "cT", "In"); 1121 print_text(h, n->string); 1122 1123 if (NULL != (n = n->next)) { 1124 assert(n->type == ROFFT_TEXT); 1125 1126 if (h->base_includes) { 1127 cp = n->string; 1128 if (*cp == '<' || *cp == '"') 1129 cp++; 1130 buf = mandoc_strdup(cp); 1131 cp = strchr(buf, '\0') - 1; 1132 if (cp >= buf && (*cp == '>' || *cp == '"')) 1133 *cp = '\0'; 1134 t = print_otag(h, TAG_A, "cThI", "In", buf); 1135 free(buf); 1136 } else 1137 t = print_otag(h, TAG_A, "cT", "In"); 1138 1139 print_text(h, n->string); 1140 print_tagq(h, t); 1141 1142 n = n->next; 1143 } 1144 1145 for ( ; n; n = n->next) { 1146 assert(n->type == ROFFT_TEXT); 1147 print_text(h, n->string); 1148 } 1149 1150 return 0; 1151 } 1152 1153 static int 1154 mdoc_vt_pre(MDOC_ARGS) 1155 { 1156 if (n->type == ROFFT_BLOCK) { 1157 synopsis_pre(h, n); 1158 return 1; 1159 } else if (n->type == ROFFT_ELEM) { 1160 synopsis_pre(h, n); 1161 } else if (n->type == ROFFT_HEAD) 1162 return 0; 1163 1164 print_otag(h, TAG_VAR, "cT", "Vt"); 1165 return 1; 1166 } 1167 1168 static int 1169 mdoc_ft_pre(MDOC_ARGS) 1170 { 1171 synopsis_pre(h, n); 1172 print_otag(h, TAG_VAR, "cT", "Ft"); 1173 return 1; 1174 } 1175 1176 static int 1177 mdoc_fn_pre(MDOC_ARGS) 1178 { 1179 struct tag *t; 1180 char nbuf[BUFSIZ]; 1181 const char *sp, *ep; 1182 int sz, pretty; 1183 1184 pretty = NODE_SYNPRETTY & n->flags; 1185 synopsis_pre(h, n); 1186 1187 /* Split apart into type and name. */ 1188 assert(n->child->string); 1189 sp = n->child->string; 1190 1191 ep = strchr(sp, ' '); 1192 if (NULL != ep) { 1193 t = print_otag(h, TAG_VAR, "cT", "Ft"); 1194 1195 while (ep) { 1196 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1197 (void)memcpy(nbuf, sp, (size_t)sz); 1198 nbuf[sz] = '\0'; 1199 print_text(h, nbuf); 1200 sp = ++ep; 1201 ep = strchr(sp, ' '); 1202 } 1203 print_tagq(h, t); 1204 } 1205 1206 t = print_otag(h, TAG_CODE, "cT", "Fn"); 1207 1208 if (sp) 1209 print_text(h, sp); 1210 1211 print_tagq(h, t); 1212 1213 h->flags |= HTML_NOSPACE; 1214 print_text(h, "("); 1215 h->flags |= HTML_NOSPACE; 1216 1217 for (n = n->child->next; n; n = n->next) { 1218 if (NODE_SYNPRETTY & n->flags) 1219 t = print_otag(h, TAG_VAR, "cTs", "Fa", 1220 "white-space", "nowrap"); 1221 else 1222 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1223 print_text(h, n->string); 1224 print_tagq(h, t); 1225 if (n->next) { 1226 h->flags |= HTML_NOSPACE; 1227 print_text(h, ","); 1228 } 1229 } 1230 1231 h->flags |= HTML_NOSPACE; 1232 print_text(h, ")"); 1233 1234 if (pretty) { 1235 h->flags |= HTML_NOSPACE; 1236 print_text(h, ";"); 1237 } 1238 1239 return 0; 1240 } 1241 1242 static int 1243 mdoc_sm_pre(MDOC_ARGS) 1244 { 1245 1246 if (NULL == n->child) 1247 h->flags ^= HTML_NONOSPACE; 1248 else if (0 == strcmp("on", n->child->string)) 1249 h->flags &= ~HTML_NONOSPACE; 1250 else 1251 h->flags |= HTML_NONOSPACE; 1252 1253 if ( ! (HTML_NONOSPACE & h->flags)) 1254 h->flags &= ~HTML_NOSPACE; 1255 1256 return 0; 1257 } 1258 1259 static int 1260 mdoc_skip_pre(MDOC_ARGS) 1261 { 1262 1263 return 0; 1264 } 1265 1266 static int 1267 mdoc_pp_pre(MDOC_ARGS) 1268 { 1269 1270 print_paragraph(h); 1271 return 0; 1272 } 1273 1274 static int 1275 mdoc_lk_pre(MDOC_ARGS) 1276 { 1277 const struct roff_node *link, *descr, *punct; 1278 struct tag *t; 1279 1280 if ((link = n->child) == NULL) 1281 return 0; 1282 1283 /* Find beginning of trailing punctuation. */ 1284 punct = n->last; 1285 while (punct != link && punct->flags & NODE_DELIMC) 1286 punct = punct->prev; 1287 punct = punct->next; 1288 1289 /* Link target and link text. */ 1290 descr = link->next; 1291 if (descr == punct) 1292 descr = link; /* no text */ 1293 t = print_otag(h, TAG_A, "cTh", "Lk", link->string); 1294 do { 1295 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1296 h->flags |= HTML_NOSPACE; 1297 print_text(h, descr->string); 1298 descr = descr->next; 1299 } while (descr != punct); 1300 print_tagq(h, t); 1301 1302 /* Trailing punctuation. */ 1303 while (punct != NULL) { 1304 h->flags |= HTML_NOSPACE; 1305 print_text(h, punct->string); 1306 punct = punct->next; 1307 } 1308 return 0; 1309 } 1310 1311 static int 1312 mdoc_mt_pre(MDOC_ARGS) 1313 { 1314 struct tag *t; 1315 char *cp; 1316 1317 for (n = n->child; n; n = n->next) { 1318 assert(n->type == ROFFT_TEXT); 1319 1320 mandoc_asprintf(&cp, "mailto:%s", n->string); 1321 t = print_otag(h, TAG_A, "cTh", "Mt", cp); 1322 print_text(h, n->string); 1323 print_tagq(h, t); 1324 free(cp); 1325 } 1326 1327 return 0; 1328 } 1329 1330 static int 1331 mdoc_fo_pre(MDOC_ARGS) 1332 { 1333 struct tag *t; 1334 1335 if (n->type == ROFFT_BODY) { 1336 h->flags |= HTML_NOSPACE; 1337 print_text(h, "("); 1338 h->flags |= HTML_NOSPACE; 1339 return 1; 1340 } else if (n->type == ROFFT_BLOCK) { 1341 synopsis_pre(h, n); 1342 return 1; 1343 } 1344 1345 if (n->child == NULL) 1346 return 0; 1347 1348 assert(n->child->string); 1349 t = print_otag(h, TAG_CODE, "cT", "Fn"); 1350 print_text(h, n->child->string); 1351 print_tagq(h, t); 1352 return 0; 1353 } 1354 1355 static void 1356 mdoc_fo_post(MDOC_ARGS) 1357 { 1358 1359 if (n->type != ROFFT_BODY) 1360 return; 1361 h->flags |= HTML_NOSPACE; 1362 print_text(h, ")"); 1363 h->flags |= HTML_NOSPACE; 1364 print_text(h, ";"); 1365 } 1366 1367 static int 1368 mdoc_in_pre(MDOC_ARGS) 1369 { 1370 struct tag *t; 1371 1372 synopsis_pre(h, n); 1373 print_otag(h, TAG_CODE, "cT", "In"); 1374 1375 /* 1376 * The first argument of the `In' gets special treatment as 1377 * being a linked value. Subsequent values are printed 1378 * afterward. groff does similarly. This also handles the case 1379 * of no children. 1380 */ 1381 1382 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) 1383 print_text(h, "#include"); 1384 1385 print_text(h, "<"); 1386 h->flags |= HTML_NOSPACE; 1387 1388 if (NULL != (n = n->child)) { 1389 assert(n->type == ROFFT_TEXT); 1390 1391 if (h->base_includes) 1392 t = print_otag(h, TAG_A, "cThI", "In", n->string); 1393 else 1394 t = print_otag(h, TAG_A, "cT", "In"); 1395 print_text(h, n->string); 1396 print_tagq(h, t); 1397 1398 n = n->next; 1399 } 1400 1401 h->flags |= HTML_NOSPACE; 1402 print_text(h, ">"); 1403 1404 for ( ; n; n = n->next) { 1405 assert(n->type == ROFFT_TEXT); 1406 print_text(h, n->string); 1407 } 1408 1409 return 0; 1410 } 1411 1412 static int 1413 mdoc_ic_pre(MDOC_ARGS) 1414 { 1415 char *id; 1416 1417 if ((id = cond_id(n)) != NULL) 1418 print_otag(h, TAG_A, "chR", "permalink", id); 1419 print_otag(h, TAG_CODE, "cTi", "Ic", id); 1420 return 1; 1421 } 1422 1423 static int 1424 mdoc_va_pre(MDOC_ARGS) 1425 { 1426 print_otag(h, TAG_VAR, "cT", "Va"); 1427 return 1; 1428 } 1429 1430 static int 1431 mdoc_ap_pre(MDOC_ARGS) 1432 { 1433 1434 h->flags |= HTML_NOSPACE; 1435 print_text(h, "\\(aq"); 1436 h->flags |= HTML_NOSPACE; 1437 return 1; 1438 } 1439 1440 static int 1441 mdoc_bf_pre(MDOC_ARGS) 1442 { 1443 const char *cattr; 1444 1445 if (n->type == ROFFT_HEAD) 1446 return 0; 1447 else if (n->type != ROFFT_BODY) 1448 return 1; 1449 1450 if (FONT_Em == n->norm->Bf.font) 1451 cattr = "Bf Em"; 1452 else if (FONT_Sy == n->norm->Bf.font) 1453 cattr = "Bf Sy"; 1454 else if (FONT_Li == n->norm->Bf.font) 1455 cattr = "Bf Li"; 1456 else 1457 cattr = "Bf No"; 1458 1459 /* Cannot use TAG_SPAN because it may contain blocks. */ 1460 print_otag(h, TAG_DIV, "c", cattr); 1461 return 1; 1462 } 1463 1464 static int 1465 mdoc_ms_pre(MDOC_ARGS) 1466 { 1467 char *id; 1468 1469 if ((id = cond_id(n)) != NULL) 1470 print_otag(h, TAG_A, "chR", "permalink", id); 1471 print_otag(h, TAG_SPAN, "cTi", "Ms", id); 1472 return 1; 1473 } 1474 1475 static int 1476 mdoc_igndelim_pre(MDOC_ARGS) 1477 { 1478 1479 h->flags |= HTML_IGNDELIM; 1480 return 1; 1481 } 1482 1483 static void 1484 mdoc_pf_post(MDOC_ARGS) 1485 { 1486 1487 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1488 h->flags |= HTML_NOSPACE; 1489 } 1490 1491 static int 1492 mdoc_rs_pre(MDOC_ARGS) 1493 { 1494 if (n->type != ROFFT_BLOCK) 1495 return 1; 1496 1497 if (n->prev && SEC_SEE_ALSO == n->sec) 1498 print_paragraph(h); 1499 1500 print_otag(h, TAG_CITE, "cT", "Rs"); 1501 return 1; 1502 } 1503 1504 static int 1505 mdoc_no_pre(MDOC_ARGS) 1506 { 1507 char *id; 1508 1509 if ((id = cond_id(n)) != NULL) 1510 print_otag(h, TAG_A, "chR", "permalink", id); 1511 print_otag(h, TAG_SPAN, "ci", "No", id); 1512 return 1; 1513 } 1514 1515 static int 1516 mdoc_li_pre(MDOC_ARGS) 1517 { 1518 char *id; 1519 1520 if ((id = cond_id(n)) != NULL) 1521 print_otag(h, TAG_A, "chR", "permalink", id); 1522 print_otag(h, TAG_CODE, "ci", "Li", id); 1523 return 1; 1524 } 1525 1526 static int 1527 mdoc_sy_pre(MDOC_ARGS) 1528 { 1529 print_otag(h, TAG_B, "cT", "Sy"); 1530 return 1; 1531 } 1532 1533 static int 1534 mdoc_lb_pre(MDOC_ARGS) 1535 { 1536 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) 1537 print_otag(h, TAG_BR, ""); 1538 1539 print_otag(h, TAG_SPAN, "cT", "Lb"); 1540 return 1; 1541 } 1542 1543 static int 1544 mdoc__x_pre(MDOC_ARGS) 1545 { 1546 const char *cattr; 1547 enum htmltag t; 1548 1549 t = TAG_SPAN; 1550 1551 switch (n->tok) { 1552 case MDOC__A: 1553 cattr = "RsA"; 1554 if (n->prev && MDOC__A == n->prev->tok) 1555 if (NULL == n->next || MDOC__A != n->next->tok) 1556 print_text(h, "and"); 1557 break; 1558 case MDOC__B: 1559 t = TAG_I; 1560 cattr = "RsB"; 1561 break; 1562 case MDOC__C: 1563 cattr = "RsC"; 1564 break; 1565 case MDOC__D: 1566 cattr = "RsD"; 1567 break; 1568 case MDOC__I: 1569 t = TAG_I; 1570 cattr = "RsI"; 1571 break; 1572 case MDOC__J: 1573 t = TAG_I; 1574 cattr = "RsJ"; 1575 break; 1576 case MDOC__N: 1577 cattr = "RsN"; 1578 break; 1579 case MDOC__O: 1580 cattr = "RsO"; 1581 break; 1582 case MDOC__P: 1583 cattr = "RsP"; 1584 break; 1585 case MDOC__Q: 1586 cattr = "RsQ"; 1587 break; 1588 case MDOC__R: 1589 cattr = "RsR"; 1590 break; 1591 case MDOC__T: 1592 cattr = "RsT"; 1593 break; 1594 case MDOC__U: 1595 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1596 return 1; 1597 case MDOC__V: 1598 cattr = "RsV"; 1599 break; 1600 default: 1601 abort(); 1602 } 1603 1604 print_otag(h, t, "c", cattr); 1605 return 1; 1606 } 1607 1608 static void 1609 mdoc__x_post(MDOC_ARGS) 1610 { 1611 1612 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1613 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1614 if (NULL == n->prev || MDOC__A != n->prev->tok) 1615 return; 1616 1617 /* TODO: %U */ 1618 1619 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1620 return; 1621 1622 h->flags |= HTML_NOSPACE; 1623 print_text(h, n->next ? "," : "."); 1624 } 1625 1626 static int 1627 mdoc_bk_pre(MDOC_ARGS) 1628 { 1629 1630 switch (n->type) { 1631 case ROFFT_BLOCK: 1632 break; 1633 case ROFFT_HEAD: 1634 return 0; 1635 case ROFFT_BODY: 1636 if (n->parent->args != NULL || n->prev->child == NULL) 1637 h->flags |= HTML_PREKEEP; 1638 break; 1639 default: 1640 abort(); 1641 } 1642 1643 return 1; 1644 } 1645 1646 static void 1647 mdoc_bk_post(MDOC_ARGS) 1648 { 1649 1650 if (n->type == ROFFT_BODY) 1651 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1652 } 1653 1654 static int 1655 mdoc_quote_pre(MDOC_ARGS) 1656 { 1657 if (n->type != ROFFT_BODY) 1658 return 1; 1659 1660 switch (n->tok) { 1661 case MDOC_Ao: 1662 case MDOC_Aq: 1663 print_text(h, n->child != NULL && n->child->next == NULL && 1664 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1665 break; 1666 case MDOC_Bro: 1667 case MDOC_Brq: 1668 print_text(h, "\\(lC"); 1669 break; 1670 case MDOC_Bo: 1671 case MDOC_Bq: 1672 print_text(h, "\\(lB"); 1673 break; 1674 case MDOC_Oo: 1675 case MDOC_Op: 1676 print_text(h, "\\(lB"); 1677 h->flags |= HTML_NOSPACE; 1678 /* Cannot use TAG_SPAN because it may contain blocks. */ 1679 print_otag(h, TAG_IDIV, "c", "Op"); 1680 break; 1681 case MDOC_En: 1682 if (NULL == n->norm->Es || 1683 NULL == n->norm->Es->child) 1684 return 1; 1685 print_text(h, n->norm->Es->child->string); 1686 break; 1687 case MDOC_Do: 1688 case MDOC_Dq: 1689 case MDOC_Qo: 1690 case MDOC_Qq: 1691 print_text(h, "\\(lq"); 1692 break; 1693 case MDOC_Po: 1694 case MDOC_Pq: 1695 print_text(h, "("); 1696 break; 1697 case MDOC_Ql: 1698 print_text(h, "\\(oq"); 1699 h->flags |= HTML_NOSPACE; 1700 print_otag(h, TAG_CODE, "c", "Li"); 1701 break; 1702 case MDOC_So: 1703 case MDOC_Sq: 1704 print_text(h, "\\(oq"); 1705 break; 1706 default: 1707 abort(); 1708 } 1709 1710 h->flags |= HTML_NOSPACE; 1711 return 1; 1712 } 1713 1714 static void 1715 mdoc_quote_post(MDOC_ARGS) 1716 { 1717 1718 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1719 return; 1720 1721 h->flags |= HTML_NOSPACE; 1722 1723 switch (n->tok) { 1724 case MDOC_Ao: 1725 case MDOC_Aq: 1726 print_text(h, n->child != NULL && n->child->next == NULL && 1727 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1728 break; 1729 case MDOC_Bro: 1730 case MDOC_Brq: 1731 print_text(h, "\\(rC"); 1732 break; 1733 case MDOC_Oo: 1734 case MDOC_Op: 1735 case MDOC_Bo: 1736 case MDOC_Bq: 1737 print_text(h, "\\(rB"); 1738 break; 1739 case MDOC_En: 1740 if (n->norm->Es == NULL || 1741 n->norm->Es->child == NULL || 1742 n->norm->Es->child->next == NULL) 1743 h->flags &= ~HTML_NOSPACE; 1744 else 1745 print_text(h, n->norm->Es->child->next->string); 1746 break; 1747 case MDOC_Qo: 1748 case MDOC_Qq: 1749 case MDOC_Do: 1750 case MDOC_Dq: 1751 print_text(h, "\\(rq"); 1752 break; 1753 case MDOC_Po: 1754 case MDOC_Pq: 1755 print_text(h, ")"); 1756 break; 1757 case MDOC_Ql: 1758 case MDOC_So: 1759 case MDOC_Sq: 1760 print_text(h, "\\(cq"); 1761 break; 1762 default: 1763 abort(); 1764 } 1765 } 1766 1767 static int 1768 mdoc_eo_pre(MDOC_ARGS) 1769 { 1770 1771 if (n->type != ROFFT_BODY) 1772 return 1; 1773 1774 if (n->end == ENDBODY_NOT && 1775 n->parent->head->child == NULL && 1776 n->child != NULL && 1777 n->child->end != ENDBODY_NOT) 1778 print_text(h, "\\&"); 1779 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1780 n->parent->head->child != NULL && (n->child != NULL || 1781 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1782 h->flags |= HTML_NOSPACE; 1783 return 1; 1784 } 1785 1786 static void 1787 mdoc_eo_post(MDOC_ARGS) 1788 { 1789 int body, tail; 1790 1791 if (n->type != ROFFT_BODY) 1792 return; 1793 1794 if (n->end != ENDBODY_NOT) { 1795 h->flags &= ~HTML_NOSPACE; 1796 return; 1797 } 1798 1799 body = n->child != NULL || n->parent->head->child != NULL; 1800 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1801 1802 if (body && tail) 1803 h->flags |= HTML_NOSPACE; 1804 else if ( ! tail) 1805 h->flags &= ~HTML_NOSPACE; 1806 } 1807