view mkbcmap.c @ 945:81184d58133c aprilli2011

Sync.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 May 2010 11:18:20 +0000
parents 51fe97e83115
children 5c2bd0ac3118
line wrap: on
line source

/*
 * mkbcmap - Create interactive XHTML+CSS+JS map from input
 * Programmed by Matti 'ccr' Hämäläinen (Ggr Pupunen)
 * (C) Copyright 2006-2009 Tecnic Software productions (TNSP)
 */
#include "maputils.h"
#include "th_args.h"
#include "th_string.h"


typedef struct {
    char c;
    char *desc;
    char *extra;
} location_t;

enum {
    OUT_ASCII,
    OUT_HTML
};

#define MAX_LOC        (512)
int         nlocations = 0;
location_t  locations[MAX_LOC];

char    *optDestFilename = NULL,
        *optMapFilename = NULL,
        *optDescFilename = NULL,
        *optUrchinFile = NULL,
        *optMapTitle = NULL;
        
BOOL    optUseTable = FALSE;
int     optOutputFormat = OUT_HTML;


/* Arguments
 */
optarg_t optList[] = {
    { 0, '?', "help",           "Show this help", OPT_NONE },
    { 1, 'v', "verbose",        "Be more verbose", OPT_NONE },
    { 2, 'o', "output",         "Output filename", OPT_ARGREQ },
    { 3, 'A', "ascii",          "Use plain ASCII output", OPT_NONE },
    { 4, 'T', "table",          "Use HTML table", OPT_NONE },
    { 5, 't', "title",          "Map title", OPT_ARGREQ },
    { 6, 'u', "urchin-file",    "Specify urchin file", OPT_ARGREQ },
};

const int optListN = (sizeof(optList) / sizeof(optarg_t));


void argShowHelp(void)
{
    th_args_help(stderr, optList, optListN, th_prog_name,
    "[options] <input mapfile> <descfile>");
}


BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
{
    switch (optN) {
    case 0:
        argShowHelp();
        exit(0);
        break;

    case 1:
        th_verbosityLevel++;
        break;
    
    case 2:
        optDestFilename = optArg;
        THMSG(2, "Output file set to '%s'.\n", optDestFilename);
        break;
    
    case 3:
        optOutputFormat = OUT_ASCII;
        break;
        
    case 4:
        optUseTable = TRUE;
        THMSG(2, "Using HTML table formatting.\n");
        break;
        
    case 5:
        optMapTitle = optArg;
        THMSG(2, "Map title string set to '%s'.\n", optMapTitle);
        break;
    
    case 6:
        optUrchinFile = optArg;
        THMSG(2, "Urchin filename set to '%s'.\n", optUrchinFile);
        break;
    
    default:
        THERR("Unknown option '%s'.\n", currArg);
        return FALSE;
    }
    
    return TRUE;
}


BOOL argHandleFile(char *currArg)
{
    if (!optMapFilename)
        optMapFilename = currArg;
    else if (!optDescFilename)
        optDescFilename = currArg;
    else {
        THERR("Too many filenames specified!\n");
        return FALSE;
    }

    return TRUE;
}



void addLoc(char c, char *desc, char *extra)
{
    if (nlocations < MAX_LOC) {
        locations[nlocations].c = c;
        locations[nlocations].desc = th_strdup(desc);
        locations[nlocations].extra = th_strdup(extra);
                
        nlocations++;
    } else {
        THERR("Maximum number of locations (%d) exceeded!\n", MAX_LOC);
        exit(5);
    }
}


int getLoc(char c)
{
    int i;
    
    for (i = 0; i < nlocations; i++)
    if (locations[i].c == c)
        return i;
    
    return -1;
}


void putEnd(FILE *f, int c, int p)
{
    if (p != -1) {
        fprintf(f, "</span>");
    }
    
    if (c >= 0) {
        BOOL isURL = FALSE;
        int i = mcGetColor(c, FALSE, TRUE);
        int l = getLoc(c);
        
        if (l >= 0 && locations[l].desc) {
            isURL = TRUE;
            i = col_light_red;
        }
        
        fprintf(f, "<span class=\"%c\">", 'a'+i);

        if (isURL) {
            fprintf(f,
            "<a onmouseover=\"stt(%d);\" "
            "onmouseout=\"htt();\" "
            "class=\"loc\" id=\"maploc%d\" "
            "href=\"?%d\">",
                l, l, l);
        }

        fprintfe(f, "%c", c);
        
        if (isURL) {
            fprintf(f, "</a>");
        }
        
    }
}


void processMapHTML(FILE *outFile, FILE *inFile)
{
    int c, p;

    c = p = -1;
    while ((c = fgetc(inFile)) != EOF) {
        if (c == '\n') {
            if (p != -1)
                fprintf(outFile, "</span>");

            c = -1;
            
            fprintf(outFile, "\n");
        } else {
            if (c != p)
                putEnd(outFile, c, p);
            else
                fprintfe(outFile, "%c", c);
        }
        
        p = c;
    }
}


