1 /* $Id: mdoc_html.c,v 1.310 2018/07/27 17:49:31 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 "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 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, ""); 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, ""); 710 print_text(h, "\\ "); 711 print_tagq(h, t); 712 t = print_otag(h, TAG_DD, ""); 713 print_text(h, "\\ "); 714 print_tagq(h, t); 715 } 716 print_otag(h, TAG_DT, ""); 717 break; 718 case ROFFT_BODY: 719 if (n->child == NULL) { 720 print_otag(h, TAG_DD, "s", "width", "auto"); 721 print_text(h, "\\ "); 722 } else 723 print_otag(h, TAG_DD, ""); 724 break; 725 default: 726 break; 727 } 728 break; 729 case LIST_column: 730 switch (n->type) { 731 case ROFFT_HEAD: 732 break; 733 case ROFFT_BODY: 734 print_otag(h, TAG_TD, ""); 735 break; 736 default: 737 print_otag(h, TAG_TR, ""); 738 } 739 default: 740 break; 741 } 742 743 return 1; 744 } 745 746 static int 747 mdoc_bl_pre(MDOC_ARGS) 748 { 749 char cattr[28]; 750 struct mdoc_bl *bl; 751 enum htmltag elemtype; 752 753 switch (n->type) { 754 case ROFFT_BODY: 755 return 1; 756 case ROFFT_HEAD: 757 return 0; 758 default: 759 break; 760 } 761 762 bl = &n->norm->Bl; 763 switch (bl->type) { 764 case LIST_bullet: 765 elemtype = TAG_UL; 766 (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); 767 break; 768 case LIST_dash: 769 case LIST_hyphen: 770 elemtype = TAG_UL; 771 (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); 772 break; 773 case LIST_item: 774 elemtype = TAG_UL; 775 (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); 776 break; 777 case LIST_enum: 778 elemtype = TAG_OL; 779 (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); 780 break; 781 case LIST_diag: 782 elemtype = TAG_DL; 783 (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); 784 break; 785 case LIST_hang: 786 elemtype = TAG_DL; 787 (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); 788 break; 789 case LIST_inset: 790 elemtype = TAG_DL; 791 (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); 792 break; 793 case LIST_ohang: 794 elemtype = TAG_DL; 795 (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); 796 break; 797 case LIST_tag: 798 if (bl->offs) 799 print_otag(h, TAG_DIV, "c", "Bd-indent"); 800 print_otag(h, TAG_DL, "c", bl->comp ? 801 "Bl-tag Bl-compact" : "Bl-tag"); 802 return 1; 803 case LIST_column: 804 elemtype = TAG_TABLE; 805 (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); 806 break; 807 default: 808 abort(); 809 } 810 if (bl->offs != NULL) 811 (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); 812 if (bl->comp) 813 (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); 814 print_otag(h, elemtype, "c", cattr); 815 return 1; 816 } 817 818 static int 819 mdoc_ex_pre(MDOC_ARGS) 820 { 821 if (n->prev) 822 print_otag(h, TAG_BR, ""); 823 return 1; 824 } 825 826 static int 827 mdoc_st_pre(MDOC_ARGS) 828 { 829 print_otag(h, TAG_SPAN, "cT", "St"); 830 return 1; 831 } 832 833 static int 834 mdoc_em_pre(MDOC_ARGS) 835 { 836 print_otag(h, TAG_I, "cT", "Em"); 837 return 1; 838 } 839 840 static int 841 mdoc_d1_pre(MDOC_ARGS) 842 { 843 if (n->type != ROFFT_BLOCK) 844 return 1; 845 846 print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); 847 848 if (n->tok == MDOC_Dl) 849 print_otag(h, TAG_CODE, "c", "Li"); 850 851 return 1; 852 } 853 854 static int 855 mdoc_sx_pre(MDOC_ARGS) 856 { 857 char *id; 858 859 id = html_make_id(n, 0); 860 print_otag(h, TAG_A, "cThR", "Sx", id); 861 free(id); 862 return 1; 863 } 864 865 static int 866 mdoc_bd_pre(MDOC_ARGS) 867 { 868 int comp, sv; 869 struct roff_node *nn; 870 871 if (n->type == ROFFT_HEAD) 872 return 0; 873 874 if (n->type == ROFFT_BLOCK) { 875 comp = n->norm->Bd.comp; 876 for (nn = n; nn && ! comp; nn = nn->parent) { 877 if (nn->type != ROFFT_BLOCK) 878 continue; 879 if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) 880 comp = 1; 881 if (nn->prev) 882 break; 883 } 884 if ( ! comp) 885 print_paragraph(h); 886 return 1; 887 } 888 889 /* Handle the -offset argument. */ 890 891 if (n->norm->Bd.offs == NULL || 892 ! strcmp(n->norm->Bd.offs, "left")) 893 print_otag(h, TAG_DIV, "c", "Bd"); 894 else 895 print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); 896 897 if (n->norm->Bd.type != DISP_unfilled && 898 n->norm->Bd.type != DISP_literal) 899 return 1; 900 901 print_otag(h, TAG_PRE, "c", "Li"); 902 903 /* This can be recursive: save & set our literal state. */ 904 905 sv = h->flags & HTML_LITERAL; 906 h->flags |= HTML_LITERAL; 907 908 for (nn = n->child; nn; nn = nn->next) { 909 print_mdoc_node(meta, nn, h); 910 /* 911 * If the printed node flushes its own line, then we 912 * needn't do it here as well. This is hacky, but the 913 * notion of selective eoln whitespace is pretty dumb 914 * anyway, so don't sweat it. 915 */ 916 switch (nn->tok) { 917 case ROFF_br: 918 case ROFF_sp: 919 case MDOC_Sm: 920 case MDOC_Bl: 921 case MDOC_D1: 922 case MDOC_Dl: 923 case MDOC_Lp: 924 case MDOC_Pp: 925 continue; 926 default: 927 break; 928 } 929 if (h->flags & HTML_NONEWLINE || 930 (nn->next && ! (nn->next->flags & NODE_LINE))) 931 continue; 932 else if (nn->next) 933 print_text(h, "\n"); 934 935 h->flags |= HTML_NOSPACE; 936 } 937 938 if (0 == sv) 939 h->flags &= ~HTML_LITERAL; 940 941 return 0; 942 } 943 944 static int 945 mdoc_pa_pre(MDOC_ARGS) 946 { 947 print_otag(h, TAG_SPAN, "cT", "Pa"); 948 return 1; 949 } 950 951 static int 952 mdoc_ad_pre(MDOC_ARGS) 953 { 954 print_otag(h, TAG_SPAN, "c", "Ad"); 955 return 1; 956 } 957 958 static int 959 mdoc_an_pre(MDOC_ARGS) 960 { 961 if (n->norm->An.auth == AUTH_split) { 962 h->flags &= ~HTML_NOSPLIT; 963 h->flags |= HTML_SPLIT; 964 return 0; 965 } 966 if (n->norm->An.auth == AUTH_nosplit) { 967 h->flags &= ~HTML_SPLIT; 968 h->flags |= HTML_NOSPLIT; 969 return 0; 970 } 971 972 if (h->flags & HTML_SPLIT) 973 print_otag(h, TAG_BR, ""); 974 975 if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) 976 h->flags |= HTML_SPLIT; 977 978 print_otag(h, TAG_SPAN, "cT", "An"); 979 return 1; 980 } 981 982 static int 983 mdoc_cd_pre(MDOC_ARGS) 984 { 985 synopsis_pre(h, n); 986 print_otag(h, TAG_CODE, "cT", "Cd"); 987 return 1; 988 } 989 990 static int 991 mdoc_dv_pre(MDOC_ARGS) 992 { 993 char *id; 994 995 if ((id = cond_id(n)) != NULL) 996 print_otag(h, TAG_A, "chR", "permalink", id); 997 print_otag(h, TAG_CODE, "cTi", "Dv", id); 998 return 1; 999 } 1000 1001 static int 1002 mdoc_ev_pre(MDOC_ARGS) 1003 { 1004 char *id; 1005 1006 if ((id = cond_id(n)) != NULL) 1007 print_otag(h, TAG_A, "chR", "permalink", id); 1008 print_otag(h, TAG_CODE, "cTi", "Ev", id); 1009 return 1; 1010 } 1011 1012 static int 1013 mdoc_er_pre(MDOC_ARGS) 1014 { 1015 char *id; 1016 1017 id = n->sec == SEC_ERRORS && 1018 (n->parent->tok == MDOC_It || 1019 (n->parent->tok == MDOC_Bq && 1020 n->parent->parent->parent->tok == MDOC_It)) ? 1021 html_make_id(n, 1) : NULL; 1022 1023 if (id != NULL) 1024 print_otag(h, TAG_A, "chR", "permalink", id); 1025 print_otag(h, TAG_CODE, "cTi", "Er", id); 1026 return 1; 1027 } 1028 1029 static int 1030 mdoc_fa_pre(MDOC_ARGS) 1031 { 1032 const struct roff_node *nn; 1033 struct tag *t; 1034 1035 if (n->parent->tok != MDOC_Fo) { 1036 print_otag(h, TAG_VAR, "cT", "Fa"); 1037 return 1; 1038 } 1039 1040 for (nn = n->child; nn; nn = nn->next) { 1041 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1042 print_text(h, nn->string); 1043 print_tagq(h, t); 1044 if (nn->next) { 1045 h->flags |= HTML_NOSPACE; 1046 print_text(h, ","); 1047 } 1048 } 1049 1050 if (n->child && n->next && n->next->tok == MDOC_Fa) { 1051 h->flags |= HTML_NOSPACE; 1052 print_text(h, ","); 1053 } 1054 1055 return 0; 1056 } 1057 1058 static int 1059 mdoc_fd_pre(MDOC_ARGS) 1060 { 1061 struct tag *t; 1062 char *buf, *cp; 1063 1064 synopsis_pre(h, n); 1065 1066 if (NULL == (n = n->child)) 1067 return 0; 1068 1069 assert(n->type == ROFFT_TEXT); 1070 1071 if (strcmp(n->string, "#include")) { 1072 print_otag(h, TAG_CODE, "cT", "Fd"); 1073 return 1; 1074 } 1075 1076 print_otag(h, TAG_CODE, "cT", "In"); 1077 print_text(h, n->string); 1078 1079 if (NULL != (n = n->next)) { 1080 assert(n->type == ROFFT_TEXT); 1081 1082 if (h->base_includes) { 1083 cp = n->string; 1084 if (*cp == '<' || *cp == '"') 1085 cp++; 1086 buf = mandoc_strdup(cp); 1087 cp = strchr(buf, '\0') - 1; 1088 if (cp >= buf && (*cp == '>' || *cp == '"')) 1089 *cp = '\0'; 1090 t = print_otag(h, TAG_A, "cThI", "In", buf); 1091 free(buf); 1092 } else 1093 t = print_otag(h, TAG_A, "cT", "In"); 1094 1095 print_text(h, n->string); 1096 print_tagq(h, t); 1097 1098 n = n->next; 1099 } 1100 1101 for ( ; n; n = n->next) { 1102 assert(n->type == ROFFT_TEXT); 1103 print_text(h, n->string); 1104 } 1105 1106 return 0; 1107 } 1108 1109 static int 1110 mdoc_vt_pre(MDOC_ARGS) 1111 { 1112 if (n->type == ROFFT_BLOCK) { 1113 synopsis_pre(h, n); 1114 return 1; 1115 } else if (n->type == ROFFT_ELEM) { 1116 synopsis_pre(h, n); 1117 } else if (n->type == ROFFT_HEAD) 1118 return 0; 1119 1120 print_otag(h, TAG_VAR, "cT", "Vt"); 1121 return 1; 1122 } 1123 1124 static int 1125 mdoc_ft_pre(MDOC_ARGS) 1126 { 1127 synopsis_pre(h, n); 1128 print_otag(h, TAG_VAR, "cT", "Ft"); 1129 return 1; 1130 } 1131 1132 static int 1133 mdoc_fn_pre(MDOC_ARGS) 1134 { 1135 struct tag *t; 1136 char nbuf[BUFSIZ]; 1137 const char *sp, *ep; 1138 int sz, pretty; 1139 1140 pretty = NODE_SYNPRETTY & n->flags; 1141 synopsis_pre(h, n); 1142 1143 /* Split apart into type and name. */ 1144 assert(n->child->string); 1145 sp = n->child->string; 1146 1147 ep = strchr(sp, ' '); 1148 if (NULL != ep) { 1149 t = print_otag(h, TAG_VAR, "cT", "Ft"); 1150 1151 while (ep) { 1152 sz = MIN((int)(ep - sp), BUFSIZ - 1); 1153 (void)memcpy(nbuf, sp, (size_t)sz); 1154 nbuf[sz] = '\0'; 1155 print_text(h, nbuf); 1156 sp = ++ep; 1157 ep = strchr(sp, ' '); 1158 } 1159 print_tagq(h, t); 1160 } 1161 1162 t = print_otag(h, TAG_CODE, "cT", "Fn"); 1163 1164 if (sp) 1165 print_text(h, sp); 1166 1167 print_tagq(h, t); 1168 1169 h->flags |= HTML_NOSPACE; 1170 print_text(h, "("); 1171 h->flags |= HTML_NOSPACE; 1172 1173 for (n = n->child->next; n; n = n->next) { 1174 if (NODE_SYNPRETTY & n->flags) 1175 t = print_otag(h, TAG_VAR, "cTs", "Fa", 1176 "white-space", "nowrap"); 1177 else 1178 t = print_otag(h, TAG_VAR, "cT", "Fa"); 1179 print_text(h, n->string); 1180 print_tagq(h, t); 1181 if (n->next) { 1182 h->flags |= HTML_NOSPACE; 1183 print_text(h, ","); 1184 } 1185 } 1186 1187 h->flags |= HTML_NOSPACE; 1188 print_text(h, ")"); 1189 1190 if (pretty) { 1191 h->flags |= HTML_NOSPACE; 1192 print_text(h, ";"); 1193 } 1194 1195 return 0; 1196 } 1197 1198 static int 1199 mdoc_sm_pre(MDOC_ARGS) 1200 { 1201 1202 if (NULL == n->child) 1203 h->flags ^= HTML_NONOSPACE; 1204 else if (0 == strcmp("on", n->child->string)) 1205 h->flags &= ~HTML_NONOSPACE; 1206 else 1207 h->flags |= HTML_NONOSPACE; 1208 1209 if ( ! (HTML_NONOSPACE & h->flags)) 1210 h->flags &= ~HTML_NOSPACE; 1211 1212 return 0; 1213 } 1214 1215 static int 1216 mdoc_skip_pre(MDOC_ARGS) 1217 { 1218 1219 return 0; 1220 } 1221 1222 static int 1223 mdoc_pp_pre(MDOC_ARGS) 1224 { 1225 1226 print_paragraph(h); 1227 return 0; 1228 } 1229 1230 static int 1231 mdoc_lk_pre(MDOC_ARGS) 1232 { 1233 const struct roff_node *link, *descr, *punct; 1234 struct tag *t; 1235 1236 if ((link = n->child) == NULL) 1237 return 0; 1238 1239 /* Find beginning of trailing punctuation. */ 1240 punct = n->last; 1241 while (punct != link && punct->flags & NODE_DELIMC) 1242 punct = punct->prev; 1243 punct = punct->next; 1244 1245 /* Link target and link text. */ 1246 descr = link->next; 1247 if (descr == punct) 1248 descr = link; /* no text */ 1249 t = print_otag(h, TAG_A, "cTh", "Lk", link->string); 1250 do { 1251 if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) 1252 h->flags |= HTML_NOSPACE; 1253 print_text(h, descr->string); 1254 descr = descr->next; 1255 } while (descr != punct); 1256 print_tagq(h, t); 1257 1258 /* Trailing punctuation. */ 1259 while (punct != NULL) { 1260 h->flags |= HTML_NOSPACE; 1261 print_text(h, punct->string); 1262 punct = punct->next; 1263 } 1264 return 0; 1265 } 1266 1267 static int 1268 mdoc_mt_pre(MDOC_ARGS) 1269 { 1270 struct tag *t; 1271 char *cp; 1272 1273 for (n = n->child; n; n = n->next) { 1274 assert(n->type == ROFFT_TEXT); 1275 1276 mandoc_asprintf(&cp, "mailto:%s", n->string); 1277 t = print_otag(h, TAG_A, "cTh", "Mt", cp); 1278 print_text(h, n->string); 1279 print_tagq(h, t); 1280 free(cp); 1281 } 1282 1283 return 0; 1284 } 1285 1286 static int 1287 mdoc_fo_pre(MDOC_ARGS) 1288 { 1289 struct tag *t; 1290 1291 if (n->type == ROFFT_BODY) { 1292 h->flags |= HTML_NOSPACE; 1293 print_text(h, "("); 1294 h->flags |= HTML_NOSPACE; 1295 return 1; 1296 } else if (n->type == ROFFT_BLOCK) { 1297 synopsis_pre(h, n); 1298 return 1; 1299 } 1300 1301 if (n->child == NULL) 1302 return 0; 1303 1304 assert(n->child->string); 1305 t = print_otag(h, TAG_CODE, "cT", "Fn"); 1306 print_text(h, n->child->string); 1307 print_tagq(h, t); 1308 return 0; 1309 } 1310 1311 static void 1312 mdoc_fo_post(MDOC_ARGS) 1313 { 1314 1315 if (n->type != ROFFT_BODY) 1316 return; 1317 h->flags |= HTML_NOSPACE; 1318 print_text(h, ")"); 1319 h->flags |= HTML_NOSPACE; 1320 print_text(h, ";"); 1321 } 1322 1323 static int 1324 mdoc_in_pre(MDOC_ARGS) 1325 { 1326 struct tag *t; 1327 1328 synopsis_pre(h, n); 1329 print_otag(h, TAG_CODE, "cT", "In"); 1330 1331 /* 1332 * The first argument of the `In' gets special treatment as 1333 * being a linked value. Subsequent values are printed 1334 * afterward. groff does similarly. This also handles the case 1335 * of no children. 1336 */ 1337 1338 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) 1339 print_text(h, "#include"); 1340 1341 print_text(h, "<"); 1342 h->flags |= HTML_NOSPACE; 1343 1344 if (NULL != (n = n->child)) { 1345 assert(n->type == ROFFT_TEXT); 1346 1347 if (h->base_includes) 1348 t = print_otag(h, TAG_A, "cThI", "In", n->string); 1349 else 1350 t = print_otag(h, TAG_A, "cT", "In"); 1351 print_text(h, n->string); 1352 print_tagq(h, t); 1353 1354 n = n->next; 1355 } 1356 1357 h->flags |= HTML_NOSPACE; 1358 print_text(h, ">"); 1359 1360 for ( ; n; n = n->next) { 1361 assert(n->type == ROFFT_TEXT); 1362 print_text(h, n->string); 1363 } 1364 1365 return 0; 1366 } 1367 1368 static int 1369 mdoc_ic_pre(MDOC_ARGS) 1370 { 1371 char *id; 1372 1373 if ((id = cond_id(n)) != NULL) 1374 print_otag(h, TAG_A, "chR", "permalink", id); 1375 print_otag(h, TAG_CODE, "cTi", "Ic", id); 1376 return 1; 1377 } 1378 1379 static int 1380 mdoc_va_pre(MDOC_ARGS) 1381 { 1382 print_otag(h, TAG_VAR, "cT", "Va"); 1383 return 1; 1384 } 1385 1386 static int 1387 mdoc_ap_pre(MDOC_ARGS) 1388 { 1389 1390 h->flags |= HTML_NOSPACE; 1391 print_text(h, "\\(aq"); 1392 h->flags |= HTML_NOSPACE; 1393 return 1; 1394 } 1395 1396 static int 1397 mdoc_bf_pre(MDOC_ARGS) 1398 { 1399 const char *cattr; 1400 1401 if (n->type == ROFFT_HEAD) 1402 return 0; 1403 else if (n->type != ROFFT_BODY) 1404 return 1; 1405 1406 if (FONT_Em == n->norm->Bf.font) 1407 cattr = "Bf Em"; 1408 else if (FONT_Sy == n->norm->Bf.font) 1409 cattr = "Bf Sy"; 1410 else if (FONT_Li == n->norm->Bf.font) 1411 cattr = "Bf Li"; 1412 else 1413 cattr = "Bf No"; 1414 1415 /* Cannot use TAG_SPAN because it may contain blocks. */ 1416 print_otag(h, TAG_DIV, "c", cattr); 1417 return 1; 1418 } 1419 1420 static int 1421 mdoc_ms_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_SPAN, "cTi", "Ms", id); 1428 return 1; 1429 } 1430 1431 static int 1432 mdoc_igndelim_pre(MDOC_ARGS) 1433 { 1434 1435 h->flags |= HTML_IGNDELIM; 1436 return 1; 1437 } 1438 1439 static void 1440 mdoc_pf_post(MDOC_ARGS) 1441 { 1442 1443 if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) 1444 h->flags |= HTML_NOSPACE; 1445 } 1446 1447 static int 1448 mdoc_rs_pre(MDOC_ARGS) 1449 { 1450 if (n->type != ROFFT_BLOCK) 1451 return 1; 1452 1453 if (n->prev && SEC_SEE_ALSO == n->sec) 1454 print_paragraph(h); 1455 1456 print_otag(h, TAG_CITE, "cT", "Rs"); 1457 return 1; 1458 } 1459 1460 static int 1461 mdoc_no_pre(MDOC_ARGS) 1462 { 1463 char *id; 1464 1465 if ((id = cond_id(n)) != NULL) 1466 print_otag(h, TAG_A, "chR", "permalink", id); 1467 print_otag(h, TAG_SPAN, "ci", "No", id); 1468 return 1; 1469 } 1470 1471 static int 1472 mdoc_li_pre(MDOC_ARGS) 1473 { 1474 char *id; 1475 1476 if ((id = cond_id(n)) != NULL) 1477 print_otag(h, TAG_A, "chR", "permalink", id); 1478 print_otag(h, TAG_CODE, "ci", "Li", id); 1479 return 1; 1480 } 1481 1482 static int 1483 mdoc_sy_pre(MDOC_ARGS) 1484 { 1485 print_otag(h, TAG_B, "cT", "Sy"); 1486 return 1; 1487 } 1488 1489 static int 1490 mdoc_lb_pre(MDOC_ARGS) 1491 { 1492 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) 1493 print_otag(h, TAG_BR, ""); 1494 1495 print_otag(h, TAG_SPAN, "cT", "Lb"); 1496 return 1; 1497 } 1498 1499 static int 1500 mdoc__x_pre(MDOC_ARGS) 1501 { 1502 const char *cattr; 1503 enum htmltag t; 1504 1505 t = TAG_SPAN; 1506 1507 switch (n->tok) { 1508 case MDOC__A: 1509 cattr = "RsA"; 1510 if (n->prev && MDOC__A == n->prev->tok) 1511 if (NULL == n->next || MDOC__A != n->next->tok) 1512 print_text(h, "and"); 1513 break; 1514 case MDOC__B: 1515 t = TAG_I; 1516 cattr = "RsB"; 1517 break; 1518 case MDOC__C: 1519 cattr = "RsC"; 1520 break; 1521 case MDOC__D: 1522 cattr = "RsD"; 1523 break; 1524 case MDOC__I: 1525 t = TAG_I; 1526 cattr = "RsI"; 1527 break; 1528 case MDOC__J: 1529 t = TAG_I; 1530 cattr = "RsJ"; 1531 break; 1532 case MDOC__N: 1533 cattr = "RsN"; 1534 break; 1535 case MDOC__O: 1536 cattr = "RsO"; 1537 break; 1538 case MDOC__P: 1539 cattr = "RsP"; 1540 break; 1541 case MDOC__Q: 1542 cattr = "RsQ"; 1543 break; 1544 case MDOC__R: 1545 cattr = "RsR"; 1546 break; 1547 case MDOC__T: 1548 cattr = "RsT"; 1549 break; 1550 case MDOC__U: 1551 print_otag(h, TAG_A, "ch", "RsU", n->child->string); 1552 return 1; 1553 case MDOC__V: 1554 cattr = "RsV"; 1555 break; 1556 default: 1557 abort(); 1558 } 1559 1560 print_otag(h, t, "c", cattr); 1561 return 1; 1562 } 1563 1564 static void 1565 mdoc__x_post(MDOC_ARGS) 1566 { 1567 1568 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) 1569 if (NULL == n->next->next || MDOC__A != n->next->next->tok) 1570 if (NULL == n->prev || MDOC__A != n->prev->tok) 1571 return; 1572 1573 /* TODO: %U */ 1574 1575 if (NULL == n->parent || MDOC_Rs != n->parent->tok) 1576 return; 1577 1578 h->flags |= HTML_NOSPACE; 1579 print_text(h, n->next ? "," : "."); 1580 } 1581 1582 static int 1583 mdoc_bk_pre(MDOC_ARGS) 1584 { 1585 1586 switch (n->type) { 1587 case ROFFT_BLOCK: 1588 break; 1589 case ROFFT_HEAD: 1590 return 0; 1591 case ROFFT_BODY: 1592 if (n->parent->args != NULL || n->prev->child == NULL) 1593 h->flags |= HTML_PREKEEP; 1594 break; 1595 default: 1596 abort(); 1597 } 1598 1599 return 1; 1600 } 1601 1602 static void 1603 mdoc_bk_post(MDOC_ARGS) 1604 { 1605 1606 if (n->type == ROFFT_BODY) 1607 h->flags &= ~(HTML_KEEP | HTML_PREKEEP); 1608 } 1609 1610 static int 1611 mdoc_quote_pre(MDOC_ARGS) 1612 { 1613 if (n->type != ROFFT_BODY) 1614 return 1; 1615 1616 switch (n->tok) { 1617 case MDOC_Ao: 1618 case MDOC_Aq: 1619 print_text(h, n->child != NULL && n->child->next == NULL && 1620 n->child->tok == MDOC_Mt ? "<" : "\\(la"); 1621 break; 1622 case MDOC_Bro: 1623 case MDOC_Brq: 1624 print_text(h, "\\(lC"); 1625 break; 1626 case MDOC_Bo: 1627 case MDOC_Bq: 1628 print_text(h, "\\(lB"); 1629 break; 1630 case MDOC_Oo: 1631 case MDOC_Op: 1632 print_text(h, "\\(lB"); 1633 h->flags |= HTML_NOSPACE; 1634 /* Cannot use TAG_SPAN because it may contain blocks. */ 1635 print_otag(h, TAG_IDIV, "c", "Op"); 1636 break; 1637 case MDOC_En: 1638 if (NULL == n->norm->Es || 1639 NULL == n->norm->Es->child) 1640 return 1; 1641 print_text(h, n->norm->Es->child->string); 1642 break; 1643 case MDOC_Do: 1644 case MDOC_Dq: 1645 case MDOC_Qo: 1646 case MDOC_Qq: 1647 print_text(h, "\\(lq"); 1648 break; 1649 case MDOC_Po: 1650 case MDOC_Pq: 1651 print_text(h, "("); 1652 break; 1653 case MDOC_Ql: 1654 print_text(h, "\\(oq"); 1655 h->flags |= HTML_NOSPACE; 1656 print_otag(h, TAG_CODE, "c", "Li"); 1657 break; 1658 case MDOC_So: 1659 case MDOC_Sq: 1660 print_text(h, "\\(oq"); 1661 break; 1662 default: 1663 abort(); 1664 } 1665 1666 h->flags |= HTML_NOSPACE; 1667 return 1; 1668 } 1669 1670 static void 1671 mdoc_quote_post(MDOC_ARGS) 1672 { 1673 1674 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) 1675 return; 1676 1677 h->flags |= HTML_NOSPACE; 1678 1679 switch (n->tok) { 1680 case MDOC_Ao: 1681 case MDOC_Aq: 1682 print_text(h, n->child != NULL && n->child->next == NULL && 1683 n->child->tok == MDOC_Mt ? ">" : "\\(ra"); 1684 break; 1685 case MDOC_Bro: 1686 case MDOC_Brq: 1687 print_text(h, "\\(rC"); 1688 break; 1689 case MDOC_Oo: 1690 case MDOC_Op: 1691 case MDOC_Bo: 1692 case MDOC_Bq: 1693 print_text(h, "\\(rB"); 1694 break; 1695 case MDOC_En: 1696 if (n->norm->Es == NULL || 1697 n->norm->Es->child == NULL || 1698 n->norm->Es->child->next == NULL) 1699 h->flags &= ~HTML_NOSPACE; 1700 else 1701 print_text(h, n->norm->Es->child->next->string); 1702 break; 1703 case MDOC_Qo: 1704 case MDOC_Qq: 1705 case MDOC_Do: 1706 case MDOC_Dq: 1707 print_text(h, "\\(rq"); 1708 break; 1709 case MDOC_Po: 1710 case MDOC_Pq: 1711 print_text(h, ")"); 1712 break; 1713 case MDOC_Ql: 1714 case MDOC_So: 1715 case MDOC_Sq: 1716 print_text(h, "\\(cq"); 1717 break; 1718 default: 1719 abort(); 1720 } 1721 } 1722 1723 static int 1724 mdoc_eo_pre(MDOC_ARGS) 1725 { 1726 1727 if (n->type != ROFFT_BODY) 1728 return 1; 1729 1730 if (n->end == ENDBODY_NOT && 1731 n->parent->head->child == NULL && 1732 n->child != NULL && 1733 n->child->end != ENDBODY_NOT) 1734 print_text(h, "\\&"); 1735 else if (n->end != ENDBODY_NOT ? n->child != NULL : 1736 n->parent->head->child != NULL && (n->child != NULL || 1737 (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1738 h->flags |= HTML_NOSPACE; 1739 return 1; 1740 } 1741 1742 static void 1743 mdoc_eo_post(MDOC_ARGS) 1744 { 1745 int body, tail; 1746 1747 if (n->type != ROFFT_BODY) 1748 return; 1749 1750 if (n->end != ENDBODY_NOT) { 1751 h->flags &= ~HTML_NOSPACE; 1752 return; 1753 } 1754 1755 body = n->child != NULL || n->parent->head->child != NULL; 1756 tail = n->parent->tail != NULL && n->parent->tail->child != NULL; 1757 1758 if (body && tail) 1759 h->flags |= HTML_NOSPACE; 1760 else if ( ! tail) 1761 h->flags &= ~HTML_NOSPACE; 1762 }