Print this page
9718 update mandoc to 1.14.4
   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 
  47 struct  htmlmdoc {
  48         int             (*pre)(MDOC_ARGS);
  49         void            (*post)(MDOC_ARGS);
  50 };
  51 
  52 static  char             *cond_id(const struct roff_node *);
  53 static  void              print_mdoc_head(MDOC_ARGS);

  54 static  void              print_mdoc_node(MDOC_ARGS);
  55 static  void              print_mdoc_nodelist(MDOC_ARGS);
  56 static  void              synopsis_pre(struct html *,
  57                                 const struct roff_node *);
  58 
  59 static  void              mdoc_root_post(MDOC_ARGS);
  60 static  int               mdoc_root_pre(MDOC_ARGS);


  61 
  62 static  void              mdoc__x_post(MDOC_ARGS);
  63 static  int               mdoc__x_pre(MDOC_ARGS);
  64 static  int               mdoc_ad_pre(MDOC_ARGS);
  65 static  int               mdoc_an_pre(MDOC_ARGS);
  66 static  int               mdoc_ap_pre(MDOC_ARGS);
  67 static  int               mdoc_ar_pre(MDOC_ARGS);
  68 static  int               mdoc_bd_pre(MDOC_ARGS);
  69 static  int               mdoc_bf_pre(MDOC_ARGS);
  70 static  void              mdoc_bk_post(MDOC_ARGS);
  71 static  int               mdoc_bk_pre(MDOC_ARGS);
  72 static  int               mdoc_bl_pre(MDOC_ARGS);
  73 static  int               mdoc_cd_pre(MDOC_ARGS);
  74 static  int               mdoc_cm_pre(MDOC_ARGS);
  75 static  int               mdoc_d1_pre(MDOC_ARGS);
  76 static  int               mdoc_dv_pre(MDOC_ARGS);
  77 static  int               mdoc_fa_pre(MDOC_ARGS);
  78 static  int               mdoc_fd_pre(MDOC_ARGS);
  79 static  int               mdoc_fl_pre(MDOC_ARGS);
  80 static  int               mdoc_fn_pre(MDOC_ARGS);


 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 
 284 void
 285 html_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 
 309 static void
 310 print_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 
 331 static void
 332 print_mdoc_nodelist(MDOC_ARGS)
 333 {
 334 
 335         while (n != NULL) {
 336                 print_mdoc_node(meta, n, h);
 337                 n = n->next;
 338         }
 339 }
 340 
 341 static void
 342 print_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;


 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 
 431 static void
 432 mdoc_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 
 448 static int
 449 mdoc_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");


 478         print_text(h, title);
 479         print_tagq(h, t);
 480 
 481         free(title);
 482         free(volume);
 483         return 1;
 484 }
 485 
 486 static char *
 487 cond_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 
 502 static int
 503 mdoc_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 
 525 static int
 526 mdoc_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 
 541 static int
 542 mdoc_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 
 561 static int
 562 mdoc_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 
 573 static int
 574 mdoc_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 
 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_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 
 608 static int
 609 mdoc_xr_pre(MDOC_ARGS)
 610 {
 611         if (NULL == n->child)
 612                 return 0;
 613 
 614         if (h->base_man)


 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         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 
 789 static int
 790 mdoc_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 
 879 static int
 880 mdoc_ex_pre(MDOC_ARGS)
 881 {
 882         if (n->prev)
 883                 print_otag(h, TAG_BR, "");
 884         return 1;
 885 }
 886 
 887 static int
 888 mdoc_st_pre(MDOC_ARGS)
 889 {
 890         print_otag(h, TAG_SPAN, "cT", "St");
 891         return 1;
 892 }
 893 
 894 static int
 895 mdoc_em_pre(MDOC_ARGS)
 896 {
 897         print_otag(h, TAG_I, "cT", "Em");
 898         return 1;
 899 }
 900 
 901 static int
 902 mdoc_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 
 915 static int
 916 mdoc_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 
 926 static int
 927 mdoc_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) {


 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 
1014 static int
1015 mdoc_pa_pre(MDOC_ARGS)
1016 {
1017         print_otag(h, TAG_I, "cT", "Pa");
1018         return 1;
1019 }
1020 
1021 static int
1022 mdoc_ad_pre(MDOC_ARGS)
1023 {
1024         print_otag(h, TAG_I, "c", "Ad");
1025         return 1;
1026 }
1027 
1028 static int
1029 mdoc_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 
1052 static int
1053 mdoc_cd_pre(MDOC_ARGS)
1054 {
1055         synopsis_pre(h, n);
1056         print_otag(h, TAG_B, "cT", "Cd");
1057         return 1;
1058 }
1059 
1060 static int
1061 mdoc_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 
1072 static int
1073 mdoc_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 
1084 static int
1085 mdoc_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 
1102 static int
1103 mdoc_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;


1125                 print_text(h, ",");
1126         }
1127 
1128         return 0;
1129 }
1130 
1131 static int
1132 mdoc_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);


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;


1358 
1359 static int
1360 mdoc_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 
1384 static void
1385 mdoc_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 
1396 static int
1397 mdoc_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


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 
1441 static int
1442 mdoc_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 
1453 static int
1454 mdoc_va_pre(MDOC_ARGS)
1455 {
1456         print_otag(h, TAG_VAR, "cT", "Va");
1457         return 1;
1458 }
1459 
1460 static int
1461 mdoc_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 
1470 static int
1471 mdoc_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 
1498 static int
1499 mdoc_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 
1510 static int
1511 mdoc_igndelim_pre(MDOC_ARGS)
1512 {
1513 
1514         h->flags |= HTML_IGNDELIM;
1515         return 1;
1516 }
1517 
1518 static void
1519 mdoc_pf_post(MDOC_ARGS)
1520 {
1521 
1522         if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1523                 h->flags |= HTML_NOSPACE;
1524 }
1525 
1526 static int
1527 mdoc_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 
1539 static int
1540 mdoc_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 
1551 static int
1552 mdoc_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 
1563 static int
1564 mdoc_sy_pre(MDOC_ARGS)
1565 {
1566         print_otag(h, TAG_B, "cT", "Sy");
1567         return 1;
1568 }
1569 
1570 static int
1571 mdoc_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 


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;


   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);


 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;


 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");


 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)


 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) {


 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;


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);


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;


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


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 


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;