Print this page
9718 update mandoc to 1.14.4
   1 /*      $Id: man_html.c,v 1.145 2017/06/25 11:42:02 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2013, 2014, 2015, 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 
  28 #include "mandoc_aux.h"
  29 #include "mandoc.h"
  30 #include "roff.h"
  31 #include "man.h"
  32 #include "out.h"
  33 #include "html.h"
  34 #include "main.h"
  35 
  36 /* FIXME: have PD set the default vspace width. */
  37 
  38 #define INDENT            5
  39 
  40 #define MAN_ARGS          const struct roff_meta *man, \
  41                           const struct roff_node *n, \
  42                           struct html *h
  43 
  44 struct  htmlman {
  45         int             (*pre)(MAN_ARGS);
  46         int             (*post)(MAN_ARGS);
  47 };
  48 
  49 static  void              print_bvspace(struct html *,
  50                                 const struct roff_node *);
  51 static  void              print_man_head(MAN_ARGS);

  52 static  void              print_man_nodelist(MAN_ARGS);
  53 static  void              print_man_node(MAN_ARGS);
  54 static  int               fillmode(struct html *, int);
  55 static  int               a2width(const struct roff_node *,
  56                                 struct roffsu *);
  57 static  int               man_B_pre(MAN_ARGS);
  58 static  int               man_HP_pre(MAN_ARGS);
  59 static  int               man_IP_pre(MAN_ARGS);
  60 static  int               man_I_pre(MAN_ARGS);
  61 static  int               man_OP_pre(MAN_ARGS);
  62 static  int               man_PP_pre(MAN_ARGS);
  63 static  int               man_RS_pre(MAN_ARGS);
  64 static  int               man_SH_pre(MAN_ARGS);
  65 static  int               man_SM_pre(MAN_ARGS);
  66 static  int               man_SS_pre(MAN_ARGS);
  67 static  int               man_UR_pre(MAN_ARGS);
  68 static  int               man_alt_pre(MAN_ARGS);
  69 static  int               man_ign_pre(MAN_ARGS);
  70 static  int               man_in_pre(MAN_ARGS);
  71 static  void              man_root_post(MAN_ARGS);
  72 static  void              man_root_pre(MAN_ARGS);


  73 
  74 static  const struct htmlman __mans[MAN_MAX - MAN_TH] = {
  75         { NULL, NULL }, /* TH */
  76         { man_SH_pre, NULL }, /* SH */
  77         { man_SS_pre, NULL }, /* SS */
  78         { man_IP_pre, NULL }, /* TP */
  79         { man_PP_pre, NULL }, /* LP */
  80         { man_PP_pre, NULL }, /* PP */
  81         { man_PP_pre, NULL }, /* P */
  82         { man_IP_pre, NULL }, /* IP */
  83         { man_HP_pre, NULL }, /* HP */
  84         { man_SM_pre, NULL }, /* SM */
  85         { man_SM_pre, NULL }, /* SB */
  86         { man_alt_pre, NULL }, /* BI */
  87         { man_alt_pre, NULL }, /* IB */
  88         { man_alt_pre, NULL }, /* BR */
  89         { man_alt_pre, NULL }, /* RB */
  90         { NULL, NULL }, /* R */
  91         { man_B_pre, NULL }, /* B */
  92         { man_I_pre, NULL }, /* I */


 122  */
 123 static void
 124 print_bvspace(struct html *h, const struct roff_node *n)
 125 {
 126 
 127         if (n->body && n->body->child)
 128                 if (n->body->child->type == ROFFT_TBL)
 129                         return;
 130 
 131         if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
 132                 if (NULL == n->prev)
 133                         return;
 134 
 135         print_paragraph(h);
 136 }
 137 
 138 void
 139 html_man(void *arg, const struct roff_man *man)
 140 {
 141         struct html     *h;

 142         struct tag      *t;
 143 
 144         h = (struct html *)arg;

 145 
 146         if ((h->oflags & HTML_FRAGMENT) == 0) {
 147                 print_gen_decls(h);
 148                 print_otag(h, TAG_HTML, "");


 149                 t = print_otag(h, TAG_HEAD, "");
 150                 print_man_head(&man->meta, man->first, h);
 151                 print_tagq(h, t);
 152                 print_otag(h, TAG_BODY, "");
 153         }
 154 
 155         man_root_pre(&man->meta, man->first, h);
 156         t = print_otag(h, TAG_DIV, "c", "manual-text");
 157         print_man_nodelist(&man->meta, man->first->child, h);
 158         print_tagq(h, t);
 159         man_root_post(&man->meta, man->first, h);
 160         print_tagq(h, NULL);
 161 }
 162 
 163 static void
 164 print_man_head(MAN_ARGS)
 165 {
 166         char    *cp;
 167 
 168         print_gen_head(h);
 169         mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
 170         print_otag(h, TAG_TITLE, "");
 171         print_text(h, cp);
 172         free(cp);
 173 }
 174 
 175 static void
 176 print_man_nodelist(MAN_ARGS)
 177 {
 178 
 179         while (n != NULL) {
 180                 print_man_node(man, n, h);
 181                 n = n->next;
 182         }
 183 }
 184 


 245         case ROFFT_ELEM:
 246                 /*
 247                  * Some in-line macros produce tags and/or text
 248                  * in the handler, so they require fill mode to be
 249                  * configured up front just like for text nodes.
 250                  * For the others, keep the traditional approach
 251                  * of doing the same, for now.
 252                  */
 253                 fillmode(h, want_fillmode);
 254                 break;
 255         case ROFFT_TEXT:
 256                 if (fillmode(h, want_fillmode) == MAN_fi &&
 257                     want_fillmode == MAN_fi &&
 258                     n->flags & NODE_LINE && *n->string == ' ' &&
 259                     (h->flags & HTML_NONEWLINE) == 0)
 260                         print_otag(h, TAG_BR, "");
 261                 if (*n->string != '\0')
 262                         break;
 263                 print_paragraph(h);
 264                 return;


 265         default:
 266                 break;
 267         }
 268 
 269         /* Produce output for this node. */
 270 
 271         child = 1;
 272         switch (n->type) {
 273         case ROFFT_TEXT:
 274                 t = h->tag;
 275                 print_text(h, n->string);
 276                 break;
 277         case ROFFT_EQN:
 278                 t = h->tag;
 279                 print_eqn(h, n->eqn);
 280                 break;
 281         case ROFFT_TBL:
 282                 /*
 283                  * This will take care of initialising all of the table
 284                  * state data for the first table, then tearing it down


 342 fillmode(struct html *h, int want)
 343 {
 344         struct tag      *pre;
 345         int              had;
 346 
 347         for (pre = h->tag; pre != NULL; pre = pre->next)
 348                 if (pre->tag == TAG_PRE)
 349                         break;
 350 
 351         had = pre == NULL ? MAN_fi : MAN_nf;
 352 
 353         if (want && want != had) {
 354                 if (want == MAN_nf)
 355                         print_otag(h, TAG_PRE, "");
 356                 else
 357                         print_tagq(h, pre);
 358         }
 359         return had;
 360 }
 361 
 362 static int
 363 a2width(const struct roff_node *n, struct roffsu *su)
 364 {
 365         if (n->type != ROFFT_TEXT)
 366                 return 0;
 367         return a2roffsu(n->string, su, SCALE_EN) != NULL;
 368 }
 369 
 370 static void
 371 man_root_pre(MAN_ARGS)
 372 {
 373         struct tag      *t, *tt;
 374         char            *title;
 375 
 376         assert(man->title);
 377         assert(man->msec);
 378         mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
 379 
 380         t = print_otag(h, TAG_TABLE, "c", "head");
 381         tt = print_otag(h, TAG_TR, "");
 382 
 383         print_otag(h, TAG_TD, "c", "head-ltitle");
 384         print_text(h, title);
 385         print_stagq(h, tt);
 386 
 387         print_otag(h, TAG_TD, "c", "head-vol");
 388         if (NULL != man->vol)
 389                 print_text(h, man->vol);
 390         print_stagq(h, tt);
 391 
 392         print_otag(h, TAG_TD, "c", "head-rtitle");
 393         print_text(h, title);
 394         print_tagq(h, t);
 395         free(title);
 396 }
 397 
 398 static void
 399 man_root_post(MAN_ARGS)
 400 {
 401         struct tag      *t, *tt;
 402 
 403         t = print_otag(h, TAG_TABLE, "c", "foot");
 404         tt = print_otag(h, TAG_TR, "");
 405 
 406         print_otag(h, TAG_TD, "c", "foot-date");
 407         print_text(h, man->date);
 408         print_stagq(h, tt);
 409 
 410         print_otag(h, TAG_TD, "c", "foot-os");
 411         if (man->os)
 412                 print_text(h, man->os);
 413         print_tagq(h, t);
 414 }
 415 
 416 static int
 417 man_SH_pre(MAN_ARGS)
 418 {
 419         char    *id;
 420 
 421         if (n->type == ROFFT_HEAD) {
 422                 id = html_make_id(n);
 423                 print_otag(h, TAG_H1, "cTi", "Sh", id);
 424                 if (id != NULL)
 425                         print_otag(h, TAG_A, "chR", "selflink", id);
 426                 free(id);
 427         }
 428         return 1;
 429 }
 430 
 431 static int
 432 man_alt_pre(MAN_ARGS)
 433 {
 434         const struct roff_node  *nn;
 435         int              i;
 436         enum htmltag     fp;
 437         struct tag      *t;
 438 
 439         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
 440                 switch (n->tok) {
 441                 case MAN_BI:
 442                         fp = i % 2 ? TAG_I : TAG_B;
 443                         break;
 444                 case MAN_IB:
 445                         fp = i % 2 ? TAG_B : TAG_I;
 446                         break;


 472                         print_tagq(h, t);
 473         }
 474         return 0;
 475 }
 476 
 477 static int
 478 man_SM_pre(MAN_ARGS)
 479 {
 480         print_otag(h, TAG_SMALL, "");
 481         if (MAN_SB == n->tok)
 482                 print_otag(h, TAG_B, "");
 483         return 1;
 484 }
 485 
 486 static int
 487 man_SS_pre(MAN_ARGS)
 488 {
 489         char    *id;
 490 
 491         if (n->type == ROFFT_HEAD) {
 492                 id = html_make_id(n);
 493                 print_otag(h, TAG_H2, "cTi", "Ss", id);
 494                 if (id != NULL)
 495                         print_otag(h, TAG_A, "chR", "selflink", id);
 496                 free(id);
 497         }
 498         return 1;
 499 }
 500 
 501 static int
 502 man_PP_pre(MAN_ARGS)
 503 {
 504 
 505         if (n->type == ROFFT_HEAD)
 506                 return 0;
 507         else if (n->type == ROFFT_BLOCK)
 508                 print_bvspace(h, n);
 509 
 510         return 1;
 511 }
 512 
 513 static int
 514 man_IP_pre(MAN_ARGS)
 515 {
 516         const struct roff_node  *nn;
 517 
 518         if (n->type == ROFFT_BODY) {
 519                 print_otag(h, TAG_DD, "c", "It-tag");
 520                 return 1;
 521         } else if (n->type != ROFFT_HEAD) {
 522                 print_otag(h, TAG_DL, "c", "Bl-tag");
 523                 return 1;
 524         }
 525 
 526         /* FIXME: width specification. */
 527 
 528         print_otag(h, TAG_DT, "c", "It-tag");
 529 
 530         /* For IP, only print the first header element. */
 531 
 532         if (MAN_IP == n->tok && n->child)
 533                 print_man_node(man, n->child, h);
 534 
 535         /* For TP, only print next-line header elements. */
 536 
 537         if (MAN_TP == n->tok) {
 538                 nn = n->child;
 539                 while (NULL != nn && 0 == (NODE_LINE & nn->flags))
 540                         nn = nn->next;
 541                 while (NULL != nn) {
 542                         print_man_node(man, nn, h);
 543                         nn = nn->next;
 544                 }
 545         }
 546 
 547         return 0;
 548 }
 549 
 550 static int
 551 man_HP_pre(MAN_ARGS)
 552 {
 553         struct roffsu    sum, sui;
 554         const struct roff_node *np;
 555 
 556         if (n->type == ROFFT_HEAD)
 557                 return 0;
 558         else if (n->type != ROFFT_BLOCK)
 559                 return 1;
 560 
 561         np = n->head->child;
 562 
 563         if (np == NULL || !a2width(np, &sum))
 564                 SCALE_HS_INIT(&sum, INDENT);
 565 
 566         sui.unit = sum.unit;
 567         sui.scale = -sum.scale;
 568 
 569         print_bvspace(h, n);
 570         print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui);

 571         return 1;
 572 }
 573 
 574 static int
 575 man_OP_pre(MAN_ARGS)
 576 {
 577         struct tag      *tt;
 578 
 579         print_text(h, "[");
 580         h->flags |= HTML_NOSPACE;
 581         tt = print_otag(h, TAG_SPAN, "c", "Op");
 582 
 583         if (NULL != (n = n->child)) {
 584                 print_otag(h, TAG_B, "");
 585                 print_text(h, n->string);
 586         }
 587 
 588         print_stagq(h, tt);
 589 
 590         if (NULL != n && NULL != n->next) {


 612         return 1;
 613 }
 614 
 615 static int
 616 man_in_pre(MAN_ARGS)
 617 {
 618         print_otag(h, TAG_BR, "");
 619         return 0;
 620 }
 621 
 622 static int
 623 man_ign_pre(MAN_ARGS)
 624 {
 625 
 626         return 0;
 627 }
 628 
 629 static int
 630 man_RS_pre(MAN_ARGS)
 631 {
 632         struct roffsu    su;
 633 
 634         if (n->type == ROFFT_HEAD)
 635                 return 0;
 636         else if (n->type == ROFFT_BODY)

 637                 return 1;
 638 
 639         SCALE_HS_INIT(&su, INDENT);
 640         if (n->head->child)
 641                 a2width(n->head->child, &su);
 642 
 643         print_otag(h, TAG_DIV, "sul", &su);
 644         return 1;
 645 }
 646 
 647 static int
 648 man_UR_pre(MAN_ARGS)
 649 {
 650         char *cp;
 651         n = n->child;
 652         assert(n->type == ROFFT_HEAD);
 653         if (n->child != NULL) {
 654                 assert(n->child->type == ROFFT_TEXT);
 655                 if (n->tok == MAN_MT) {
 656                         mandoc_asprintf(&cp, "mailto:%s", n->child->string);
 657                         print_otag(h, TAG_A, "cTh", "Mt", cp);
 658                         free(cp);
 659                 } else
 660                         print_otag(h, TAG_A, "cTh", "Lk", n->child->string);
 661         }
 662 
 663         assert(n->next->type == ROFFT_BODY);
 664         if (n->next->child != NULL)
   1 /*      $Id: man_html.c,v 1.153 2018/07/27 17:49:31 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2013,2014,2015,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 
  28 #include "mandoc_aux.h"
  29 #include "mandoc.h"
  30 #include "roff.h"
  31 #include "man.h"
  32 #include "out.h"
  33 #include "html.h"
  34 #include "main.h"
  35 
  36 /* FIXME: have PD set the default vspace width. */
  37 


  38 #define MAN_ARGS          const struct roff_meta *man, \
  39                           const struct roff_node *n, \
  40                           struct html *h
  41 
  42 struct  htmlman {
  43         int             (*pre)(MAN_ARGS);
  44         int             (*post)(MAN_ARGS);
  45 };
  46 
  47 static  void              print_bvspace(struct html *,
  48                                 const struct roff_node *);
  49 static  void              print_man_head(const struct roff_meta *,
  50                                 struct html *);
  51 static  void              print_man_nodelist(MAN_ARGS);
  52 static  void              print_man_node(MAN_ARGS);
  53 static  int               fillmode(struct html *, int);


  54 static  int               man_B_pre(MAN_ARGS);
  55 static  int               man_HP_pre(MAN_ARGS);
  56 static  int               man_IP_pre(MAN_ARGS);
  57 static  int               man_I_pre(MAN_ARGS);
  58 static  int               man_OP_pre(MAN_ARGS);
  59 static  int               man_PP_pre(MAN_ARGS);
  60 static  int               man_RS_pre(MAN_ARGS);
  61 static  int               man_SH_pre(MAN_ARGS);
  62 static  int               man_SM_pre(MAN_ARGS);
  63 static  int               man_SS_pre(MAN_ARGS);
  64 static  int               man_UR_pre(MAN_ARGS);
  65 static  int               man_alt_pre(MAN_ARGS);
  66 static  int               man_ign_pre(MAN_ARGS);
  67 static  int               man_in_pre(MAN_ARGS);
  68 static  void              man_root_post(const struct roff_meta *,
  69                                 struct html *);
  70 static  void              man_root_pre(const struct roff_meta *,
  71                                 struct html *);
  72 
  73 static  const struct htmlman __mans[MAN_MAX - MAN_TH] = {
  74         { NULL, NULL }, /* TH */
  75         { man_SH_pre, NULL }, /* SH */
  76         { man_SS_pre, NULL }, /* SS */
  77         { man_IP_pre, NULL }, /* TP */
  78         { man_PP_pre, NULL }, /* LP */
  79         { man_PP_pre, NULL }, /* PP */
  80         { man_PP_pre, NULL }, /* P */
  81         { man_IP_pre, NULL }, /* IP */
  82         { man_HP_pre, NULL }, /* HP */
  83         { man_SM_pre, NULL }, /* SM */
  84         { man_SM_pre, NULL }, /* SB */
  85         { man_alt_pre, NULL }, /* BI */
  86         { man_alt_pre, NULL }, /* IB */
  87         { man_alt_pre, NULL }, /* BR */
  88         { man_alt_pre, NULL }, /* RB */
  89         { NULL, NULL }, /* R */
  90         { man_B_pre, NULL }, /* B */
  91         { man_I_pre, NULL }, /* I */


 121  */
 122 static void
 123 print_bvspace(struct html *h, const struct roff_node *n)
 124 {
 125 
 126         if (n->body && n->body->child)
 127                 if (n->body->child->type == ROFFT_TBL)
 128                         return;
 129 
 130         if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS)
 131                 if (NULL == n->prev)
 132                         return;
 133 
 134         print_paragraph(h);
 135 }
 136 
 137 void
 138 html_man(void *arg, const struct roff_man *man)
 139 {
 140         struct html             *h;
 141         struct roff_node        *n;
 142         struct tag              *t;
 143 
 144         h = (struct html *)arg;
 145         n = man->first->child;
 146 
 147         if ((h->oflags & HTML_FRAGMENT) == 0) {
 148                 print_gen_decls(h);
 149                 print_otag(h, TAG_HTML, "");
 150                 if (n->type == ROFFT_COMMENT)
 151                         print_gen_comment(h, n);
 152                 t = print_otag(h, TAG_HEAD, "");
 153                 print_man_head(&man->meta, h);
 154                 print_tagq(h, t);
 155                 print_otag(h, TAG_BODY, "");
 156         }
 157 
 158         man_root_pre(&man->meta, h);
 159         t = print_otag(h, TAG_DIV, "c", "manual-text");
 160         print_man_nodelist(&man->meta, n, h);
 161         print_tagq(h, t);
 162         man_root_post(&man->meta, h);
 163         print_tagq(h, NULL);
 164 }
 165 
 166 static void
 167 print_man_head(const struct roff_meta *man, struct html *h)
 168 {
 169         char    *cp;
 170 
 171         print_gen_head(h);
 172         mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
 173         print_otag(h, TAG_TITLE, "");
 174         print_text(h, cp);
 175         free(cp);
 176 }
 177 
 178 static void
 179 print_man_nodelist(MAN_ARGS)
 180 {
 181 
 182         while (n != NULL) {
 183                 print_man_node(man, n, h);
 184                 n = n->next;
 185         }
 186 }
 187 


 248         case ROFFT_ELEM:
 249                 /*
 250                  * Some in-line macros produce tags and/or text
 251                  * in the handler, so they require fill mode to be
 252                  * configured up front just like for text nodes.
 253                  * For the others, keep the traditional approach
 254                  * of doing the same, for now.
 255                  */
 256                 fillmode(h, want_fillmode);
 257                 break;
 258         case ROFFT_TEXT:
 259                 if (fillmode(h, want_fillmode) == MAN_fi &&
 260                     want_fillmode == MAN_fi &&
 261                     n->flags & NODE_LINE && *n->string == ' ' &&
 262                     (h->flags & HTML_NONEWLINE) == 0)
 263                         print_otag(h, TAG_BR, "");
 264                 if (*n->string != '\0')
 265                         break;
 266                 print_paragraph(h);
 267                 return;
 268         case ROFFT_COMMENT:
 269                 return;
 270         default:
 271                 break;
 272         }
 273 
 274         /* Produce output for this node. */
 275 
 276         child = 1;
 277         switch (n->type) {
 278         case ROFFT_TEXT:
 279                 t = h->tag;
 280                 print_text(h, n->string);
 281                 break;
 282         case ROFFT_EQN:
 283                 t = h->tag;
 284                 print_eqn(h, n->eqn);
 285                 break;
 286         case ROFFT_TBL:
 287                 /*
 288                  * This will take care of initialising all of the table
 289                  * state data for the first table, then tearing it down


 347 fillmode(struct html *h, int want)
 348 {
 349         struct tag      *pre;
 350         int              had;
 351 
 352         for (pre = h->tag; pre != NULL; pre = pre->next)
 353                 if (pre->tag == TAG_PRE)
 354                         break;
 355 
 356         had = pre == NULL ? MAN_fi : MAN_nf;
 357 
 358         if (want && want != had) {
 359                 if (want == MAN_nf)
 360                         print_otag(h, TAG_PRE, "");
 361                 else
 362                         print_tagq(h, pre);
 363         }
 364         return had;
 365 }
 366 








 367 static void
 368 man_root_pre(const struct roff_meta *man, struct html *h)
 369 {
 370         struct tag      *t, *tt;
 371         char            *title;
 372 
 373         assert(man->title);
 374         assert(man->msec);
 375         mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
 376 
 377         t = print_otag(h, TAG_TABLE, "c", "head");
 378         tt = print_otag(h, TAG_TR, "");
 379 
 380         print_otag(h, TAG_TD, "c", "head-ltitle");
 381         print_text(h, title);
 382         print_stagq(h, tt);
 383 
 384         print_otag(h, TAG_TD, "c", "head-vol");
 385         if (NULL != man->vol)
 386                 print_text(h, man->vol);
 387         print_stagq(h, tt);
 388 
 389         print_otag(h, TAG_TD, "c", "head-rtitle");
 390         print_text(h, title);
 391         print_tagq(h, t);
 392         free(title);
 393 }
 394 
 395 static void
 396 man_root_post(const struct roff_meta *man, struct html *h)
 397 {
 398         struct tag      *t, *tt;
 399 
 400         t = print_otag(h, TAG_TABLE, "c", "foot");
 401         tt = print_otag(h, TAG_TR, "");
 402 
 403         print_otag(h, TAG_TD, "c", "foot-date");
 404         print_text(h, man->date);
 405         print_stagq(h, tt);
 406 
 407         print_otag(h, TAG_TD, "c", "foot-os");
 408         if (man->os)
 409                 print_text(h, man->os);
 410         print_tagq(h, t);
 411 }
 412 
 413 static int
 414 man_SH_pre(MAN_ARGS)
 415 {
 416         char    *id;
 417 
 418         if (n->type == ROFFT_HEAD) {
 419                 id = html_make_id(n, 1);
 420                 print_otag(h, TAG_H1, "cTi", "Sh", id);
 421                 if (id != NULL)
 422                         print_otag(h, TAG_A, "chR", "permalink", id);

 423         }
 424         return 1;
 425 }
 426 
 427 static int
 428 man_alt_pre(MAN_ARGS)
 429 {
 430         const struct roff_node  *nn;
 431         int              i;
 432         enum htmltag     fp;
 433         struct tag      *t;
 434 
 435         for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
 436                 switch (n->tok) {
 437                 case MAN_BI:
 438                         fp = i % 2 ? TAG_I : TAG_B;
 439                         break;
 440                 case MAN_IB:
 441                         fp = i % 2 ? TAG_B : TAG_I;
 442                         break;


 468                         print_tagq(h, t);
 469         }
 470         return 0;
 471 }
 472 
 473 static int
 474 man_SM_pre(MAN_ARGS)
 475 {
 476         print_otag(h, TAG_SMALL, "");
 477         if (MAN_SB == n->tok)
 478                 print_otag(h, TAG_B, "");
 479         return 1;
 480 }
 481 
 482 static int
 483 man_SS_pre(MAN_ARGS)
 484 {
 485         char    *id;
 486 
 487         if (n->type == ROFFT_HEAD) {
 488                 id = html_make_id(n, 1);
 489                 print_otag(h, TAG_H2, "cTi", "Ss", id);
 490                 if (id != NULL)
 491                         print_otag(h, TAG_A, "chR", "permalink", id);

 492         }
 493         return 1;
 494 }
 495 
 496 static int
 497 man_PP_pre(MAN_ARGS)
 498 {
 499 
 500         if (n->type == ROFFT_HEAD)
 501                 return 0;
 502         else if (n->type == ROFFT_BLOCK)
 503                 print_bvspace(h, n);
 504 
 505         return 1;
 506 }
 507 
 508 static int
 509 man_IP_pre(MAN_ARGS)
 510 {
 511         const struct roff_node  *nn;
 512 
 513         if (n->type == ROFFT_BODY) {
 514                 print_otag(h, TAG_DD, "");
 515                 return 1;
 516         } else if (n->type != ROFFT_HEAD) {
 517                 print_otag(h, TAG_DL, "c", "Bl-tag");
 518                 return 1;
 519         }
 520 
 521         /* FIXME: width specification. */
 522 
 523         print_otag(h, TAG_DT, "");
 524 
 525         /* For IP, only print the first header element. */
 526 
 527         if (MAN_IP == n->tok && n->child)
 528                 print_man_node(man, n->child, h);
 529 
 530         /* For TP, only print next-line header elements. */
 531 
 532         if (MAN_TP == n->tok) {
 533                 nn = n->child;
 534                 while (NULL != nn && 0 == (NODE_LINE & nn->flags))
 535                         nn = nn->next;
 536                 while (NULL != nn) {
 537                         print_man_node(man, nn, h);
 538                         nn = nn->next;
 539                 }
 540         }
 541 
 542         return 0;
 543 }
 544 
 545 static int
 546 man_HP_pre(MAN_ARGS)
 547 {



 548         if (n->type == ROFFT_HEAD)
 549                 return 0;


 550 
 551         if (n->type == ROFFT_BLOCK) {







 552                 print_bvspace(h, n);
 553                 print_otag(h, TAG_DIV, "c", "HP");
 554         }
 555         return 1;
 556 }
 557 
 558 static int
 559 man_OP_pre(MAN_ARGS)
 560 {
 561         struct tag      *tt;
 562 
 563         print_text(h, "[");
 564         h->flags |= HTML_NOSPACE;
 565         tt = print_otag(h, TAG_SPAN, "c", "Op");
 566 
 567         if (NULL != (n = n->child)) {
 568                 print_otag(h, TAG_B, "");
 569                 print_text(h, n->string);
 570         }
 571 
 572         print_stagq(h, tt);
 573 
 574         if (NULL != n && NULL != n->next) {


 596         return 1;
 597 }
 598 
 599 static int
 600 man_in_pre(MAN_ARGS)
 601 {
 602         print_otag(h, TAG_BR, "");
 603         return 0;
 604 }
 605 
 606 static int
 607 man_ign_pre(MAN_ARGS)
 608 {
 609 
 610         return 0;
 611 }
 612 
 613 static int
 614 man_RS_pre(MAN_ARGS)
 615 {


 616         if (n->type == ROFFT_HEAD)
 617                 return 0;
 618         if (n->type == ROFFT_BLOCK)
 619                 print_otag(h, TAG_DIV, "c", "Bd-indent");
 620         return 1;







 621 }
 622 
 623 static int
 624 man_UR_pre(MAN_ARGS)
 625 {
 626         char *cp;
 627         n = n->child;
 628         assert(n->type == ROFFT_HEAD);
 629         if (n->child != NULL) {
 630                 assert(n->child->type == ROFFT_TEXT);
 631                 if (n->tok == MAN_MT) {
 632                         mandoc_asprintf(&cp, "mailto:%s", n->child->string);
 633                         print_otag(h, TAG_A, "cTh", "Mt", cp);
 634                         free(cp);
 635                 } else
 636                         print_otag(h, TAG_A, "cTh", "Lk", n->child->string);
 637         }
 638 
 639         assert(n->next->type == ROFFT_BODY);
 640         if (n->next->child != NULL)