void printBlockHTML(FILE *outFile, int nStart, int nEnd)
{
    int n;
    
    if (nStart >= nlocations || nStart < 0 || nStart > nEnd) return;
    if (nEnd >= nlocations) nEnd = nlocations-1;
    
    fprintf(outFile, "<td>\n");
    
    for (n = nStart; n <= nEnd; n++) {
        fprintf(outFile,
        "<a class=\"loc\" id=\"listloc%d\" href=\"?%d\" onmouseover=\"qh(%d)", n, n, n);

        if (locations[n].extra) {
            fprintf(outFile, ";stt(%d);", n);
        }

        fprintf(outFile, "\" "
        "onmouseout=\"qn(%d);htt();\">", n);

        fprintf(outFile,
        "[<span class=\"%c\">",
        'a' + (locations[n].c % 14) + 1);
        
        fprintfe(outFile, "%c", locations[n].c);
        
        fprintf(outFile, "</span>]");
        
        if (locations[n].desc) {
            fprintf(outFile,
            " <span style=\"color: gray;\">%s</span>",
            locations[n].desc);
        }
        
        fprintf(outFile, "</a>");
        fprintf(outFile, "<br />\n");
    }
    
    fprintf(outFile, "</td>\n");
}


int copyFile(FILE *outFile, char *filename)
{
    FILE *inFile;
    
    if ((inFile = fopen(filename, "rb")) != NULL) {
        int c;
        while ((c = fgetc(inFile)) != EOF) {
            if (fputc(c, outFile) == EOF) {
                fclose(inFile);
                return -2;
            }
        }
        fclose(inFile);
        return 0;
    } else {
        THERR("File '%s' not found!\n", optUrchinFile);
        return -1;
    }
}


void outputHTML(FILE *outFile, FILE *inFile)
{
    int n, k;
    
    /* Output XHTML header */
    mcXHTMLhead(outFile, optMapTitle);
    
    /* Output CSS style information */
    fprintf(outFile,
    " <script type=\"text/javascript\" src=\"tooltip.js\"></script>\n"
    " <style type=\"text/css\">\n"
    "  body { background: black; color: white; font-family: Arial, Verdana, sans-serif; %s }\n"
    "  a.loc { background: black; color: red; text-decoration: none; }\n"
    "  a.loc:hover { background: white; color: red; }\n"
    "  pre.map { font-size: 9pt; %s }\n",
    optUseTable ? "" : "height: 100%; padding: 0; margin: 0;",
    optUseTable ? "" : "overflow: auto;"
    );
    
    fprintf(outFile,
    "  .tooltip { position: absolute; display: none; min-width: 200px; background-color: white; color: black; padding: 5px; border: 1px solid gray; font-size: 8pt; z-index: 15; }\n"
    "  .holder { display: block; color: #000; }\n"
    "  * html .tooltip { border-right: 400px solid #fff; }\n"
    "  * html .holder { display: inline-block; position: relative; margin-right: -400px; }\n"
    "  div.loctab table tr { vertical-align: top; font-size: 8pt; }\n"
    );

    if (!optUseTable) {
    fprintf(outFile,
    "  div.map {\n"
    "    font-weight: bold;\n"
    "    position: absolute;\n"
    "    left: 0;\n"
    "    right: 0;\n"
    "    top: 0;\n"
    "    margin: 0;\n"
    "    padding: 0;\n"
    "    height: 75%%;\n"
    "    overflow: auto;\n"
    "    text-align: center;\n"
    "  }\n"
    "  div.loctab {\n"
    "    background: black;\n"
    "    color: white;\n"
    "    position: absolute;\n"
    "    right: 0;\n"
    "    left: 0;\n"
    "    height: 20%%;\n"
    "    bottom: 0;\n"
    "    padding: 4px;\n"
    "    z-index: 10;\n"
    "    border-top: 4px solid gray;\n"
    "    font-size: 8pt;\n"
    "    overflow: auto;\n"
    "  }\n"
    "  div.loctab table { width: 95%%; }\n"
    );
    }

    mcXHTMLcolors(outFile, "span", NULL, NULL);
    
    fprintf(outFile,
    " </style>\n"
    "</head>\n"
    "<body onload=\"httOnLoad();\">\n"
    );
    
    if (optUseTable && optMapTitle) {
      fprintf(outFile, "<h3>");
      fprinte(outFile, optMapTitle);
      fprintf(outFile, "</h3>\n");
    }
    
    if (optUseTable)
    fprintf(outFile,
    "<table>\n"
    " <tr>\n"
    "  <td>\n"
    );
    
    fprintf(outFile,
    "<div class=\"map\">"
    "<pre class=\"map\">"
    );
    
    /* Process input, convert to map */
    processMapHTML(outFile, inFile);
    
    /* Another table */
    fprintf(outFile,
    "</pre>"
    "</div>\n"
    );
    
    if (optUseTable) {
    fprintf(outFile,
    "  </td>\n"
    "  <td>\n"
    );
    }
    
    fprintf(outFile,
    "<div class=\"loctab\">\n"
    "<table><tr>\n"
    );

    k = (nlocations / (optUseTable ? 2 : 4)) + 1;
    for (n = 0; n < nlocations; n += k)
      printBlockHTML(outFile, n, n + k - 1);

    fprintf(outFile,
    "</tr></table>\n"
    "</div>\n"
    );
    
    if (optUseTable) {
    fprintf(outFile,
    "  </td>\n"
    " </tr>\n"
    "</table>\n"
    );
    }

    for (n = 0; n < nlocations; n++)
    if (locations[n].desc) {
        fprintf(outFile,
        "<div class=\"tooltip\" id=\"tt%d\"><div class=\"holder\"><b>%s</b>",
        n, locations[n].desc);

        if (locations[n].extra) {
            fprintf(outFile, "<br />%s",
            locations[n].extra);
        }
        fprintf(outFile, "</div></div>\n");
    }

    if (optUrchinFile)
        copyFile(outFile, optUrchinFile);
    
    /* XHTML document end tags */
    fprintf(outFile,
    "</body>\n"
    "</html>\n"
    );
}


