]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - man/mantohtml.c
Initial work on man page modernization (STR #4372)
[thirdparty/cups.git] / man / mantohtml.c
index 3c18e82a5d67bf05dcded2d10c5a6403fb9ce108..1f0b6aaa67452f52b9de04df1bba4c929ceae5b5 100644 (file)
@@ -1,22 +1,16 @@
 /*
- * "$Id: mantohtml.c 7720 2008-07-11 22:46:21Z mike $"
+ * "$Id$"
  *
- *   Man page to HTML conversion program.
+ * Man page to HTML conversion program.
  *
- *   Copyright 2007-2010 by Apple Inc.
- *   Copyright 2004-2006 by Easy Software Products.
+ * Copyright 2007-2010, 2014 by Apple Inc.
+ * Copyright 2004-2006 by Easy Software Products.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * Contents:
- *
- *   main()        - Convert a man page to HTML.
- *   putc_entity() - Put a single character, using entities as needed.
- *   strmove()     - Move characters within a string.
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  */
 
 /*
  */
 
 #include <cups/string-private.h>
+#include <cups/array-private.h>
 #include <unistd.h>
 
 
+/*
+ * Local globals...
+ */
+
+static const char                      /* Start/end tags for fonts */
+       * const start_fonts[] = { "", "<b>", "<i>" },
+       * const end_fonts[] = { "", "</b>", "</i>" };
+
+
 /*
  * Local functions...
  */
 
-static void    putc_entity(int ch, FILE *fp);
+static void    html_alternate(const char *s, const char *first, const char *second, FILE *fp);
+static void    html_fputs(const char *s, int *font, FILE *fp);
+static void    html_putc(int ch, FILE *fp);
 static void    strmove(char *d, const char *s);
 
 
@@ -47,20 +53,14 @@ main(int  argc,                             /* I - Number of command-line args */
                *outfile;               /* Output file */
   char         line[1024],             /* Line from file */
                *lineptr,               /* Pointer into line */
-               *endptr,                /* Pointer to end of current */
-               endchar,                /* End character */
-               *paren,                 /* Pointer to parenthesis */
                name[1024];             /* Man page name */
-  int          section,                /* Man page section */
-               pre,                    /* Preformatted */
-               font,                   /* Current font */
-               blist,                  /* In a bullet list? */
-               list,                   /* In a list? */
-               linenum;                /* Current line number */
-  const char   *post;                  /* Text to add after the current line */
-  static const char                    /* Start/end tags for fonts */
-       * const start_fonts[] = { "", "<b>", "<i>" },
-       * const end_fonts[] = { "", "</b>", "</i>" };
+  int          section = -1,           /* Man page section */
+               pre = 0,                /* Preformatted */
+               font = 0,               /* Current font */
+               linenum = 0;            /* Current line number */
+  const char   *list = NULL;           /* Current list, if any */
+  const char   *post = NULL;           /* Text to add after the current line */
+
 
  /*
   * Check arguments...
@@ -103,24 +103,20 @@ main(int  argc,                           /* I - Number of command-line args */
   * Read from input and write the output...
   */
 
-  fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
-        "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
+  fputs("<!DOCTYPE HTML>\n"
         "<html>\n"
        "<!-- SECTION: Man Pages -->\n"
        "<head>\n"
        "\t<link rel=\"stylesheet\" type=\"text/css\" "
        "href=\"../cups-printable.css\">\n", outfile);
 
-  blist   = 0;
-  font    = 0;
-  list    = 0;
-  linenum = 0;
-  pre     = 0;
-  post    = NULL;
-  section = -1;
-
   while (fgets(line, sizeof(line), infile))
   {
+    size_t linelen = strlen(line);     /* Length of line */
+
+    if (linelen > 0 && line[linelen - 1] == '\n')
+      line[linelen - 1] = '\0';
+
     linenum ++;
 
     if (line[0] == '.')
@@ -163,247 +159,390 @@ main(int  argc,                         /* I - Number of command-line args */
         int first = 1;
 
        fputs(end_fonts[font], outfile);
-
-        if (blist)
-       {
-         fputs("</li>\n</ul>\n", outfile);
-         blist = 0;
-       }
+       font = 0;
 
         if (list)
        {
-         if (list == 1)
-           fputs("</dt>\n", outfile);
-         else if (list)
-           fputs("</dd>\n", outfile);
-
-         fputs("</dl>\n", outfile);
-         list = 0;
+         fprintf(outfile, "</%s>\n", list);
+         list = NULL;
        }
 
-        line[strlen(line) - 1] = '\0'; /* Strip LF */
-
         if (line[2] == 'H')
          fputs("<h2 class=\"title\"><a name=\"", outfile);
        else
          fputs("<h3><a name=\"", outfile);
 
         for (lineptr = line + 4; *lineptr; lineptr ++)
-         if (*lineptr == '\"')
+         if (*lineptr  == '\"')
            continue;
-         else if (*lineptr == ' ')
-           putc_entity('_', outfile);
+         else if (isalnum(*lineptr & 255))
+           html_putc(*lineptr, outfile);
          else
-           putc_entity(*lineptr, outfile);
+           html_putc('_', outfile);
 
        fputs("\">", outfile);
 
         for (lineptr = line + 4; *lineptr; lineptr ++)
+        {
          if (*lineptr == '\"')
            continue;
          else if (*lineptr == ' ')
          {
-           putc_entity(' ', outfile);
+           html_putc(' ', outfile);
 
             first = 1;
          }
          else
          {
            if (first)
-             putc_entity(*lineptr, outfile);
+             html_putc(*lineptr, outfile);
            else
-             putc_entity(tolower(*lineptr), outfile);
+             html_putc(tolower(*lineptr & 255), outfile);
 
             first = 0;
           }
+        }
 
         if (line[2] == 'H')
-         fprintf(outfile, "</a></h2>\n%s", start_fonts[font]);
+         fputs("</a></h2>\n", outfile);
        else
-         fprintf(outfile, "</a></h3>\n%s", start_fonts[font]);
+         fputs("</a></h3>\n", outfile);
       }
-      else if (!strncmp(line, ".LP", 3) || !strncmp(line, ".PP", 3))
+      else if (!strncmp(line, ".", 3))
       {
        /*
-        * New paragraph...
+        * Grab bold text...
        */
 
        fputs(end_fonts[font], outfile);
+       font = 0;
 
-        if (blist)
-       {
-         fputs("</li>\n</ul>\n", outfile);
-         blist = 0;
-       }
+        html_alternate(line + 3, "b", "b", outfile);
+      }
+      else if (!strncmp(line, ".I ", 3))
+      {
+       /*
+        * Grab italic text...
+       */
 
-        if (list)
-       {
-         if (list == 1)
-           fputs("</dt>\n", outfile);
-         else if (list)
-           fputs("</dd>\n", outfile);
+       fputs(end_fonts[font], outfile);
+       font = 0;
 
-         fputs("</dl>\n", outfile);
-         list = 0;
-       }
+        html_alternate(line + 3, "i", "i", outfile);
+      }
+      else if (!strncmp(line, ".BI ", 4))
+      {
+       /*
+        * Alternating bold and italic text...
+        */
 
-       fputs("<p>", outfile);
+       fputs(end_fonts[font], outfile);
        font = 0;
+
+        html_alternate(line + 4, "b", "i", outfile);
       }
-      else if (!strncmp(line, ".TP ", 4))
+      else if (!strncmp(line, ".BR ", 4))
       {
        /*
-        * Grab list...
-       */
+        * Alternating bold and roman (plain) text...
+        */
 
        fputs(end_fonts[font], outfile);
+       font = 0;
 
-        if (blist)
-       {
-         fputs("</li>\n</ul>\n", outfile);
-         blist = 0;
-       }
+        html_alternate(line + 4, "b", NULL, outfile);
+      }
+      else if (!strncmp(line, ".IB ", 4))
+      {
+       /*
+        * Alternating italic and bold text...
+        */
 
-        if (!list)
-         fputs("<dl>\n", outfile);
-       else if (list == 1)
-         fputs("</dt>\n", outfile);
-       else if (list)
-         fputs("</dd>\n", outfile);
+       fputs(end_fonts[font], outfile);
+       font = 0;
 
-       fputs("<dt>", outfile);
-       list = 1;
+        html_alternate(line + 4, "i", "b", outfile);
+      }
+      else if (!strncmp(line, ".IR ", 4))
+      {
+       /*
+        * Alternating italic and roman (plain) text...
+        */
+
+       fputs(end_fonts[font], outfile);
        font = 0;
+
+        html_alternate(line + 4, "i", NULL, outfile);
       }
-      else if (!strncmp(line, ".br", 3))
+      else if (!strncmp(line, ".RB ", 4))
       {
        /*
-        * Grab line break...
-       */
+        * Alternating roman (plain) and bold text...
+        */
 
-       if (list == 1)
-       {
-         fputs("</dt>\n<dd>", outfile);
-         list = 2;
-       }
-        else if (list)
-         fputs("</dd>\n<dd>", outfile);
-       else
-         fputs("<br>\n", outfile);
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        html_alternate(line + 4, NULL, "b", outfile);
       }
-      else if (!strncmp(line, ".de ", 4))
+      else if (!strncmp(line, ".RI ", 4))
       {
        /*
-        * Define macro - ignore...
-       */
+        * Alternating roman (plain) and italic text...
+        */
 
-        while (fgets(line, sizeof(line), infile))
-       {
-         linenum ++;
+       fputs(end_fonts[font], outfile);
+       font = 0;
 
-         if (!strncmp(line, "..", 2))
-           break;
-       }
+        html_alternate(line + 4, NULL, "i", outfile);
       }
-      else if (!strncmp(line, ".RS", 3))
+      else if (!strncmp(line, ".SB ", 4))
       {
        /*
-        * Indent...
-       */
+        * Alternating small and bold text...
+        */
+
+       fputs(end_fonts[font], outfile);
+       font = 0;
 
-        fputs("<div style='margin-left: 3em;'>\n", outfile);
+        html_alternate(line + 4, "small", "b", outfile);
       }
-      else if (!strncmp(line, ".RE", 3))
+      else if (!strncmp(line, ".SM ", 4))
       {
        /*
-        * Unindent...
-       */
+        * Small text...
+        */
 
-        fputs("</div>\n", outfile);
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        html_alternate(line + 4, "small", "small", outfile);
       }
-      else if (!strncmp(line, ".ds ", 4) || !strncmp(line, ".rm ", 4) ||
-               !strncmp(line, ".tr ", 4) || !strncmp(line, ".hy ", 4) ||
-               !strncmp(line, ".IX ", 4) || !strncmp(line, ".PD", 3) ||
-              !strncmp(line, ".Sp", 3))
+      else if (!strcmp(line, ".LP") || !strcmp(line, ".PP") || !strcmp(line, ".P"))
       {
        /*
-        * Ignore unused commands...
+        * New paragraph...
        */
+
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        if (list)
+        {
+          fprintf(outfile, "</%s>\n", list);
+          list = NULL;
+        }
+
+       fputs("<p>", outfile);
       }
-      else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3))
+      else if (!strcmp(line, ".RS") || !strncmp(line, ".RS ", 4))
       {
        /*
-        * Start preformatted...
+        * Indent...
        */
 
-        pre = 1;
-       fputs("<pre>\n", outfile);
+       float amount = 3.0;             /* Indentation */
+
+        if (line[3])
+          amount = atof(line + 4);
+
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        if (list)
+        {
+          fprintf(outfile, "</%s>\n", list);
+          list = NULL;
+        }
+
+        fprintf(outfile, "<div style=\"margin-left: %.1fem;\">\n", amount);
       }
-      else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3))
+      else if (!strcmp(line, ".RE"))
       {
        /*
-        * End preformatted...
+        * Unindent...
        */
 
-        if (pre)
-       {
-          pre = 0;
-         fputs("</pre>\n", outfile);
-       }
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        fputs("</div>\n", outfile);
       }
