/* Copyright (c) 1995 DJ Delorie, 334 North Road, Deerfield NH USA
   Distributed under the terms of the GNU GPL, version 2 or later.
   Note: The HTML output of this program is NOT considered a derived
   work of this program.  */

/*
   Original work by dj@delorie.com

   Usage: man2html < program.man > program.html
*/



#include <stdio.h>
#include <ctype.h>
#include <string.h>

int in_tt = 0;
int next_line_dd = 0;
int need_undl = 0;
int got_regular_line = 0;
int need_re = 0;
int fill_mode = 1;
int current_BI = 0;
int skip_nl = 0;

int
process_line();


char *
get_token(char *inp, char *buf)
{
    int quoted = 0;
    /* skip whitespace */
    while (*inp && isspace(*inp))
        inp++;
    if (*inp == 0)
        return 0;

    while (*inp)
    {
        switch (*inp)
        {
        case '"':
            quoted = 1-quoted;
            break;
        case '\\':
            *buf++ = *inp;
            *buf++ = *++inp;
            break;
        default:
            if (isspace(*inp) && !quoted)
            {
                *buf = 0;
                return inp;
            }
            *buf++ = *inp;
            break;
        }
        inp++;
    }
    *buf = 0;
    return inp;
}

void
clean(char *cp)
{
    char foo[1000];
    char *rp = foo;
    char *ocp = cp;
    if (strncmp(cp, ".if t ", 6) == 0)
        cp += 6;
    while (*cp)
    {
        switch (*cp)
        {
        case '\\':
            cp++;
            switch (*cp)
            {
            case 'E':
            case 'F':
            case 'g':
            case 'b':
            case 'r':
            case 'B':
                *rp++ = '\\';
                *rp++ = *cp++;
                break;
            case '/':
            case '-':
            case '\\':
            case '+':
            case '.':
            case 10:
            case 0:
            case ' ':
            case '=':
            case '\'':
            case '`':
            case '[':
            case ']':
            case ':':
            case '}':
            case '{':
                *rp++ = *cp++;
                break;
            case '|':
            case '^':
            case '"':
            case 'd':
            case 'u':
            case 'n':
            case '&':
            case 'w':
            case '%':
            case 'v':
            case 'k':
                cp++;
                break;
            case 't':
                *rp++ = ' ';
                cp++;
                break;
            case '0':
                *rp++ = ' ';
                cp++;
                break;
            case 'c':
                if (cp[1] == '\n')
                {
                    skip_nl = 1;
                    cp++;
                }
                cp++;
                break;
            case 'e':
                *rp++ = '\\';
                cp++;
                break;
            case 's':
                cp++;
                cp++;
                while (isdigit(*cp))
                    cp++;
                break;
            case 'f':
                if (current_BI)
                {
                    *rp++ = '<';
                    *rp++ = '/';
                    *rp++ = current_BI;
                    *rp++ = '>';
                    current_BI = 0;
                }
                if (in_tt)
                {
                    strcpy(rp, "</tt>");
                    rp += 5;
                    in_tt = 0;
                }
                switch (*++cp)
                {
                case '(':
                    if (cp[1] == 'C' && cp[2] == 'W')
                    {
                        strcpy(rp, "<tt>");
                        rp += 4;
                        in_tt = 1;
                        cp += 2;
                    }
                    else
                        fprintf(stderr, "unknown font %.3s\n", cp);
                    break;
                case 'B':
                    current_BI = 'b';
                    *rp++ = '<';
                    *rp++ = 'b';
                    *rp++ = '>';
                    break;
                case 'R':
                case 'P':
                    break;
                case 'I':
                    current_BI = 'i';
                    *rp++ = '<';
                    *rp++ = 'i';
                    *rp++ = '>';
                    break;
                }
                cp++;
                break;
            case '*':
                cp++;
                if (cp[0] == '(')
                {
                    cp++;
                    if (cp[0] == 'l' && cp[1] == 'q')
                        *rp++ = '`';
                    else if (cp[0] == 'r' && cp[1] == 'q')
                        *rp++ = '\'';
                    else
                    {
                        sprintf(rp, "[%.2s]", cp);
                        rp += 4;
                    }
                    cp += 2;
                }
                else if (cp[0] == 'r')
                {
                    cp++;
                    strcpy(rp, "RCS");
                    rp += 3;
                }
                else
                {
                    sprintf(rp, "[%c]", *cp);
                    rp += 3;
                }
                break;
            case '(':
                if (cp[1] == 'c' && cp[2] == 'o')
                    *rp++ = (char)0xa9;
                else if (cp[1] == 'b' && cp[2] == 'v')
                    *rp++ = '|';
                else if (cp[1] == 'e' && cp[2] == 'm')
                    *rp++ = ' ';
                else if (cp[1] == '+' && cp[2] == '-')
                    *rp++ = (char)0xb1;
                else if (cp[1] == 't' && cp[2] == 'i')
                    *rp++ = '~';
                else if (cp[1] == 't' && cp[2] == 's')
                    *rp++ = '"';
                else if (cp[1] == 'p' && cp[2] == 'l')
                    *rp++ = '+';
                else if (cp[1] == 'm' && cp[2] == 'i')
                    *rp++ = '-';
                else if (cp[1] == 'f' && cp[2] == 'm')
                    *rp++ = '\'';
                else if (cp[1] == 'm' && cp[2] == 'u')
                    *rp++ = 'x';
                else if (cp[1] == 'b' && cp[2] == 'u')
                {
                    strcpy(rp, "<li>");
                    rp += 4;
                }
                else if (cp[1] == '>' && cp[2] == '=')
                {
                    *rp++ = '>';
                    *rp++ = '=';
                }
                else if (cp[1] == '*' && cp[2] == '*')
                {
                    *rp++ = '*';
                    *rp++ = '*';
                }
                else
                    fprintf(stderr, "unknown meta-character (%c%c\n", cp[1], cp[2]);
                cp += 3;
                break;
            default:
                fprintf(stderr, "unknown escape \\%c (%d)\n", *cp, *cp);
                break;
            }
            break;
        case '&':
            *rp++ = '&';
            *rp++ = 'a';
            *rp++ = 'm';
            *rp++ = 'p';
            *rp++ = ';';
            cp++;
            break;
        case '<':
            *rp++ = '&';
            *rp++ = 'l';
            *rp++ = 't';
            *rp++ = ';';
            cp++;
            break;
        case '>':
            *rp++ = '&';
            *rp++ = 'g';
            *rp++ = 't';
            *rp++ = ';';
            cp++;
            break;
        default:
            *rp++ = *cp++;
            break;
        }
    }
    *rp = 0;
    strcpy(ocp, foo);
}

