#define CHAZ_USE_SHORT_NAMES

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/Util.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/OperatingSystem.h"

static char dev_null[20] = "";

#ifdef _WIN32
static char *exe_ext = ".exe";
static char *obj_ext = ".obj";
static char *local_command_start = ".\\";
#else
static char *exe_ext = "";
static char *obj_ext = "";
static char *local_command_start = "./";
#endif

static void
S_probe_dev_null(void);

/* Compile a small wrapper application which is used to redirect error output
 * to dev_null.
 */
static void
S_build_charm_run();

static chaz_bool_t charm_run_initialized = false;
static chaz_bool_t charm_run_ok = false;

void
OS_init() 
{
    if (Util_verbosity) {
        printf("Initializing Charmonizer/Core/OperatingSystem...\n");
    }

    S_probe_dev_null();
}

static void
S_probe_dev_null(void)
{
    if (Util_verbosity) {
        printf("Trying to find a bit-bucket a la /dev/null...\n");
    }

#ifdef _WIN32
    strcpy(dev_null, "nul");
#else
    {
        char *const options[] = {
            "/dev/null", 
            "/dev/nul", 
            NULL
        };
        int i;

        /* Iterate through names of possible devnulls trying to open them. */
        for (i = 0; options[i] != NULL; i++) {
            if (Util_can_open_file(options[i])) {
                strcpy(dev_null, options[i]);
                return;
            }
        }

        /* Bail out because we couldn't find anything like /dev/null. */
        Util_die("Couldn't find anything like /dev/null");
    }
#endif
}

void
OS_clean_up(void)
{
    OS_remove_exe("_charm_run");
}

const char*
OS_exe_ext(void)
{
    return exe_ext;
}

const char*
OS_obj_ext(void)
{
    return obj_ext;
}

const char*
OS_dev_null(void)
{
    return dev_null;
}

static char charm_run_code[] = 
    QUOTE(  #include <stdio.h>                                           )
    QUOTE(  #include <stdlib.h>                                          )
    QUOTE(  #include <string.h>                                          )
    QUOTE(  #include <stddef.h>                                          )
    QUOTE(  int main(int argc, char **argv)                              )
    QUOTE(  {                                                            )
    QUOTE(      char *command;                                           )
    QUOTE(      size_t command_len = 1; /* Terminating null. */          )
    QUOTE(      int i;                                                   )
    QUOTE(      int retval;                                              )
                /* Rebuild command line args. */ 
    QUOTE(      for (i = 1; i < argc; i++) {                             )
    QUOTE(          command_len += strlen(argv[i]) + 1;                  )
    QUOTE(      }                                                        )
    QUOTE(      command = (char*)calloc(command_len, sizeof(char));      )
    QUOTE(      if (command == NULL) {                                   )
    QUOTE(          fprintf(stderr, "calloc failed\n");                  )
    QUOTE(          exit(1);                                             )
    QUOTE(      }                                                        )
    QUOTE(      for (i = 1; i < argc; i++) {                             )
    QUOTE(          strcat( strcat(command, " "), argv[i] );             )
    QUOTE(      }                                                        )
                /* Redirect all output to /dev/null or equivalent. */
    QUOTE(      freopen("%s", "w", stdout);                              )
    QUOTE(      freopen("%s", "w", stderr);                              )
                /* Run commmand and return its value to parent. */
    QUOTE(      retval = system(command);                                )
    QUOTE(      free(command);                                           )
    QUOTE(      return retval;                                           )
    QUOTE(  }                                                            );

static void
S_build_charm_run()
{
    chaz_bool_t compile_succeeded = false;
    const char *dev_null = OS_dev_null();
    size_t needed = sizeof(charm_run_code)
                  + strlen(dev_null)
                  + strlen(dev_null)
                  + 20;
    char *code = (char*)malloc(needed);

    sprintf(code, charm_run_code, dev_null, dev_null);
    compile_succeeded = CC_compile_exe("_charm_run.c", "_charm_run", 
        code, strlen(code));
    if (!compile_succeeded) {
        Util_die("failed to compile _charm_run helper utility");
    }

    remove("_charm_run.c");
    free(code);
    charm_run_ok = true;
}

void
OS_remove_exe(char *name)
{
    char *exe_name = (char*)malloc(strlen(name) + strlen(exe_ext) + 1);
    sprintf(exe_name, "%s%s", name, exe_ext);
    remove(exe_name);
    free(exe_name);
}

void
OS_remove_obj(char *name)
{
    char *obj_name = (char*)malloc(strlen(name) + strlen(obj_ext) + 1);
    sprintf(obj_name, "%s%s", name, obj_ext);
    remove(obj_name);
    free(obj_name);
}

int
OS_run_local(char *arg1, ...)
{
    va_list  args;
    size_t   len     = strlen(local_command_start) + strlen(arg1);
    char    *command = (char*)malloc(len + 1);
    int      retval;
    char    *arg;

    /* Append all supplied texts. */
    sprintf(command, "%s%s", local_command_start, arg1);
    va_start(args, arg1);
    while (NULL != (arg = va_arg(args, char*))) {
        len += strlen(arg);
        command = (char*)realloc(command, len + 1);
        strcat(command, arg);
    }
    va_end(args);

    /* Run the command. */
    retval = system(command);
    free(command);
    return retval;
}

int
OS_run_quietly(const char *command)
{
    int retval = 1;
    
    if (!charm_run_initialized) {
        charm_run_initialized = true;
        S_build_charm_run();
    }

    if (!charm_run_ok) {
        retval = system(command);
    }
    else {
        retval = OS_run_local("_charm_run ", command, NULL);
    }

    return retval;
}

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

