/* charmonize.c -- Create Charmony.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "Charmonizer/Probe.h"
#include "Charmonizer/Probe/DirSep.h"
#include "Charmonizer/Probe/FuncMacro.h"
#include "Charmonizer/Probe/Headers.h"
#include "Charmonizer/Probe/Integers.h"
#include "Charmonizer/Probe/LargeFiles.h"
#include "Charmonizer/Probe/UnusedVars.h"
#include "Charmonizer/Probe/VariadicMacros.h"

char *cc_command, *cc_flags, *os_name, *verbosity_str;

/* Process command line args, set up Charmonizer, etc. 
 */
void
init(int argc, char **argv);

/* Find <tag_name> and </tag_name> within a string and return the text between
 * them as a newly allocated substring.
 */
static char*
extract_delim(char *source, size_t source_len, const char *tag_name);

/* Version of extract delim which dies rather than returns NULL upon failure.
 */
static char*
extract_delim_and_verify(char *source, size_t source_len, 
                         const char *tag_name);

/* Write some stuff to the end of charmony.h
 */
static void
write_charmony_postamble(void);

/* Print a message to stderr and exit.
 */
void
die(char *format, ...);

int main(int argc, char **argv) 
{
    init(argc, argv);

    /* modules section */
    chaz_DirSep_run();
    chaz_Headers_run();
    chaz_FuncMacro_run();
    chaz_Integers_run();
    chaz_LargeFiles_run();
    chaz_UnusedVars_run();
    chaz_VariadicMacros_run();

    /* write custom postamble */
    write_charmony_postamble();

    /* clean up */
    chaz_Probe_clean_up();
    free(cc_command);
    free(cc_flags);
    free(os_name);
    free(verbosity_str);

    return 0;
}

void
init(int argc, char **argv) 
{
    char *infile_str;
    size_t infile_len;

    /* parse the infile */
    if (argc != 2)
        die("Usage: ./charmonize INFILE");
    infile_str = chaz_Probe_slurp_file(argv[1], &infile_len);
    cc_command = extract_delim_and_verify(infile_str, infile_len, 
        "charm_cc_command");
    cc_flags = extract_delim_and_verify(infile_str, infile_len, 
        "charm_cc_flags");
    os_name = extract_delim_and_verify(infile_str, infile_len, 
        "charm_os_name");
    verbosity_str = extract_delim(infile_str, infile_len, "charm_verbosity");

    /* set up Charmonizer */
    if (verbosity_str != NULL) {
        const long verbosity = strtol(verbosity_str, NULL, 10);
        chaz_Probe_set_verbosity(verbosity);
    }
    chaz_Probe_init(os_name, cc_command, cc_flags, NULL);

    /* clean up */
    free(infile_str);
}

static char*
extract_delim(char *source, size_t source_len, const char *tag_name)
{
    const size_t tag_name_len = strlen(tag_name);
    const size_t opening_delim_len = tag_name_len + 2;
    const size_t closing_delim_len = tag_name_len + 3;
    char opening_delim[100];
    char closing_delim[100];
    const char *limit = source + source_len - closing_delim_len;
    char *start, *end;
    char *retval = NULL;

    /* sanity check, then create delimiter strings to match against */
    if (tag_name_len > 95)
        die("tag name too long: '%s'");
    sprintf(opening_delim, "<%s>", tag_name);
    sprintf(closing_delim, "</%s>", tag_name);
    
    /* find opening <delimiter> */
    for (start = source; start < limit; start++) {
        if (strncmp(start, opening_delim, opening_delim_len) == 0) {
            start += opening_delim_len;
            break;
        }
    }

    /* find closing </delimiter> */
    for (end = start; end < limit; end++) {
        if (strncmp(end, closing_delim, closing_delim_len) == 0) {
            const size_t retval_len = end - start;
            retval = (char*)malloc((retval_len + 1) * sizeof(char));
            retval[retval_len] = '\0';
            strncpy(retval, start, retval_len);
            break;
        }
    }
    
    return retval;
}

static char*
extract_delim_and_verify(char *source, size_t source_len, const char *tag_name)
{
    char *retval = extract_delim(source, source_len, tag_name);
    if (retval == NULL)
        die("Couldn't extract value for '%s'", tag_name);
    return retval;
}

static void
write_charmony_postamble(void)
{
    FILE *charmony_fh = chaz_Probe_get_charmony_fh();

    if (strcmp(os_name, "mswin32") == 0) {
        fprintf(charmony_fh,
            "#ifndef KINO_HAS_LARGE_FILE_SUPPORT\n"
            "  #include \"win32lfs.h\"\n"  
            "#endif /* KINO_HAS_LARGE_FILE_SUPPORT */\n"
        );
    }
}

void 
die(char* format, ...) 
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
    exit(1);
}

/* Copyright 2006-2007 Marvin Humphrey
 *
 * This program is free software; you can redistribute it and/or modify
 * under the same terms as Perl itself.
 */