DRUID Development Docs: cmconvert.c

Gadget Sourcecode: cmconvert.c

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

#define MAXFIELDS   100
#define LINECHARS   10000



typedef struct LINE_s {
    char    *text;
    char    *fields[MAXFIELDS];
    int      n_fields;
    int      alloc;
} LINE;

typedef struct CLUE_s {
    int num;
    char *arrival_code;
    char *gc_name;
    char *title;
    char *arrival_text;
    char *solution;
    char *confirm_code;
    char *confirm_text;
    int conf_cnt;
    int is_override;

    struct CLUE_s *next;
} CLUE;

CLUE  *g_clue_head = 0;
CLUE **g_clue_tail = &g_clue_head;
int    g_num_clues = 0;

char *g_infile = "clue_messages.stxt";
char *g_outfile = 0;
int g_debug = 0;

int g_override_cnt = 0;
char *g_override_list[100];




#define ASSERT(cond)    ASSERT1(cond,__LINE__)
#define ASSERT1(cond,l) { if (!(cond)) { assert_failed(#cond,l); }}

void assert_failed(char *cond, int line)
{
    fprintf(stderr,"ASSERT FAILED (line %d): %s\n",line,cond);
    exit(1);
}

LINE *line_new(void)
{
    LINE *line = (LINE*)malloc(sizeof(LINE));
    line->alloc = LINECHARS;
    line->text = (char*)malloc(line->alloc);
    line->n_fields = 0;
    return line;
}

void line_delete(LINE *line)
{
    if (line->text) {
        free(line->text);
    }
    free(line);
}

int  line_read(LINE *line, FILE *fp)
{
    enum {
        STARTFIELD,
        NOQUOTE,
        NOQUOTE_BSLASH,
        DQUOTE,
        DQUOTE_BSLASH,
        ENDLINE } state = STARTFIELD;
    int escape = 0;
    int dquote = 0;
    int c = getc(fp);
    char *s = line->text;

    line->n_fields = 0;

    if (c == EOF) {
        *s = 0;
        return 0;
    }

    ungetc(c,fp);


    while(state != ENDLINE) {
        ASSERT(s-line->text < line->alloc);
        c = getc(fp);
        switch(state) {
            case STARTFIELD:
                ASSERT(line->n_fields < MAXFIELDS);
                line->fields[line->n_fields++] = s;
                state = NOQUOTE;

            case NOQUOTE:
                switch(c) {
                    case ',':
                        *(s++) = 0;
                        state = STARTFIELD;
                        break;
                    case '"':
                        state = DQUOTE;
                        break;
                    case '\\':
                        state = NOQUOTE_BSLASH;
                        break;
                    case '\n':
                        *(s++) = 0;
                        state = ENDLINE;
                        break;
                    default:
                        *(s++) = c;
                        break;
                }
                break;

            case NOQUOTE_BSLASH:
            case DQUOTE_BSLASH:
                switch(c) {
                    case '\n':
                        break;
                    case 'n':
                        *(s++) = '\n';
                        break;
                    default:
                        *(s++) = c;
                        break;
                }
                state--;
                break;

            case DQUOTE:
                switch(c) {
                    case '"':
                        c = getc(fp);
                        if (c == '"') {
                            (*s++) = '"';
                        } else {
                            ungetc(c,fp);
                            state = NOQUOTE;
                        }
                        break;
                    case '\\':
                        state = NOQUOTE_BSLASH;
                        break;
                    default:
                        *(s++) = c;
                        break;
                }
                break;

            default:
                fprintf(stderr,"BAD STATE %d\n",state);
                exit(1);
        }
    }

    if (g_debug) {
        int j;
        static char *fname[] = {
            "arrival_code",
            "gc_name",
            "title",
            "arrival_text",
            "confirm_code",
            "confirm_text",
        };
        fprintf(stderr,"===============================================\n");
        for (j=0; j<line->n_fields; j++) {
            fprintf(stderr,"  field[%3d] %12s = '%s'\n",j,fname[j],line->fields[j]);
        }
    }
    return 1;
}

char *to_printable_nocr(char *s)
{
    static char buf[1000];
    char *d = buf;
    while(*s) {
        if (isprint(*s) && *s != '\n') {
            *d = *s;
        } else {
            *d = '@';
        }
        d++;
        s++;
    }

    *d = 0;
    return buf;
}