-      else if (!strncmp(line, ".IP \\(bu", 8))
+      else if (!strcmp(line, ".HP") || !strncmp(line, ".HP ", 4) ||
+               !strcmp(line, ".TP") || !strncmp(line, ".TP ", 4))
       {
        /*
-        * Bullet list...
+        * Hanging paragraph/tagged list...
+        *
+        * .HP i
+        * .TP i
        */
 
-        if (blist)
-         fputs("</li>\n", outfile);
-       else
-       {
-         fputs("<ul>\n", outfile);
-         blist = 1;
-       }
+       float amount = 3.0;             /* Indentation */
+
+        if (line[3])
+          amount = atof(line + 4);
 
-       fputs("<li>", outfile);
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        if (list)
+        {
+          fprintf(outfile, "</%s>\n", list);
+          list = NULL;
+        }
+
+        fprintf(outfile, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount, -amount);
+
+        if (line[1] == 'T')
+          post = "<br>\n";
       }
       else if (!strncmp(line, ".IP ", 4))
       {
        /*
         * Indented paragraph...
+        *
+        * .IP x i
        */
 
-        if (blist)
+        float amount = 3.0;            /* Indentation */
+        const char *newlist = NULL;    /* New list style */
+        const char *newtype = NULL;    /* New list numbering type */
+
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        lineptr = line + 4;
+        while (isspace(*lineptr & 255))
+          lineptr ++;
+
+        if (!strncmp(lineptr, "\\(bu", 4) || !strncmp(lineptr, "\\(em", 4))
        {
-         fputs("</li>\n</ul>\n", outfile);
-         blist = 0;
-       }
+        /*
+         * Bullet list...
+         */
 
-       fputs("<p style='margin-left: 3em;'>", outfile);
+          newlist = "ul";
+       }
+       else if (isdigit(*lineptr & 255))
+       {
+        /*
+         * Numbered list...
+         */
 
-        for (lineptr = line + 4; isspace(*lineptr); lineptr ++);
+          newlist = "ol";
+       }
+       else if (islower(*lineptr & 255))
+       {
+        /*
+         * Lowercase alpha list...
+         */
 
-        if (*lineptr == '\"')
+          newlist = "ol";
+          newtype = "a";
+       }
+       else if (isupper(*lineptr & 255))
        {
-         strmove(line, lineptr + 1);
+        /*
+         * Lowercase alpha list...
+         */
+
+          newlist = "ol";
+          newtype = "A";
+       }
+
+        while (!isspace(*lineptr & 255))
+          lineptr ++;
+        while (isspace(*lineptr & 255))
+          lineptr ++;
+
+        if (isdigit(*lineptr & 255))
+          amount = atof(lineptr);
 
