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