char *code_dup(char *code)
{
    char *nc = malloc(strlen(code)+1);
    char *s = code;
    char *d = nc;

    while(*s) {
        *d = toupper(*s);
        if (isupper(*d) || isdigit(*d)) d++;
        s++;
    }

    *d = 0;

    return nc;
}

void add_clue(
            char *arrival_code,
            char *gc_name,
            char *title,
            char *arrival_text,
            char *solution,
            char *confirm_code,
            char *confirm_text)
{
    char buf[1000];
    CLUE *clue = (CLUE*)malloc(sizeof(CLUE));
    clue->num           = g_num_clues++;

    clue->is_override = 0;

    if (*arrival_code == 0 || *arrival_code == '?') {
        arrival_code = "X";
    }
    clue->arrival_code = code_dup(arrival_code);

    if (*gc_name == 0 || *gc_name == '?') {
        gc_name = buf;
        sprintf(buf,"clue %d",clue->num);
    }
    clue->gc_name = strdup(to_printable_nocr(gc_name));


    if (*title == 0 || *title == '?') {
        title = clue->gc_name;
    }
    clue->title = strdup(to_printable_nocr(title));


    if (*confirm_code == 0 || *confirm_code == '?') {
        static char *extra_confirms[] = {
                "DIAMONDS",
                "CROCODILE",
                "ROUTER",
                "ANIMATOR",
                "FOXHUNT",
                "TABLETOP",
                "BANANA",
                0
                };
        static char **extra = extra_confirms;
        confirm_code = *extra;
        extra++;
        ASSERT(confirm_code);
    }
    clue->confirm_code = code_dup(confirm_code);

    if (!strcmp(clue->arrival_code,"OVERRIDE")) {
        clue->is_override = 1;
        clue->confirm_code = strdup("X");
    }

    if (*arrival_text == 0 || *arrival_text == '?') {
        arrival_text = "X";
        if (!clue->is_override) {
            arrival_text = title;
        }
    }
    clue->arrival_text  = strdup(arrival_text);



    if (*solution == 0 || *solution == '?') {
        solution = "X";
    }
    clue->solution      = strdup(solution);

    if (*confirm_text == 0 || *confirm_text == '?') {
        confirm_text = ":My antenna must be damaged.  Call Duct Tape Man at 650-743-7203 for instructions.";
    }
    clue->confirm_text = strdup(confirm_text);







    if (g_debug) {
        fprintf(stderr,"Add clue[%d]: '%s'\n",clue->num, clue->title);
    }
    clue->conf_cnt = 0;

    clue->next = 0;
    *g_clue_tail = clue;
    g_clue_tail = &(clue->next);
}

void get_messages(FILE *in)
{
    int linenum = 0;
    int num = 0;
    LINE *line = line_new();

    while(line_read(line, in)) {
        linenum++;
        if (line->n_fields < 1) continue;
        if (num == 0 && line->fields[0][0] == 0) continue;
        if (num == 0 && !strcmp(line->fields[0],"ARRIVAL CODE")) continue;

        if (line->n_fields != 7) {
            fprintf(stderr,"ERROR: line %d has %d fields (should have 7)\n",
                    linenum,line->n_fields);
            continue;
        }
        num++;

        add_clue(
            line->fields[0],    // arrival code
            line->fields[1],    // gc name
            line->fields[2],    // title
            line->fields[3],    // mission briefing/arrival text
            line->fields[6],    // solution
            line->fields[4],    // confirm code
            line->fields[5]);   // confirm text
    }

    line_delete(line);
}

void output_text(FILE *out, char *text)
{
    char *str = strdup(text);
    char *s = str;
    char *e;
    int line = 0;

    while(*s) {

        // find 20 characters (find wordbreaks)
        for (e=s; *e != 0 && *e != '\n' && e-s < 20; e++);

        if (e-s >= 20) {
            char *e1 = e;
            while(1) {
                if (e == s) {
                    e = e1;
                    break;
                }
                if (*e == '\n') break;
                if (*e == ' ') break;
                if (*e == 0) break;
                if (e[-1] == '-') break;
                e--;
            }
            while(s != e && (e[-1] == ' ' || e[-1] == '\t')) e--;
        }

        // output 1 line
        putc('"',out);
        while(s != e) {
            if (*s == '\t') {
                fprintf(stderr,"ERROR: TAB is not supported! (using space)\n");
                fprintf(stderr,"    TAB in string '%s'\n",text);
                *s = ' ';
            }
            if (*s == '"') {
                putc('\\',out);
            }
            putc(*s,out);
            s++;
        }
        putc('"',out);
        putc('\n',out);
        line++;

        // skip leading spaces (unless explicit carriage return)
        switch(*e) {
            case ' ':
            case '\t':
                while (*e == ' ' || *e == '\t') e++;
                break;
            case '\n':
                e++;
                break;
            default:
                break;
        }
        s = e;
    }

    while(line < 2) {
        fprintf(out,"\" \"\n");
        line++;
    }

    free(str);
}