-         if ((lineptr = strchr(line, '\"')) != NULL)
-           *lineptr = '\0';
+        if (newlist && list && strcmp(newlist, list))
+        {
+          fprintf(outfile, "</%s>\n", list);
+          list = NULL;
         }
-       else
-       {
-         strmove(line, lineptr);
 
-         if ((lineptr = strchr(line, ' ')) != NULL)
-           *lineptr = '\0';
+        if (newlist && !list)
+        {
+          if (newtype)
+            fprintf(outfile, "<%s type=\"%s\">\n", newlist, newtype);
+          else
+            fprintf(outfile, "<%s>\n", newlist);
+
+          list = newlist;
         }
 
+        if (list)
+          fprintf(outfile, "<li style=\"margin-left: %.1fem;\">", amount);
+        else
+          fprintf(outfile, "<p style=\"margin-left: %.1fem;\">", amount);
+      }
+      else if (!strncmp(line, ".br", 3))
+      {
+       /*
+        * Grab line break...
+       */
+
+       fputs("<br>\n", outfile);
+      }
+      else if (!strncmp(line, ".de ", 4))
+      {
+       /*
+        * Define macro - ignore...
+       */
+
+        while (fgets(line, sizeof(line), infile))
+       {
+         linenum ++;
+
+         if (!strncmp(line, "..", 2))
+           break;
+       }
+      }
+      else if (!strncmp(line, ".ds ", 4) || !strncmp(line, ".rm ", 4) ||
+               !strncmp(line, ".tr ", 4) || !strncmp(line, ".hy ", 4) ||
+               !strncmp(line, ".IX ", 4) || !strncmp(line, ".PD", 3) ||
+              !strncmp(line, ".Sp", 3))
+      {
+       /*
+        * Ignore unused commands...
+       */
+      }
+      else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3))
+      {
+       /*
+        * Start preformatted...
+       */
+
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        if (list)
+       {
+         fprintf(outfile, "</%s>\n", list);
+         list = NULL;
+       }
+
+        pre = 1;
+       fputs("<pre>\n", outfile);
+      }
+      else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3))
+      {
        /*
-        * Process the text as if it was in-line...
+        * End preformatted...
        */
 
-        post = "\n<br>\n<br>";
-        goto process_text;
+       fputs(end_fonts[font], outfile);
+       font = 0;
+
+        if (pre)
+       {
+          pre = 0;
+         fputs("</pre>\n", outfile);
+       }
       }
       else if (!strncmp(line, ".\\}", 3))
       {
@@ -441,24 +580,6 @@ main(int  argc,                            /* I - Number of command-line args */
        */
       }
 #endif /* 0 */
-      else if (!strncmp(line, ".B ", 3))
-      {
-       /*
-        * Grab bold text...
-       */
-
-       fprintf(outfile, "%s<b>%s</b>%s", end_fonts[font], line + 3,
-               start_fonts[font]);
-      }
-      else if (!strncmp(line, ".I ", 3))
-      {
-       /*
-        * Grab italic text...
-       */
-
-       fprintf(outfile, "%s<i>%s</i>%s", end_fonts[font], line + 3,
-               start_fonts[font]);
-      }
       else if (strncmp(line, ".\\\"", 3))
       {
        /*
@@ -470,15 +591,14 @@ main(int  argc,                           /* I - Number of command-line args */
        else if ((lineptr = strchr(line, '\n')) != NULL)
          *lineptr = '\0';
 
-        fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d!\n",
-               line, linenum);
+        fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d.\n",  line, linenum);
       }
 
      /*
       * Skip continuation lines...
       */
 
