Logo Search packages:      
Sourcecode: bash version File versions

man2html.c

/*
 * This program was written by Richard Verhoeven (NL:5482ZX35)
 * at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
 *
 * Permission is granted to distribute, modify and use this program as long
 * as this comment is not removed or changed.
 *
 * THIS IS A MODIFIED VERSION.  IT WAS MODIFIED BY chet@po.cwru.edu FOR
 * USE BY BASH.
 */

/*
 * man2html will add links to the converted manpages. The function add_links
 * is used for that. At the moment it will add links as follows, where
 *     indicates what should match to start with:
 * ^^^
 * Recognition           Item            Link
 * ----------------------------------------------------------
 * name(*)               Manpage         ../man?/name.*
 *     ^
 * name@hostname         Email address   mailto:name@hostname
 *     ^
 * method://string       URL             method://string
 *       ^^^
 * www.host.name         WWW server      http://www.host.name
 * ^^^^
 * ftp.host.name         FTP server      ftp://ftp.host.name
 * ^^^^
 * <file.h>              Include file    file:/usr/include/file.h
 *      ^^^
 *
 * Since man2html does not check if manpages, hosts or email addresses exist,
 * some links might not work. For manpages, some extra checks are performed
 * to make sure not every () pair creates a link. Also out of date pages
 * might point to incorrect places.
 *
 * The program will not allow users to get system specific files, such as
 * /etc/passwd. It will check that "man" is part of the specified file and
 * that  "/../" isn't. Even if someone manages to get such file, man2html will
 * handle it like a manpage and will usually not produce any output (or crash).
 *
 * If you find any bugs when normal manpages are converted, please report
 * them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle
 * the manpage correct.
 *
 * Known bugs and missing features:
 *
 *  * Equations are not converted at all.
 *  * Tables are converted but some features are not possible in html.
 *  * The tabbing environment is converted by counting characters and adding
 *    spaces. This might go wrong (outside <PRE>)
 *  * Some pages look beter if man2html works in troff mode, especially pages
 *    with tables. You can deside at compile time which made you want to use.
 *
 *    -DNROFF=0     troff mode
 *    -DNROFF=1     nroff mode   (default)
 *
 *    if you install both modes, you should compile with the correct CGIBASE.
 *  * Some manpages rely on the fact that troff/nroff is used to convert
 *    them and use features which are not descripted in the man manpages.
 *    (definitions, calculations, conditionals, requests). I can't guarantee
 *    that all these features work on all manpages. (I didn't have the
 *    time to look through all the available manpages.)
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define NROFF 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <ctype.h>
#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>

#define NULL_TERMINATED(n) ((n) + 1)

#define HUGE_STR_MAX  10000
#define LARGE_STR_MAX 2000
#define MED_STR_MAX   500
#define SMALL_STR_MAX 100
#define TINY_STR_MAX  10

#define MAX_MAN_PATHS 100     /* Max number of directories */
#define MAX_ZCATS     10      /* Max number of zcat style programs */
#define MAX_WORDLIST  100

#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#ifndef EXIT_USAGE
#define EXIT_USAGE 2
#endif

static char location_base[NULL_TERMINATED(MED_STR_MAX)] = "";

char   *signature = "<HR>\nThis document was created by man2html from %s.<BR>\nTime: %s\n";

/* timeformat for signature */
#define TIMEFORMAT "%d %B %Y %T %Z"

char *manpage;

/* BSD mandoc Bl/El lists to HTML list types */
#define BL_DESC_LIST   1
#define BL_BULLET_LIST 2
#define BL_ENUM_LIST   4

/* BSD mandoc Bd/Ed example(?) blocks */
#define BD_LITERAL  1
#define BD_INDENT   2

#ifndef HAVE_STRERROR
static char *
strerror(int e)
{
      static char emsg[40];

#if defined (HAVE_SYS_ERRLIST)
      extern int sys_nerr;
      extern char *sys_errlist[];

      if (e > 0 && e < sys_nerr)
            return (sys_errlist[e]);
      else
#endif                        /* HAVE_SYS_ERRLIST */
      {
            sprintf(emsg, "Unknown system error %d", e);
            return (&emsg[0]);
      }
}
#endif                        /* !HAVE_STRERROR */

static char *
strgrow(char *old, int len)
{
      char   *new = realloc(old, (strlen(old) + len + 1) * sizeof(char));

      if (!new) {
            fprintf(stderr, "man2html: out of memory");
            exit(EXIT_FAILURE);
      }
      return new;
}

static char *
stralloc(int len)
{
      /* allocate enough for len + NULL */
      char   *new = malloc((len + 1) * sizeof(char));

      if (!new) {
            fprintf(stderr, "man2html: out of memory");
            exit(EXIT_FAILURE);
      }
      return new;
}

/*
 * Some systems don't have strdup so lets use our own - which can also
 * check for out of memory.
 */
static char *
strduplicate(char *from)
{
      char   *new = stralloc(strlen(from));

      strcpy(new, from);
      return new;
}

/* Assumes space for n plus a null */
static char *
strmaxcpy(char *to, char *from, int n)
{
      int     len = strlen(from);

      strncpy(to, from, n);
      to[(len <= n) ? len : n] = '\0';
      return to;
}

static char *
strmaxcat(char *to, char *from, int n)
{
      int     to_len = strlen(to);

      if (to_len < n) {
            int     from_len = strlen(from);
            int     cp = (to_len + from_len <= n) ? from_len : n - to_len;

            strncpy(to + to_len, from, cp);
            to[to_len + cp] = '\0';
      }
      return to;
}

/* Assumes space for limit plus a null */
static char *
strlimitcpy(char *to, char *from, int n, int limit)
{
      int     len = n > limit ? limit : n;

      strmaxcpy(to, from, len);
      to[len] = '\0';
      return to;
}

/*
 * takes string and escapes all metacharacters.  should be used before
 * including string in system() or similar call.
 */
static char *
escape_input(char *str)
{
      int     i, j = 0;
      static char new[NULL_TERMINATED(MED_STR_MAX)];

      if (strlen(str) * 2 + 1 > MED_STR_MAX) {
            fprintf(stderr,
                  "man2html: escape_input - str too long:\n%-80s...\n",
                  str);
            exit(EXIT_FAILURE);
      }
      for (i = 0; i < strlen(str); i++) {
            if (!(((str[i] >= 'A') && (str[i] <= 'Z')) ||
                  ((str[i] >= 'a') && (str[i] <= 'z')) ||
                  ((str[i] >= '0') && (str[i] <= '9')))) {
                  new[j] = '\\';
                  j++;
            }
            new[j] = str[i];
            j++;
      }
      new[j] = '\0';
      return new;
}

static void
usage(void)
{
      fprintf(stderr, "man2html: usage: man2html filename\n");
}



/*
 * below this you should not change anything unless you know a lot
 * about this program or about troff.
 */

typedef struct STRDEF STRDEF;
struct STRDEF {
      int     nr, slen;
      char   *st;
      STRDEF *next;
};

typedef struct INTDEF INTDEF;
struct INTDEF {
      int     nr;
      int     val;
      int     incr;
      INTDEF *next;
};

static char NEWLINE[2] = "\n";
static char idxlabel[6] = "ixAAA";

#define INDEXFILE "/tmp/manindex.list"

static char *fname;
static FILE *idxfile;

static STRDEF *chardef, *strdef, *defdef;
static INTDEF *intdef;

#define V(A,B) ((A)*256+(B))

static INTDEF standardint[] = {
      {V('n', ' '), NROFF, 0, NULL},
      {V('t', ' '), 1 - NROFF, 0, NULL},
      {V('o', ' '), 1, 0, NULL},
      {V('e', ' '), 0, 0, NULL},
      {V('.', 'l'), 70, 0, NULL},
      {V('.', '$'), 0, 0, NULL},
      {V('.', 'A'), NROFF, 0, NULL},
      {V('.', 'T'), 1 - NROFF, 0, NULL},
      {V('.', 'V'), 1, 0, NULL},    /* the me package tests for this */
{0, 0, 0, NULL}};

static STRDEF standardstring[] = {
      {V('R', ' '), 1, "&#174;", NULL},
      {V('l', 'q'), 2, "``", NULL},
      {V('r', 'q'), 2, "''", NULL},
      {0, 0, NULL, NULL}
};


static STRDEF standardchar[] = {
      {V('*', '*'), 1, "*", NULL},
      {V('*', 'A'), 1, "A", NULL},
      {V('*', 'B'), 1, "B", NULL},
      {V('*', 'C'), 2, "Xi", NULL},
      {V('*', 'D'), 5, "Delta", NULL},
      {V('*', 'E'), 1, "E", NULL},
      {V('*', 'F'), 3, "Phi", NULL},
      {V('*', 'G'), 5, "Gamma", NULL},
      {V('*', 'H'), 5, "Theta", NULL},
      {V('*', 'I'), 1, "I", NULL},
      {V('*', 'K'), 1, "K", NULL},
      {V('*', 'L'), 6, "Lambda", NULL},
      {V('*', 'M'), 1, "M", NULL},
      {V('*', 'N'), 1, "N", NULL},
      {V('*', 'O'), 1, "O", NULL},
      {V('*', 'P'), 2, "Pi", NULL},
      {V('*', 'Q'), 3, "Psi", NULL},
      {V('*', 'R'), 1, "P", NULL},
      {V('*', 'S'), 5, "Sigma", NULL},
      {V('*', 'T'), 1, "T", NULL},
      {V('*', 'U'), 1, "Y", NULL},
      {V('*', 'W'), 5, "Omega", NULL},
      {V('*', 'X'), 1, "X", NULL},
      {V('*', 'Y'), 1, "H", NULL},
      {V('*', 'Z'), 1, "Z", NULL},
      {V('*', 'a'), 5, "alpha", NULL},
      {V('*', 'b'), 4, "beta", NULL},
      {V('*', 'c'), 2, "xi", NULL},
      {V('*', 'd'), 5, "delta", NULL},
      {V('*', 'e'), 7, "epsilon", NULL},
      {V('*', 'f'), 3, "phi", NULL},
      {V('*', 'g'), 5, "gamma", NULL},
      {V('*', 'h'), 5, "theta", NULL},
      {V('*', 'i'), 4, "iota", NULL},
      {V('*', 'k'), 5, "kappa", NULL},
      {V('*', 'l'), 6, "lambda", NULL},
      {V('*', 'm'), 1, "&#181;", NULL},
      {V('*', 'n'), 2, "nu", NULL},
      {V('*', 'o'), 1, "o", NULL},
      {V('*', 'p'), 2, "pi", NULL},
      {V('*', 'q'), 3, "psi", NULL},
      {V('*', 'r'), 3, "rho", NULL},
      {V('*', 's'), 5, "sigma", NULL},
      {V('*', 't'), 3, "tau", NULL},
      {V('*', 'u'), 7, "upsilon", NULL},
      {V('*', 'w'), 5, "omega", NULL},
      {V('*', 'x'), 3, "chi", NULL},
      {V('*', 'y'), 3, "eta", NULL},
      {V('*', 'z'), 4, "zeta", NULL},
      {V('t', 's'), 5, "sigma", NULL},
      {V('+', '-'), 1, "&#177;", NULL},
      {V('1', '2'), 1, "&#189;", NULL},
      {V('1', '4'), 1, "&#188;", NULL},
      {V('3', '4'), 1, "&#190;", NULL},
      {V('F', 'i'), 3, "ffi", NULL},
      {V('F', 'l'), 3, "ffl", NULL},
      {V('a', 'a'), 1, "&#180;", NULL},
      {V('a', 'p'), 1, "~", NULL},
      {V('b', 'r'), 1, "|", NULL},
      {V('b', 'u'), 1, "*", NULL},
      {V('b', 'v'), 1, "|", NULL},
      {V('c', 'i'), 1, "o", NULL},
      {V('c', 'o'), 1, "&#169;", NULL},
      {V('c', 't'), 1, "&#162;", NULL},
      {V('d', 'e'), 1, "&#176;", NULL},
      {V('d', 'g'), 1, "+", NULL},
      {V('d', 'i'), 1, "&#247;", NULL},
      {V('e', 'm'), 1, "-", NULL},
      {V('e', 'm'), 3, "---", NULL},
      {V('e', 'q'), 1, "=", NULL},
      {V('e', 's'), 1, "&#216;", NULL},
      {V('f', 'f'), 2, "ff", NULL},
      {V('f', 'i'), 2, "fi", NULL},
      {V('f', 'l'), 2, "fl", NULL},
      {V('f', 'm'), 1, "&#180;", NULL},
      {V('g', 'a'), 1, "`", NULL},
      {V('h', 'y'), 1, "-", NULL},
      {V('l', 'c'), 2, "|&#175;", NULL},
      {V('l', 'f'), 2, "|_", NULL},
      {V('l', 'k'), 1, "<FONT SIZE=+2>{</FONT>", NULL},
      {V('m', 'i'), 1, "-", NULL},
      {V('m', 'u'), 1, "&#215;", NULL},
      {V('n', 'o'), 1, "&#172;", NULL},
      {V('o', 'r'), 1, "|", NULL},
      {V('p', 'l'), 1, "+", NULL},
      {V('r', 'c'), 2, "&#175;|", NULL},
      {V('r', 'f'), 2, "_|", NULL},
      {V('r', 'g'), 1, "&#174;", NULL},
      {V('r', 'k'), 1, "<FONT SIZE=+2>}</FONT>", NULL},
      {V('r', 'n'), 1, "&#175;", NULL},
      {V('r', 'u'), 1, "_", NULL},
      {V('s', 'c'), 1, "&#167;", NULL},
      {V('s', 'l'), 1, "/", NULL},
      {V('s', 'q'), 2, "[]", NULL},
      {V('u', 'l'), 1, "_", NULL},
      {0, 0, NULL, NULL}
};