void output_message(FILE *out, CLUE *clue, char *text, char *basename, char *sfx)
{
    fprintf(out,"TEXT mode_%s_%d%s POP\n",
        basename,
        clue->num,
        sfx);

    output_text(out,text);

    fprintf(out,"END\n");
    fprintf(out,"\n");
}

//
// parse time string and output valid time hh:mm
//
char *time_string(char *in)
{
    static char buf[100];
    char *s = in;
    int hh=0, mm=0;

    if (s == 0) return "00:00";

    if (*s && isdigit(*s)) do {
        char *e = 0;
        hh = strtol(s,&e,10);
        if (!e || e==s) {
            s++;
            continue;
        }
        if (*e != ':') {
            s = e;
            continue;
        }
        s = e+1;

        if (!isdigit(*s)) continue;
        mm = strtol(s,&e,10);
        if (!e || e==s) {
            s++;
            continue;
        }
        if (*e == ' ' || *e == '\t' || *e==0) {
            if (mm >= 60 || hh >= 48) {
                fprintf(stderr,"Error: invalid time: '%s'\n",in);
                return "00:00";
            }
            sprintf(buf,"%02d:%02d",hh,mm);
            return buf;
        }
    } while(0);

    fprintf(stderr,"Error: could not parse time: '%s'\n",in);
    return "00:00";
}

typedef struct CONFIRM_s {
    char *text;
    char *earliest_time;
} CONFIRM;

