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