/* default: print code */


static char eqndelimopen = 0, eqndelimclose = 0;
static char escapesym = '\\', nobreaksym = '\'', controlsym = '.', fieldsym = 0, padsym = 0;

static char *buffer = NULL;
static int buffpos = 0, buffmax = 0;
static int scaninbuff = 0;
static int itemdepth = 0;
static int dl_set[20] = {0};
static int still_dd = 0;
static int tabstops[20] = {8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96};
static int maxtstop = 12;
static int curpos = 0;

static char *scan_troff(char *c, int san, char **result);
static char *scan_troff_mandoc(char *c, int san, char **result);

static char **argument = NULL;

static char charb[TINY_STR_MAX];

static void
print_sig(void)
{
      char    datbuf[NULL_TERMINATED(MED_STR_MAX)];
      struct tm *timetm;
      time_t  clock;

      datbuf[0] = '\0';
      clock = time(NULL);
      timetm = localtime(&clock);
      strftime(datbuf, MED_STR_MAX, TIMEFORMAT, timetm);
      printf(signature, manpage, datbuf);
}

static char *
expand_char(int nr)
{
      STRDEF *h;

      h = chardef;
      if (!nr)
            return NULL;
      while (h)
            if (h->nr == nr) {
                  curpos += h->slen;
                  return h->st;
            } else
                  h = h->next;
      charb[0] = nr / 256;
      charb[1] = nr % 256;
      charb[2] = '\0';
      if (charb[0] == '<') {  /* Fix up <= */
            charb[4] = charb[1];
            strncpy(charb, "&lt;", 4);
            charb[5] = '\0';
      }
      curpos += 2;
      return charb;
}

static char *
expand_string(int nr)
{
      STRDEF *h = strdef;

      if (!nr)
            return NULL;
      while (h)
            if (h->nr == nr) {
                  curpos += h->slen;
                  return h->st;
            } else
                  h = h->next;
      return NULL;
}

static char *
read_man_page(char *filename)
{
      char   *man_buf = NULL;
      int     i;
      FILE   *man_stream = NULL;
      struct stat stbuf;
      int     buf_size;

      if (stat(filename, &stbuf) == -1)
            return NULL;

      buf_size = stbuf.st_size;
      man_buf = stralloc(buf_size + 5);
      man_stream = fopen(filename, "r");
      if (man_stream) {
            man_buf[0] = '\n';
            if (fread(man_buf + 1, 1, buf_size, man_stream) == buf_size) {
                  man_buf[buf_size] = '\n';
                  man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
            } else {
                  man_buf = NULL;
            }
            fclose(man_stream);
      }
      return man_buf;
}


static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
static int obp = 0;
static int no_newline_output = 0;
static int newline_for_fun = 0;
static int output_possible = 0;
static int out_length = 0;

/*
 * Add the links to the output. At the moment the following are
 * recognized:
 * 
#if 0
 *    name(*)                 -> ../man?/name.*
#endif
 *    method://string         -> method://string
 *    www.host.name           -> http://www.host.name
 *    ftp.host.name           -> ftp://ftp.host.name
 *    name@host               -> mailto:name@host
 *    <name.h>                -> file:/usr/include/name.h   (guess)
 * 
 * Other possible links to add in the future:
 * 
 * /dir/dir/file  -> file:/dir/dir/file
 */
static void
add_links(char *c)
{
      int     i, j, nr;
      char   *f, *g, *h;
      char   *idtest[6];      /* url, mailto, www, ftp, manpage */

      out_length += strlen(c);
      /* search for (section) */
      nr = 0;
      idtest[0] = strstr(c + 1, "://");
      idtest[1] = strchr(c + 1, '@');
      idtest[2] = strstr(c, "www.");
      idtest[3] = strstr(c, "ftp.");
#if 0
      idtest[4] = strchr(c + 1, '(');
#else
      idtest[4] = 0;
#endif
      idtest[5] = strstr(c + 1, ".h&gt;");
      for (i = 0; i < 6; i++)
            nr += (idtest[i] != NULL);
      while (nr) {
            j = -1;
            for (i = 0; i < 6; i++)
                  if (idtest[i] && (j < 0 || idtest[i] < idtest[j]))
                        j = i;
            switch (j) {
            case 5:     /* <name.h> */
                  f = idtest[5];
                  h = f + 2;
                  g = f;
                  while (g > c && g[-1] != ';')
                        g--;
                  if (g != c) {
                        char    t;

                        t = *g;
                        *g = '\0';
                        fputs(c, stdout);
                        *g = t;
                        *h = '\0';
                        printf("<A HREF=\"file:/usr/include/%s\">%s</A>&gt;", g, g);
                        c = f + 6;
                  } else {
                        f[5] = '\0';
                        fputs(c, stdout);
                        f[5] = ';';
                        c = f + 5;
                  }
                  break;
            case 4:     /* manpage */
#if 0
                  f = idtest[j];
                  /* check section */
                  g = strchr(f, ')');
                  if (g && f - g < 6 && (isalnum(f[-1]) || f[-1] == '>') &&
                      ((isdigit(f[1]) && f[1] != '0' &&
                        (f[2] == ')' || (isalpha(f[2]) && f[3] == ')') || f[2] == 'X')) ||
                     (f[2] == ')' && (f[1] == 'n' || f[1] == 'l')))) {
                        /* this might be a link */
                        h = f - 1;
                        /* skip html makeup */
                        while (h > c && *h == '>') {
                              while (h != c && *h != '<')
                                    h--;
                              if (h != c)
                                    h--;
                        }
                        if (isalnum(*h)) {
                              char    t, sec, subsec, *e;

                              e = h + 1;
                              sec = f[1];
                              subsec = f[2];
                              if ((subsec == 'X' && f[3] != ')') || subsec == ')')
                                    subsec = '\0';
                              while (h > c && (isalnum(h[-1]) || h[-1] == '_' ||
                                    h[-1] == '-' || h[-1] == '.'))
                                    h--;
                              t = *h;
                              *h = '\0';
                              fputs(c, stdout);
                              *h = t;
                              t = *e;
                              *e = '\0';
                              if (subsec)
                                    printf("<A HREF=\""
                                           CGIBASE
                                      "?man%c/%s.%c%c\">%s</A>",
                                           sec, h, sec, tolower(subsec), h);
                              else
                                    printf("<A HREF=\""
                                           CGIBASE
                                        "?man%c/%s.%c\">%s</A>",
                                           sec, h, sec, h);
                              *e = t;
                              c = e;
                        }
                  }
                  *f = '\0';
                  fputs(c, stdout);
                  *f = '(';
                  idtest[4] = f - 1;
                  c = f;
#endif
                  break;      /* manpage */
            case 3:     /* ftp */
            case 2:     /* www */
                  g = f = idtest[j];
                  while (*g && (isalnum(*g) || *g == '_' || *g == '-' || *g == '+' ||
                              *g == '.'))
                        g++;
                  if (g[-1] == '.')
                        g--;
                  if (g - f > 4) {
                        char    t;

                        t = *f;
                        *f = '\0';
                        fputs(c, stdout);
                        *f = t;
                        t = *g;
                        *g = '\0';
                        printf("<A HREF=\"%s://%s\">%s</A>", (j == 3 ? "ftp" : "http"),
                               f, f);
                        *g = t;
                        c = g;
                  } else {
                        f[3] = '\0';
                        fputs(c, stdout);
                        c = f + 3;
                        f[3] = '.';
                  }
                  break;
            case 1:     /* mailto */
                  g = f = idtest[1];
                  while (g > c && (isalnum(g[-1]) || g[-1] == '_' || g[-1] == '-' ||
                        g[-1] == '+' || g[-1] == '.' || g[-1] == '%'))
                        g--;
                  h = f + 1;
                  while (*h && (isalnum(*h) || *h == '_' || *h == '-' || *h == '+' ||
                              *h == '.'))
                        h++;
                  if (*h == '.')
                        h--;
                  if (h - f > 4 && f - g > 1) {
                        char    t;

                        t = *g;
                        *g = '\0';
                        fputs(c, stdout);
                        *g = t;
                        t = *h;
                        *h = '\0';
                        printf("<A HREF=\"mailto:%s\">%s</A>", g, g);
                        *h = t;
                        c = h;
                  } else {
                        *f = '\0';
                        fputs(c, stdout);
                        *f = '@';
                        idtest[1] = c;
                        c = f;
                  }
                  break;
            case 0:     /* url */
                  g = f = idtest[0];
                  while (g > c && isalpha(g[-1]) && islower(g[-1]))
                        g--;
                  h = f + 3;
                  while (*h && !isspace(*h) && *h != '<' && *h != '>' && *h != '"' &&
                         *h != '&')
                        h++;
                  if (f - g > 2 && f - g < 7 && h - f > 3) {
                        char    t;

                        t = *g;
                        *g = '\0';
                        fputs(c, stdout);
                        *g = t;
                        t = *h;
                        *h = '\0';
                        printf("<A HREF=\"%s\">%s</A>", g, g);
                        *h = t;
                        c = h;
                  } else {
                        f[1] = '\0';
                        fputs(c, stdout);
                        f[1] = '/';
                        c = f + 1;
                  }
                  break;
            default:
                  break;
            }
            nr = 0;
            if (idtest[0] && idtest[0] < c)
                  idtest[0] = strstr(c + 1, "://");
            if (idtest[1] && idtest[1] < c)
                  idtest[1] = strchr(c + 1, '@');
            if (idtest[2] && idtest[2] < c)
                  idtest[2] = strstr(c, "www.");
            if (idtest[3] && idtest[3] < c)
                  idtest[3] = strstr(c, "ftp.");
            if (idtest[4] && idtest[4] < c)
                  idtest[4] = strchr(c + 1, '(');
            if (idtest[5] && idtest[5] < c)
                  idtest[5] = strstr(c + 1, ".h&gt;");
            for (i = 0; i < 6; i++)
                  nr += (idtest[i] != NULL);
      }
      fputs(c, stdout);
}

static int current_font = 0;
static int current_size = 0;
static int fillout = 1;

static void
out_html(char *c)
{
      if (!c)
            return;
      if (no_newline_output) {
            int     i = 0;

            no_newline_output = 1;
            while (c[i]) {
                  if (!no_newline_output)
                        c[i - 1] = c[i];
                  if (c[i] == '\n')
                        no_newline_output = 1;
                  i++;
            }
            if (!no_newline_output)
                  c[i - 1] = 0;
      }
      if (scaninbuff) {
            while (*c) {
                  if (buffpos >= buffmax) {
                        char   *h;

                        h = realloc(buffer, buffmax * 2);
                        if (!h)
                              return;
                        buffer = h;
                        buffmax *= 2;
                  }
                  buffer[buffpos++] = *c++;
            }
      } else if (output_possible) {
            while (*c) {
                  outbuffer[obp++] = *c;
                  if (*c == '\n' || obp > HUGE_STR_MAX) {
                        outbuffer[obp] = '\0';
                        add_links(outbuffer);
                        obp = 0;
                  }
                  c++;
            }
      }
}

#define FO0 ""
#define FC0 ""
#define FO1 "<I>"
#define FC1 "</I>"
#define FO2 "<B>"
#define FC2 "</B>"
#define FO3 "<TT>"
#define FC3 "</TT>"

static char *switchfont[16] = {
      "", FC0 FO1, FC0 FO2, FC0 FO3,
      FC1 FO0, "", FC1 FO2, FC1 FO3,
      FC2 FO0, FC2 FO1, "", FC2 FO3,
      FC3 FO0, FC3 FO1, FC3 FO2, ""
};

static char *
change_to_font(int nr)
{
      int     i;

      switch (nr) {
      case '0':
            nr++;
      case '1':
      case '2':
      case '3':
      case '4':
            nr = nr - '1';
            break;
      case V('C', 'W'):
            nr = 3;
            break;
      case 'L':
            nr = 3;
            break;
      case 'B':
            nr = 2;
            break;
      case 'I':
            nr = 1;
            break;
      case 'P':
      case 'R':
            nr = 0;
            break;
      case 0:
      case 1:
      case 2:
      case 3:
            break;
      default:
            nr = 0;
            break;
      }
      i = current_font * 4 + nr % 4;
      current_font = nr % 4;
      return switchfont[i];
}

static char sizebuf[200];

static char *
change_to_size(int nr)
{
      int     i;

      switch (nr) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
            nr = nr - '0';
            break;
      case '\0':
            break;
      default:
            nr = current_size + nr;
            if (nr > 9)
                  nr = 9;
            if (nr < -9)
                  nr = -9;
            break;
      }
      if (nr == current_size)
            return "";
      i = current_font;
      sizebuf[0] = '\0';
      strcat(sizebuf, change_to_font(0));
      if (current_size)
            strcat(sizebuf, "</FONT>");
      current_size = nr;
      if (nr) {
            int     l;

            strcat(sizebuf, "<FONT SIZE=");
            l = strlen(sizebuf);
            if (nr > 0)
                  sizebuf[l++] = '+';
            else
                  sizebuf[l++] = '-', nr = -nr;
            sizebuf[l++] = nr + '0';
            sizebuf[l++] = '>';
            sizebuf[l] = '\0';
      }
      strcat(sizebuf, change_to_font(i));
      return sizebuf;
}