-      lineptr = line + strlen(line) - 2;
+      lineptr = line + strlen(line) - 1;
       if (lineptr >= line && *lineptr == '\\')
       {
         while (fgets(line, sizeof(line), infile))
@@ -497,169 +617,24 @@ main(int  argc,                          /* I - Number of command-line args */
       * Process man page text...
       */
 
-process_text:
-
-      for (lineptr = line; *lineptr; lineptr ++)
-      {
-        if (!strncmp(lineptr, "http://", 7))
-       {
-        /*
-         * Embed URL...
-         */
-
-          for (endptr = lineptr + 7;
-              *endptr && !isspace(*endptr & 255);
-              endptr ++);
-
-          endchar = *endptr;
-         *endptr = '\0';
-
-          fprintf(outfile, "<a href='%s'>%s</a>", lineptr, lineptr);
-         *endptr = endchar;
-         lineptr = endptr - 1;
-       }
-       else if (!strncmp(lineptr, "\\fI", 3) &&
-                (endptr = strstr(lineptr, "\\fR")) != NULL &&
-                (paren = strchr(lineptr, '(')) != NULL &&
-                paren < endptr)
-        {
-        /*
-         * Link to man page?
-         */
-
-          char manfile[1024],          /* Man page filename */
-               manurl[1024];           /* Man page URL */
-
-
-         /*
-         * See if the man file is available locally...
-         */
-
-          lineptr += 3;
-         endchar = *paren;
-         *paren  = '\0';
-
-         snprintf(manfile, sizeof(manfile), "%s.man", lineptr);
-         snprintf(manurl, sizeof(manurl), "man-%s.html?TOPIC=Man+Pages",
-                  lineptr);
-
-         *paren  = endchar;
-         endchar = *endptr;
-         *endptr = '\0';
-
-         if (access(manfile, 0))
-         {
-          /*
-           * Not a local man page, just do it italic...
-           */
-
-           fputs("<i>", outfile);
-           while (*lineptr)
-             putc_entity(*lineptr++, outfile);
-           fputs("</i>", outfile);
-         }
-         else
-         {
-          /*
-           * Local man page, do a link...
-           */
-
-           fprintf(outfile, "<a href='%s'>", manurl);
-           while (*lineptr)
-             putc_entity(*lineptr++, outfile);
-           fputs("</a>", outfile);
-         }
-
-          *endptr = endchar;
-         lineptr = endptr + 2;
-       }
-        else if (*lineptr == '\\')
-       {
-         lineptr ++;
-         if (!*lineptr)
-           break;
-         else if (isdigit(lineptr[0]) && isdigit(lineptr[1]) &&
-                  isdigit(lineptr[2]))
-         {
-           fprintf(outfile, "&#%d;", ((lineptr[0] - '0') * 8 +
-                                      lineptr[1] - '0') * 8 +
-                                     lineptr[2] - '0');
-           lineptr += 2;
-         }
-         else if (*lineptr == '&')
-           continue;
-         else if (*lineptr == 's')
-         {
-           while (lineptr[1] == '-' || isdigit(lineptr[1]))
-             lineptr ++;
-         }
-         else if (*lineptr == '*')
-         {
-           lineptr += 2;
-         }
-         else if (*lineptr != 'f')
-           putc_entity(*lineptr, outfile);
-         else
-         {
-           lineptr ++;
-           if (!*lineptr)
-             break;
-           else
-           {
-             fputs(end_fonts[font], outfile);
-
-             switch (*lineptr)
-             {
-               default : /* Regular */
-                   font = 0;
-                   break;
-               case 'B' : /* Bold */
-               case 'b' :
-                   font = 1;
-                   break;
-               case 'I' : /* Italic */
-               case 'i' :
-                   font = 2;
-                   break;
-             }
-
-             fputs(start_fonts[font], outfile);
-           }
-         }
-       }
-       else
-         putc_entity(*lineptr, outfile);
-      }
+      html_fputs(line, &font, outfile);
+      putc('\n', outfile);
 
       if (post)
       {
         fputs(post, outfile);
        post = NULL;
       }
-
-      if (list == 1)
-      {
-       fputs("</dt>\n<dd>", outfile);
-       list = 2;
-      }
     }
   }
 
   fprintf(outfile, "%s\n", end_fonts[font]);