void output_confirms(FILE *out, CLUE *clue)
{
    CONFIRM conf[100];
    int cnt = 0;
    char *text = strdup(clue->confirm_text);
    char *s = text;
    int is_override = 0;

    conf[0].text = 0;
    conf[0].earliest_time = 0;

    if (!strcmp(text,"X")) {
        free(text);
        return;
    }

    while(*s) {
        char *s1 = s;
        while(isspace(*s)) s++;
        if (*s == 0) break;
        if (clue->is_override) {
            while(*s && *s != ':') s++;
        }
        if (*s == '>' || *s == '<') {
            int digit = 0;
            int state = 1;
            char dir = *s;
            char *t = 0;
            s++;
            while(isspace(*s)) s++;
            if (*s == '=') s++;
            while(isspace(*s)) s++;
            //t = s;
            while(1) {
                if (*s == 0) break;
                if (*s == '\n') break;
                switch(state) {
                    case 0:     // look for space
                        if (isspace(*s)) state=1;
                    case 1:     // look for hour1 or space
                        if (isdigit(*s)) {
                            state=2;
                            t=s;
                        } else if (isspace(*s)) state=1;
                        else state=0;
                        break;
                    case 2:     // look for colon or hour2
                        if (isdigit(*s)) state=3;
                        else if (*s==':') state=4;
                        else state=100;
                        break;
                    case 3:     // look for colon
                        if (*s==':') state=4;
                        else state=100;
                        break;
                    case 4:     // look for minutes1
                        if (isdigit(*s)) state=5;
                        else state=100;
                        break;
                    case 5:     // look for minutes2
                        if (isdigit(*s)) state=6;
                        else state=100;
                        break;
                    case 6:     // look for space following time
                        if (isspace(*s)) {
                            *s = 0;
                            state=90;
                        } else state=100;
                        break;

                    case 90:    // time found
                    case 100:   // no time found
                    default:
                        break;
                }
                if (*s == ':' && !digit) break;
                digit = isdigit(*s);
                s++;
            }

            if (*s != ':' || state!=90) {
                fprintf(stderr,"Error parsing time %d (%.*s) in clue %d:\n%s\n",
                    cnt,
                    s-s1,s1,
                    clue->num,
                    clue->confirm_text);
            } else {
                *s = 0;
                s++;
                while(isspace(*s)) s++;
                if (*s == '>' || *s == '<') {
                    fprintf(stderr,"Error parsing confirm %d in clue %d:\n"
                        "\tzero length (or starts with '>' or '<')\n%s\n",
                        cnt,
                        clue->num,
                        clue->confirm_text);

                } else if (dir == '>') {
                    if (cnt == 0) cnt++;
                    conf[cnt].earliest_time = t;
                    conf[cnt].text = s;
                    cnt++;

                } else if (conf[cnt].text) {
                    fprintf(stderr,
                        "Error in clue %d:  2 earliest time (or default) confirms\n%s\n",
                        clue->num,
                        clue->confirm_text);

                } else {
                    conf[0].text = s;
                    if (cnt == 0) cnt++;
                }
            }
        } else if (conf[0].text) {
            fprintf(stderr,
                "Error in clue %d:  2 default (or earliest time) confirms\n%s\n",
                clue->num,
                clue->confirm_text);

        } else {
            char *s3 = s;
            int digit=0;
            while (*s3 && *s3 != '\n' && (*s3 != ':' || digit)) {
                digit = isdigit(*s3);
                s3++;
            }
            if (*s3 == ':') {
                s = s3+1;
            } else {
                fprintf(stderr,"ERROR: no colon found in '%s'\n",
                    clue->confirm_text);
            }
            while(isspace(*s) && *s != '\n') *s++;
            conf[0].text = s;
            if (cnt == 0) cnt++;
        }

        while(*s) {
            if (*s == '\n') s1 = s+1;
            if (s == s1 && (*s =='>' || *s == '<')) {
                ASSERT(s[-1] == '\n');
                s[-1] = 0;
                break;
            }
            s++;
        }

    }

    if (cnt < 1) {
        fprintf(stderr,"ERROR: no confirm messages in clue %d\n%s\n",
            clue->num,
            clue->confirm_text);
        conf[0].earliest_time = 0;
        conf[0].text = strdup(" ");
        cnt++;
    } else if (conf[0].text == 0) {
        if (cnt == 2 && !strcmp(clue->arrival_code,"OVERRIDE")) {
            is_override = 1;
        } else {
            fprintf(stderr,"ERROR: no default (zero-time) confirm message "
                "in clue %d:\n%s\n",
                clue->num,
                clue->confirm_text);
        }
    }

    //fprintf(stderr,"IMPLEMENT CONFIRM SORT!!\n");



    if (!is_override) {
        int i;
        int offset = 0;
        fprintf(out,"CONFIRMS clue_%d\n",clue->num);
        for (i=0; i<cnt; i++) {
            char *time;
            if (conf[i].text == 0) {
                offset++;
                continue;
            }
        
            time = (i-offset == 0) ?
                        time_string(0) :
                        time_string(conf[i].earliest_time),
            
            fprintf(out,"confirm_%d_t%d\t\t\"%s\"\t# after %s\n",
                clue->num,
                i-offset,
                time,
                time);
    
        }
        fprintf(out,"END\n");
        fprintf(out,"\n");
    }

    if (is_override) {
            char buf[100];
            ASSERT(cnt == 2 && conf[1].text && conf[1].earliest_time);

            sprintf(buf,"_t%d",g_override_cnt);
            g_override_list[g_override_cnt] = strdup(conf[1].earliest_time);
            g_override_cnt++;
        
            output_message(out, clue, conf[1].text, "override",buf);
            cnt = 0;
    } else {
        int i;
        int offset = 0;
        for (i=0; i<cnt; i++) {
            char buf[100];
            if (conf[i].text == 0) {
                offset++;
                continue;
            }

            sprintf(buf,"_t%d",i-offset);
        
            output_message(out, clue, conf[i].text, "confirm",buf);
        }
    }

    clue->conf_cnt = cnt;

    free(text);
}