static int asint = 0;
static int intresult = 0;

#define SKIPEOL while (*c && *c++!='\n')

static int skip_escape = 0;
static int single_escape = 0;

static char *
scan_escape(char *c)
{
      char   *h = NULL;
      char    b[5];
      INTDEF *intd;
      int     exoutputp, exskipescape;
      int     i, j;

      intresult = 0;
      switch (*c) {
      case 'e':
            h = "\\";
            curpos++;
            break;
      case '0':
      case ' ':
            h = "&nbsp;";
            curpos++;
            break;
      case '|':
            h = "";
            break;
      case '"':
            SKIPEOL;
            c--;
            h = "";
            break;
      case '$':
            if (argument) {
                  c++;
                  i = (*c - '1');
                  if (!(h = argument[i]))
                        h = "";
            }
            break;
      case 'z':
            c++;
            if (*c == '\\') {
                  c = scan_escape(c + 1);
                  c--;
                  h = "";
            } else {
                  b[0] = *c;
                  b[1] = '\0';
                  h = "";
            }
            break;
      case 'k':
            c++;
            if (*c == '(')
                  c += 2;
      case '^':
      case '!':
      case '%':
      case 'a':
      case 'd':
      case 'r':
      case 'u':
      case '\n':
      case '&':
            h = "";
            break;
      case '(':
            c++;
            i = c[0] * 256 + c[1];
            c++;
            h = expand_char(i);
            break;
      case '*':
            c++;
            if (*c == '(') {
                  c++;
                  i = c[0] * 256 + c[1];
                  c++;
            } else
                  i = *c * 256 + ' ';
            h = expand_string(i);
            break;
      case 'f':
            c++;
            if (*c == '\\') {
                  c++;
                  c = scan_escape(c);
                  c--;
                  i = intresult;
            } else if (*c != '(')
                  i = *c;
            else {
                  c++;
                  i = c[0] * 256 + c[1];
                  c++;
            }
            if (!skip_escape)
                  h = change_to_font(i);
            else
                  h = "";
            break;
      case 's':
            c++;
            j = 0;
            i = 0;
            if (*c == '-') {
                  j = -1;
                  c++;
            } else if (*c == '+') {
                  j = 1;
                  c++;
            }
            if (*c == '0')
                  c++;
            else if (*c == '\\') {
                  c++;
                  c = scan_escape(c);
                  i = intresult;
                  if (!j)
                        j = 1;
            } else
                  while (isdigit(*c) && (!i || (!j && i < 4)))
                        i = i * 10 + (*c++) - '0';
            if (!j) {
                  j = 1;
                  if (i)
                        i = i - 10;
            }
            if (!skip_escape)
                  h = change_to_size(i * j);
            else
                  h = "";
            c--;
            break;
      case 'n':
            c++;
            j = 0;
            switch (*c) {
            case '+':
                  j = 1;
                  c++;
                  break;
            case '-':
                  j = -1;
                  c++;
                  break;
            default:
                  break;
            }
            if (*c == '(') {
                  c++;
                  i = V(c[0], c[1]);
                  c = c + 1;
            } else {
                  i = V(c[0], ' ');
            }
            intd = intdef;
            while (intd && intd->nr != i)
                  intd = intd->next;
            if (intd) {
                  intd->val = intd->val + j * intd->incr;
                  intresult = intd->val;
            } else {
                  switch (i) {
                  case V('.', 's'):
                        intresult = current_size;
                        break;
                  case V('.', 'f'):
                        intresult = current_font;
                        break;
                  default:
                        intresult = 0;
                        break;
                  }
            }
            h = "";
            break;
      case 'w':
            c++;
            i = *c;
            c++;
            exoutputp = output_possible;
            exskipescape = skip_escape;
            output_possible = 0;
            skip_escape = 1;
            j = 0;
            while (*c != i) {
                  j++;
                  if (*c == escapesym)
                        c = scan_escape(c + 1);
                  else
                        c++;
            }
            output_possible = exoutputp;
            skip_escape = exskipescape;
            intresult = j;
            break;
      case 'l':
            h = "<HR>";
            curpos = 0;
      case 'b':
      case 'v':
      case 'x':
      case 'o':
      case 'L':
      case 'h':
            c++;
            i = *c;
            c++;
            exoutputp = output_possible;
            exskipescape = skip_escape;
            output_possible = 0;
            skip_escape = 1;
            while (*c != i)
                  if (*c == escapesym)
                        c = scan_escape(c + 1);
                  else
                        c++;
            output_possible = exoutputp;
            skip_escape = exskipescape;
            break;
      case 'c':
            no_newline_output = 1;
            break;
      case '{':
            newline_for_fun++;
            h = "";
            break;
      case '}':
            if (newline_for_fun)
                  newline_for_fun--;
            h = "";
            break;
      case 'p':
            h = "<BR>\n";
            curpos = 0;
            break;
      case 't':
            h = "\t";
            curpos = (curpos + 8) & 0xfff8;
            break;
      case '<':
            h = "&lt;";
            curpos++;
            break;
      case '>':
            h = "&gt;";
            curpos++;
            break;
      case '\\':
            if (single_escape) {
                  c--;
                  break;
            }
      default:
            b[0] = *c;
            b[1] = 0;
            h = b;
            curpos++;
            break;
      }
      c++;
      if (!skip_escape)
            out_html(h);
      return c;
}

typedef struct TABLEITEM TABLEITEM;

struct TABLEITEM {
      char   *contents;
      int     size, align, valign, colspan, rowspan, font, vleft, vright, space,
              width;
      TABLEITEM *next;
};

static TABLEITEM emptyfield = {NULL, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, NULL};

typedef struct TABLEROW TABLEROW;

struct TABLEROW {
      TABLEITEM *first;
      TABLEROW *prev, *next;
};

static char *tableopt[] = {
      "center", "expand", "box", "allbox", "doublebox",
      "tab", "linesize", "delim", NULL
};
static int tableoptl[] = {6, 6, 3, 6, 9, 3, 8, 5, 0};

static void
clear_table(TABLEROW * table)
{
      TABLEROW *tr1, *tr2;
      TABLEITEM *ti1, *ti2;

      tr1 = table;
      while (tr1->prev)
            tr1 = tr1->prev;
      while (tr1) {
            ti1 = tr1->first;
            while (ti1) {
                  ti2 = ti1->next;
                  if (ti1->contents)
                        free(ti1->contents);
                  free(ti1);
                  ti1 = ti2;
            }
            tr2 = tr1;
            tr1 = tr1->next;
            free(tr2);
      }
}

static char *scan_expression(char *c, int *result);

static char *
scan_format(char *c, TABLEROW ** result, int *maxcol)
{
      TABLEROW *layout, *currow;
      TABLEITEM *curfield;
      int     i, j;

      if (*result) {
            clear_table(*result);
      }
      layout = currow = (TABLEROW *) malloc(sizeof(TABLEROW));
      currow->next = currow->prev = NULL;
      currow->first = curfield = (TABLEITEM *) malloc(sizeof(TABLEITEM));
      *curfield = emptyfield;
      while (*c && *c != '.') {
            switch (*c) {
            case 'C':
            case 'c':
            case 'N':
            case 'n':
            case 'R':
            case 'r':
            case 'A':
            case 'a':
            case 'L':
            case 'l':
            case 'S':
            case 's':
            case '^':
            case '_':
                  if (curfield->align) {
                        curfield->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
                        curfield = curfield->next;
                        *curfield = emptyfield;
                  }
                  curfield->align = toupper(*c);
                  c++;
                  break;
            case 'i':
            case 'I':
            case 'B':
            case 'b':
                  curfield->font = toupper(*c);
                  c++;
                  break;
            case 'f':
            case 'F':
                  c++;
                  curfield->font = toupper(*c);
                  c++;
                  if (!isspace(*c))
                        c++;
                  break;
            case 't':
            case 'T':
                  curfield->valign = 't';
                  c++;
                  break;
            case 'p':
            case 'P':
                  c++;
                  i = j = 0;
                  if (*c == '+') {
                        j = 1;
                        c++;
                  }
                  if (*c == '-') {
                        j = -1;
                        c++;
                  }
                  while (isdigit(*c))
                        i = i * 10 + (*c++) - '0';
                  if (j)
                        curfield->size = i * j;
                  else
                        curfield->size = j - 10;
                  break;
            case 'v':
            case 'V':
            case 'w':
            case 'W':
                  c = scan_expression(c + 2, &curfield->width);
                  break;
            case '|':
                  if (curfield->align)
                        curfield->vleft++;
                  else
                        curfield->vright++;
                  c++;
                  break;
            case 'e':
            case 'E':
                  c++;
                  break;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                  i = 0;
                  while (isdigit(*c))
                        i = i * 10 + (*c++) - '0';
                  curfield->space = i;
                  break;
            case ',':
            case '\n':
                  currow->next = (TABLEROW *) malloc(sizeof(TABLEROW));
                  currow->next->prev = currow;
                  currow = currow->next;
                  currow->next = NULL;
                  curfield = currow->first = (TABLEITEM *) malloc(sizeof(TABLEITEM));
                  *curfield = emptyfield;
                  c++;
                  break;
            default:
                  c++;
                  break;
            }
      }
      if (*c == '.')
            while (*c++ != '\n');
      *maxcol = 0;
      currow = layout;
      while (currow) {
            curfield = layout->first;
            i = 0;
            while (curfield) {
                  i++;
                  curfield = curfield->next;
            }
            if (i > *maxcol)
                  *maxcol = i;
            currow = currow->next;
      }
      *result = layout;
      return c;
}

static TABLEROW *
next_row(TABLEROW * tr)
{
      if (tr->next) {
            tr = tr->next;
            if (!tr->next)
                  next_row(tr);
            return tr;
      } else {
            TABLEITEM *ti, *ti2;

            tr->next = (TABLEROW *) malloc(sizeof(TABLEROW));
            tr->next->prev = tr;
            ti = tr->first;
            tr = tr->next;
            tr->next = NULL;
            if (ti)
                  tr->first = ti2 = (TABLEITEM *) malloc(sizeof(TABLEITEM));
            else
                  tr->first = ti2 = NULL;
            while (ti != ti2) {
                  *ti2 = *ti;
                  ti2->contents = NULL;
                  if ((ti = ti->next)) {
                        ti2->next = (TABLEITEM *) malloc(sizeof(TABLEITEM));
                  }
                  ti2 = ti2->next;
            }
            return tr;
      }
}

static char itemreset[20] = "\\fR\\s0";

