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