Print this page
Update to 1.12.3.
   1 /*      $Id: mdoc_html.c,v 1.182 2011/11/03 20:37:00 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  *
   5  * Permission to use, copy, modify, and distribute this software for any
   6  * purpose with or without fee is hereby granted, provided that the above
   7  * copyright notice and this permission notice appear in all copies.
   8  *
   9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16  */
  17 #ifdef HAVE_CONFIG_H
  18 #include "config.h"
  19 #endif
  20 
  21 #include <sys/types.h>
  22 
  23 #include <assert.h>
  24 #include <ctype.h>
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #include <unistd.h>
  29 
  30 #include "mandoc.h"
  31 #include "out.h"
  32 #include "html.h"
  33 #include "mdoc.h"
  34 #include "main.h"
  35 
  36 #define INDENT           5
  37 
  38 #define MDOC_ARGS         const struct mdoc_meta *m, \
  39                           const struct mdoc_node *n, \
  40                           struct html *h
  41 
  42 #ifndef MIN
  43 #define MIN(a,b)        ((/*CONSTCOND*/(a)<(b))?(a):(b))
  44 #endif
  45 
  46 struct  htmlmdoc {
  47         int             (*pre)(MDOC_ARGS);
  48         void            (*post)(MDOC_ARGS);
  49 };
  50 
  51 static  void              print_mdoc(MDOC_ARGS);
  52 static  void              print_mdoc_head(MDOC_ARGS);
  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 mdoc_node *);
  57 
  58 static  void              a2width(const char *, struct roffsu *);


 243         {mdoc__x_pre, mdoc__x_post}, /* %U */ 
 244         {NULL, NULL}, /* Ta */ 
 245 };
 246 
 247 static  const char * const lists[LIST_MAX] = {
 248         NULL,
 249         "list-bul",
 250         "list-col",
 251         "list-dash",
 252         "list-diag",
 253         "list-enum",
 254         "list-hang",
 255         "list-hyph",
 256         "list-inset",
 257         "list-item",
 258         "list-ohang",
 259         "list-tag"
 260 };
 261 
 262 void
 263 html_mdoc(void *arg, const struct mdoc *m)
 264 {
 265 
 266         print_mdoc(mdoc_meta(m), mdoc_node(m), (struct html *)arg);

 267         putchar('\n');
 268 }
 269 
 270 
 271 /*
 272  * Calculate the scaling unit passed in a `-width' argument.  This uses
 273  * either a native scaling unit (e.g., 1i, 2m) or the string length of
 274  * the value.
 275  */
 276 static void
 277 a2width(const char *p, struct roffsu *su)
 278 {
 279 
 280         if ( ! a2roffsu(p, su, SCALE_MAX)) {
 281                 su->unit = SCALE_BU;
 282                 su->scale = html_strlen(p);
 283         }
 284 }
 285 
 286 


 344                 SCALE_HS_INIT(su, INDENT);
 345         else if (0 == strcmp(p, "indent-two"))
 346                 SCALE_HS_INIT(su, INDENT * 2);
 347         else if ( ! a2roffsu(p, su, SCALE_MAX))
 348                 SCALE_HS_INIT(su, html_strlen(p));
 349 }
 350 
 351 
 352 static void
 353 print_mdoc(MDOC_ARGS)
 354 {
 355         struct tag      *t, *tt;
 356         struct htmlpair  tag;
 357 
 358         PAIR_CLASS_INIT(&tag, "mandoc");
 359 
 360         if ( ! (HTML_FRAGMENT & h->oflags)) {
 361                 print_gen_decls(h);
 362                 t = print_otag(h, TAG_HTML, 0, NULL);
 363                 tt = print_otag(h, TAG_HEAD, 0, NULL);
 364                 print_mdoc_head(m, n, h);
 365                 print_tagq(h, tt);
 366                 print_otag(h, TAG_BODY, 0, NULL);
 367                 print_otag(h, TAG_DIV, 1, &tag);
 368         } else 
 369                 t = print_otag(h, TAG_DIV, 1, &tag);
 370 
 371         print_mdoc_nodelist(m, n, h);
 372         print_tagq(h, t);
 373 }
 374 
 375 
 376 /* ARGSUSED */
 377 static void
 378 print_mdoc_head(MDOC_ARGS)
 379 {
 380 
 381         print_gen_head(h);
 382         bufinit(h);
 383         bufcat_fmt(h, "%s(%s)", m->title, m->msec);
 384 
 385         if (m->arch)
 386                 bufcat_fmt(h, " (%s)", m->arch);
 387 
 388         print_otag(h, TAG_TITLE, 0, NULL);
 389         print_text(h, h->buf);
 390 }
 391 
 392 
 393 static void
 394 print_mdoc_nodelist(MDOC_ARGS)
 395 {
 396 
 397         print_mdoc_node(m, n, h);
 398         if (n->next)
 399                 print_mdoc_nodelist(m, n->next, h);
 400 }
 401 
 402 
 403 static void
 404 print_mdoc_node(MDOC_ARGS)
 405 {
 406         int              child;
 407         struct tag      *t;
 408 
 409         child = 1;
 410         t = h->tags.head;
 411 
 412         switch (n->type) {
 413         case (MDOC_ROOT):
 414                 child = mdoc_root_pre(m, n, h);
 415                 break;
 416         case (MDOC_TEXT):
 417                 /* No tables in this mode... */
 418                 assert(NULL == h->tblt);
 419 
 420                 /*
 421                  * Make sure that if we're in a literal mode already
 422                  * (i.e., within a <PRE>) don't print the newline.
 423                  */
 424                 if (' ' == *n->string && MDOC_LINE & n->flags)
 425                         if ( ! (HTML_LITERAL & h->flags))
 426                                 print_otag(h, TAG_BR, 0, NULL);
 427                 if (MDOC_DELIMC & n->flags)
 428                         h->flags |= HTML_NOSPACE;
 429                 print_text(h, n->string);
 430                 if (MDOC_DELIMO & n->flags)
 431                         h->flags |= HTML_NOSPACE;
 432                 return;
 433         case (MDOC_EQN):
 434                 print_eqn(h, n->eqn);


 437                 /*
 438                  * This will take care of initialising all of the table
 439                  * state data for the first table, then tearing it down
 440                  * for the last one.
 441                  */
 442                 print_tbl(h, n->span);
 443                 return;
 444         default:
 445                 /*
 446                  * Close out the current table, if it's open, and unset
 447                  * the "meta" table state.  This will be reopened on the
 448                  * next table element.
 449                  */
 450                 if (h->tblt) {
 451                         print_tblclose(h);
 452                         t = h->tags.head;
 453                 }
 454 
 455                 assert(NULL == h->tblt);
 456                 if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
 457                         child = (*mdocs[n->tok].pre)(m, n, h);
 458                 break;
 459         }
 460 
 461         if (HTML_KEEP & h->flags) {
 462                 if (n->prev && n->prev->line != n->line) {

 463                         h->flags &= ~HTML_KEEP;
 464                         h->flags |= HTML_PREKEEP;
 465                 } else if (NULL == n->prev) {
 466                         if (n->parent && n->parent->line != n->line) {
 467                                 h->flags &= ~HTML_KEEP;
 468                                 h->flags |= HTML_PREKEEP;
 469                         }
 470                 }
 471         }
 472 
 473         if (child && n->child)
 474                 print_mdoc_nodelist(m, n->child, h);
 475 
 476         print_stagq(h, t);
 477 
 478         switch (n->type) {
 479         case (MDOC_ROOT):
 480                 mdoc_root_post(m, n, h);
 481                 break;
 482         case (MDOC_EQN):
 483                 break;
 484         default:
 485                 if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
 486                         (*mdocs[n->tok].post)(m, n, h);
 487                 break;
 488         }
 489 }
 490 
 491 /* ARGSUSED */
 492 static void
 493 mdoc_root_post(MDOC_ARGS)
 494 {
 495         struct htmlpair  tag[3];
 496         struct tag      *t, *tt;
 497 
 498         PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
 499         PAIR_CLASS_INIT(&tag[1], "foot");
 500         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
 501         t = print_otag(h, TAG_TABLE, 3, tag);
 502         PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
 503         print_otag(h, TAG_COL, 1, tag);
 504         print_otag(h, TAG_COL, 1, tag);
 505 
 506         print_otag(h, TAG_TBODY, 0, NULL);
 507 
 508         tt = print_otag(h, TAG_TR, 0, NULL);
 509 
 510         PAIR_CLASS_INIT(&tag[0], "foot-date");
 511         print_otag(h, TAG_TD, 1, tag);
 512         print_text(h, m->date);
 513         print_stagq(h, tt);
 514 
 515         PAIR_CLASS_INIT(&tag[0], "foot-os");
 516         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
 517         print_otag(h, TAG_TD, 2, tag);
 518         print_text(h, m->os);
 519         print_tagq(h, t);
 520 }
 521 
 522 
 523 /* ARGSUSED */
 524 static int
 525 mdoc_root_pre(MDOC_ARGS)
 526 {
 527         struct htmlpair  tag[3];
 528         struct tag      *t, *tt;
 529         char             b[BUFSIZ], title[BUFSIZ];
 530 
 531         strlcpy(b, m->vol, BUFSIZ);
 532 
 533         if (m->arch) {
 534                 strlcat(b, " (", BUFSIZ);
 535                 strlcat(b, m->arch, BUFSIZ);
 536                 strlcat(b, ")", BUFSIZ);
 537         }
 538 
 539         snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
 540 
 541         PAIR_SUMMARY_INIT(&tag[0], "Document Header");
 542         PAIR_CLASS_INIT(&tag[1], "head");
 543         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
 544         t = print_otag(h, TAG_TABLE, 3, tag);
 545         PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
 546         print_otag(h, TAG_COL, 1, tag);
 547         print_otag(h, TAG_COL, 1, tag);
 548         print_otag(h, TAG_COL, 1, tag);
 549 
 550         print_otag(h, TAG_TBODY, 0, NULL);
 551 
 552         tt = print_otag(h, TAG_TR, 0, NULL);
 553 
 554         PAIR_CLASS_INIT(&tag[0], "head-ltitle");
 555         print_otag(h, TAG_TD, 1, tag);
 556         print_text(h, title);
 557         print_stagq(h, tt);
 558 
 559         PAIR_CLASS_INIT(&tag[0], "head-vol");


 672 
 673         print_text(h, "\\(em");
 674         PAIR_CLASS_INIT(&tag, "desc");
 675         print_otag(h, TAG_SPAN, 1, &tag);
 676         return(1);
 677 }
 678 
 679 
 680 static int
 681 mdoc_nm_pre(MDOC_ARGS)
 682 {
 683         struct htmlpair  tag;
 684         struct roffsu    su;
 685         int              len;
 686 
 687         switch (n->type) {
 688         case (MDOC_ELEM):
 689                 synopsis_pre(h, n);
 690                 PAIR_CLASS_INIT(&tag, "name");
 691                 print_otag(h, TAG_B, 1, &tag);
 692                 if (NULL == n->child && m->name)
 693                         print_text(h, m->name);
 694                 return(1);
 695         case (MDOC_HEAD):
 696                 print_otag(h, TAG_TD, 0, NULL);
 697                 if (NULL == n->child && m->name)
 698                         print_text(h, m->name);
 699                 return(1);
 700         case (MDOC_BODY):
 701                 print_otag(h, TAG_TD, 0, NULL);
 702                 return(1);
 703         default:
 704                 break;
 705         }
 706 
 707         synopsis_pre(h, n);
 708         PAIR_CLASS_INIT(&tag, "synopsis");
 709         print_otag(h, TAG_TABLE, 1, &tag);
 710 
 711         for (len = 0, n = n->child; n; n = n->next)
 712                 if (MDOC_TEXT == n->type)
 713                         len += html_strlen(n->string);
 714 
 715         if (0 == len && m->name)
 716                 len = html_strlen(m->name);
 717 
 718         SCALE_HS_INIT(&su, (double)len);
 719         bufinit(h);
 720         bufcat_su(h, "width", &su);
 721         PAIR_STYLE_INIT(&tag, h);
 722         print_otag(h, TAG_COL, 1, &tag);
 723         print_otag(h, TAG_COL, 0, NULL);
 724         print_otag(h, TAG_TBODY, 0, NULL);
 725         print_otag(h, TAG_TR, 0, NULL);
 726         return(1);
 727 }
 728 
 729 
 730 /* ARGSUSED */
 731 static int
 732 mdoc_xr_pre(MDOC_ARGS)
 733 {
 734         struct htmlpair  tag[2];
 735 
 736         if (NULL == n->child)


 964                 case (LIST_column):
 965                         print_otag(h, TAG_TR, 1, tag);
 966                         break;
 967                 default:
 968                         break;
 969                 }
 970         }
 971 
 972         return(1);
 973 }
 974 
 975 /* ARGSUSED */
 976 static int
 977 mdoc_bl_pre(MDOC_ARGS)
 978 {
 979         int              i;
 980         struct htmlpair  tag[3];
 981         struct roffsu    su;
 982         char             buf[BUFSIZ];
 983 
 984         bufinit(h);
 985 
 986         if (MDOC_BODY == n->type) {
 987                 if (LIST_column == n->norm->Bl.type)
 988                         print_otag(h, TAG_TBODY, 0, NULL);
 989                 return(1);
 990         }
 991 
 992         if (MDOC_HEAD == n->type) {
 993                 if (LIST_column != n->norm->Bl.type)
 994                         return(0);
 995 
 996                 /*
 997                  * For each column, print out the <COL> tag with our
 998                  * suggested width.  The last column gets min-width, as
 999                  * in terminal mode it auto-sizes to the width of the
1000                  * screen and we want to preserve that behaviour.
1001                  */
1002 
1003                 for (i = 0; i < (int)n->norm->Bl.ncols; i++) {

1004                         a2width(n->norm->Bl.cols[i], &su);
1005                         if (i < (int)n->norm->Bl.ncols - 1)
1006                                 bufcat_su(h, "width", &su);
1007                         else
1008                                 bufcat_su(h, "min-width", &su);
1009                         PAIR_STYLE_INIT(&tag[0], h);
1010                         print_otag(h, TAG_COL, 1, tag);
1011                 }
1012 
1013                 return(0);
1014         }
1015 
1016         SCALE_VS_INIT(&su, 0);

1017         bufcat_su(h, "margin-top", &su);
1018         bufcat_su(h, "margin-bottom", &su);
1019         PAIR_STYLE_INIT(&tag[0], h);
1020 
1021         assert(lists[n->norm->Bl.type]);
1022         strlcpy(buf, "list ", BUFSIZ);
1023         strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
1024         PAIR_INIT(&tag[1], ATTR_CLASS, buf);
1025 
1026         /* Set the block's left-hand margin. */
1027 
1028         if (n->norm->Bl.offs) {
1029                 a2offs(n->norm->Bl.offs, &su);
1030                 bufcat_su(h, "margin-left", &su);
1031         }
1032 
1033         switch (n->norm->Bl.type) {
1034         case(LIST_bullet):
1035                 /* FALLTHROUGH */
1036         case(LIST_dash):


1208         bufinit(h);
1209         bufcat_su(h, "margin-left", &su);
1210         PAIR_STYLE_INIT(&tag[0], h);
1211 
1212         if (DISP_unfilled != n->norm->Bd.type && 
1213                         DISP_literal != n->norm->Bd.type) {
1214                 PAIR_CLASS_INIT(&tag[1], "display");
1215                 print_otag(h, TAG_DIV, 2, tag);
1216                 return(1);
1217         }
1218 
1219         PAIR_CLASS_INIT(&tag[1], "lit display");
1220         print_otag(h, TAG_PRE, 2, tag);
1221 
1222         /* This can be recursive: save & set our literal state. */
1223 
1224         sv = h->flags & HTML_LITERAL;
1225         h->flags |= HTML_LITERAL;
1226 
1227         for (nn = n->child; nn; nn = nn->next) {
1228                 print_mdoc_node(m, nn, h);
1229                 /*
1230                  * If the printed node flushes its own line, then we
1231                  * needn't do it here as well.  This is hacky, but the
1232                  * notion of selective eoln whitespace is pretty dumb
1233                  * anyway, so don't sweat it.
1234                  */
1235                 switch (nn->tok) {
1236                 case (MDOC_Sm):
1237                         /* FALLTHROUGH */
1238                 case (MDOC_br):
1239                         /* FALLTHROUGH */
1240                 case (MDOC_sp):
1241                         /* FALLTHROUGH */
1242                 case (MDOC_Bl):
1243                         /* FALLTHROUGH */
1244                 case (MDOC_D1):
1245                         /* FALLTHROUGH */
1246                 case (MDOC_Dl):
1247                         /* FALLTHROUGH */
1248                 case (MDOC_Lp):


2256                 break;
2257         case (MDOC_Qo):
2258                 /* FALLTHROUGH */
2259         case (MDOC_Qq):
2260                 /* FALLTHROUGH */
2261         case (MDOC_Do):
2262                 /* FALLTHROUGH */
2263         case (MDOC_Dq):
2264                 print_text(h, "\\(rq");
2265                 break;
2266         case (MDOC_Po):
2267                 /* FALLTHROUGH */
2268         case (MDOC_Pq):
2269                 print_text(h, ")");
2270                 break;
2271         case (MDOC_Ql):
2272                 /* FALLTHROUGH */
2273         case (MDOC_So):
2274                 /* FALLTHROUGH */
2275         case (MDOC_Sq):
2276                 print_text(h, "\\(aq");
2277                 break;
2278         default:
2279                 abort();
2280                 /* NOTREACHED */
2281         }
2282 }
2283 
2284 
   1 /*      $Id: mdoc_html.c,v 1.186 2013/12/24 20:45:27 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  *
   5  * Permission to use, copy, modify, and distribute this software for any
   6  * purpose with or without fee is hereby granted, provided that the above
   7  * copyright notice and this permission notice appear in all copies.
   8  *
   9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16  */
  17 #ifdef HAVE_CONFIG_H
  18 #include "config.h"
  19 #endif
  20 
  21 #include <sys/types.h>
  22 
  23 #include <assert.h>
  24 #include <ctype.h>
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #include <unistd.h>
  29 
  30 #include "mandoc.h"
  31 #include "out.h"
  32 #include "html.h"
  33 #include "mdoc.h"
  34 #include "main.h"
  35 
  36 #define INDENT           5
  37 
  38 #define MDOC_ARGS         const struct mdoc_meta *meta, \
  39                           const struct mdoc_node *n, \
  40                           struct html *h
  41 
  42 #ifndef MIN
  43 #define MIN(a,b)        ((/*CONSTCOND*/(a)<(b))?(a):(b))
  44 #endif
  45 
  46 struct  htmlmdoc {
  47         int             (*pre)(MDOC_ARGS);
  48         void            (*post)(MDOC_ARGS);
  49 };
  50 
  51 static  void              print_mdoc(MDOC_ARGS);
  52 static  void              print_mdoc_head(MDOC_ARGS);
  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 mdoc_node *);
  57 
  58 static  void              a2width(const char *, struct roffsu *);


 243         {mdoc__x_pre, mdoc__x_post}, /* %U */ 
 244         {NULL, NULL}, /* Ta */ 
 245 };
 246 
 247 static  const char * const lists[LIST_MAX] = {
 248         NULL,
 249         "list-bul",
 250         "list-col",
 251         "list-dash",
 252         "list-diag",
 253         "list-enum",
 254         "list-hang",
 255         "list-hyph",
 256         "list-inset",
 257         "list-item",
 258         "list-ohang",
 259         "list-tag"
 260 };
 261 
 262 void
 263 html_mdoc(void *arg, const struct mdoc *mdoc)
 264 {
 265 
 266         print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc),
 267                         (struct html *)arg);
 268         putchar('\n');
 269 }
 270 
 271 
 272 /*
 273  * Calculate the scaling unit passed in a `-width' argument.  This uses
 274  * either a native scaling unit (e.g., 1i, 2m) or the string length of
 275  * the value.
 276  */
 277 static void
 278 a2width(const char *p, struct roffsu *su)
 279 {
 280 
 281         if ( ! a2roffsu(p, su, SCALE_MAX)) {
 282                 su->unit = SCALE_BU;
 283                 su->scale = html_strlen(p);
 284         }
 285 }
 286 
 287 


 345                 SCALE_HS_INIT(su, INDENT);
 346         else if (0 == strcmp(p, "indent-two"))
 347                 SCALE_HS_INIT(su, INDENT * 2);
 348         else if ( ! a2roffsu(p, su, SCALE_MAX))
 349                 SCALE_HS_INIT(su, html_strlen(p));
 350 }
 351 
 352 
 353 static void
 354 print_mdoc(MDOC_ARGS)
 355 {
 356         struct tag      *t, *tt;
 357         struct htmlpair  tag;
 358 
 359         PAIR_CLASS_INIT(&tag, "mandoc");
 360 
 361         if ( ! (HTML_FRAGMENT & h->oflags)) {
 362                 print_gen_decls(h);
 363                 t = print_otag(h, TAG_HTML, 0, NULL);
 364                 tt = print_otag(h, TAG_HEAD, 0, NULL);
 365                 print_mdoc_head(meta, n, h);
 366                 print_tagq(h, tt);
 367                 print_otag(h, TAG_BODY, 0, NULL);
 368                 print_otag(h, TAG_DIV, 1, &tag);
 369         } else 
 370                 t = print_otag(h, TAG_DIV, 1, &tag);
 371 
 372         print_mdoc_nodelist(meta, n, h);
 373         print_tagq(h, t);
 374 }
 375 
 376 
 377 /* ARGSUSED */
 378 static void
 379 print_mdoc_head(MDOC_ARGS)
 380 {
 381 
 382         print_gen_head(h);
 383         bufinit(h);
 384         bufcat_fmt(h, "%s(%s)", meta->title, meta->msec);
 385 
 386         if (meta->arch)
 387                 bufcat_fmt(h, " (%s)", meta->arch);
 388 
 389         print_otag(h, TAG_TITLE, 0, NULL);
 390         print_text(h, h->buf);
 391 }
 392 
 393 
 394 static void
 395 print_mdoc_nodelist(MDOC_ARGS)
 396 {
 397 
 398         print_mdoc_node(meta, n, h);
 399         if (n->next)
 400                 print_mdoc_nodelist(meta, n->next, h);
 401 }
 402 
 403 
 404 static void
 405 print_mdoc_node(MDOC_ARGS)
 406 {
 407         int              child;
 408         struct tag      *t;
 409 
 410         child = 1;
 411         t = h->tags.head;
 412 
 413         switch (n->type) {
 414         case (MDOC_ROOT):
 415                 child = mdoc_root_pre(meta, n, h);
 416                 break;
 417         case (MDOC_TEXT):
 418                 /* No tables in this mode... */
 419                 assert(NULL == h->tblt);
 420 
 421                 /*
 422                  * Make sure that if we're in a literal mode already
 423                  * (i.e., within a <PRE>) don't print the newline.
 424                  */
 425                 if (' ' == *n->string && MDOC_LINE & n->flags)
 426                         if ( ! (HTML_LITERAL & h->flags))
 427                                 print_otag(h, TAG_BR, 0, NULL);
 428                 if (MDOC_DELIMC & n->flags)
 429                         h->flags |= HTML_NOSPACE;
 430                 print_text(h, n->string);
 431                 if (MDOC_DELIMO & n->flags)
 432                         h->flags |= HTML_NOSPACE;
 433                 return;
 434         case (MDOC_EQN):
 435                 print_eqn(h, n->eqn);


 438                 /*
 439                  * This will take care of initialising all of the table
 440                  * state data for the first table, then tearing it down
 441                  * for the last one.
 442                  */
 443                 print_tbl(h, n->span);
 444                 return;
 445         default:
 446                 /*
 447                  * Close out the current table, if it's open, and unset
 448                  * the "meta" table state.  This will be reopened on the
 449                  * next table element.
 450                  */
 451                 if (h->tblt) {
 452                         print_tblclose(h);
 453                         t = h->tags.head;
 454                 }
 455 
 456                 assert(NULL == h->tblt);
 457                 if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
 458                         child = (*mdocs[n->tok].pre)(meta, n, h);
 459                 break;
 460         }
 461 
 462         if (HTML_KEEP & h->flags) {
 463                 if (n->prev ? (n->prev->lastline != n->line) :
 464                     (n->parent && n->parent->line != n->line)) {
 465                         h->flags &= ~HTML_KEEP;
 466                         h->flags |= HTML_PREKEEP;




 467                 }
 468         }

 469 
 470         if (child && n->child)
 471                 print_mdoc_nodelist(meta, n->child, h);
 472 
 473         print_stagq(h, t);
 474 
 475         switch (n->type) {
 476         case (MDOC_ROOT):
 477                 mdoc_root_post(meta, n, h);
 478                 break;
 479         case (MDOC_EQN):
 480                 break;
 481         default:
 482                 if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
 483                         (*mdocs[n->tok].post)(meta, n, h);
 484                 break;
 485         }
 486 }
 487 
 488 /* ARGSUSED */
 489 static void
 490 mdoc_root_post(MDOC_ARGS)
 491 {
 492         struct htmlpair  tag[3];
 493         struct tag      *t, *tt;
 494 
 495         PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
 496         PAIR_CLASS_INIT(&tag[1], "foot");
 497         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
 498         t = print_otag(h, TAG_TABLE, 3, tag);
 499         PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
 500         print_otag(h, TAG_COL, 1, tag);
 501         print_otag(h, TAG_COL, 1, tag);
 502 
 503         print_otag(h, TAG_TBODY, 0, NULL);
 504 
 505         tt = print_otag(h, TAG_TR, 0, NULL);
 506 
 507         PAIR_CLASS_INIT(&tag[0], "foot-date");
 508         print_otag(h, TAG_TD, 1, tag);
 509         print_text(h, meta->date);
 510         print_stagq(h, tt);
 511 
 512         PAIR_CLASS_INIT(&tag[0], "foot-os");
 513         PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
 514         print_otag(h, TAG_TD, 2, tag);
 515         print_text(h, meta->os);
 516         print_tagq(h, t);
 517 }
 518 
 519 
 520 /* ARGSUSED */
 521 static int
 522 mdoc_root_pre(MDOC_ARGS)
 523 {
 524         struct htmlpair  tag[3];
 525         struct tag      *t, *tt;
 526         char             b[BUFSIZ], title[BUFSIZ];
 527 
 528         strlcpy(b, meta->vol, BUFSIZ);
 529 
 530         if (meta->arch) {
 531                 strlcat(b, " (", BUFSIZ);
 532                 strlcat(b, meta->arch, BUFSIZ);
 533                 strlcat(b, ")", BUFSIZ);
 534         }
 535 
 536         snprintf(title, BUFSIZ - 1, "%s(%s)", meta->title, meta->msec);
 537 
 538         PAIR_SUMMARY_INIT(&tag[0], "Document Header");
 539         PAIR_CLASS_INIT(&tag[1], "head");
 540         PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
 541         t = print_otag(h, TAG_TABLE, 3, tag);
 542         PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
 543         print_otag(h, TAG_COL, 1, tag);
 544         print_otag(h, TAG_COL, 1, tag);
 545         print_otag(h, TAG_COL, 1, tag);
 546 
 547         print_otag(h, TAG_TBODY, 0, NULL);
 548 
 549         tt = print_otag(h, TAG_TR, 0, NULL);
 550 
 551         PAIR_CLASS_INIT(&tag[0], "head-ltitle");
 552         print_otag(h, TAG_TD, 1, tag);
 553         print_text(h, title);
 554         print_stagq(h, tt);
 555 
 556         PAIR_CLASS_INIT(&tag[0], "head-vol");


 669 
 670         print_text(h, "\\(em");
 671         PAIR_CLASS_INIT(&tag, "desc");
 672         print_otag(h, TAG_SPAN, 1, &tag);
 673         return(1);
 674 }
 675 
 676 
 677 static int
 678 mdoc_nm_pre(MDOC_ARGS)
 679 {
 680         struct htmlpair  tag;
 681         struct roffsu    su;
 682         int              len;
 683 
 684         switch (n->type) {
 685         case (MDOC_ELEM):
 686                 synopsis_pre(h, n);
 687                 PAIR_CLASS_INIT(&tag, "name");
 688                 print_otag(h, TAG_B, 1, &tag);
 689                 if (NULL == n->child && meta->name)
 690                         print_text(h, meta->name);
 691                 return(1);
 692         case (MDOC_HEAD):
 693                 print_otag(h, TAG_TD, 0, NULL);
 694                 if (NULL == n->child && meta->name)
 695                         print_text(h, meta->name);
 696                 return(1);
 697         case (MDOC_BODY):
 698                 print_otag(h, TAG_TD, 0, NULL);
 699                 return(1);
 700         default:
 701                 break;
 702         }
 703 
 704         synopsis_pre(h, n);
 705         PAIR_CLASS_INIT(&tag, "synopsis");
 706         print_otag(h, TAG_TABLE, 1, &tag);
 707 
 708         for (len = 0, n = n->child; n; n = n->next)
 709                 if (MDOC_TEXT == n->type)
 710                         len += html_strlen(n->string);
 711 
 712         if (0 == len && meta->name)
 713                 len = html_strlen(meta->name);
 714 
 715         SCALE_HS_INIT(&su, (double)len);
 716         bufinit(h);
 717         bufcat_su(h, "width", &su);
 718         PAIR_STYLE_INIT(&tag, h);
 719         print_otag(h, TAG_COL, 1, &tag);
 720         print_otag(h, TAG_COL, 0, NULL);
 721         print_otag(h, TAG_TBODY, 0, NULL);
 722         print_otag(h, TAG_TR, 0, NULL);
 723         return(1);
 724 }
 725 
 726 
 727 /* ARGSUSED */
 728 static int
 729 mdoc_xr_pre(MDOC_ARGS)
 730 {
 731         struct htmlpair  tag[2];
 732 
 733         if (NULL == n->child)


 961                 case (LIST_column):
 962                         print_otag(h, TAG_TR, 1, tag);
 963                         break;
 964                 default:
 965                         break;
 966                 }
 967         }
 968 
 969         return(1);
 970 }
 971 
 972 /* ARGSUSED */
 973 static int
 974 mdoc_bl_pre(MDOC_ARGS)
 975 {
 976         int              i;
 977         struct htmlpair  tag[3];
 978         struct roffsu    su;
 979         char             buf[BUFSIZ];
 980 


 981         if (MDOC_BODY == n->type) {
 982                 if (LIST_column == n->norm->Bl.type)
 983                         print_otag(h, TAG_TBODY, 0, NULL);
 984                 return(1);
 985         }
 986 
 987         if (MDOC_HEAD == n->type) {
 988                 if (LIST_column != n->norm->Bl.type)
 989                         return(0);
 990 
 991                 /*
 992                  * For each column, print out the <COL> tag with our
 993                  * suggested width.  The last column gets min-width, as
 994                  * in terminal mode it auto-sizes to the width of the
 995                  * screen and we want to preserve that behaviour.
 996                  */
 997 
 998                 for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
 999                         bufinit(h);
1000                         a2width(n->norm->Bl.cols[i], &su);
1001                         if (i < (int)n->norm->Bl.ncols - 1)
1002                                 bufcat_su(h, "width", &su);
1003                         else
1004                                 bufcat_su(h, "min-width", &su);
1005                         PAIR_STYLE_INIT(&tag[0], h);
1006                         print_otag(h, TAG_COL, 1, tag);
1007                 }
1008 
1009                 return(0);
1010         }
1011 
1012         SCALE_VS_INIT(&su, 0);
1013         bufinit(h);
1014         bufcat_su(h, "margin-top", &su);
1015         bufcat_su(h, "margin-bottom", &su);
1016         PAIR_STYLE_INIT(&tag[0], h);
1017 
1018         assert(lists[n->norm->Bl.type]);
1019         strlcpy(buf, "list ", BUFSIZ);
1020         strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
1021         PAIR_INIT(&tag[1], ATTR_CLASS, buf);
1022 
1023         /* Set the block's left-hand margin. */
1024 
1025         if (n->norm->Bl.offs) {
1026                 a2offs(n->norm->Bl.offs, &su);
1027                 bufcat_su(h, "margin-left", &su);
1028         }
1029 
1030         switch (n->norm->Bl.type) {
1031         case(LIST_bullet):
1032                 /* FALLTHROUGH */
1033         case(LIST_dash):


1205         bufinit(h);
1206         bufcat_su(h, "margin-left", &su);
1207         PAIR_STYLE_INIT(&tag[0], h);
1208 
1209         if (DISP_unfilled != n->norm->Bd.type && 
1210                         DISP_literal != n->norm->Bd.type) {
1211                 PAIR_CLASS_INIT(&tag[1], "display");
1212                 print_otag(h, TAG_DIV, 2, tag);
1213                 return(1);
1214         }
1215 
1216         PAIR_CLASS_INIT(&tag[1], "lit display");
1217         print_otag(h, TAG_PRE, 2, tag);
1218 
1219         /* This can be recursive: save & set our literal state. */
1220 
1221         sv = h->flags & HTML_LITERAL;
1222         h->flags |= HTML_LITERAL;
1223 
1224         for (nn = n->child; nn; nn = nn->next) {
1225                 print_mdoc_node(meta, nn, h);
1226                 /*
1227                  * If the printed node flushes its own line, then we
1228                  * needn't do it here as well.  This is hacky, but the
1229                  * notion of selective eoln whitespace is pretty dumb
1230                  * anyway, so don't sweat it.
1231                  */
1232                 switch (nn->tok) {
1233                 case (MDOC_Sm):
1234                         /* FALLTHROUGH */
1235                 case (MDOC_br):
1236                         /* FALLTHROUGH */
1237                 case (MDOC_sp):
1238                         /* FALLTHROUGH */
1239                 case (MDOC_Bl):
1240                         /* FALLTHROUGH */
1241                 case (MDOC_D1):
1242                         /* FALLTHROUGH */
1243                 case (MDOC_Dl):
1244                         /* FALLTHROUGH */
1245                 case (MDOC_Lp):


2253                 break;
2254         case (MDOC_Qo):
2255                 /* FALLTHROUGH */
2256         case (MDOC_Qq):
2257                 /* FALLTHROUGH */
2258         case (MDOC_Do):
2259                 /* FALLTHROUGH */
2260         case (MDOC_Dq):
2261                 print_text(h, "\\(rq");
2262                 break;
2263         case (MDOC_Po):
2264                 /* FALLTHROUGH */
2265         case (MDOC_Pq):
2266                 print_text(h, ")");
2267                 break;
2268         case (MDOC_Ql):
2269                 /* FALLTHROUGH */
2270         case (MDOC_So):
2271                 /* FALLTHROUGH */
2272         case (MDOC_Sq):
2273                 print_text(h, "\\(cq");
2274                 break;
2275         default:
2276                 abort();
2277                 /* NOTREACHED */
2278         }
2279 }
2280 
2281