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