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