src/c2html.c

Sun, 19 Apr 2015 10:48:00 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 19 Apr 2015 10:48:00 +0200
changeset 24
e43dee5892f4
parent 23
f44a185b678b
child 25
f82aa7afe872
permissions
-rw-r--r--

improved code structure and added option for disabling line numbers

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2015 Mike Becker. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include "c2html.h"

inputfile_t *inputfilebuffer(size_t capacity) {
    inputfile_t *inputfile = (inputfile_t*) malloc(sizeof(inputfile_t));
    inputfile->lines = (char**) malloc(capacity * sizeof(char*));
    inputfile->capacity = capacity;
    inputfile->count = 0;
    inputfile->maxlinewidth = 0;

    return inputfile;
}

void addline(inputfile_t *inputfile, char* line, size_t width) {
    char *l = (char*) malloc(width+1);
    memcpy(l, line, width);
    l[width] = 0;
    if (inputfile->count >= inputfile->capacity) {
        inputfile->capacity <<= 1;
        inputfile->lines = realloc(inputfile->lines, inputfile->capacity);
    }
    inputfile->lines[inputfile->count] = l;
    inputfile->maxlinewidth =
        width > inputfile->maxlinewidth ? width : inputfile->maxlinewidth;
    inputfile->count++;
}

void freeinputfilebuffer(inputfile_t *inputfile) {
    for (int i = 0 ; i < inputfile->count ; i++) {
        free(inputfile->lines[i]);
    }
    free(inputfile->lines);
    free(inputfile);
}

inputfile_t *readinput(char *filename) {

    int fd = open(filename, O_RDONLY);
    if (fd == -1) return NULL;

    inputfile_t *inputfile = inputfilebuffer(512);

    char buf[INPUTBUF_SIZE];
    ssize_t r;

    size_t maxlinewidth = 256;
    char *line = (char*) malloc(maxlinewidth);
    size_t col = 0;

    while ((r = read(fd, buf, INPUTBUF_SIZE)) > 0) {
        for (size_t i = 0 ; i < r ; i++) {
            if (col >= maxlinewidth-4) {
                maxlinewidth <<= 1;
                line = realloc(line, maxlinewidth);
            }

            if (buf[i] == '\n') {
                line[col++] = '\n';
                line[col] = 0;
                addline(inputfile, line, col);
                col = 0;
            } else {
                line[col++] = buf[i];
            }
        }
    }

    free(line);

    close(fd);

    return inputfile;
}

void printhelp() {
    printf("Formats source code using HTML.\n\nUsage:\n"
        "  c2html [Options] FILE\n\n"
        " Options:\n"
        "  -h                    Prints this help message\n"
        "  -j                    Highlight Java instead of C source code\n"
        "  -o <output>           Output file (stdout, if not specified)\n"
        "  -H <header>           Prepend header file\n"
        "  -F <footer>           Append footer file\n"
        "  -p                    Disable highlighting (plain text)\n"
        "  -l                    Disable line numbers\n"
        "\n");


}

int lnint(size_t lnc) {
    int w = 1, p = 1;
    while ((p*=10) < lnc) w++;
    return w;
}

int copyfile(char *filename, FILE *dest) {
    if (!filename) {
        return 0;
    }
    
    FILE *src = fopen(filename, "r");
    if (src) {
        char buf[4096];
        int r;
        while ((r = fread(buf, 1, 4096, src)) > 0) {
            fwrite(buf, 1, r, dest);
        }
        fclose(src);
        return 0;
    } else {
        return -1;
    }
}

