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