int un_bi()
{
    if (current_BI)
    {
        printf("</%c>", current_BI);
        current_BI = 0;
    }
    return 0;
}

int
process_line_til_regular()
{
    got_regular_line = 0;
    while (!got_regular_line)
        process_line();
    return 0;
}

void
bol()
{
    got_regular_line = 1;
    if (next_line_dd)
        printf("<dd>");
    next_line_dd = 0;
}

void
eol()
{
    if (!fill_mode)
        printf("<br>");
}

void
twoggle(char *a, char *b, char *l)
{
    int first = 1;
    char *c;
    char buf[1000];
    bol();
    while ((l = get_token(l, buf)))
    {
        clean(buf);
        c = first ? a : b;
        if (c)
            printf("<%s>%s</%s>", c, buf, c);
        else
            printf("%s", buf);
        if (a && b && strcmp(a, b) == 0)
            putchar(' ');
        first = 1-first;
    }
    un_bi();
    if (!skip_nl)
        printf("\n");
    eol();
    got_regular_line = 1;
}

int
process_line()
{
    char buf[1000], cmd[10];
    char token[1000];
    if (fgets(buf, 1000, stdin) == 0)
        return 0;

    skip_nl = 0;
    if (buf[0] != '.')
    {
        if (strncmp(buf, "'\\\"", 3) == 0)
            return 1;
        clean(buf);
        bol();
        fputs(buf, stdout);
        if (buf[0] == 0 || buf[0] == '\n')
            printf("<p>");
        eol();
        return 1;
    }

    if (sscanf(buf, "%s %[^\n]", cmd, buf) == 1)
        buf[0] = 0;
    if (strcmp(cmd, "..") == 0)
    {
    }
    else if (strcmp(cmd, ".B") == 0)
    {
        if (buf[0])
            twoggle("b", "b", buf);
        else
        {
            printf("<b>");
            process_line_til_regular();
            printf("</b>");
        }
    }
    else if (strcmp(cmd, ".I") == 0)
    {
        if (buf[0])
            twoggle("i", "i", buf);
        else
        {
            printf("<i>");
            process_line_til_regular();
            printf("</i>");
        }
    }
    else if (strcmp(cmd, ".BI") == 0)
        twoggle("b", "i", buf);
    else if (strcmp(cmd, ".IB") == 0)
        twoggle("i", "b", buf);
    else if (strcmp(cmd, ".BR") == 0)
        twoggle("b", 0, buf);
    else if (strcmp(cmd, ".RB") == 0)
        twoggle(0, "b", buf);
    else if (strcmp(cmd, ".IR") == 0)
        twoggle("i", 0, buf);
    else if (strcmp(cmd, ".RI") == 0)
        twoggle(0, "i", buf);
    else if (strcmp(cmd, ".nf") == 0)
    {
        if (fill_mode)
            printf("<pre>\n");
        fill_mode = 0;
    }
    else if (strcmp(cmd, ".fi") == 0)
    {
        if (!fill_mode)
            printf("</pre>\n");
        fill_mode = 1;
    }
    else if (strcmp(cmd, ".br") == 0
             || strcmp(cmd, ".Sp") == 0
             || strcmp(cmd, ".ti") == 0)
    {
        if (need_undl)
        {
            need_undl = 0;
            printf("</dl>");
        }
        printf("<br>\n");
    }
    else if (strcmp(cmd, ".LP") == 0
             || strcmp(cmd, ".PP") == 0
             || strcmp(cmd, ".sp") == 0
             || strcmp(cmd, ".P") == 0)
    {
        if (need_undl)
        {
            need_undl = 0;
            printf("</dl>");
        }
        printf("\n<p>\n");
    }
    else if (strcmp(cmd, ".RS") == 0)
    {
        printf("<ul>");
        need_re ++;
    }
    else if (strcmp(cmd, ".RE") == 0)
    {
        if (need_re)
        {
            printf("</ul>");
            need_re --;
        }
    }
    else if (strcmp(cmd, ".SH") == 0
             || strcmp(cmd, ".SS") == 0)
    {
        char *cp = buf;
        int got_token = 0;
        while (need_re)
        {
            printf("</ul>");
            need_re--;
        }
        if (need_undl)
        {
            printf("</dl>");
            need_undl = 0;
        }
        printf("\n</ul><H2>");
        while ((cp = get_token(cp, token)))
        {
            got_token = 1;
            clean(token);
            printf("%s ", token);
        }
        if (!got_token)
        {
            if (fgets(buf, 1000, stdin) == 0)
                return 0;
            printf("%s", buf);
        }
        printf("</H2><ul>\n\n");
        un_bi();
        got_regular_line = 1;
        if (!fill_mode)
            printf("</pre>");
        fill_mode = 1;
    }
    else if (strcmp(cmd, ".SM") == 0)
    {
        if (buf[0])
        {
            bol();
            clean(buf);
            printf("<code>%s</code>\n", buf);
            eol();
        }
        else
        {
            printf("<code>");
            process_line_til_regular();
            printf("</code>");
        }
    }
    else if (strcmp(cmd, ".TH") == 0)
    {
        int all_upper = 1, i;
        get_token(buf, buf);
        for (i=0; buf[i]; i++)
            if (islower(buf[i]))
                all_upper = 0;
        if (all_upper)
            for (i=0; buf[i]; i++)
                if (isupper(buf[i]))
                    buf[i] = tolower(buf[i]);
        printf("<!--#exec cmd=\"header %s\" -->\n", buf);
        printf("<ul>");
    }
    else if (strcmp(cmd, ".TP") == 0
             || strcmp(cmd, ".Tp") == 0)
    {
        if (!need_undl)
        {
            printf("<p><dl compact>");
            need_undl = 1;
        }
        printf("<dt>");
        next_line_dd = 0;
        process_line_til_regular();
        next_line_dd = 1;
    }
    else if (strcmp(cmd, ".IP") == 0)
    {
        if (!need_undl)
        {
            printf("<p><dl compact>");
            need_undl = 1;
        }
        get_token(buf, buf);
        clean(buf);
        printf("<dt>%s", buf);
        next_line_dd = 1;
    }
    else if (strcmp(cmd, ".TQ") == 0)
    {
        printf("<dt>");
        next_line_dd = 0;
        process_line_til_regular();
        next_line_dd = 1;
    }
    else if (strcmp(cmd, ".FN") == 0)
    {
        bol();
        get_token(buf, buf);
        printf("<code>%s</code>\n", buf);
        got_regular_line = 1;
        eol();
    }
    /* Tcl macros */
    else if (strcmp(cmd, ".AP") == 0)
    {
        char *cp = buf;
        cp = get_token(cp, token);
        printf("<p>%s", token);
        cp = get_token(cp, token);
        printf(" <b>%s</b>", token);
        cp = get_token(cp, token);
        printf(" (<i>%s</i>) -\n", token);
    }
    else if (strcmp(cmd, ".DS") == 0)
        printf("<pre>\n");
    else if (strcmp(cmd, ".DE") == 0)
        printf("</pre>\n");
    /* end of Tcl macros */
    else if (strcmp(cmd, ".\"") == 0)
    {
    }
    else if (strcmp(cmd, ".de") == 0)
    {
        do
        {
            if (fgets(buf, 1000, stdin) == 0)
                return 0;
        }
        while (buf[0] != '.' || buf[1] != '.');
    }

    return 1;
}


int main()
{
    while (process_line());
    printf("</ul>\n<!--#exec cmd=\"trailer\" -->\n");
    return 0;
}

/*
 * Local Variables: ***
 * mode: C++ ***
 * tab-width: 8 ***
 * c-basic-offset: 2 ***
 * indent-tabs-mode: t ***
 * End: ***
 * ex: shiftwidth=2 tabstop=8
 */