#define WRITECONST(stream, out, cstr) out(cstr, 1, sizeof(cstr)-1, stream)
int formatfile(
        highlighter_t *highlighter,
        inputfile_t *in,
        fmt_write_func out,
        void *stream,
        _Bool showln) {
    // formats an input file and writes the result to out
    
    char *line = malloc(in->maxlinewidth*64);
    if(!line) {
        return 1;
    }
    WRITECONST(stream, out, "<pre>\n");
    
    int lnw = lnint(in->count);
    for (int i = 0 ; i < in->count ; i++) {
        char *ln = line;
        if (highlighter) {
            highlighter->parser(in->lines[i], line, highlighter);
        } else {
            ln = in->lines[i];
        }
        
        // write line number
        if (showln) {
            WRITECONST(stream, out, "<span class=\"c2html-lineno\">");
            char lnbuf[16];
            int len = snprintf(lnbuf, 16, "%*d ", lnw, i+1);
            out(lnbuf, 1, len, stream);
            WRITECONST(stream, out, "</span> ");
        }
        
        // write formated (or plain) code line
        out(ln, 1, strlen(ln), stream);
    }
    
    WRITECONST(stream, out, "</pre>\n");
    free(line);
    return 0;
}

void init_c_highlighter(highlighter_t *highlighter) {
    memset(highlighter, 0, sizeof(highlighter_t));
    highlighter->isdirective = iscdirective;
    highlighter->istype = isctype;
    highlighter->keywords = ckeywords;
    highlighter->parser = cparseline;
}

void init_java_highlighter(highlighter_t *highlighter) {
    memset(highlighter, 0, sizeof(highlighter_t));
    highlighter->isdirective = isjdirective;
    highlighter->istype = isjtype;
    highlighter->keywords = jkeywords;
    highlighter->parser = jparseline;
}

int main(int argc, char** argv) {
    int retcode = EXIT_SUCCESS;
    
    settings_t settings;
    memset(&settings, 0, sizeof(settings));
    settings.highlight = 1;
    settings.showlinenumbers = 1;
    
    int lang = C2HTML_C;

    char optc;
    while ((optc = getopt(argc, argv, "hljo:pH:F:")) != -1) {
        switch (optc) {
            case 'o':
                if (!(optarg[0] == '-' && optarg[1] == 0)) {
                    settings.outfilename = optarg;
                }
                break;
            case 'F':
                settings.footerfile = optarg;
                break;
            case 'H':
                settings.headerfile = optarg;
                break;
            case 'j':
                lang = C2HTML_JAVA;
                break;
            case 'p':
                settings.highlight = 0;
                break;
            case 'l':
                settings.showlinenumbers = 0;
                break;
            case 'h':
                printhelp();
                return 0;
            default:
                return 1;
        }
    }

    if (optind != argc-1) {
        printhelp();
        return 1;
    } else {
        settings.infilename = argv[optind];
        FILE *fout;
        if (settings.outfilename) {
            fout = fopen(settings.outfilename, "w");
            if (!fout) {
                perror("Error opening output file");
                return -1;
            }
        } else {
            fout = stdout;
        }
        
        if (copyfile(settings.headerfile, fout)) {
            perror("Error opening header file");
            retcode = -1;
            goto prog_end;
        }
        
        highlighter_t highlighter;
        highlighter_t *hptr = &highlighter;
        switch (lang) {
            case C2HTML_C:
                init_c_highlighter(&highlighter);
                break;
            case C2HTML_JAVA:
                init_java_highlighter(&highlighter);
                break;
            default:
                hptr = NULL;
                break;
        }
        if (!settings.highlight) {
            hptr = NULL;
        }

        inputfile_t *inputfile = readinput(settings.infilename);
        if (inputfile) {
            formatfile(
                    hptr,
                    inputfile,
                    (fmt_write_func)fwrite,
                    fout,
                    settings.showlinenumbers);
        } else {
            perror("Error opening input file");
            retcode = -1;
        }
        
        if (copyfile(settings.footerfile, fout)) {
            perror("Error opening footer file");
            retcode = -1;
        }
        
        prog_end:        
        if (fout != stdout) {
            fclose(fout);
        }

        return retcode;
    }
}

mercurial