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