void output_overrides(FILE *out)
{
    CLUE *clue;
    int i;
    int cnt;
    int cnt2;

#if 0
    fprintf(stderr,"%d time overrides found\n",g_override_cnt);
    ASSERT(g_override_cnt == 2);
    fprintf(out,"CONFIRMS overrides\n");
    for (i=0; i<g_override_cnt; i++) {
        fprintf(out,"override_t%d\t\t\"%s\"\n",
            i,
            time_string(g_override_list[i]));
    }
    fprintf(out,"END\n");
    fprintf(out,"\n");
#endif

    
    fprintf(out,"CONFIRMS clue_1000\n");
    for(clue = g_clue_head; clue; clue=clue->next) {
        for (i=0; i<clue->conf_cnt; i++) {
            fprintf(out, "confirm_%d_t%d\t\t\"00:00\"\n",
                clue->num,
                i);
        }
    }
    fprintf(out,"END\n");
    fprintf(out,"\n");

    fprintf(out,"MENU mode_conf_test \"Confirm Menu\"\n");
    cnt=0;
    cnt2 = 1;
    for(clue = g_clue_head; clue; clue=clue->next) {
        if (cnt >= 18) {
            cnt = 0;
            cnt2++;
            fprintf(out, "SET mode_conf_test%d \"Next...\"\n",cnt2);
            fprintf(out, "END\n");
            fprintf(out,"MENU mode_conf_test%d \"Confirm Menu (cont)\"\n",cnt2);
        }
        for (i=0; i<clue->conf_cnt; i++) {
            cnt++;
            fprintf(out, "mode_confirm_%d_t%d\t\t\"%.17s v%d\"\n",
                clue->num,
                i,
                clue->title,
                i);
        }
    }
    fprintf(out, "POP \"Debug Menu\"\n");
    fprintf(out,"END\n");
    fprintf(out,"\n");

    fprintf(out,"MENU mode_arr_test \"Arrival Menu\"\n");
    cnt=0;
    for(clue = g_clue_head; clue; clue=clue->next) {
        cnt++;
        if (cnt == 18) {
            fprintf(out, "SET mode_arr_test2 \"Next...\"\n");
            fprintf(out, "END\n");
            fprintf(out,"MENU mode_arr_test2 \"Arrival Menu (cont)\"\n");
        }
        if (clue->is_override) continue;
        fprintf(out, "mode_arrival_%d\t\t\"%.20s\"\n",
            clue->num,
            clue->title);
    }
    fprintf(out, "POP \"Debug Menu\"\n");
    fprintf(out,"END\n");
    fprintf(out,"\n");

    fprintf(out,"MENU mode_sol_test \"Solution Menu\"\n");
    cnt=0;
    for(clue = g_clue_head; clue; clue=clue->next) {
        cnt++;
        if (cnt == 18) {
            fprintf(out, "SET mode_sol_test2 \"Next...\"\n");
            fprintf(out, "END\n");
            fprintf(out,"MENU mode_sol_test2 \"Solution Menu cont.\"\n");
        }
        if (clue->is_override) continue;
        fprintf(out, "mode_solution_%d\t\t\"%.20s\"\n",
            clue->num,
            clue->title);
    }
    fprintf(out, "POP \"Debug Menu\"\n");
    fprintf(out,"END\n");
    fprintf(out,"\n");

    fprintf(out,"TEXT mode_ccode_test POP\n");
    for(clue = g_clue_head; clue; clue=clue->next) {
        fprintf(out, "\"%2d:%.17s\"\n",
            clue->num,
            clue->title);
        fprintf(out, "\"   %.17s\"\n",
            clue->confirm_code);
    }
    fprintf(out,"END\n");
    fprintf(out,"\n");
}