static char *
scan_table(char *c)
{
      char   *t, *h, *g;
      int     center = 0, expand = 0, box = 0, border = 0, linesize = 1;
      int     i, j, maxcol = 0, finished = 0;
      int     oldfont, oldsize, oldfillout;
      char    itemsep = '\t';
      TABLEROW *layout = NULL, *currow, *ftable;
      TABLEITEM *curfield;

      while (*c++ != '\n');
      h = c;
      if (*h == '.')
            return c - 1;
      oldfont = current_font;
      oldsize = current_size;
      oldfillout = fillout;
      out_html(change_to_font(0));
      out_html(change_to_size(0));
      if (!fillout) {
            fillout = 1;
            out_html("</PRE>");
      }
      while (*h && *h != '\n')
            h++;
      if (h[-1] == ';') {
            /* scan table options */
            while (c < h) {
                  while (isspace(*c))
                        c++;
                  for (i = 0; tableopt[i] && strncmp(tableopt[i], c, tableoptl[i]); i++);
                  c = c + tableoptl[i];
                  switch (i) {
                  case 0:
                        center = 1;
                        break;
                  case 1:
                        expand = 1;
                        break;
                  case 2:
                        box = 1;
                        break;
                  case 3:
                        border = 1;
                        break;
                  case 4:
                        box = 2;
                        break;
                  case 5:
                        while (*c++ != '(');
                        itemsep = *c++;
                        break;
                  case 6:
                        while (*c++ != '(');
                        linesize = 0;
                        while (isdigit(*c))
                              linesize = linesize * 10 + (*c++) - '0';
                        break;
                  case 7:
                        while (*c != ')')
                              c++;
                  default:
                        break;
                  }
                  c++;
            }
            c = h + 1;
      }
      /* scan layout */
      c = scan_format(c, &layout, &maxcol);
      currow = layout;
      next_row(currow);
      curfield = layout->first;
      i = 0;
      while (!finished) {
            /* search item */
            h = c;
            if ((*c == '_' || *c == '=') && (c[1] == itemsep || c[1] == '\n')) {
                  if (c[-1] == '\n' && c[1] == '\n') {
                        if (currow->prev) {
                              currow->prev->next = (TABLEROW *) malloc(sizeof(TABLEROW));
                              currow->prev->next->next = currow;
                              currow->prev->next->prev = currow->prev;
                              currow->prev = currow->prev->next;
                        } else {
                              currow->prev = layout = (TABLEROW *) malloc(sizeof(TABLEROW));
                              currow->prev->prev = NULL;
                              currow->prev->next = currow;
                        }
                        curfield = currow->prev->first =
                              (TABLEITEM *) malloc(sizeof(TABLEITEM));
                        *curfield = emptyfield;
                        curfield->align = *c;
                        curfield->colspan = maxcol;
                        curfield = currow->first;
                        c = c + 2;
                  } else {
                        if (curfield) {
                              curfield->align = *c;
                              do {
                                    curfield = curfield->next;
                              } while (curfield && curfield->align == 'S');
                        }
                        if (c[1] == '\n') {
                              currow = next_row(currow);
                              curfield = currow->first;
                        }
                        c = c + 2;
                  }
            } else if (*c == 'T' && c[1] == '{') {
                  h = c + 2;
                  c = strstr(h, "\nT}");
                  c++;
                  *c = '\0';
                  g = NULL;
                  scan_troff(h, 0, &g);
                  scan_troff(itemreset, 0, &g);
                  *c = 'T';
                  c += 3;
                  if (curfield) {
                        curfield->contents = g;
                        do {
                              curfield = curfield->next;
                        } while (curfield && curfield->align == 'S');
                  } else if (g)
                        free(g);
                  if (c[-1] == '\n') {
                        currow = next_row(currow);
                        curfield = currow->first;
                  }
            } else if (*c == '.' && c[1] == 'T' && c[2] == '&' && c[-1] == '\n') {
                  TABLEROW *hr;

                  while (*c++ != '\n');
                  hr = currow;
                  currow = currow->prev;
                  hr->prev = NULL;
                  c = scan_format(c, &hr, &i);
                  hr->prev = currow;
                  currow->next = hr;
                  currow = hr;
                  next_row(currow);
                  curfield = currow->first;
            } else if (*c == '.' && c[1] == 'T' && c[2] == 'E' && c[-1] == '\n') {
                  finished = 1;
                  while (*c++ != '\n');
                  if (currow->prev)
                        currow->prev->next = NULL;
                  currow->prev = NULL;
                  clear_table(currow);
            } else if (*c == '.' && c[-1] == '\n' && !isdigit(c[1])) {
                  /*
                   * skip troff request inside table (usually only .sp
                   * )
                   */
                  while (*c++ != '\n');
            } else {
                  h = c;
                  while (*c && (*c != itemsep || c[-1] == '\\') &&
                         (*c != '\n' || c[-1] == '\\'))
                        c++;
                  i = 0;
                  if (*c == itemsep) {
                        i = 1;
                        *c = '\n';
                  }
                  if (h[0] == '\\' && h[2] == '\n' &&
                      (h[1] == '_' || h[1] == '^')) {
                        if (curfield) {
                              curfield->align = h[1];
                              do {
                                    curfield = curfield->next;
                              } while (curfield && curfield->align == 'S');
                        }
                        h = h + 3;
                  } else {
                        g = NULL;
                        h = scan_troff(h, 1, &g);
                        scan_troff(itemreset, 0, &g);
                        if (curfield) {
                              curfield->contents = g;
                              do {
                                    curfield = curfield->next;
                              } while (curfield && curfield->align == 'S');
                        } else if (g)
                              free(g);
                  }
                  if (i)
                        *c = itemsep;
                  c = h;
                  if (c[-1] == '\n') {
                        currow = next_row(currow);
                        curfield = currow->first;
                  }
            }
      }
      /* calculate colspan and rowspan */
      currow = layout;
      while (currow->next)
            currow = currow->next;
      while (currow) {
            TABLEITEM *ti, *ti1 = NULL, *ti2 = NULL;

            ti = currow->first;
            if (currow->prev)
                  ti1 = currow->prev->first;
            while (ti) {
                  switch (ti->align) {
                  case 'S':
                        if (ti2) {
                              ti2->colspan++;
                              if (ti2->rowspan < ti->rowspan)
                                    ti2->rowspan = ti->rowspan;
                        }
                        break;
                  case '^':
                        if (ti1)
                              ti1->rowspan++;
                  default:
                        if (!ti2)
                              ti2 = ti;
                        else {
                              do {
                                    ti2 = ti2->next;
                              } while (ti2 && curfield->align == 'S');
                        }
                        break;
                  }
                  ti = ti->next;
                  if (ti1)
                        ti1 = ti1->next;
            }
            currow = currow->prev;
      }
      /* produce html output */
      if (center)
            out_html("<CENTER>");
      if (box == 2)
            out_html("<TABLE BORDER><TR><TD>");
      out_html("<TABLE");
      if (box || border) {
            out_html(" BORDER");
            if (!border)
                  out_html("><TR><TD><TABLE");
            if (expand)
                  out_html(" WIDTH=100%");
      }
      out_html(">\n");
      currow = layout;
      while (currow) {
            j = 0;
            out_html("<TR VALIGN=top>");
            curfield = currow->first;
            while (curfield) {
                  if (curfield->align != 'S' && curfield->align != '^') {
                        out_html("<TD");
                        switch (curfield->align) {
                        case 'N':
                              curfield->space += 4;
                        case 'R':
                              out_html(" ALIGN=right");
                              break;
                        case 'C':
                              out_html(" ALIGN=center");
                        default:
                              break;
                        }
                        if (!curfield->valign && curfield->rowspan > 1)
                              out_html(" VALIGN=center");
                        if (curfield->colspan > 1) {
                              char    buf[5];

                              out_html(" COLSPAN=");
                              sprintf(buf, "%i", curfield->colspan);
                              out_html(buf);
                        }
                        if (curfield->rowspan > 1) {
                              char    buf[5];

                              out_html(" ROWSPAN=");
                              sprintf(buf, "%i", curfield->rowspan);
                              out_html(buf);
                        }
                        j = j + curfield->colspan;
                        out_html(">");
                        if (curfield->size)
                              out_html(change_to_size(curfield->size));
                        if (curfield->font)
                              out_html(change_to_font(curfield->font));
                        switch (curfield->align) {
                        case '=':
                              out_html("<HR><HR>");
                              break;
                        case '_':
                              out_html("<HR>");
                              break;
                        default:
                              if (curfield->contents)
                                    out_html(curfield->contents);
                              break;
                        }
                        if (curfield->space)
                              for (i = 0; i < curfield->space; i++)
                                    out_html("&nbsp;");
                        if (curfield->font)
                              out_html(change_to_font(0));
                        if (curfield->size)
                              out_html(change_to_size(0));
                        if (j >= maxcol && curfield->align > '@' && curfield->align != '_')
                              out_html("<BR>");
                        out_html("</TD>");
                  }
                  curfield = curfield->next;
            }
            out_html("</TR>\n");
            currow = currow->next;
      }
      if (box && !border)
            out_html("</TABLE>");
      out_html("</TABLE>");
      if (box == 2)
            out_html("</TABLE>");
      if (center)
            out_html("</CENTER>\n");
      else
            out_html("\n");
      if (!oldfillout)
            out_html("<PRE>");
      fillout = oldfillout;
      out_html(change_to_size(oldsize));
      out_html(change_to_font(oldfont));
      return c;
}

static char *
scan_expression(char *c, int *result)
{
      int     value = 0, value2, j = 0, sign = 1, opex = 0;
      char    oper = 'c';

      if (*c == '!') {
            c = scan_expression(c + 1, &value);
            value = (!value);
      } else if (*c == 'n') {
            c++;
            value = NROFF;
      } else if (*c == 't') {
            c++;
            value = 1 - NROFF;
      } else if (*c == '\'' || *c == '"' || *c < ' ' || (*c == '\\' && c[1] == '(')) {
            /*
             * ?string1?string2? test if string1 equals string2.
             */
            char   *st1 = NULL, *st2 = NULL, *h;
            char   *tcmp = NULL;
            char    sep;

            sep = *c;
            if (sep == '\\') {
                  tcmp = c;
                  c = c + 3;
            }
            c++;
            h = c;
            while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
                  c++;
            *c = '\n';
            scan_troff(h, 1, &st1);
            *c = sep;
            if (tcmp)
                  c = c + 3;
            c++;
            h = c;
            while (*c != sep && (!tcmp || strncmp(c, tcmp, 4)))
                  c++;
            *c = '\n';
            scan_troff(h, 1, &st2);
            *c = sep;
            if (!st1 && !st2)
                  value = 1;
            else if (!st1 || !st2)
                  value = 0;
            else
                  value = (!strcmp(st1, st2));
            if (st1)
                  free(st1);
            if (st2)
                  free(st2);
            if (tcmp)
                  c = c + 3;
            c++;
      } else {
            while (*c && !isspace(*c) && *c != ')') {
                  opex = 0;
                  switch (*c) {
                  case '(':
                        c = scan_expression(c + 1, &value2);
                        value2 = sign * value2;
                        opex = 1;
                        break;
                  case '.':
                  case '0':
                  case '1':
                  case '2':
                  case '3':
                  case '4':
                  case '5':
                  case '6':
                  case '7':
                  case '8':
                  case '9':{
                              int     num = 0, denum = 1;

                              value2 = 0;
                              while (isdigit(*c))
                                    value2 = value2 * 10 + ((*c++) - '0');
                              if (*c == '.') {
                                    c++;
                                    while (isdigit(*c)) {
                                          num = num * 10 + ((*c++) - '0');
                                          denum = denum * 10;
                                    }
                              }
                              if (isalpha(*c)) {
                                    /* scale indicator */
                                    switch (*c) {
                                    case 'i':   /* inch -> 10pt */
                                          value2 = value2 * 10 + (num * 10 + denum / 2) / denum;
                                          num = 0;
                                          break;
                                    default:
                                          break;
                                    }
                                    c++;
                              }
                              value2 = value2 + (num + denum / 2) / denum;
                              value2 = sign * value2;
                              opex = 1;
                              break;
                        }
                  case '\\':
                        c = scan_escape(c + 1);
                        value2 = intresult * sign;
                        if (isalpha(*c))
                              c++;  /* scale indicator */
                        opex = 1;
                        break;
                  case '-':
                        if (oper) {
                              sign = -1;
                              c++;
                              break;
                        }
                  case '>':
                  case '<':
                  case '+':
                  case '/':
                  case '*':
                  case '%':
                  case '&':
                  case '=':
                  case ':':
                        if (c[1] == '=')
                              oper = (*c++) + 16;
                        else
                              oper = *c;
                        c++;
                        break;
                  default:
                        c++;
                        break;
                  }
                  if (opex) {
                        sign = 1;
                        switch (oper) {
                        case 'c':
                              value = value2;
                              break;
                        case '-':
                              value = value - value2;
                              break;
                        case '+':
                              value = value + value2;
                              break;
                        case '*':
                              value = value * value2;
                              break;
                        case '/':
                              if (value2)
                                    value = value / value2;
                              break;
                        case '%':
                              if (value2)
                                    value = value % value2;
                              break;
                        case '<':
                              value = (value < value2);
                              break;
                        case '>':
                              value = (value > value2);
                              break;
                        case '>' + 16:
                              value = (value >= value2);
                              break;
                        case '<' + 16:
                              value = (value <= value2);
                              break;
                        case '=':
                        case '=' + 16:
                              value = (value == value2);
                              break;
                        case '&':
                              value = (value && value2);
                              break;
                        case ':':
                              value = (value || value2);
                              break;
                        default:
                              fprintf(stderr, "man2html: unknown operator %c.\n", oper);
                        }
                        oper = 0;
                  }
            }
            if (*c == ')')
                  c++;
      }
      *result = value;
      return c;
}

static void
trans_char(char *c, char s, char t)
{
      char   *sl = c;
      int     slash = 0;

      while (*sl != '\n' || slash) {
            if (!slash) {
                  if (*sl == escapesym)
                        slash = 1;
                  else if (*sl == s)
                        *sl = t;
            } else
                  slash = 0;
            sl++;
      }
}

/* Remove \a from C in place.  Return modified C. */
static char *
unescape (char *c)
{
      int   i, l;

      l = strlen (c);
      i = 0;
      while (i < l && c[i]) {
            if (c[i] == '\a') {
                  if (c[i+1])
                        strcpy(c + i, c + i + 1);     /* should be memmove */
                  else {
                        c[i] = '\0';
                        break;
                  }
            }
            i++;
      }
      return c;
}
      
static char *
fill_words(char *c, char *words[], int *n)
{
      char   *sl = c;
      int     slash = 0;
      int     skipspace = 0;

      *n = 0;
      words[*n] = sl;
      while (*sl && (*sl != '\n' || slash)) {
            if (!slash) {
                  if (*sl == '"') {
                        *sl = '\a';
                        skipspace = !skipspace;
                  } else if (*sl == '\a') {
                        /* handle already-translated " */
                        skipspace = !skipspace;
                  } else if (*sl == escapesym)
                        slash = 1;
                  else if ((*sl == ' ' || *sl == '\t') && !skipspace) {
                        *sl = '\n';
                        if (words[*n] != sl)
                              (*n)++;
                        words[*n] = sl + 1;
                  }
            } else {
                  if (*sl == '"') {
                        sl--;
                        *sl = '\n';
                        if (words[*n] != sl)
                              (*n)++;
                        sl++;
                        while (*sl && *sl != '\n')
                              sl++;
                        words[*n] = sl;
                        sl--;
                  }
                  slash = 0;
            }
            sl++;
      }
      if (sl != words[*n])
            (*n)++;
      return sl;
}