void outputASCII(FILE *outFile, FILE *inFile)
{
    int c, n, m;
    size_t i;
    char fmt[32];
     
    /* Output title */
    if (optMapTitle) {
        fprintf(outFile, "%s\n", optMapTitle);
        for (i = 0; i < strlen(optMapTitle); i++)
            fputc('-', outFile);
        fprintf(outFile, "\n\n");
    }
    
    /* Process input, convert to map */
    while ((c = fgetc(inFile)) != EOF) {
        fputc(c, outFile);
    }
    
    fprintf(outFile, "\n");
    
    /* Find max width of location descs */
    for (m = 0, n = 0; n < nlocations; n++) {
        c = strlen(locations[n].desc);
        if (c > m) m = c;
    }
    
    snprintf(fmt, sizeof(fmt), "%%c %%-%ds | ", m);
    
    /* Print location lists */
    m = (nlocations / 2);
    for (n = 0; n < m; n++) {
        c = m + n;
        fprintf(outFile, fmt,
        locations[n].c, locations[n].desc);
        
        if (c < nlocations) {
            fprintf(outFile, "%c %s",
            locations[c].c, locations[c].desc);
        }
        
        fprintf(outFile, "\n");
    }

    fprintf(outFile, "\n");

    if (optUrchinFile)
        copyFile(outFile, optUrchinFile);
}


int parseLocFile(char *filename)
{
    FILE *inFile;
    char s[4096];
    size_t i, j, k;
    
    if ((inFile = fopen(filename, "rb")) == NULL) {
        THERR("Could not open file '%s' for reading.\n",
            filename);
        return -1;
    }
    
    while (fgets(s, sizeof(s), inFile)) {
        int len = strlen(s);
        if (len < 3 || s[0] == 0) continue;

        if (s[len-1] == '\n' || s[len-1] == '\r')
            s[len-1] = 0;
        i = 1;
        th_findnext(s, &i);
        j = i;
        while (s[i] && s[i] != '[' && !th_iscrlf(s[i])) i++;
        if (s[i] == '[') {
            k = i;
            while (s[k] && s[k] != ']' && !th_iscrlf(s[k])) k++;
            s[k] = 0;
            s[i] = 0;
            addLoc(s[0], &s[j], &s[i+1]);
        } else {
            s[i] = 0;
            addLoc(s[0], &s[j], NULL);
        }
    }
    
    fclose(inFile);
    return 0;
}


int main(int argc, char *argv[])
{
    FILE *inFile, *outFile;
    
    th_init("mkbcmap", "ASCII map converter", "0.3", NULL, NULL);
    th_verbosityLevel = 0;
    
    /* Parse arguments */
    if (!th_args_process(argc, argv, optList, optListN,
        argHandleOpt, argHandleFile, TRUE))
        exit(1);
    
    if (optMapFilename == NULL || optDescFilename == NULL) {
        THERR("You need to specify at least map and desc filenames. (try --help)\n");
        exit(0);
    }
    
    /* Parse desc file */
    if (parseLocFile(optDescFilename) < 0) {
        THERR("Could not properly parse desc file '%s'\n",
            optDescFilename);
        exit(3);
    }

    THMSG(1, "Parsed %d locations from '%s'.\n",
        nlocations, optDescFilename);

    /* Open mapfile */
    if ((inFile = fopen(optMapFilename, "rb")) == NULL) {
        THERR("Could not open mapfile '%s' for reading.\n",
            optMapFilename);
        exit(3);
    }
    
    if (optDestFilename == NULL)
        outFile = stdout;
    else if ((outFile = fopen(optDestFilename, "wb")) == NULL) {
        fclose(inFile);
        THERR("Error opening output file '%s'!\n",
            optDestFilename);
        exit(1);
    }
    
    THMSG(1, "Using input map '%s', processing ..\n", optMapFilename);
    
    /* Output data */
    switch (optOutputFormat) {
        case OUT_ASCII:
            outputASCII(outFile, inFile);
            break;
        
        case OUT_HTML:
            outputHTML(outFile, inFile);
            break;
    }
    
    /* Close input and output files */
    fclose(inFile);
    fclose(outFile);
    
    exit(0);
    return 0;
}