-
-  if (blist)
-  {
-    fputs("</li>\n</ul>\n", outfile);
-  }
+  font = 0;
 
   if (list)
   {
-    if (list == 1)
-      fputs("</dt>\n", outfile);
-    else if (list)
-      fputs("</dd>\n", outfile);
-
-    fputs("</dl>\n", outfile);
+    fprintf(outfile, "</%s>\n", list);
+    list = NULL;
   }
 
   fputs("</body>\n"
@@ -684,12 +659,280 @@ process_text:
 
 
 /*
- * 'putc_entity()' - Put a single character, using entities as needed.
+ * 'html_alternate()' - Alternate words between two styles of text.
+ */
+
+static void
+html_alternate(const char *s,          /* I - String */
+               const char *first,      /* I - First style or NULL */
+               const char *second,     /* I - Second style of NULL */
+               FILE       *fp)         /* I - File */
+{
+  int          i = 0;                  /* Which style */
+  int          quote = 0;              /* Saw quote? */
+  int          dolinks,                /* Do hyperlinks to other man pages? */
+               link = 0;               /* Doing a link now? */
+
+
+ /*
+  * Skip leading whitespace...
+  */
+
+  while (isspace(*s & 255))
+    s ++;
+
+  dolinks = first && !strcmp(first, "b") && !second;
+
+  while (*s)
+  {
+    if (!i && dolinks)
+    {
+     /*
+      * See if we need to make a link to a man page...
+      */
+
+      const char *end;                 /* End of current word */
+      const char *next;                        /* Start of next word */
+
+      for (end = s; *end && !isspace(*end & 255); end ++);
+      for (next = end; isspace(*next & 255); next ++);
+
+      if (isalnum(*s & 255) && *next == '(')
+      {
+       /*
+       * See if the man file is available locally...
+       */
+
+       char    name[1024],             /* Name */
+               manfile[1024],          /* Man page filename */
+               manurl[1024];           /* Man page URL */
+
+        strlcpy(name, s, sizeof(name));
+        if ((size_t)(end - s) < sizeof(name))
+          name[end - s] = '\0';
+
+        snprintf(manfile, sizeof(manfile), "%s.man", name);
+       snprintf(manurl, sizeof(manurl), "man-%s.html?TOPIC=Man+Pages", name);
+
+       if (!access(manfile, 0))
+       {
+        /*
+         * Local man page, do a link...
+         */
+
+         fprintf(fp, "<a href=\"%s\">", manurl);
+         link = 1;
+       }
+      }
+    }
+
+    if (!i && first)
+      fprintf(fp, "<%s>", first);
+    else if (i && second)
+      fprintf(fp, "<%s>", second);
+
+    while ((!isspace(*s & 255) || quote) && *s)
+    {
+      if (*s == '\"')
+        quote = !quote;
+      else if (*s == '\\' && s[1])
+      {
+        s ++;
+        html_putc(*s++, fp);
+      }
+      else
+        html_putc(*s++, fp);
+    }
+
+    if (!i && first)
+      fprintf(fp, "</%s>", first);
+    else if (i && second)
+      fprintf(fp, "</%s>", second);
+
+    if (i && link)
+    {
+      fputs("</a>", fp);
+      link = 0;
+    }
+
+    i = 1 - i;
+
+   /*
+    * Skip trailing whitespace...
+    */
+
+    while (isspace(*s & 255))
+      s ++;
+
+    if (*s && *s != '(')
+      putc(' ', fp);
+  }
+
+  putc('\n', fp);
+}
+
+/*
+ * 'html_fputs()' - Output a string, quoting as needed HTML entities.
+ */
+
+static void
+html_fputs(const char *s,              /* I  - String */
+           int        *font,           /* IO - Font */
+           FILE       *fp)             /* I  - File */
+{
+  while (*s)
+  {
+    if (*s == '\\')
+    {
+      s ++;
+      if (!*s)
+       break;
+
+      if (*s == 'f')
+      {
+        int    newfont;                /* New font */
+
+        s ++;
+        if (!*s)
+          break;
+
+        if (!font)
+        {
+          s ++;
+          continue;
+        }
+
+        switch (*s++)
+        {
+          case 'R' :
+          case 'P' :
+              newfont = 0;
+              break;
+
+          case 'b' :
+          case 'B' :
+              newfont = 1;
+              break;
+
+          case 'i' :
+          case 'I' :
+              newfont = 2;
+              break;
+
+          default :
+              fprintf(stderr, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s[-1]);
+              newfont = *font;
+              break;
+        }
+
+        if (newfont != *font)
+        {
+         fputs(end_fonts[*font], fp);
+         *font = newfont;
+         fputs(start_fonts[*font], fp);
+       }
+      }
+      else if (*s == '*')
+      {
+       /*
+        * Substitute macro...
+        */
+
+        s ++;
+        if (!*s)
+          break;
+
+        switch (*s++)
+        {
+          case 'R' :
+              fputs("&reg;", fp);
+              break;
+
+          case '(' :
+             if (!strncmp(s, "lq", 2))
+               fputs("&ldquo;", fp);
+             else if (!strncmp(s, "rq", 2))
+               fputs("&rdquo;", fp);
+              else if (!strncmp(s, "Tm", 2))
+                fputs("<sup>TM</sup>", fp);
+              else
+                fprintf(stderr, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s);
+
+              if (*s)
+                s ++;
+              if (*s)
+                s ++;
+              break;
+
+          default :
+              fprintf(stderr, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s[-1]);
+              break;
+        }
+      }
+      else if (*s == '[')
+      {
+       /*
+        * Substitute escaped character...
+        */
+
+        s ++;
+       if (!strncmp(s, "co]", 3))
+         fputs("&copy;", fp);
+       else if (!strncmp(s, "de]", 3))
+         fputs("&deg;", fp);
+        else if (!strncmp(s, "rg]", 3))
+         fputs("&reg;", fp);
+       else if (!strncmp(s, "tm]", 3))
+         fputs("<sup>TM</sup>", fp);
+
+       if (*s)
+         s ++;
+       if (*s)
+         s ++;
+       if (*s)
+         s ++;
+      }
+      else if (isdigit(s[0]) && isdigit(s[1]) &&
+              isdigit(s[2]))
+      {
+       fprintf(fp, "&#%d;", ((s[0] - '0') * 8 + s[1] - '0') * 8 + s[2] - '0');
+       s += 3;
+      }
+      else
+      {
+        if (*s != '\\' && *s == '\"' && *s == '\'' && *s == '-')
+          fprintf(stderr, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s);
+
+        html_putc(*s++, fp);
+      }
+    }
+    else if (!strncmp(s, "http://", 7) || !strncmp(s, "https://", 8) || !strncmp(s, "ftp://", 6))
+    {
+     /*
+      * Embed URL...
+      */
+
+      const char *end = s + 6;         /* End of URL */
+
+      while (*end && !isspace(*end & 255))
+       end ++;
+
+      fprintf(fp, "<a href=\"%*s\">%*s</a>", (int)(end - s), s, (int)(end - s), s);
+      s = end;
+    }
+    else
+      html_putc(*s++ & 255, fp);
+  }
+}
+
+
+/*
+ * 'html_putc()' - Put a single character, using entities as needed.
  */
 
 static void
-putc_entity(int  ch,                   /* I - Character */
-            FILE *fp)                  /* I - File */
+html_putc(int  ch,                     /* I - Character */
+         FILE *fp)                     /* I - File */
 {
   if (ch == '&')
     fputs("&amp;", fp);
@@ -716,5 +959,5 @@ strmove(char       *d,                      /* I - Destination */
 
 
 /*
- * End of "$Id: mantohtml.c 7720 2008-07-11 22:46:21Z mike $".
+ * End of "$Id$".
  */