static char *abbrev_list[] = {
      "GSBG", "Getting Started ",
      "SUBG", "Customizing SunOS",
      "SHBG", "Basic Troubleshooting",
      "SVBG", "SunView User's Guide",
      "MMBG", "Mail and Messages",
      "DMBG", "Doing More with SunOS",
      "UNBG", "Using the Network",
      "GDBG", "Games, Demos &amp; Other Pursuits",
      "CHANGE", "SunOS 4.1 Release Manual",
      "INSTALL", "Installing SunOS 4.1",
      "ADMIN", "System and Network Administration",
      "SECUR", "Security Features Guide",
      "PROM", "PROM User's Manual",
      "DIAG", "Sun System Diagnostics",
      "SUNDIAG", "Sundiag User's Guide",
      "MANPAGES", "SunOS Reference Manual",
      "REFMAN", "SunOS Reference Manual",
      "SSI", "Sun System Introduction",
      "SSO", "System Services Overview",
      "TEXT", "Editing Text Files",
      "DOCS", "Formatting Documents",
      "TROFF", "Using <B>nroff</B> and <B>troff</B>",
      "INDEX", "Global Index",
      "CPG", "C Programmer's Guide",
      "CREF", "C Reference Manual",
      "ASSY", "Assembly Language Reference",
      "PUL", "Programming Utilities and Libraries",
      "DEBUG", "Debugging Tools",
      "NETP", "Network Programming",
      "DRIVER", "Writing Device Drivers",
      "STREAMS", "STREAMS Programming",
      "SBDK", "SBus Developer's Kit",
      "WDDS", "Writing Device Drivers for the SBus",
      "FPOINT", "Floating-Point Programmer's Guide",
      "SVPG", "SunView 1 Programmer's Guide",
      "SVSPG", "SunView 1 System Programmer's Guide",
      "PIXRCT", "Pixrect Reference Manual",
      "CGI", "SunCGI Reference Manual",
      "CORE", "SunCore Reference Manual",
      "4ASSY", "Sun-4 Assembly Language Reference",
      "SARCH", "<FONT SIZE=-1>SPARC</FONT> Architecture Manual",
      "KR", "The C Programming Language",
NULL, NULL};

static char *
lookup_abbrev(char *c)
{
      int     i = 0;

      if (!c)
            return "";
      while (abbrev_list[i] && strcmp(c, abbrev_list[i]))
            i = i + 2;
      if (abbrev_list[i])
            return abbrev_list[i + 1];
      else
            return c;
}

static char manidx[NULL_TERMINATED(HUGE_STR_MAX)];
static int subs = 0;
static int mip = 0;
static char label[5] = "lbAA";

static void
add_to_index(int level, char *item)
{
      char   *c = NULL;

      label[3]++;
      if (label[3] > 'Z') {
            label[3] = 'A';
            label[2]++;
      }
      if (level != subs) {
            if (subs) {
                  strmaxcpy(manidx + mip, "</DL>\n", HUGE_STR_MAX - mip);
                  mip += 6;
            } else {
                  strmaxcpy(manidx + mip, "<DL>\n", HUGE_STR_MAX - mip);
                  mip += 5;
            }
      }
      subs = level;
      scan_troff(item, 1, &c);
      sprintf(manidx + mip, "<DT><A HREF=\"#%s\">%s</A><DD>\n", label, c);
      if (c)
            free(c);
      while (manidx[mip])
            mip++;
}

static char *
skip_till_newline(char *c)
{
      int     lvl = 0;

      while (*c && *c != '\n' || lvl > 0) {
            if (*c == '\\') {
                  c++;
                  if (*c == '}')
                        lvl--;
                  else if (*c == '{')
                        lvl++;
            }
            c++;
      }
      c++;
      if (lvl < 0 && newline_for_fun) {
            newline_for_fun = newline_for_fun + lvl;
            if (newline_for_fun < 0)
                  newline_for_fun = 0;
      }
      return c;
}

static int ifelseval = 0;