void output_clue(FILE *out, CLUE *clue)
{
    char buf[1000];

    if (g_debug) {
        fprintf(stderr,"=============== %d\n",clue->num);
        fprintf(stderr,"arrival: '%s'\n",clue->arrival_code);
        fprintf(stderr,"confirm: '%s'\n",clue->confirm_code);
    }

    fprintf(out,"# CLUE %d  - '%s' (%s)\n",
            clue->num,
            clue->title,
            clue->gc_name);

#if 0
    if (!clue->is_override) {
        //
        // title string
        //
        fprintf(out, "str_clue%-4d\t\t\"%s\"\n",
                clue->num,
                clue->title);
    }
#endif


    //
    // CODES
    //
    if (!clue->is_override &&
        strcmp(clue->confirm_code,"NA") &&
        strcmp(clue->confirm_code,"X") &&
        strcmp(clue->confirm_code,"") &&
        strcmp(clue->arrival_code,"OVERRIDE")) {
        fprintf(out,"CODE \"%s\" func_fn_confirm puz%d\t\t# confirm code for %s (%s)\n",
            clue->confirm_code,
            clue->num,
            clue->title,
            clue->gc_name);

        fprintf(out,"CODECHECK \"%s\"\t\t# confirm\n",clue->confirm_code);
    }


    if (!clue->is_override &&
        strcmp(clue->arrival_code,"NA") &&
        strcmp(clue->arrival_code,"X") &&
        strcmp(clue->arrival_code,"OVERRIDE")) {
#if 0
        fprintf(out,"CODE \"%s\" func_fn_arrival2 parmval_%d parmval_30\t\t# arrival code for %s (%s)\n",
            clue->arrival_code,
            clue->num,
            clue->title,
            clue->gc_name);

#endif
        fprintf(out,"CODECHECK \"%s\" clue_%d\t\t# arrival\n",
            clue->arrival_code,
            clue->num);
    }

    //
    // arrival message
    //
    fprintf(out,"\n");
    if (!clue->is_override && strcmp(clue->arrival_text,"X")) {
        output_message(out, clue, clue->arrival_text, "arrival","");
    }

    //
    // confirm message(s)
    //
    fprintf(out,"\n");
    //output_message(out, clue, clue->confirm_text, "confirm","");
    output_confirms(out, clue);

    //
    // solution message
    //
    fprintf(out,"\n");
    if (!clue->is_override) {
        sprintf(buf,"%s\nSolution: '%s'\nCode: '%s'\nJU Internal name: '%s'",
            clue->title,
            clue->solution,
            clue->confirm_code,
            clue->gc_name);
        output_message(out, clue, buf, "solution","");
    }


    
    fprintf(out,"\n");
    fprintf(out,"\n");
}

void output(FILE *out)
{
    CLUE *clue = g_clue_head;

    fprintf(out,"# Justice Unlimited Gadget Microcode\n");
    fprintf(out,"# (C) 2004 Nathan (Acorn) Pooley\n");
    fprintf(out,"#\n");
    fprintf(out,"# %s\n",g_outfile?g_outfile:"stdout");
    fprintf(out,"#\n");
    fprintf(out,"#@DOC@ Clue Messages \n");
    fprintf(out,"#\n");
    fprintf(out,"# DO NOT EDIT THIS FILE!!! - autogenerated by cmconvert,c\n");
    fprintf(out,"#\n");
    fprintf(out,"\n");


    for(; clue; clue=clue->next) {
        output_clue(out,clue);
    }

    //
    // overrides
    //
    fprintf(out,"\n");
    output_overrides(out);

}


char *get_parm(int *n, int argc, char *argv[])
{
    if (argv[*n][2] == 0) {
        if (*n >= argc-1) {
            fprintf(stderr,"Option '%s' must be followed by an argument\n",
                argv[*n]);
            exit(1);
        }
        (*n)++;
        return  argv[*n];
    } else {
        return &argv[*n][2];
    }
}


int main(int argc, char *argv[])
{
    FILE *fp;
    int i;

    for (i=1; i<argc; i++) {
        if (argv[i][0] == '-') {
            switch(argv[i][1]) {
                case 'i':
                    g_infile = strdup(get_parm(&i, argc, argv));
                    break;
                case 'o':
                    g_outfile = strdup(get_parm(&i, argc, argv));
                    break;
                case 'd':
                    g_debug = 1;
                default:
                    fprintf(stderr,"Unknown option '%s'\n",argv[i]);
            }
        } else {
            fprintf(stderr,"Unknown argument %s",argv[i]);
            exit(1);
        }

    }

    fp = fopen(g_infile, "r");
    if (!fp) {
        fprintf(stderr,"ERROR: Could not read infile '%s'\n",g_infile);
        exit(1);
    }

    get_messages(fp);
    fclose(fp);

    if (g_outfile) {
        fp = fopen(g_outfile,"w");
        if (!fp) {
            fprintf(stderr,"ERROR: Could not write outfile '%s'\n",g_outfile);
            exit(1);
        }
    } else {
        fp = stdout;
    }

    output(fp);

    if (g_outfile) {
        fclose(fp);
    }

    return 0;
}

This file Copyright (C) 2004 by Nathan (Acorn) Pooley
Go to DRUID Development page
Go to DRUID page
Go to JU Gadgets page
Go to Justice Unlimited homepage
Go to Acorn's personal webpage
Contact Acorn
See comments from others
Post your own comments
File created by do_doc at Wed Aug 4 18:04:42 2004