1 /* $OpenBSD: mdoc_html.c,v 1.202 2019/01/18 14:36:16 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 html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); 353 354 child = 1; 355 n->flags &= ~NODE_ENDED; 356 switch (n->type) { 357 case ROFFT_TEXT: 358 t = h->tag; 359 t->refcnt++; 360 361 /* No tables in this mode... */ 362 assert(NULL == h->tblt); 363 364 /* 365 * Make sure that if we're in a literal mode already 366 * (i.e., within a <PRE>) don't print the newline. 367 */ 368 if (*n->string == ' ' && n->flags & NODE_LINE && 369 (h->flags & HTML_NONEWLINE) == 0 && 370 (n->flags & NODE_NOFILL) == 0) 371 print_otag(h, TAG_BR, ""); 372 if (NODE_DELIMC & n->flags) 373 h->flags |= HTML_NOSPACE; 374 print_text(h, n->string); 375 if (NODE_DELIMO & n->flags) 376 h->flags |= HTML_NOSPACE; 377 break; 378 case ROFFT_EQN: 379 t = h->tag; 380 t->refcnt++; 381 print_eqn(h, n->eqn); 382 break; 383 case ROFFT_TBL: 384 /* 385 * This will take care of initialising all of the table 386 * state data for the first table, then tearing it down 387 * for the last one. 388 */ 389 print_tbl(h, n->span); 390 return; 391 default: 392 /* 393 * Close out the current table, if it's open, and unset 394 * the "meta" table state. This will be reopened on the 395 * next table element. 396 */ 397 if (h->tblt != NULL) 398 print_tblclose(h); 399 assert(h->tblt == NULL); 400 t = h->tag; 401 t->refcnt++; 402 if (n->tok < ROFF_MAX) { 403 roff_html_pre(h, n); 404 t->refcnt--; 405 print_stagq(h, t); 406 return; 407 } 408 assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); 409 if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL && 410 (n->end == ENDBODY_NOT || n->child != NULL)) 411 child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta, 412 n, h); 413 break; 414 } 415 416 if (h->flags & HTML_KEEP && n->flags & NODE_LINE) { 417 h->flags &= ~HTML_KEEP; 418 h->flags |= HTML_PREKEEP; 419 } 420 421 if (child && n->child != NULL) 422 print_mdoc_nodelist(meta, n->child, h); 423 424 t->refcnt--; 425 print_stagq(h, t); 426 427 switch (n->type) { 428 case ROFFT_TEXT: 429 case ROFFT_EQN: 430 break; 431 default: 432 if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL || 433 n->flags & NODE_ENDED) 434 break; 435 (*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h); 436 if (n->end != ENDBODY_NOT) 437 n->body->flags |= NODE_ENDED; 438 break; 439 } 440 441 if (n->flags & NODE_NOFILL && 442 (n->next == NULL || n->next->flags & NODE_LINE)) { 443 h->col++; 444 print_endline(h); 445 } 446 } 447 448 static void 449 mdoc_root_post(const struct roff_meta *meta, struct html *h) 450 { 451 struct tag *t, *tt; 452 453 t = print_otag(h, TAG_TABLE, "c", "foot"); 454 tt = print_otag(h, TAG_TR, ""); 455 456 print_otag(h, TAG_TD, "c", "foot-date"); 457 print_text(h, meta->date); 458 print_stagq(h, tt); 459 460 print_otag(h, TAG_TD, "c", "foot-os"); 461 print_text(h, meta->os); 462 print_tagq(h, t); 463 } 464 465 static int 466 mdoc_root_pre(const struct roff_meta *meta, struct html *h) 467 { 468 struct tag *t, *tt; 469 char *volume, *title; 470 471 if (NULL == meta->arch) 472 volume = mandoc_strdup(meta->vol); 473 else 474 mandoc_asprintf(&volume, "%s (%s)", 475 meta->vol, meta->arch); 476 477 if (NULL == meta->msec) 478 title = mandoc_strdup(meta->title); 479 else 480 mandoc_asprintf(&title, "%s(%s)", 481 meta->title, meta->msec); 482 483 t = print_otag(h, TAG_TABLE, "c", "head"); 484 tt = print_otag(h, TAG_TR, ""); 485 486 print_otag(h, TAG_TD, "c", "head-ltitle"); 487 print_text(h, title); 488 print_stagq(h, tt); 489 490 print_otag(h, TAG_TD, "c", "head-vol"); 491 print_text(h, volume); 492 print_stagq(h, tt); 493 494 print_otag(h, TAG_TD, "c", "head-rtitle"); 495 print_text(h, title); 496 print_tagq(h, t); 497 498 free(title); 499 free(volume); 500 return 1; 501 } 502 503 static char * 504 cond_id(const struct roff_node *n) 505 { 506 if (n->child != NULL && 507 n->child->type == ROFFT_TEXT && 508 (n->prev == NULL || 509 (n->prev->type == ROFFT_TEXT && 510 strcmp(n->prev->string, "|") == 0)) && 511 (n->parent->tok == MDOC_It || 512 (n->parent->tok == MDOC_Xo && 513 n->parent->parent->prev == NULL && 514 n->parent->parent->parent->tok == MDOC_It))) 515 return html_make_id(n, 1); 516 return NULL; 517 } 518 519 static int 520 mdoc_sh_pre(MDOC_ARGS) 521 { 522 struct roff_node *sn, *subn; 523 struct tag *t, *tsec, *tsub; 524 char *id; 525 int sc; 526 527 switch (n->type) { 528 case ROFFT_BLOCK: 529 html_close_paragraph(h); 530 if ((h->oflags & HTML_TOC) == 0 || 531 h->flags & HTML_TOCDONE || 532 n->sec <= SEC_SYNOPSIS) 533 break; 534 h->flags |= HTML_TOCDONE; 535 sc = 0; 536 for (sn = n->next; sn != NULL; sn = sn->next) 537 if (sn->sec == SEC_CUSTOM) 538 if (++sc == 2) 539 break; 540 if (sc < 2) 541 break; 542 t = print_otag(h, TAG_H1, "c", "Sh"); 543 print_text(h, "TABLE OF CONTENTS"); 544 print_tagq(h, t); 545 t = print_otag(h, TAG_UL, "c", "Bl-compact"); 546 for (sn = n; sn != NULL; sn = sn->next) { 547 tsec = print_otag(h, TAG_LI, ""); 548 id = html_make_id(sn->head, 0); 549 tsub = print_otag(h, TAG_A, "hR", id); 550 free(id); 551 print_mdoc_nodelist(meta, sn->head->child, h); 552 print_tagq(h, tsub); 553 tsub = NULL; 554 for (subn = sn->body->child; subn != NULL; 555 subn = subn->next) { 556 if (subn->tok != MDOC_Ss) 557 continue; 558 id = html_make_id(subn->head, 0); 559 if (id == NULL) 560 continue; 561 if (tsub == NULL) 562 print_otag(h, TAG_UL, 563 "c", "Bl-compact"); 564 tsub = print_otag(h, TAG_LI, ""); 565 print_otag(h, TAG_A, "hR", id); 566 free(id); 567 print_mdoc_nodelist(meta, 568 subn->head->child, h); 569 print_tagq(h, tsub); 570 } 571 print_tagq(h, tsec); 572 } 573 print_tagq(h, t); 574 break; 575 case ROFFT_HEAD: 576 id = html_make_id(n, 1); 577 print_otag(h, TAG_H1, "ci", "Sh", id); 578 if (id != NULL) 579 print_otag(h, TAG_A, "chR", "permalink", id); 580 break; 581 case ROFFT_BODY: 582 if (n->sec == SEC_AUTHORS) 583 h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); 584 break; 585 default: 586 break; 587 } 588 return 1; 589 } 590 591 static int 592 mdoc_ss_pre(MDOC_ARGS) 593 { 594 char *id; 595 596 switch (n->type) { 597 case ROFFT_BLOCK: 598 html_close_paragraph(h); 599 return 1; 600 case ROFFT_HEAD: 601 break; 602 case ROFFT_BODY: 603 return 1; 604 default: 605 abort(); 606 } 607 608 id = html_make_id(n, 1); 609 print_otag(h, TAG_H2, "ci", "Ss", id); 610 if (id != NULL) 611 print_otag(h, TAG_A, "chR", "permalink", id); 612 return 1; 613 } 614 615 static int 616 mdoc_fl_pre(MDOC_ARGS) 617 { 618 char *id; 619 620 if ((id = cond_id(n)) != NULL) 621 print_otag(h, TAG_A, "chR", "permalink", id); 622 print_otag(h, TAG_CODE, "ci", "Fl", id); 623 624 print_text(h, "\\-"); 625 if (!(n->child == NULL && 626 (n->next == NULL || 627 n->next->type == ROFFT_TEXT || 628 n->next->flags & NODE_LINE))) 629 h->flags |= HTML_NOSPACE; 630 631 return 1; 632 } 633 634 static int 635 mdoc_cm_pre(MDOC_ARGS) 636 { 637 char *id; 638 639 if ((id = cond_id(n)) != NULL) 640 print_otag(h, TAG_A, "chR", "permalink", id); 641 print_otag(h, TAG_CODE, "ci", "Cm", id); 642 return 1; 643 } 644 645 static int 646 mdoc_nd_pre(MDOC_ARGS) 647 { 648 switch (n->type) { 649 case ROFFT_BLOCK: 650 html_close_paragraph(h); 651 return 1; 652 case ROFFT_HEAD: 653 return 0; 654 case ROFFT_BODY: 655 break; 656 default: 657 abort(); 658 } 659 print_text(h, "\\(em"); 660 /* Cannot use TAG_SPAN because it may contain blocks. */ 661 print_otag(h, TAG_DIV, "c", "Nd"); 662 return 1; 663 } 664 665 static int 666 mdoc_nm_pre(MDOC_ARGS) 667 { 668 switch (n->type) { 669 case ROFFT_BLOCK: 670 break; 671 case ROFFT_HEAD: 672 print_otag(h, TAG_TD, ""); 673 /* FALLTHROUGH */ 674 case ROFFT_ELEM: 675 print_otag(h, TAG_CODE, "c", "Nm"); 676 return 1; 677 case ROFFT_BODY: 678 print_otag(h, TAG_TD, ""); 679 return 1; 680 default: 681 abort(); 682 } 683 html_close_paragraph(h); 684 synopsis_pre(h, n); 685 print_otag(h, TAG_TABLE, "c", "Nm"); 686 print_otag(h, TAG_TR, ""); 687 return 1; 688 } 689 690 static int 691 mdoc_xr_pre(MDOC_ARGS) 692 { 693 if (NULL == n->child) 694 return 0; 695 696 if (h->base_man1) 697 print_otag(h, TAG_A, "chM", "Xr", 698 n->child->string, n->child->next == NULL ? 699 NULL : n->child->next->string); 700 else 701 print_otag(h, TAG_A, "c", "Xr"); 702 703 n = n->child; 704 print_text(h, n->string); 705 706 if (NULL == (n = n->next)) 707 return 0; 708 709 h->flags |= HTML_NOSPACE; 710 print_text(h, "("); 711 h->flags |= HTML_NOSPACE; 712 print_text(h, n->string); 713 h->flags |= HTML_NOSPACE; 714 print_text(h, ")"); 715 return 0; 716 } 717 718 static int 719 mdoc_ns_pre(MDOC_ARGS) 720 { 721 722 if ( ! (NODE_LINE & n->flags)) 723 h->flags |= HTML_NOSPACE; 724 return 1; 725 } 726 727 static int 728 mdoc_ar_pre(MDOC_ARGS) 729 { 730 print_otag(h, TAG_VAR, "c", "Ar"); 731 return 1; 732 } 733 734 static int 735 mdoc_xx_pre(MDOC_ARGS) 736 { 737 print_otag(h, TAG_SPAN, "c", "Ux"); 738 return 1; 739 } 740 741 static int 742 mdoc_it_pre(MDOC_ARGS) 743 { 744 const struct roff_node *bl; 745 enum mdoc_list type; 746 747 bl = n->parent; 748 while (bl->tok != MDOC_Bl) 749 bl = bl->parent; 750 type = bl->norm->Bl.type; 751 752 switch (type) { 753 case LIST_bullet: 754 case LIST_dash: 755 case LIST_hyphen: 756 case LIST_item: 757 case LIST_enum: 758 switch (n->type) { 759 case ROFFT_HEAD: 760 return 0; 761 case ROFFT_BODY: 762 print_otag(h, TAG_LI, ""); 763 break; 764 default: 765 break; 766 } 767 break; 768 case LIST_diag: 769 case LIST_hang: 770 case LIST_inset: 771 case LIST_ohang: 772 switch (n->type) { 773 case ROFFT_HEAD: 774 print_otag(h, TAG_DT, ""); 775 break; 776 case ROFFT_BODY: 777 print_otag(h, TAG_DD, ""); 778 break; 779 default: 780 break; 781 } 782 break; 783 case LIST_tag: 784 switch (n->type) { 785 case ROFFT_HEAD: 786 print_otag(h, TAG_DT, ""); 787 break; 788 case ROFFT_BODY: 789 if (n->child == NULL) { 790 print_otag(h, TAG_DD, "s", "width", "auto"); 791 print_text(h, "\\ "); 792 } else 793 print_otag(h, TAG_DD, ""); 794 break; 795 default: 796 break; 797 } 798 break; 799 case LIST_column: 800 switch (n->type) { 801 case ROFFT_HEAD: 802 break; 803 case ROFFT_BODY: 804 print_otag(h, TAG_TD, ""); 805 break; 806 default: 807 print_otag(h, TAG_TR, ""); 808 } 809 default: 810 break; 811 } 812 813 return 1; 814 } 815 816 static int 817 mdoc_bl_pre(MDOC_ARGS) 818 { 819 char cattr[32]; 820 struct mdoc_bl *bl; 821 enum htmltag elemtype; 822 823 switch (n->type) { 824 case ROFFT_BLOCK: 825 html_close_paragraph(h); 826 break; 827 case ROFFT_HEAD: 828 return 0; 829 case ROFFT_BODY: 830 return 1; 831 default: 832 abort(); 833 } 834 835 bl = &n->norm->Bl; 836 switch (bl->type) { 837 case LIST_bullet: 838 elemtype = TAG_UL; 839 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 840 break; 841 case LIST_dash: 842 case LIST_hyphen: 843 elemtype = TAG_UL; 844 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 845 break; 846 case LIST_item: 847 elemtype = TAG_UL; 848 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 849 break; 850 case LIST_enum: 851 elemtype = TAG_OL; 852 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 853 break; 854 case LIST_diag: 855 elemtype = TAG_DL; 856 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 857 break; 858 case LIST_hang: 859 elemtype = TAG_DL; 860 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 861 break; 862 case LIST_inset: 863 elemtype = TAG_DL; 864 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 865 break; 866 case LIST_ohang: 867 elemtype = TAG_DL; 868 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 869 break; 870 case LIST_tag: 871 if (bl->offs) 872 print_otag(h, TAG_DIV, "c", "Bd-indent"); 873 print_otag(h, TAG_DL, "c", bl->comp ? 874 "Bl-tag Bl-compact" : "Bl-tag"); 875 return 1; 876 case LIST_column: 877 elemtype = TAG_TABLE; 878 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 879 break; 880 default: 881 abort(); 882 } 883 if (bl->offs != NULL) 884 (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); 885 if (bl->comp) 886 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 887 print_otag(h, elemtype, "c", cattr); 888 return 1; 889 } 890 891 static int 892 mdoc_ex_pre(MDOC_ARGS) 893 { 894 if (n->prev) 895 print_otag(h, TAG_BR, ""); 896 return 1; 897 } 898 899 static int 900 mdoc_st_pre(MDOC_ARGS) 901 { 902 print_otag(h, TAG_SPAN, "c", "St"); 903 return 1; 904 } 905 906 static int 907 mdoc_em_pre(MDOC_ARGS) 908 { 909 print_otag(h, TAG_I, "c", "Em"); 910 return 1; 911 } 912 913 static int 914 mdoc_d1_pre(MDOC_ARGS) 915 { 916 switch (n->type) { 917 case ROFFT_BLOCK: 918 html_close_paragraph(h); 919 break; 920 case ROFFT_HEAD: 921 return 0; 922 case ROFFT_BODY: 923 return 1; 924 default: 925 abort(); 926 } 927 print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); 928 if (n->tok == MDOC_Dl) 929 print_otag(h, TAG_CODE, "c", "Li"); 930 return 1; 931 } 932 933 static int 934 mdoc_sx_pre(MDOC_ARGS) 935 { 936 char *id; 937 938 id = html_make_id(n, 0); 939 print_otag(h, TAG_A, "chR", "Sx", id); 940 free(id); 941 return 1; 942 } 943 944 static int 945 mdoc_bd_pre(MDOC_ARGS) 946 { 947 char buf[16]; 948 struct roff_node *nn; 949 int comp; 950 951 switch (n->type) { 952 case ROFFT_BLOCK: 953 html_close_paragraph(h); 954 return 1; 955 case ROFFT_HEAD: 956 return 0; 957 case ROFFT_BODY: 958 break; 959 default: 960 abort(); 961 } 962 963 /* Handle preceding whitespace. */ 964 965 comp = n->norm->Bd.comp; 966 for (nn = n; nn != NULL && comp == 0; nn = nn->parent) { 967 if (nn->type != ROFFT_BLOCK) 968 continue; 969 if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) 970 comp = 1; 971 if (nn->prev != NULL) 972 break; 973 } 974 (void)strlcpy(buf, "Bd", sizeof(buf)); 975 if (comp == 0) 976 (void)strlcat(buf, " Pp", sizeof(buf)); 977 978 /* Handle the -offset argument. */ 979 980 if (n->norm->Bd.offs != NULL && 981 strcmp(n->norm->Bd.offs, "left") != 0) 982 (void)strlcat(buf, " Bd-indent", sizeof(buf)); 983 984 print_otag(h, TAG_DIV, "c", buf); 985 return 1; 986 } 987 988 static int 989 mdoc_pa_pre(MDOC_ARGS) 990 { 991 print_otag(h, TAG_SPAN, "c", "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, "c", "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, "c", "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, "ci", "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, "ci", "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, "ci", "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, "c", "Fa"); 1081 return 1; 1082 } 1083 1084 for (nn = n->child; nn; nn = nn->next) { 1085 t = print_otag(h, TAG_VAR, "c", "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, "c", "Fd"); 1117 return 1; 1118 } 1119 1120 print_otag(h, TAG_CODE, "c", "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, "chI", "In", buf); 1135 free(buf); 1136 } else 1137 t = print_otag(h, TAG_A, "c", "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, "c", "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, "c", "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, "c", "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, "c", "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, "cs", "Fa", 1220 "white-space", "nowrap"); 1221 else 1222 t = print_otag(h, TAG_VAR, "c", "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 if ((n->flags & NODE_NOFILL) == 0) { 1270 html_close_paragraph(h); 1271 print_otag(h, TAG_P, "c", "Pp"); 1272 } 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, "ch", "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, "ch", "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, "c", "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, "c", "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, "chI", "In", n->string); 1395 else 1396 t = print_otag(h, TAG_A, "c", "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, "ci", "Ic", id); 1422 return 1; 1423 } 1424 1425 static int 1426 mdoc_va_pre(MDOC_ARGS) 1427 { 1428 print_otag(h, TAG_VAR, "c", "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 switch (n->type) { 1448 case ROFFT_BLOCK: 1449 html_close_paragraph(h); 1450 return 1; 1451 case ROFFT_HEAD: 1452 return 0; 1453 case ROFFT_BODY: 1454 break; 1455 default: 1456 abort(); 1457 } 1458 1459 if (FONT_Em == n->norm->Bf.font) 1460 cattr = "Bf Em"; 1461 else if (FONT_Sy == n->norm->Bf.font) 1462 cattr = "Bf Sy"; 1463 else if (FONT_Li == n->norm->Bf.font) 1464 cattr = "Bf Li"; 1465 else 1466 cattr = "Bf No"; 1467 1468 /* Cannot use TAG_SPAN because it may contain blocks. */ 1469 print_otag(h, TAG_DIV, "c", cattr); 1470 return 1; 1471 } 1472 1473 static int 1474 mdoc_ms_pre(MDOC_ARGS) 1475 { 1476 char *id; 1477 1478 if ((id = cond_id(n)) != NULL) 1479 print_otag(h, TAG_A, "chR", "permalink", id); 1480 print_otag(h, TAG_SPAN, "ci", "Ms", id); 1481 return 1; 1482 } 1483 1484 static int 1485 mdoc_igndelim_pre(MDOC_ARGS) 1486 { 1487 1488 h->flags |= HTML_IGNDELIM; 1489 return 1; 1490 } 1491 1492 static void 1493 mdoc_pf_post(MDOC_ARGS) 1494 { 1495 1496 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1497 h->flags |= HTML_NOSPACE; 1498 } 1499 1500 static int 1501 mdoc_rs_pre(MDOC_ARGS) 1502 { 1503 switch (n->type) { 1504 case ROFFT_BLOCK: 1505 if (n->sec == SEC_SEE_ALSO) 1506 html_close_paragraph(h); 1507 break; 1508 case ROFFT_HEAD: 1509 return 0; 1510 case ROFFT_BODY: 1511 if (n->sec == SEC_SEE_ALSO) 1512 print_otag(h, TAG_P, "c", "Pp"); 1513 print_otag(h, TAG_CITE, "c", "Rs"); 1514 break; 1515 default: 1516 abort(); 1517 } 1518 return 1; 1519 } 1520 1521 static int 1522 mdoc_no_pre(MDOC_ARGS) 1523 { 1524 char *id; 1525 1526 if ((id = cond_id(n)) != NULL) 1527 print_otag(h, TAG_A, "chR", "permalink", id); 1528 print_otag(h, TAG_SPAN, "ci", "No", id); 1529 return 1; 1530 } 1531 1532 static int 1533 mdoc_li_pre(MDOC_ARGS) 1534 { 1535 char *id; 1536 1537 if ((id = cond_id(n)) != NULL) 1538 print_otag(h, TAG_A, "chR", "permalink", id); 1539 print_otag(h, TAG_CODE, "ci", "Li", id); 1540 return 1; 1541 } 1542 1543 static int 1544 mdoc_sy_pre(MDOC_ARGS) 1545 { 1546 print_otag(h, TAG_B, "c", "Sy"); 1547 return 1; 1548 } 1549 1550 static int 1551 mdoc_lb_pre(MDOC_ARGS) 1552 { 1553 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) 1554 print_otag(h, TAG_BR, ""); 1555 1556 print_otag(h, TAG_SPAN, "c", "Lb"); 1557 return 1; 1558 } 1559 1560 static int 1561 mdoc__x_pre(MDOC_ARGS) 1562 { 1563 const char *cattr; 1564 enum htmltag t; 1565 1566 t = TAG_SPAN; 1567 1568 switch (n->tok) { 1569 case MDOC__A: 1570 cattr = "RsA"; 1571 if (n->prev && MDOC__A == n->prev->tok) 1572 if (NULL == n->next || MDOC__A != n->next->tok) 1573 print_text(h, "and"); 1574 break; 1575 case MDOC__B: 1576 t = TAG_I; 1577 cattr = "RsB"; 1578 break; 1579 case MDOC__C: 1580 cattr = "RsC"; 1581 break; 1582 case MDOC__D: 1583 cattr = "RsD"; 1584 break; 1585 case MDOC__I: 1586 t = TAG_I; 1587 cattr = "RsI"; 1588 break; 1589 case MDOC__J: 1590 t = TAG_I; 1591 cattr = "RsJ"; 1592 break; 1593 case MDOC__N: 1594 cattr = "RsN"; 1595 break; 1596 case MDOC__O: 1597 cattr = "RsO"; 1598 break; 1599 case MDOC__P: 1600 cattr = "RsP"; 1601 break; 1602 case MDOC__Q: 1603 cattr = "RsQ"; 1604 break; 1605 case MDOC__R: 1606 cattr = "RsR"; 1607 break; 1608 case MDOC__T: 1609 cattr = "RsT"; 1610 break; 1611 case MDOC__U: 1612 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1613 return 1; 1614 case MDOC__V: 1615 cattr = "RsV"; 1616 break; 1617 default: 1618 abort(); 1619 } 1620 1621 print_otag(h, t, "c", cattr); 1622 return 1; 1623 } 1624 1625 static void 1626 mdoc__x_post(MDOC_ARGS) 1627 { 1628 1629 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1630 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1631 if (NULL == n->prev || MDOC__A != n->prev->tok) 1632 return; 1633 1634 /* TODO: %U */ 1635 1636 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1637 return; 1638 1639 h->flags |= HTML_NOSPACE; 1640 print_text(h, n->next ? "," : "."); 1641 } 1642 1643 static int 1644 mdoc_bk_pre(MDOC_ARGS) 1645 { 1646 1647 switch (n->type) { 1648 case ROFFT_BLOCK: 1649 break; 1650 case ROFFT_HEAD: 1651 return 0; 1652 case ROFFT_BODY: 1653 if (n->parent->args != NULL || n->prev->child == NULL) 1654 h->flags |= HTML_PREKEEP; 1655 break; 1656 default: 1657 abort(); 1658 } 1659 1660 return 1; 1661 } 1662 1663 static void 1664 mdoc_bk_post(MDOC_ARGS) 1665 { 1666 1667 if (n->type == ROFFT_BODY) 1668 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1669 } 1670 1671 static int 1672 mdoc_quote_pre(MDOC_ARGS) 1673 { 1674 if (n->type != ROFFT_BODY) 1675 return 1; 1676 1677 switch (n->tok) { 1678 case MDOC_Ao: 1679 case MDOC_Aq: 1680 print_text(h, n->child != NULL && n->child->next == NULL && 1681 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1682 break; 1683 case MDOC_Bro: 1684 case MDOC_Brq: 1685 print_text(h, "\\(lC"); 1686 break; 1687 case MDOC_Bo: 1688 case MDOC_Bq: 1689 print_text(h, "\\(lB"); 1690 break; 1691 case MDOC_Oo: 1692 case MDOC_Op: 1693 print_text(h, "\\(lB"); 1694 /* 1695 * Give up on semantic markup for now. 1696 * We cannot use TAG_SPAN because .Oo may contain blocks. 1697 * We cannot use TAG_IDIV because we might be in a 1698 * phrasing context (like .Dl or .Pp); we cannot 1699 * close out a .Pp at this point either because 1700 * that would break the line. 1701 */ 1702 /* XXX print_otag(h, TAG_???, "c", "Op"); */ 1703 break; 1704 case MDOC_En: 1705 if (NULL == n->norm->Es || 1706 NULL == n->norm->Es->child) 1707 return 1; 1708 print_text(h, n->norm->Es->child->string); 1709 break; 1710 case MDOC_Do: 1711 case MDOC_Dq: 1712 case MDOC_Qo: 1713 case MDOC_Qq: 1714 print_text(h, "\\(lq"); 1715 break; 1716 case MDOC_Po: 1717 case MDOC_Pq: 1718 print_text(h, "("); 1719 break; 1720 case MDOC_Ql: 1721 print_text(h, "\\(oq"); 1722 h->flags |= HTML_NOSPACE; 1723 print_otag(h, TAG_CODE, "c", "Li"); 1724 break; 1725 case MDOC_So: 1726 case MDOC_Sq: 1727 print_text(h, "\\(oq"); 1728 break; 1729 default: 1730 abort(); 1731 } 1732 1733 h->flags |= HTML_NOSPACE; 1734 return 1; 1735 } 1736 1737 static void 1738 mdoc_quote_post(MDOC_ARGS) 1739 { 1740 1741 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1742 return; 1743 1744 h->flags |= HTML_NOSPACE; 1745 1746 switch (n->tok) { 1747 case MDOC_Ao: 1748 case MDOC_Aq: 1749 print_text(h, n->child != NULL && n->child->next == NULL && 1750 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1751 break; 1752 case MDOC_Bro: 1753 case MDOC_Brq: 1754 print_text(h, "\\(rC"); 1755 break; 1756 case MDOC_Oo: 1757 case MDOC_Op: 1758 case MDOC_Bo: 1759 case MDOC_Bq: 1760 print_text(h, "\\(rB"); 1761 break; 1762 case MDOC_En: 1763 if (n->norm->Es == NULL || 1764 n->norm->Es->child == NULL || 1765 n->norm->Es->child->next == NULL) 1766 h->flags &= ~HTML_NOSPACE; 1767 else 1768 print_text(h, n->norm->Es->child->next->string); 1769 break; 1770 case MDOC_Qo: 1771 case MDOC_Qq: 1772 case MDOC_Do: 1773 case MDOC_Dq: 1774 print_text(h, "\\(rq"); 1775 break; 1776 case MDOC_Po: 1777 case MDOC_Pq: 1778 print_text(h, ")"); 1779 break; 1780 case MDOC_Ql: 1781 case MDOC_So: 1782 case MDOC_Sq: 1783 print_text(h, "\\(cq"); 1784 break; 1785 default: 1786 abort(); 1787 } 1788 } 1789 1790 static int 1791 mdoc_eo_pre(MDOC_ARGS) 1792 { 1793 1794 if (n->type != ROFFT_BODY) 1795 return 1; 1796 1797 if (n->end == ENDBODY_NOT && 1798 n->parent->head->child == NULL && 1799 n->child != NULL && 1800 n->child->end != ENDBODY_NOT) 1801 print_text(h, "\\&"); 1802 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1803 n->parent->head->child != NULL && (n->child != NULL || 1804 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1805 h->flags |= HTML_NOSPACE; 1806 return 1; 1807 } 1808 1809 static void 1810 mdoc_eo_post(MDOC_ARGS) 1811 { 1812 int body, tail; 1813 1814 if (n->type != ROFFT_BODY) 1815 return; 1816 1817 if (n->end != ENDBODY_NOT) { 1818 h->flags &= ~HTML_NOSPACE; 1819 return; 1820 } 1821 1822 body = n->child != NULL || n->parent->head->child != NULL; 1823 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1824 1825 if (body && tail) 1826 h->flags |= HTML_NOSPACE; 1827 else if ( ! tail) 1828 h->flags &= ~HTML_NOSPACE; 1829 } 1830 1831 static int 1832 mdoc_abort_pre(MDOC_ARGS) 1833 { 1834 abort(); 1835 } 1836