static char *
scan_request(char *c)
{
      /* BSD Mandoc stuff */
      static int mandoc_synopsis = 0;     /* True if we are in the synopsis
                               * section */
      static int mandoc_command = 0;      /* True if this is mandoc page */
      static int mandoc_bd_options; /* Only copes with non-nested Bd's */

      int     i, j, mode = 0;
      char   *h;
      char   *wordlist[MAX_WORDLIST];
      int     words;
      char   *sl;
      STRDEF *owndef;

      while (*c == ' ' || *c == '\t')
            c++;
      if (c[0] == '\n')
            return c + 1;
      if (c[1] == '\n')
            j = 1;
      else
            j = 2;
      while (c[j] == ' ' || c[j] == '\t')
            j++;
      if (c[0] == escapesym) {
            /* some pages use .\" .\$1 .\} */
            /* .\$1 is too difficult/stupid */
            if (c[1] == '$')
                  c = skip_till_newline(c);
            else
                  c = scan_escape(c + 1);
      } else {
            i = V(c[0], c[1]);
            switch (i) {
            case V('a', 'b'):
                  h = c + j;
                  while (*h && *h != '\n')
                        h++;
                  *h = '\0';
                  if (scaninbuff && buffpos) {
                        buffer[buffpos] = '\0';
                        puts(buffer);
                  }
                  /* fprintf(stderr, "%s\n", c+2); */
                  exit(0);
                  break;
            case V('d', 'i'):
                  {
                        STRDEF *de;
                        int     oldcurpos = curpos;

                        c = c + j;
                        i = V(c[0], c[1]);
                        if (*c == '\n') {
                              c++;
                              break;
                        }
                        while (*c && *c != '\n')
                              c++;
                        c++;
                        h = c;
                        while (*c && strncmp(c, ".di", 3))
                              while (*c && *c++ != '\n');
                        *c = '\0';
                        de = strdef;
                        while (de && de->nr != i)
                              de = de->next;
                        if (!de) {
                              de = (STRDEF *) malloc(sizeof(STRDEF));
                              de->nr = i;
                              de->slen = 0;
                              de->next = strdef;
                              de->st = NULL;
                              strdef = de;
                        } else {
                              if (de->st)
                                    free(de->st);
                              de->slen = 0;
                              de->st = NULL;
                        }
                        scan_troff(h, 0, &de->st);
                        *c = '.';
                        while (*c && *c++ != '\n');
                        break;
                  }
            case V('d', 's'):
                  mode = 1;
            case V('a', 's'):
                  {
                        STRDEF *de;
                        int     oldcurpos = curpos;

                        c = c + j;
                        i = V(c[0], c[1]);
                        j = 0;
                        while (c[j] && c[j] != '\n')
                              j++;
                        if (j < 3) {
                              c = c + j;
                              break;
                        }
                        if (c[1] == ' ')
                              c = c + 1;
                        else
                              c = c + 2;
                        while (isspace(*c))
                              c++;
                        if (*c == '"')
                              c++;
                        de = strdef;
                        while (de && de->nr != i)
                              de = de->next;
                        single_escape = 1;
                        curpos = 0;
                        if (!de) {
                              char   *h;

                              de = (STRDEF *) malloc(sizeof(STRDEF));
                              de->nr = i;
                              de->slen = 0;
                              de->next = strdef;
                              de->st = NULL;
                              strdef = de;
                              h = NULL;
                              c = scan_troff(c, 1, &h);
                              de->st = h;
                              de->slen = curpos;
                        } else {
                              if (mode) {
                                    char   *h = NULL;

                                    c = scan_troff(c, 1, &h);
                                    free(de->st);
                                    de->slen = 0;
                                    de->st = h;
                              } else
                                    c = scan_troff(c, 1, &de->st);
                              de->slen += curpos;
                        }
                        single_escape = 0;
                        curpos = oldcurpos;
                  }
                  break;
            case V('b', 'r'):
                  if (still_dd)
                        out_html("<DD>");
                  else
                        out_html("<BR>\n");
                  curpos = 0;
                  c = c + j;
                  if (c[0] == escapesym) {
                        c = scan_escape(c + 1);
                  }
                  c = skip_till_newline(c);
                  break;
            case V('c', '2'):
                  c = c + j;
                  if (*c != '\n') {
                        nobreaksym = *c;
                  } else
                        nobreaksym = '\'';
                  c = skip_till_newline(c);
                  break;
            case V('c', 'c'):
                  c = c + j;
                  if (*c != '\n') {
                        controlsym = *c;
                  } else
                        controlsym = '.';
                  c = skip_till_newline(c);
                  break;
            case V('c', 'e'):
                  c = c + j;
                  if (*c == '\n') {
                        i = 1;
                  } else {
                        i = 0;
                        while ('0' <= *c && *c <= '9') {
                              i = i * 10 + *c - '0';
                              c++;
                        }
                  }
                  c = skip_till_newline(c);
                  /* center next i lines */
                  if (i > 0) {
                        out_html("<CENTER>\n");
                        while (i && *c) {
                              char   *line = NULL;

                              c = scan_troff(c, 1, &line);
                              if (line && strncmp(line, "<BR>", 4)) {
                                    out_html(line);
                                    out_html("<BR>\n");
                                    i--;
                              }
                        }
                        out_html("</CENTER>\n");
                        curpos = 0;
                  }
                  break;
            case V('e', 'c'):
                  c = c + j;
                  if (*c != '\n') {
                        escapesym = *c;
                  } else
                        escapesym = '\\';
                  break;
                  c = skip_till_newline(c);
            case V('e', 'o'):
                  escapesym = '\0';
                  c = skip_till_newline(c);
                  break;
            case V('e', 'x'):
                  exit(0);
                  break;
            case V('f', 'c'):
                  c = c + j;
                  if (*c == '\n') {
                        fieldsym = padsym = '\0';
                  } else {
                        fieldsym = c[0];
                        padsym = c[1];
                  }
                  c = skip_till_newline(c);
                  break;
            case V('f', 'i'):
                  if (!fillout) {
                        out_html(change_to_font(0));
                        out_html(change_to_size('0'));
                        out_html("</PRE>\n");
                  }
                  curpos = 0;
                  fillout = 1;
                  c = skip_till_newline(c);
                  break;
            case V('f', 't'):
                  c = c + j;
                  if (*c == '\n') {
                        out_html(change_to_font(0));
                  } else {
                        if (*c == escapesym) {
                              int     fn;

                              c = scan_expression(c, &fn);
                              c--;
                              out_html(change_to_font(fn));
                        } else {
                              out_html(change_to_font(*c));
                              c++;
                        }
                  }
                  c = skip_till_newline(c);
                  break;
            case V('e', 'l'):
                  /* .el anything : else part of if else */
                  if (ifelseval) {
                        c = c + j;
                        c[-1] = '\n';
                        c = scan_troff(c, 1, NULL);
                  } else
                        c = skip_till_newline(c + j);
                  break;
            case V('i', 'e'):
                  /* .ie c anything : then part of if else */
            case V('i', 'f'):
                  /*
                   * .if c anything .if !c anything .if N anything .if
                   * !N anything .if 'string1'string2' anything .if
                   * !'string1'string2' anything
                   */
                  c = c + j;
                  c = scan_expression(c, &i);
                  ifelseval = !i;
                  if (i) {
                        *c = '\n';
                        c++;
                        c = scan_troff(c, 1, NULL);
                  } else
                        c = skip_till_newline(c);
                  break;
            case V('i', 'g'):
                  {
                        char   *endwith = "..\n";

                        i = 3;
                        c = c + j;
                        if (*c != '\n') {
                              endwith = c - 1;
                              i = 1;
                              c[-1] = '.';
                              while (*c && *c != '\n')
                                    c++, i++;
                        }
                        c++;
                        while (*c && strncmp(c, endwith, i))
                              while (*c++ != '\n');
                        while (*c++ != '\n');
                        break;
                  }
            case V('n', 'f'):
                  if (fillout) {
                        out_html(change_to_font(0));
                        out_html(change_to_size('0'));
                        out_html("<PRE>\n");
                  }
                  curpos = 0;
                  fillout = 0;
                  c = skip_till_newline(c);
                  break;
            case V('p', 's'):
                  c = c + j;
                  if (*c == '\n') {
                        out_html(change_to_size('0'));
                  } else {
                        j = 0;
                        i = 0;
                        if (*c == '-') {
                              j = -1;
                              c++;
                        } else if (*c == '+') {
                              j = 1;
                              c++;
                        }
                        c = scan_expression(c, &i);
                        if (!j) {
                              j = 1;
                              if (i > 5)
                                    i = i - 10;
                        }
                        out_html(change_to_size(i * j));
                  }
                  c = skip_till_newline(c);
                  break;
            case V('s', 'p'):
                  c = c + j;
                  if (fillout)
                        out_html("<P>");
                  else {
                        out_html(NEWLINE);
                        NEWLINE[0] = '\n';
                  }
                  curpos = 0;
                  c = skip_till_newline(c);
                  break;
            case V('s', 'o'):
                  {
                        FILE   *f;
                        struct stat stbuf;
                        int     l = 0;
                        char   *buf;
                        char   *name = NULL;

                        curpos = 0;
                        c = c + j;
                        if (*c == '/') {
                              h = c;
                        } else {
                              h = c - 3;
                              h[0] = '.';
                              h[1] = '.';
                              h[2] = '/';
                        }
                        while (*c != '\n')
                              c++;
                        *c = '\0';
                        scan_troff(h, 1, &name);
                        if (name[3] == '/')
                              h = name + 3;
                        else
                              h = name;
                        if (stat(h, &stbuf) != -1)
                              l = stbuf.st_size;
                        buf = stralloc(l + 4);
#if NOCGI
                        if (!out_length) {
                              char   *t, *s;

                              t = strrchr(fname, '/');
                              if (!t)
                                    t = fname;
                              fprintf(stderr, "ln -s %s.html %s.html\n", h, t);
                              s = strrchr(t, '.');
                              if (!s)
                                    s = t;
                              printf("<HTML><HEAD><TITLE> Manpage of %s</TITLE>\n"
                                     "</HEAD><BODY>\n"
                                     "See the manpage for <A HREF=\"%s.html\">%s</A>.\n"
                                     "</BODY></HTML>\n",
                                     s, h, h);
                        } else
#endif
                        {
                              /*
                               * this works alright, except for
                               * section 3
                               */
                              buf = read_man_page(h);
                              if (!buf) {

                                    fprintf(stderr, "man2html: unable to open or read file %s.\n",
                                          h);
                                    out_html("<BLOCKQUOTE>"
                                           "man2html: unable to open or read file.\n");
                                    out_html(h);
                                    out_html("</BLOCKQUOTE>\n");
                              } else {
                                    buf[0] = buf[l] = '\n';
                                    buf[l + 1] = buf[l + 2] = '\0';
                                    scan_troff(buf + 1, 0, NULL);
                              }
                              if (buf)
                                    free(buf);
                        }
                        *c++ = '\n';
                        break;
                  }
            case V('t', 'a'):
                  c = c + j;
                  j = 0;
                  while (*c != '\n') {
                        sl = scan_expression(c, &tabstops[j]);
                        if (*c == '-' || *c == '+')
                              tabstops[j] += tabstops[j - 1];
                        c = sl;
                        while (*c == ' ' || *c == '\t')
                              c++;
                        j++;
                  }
                  maxtstop = j;
                  curpos = 0;
                  break;
            case V('t', 'i'):
                  /*
                   * while (itemdepth || dl_set[itemdepth]) {
                   * out_html("</DL>\n"); if (dl_set[itemdepth])
                   * dl_set[itemdepth]=0; else itemdepth--; }
                   */
                  out_html("<BR>\n");
                  c = c + j;
                  c = scan_expression(c, &j);
                  for (i = 0; i < j; i++)
                        out_html("&nbsp;");
                  curpos = j;
                  c = skip_till_newline(c);
                  break;
            case V('t', 'm'):
                  c = c + j;
                  h = c;
                  while (*c != '\n')
                        c++;
                  *c = '\0';
                  /* fprintf(stderr,"%s\n", h); */
                  *c = '\n';
                  break;
            case V('B', ' '):
            case V('B', '\n'):
            case V('I', ' '):
            case V('I', '\n'):
                  /* parse one line in a certain font */
                  out_html(change_to_font(*c));
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('O', 'P'): /* groff manpages use this
                               * construction */
                  /* .OP a b : [ <B>a</B> <I>b</I> ] */
                  mode = 1;
                  c[0] = 'B';
                  c[1] = 'I';
                  out_html(change_to_font('R'));
                  out_html("[");
                  curpos++;
            case V('B', 'R'):
            case V('B', 'I'):
            case V('I', 'B'):
            case V('I', 'R'):
            case V('R', 'B'):
            case V('R', 'I'):
                  {
                        char    font[2];

                        font[0] = c[0];
                        font[1] = c[1];
                        c = c + j;
                        if (*c == '\n')
                              c++;
                        sl = fill_words(c, wordlist, &words);
                        c = sl + 1;
                        /*
                         * .BR name (section) indicates a link. It
                         * will be added in the output routine.
                         */
                        for (i = 0; i < words; i++) {
                              if (mode) {
                                    out_html(" ");
                                    curpos++;
                              }
                              wordlist[i][-1] = ' ';
                              out_html(change_to_font(font[i & 1]));
                              scan_troff(wordlist[i], 1, NULL);
                        }
                        out_html(change_to_font('R'));
                        if (mode) {
                              out_html(" ]");
                              curpos++;
                        }
                        out_html(NEWLINE);
                        if (!fillout)
                              curpos = 0;
                        else
                              curpos++;
                  }
                  break;
            case V('D', 'T'):
                  for (j = 0; j < 20; j++)
                        tabstops[j] = (j + 1) * 8;
                  maxtstop = 20;
                  c = skip_till_newline(c);
                  break;
            case V('I', 'P'):
                  sl = fill_words(c + j, wordlist, &words);
                  c = sl + 1;
                  if (!dl_set[itemdepth]) {
                        out_html("<DL COMPACT>\n");
                        dl_set[itemdepth] = 1;
                  }
                  out_html("<DT>");
                  if (words) {
                        scan_troff(wordlist[0], 1, NULL);
                  }
                  out_html("<DD>");
                  curpos = 0;
                  break;
            case V('T', 'P'):
                  if (!dl_set[itemdepth]) {
                        out_html("<DL COMPACT>\n");
                        dl_set[itemdepth] = 1;
                  }
                  out_html("<DT>");
                  c = skip_till_newline(c);
                  /* somewhere a definition ends with '.TP' */
                  if (!*c)
                        still_dd = 1;
                  else {
                        c = scan_troff(c, 1, NULL);
                        out_html("<DD>");
                  }
                  curpos = 0;
                  break;
            case V('I', 'X'):
                  /* general index */
                  sl = fill_words(c + j, wordlist, &words);
                  c = sl + 1;
                  j = 4;
                  while (idxlabel[j] == 'Z')
                        idxlabel[j--] = 'A';
                  idxlabel[j]++;
#ifdef MAKEINDEX
                  fprintf(idxfile, "%s@%s@", fname, idxlabel);
                  for (j = 0; j < words; j++) {
                        h = NULL;
                        scan_troff(wordlist[j], 1, &h);
                        fprintf(idxfile, "_\b@%s", h);
                        free(h);
                  }
                  fprintf(idxfile, "\n");
#endif
                  out_html("<A NAME=\"");
                  out_html(idxlabel);
                  /*
                   * this will not work in mosaic (due to a bug).
                   * Adding '&nbsp;' between '>' and '<' solves it, but
                   * creates some space. A normal space does not work.
                   */
                  out_html("\"></A>");
                  break;
            case V('L', 'P'):
            case V('P', 'P'):
                  if (dl_set[itemdepth]) {
                        out_html("</DL>\n");
                        dl_set[itemdepth] = 0;
                  }
                  if (fillout)
                        out_html("<P>\n");
                  else {
                        out_html(NEWLINE);
                        NEWLINE[0] = '\n';
                  }
                  curpos = 0;
                  c = skip_till_newline(c);
                  break;
            case V('H', 'P'):
                  if (!dl_set[itemdepth]) {
                        out_html("<DL COMPACT>");
                        dl_set[itemdepth] = 1;
                  }
                  out_html("<DT>\n");
                  still_dd = 1;
                  c = skip_till_newline(c);
                  curpos = 0;
                  break;
            case V('P', 'D'):
                  c = skip_till_newline(c);
                  break;
            case V('R', 's'): /* BSD mandoc */
            case V('R', 'S'):
                  sl = fill_words(c + j, wordlist, &words);
                  j = 1;
                  if (words > 0)
                        scan_expression(wordlist[0], &j);
                  if (j >= 0) {
                        itemdepth++;
                        dl_set[itemdepth] = 0;
                        out_html("<DL COMPACT><DT><DD>");
                        c = skip_till_newline(c);
                        curpos = 0;
                        break;
                  }
            case V('R', 'e'): /* BSD mandoc */
            case V('R', 'E'):
                  if (itemdepth > 0) {
                        if (dl_set[itemdepth])
                              out_html("</DL>");
                        out_html("</DL>\n");
                        itemdepth--;
                  }
                  c = skip_till_newline(c);
                  curpos = 0;
                  break;
            case V('S', 'B'):
                  out_html(change_to_size(-1));
                  out_html(change_to_font('B'));
                  c = scan_troff(c + j, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html(change_to_size('0'));
                  break;
            case V('S', 'M'):
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html(change_to_size(-1));
                  trans_char(c, '"', '\a');
                  c = scan_troff(c, 1, NULL);
                  out_html(change_to_size('0'));
                  break;
            case V('S', 's'): /* BSD mandoc */
                  mandoc_command = 1;
            case V('S', 'S'):
                  mode = 1;
            case V('S', 'h'): /* BSD mandoc */
                  /* hack for fallthru from above */
                  mandoc_command = !mode || mandoc_command;
            case V('S', 'H'):
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  while (itemdepth || dl_set[itemdepth]) {
                        out_html("</DL>\n");
                        if (dl_set[itemdepth])
                              dl_set[itemdepth] = 0;
                        else if (itemdepth > 0)
                              itemdepth--;
                  }
                  out_html(change_to_font(0));
                  out_html(change_to_size(0));
                  if (!fillout) {
                        fillout = 1;
                        out_html("</PRE>");
                  }
                  trans_char(c, '"', '\a');
                  add_to_index(mode, c);
                  out_html("<A NAME=\"");
                  out_html(label);
                  /* &nbsp; for mosaic users */
                  if (mode)
                        out_html("\">&nbsp;</A>\n<H3>");
                  else
                        out_html("\">&nbsp;</A>\n<H2>");
                  mandoc_synopsis = strncmp(c, "SYNOPSIS", 8) == 0;
                  c = mandoc_command ? scan_troff_mandoc(c, 1, NULL) : scan_troff(c, 1, NULL);
                  if (mode)
                        out_html("</H3>\n");
                  else
                        out_html("</H2>\n");
                  curpos = 0;
                  break;
            case V('T', 'S'):
                  c = scan_table(c);
                  break;
            case V('D', 't'): /* BSD mandoc */
                  mandoc_command = 1;
            case V('T', 'H'):
                  if (!output_possible) {
                        sl = fill_words(c + j, wordlist, &words);
                        if (words > 1) {
                              char    page_and_sec[128];

                              for (i = 1; i < words; i++)
                                    wordlist[i][-1] = '\0';
                              *sl = '\0';
                              output_possible = 1;
                              sprintf(page_and_sec, "%s(%s)", wordlist[0], wordlist[1]);
                              out_html("<HTML><HEAD>\n<TITLE>");
                              out_html(page_and_sec);
                              out_html(" Manual Page");
                              out_html("</TITLE>\n</HEAD>\n<BODY>");
                              out_html("<TABLE WIDTH=100%>\n");
                              out_html("<TH ALIGN=LEFT>");
                              out_html(page_and_sec);
                              out_html("<TH ALIGN=CENTER>");
                              out_html(unescape(wordlist[2]));
                              out_html("<TH ALIGN=RIGHT>");
                              out_html(page_and_sec);
                              out_html("\n</TABLE>\n");
                              out_html("<BR><A HREF=\"#index\">Index</A>\n");
                              *sl = '\n';
                              out_html("<HR>\n");
                              if (mandoc_command)
                                    out_html("<BR>BSD mandoc<BR>");
                        }
                        c = sl + 1;
                  } else
                        c = skip_till_newline(c);
                  curpos = 0;
                  break;
            case V('T', 'X'):
                  sl = fill_words(c + j, wordlist, &words);
                  *sl = '\0';
                  out_html(change_to_font('I'));
                  if (words > 1)
                        wordlist[1][-1] = '\0';
                  c = lookup_abbrev(wordlist[0]);
                  curpos += strlen(c);
                  out_html(c);
                  out_html(change_to_font('R'));
                  if (words > 1)
                        out_html(wordlist[1]);
                  *sl = '\n';
                  c = sl + 1;
                  break;
            case V('r', 'm'):
                  /* .rm xx : Remove request, macro or string */
            case V('r', 'n'):
                  /*
                   * .rn xx yy : Rename request, macro or string xx to
                   * yy
                   */
                  {
                        STRDEF *de;

                        c = c + j;
                        i = V(c[0], c[1]);
                        c = c + 2;
                        while (isspace(*c) && *c != '\n')
                              c++;
                        j = V(c[0], c[1]);
                        while (*c && *c != '\n')
                              c++;
                        c++;
                        de = strdef;
                        while (de && de->nr != j)
                              de = de->next;
                        if (de) {
                              if (de->st)
                                    free(de->st);
                              de->nr = 0;
                        }
                        de = strdef;
                        while (de && de->nr != i)
                              de = de->next;
                        if (de)
                              de->nr = j;
                        break;
                  }
            case V('n', 'x'):
                  /* .nx filename : next file. */
            case V('i', 'n'):
                  /* .in +-N : Indent */
                  c = skip_till_newline(c);
                  break;
            case V('n', 'r'):
                  /*
                   * .nr R +-N M: define and set number register R by
                   * +-N; auto-increment by M
                   */
                  {
                        INTDEF *intd;

                        c = c + j;
                        i = V(c[0], c[1]);
                        c = c + 2;
                        intd = intdef;
                        while (intd && intd->nr != i)
                              intd = intd->next;
                        if (!intd) {
                              intd = (INTDEF *) malloc(sizeof(INTDEF));
                              intd->nr = i;
                              intd->val = 0;
                              intd->incr = 0;
                              intd->next = intdef;
                              intdef = intd;
                        }
                        while (*c == ' ' || *c == '\t')
                              c++;
                        c = scan_expression(c, &intd->val);
                        if (*c != '\n') {
                              while (*c == ' ' || *c == '\t')
                                    c++;
                              c = scan_expression(c, &intd->incr);
                        }
                        c = skip_till_newline(c);
                        break;
                  }
            case V('a', 'm'):
                  /* .am xx yy : append to a macro. */
                  /* define or handle as .ig yy */
                  mode = 1;
            case V('d', 'e'):
                  /*
                   * .de xx yy : define or redefine macro xx; end at
                   * .yy (..)
                   */
                  /* define or handle as .ig yy */
                  {
                        STRDEF *de;
                        int     olen = 0;

                        c = c + j;
                        sl = fill_words(c, wordlist, &words);
                        i = V(c[0], c[1]);
                        j = 2;
                        if (words == 1)
                              wordlist[1] = "..";
                        else {
                              wordlist[1]--;
                              wordlist[1][0] = '.';
                              j = 3;
                        }
                        c = sl + 1;
                        sl = c;
                        while (*c && strncmp(c, wordlist[1], j))
                              c = skip_till_newline(c);
                        de = defdef;
                        while (de && de->nr != i)
                              de = de->next;
                        if (mode && de)
                              olen = strlen(de->st);
                        j = olen + c - sl;
                        h = stralloc(j * 2 + 4);
                        if (h) {
                              for (j = 0; j < olen; j++)
                                    h[j] = de->st[j];
                              if (!j || h[j - 1] != '\n')
                                    h[j++] = '\n';
                              while (sl != c) {
                                    if (sl[0] == '\\' && sl[1] == '\\') {
                                          h[j++] = '\\';
                                          sl++;
                                    } else
                                          h[j++] = *sl;
                                    sl++;
                              }
                              h[j] = '\0';
                              if (de) {
                                    if (de->st)
                                          free(de->st);
                                    de->st = h;
                              } else {
                                    de = (STRDEF *) malloc(sizeof(STRDEF));
                                    de->nr = i;
                                    de->next = defdef;
                                    de->st = h;
                                    defdef = de;
                              }
                        }
                  }
                  c = skip_till_newline(c);
                  break;
            case V('B', 'l'): /* BSD mandoc */
                  {
                        char    list_options[NULL_TERMINATED(MED_STR_MAX)];
                        char   *nl = strchr(c, '\n');

                        c = c + j;
                        if (dl_set[itemdepth]) {      /* These things can
                                                 * nest. */
                              itemdepth++;
                        }
                        if (nl) {   /* Parse list options */
                              strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
                        }
                        if (strstr(list_options, "-bullet")) {    /* HTML Unnumbered List */
                              dl_set[itemdepth] = BL_BULLET_LIST;
                              out_html("<UL>\n");
                        } else if (strstr(list_options, "-enum")) {     /* HTML Ordered List */
                              dl_set[itemdepth] = BL_ENUM_LIST;
                              out_html("<OL>\n");
                        } else {    /* HTML Descriptive List */
                              dl_set[itemdepth] = BL_DESC_LIST;
                              out_html("<DL COMPACT>\n");
                        }
                        if (fillout)
                              out_html("<P>\n");
                        else {
                              out_html(NEWLINE);
                              NEWLINE[0] = '\n';
                        }
                        curpos = 0;
                        c = skip_till_newline(c);
                        break;
                  }
            case V('E', 'l'): /* BSD mandoc */
                  c = c + j;
                  if (dl_set[itemdepth] & BL_DESC_LIST) {
                        out_html("</DL>\n");
                  } else if (dl_set[itemdepth] & BL_BULLET_LIST) {
                        out_html("</UL>\n");
                  } else if (dl_set[itemdepth] & BL_ENUM_LIST) {
                        out_html("</OL>\n");
                  }
                  dl_set[itemdepth] = 0;
                  if (itemdepth > 0)
                        itemdepth--;
                  if (fillout)
                        out_html("<P>\n");
                  else {
                        out_html(NEWLINE);
                        NEWLINE[0] = '\n';
                  }
                  curpos = 0;
                  c = skip_till_newline(c);
                  break;
            case V('I', 't'): /* BSD mandoc */
                  c = c + j;
                  if (strncmp(c, "Xo", 2) == 0 && isspace(*(c + 2))) {
                        c = skip_till_newline(c);
                  }
                  if (dl_set[itemdepth] & BL_DESC_LIST) {
                        out_html("<DT>");
                        out_html(change_to_font('B'));
                        if (*c == '\n') { /* Don't allow embedded
                                           * comms after a newline */
                              c++;
                              c = scan_troff(c, 1, NULL);
                        } else {    /* Do allow embedded comms on
                                     * the same line. */
                              c = scan_troff_mandoc(c, 1, NULL);
                        }
                        out_html(change_to_font('R'));
                        out_html(NEWLINE);
                        out_html("<DD>");
                  } else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST)) {
                        out_html("<LI>");
                        c = scan_troff_mandoc(c, 1, NULL);
                        out_html(NEWLINE);
                  }
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('B', 'k'): /* BSD mandoc */
            case V('E', 'k'): /* BSD mandoc */
            case V('D', 'd'): /* BSD mandoc */
            case V('O', 's'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('B', 't'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  out_html(" is currently in beta test.");
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('B', 'x'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html("BSD ");
                  c = scan_troff_mandoc(c, 1, NULL);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('D', 'l'): /* BSD mandoc */
                  c = c + j;
                  out_html(NEWLINE);
                  out_html("<BLOCKQUOTE>");
                  out_html(change_to_font('L'));
                  if (*c == '\n')
                        c++;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html("</BLOCKQUOTE>");
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('B', 'd'): /* BSD mandoc */
                  {     /* Seems like a kind of example/literal mode */
                        char    bd_options[NULL_TERMINATED(MED_STR_MAX)];
                        char   *nl = strchr(c, '\n');

                        c = c + j;
                        if (nl) {
                              strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
                        }
                        out_html(NEWLINE);
                        mandoc_bd_options = 0;  /* Remember options for
                                           * terminating Bl */
                        if (strstr(bd_options, "-offset indent")) {
                              mandoc_bd_options |= BD_INDENT;
                              out_html("<BLOCKQUOTE>\n");
                        }
                        if (strstr(bd_options, "-literal")
                            || strstr(bd_options, "-unfilled")) {
                              if (fillout) {
                                    mandoc_bd_options |= BD_LITERAL;
                                    out_html(change_to_font(0));
                                    out_html(change_to_size('0'));
                                    out_html("<PRE>\n");
                              }
                              curpos = 0;
                              fillout = 0;
                        }
                        c = skip_till_newline(c);
                        break;
                  }
            case V('E', 'd'): /* BSD mandoc */
                  if (mandoc_bd_options & BD_LITERAL) {
                        if (!fillout) {
                              out_html(change_to_font(0));
                              out_html(change_to_size('0'));
                              out_html("</PRE>\n");
                        }
                  }
                  if (mandoc_bd_options & BD_INDENT)
                        out_html("</BLOCKQUOTE>\n");
                  curpos = 0;
                  fillout = 1;
                  c = skip_till_newline(c);
                  break;
            case V('B', 'e'): /* BSD mandoc */
                  c = c + j;
                  if (fillout)
                        out_html("<P>");
                  else {
                        out_html(NEWLINE);
                        NEWLINE[0] = '\n';
                  }
                  curpos = 0;
                  c = skip_till_newline(c);
                  break;
            case V('X', 'r'): /* BSD mandoc */
                  {
                        /*
                         * Translate xyz 1 to xyz(1) Allow for
                         * multiple spaces.  Allow the section to be
                         * missing.
                         */
                        char    buff[NULL_TERMINATED(MED_STR_MAX)];
                        char   *bufptr;

                        trans_char(c, '"', '\a');
                        bufptr = buff;
                        c = c + j;
                        if (*c == '\n')
                              c++;  /* Skip spaces */
                        while (isspace(*c) && *c != '\n')
                              c++;
                        while (isalnum(*c)) {   /* Copy the xyz part */
                              *bufptr = *c;
                              bufptr++;
                              if (bufptr >= buff + MED_STR_MAX)
                                    break;
                              c++;
                        }
                        while (isspace(*c) && *c != '\n')
                              c++;  /* Skip spaces */
                        if (isdigit(*c)) {      /* Convert the number if
                                           * there is one */
                              *bufptr = '(';
                              bufptr++;
                              if (bufptr < buff + MED_STR_MAX) {
                                    while (isalnum(*c)) {
                                          *bufptr = *c;
                                          bufptr++;
                                          if (bufptr >= buff + MED_STR_MAX)
                                                break;
                                          c++;
                                    }
                                    if (bufptr < buff + MED_STR_MAX) {
                                          *bufptr = ')';
                                          bufptr++;
                                    }
                              }
                        }
                        while (*c != '\n') {    /* Copy the remainder */
                              if (!isspace(*c)) {
                                    *bufptr = *c;
                                    bufptr++;
                                    if (bufptr >= buff + MED_STR_MAX)
                                          break;
                              }
                              c++;
                        }
                        *bufptr = '\n';
                        scan_troff_mandoc(buff, 1, NULL);

                        out_html(NEWLINE);
                        if (fillout)
                              curpos++;
                        else
                              curpos = 0;
                  }
                  break;
            case V('F', 'l'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  out_html("-");
                  if (*c != '\n') {
                        out_html(change_to_font('B'));
                        c = scan_troff_mandoc(c, 1, NULL);
                        out_html(change_to_font('R'));
                  }
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('P', 'a'): /* BSD mandoc */
            case V('P', 'f'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('P', 'p'): /* BSD mandoc */
                  if (fillout)
                        out_html("<P>\n");
                  else {
                        out_html(NEWLINE);
                        NEWLINE[0] = '\n';
                  }
                  curpos = 0;
                  c = skip_till_newline(c);
                  break;
            case V('D', 'q'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html("``");
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html("''");
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('O', 'p'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html(change_to_font('R'));
                  out_html("[");
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html("]");
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('O', 'o'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html(change_to_font('R'));
                  out_html("[");
                  c = scan_troff_mandoc(c, 1, NULL);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('O', 'c'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html("]");
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('P', 'q'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html("(");
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(")");
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('Q', 'l'): /* BSD mandoc */
                  {     /* Single quote first word in the line */
                        char   *sp;

                        trans_char(c, '"', '\a');
                        c = c + j;
                        if (*c == '\n')
                              c++;
                        sp = c;
                        do {  /* Find first whitespace after the
                               * first word that isn't a mandoc
                               * macro */
                              while (*sp && isspace(*sp))
                                    sp++;
                              while (*sp && !isspace(*sp))
                                    sp++;
                        } while (*sp && isupper(*(sp - 2)) && islower(*(sp - 1)));

                        /*
                         * Use a newline to mark the end of text to
                         * be quoted
                         */
                        if (*sp)
                              *sp = '\n';
                        out_html("`");    /* Quote the text */
                        c = scan_troff_mandoc(c, 1, NULL);
                        out_html("'");
                        out_html(NEWLINE);
                        if (fillout)
                              curpos++;
                        else
                              curpos = 0;
                        break;
                  }
            case V('S', 'q'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html("`");
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html("'");
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('A', 'r'): /* BSD mandoc */
                  /* parse one line in italics */
                  out_html(change_to_font('I'));
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n') { /* An empty Ar means "file
                                     * ..." */
                        out_html("file ...");
                  } else {
                        c = scan_troff_mandoc(c, 1, NULL);
                  }
                  out_html(change_to_font('R'));
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('A', 'd'): /* BSD mandoc */
            case V('E', 'm'): /* BSD mandoc */
            case V('V', 'a'): /* BSD mandoc */
            case V('X', 'c'): /* BSD mandoc */
                  /* parse one line in italics */
                  out_html(change_to_font('I'));
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('N', 'd'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html(" - ");
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('N', 'm'): /* BSD mandoc */
                  {
                        static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = "";

                        trans_char(c, '"', '\a');
                        c = c + j;
                        if (mandoc_synopsis) {  /* Break lines only in
                                           * the Synopsis. The
                                           * Synopsis section
                                           * seems to be treated
                                           * as a special case -
                                           * Bummer! */
                              static int count = 0;   /* Don't break on the
                                                 * first Nm */

                              if (count) {
                                    out_html("<BR>");
                              } else {
                                    char   *end = strchr(c, '\n');

                                    if (end) {  /* Remember the name for
                                                 * later. */
                                          strlimitcpy(mandoc_name, c, end - c, SMALL_STR_MAX);
                                    }
                              }
                              count++;
                        }
                        out_html(change_to_font('B'));
                        while (*c == ' ' || *c == '\t')
                              c++;
                        if (*c == '\n') { /* If Nm has no
                                           * argument, use one
                                           * from an earlier Nm
                                           * command that did have
                                           * one.  Hope there
                                           * aren't too many
                                           * commands that do
                                           * this. */
                              out_html(mandoc_name);
                        } else {
                              c = scan_troff_mandoc(c, 1, NULL);
                        }
                        out_html(change_to_font('R'));
                        out_html(NEWLINE);
                        if (fillout)
                              curpos++;
                        else
                              curpos = 0;
                        break;
                  }
            case V('C', 'd'): /* BSD mandoc */
            case V('C', 'm'): /* BSD mandoc */
            case V('I', 'c'): /* BSD mandoc */
            case V('M', 's'): /* BSD mandoc */
            case V('O', 'r'): /* BSD mandoc */
            case V('S', 'y'): /* BSD mandoc */
                  /* parse one line in bold */
                  out_html(change_to_font('B'));
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('D', 'v'): /* BSD mandoc */
            case V('E', 'v'): /* BSD mandoc */
            case V('F', 'r'): /* BSD mandoc */
            case V('L', 'i'): /* BSD mandoc */
            case V('N', 'o'): /* BSD mandoc */
            case V('N', 's'): /* BSD mandoc */
            case V('T', 'n'): /* BSD mandoc */
            case V('n', 'N'): /* BSD mandoc */
                  trans_char(c, '"', '\a');
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  out_html(change_to_font('B'));
                  c = scan_troff_mandoc(c, 1, NULL);
                  out_html(change_to_font('R'));
                  out_html(NEWLINE);
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('%', 'A'): /* BSD mandoc biblio stuff */
            case V('%', 'D'):
            case V('%', 'N'):
            case V('%', 'O'):
            case V('%', 'P'):
            case V('%', 'Q'):
            case V('%', 'V'):
                  c = c + j;
                  if (*c == '\n')
                        c++;
                  c = scan_troff(c, 1, NULL);   /* Don't allow embedded
                                           * mandoc coms */
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            case V('%', 'B'):
            case V('%', 'J'):
            case V('%', 'R'):
            case V('%', 'T'):
                  c = c + j;
                  out_html(change_to_font('I'));
                  if (*c == '\n')
                        c++;
                  c = scan_troff(c, 1, NULL);   /* Don't allow embedded
                                           * mandoc coms */
                  out_html(change_to_font('R'));
                  if (fillout)
                        curpos++;
                  else
                        curpos = 0;
                  break;
            default:
                  /* search macro database of self-defined macros */
                  owndef = defdef;
                  while (owndef && owndef->nr != i)
                        owndef = owndef->next;
                  if (owndef) {
                        char  **oldargument;
                        int     deflen;
                        int     onff;

                        sl = fill_words(c + j, wordlist, &words);
                        c = sl + 1;
                        *sl = '\0';
                        for (i = 1; i < words; i++)
                              wordlist[i][-1] = '\0';
                        for (i = 0; i < words; i++) {
                              char   *h = NULL;

                              if (mandoc_command) {
                                    scan_troff_mandoc(wordlist[i], 1, &h);
                              } else {
                                    scan_troff(wordlist[i], 1, &h);
                              }
                              wordlist[i] = h;
                        }
                        for (i = words; i < 20; i++)
                              wordlist[i] = NULL;
                        deflen = strlen(owndef->st);
                        for (i = 0; owndef->st[deflen + 2 + i] = owndef->st[i]; i++);
                        oldargument = argument;
                        argument = wordlist;
                        onff = newline_for_fun;
                        if (mandoc_command) {
                              scan_troff_mandoc(owndef->st + deflen + 2, 0, NULL);
                        } else {
                              scan_troff(owndef->st + deflen + 2, 0, NULL);
                        }
                        newline_for_fun = onff;
                        argument = oldargument;
                        for (i = 0; i < words; i++)
                              if (wordlist[i])
                                    free(wordlist[i]);
                        *sl = '\n';
                  } else if (mandoc_command &&
                           ((isupper(*c) && islower(*(c + 1)))
                            || (islower(*c) && isupper(*(c + 1))))
                        ) {   /* Let through any BSD mandoc
                               * commands that haven't been delt
                               * with. I don't want to miss
                               * anything out of the text. */
                        char    buf[4];

                        strncpy(buf, c, 2);
                        buf[2] = ' ';
                        buf[3] = '\0';
                        out_html(buf);    /* Print the command (it
                                     * might just be text). */
                        c = c + j;
                        trans_char(c, '"', '\a');
                        if (*c == '\n')
                              c++;
                        out_html(change_to_font('R'));
                        c = scan_troff(c, 1, NULL);
                        out_html(NEWLINE);
                        if (fillout)
                              curpos++;
                        else
                              curpos = 0;
                  } else {
                        c = skip_till_newline(c);
                  }
                  break;
            }
      }
      if (fillout) {
            out_html(NEWLINE);
            curpos++;
      }
      NEWLINE[0] = '\n';
      return c;
}

static void
flush(void)
{
}

static int contained_tab = 0;
static int mandoc_line = 0;   /* Signals whether to look for embedded
                         * mandoc commands. */

/* san : stop at newline */
static char *
scan_troff(char *c, int san, char **result)
{
      char   *h;
      char    intbuff[NULL_TERMINATED(MED_STR_MAX)];
      int     ibp = 0;
      int     i;
      char   *exbuffer;
      int     exbuffpos, exbuffmax, exscaninbuff, exnewline_for_fun;
      int     usenbsp = 0;

#define FLUSHIBP  if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }

      exbuffer = buffer;
      exbuffpos = buffpos;
      exbuffmax = buffmax;
      exnewline_for_fun = newline_for_fun;
      exscaninbuff = scaninbuff;
      newline_for_fun = 0;
      if (result) {
            if (*result) {
                  buffer = *result;
                  buffpos = strlen(buffer);
                  buffmax = buffpos;
            } else {
                  buffer = stralloc(LARGE_STR_MAX);
                  buffpos = 0;
                  buffmax = LARGE_STR_MAX;
            }
            scaninbuff = 1;
      }
      h = c;
      /* start scanning */

      while (*h && (!san || newline_for_fun || *h != '\n')) {

            if (*h == escapesym) {
                  h++;
                  FLUSHIBP;
                  h = scan_escape(h);
            } else if (*h == controlsym && h[-1] == '\n') {
                  h++;
                  FLUSHIBP;
                  h = scan_request(h);
                  if (san && h[-1] == '\n')
                        h--;
            } else if (mandoc_line
                     && *(h) && isupper(*(h))
                     && *(h + 1) && islower(*(h + 1))
                     && *(h + 2) && isspace(*(h + 2))) {
                  /*
                   * BSD imbedded command eg ".It Fl Ar arg1 Fl Ar
                   * arg2"
                   */
                  FLUSHIBP;
                  h = scan_request(h);
                  if (san && h[-1] == '\n')
                        h--;
            } else if (*h == nobreaksym && h[-1] == '\n') {
                  h++;
                  FLUSHIBP;
                  h = scan_request(h);
                  if (san && h[-1] == '\n')
                        h--;
            } else {
                  int     mx;

                  if (h[-1] == '\n' && still_dd && isalnum(*h)) {
                        /*
                         * sometimes a .HP request is not followed by
                         * a .br request
                         */
                        FLUSHIBP;
                        out_html("<DD>");
                        curpos = 0;
                        still_dd = 0;
                  }
                  switch (*h) {
                  case '&':
                        intbuff[ibp++] = '&';
                        intbuff[ibp++] = 'a';
                        intbuff[ibp++] = 'm';
                        intbuff[ibp++] = 'p';
                        intbuff[ibp++] = ';';
                        curpos++;
                        break;
                  case '<':
                        intbuff[ibp++] = '&';
                        intbuff[ibp++] = 'l';
                        intbuff[ibp++] = 't';
                        intbuff[ibp++] = ';';
                        curpos++;
                        break;
                  case '>':
                        intbuff[ibp++] = '&';
                        intbuff[ibp++] = 'g';
                        intbuff[ibp++] = 't';
                        intbuff[ibp++] = ';';
                        curpos++;
                        break;
                  case '"':
                        intbuff[ibp++] = '&';
                        intbuff[ibp++] = 'q';
                        intbuff[ibp++] = 'u';
                        intbuff[ibp++] = 'o';
                        intbuff[ibp++] = 't';
                        intbuff[ibp++] = ';';
                        curpos++;
                        break;
                  case '\n':
                        if (h[-1] == '\n' && fillout) {
                              intbuff[ibp++] = '<';
                              intbuff[ibp++] = 'P';
                              intbuff[ibp++] = '>';
                        }
                        if (contained_tab && fillout) {
                              intbuff[ibp++] = '<';
                              intbuff[ibp++] = 'B';
                              intbuff[ibp++] = 'R';
                              intbuff[ibp++] = '>';
                        }
                        contained_tab = 0;
                        curpos = 0;
                        usenbsp = 0;
                        intbuff[ibp++] = '\n';
                        break;
                  case '\t':
                        {
                              int     curtab = 0;

                              contained_tab = 1;
                              FLUSHIBP;
                              /* like a typewriter, not like TeX */
                              tabstops[19] = curpos + 1;
                              while (curtab < maxtstop && tabstops[curtab] <= curpos)
                                    curtab++;
                              if (curtab < maxtstop) {
                                    if (!fillout) {
                                          while (curpos < tabstops[curtab]) {
                                                intbuff[ibp++] = ' ';
                                                if (ibp > 480) {
                                                      FLUSHIBP;
                                                }
                                                curpos++;
                                          }
                                    } else {
                                          out_html("<TT>");
                                          while (curpos < tabstops[curtab]) {
                                                out_html("&nbsp;");
                                                curpos++;
                                          }
                                          out_html("</TT>");
                                    }
                              }
                        }
                        break;
                  default:
                        if (*h == ' ' && (h[-1] == '\n' || usenbsp)) {
                              FLUSHIBP;
                              if (!usenbsp && fillout) {
                                    out_html("<BR>");
                                    curpos = 0;
                              }
                              usenbsp = fillout;
                              if (usenbsp)
                                    out_html("&nbsp;");
                              else
                                    intbuff[ibp++] = ' ';
                        } else if (*h > 31 && *h < 127)
                              intbuff[ibp++] = *h;
                        else if (((unsigned char) (*h)) > 127) {
                              intbuff[ibp++] = '&';
                              intbuff[ibp++] = '#';
                              intbuff[ibp++] = '0' + ((unsigned char) (*h)) / 100;
                              intbuff[ibp++] = '0' + (((unsigned char) (*h)) % 100) / 10;
                              intbuff[ibp++] = '0' + ((unsigned char) (*h)) % 10;
                              intbuff[ibp++] = ';';
                        }
                        curpos++;
                        break;
                  }
                  if (ibp > (MED_STR_MAX - 20))
                        FLUSHIBP;
                  h++;
            }
      }
      FLUSHIBP;
      if (buffer)
            buffer[buffpos] = '\0';
      if (san && *h)
            h++;
      newline_for_fun = exnewline_for_fun;
      if (result) {
            *result = buffer;
            buffer = exbuffer;
            buffpos = exbuffpos;
            buffmax = exbuffmax;
            scaninbuff = exscaninbuff;
      }
      return h;
}


static char *
scan_troff_mandoc(char *c, int san, char **result)
{
      char   *ret, *end = c;
      int     oldval = mandoc_line;

      mandoc_line = 1;
      while (*end && *end != '\n') {
            end++;
      }

      if (end > c + 2
          && ispunct(*(end - 1))
          && isspace(*(end - 2)) && *(end - 2) != '\n') {
            /*
             * Don't format lonely punctuation E.g. in "xyz ," format the
             * xyz and then append the comma removing the space.
             */
            *(end - 2) = '\n';
            ret = scan_troff(c, san, result);
            *(end - 2) = *(end - 1);
            *(end - 1) = ' ';
      } else {
            ret = scan_troff(c, san, result);
      }
      mandoc_line = oldval;
      return ret;
}

main(int argc, char **argv)
{
      FILE   *f;
      char   *t;
      int     l, i;
      char   *buf;
      char   *h, *fullname;
      STRDEF *stdf;

      t = NULL;
      while ((i = getopt(argc, argv, "")) != EOF) {
            switch (i) {
            default:
                  usage();
                  exit(EXIT_USAGE);
            }
      }

      if (argc != 2) {
            usage();
            exit(EXIT_USAGE);
      }
      manpage = h = t = argv[1];
      i = 0;

      buf = read_man_page(h);
      if (!buf) {
            fprintf(stderr, "man2html: cannot read %s: %s\n", h, strerror(errno));
            exit(1);
      }
#ifdef MAKEINDEX
      idxfile = fopen(INDEXFILE, "a");
#endif
      stdf = &standardchar[0];
      i = 0;
      while (stdf->nr) {
            stdf->next = &standardchar[i];
            stdf = stdf->next;
            i++;
      }
      chardef = &standardchar[0];

      stdf = &standardstring[0];
      i = 0;
      while (stdf->nr) {
            stdf->next = &standardstring[i];
            stdf = stdf->next;
            i++;
      }
      strdef = &standardstring[0];

      intdef = &standardint[0];
      i = 0;
      while (intdef->nr) {
            intdef->next = &standardint[i];
            intdef = intdef->next;
            i++;
      }
      intdef = &standardint[0];

      defdef = NULL;

      scan_troff(buf + 1, 0, NULL);

      while (itemdepth || dl_set[itemdepth]) {
            out_html("</DL>\n");
            if (dl_set[itemdepth])
                  dl_set[itemdepth] = 0;
            else if (itemdepth > 0)
                  itemdepth--;
      }

      out_html(change_to_font(0));
      out_html(change_to_size(0));
      if (!fillout) {
            fillout = 1;
            out_html("</PRE>");
      }
      out_html(NEWLINE);

      if (output_possible) {
            /* &nbsp; for mosaic users */
            fputs("<HR>\n<A NAME=\"index\">&nbsp;</A><H2>Index</H2>\n<DL>\n", stdout);
            manidx[mip] = 0;
            fputs(manidx, stdout);
            if (subs)
                  fputs("</DL>\n", stdout);
            fputs("</DL>\n", stdout);
            print_sig();
            fputs("</BODY>\n</HTML>\n", stdout);
      } else
            fprintf(stderr, "man2html: no output produced\n");
#ifdef MAKEINDEX
      if (idxfile)
            fclose(idxfile);
#endif
      exit(EXIT_SUCCESS);
}

Generated by  Doxygen 1.6.0   Back to index