/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <assert.h>

#ifdef __EMX__
#  define SHELL_CMD  "sh"
#  define GEN_EXPORTS "emxexp"
#  define DEF2IMPLIB_CMD "emximp"
#  define SHARE_SW   "-Zdll -Zmtd"
#  define USE_OMF 1
#  define TRUNCATE_DLL_NAME
#  define DYNAMIC_LIB_EXT "dll"
#  define EXE_EXT ".exe"

#  if USE_OMF
     /* OMF is the native format under OS/2 */
#    define STATIC_LIB_EXT "lib"
#    define OBJECT_EXT     "obj"
#    define LIBRARIAN      "emxomfar"
#    define LIBRARIAN_OPTS "cr"
#  else
     /* but the alternative, a.out, can fork() which is sometimes necessary */
#    define STATIC_LIB_EXT "a"
#    define OBJECT_EXT     "o"
#    define LIBRARIAN      "ar"
#    define LIBRARIAN_OPTS "cr"
#  endif
#endif

#if defined(__APPLE__)
#  define SHELL_CMD  "/bin/sh"
#  define DYNAMIC_LIB_EXT "dylib"
#  define MODULE_LIB_EXT  "so"
#  define STATIC_LIB_EXT "a"
#  define OBJECT_EXT     "o"
#  define LIBRARIAN      "ar"
#  define LIBRARIAN_OPTS "cr"
/* man libtool(1) documents ranlib option of -c.  */
#  define RANLIB "ranlib"
#  define PIC_FLAG "-fPIC -fno-common"
#  define SHARED_OPTS "-dynamiclib"
#  define MODULE_OPTS "-bundle"
#  define DYNAMIC_LINK_OPTS "-flat_namespace -undefined suppress"
#  define dynamic_link_version_func darwin_dynamic_link_function
#  define DYNAMIC_INSTALL_NAME "-install_name"
#  define DYNAMIC_LINK_NO_INSTALL "-dylib_file"
#  define HAS_REALPATH
/*-install_name  /Users/jerenk/apache-2.0-cvs/lib/libapr.0.dylib -compatibility_version 1 -current_version 1.0 */
#  define LD_LIBRARY_PATH "DYLD_LIBRARY_PATH"
#endif

#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__)
#  define SHELL_CMD  "/bin/sh"
#  define DYNAMIC_LIB_EXT "so"
#  define MODULE_LIB_EXT  "so"
#  define STATIC_LIB_EXT "a"
#  define OBJECT_EXT     "o"
#  define LIBRARIAN      "ar"
#  define LIBRARIAN_OPTS "cr"
#  define RANLIB "ranlib"
#  define PIC_FLAG "-fPIC"
#  define RPATH "-rpath"
#  define SHARED_OPTS "-shared"
#  define MODULE_OPTS "-shared"
#  define DYNAMIC_LINK_OPTS "-export-dynamic"
#  define LINKER_FLAG_PREFIX "-Wl,"
#  define ADD_MINUS_L
#  define LD_RUN_PATH "LD_RUN_PATH"
#  define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
#endif

#if defined(sun)
#  define SHELL_CMD  "/bin/sh"
#  define DYNAMIC_LIB_EXT "so"
#  define MODULE_LIB_EXT  "so"
#  define STATIC_LIB_EXT "a"
#  define OBJECT_EXT     "o"
#  define LIBRARIAN      "ar"
#  define LIBRARIAN_OPTS "cr"
#  define RANLIB "ranlib"
#  define PIC_FLAG "-KPIC"
#  define RPATH "-R"
#  define SHARED_OPTS "-G"
#  define MODULE_OPTS "-G"
#  define DYNAMIC_LINK_OPTS ""
#  define LINKER_FLAG_NO_EQUALS
#  define ADD_MINUS_L
#  define HAS_REALPATH
#  define LD_RUN_PATH "LD_RUN_PATH"
#  define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
#endif

#if defined(_OSD_POSIX)
#  define SHELL_CMD  "/usr/bin/sh"
#  define DYNAMIC_LIB_EXT "so"
#  define MODULE_LIB_EXT  "so"
#  define STATIC_LIB_EXT "a"
#  define OBJECT_EXT     "o"
#  define LIBRARIAN      "ar"
#  define LIBRARIAN_OPTS "cr"
#  define SHARED_OPTS "-G"
#  define MODULE_OPTS "-G"
#  define LINKER_FLAG_PREFIX "-Wl,"
#  define NEED_SNPRINTF
#endif

#if defined(sinix) && defined(mips) && defined(__SNI_TARG_UNIX)
#  define SHELL_CMD  "/usr/bin/sh"
#  define DYNAMIC_LIB_EXT "so"
#  define MODULE_LIB_EXT  "so"
#  define STATIC_LIB_EXT "a"
#  define OBJECT_EXT     "o"
#  define LIBRARIAN      "ar"
#  define LIBRARIAN_OPTS "cr"
#  define RPATH "-Brpath"
#  define SHARED_OPTS "-G"
#  define MODULE_OPTS "-G"
#  define DYNAMIC_LINK_OPTS "-Wl,-Blargedynsym"
#  define LINKER_FLAG_PREFIX "-Wl,"
#  define NEED_SNPRINTF
#  define LD_RUN_PATH "LD_RUN_PATH"
#  define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
#endif

#ifndef SHELL_CMD
#error Unsupported platform: Please add defines for SHELL_CMD etc. for your platform.
#endif

#ifdef NEED_SNPRINTF
#include <stdarg.h>
#endif

#ifdef __EMX__
#include <process.h>
#endif

#ifndef PATH_MAX
#define PATH_MAX 1024
#endif


/* We want to say we are libtool 1.4 for shlibtool compatibility. */
#define VERSION "1.4"

enum tool_mode_t {
    mUnknown,
    mCompile,
    mLink,
    mInstall,
};

enum output_t {
    otGeneral,
    otObject,
    otProgram,
    otLibrary,
    otStaticLibraryOnly,
    otDynamicLibraryOnly,
    otModule,
};

enum pic_mode_e {
    pic_UNKNOWN,
    pic_PREFER,
    pic_AVOID,
};

enum lib_type {
    type_UNKNOWN,
    type_DYNAMIC_LIB,
    type_STATIC_LIB,
    type_MODULE_LIB,
    type_OBJECT,
};

typedef struct {
    const char **vals;
    int num; 
} count_chars;

typedef struct {
    const char *normal;
    const char *install;
} library_name;

typedef struct {
    count_chars *normal;
    count_chars *install;
    count_chars *dependencies;
} library_opts;

typedef struct {
    int silent;
    int shared;
    int export_all;
    int dry_run;
    enum pic_mode_e pic_mode;
    int export_dynamic;
    int no_install;
} options_t;

typedef struct {
    enum tool_mode_t mode;
    enum output_t output;
    options_t options;

    char *output_name;
    char *fake_output_name;
    char *basename;

    const char *install_path;
    const char *compiler;
    const char *program;
    count_chars *program_opts;

    count_chars *arglist;
    count_chars *tmp_dirs;
    count_chars *obj_files;
    count_chars *dep_rpaths;
    count_chars *rpaths;

    library_name static_name;
    library_name shared_name;
    library_name module_name;

    library_opts static_opts;
    library_opts shared_opts;

    const char *version_info;
} command_t;

#ifdef RPATH
void add_rpath(count_chars *cc, const char *path);
#endif

#if defined(NEED_SNPRINTF)
/* Write at most n characters to the buffer in str, return the
 * number of chars written or -1 if the buffer would have been
 * overflowed.
 *
 * This is portable to any POSIX-compliant system has /dev/null
 */
static FILE *f=NULL;
static int vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
{
       int res;

       if (f == NULL)
               f = fopen("/dev/null","w");
       if (f == NULL)
               return -1;

       setvbuf( f, str, _IOFBF, n );

       res = vfprintf( f, fmt, ap );

       if ( res > 0 && res < n ) {
               res = vsprintf( str, fmt, ap );
       }
       return res;
}
static int snprintf( char *str, size_t n, const char *fmt, ... )
{
        va_list ap;
        int res;
 
        va_start( ap, fmt );
        res = vsnprintf( str, n, fmt, ap );
        va_end( ap );
        return res;
}
#endif

void init_count_chars(count_chars *cc)
{
    cc->vals = (const char**)malloc(PATH_MAX);
    cc->num = 0;
}

void clear_count_chars(count_chars *cc)
{
    int i;
    for (i = 0; i < cc->num; i++) {
        cc->vals[i] = 0;
    }

    cc->num = 0;
}

void push_count_chars(count_chars *cc, const char *newval)
{
    cc->vals[cc->num++] = newval;
}

void insert_count_chars(count_chars *cc, const char *newval, int position)
{
    int i;

    for (i = cc->num; i > position; i--) {
        cc->vals[i] = cc->vals[i-1];
    }

    cc->vals[position] = newval;
    cc->num++;
}

void append_count_chars(count_chars *cc, count_chars *cctoadd)
{
    int i;
    for (i = 0; i < cctoadd->num; i++) {
        if (cctoadd->vals[i]) {
            push_count_chars(cc, cctoadd->vals[i]);
        }
    }
}

const char *flatten_count_chars(count_chars *cc)
{
    int i, size;
    char *newval;

    size = 0;
    for (i = 0; i < cc->num; i++) {
        if (cc->vals[i]) {
            size += strlen(cc->vals[i]) + 1;
        }
    }

    newval = (char*)malloc(size + 1);
    newval[0] = 0;

    for (i = 0; i < cc->num; i++) {
        if (cc->vals[i]) {
            strcat(newval, cc->vals[i]);
            strcat(newval, " ");
        }
    }

    return newval;
}

char *shell_esc(const char *str)
{
    int in_quote = 0;
    char *cmd;
    unsigned char *d;
    const unsigned char *s;

    cmd = (char *)malloc(2 * strlen(str) + 1);
    d = (unsigned char *)cmd;
    s = (const unsigned char *)str;

    for (; *s; ++s) {
        if (*s == '"') {
            *d++ = '\\';
            in_quote++;
        }
        else if (*s == '\\' || (*s == ' ' && (in_quote % 2))) {
            *d++ = '\\';
        }
        *d++ = *s;
    }

    *d = '\0';
    return cmd;
}

int external_spawn(command_t *cmd, const char *file, const char **argv)
{
    if (!cmd->options.silent) {
        const char **argument = argv;
        printf("Executing: ");
        while (*argument) {
            printf("%s ", *argument);
            argument++;
        }
        puts("");
    }

    if (cmd->options.dry_run) {
        return 0;
    }
#ifdef __EMX__
    return spawnvp(P_WAIT, file, argv);
#else
    {
        pid_t pid;
        pid = fork();
        if (pid == 0) {
            return execvp(argv[0], (char**)argv);
        }
        else {
            int statuscode;
            waitpid(pid, &statuscode, 0);
            if (WIFEXITED(statuscode)) {
                return WEXITSTATUS(statuscode);
            }
            return 0;
        }
    }
#endif
}

int run_command(command_t *cmd_data, count_chars *cc)
{
    char *command;
    const char *spawn_args[4];
    count_chars tmpcc;

    init_count_chars(&tmpcc);

    if (cmd_data->program) {
        push_count_chars(&tmpcc, cmd_data->program);
    }

    append_count_chars(&tmpcc, cmd_data->program_opts);

    append_count_chars(&tmpcc, cc);

    command = shell_esc(flatten_count_chars(&tmpcc));

    spawn_args[0] = SHELL_CMD;
    spawn_args[1] = "-c";
    spawn_args[2] = command;
    spawn_args[3] = NULL;
    return external_spawn(cmd_data, spawn_args[0], (const char**)spawn_args);
}

/*
 * print configuration
 * shlibpath_var is used in configure.
 */
void print_config()
{
#ifdef LD_RUN_PATH
    printf("runpath_var=%s\n", LD_RUN_PATH);
#endif
#ifdef LD_LIBRARY_PATH
    printf("shlibpath_var=%s\n", LD_LIBRARY_PATH);
#endif
#ifdef SHELL_CMD
    printf("SHELL=\"%s\"\n", SHELL_CMD);
#endif
}
/*
 * Add a directory to the runtime library search path.
 */
void add_runtimedirlib(char *arg, command_t *cmd_data)
{
#ifdef RPATH
    add_rpath(cmd_data->shared_opts.dependencies, arg);
#else
#endif
}

int parse_long_opt(char *arg, command_t *cmd_data)
{
    char *equal_pos = strchr(arg, '=');
    char var[50];
    char value[500];

    if (equal_pos) {
        strncpy(var, arg, equal_pos - arg);
        var[equal_pos - arg] = 0;
        strcpy(value, equal_pos + 1);
    } else {
        strcpy(var, arg);
    }

    if (strcmp(var, "silent") == 0) {
        cmd_data->options.silent = 1;
    } else if (strcmp(var, "mode") == 0) {
        if (strcmp(value, "compile") == 0) {
            cmd_data->mode = mCompile;
            cmd_data->output = otObject;
        }

        if (strcmp(value, "link") == 0) {
            cmd_data->mode = mLink;
            cmd_data->output = otLibrary;
        }

        if (strcmp(value, "install") == 0) {
            cmd_data->mode = mInstall;
        }
    } else if (strcmp(var, "shared") == 0) {
        if (cmd_data->mode == mLink) {
            cmd_data->output = otDynamicLibraryOnly;
        }
        cmd_data->options.shared = 1;
    } else if (strcmp(var, "export-all") == 0) {
        cmd_data->options.export_all = 1;
    } else if (strcmp(var, "dry-run") == 0) {
        printf("Dry-run mode on!\n");
        cmd_data->options.dry_run = 1;
    } else if (strcmp(var, "version") == 0) {
        printf("Version " VERSION "\n");
    } else if (strcmp(var, "help") == 0) {
        printf("Sorry.  No help available.\n");
    } else if (strcmp(var, "config") == 0) {
        print_config();
    } else if (strcmp(var, "tag") == 0) {
        if (strcmp(value, "CC") == 0) {
            /* Do nothing. */
        }
        if (strcmp(value, "CXX") == 0) {
            /* Do nothing. */
        }
    } else {
        return 0;
    }

    return 1;
}

/* Return 1 if we eat it. */
int parse_short_opt(char *arg, command_t *cmd_data)
{
    if (strcmp(arg, "export-dynamic") == 0) {
        cmd_data->options.export_dynamic = 1;
        return 1;
    }

    if (strcmp(arg, "module") == 0) {
        cmd_data->output = otModule;
        return 1;
    }

    if (strcmp(arg, "Zexe") == 0) {
        return 1;
    }

    if (strcmp(arg, "avoid-version") == 0) {
        return 1;
    }

    if (strcmp(arg, "prefer-pic") == 0) {
        cmd_data->options.pic_mode = pic_PREFER;
        return 1;
    }

    if (strcmp(arg, "prefer-non-pic") == 0) {
        cmd_data->options.pic_mode = pic_AVOID;
        return 1;
    }

    if (strcmp(arg, "static") == 0) {
        /* Don't respect it for now. */
        return 1;
    }

    if (cmd_data->mode == mLink) {
        if (strcmp(arg, "no-install") == 0) {
            cmd_data->options.no_install = 1;
            return 1;
        }
        if (arg[0] == 'L' || arg[0] == 'l') {
            /* Hack... */
            arg--;
            push_count_chars(cmd_data->shared_opts.dependencies, arg);
            return 1;
        } else if (arg[0] == 'R' && arg[1]) {
            /* -Rdir Add dir to runtime library search path. */
            add_runtimedirlib(&arg[1], cmd_data);
            return 1;
        }
    }
    return 0;
}

char *truncate_dll_name(char *path)
{
    /* Cut DLL name down to 8 characters after removing any mod_ prefix */
    char *tmppath = strdup(path);
    char *newname = strrchr(tmppath, '/') + 1;
    char *ext = strrchr(tmppath, '.');
    int len;

    if (ext == NULL)
        return tmppath;

    len = ext - newname;

    if (strncmp(newname, "mod_", 4) == 0) {
        strcpy(newname, newname + 4);
        len -= 4;
    }

    if (len > 8) {
        strcpy(newname + 8, strchr(newname, '.'));
    }

    return tmppath;
}

long safe_strtol(const char *nptr, const char **endptr, int base)
{
    long rv;

    errno = 0;

    rv = strtol(nptr, (char**)endptr, 10);

    if (errno == ERANGE) {
        return 0;
    }

    return rv; 
}

/* version_info is in the form of MAJOR:MINOR:PATCH */
const char *darwin_dynamic_link_function(const char *version_info)
{
    char *newarg;
    long major, minor, patch;

    major = 0;
    minor = 0;
    patch = 0;

    if (version_info) {
        major = safe_strtol(version_info, &version_info, 10);

        if (version_info) {
            if (version_info[0] == ':') {
                version_info++;
            }

            minor = safe_strtol(version_info, &version_info, 10);

            if (version_info) {
                if (version_info[0] == ':') {
                    version_info++;
                }

                patch = safe_strtol(version_info, &version_info, 10);

            }
        }
    }

    /* Avoid -dylib_compatibility_version must be greater than zero errors. */
    if (major == 0) {
        major = 1;
    }
    newarg = (char*)malloc(100);
    snprintf(newarg, 99,
             "-compatibility_version %ld -current_version %ld.%ld",
             major, major, minor);

    return newarg;
}

/* genlib values
 * 0 - static
 * 1 - dynamic
 * 2 - module
 */
char *gen_library_name(const char *name, int genlib)
{
    char *newarg, *newext;

    newarg = (char *)malloc(strlen(name) + 10);
    strcpy(newarg, ".libs/");

    if (genlib == 2 && strncmp(name, "lib", 3) == 0) {
        name += 3;
    }

    strcat(newarg, name);

    newext = strrchr(newarg, '.') + 1;

    switch (genlib) {
    case 0:
        strcpy(newext, STATIC_LIB_EXT);
        break;
    case 1:
        strcpy(newext, DYNAMIC_LIB_EXT);
        break;
    case 2:
        strcpy(newext, MODULE_LIB_EXT);
        break;
    }

    return newarg;
}

/* genlib values
 * 0 - static
 * 1 - dynamic
 * 2 - module
 */
char *gen_install_name(const char *name, int genlib)
{
    struct stat sb;
    char *newname;
    int rv;

    newname = gen_library_name(name, genlib);

    /* Check if it exists. If not, return NULL.  */
    rv = stat(newname, &sb);

    if (rv) {
        return NULL;
    }

    return newname;
}

char *check_object_exists(command_t *cmd, const char *arg, int arglen)
{
    char *newarg, *ext;
    int pass, rv;

    newarg = (char *)malloc(arglen + 10);
    memcpy(newarg, arg, arglen);
    newarg[arglen] = 0;
    ext = newarg + arglen;

    pass = 0;

    do {
        struct stat sb;

        switch (pass) {
        case 0:
            strcpy(ext, OBJECT_EXT);
            break;
/*
        case 1:
            strcpy(ext, NO_PIC_EXT);
            break;
*/
        default:
            break;
        } 

        if (!cmd->options.silent) {
            printf("Checking (obj): %s\n", newarg);
        }
        rv = stat(newarg, &sb);
    }
    while (rv != 0 && ++pass < 1);

    if (rv == 0) {
        if (pass == 1) {
            cmd->options.pic_mode = pic_AVOID;
        }
        return newarg;
    }

    return NULL;
}

/* libdircheck values:
 * 0 - no .libs suffix
 * 1 - .libs suffix
 */
char *check_library_exists(command_t *cmd, const char *arg, int pathlen,
                           int libdircheck, enum lib_type *libtype)
{
    char *newarg, *ext;
    int pass, rv, newpathlen;

    newarg = (char *)malloc(strlen(arg) + 10);
    strcpy(newarg, arg);
    newarg[pathlen] = 0;

    newpathlen = pathlen;
    if (libdircheck) {
        strcat(newarg, ".libs/");
        newpathlen += sizeof(".libs/") - 1;
    }

    strcpy(newarg+newpathlen, arg+pathlen);
    ext = strrchr(newarg, '.') + 1;

    pass = 0;

    do {
        struct stat sb;

        switch (pass) {
        case 0:
            if (cmd->options.pic_mode != pic_AVOID || cmd->options.shared) {
                strcpy(ext, DYNAMIC_LIB_EXT);
                *libtype = type_DYNAMIC_LIB;
                break;
            }
            pass = 1;
        case 1:
            strcpy(ext, STATIC_LIB_EXT);
            *libtype = type_STATIC_LIB;
            break;
        case 2:
            strcpy(ext, MODULE_LIB_EXT);
            *libtype = type_MODULE_LIB;
            break;
        case 3:
            strcpy(ext, OBJECT_EXT);
            *libtype = type_OBJECT;
            break;
        default:
            *libtype = type_UNKNOWN;
            break;
        } 

        if (!cmd->options.silent) {
            printf("Checking (lib): %s\n", newarg);
        }
        rv = stat(newarg, &sb);
    }
    while (rv != 0 && ++pass < 4);

    if (rv == 0) {
        return newarg;
    }

    return NULL;
}

char * load_install_path(const char *arg)
{
    FILE *f;
    char *path;

    path = malloc(PATH_MAX);

    f = fopen(arg,"r");
    if (f == NULL) {
        return NULL;
    }
    fgets(path, PATH_MAX, f);
    fclose(f);
    if (path[strlen(path)-1] == '\n') {
        path[strlen(path)-1] = '\0';
    }
    /* Check that we have an absolute path.
     * Otherwise the file could be a GNU libtool file.
     */
    if (path[0] != '/') {
        return NULL;
    }
    return path;
}

char * load_noinstall_path(const char *arg, int pathlen)
{
    char *newarg, *expanded_path;
    int newpathlen;

    newarg = (char *)malloc(strlen(arg) + 10);
    strcpy(newarg, arg);
    newarg[pathlen] = 0;

    newpathlen = pathlen;
    strcat(newarg, ".libs");
    newpathlen += sizeof(".libs") - 1;
    newarg[newpathlen] = 0;

#ifdef HAS_REALPATH
    expanded_path = malloc(PATH_MAX);
    expanded_path = realpath(newarg, expanded_path);
    /* Uh, oh.  There was an error.  Fall back on our first guess. */
    if (!expanded_path) {
        expanded_path = newarg;
    }
#else
    /* We might get ../ or something goofy.  Oh, well. */
    expanded_path = newarg;
#endif

    return expanded_path;
}

/* Read the final install location and add it to runtime library search path. */
#ifdef RPATH
void add_rpath(count_chars *cc, const char *path)
{
    int size = 0;
    char *tmp;

#ifdef LINKER_FLAG_PREFIX
    size = strlen(LINKER_FLAG_PREFIX);
#endif
    size = size + strlen(path) + strlen(RPATH) + 2;
    tmp = malloc(size);
    if (tmp == NULL) {
        return;
    }
#ifdef LINKER_FLAG_PREFIX
    strcpy(tmp, LINKER_FLAG_PREFIX);
    strcat(tmp, RPATH);
#else
    strcpy(tmp, RPATH);
#endif
#ifndef LINKER_FLAG_NO_EQUALS
    strcat(tmp, "=");
#endif
    strcat(tmp, path);

    push_count_chars(cc, tmp);
}

void add_rpath_file(count_chars *cc, const char *arg)
{
    const char *path;

    path = load_install_path(arg);
    if (path) {
        add_rpath(cc, path);
    }
}

void add_rpath_noinstall(count_chars *cc, const char *arg, int pathlen)
{
    const char *path;

    path = load_noinstall_path(arg, pathlen);
    if (path) {
        add_rpath(cc, path);
    }
}
#endif

#ifdef DYNAMIC_LINK_NO_INSTALL
void add_dylink_noinstall(count_chars *cc, const char *arg, int pathlen,
                          int extlen)
{
    const char *install_path, *current_path, *name;
    char *exp_argument;
    int i_p_len, c_p_len, name_len, dyext_len, cur_len;

    install_path = load_install_path(arg);
    current_path = load_noinstall_path(arg, pathlen);

    if (!install_path || !current_path) {
        return;
    }

    push_count_chars(cc, DYNAMIC_LINK_NO_INSTALL);

    i_p_len = strlen(install_path);
    c_p_len = strlen(current_path);

    name = arg+pathlen;
    name_len = extlen-pathlen;
    dyext_len = sizeof(DYNAMIC_LIB_EXT) - 1;

    /* No, we need to replace the extension. */
    exp_argument = (char *)malloc(i_p_len + c_p_len + (name_len*2) +
                                  (dyext_len*2) + 2);

    cur_len = 0;
    strcpy(exp_argument, install_path);
    cur_len += i_p_len;
    exp_argument[cur_len++] = '/';
    strncpy(exp_argument+cur_len, name, extlen-pathlen);
    cur_len += name_len;
    strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT);
    cur_len += dyext_len;
    exp_argument[cur_len++] = ':';
    strcpy(exp_argument+cur_len, current_path);
    cur_len += c_p_len;
    exp_argument[cur_len++] = '/';
    strncpy(exp_argument+cur_len, name, extlen-pathlen);
    cur_len += name_len;
    strcpy(exp_argument+cur_len, DYNAMIC_LIB_EXT);
    cur_len += dyext_len;

    push_count_chars(cc, exp_argument);
}
#endif

/* use -L -llibname to allow to use installed libraries */
void add_minus_l(count_chars *cc, const char *arg)
{
    char *newarg;
    char *name = strrchr(arg, '/');
    char *file = strrchr(arg, '.');
    char *lib  = strstr(name, "lib");

    if (name !=NULL && file != NULL && lib == name+1) {
        *name = '\0';
        *file = '\0';
        file = name;
        file = file+4;
        push_count_chars(cc, "-L");
	push_count_chars(cc, arg);
	/* we need one argument like -lapr-1 */
        newarg = malloc(strlen(file) + 3);
        strcpy(newarg, "-l");
        strcat(newarg, file);
        push_count_chars(cc, newarg);
    } else {
        push_count_chars(cc, arg);
    }
}

void add_linker_flag_prefix(count_chars *cc, const char *arg)
{
#ifndef LINKER_FLAG_PREFIX
    push_count_chars(cc, arg);
#else
    char *newarg;
    newarg = (char*)malloc(strlen(arg) + sizeof(LINKER_FLAG_PREFIX) + 1);
    strcpy(newarg, LINKER_FLAG_PREFIX);
    strcat(newarg, arg);
    push_count_chars(cc, newarg);
#endif
}

int parse_input_file_name(char *arg, command_t *cmd_data)
{
    char *ext = strrchr(arg, '.');
    char *name = strrchr(arg, '/');
    int pathlen;
    enum lib_type libtype;
    char *newarg;

    if (!ext) {
        return 0;
    }

    ext++;

    if (name == NULL) {
        name = strrchr(arg, '\\');

        if (name == NULL) {
            name = arg;
        } else {
            name++;
        }
    } else {
        name++;
    }

    pathlen = name - arg;

    if (strcmp(ext, "lo") == 0) {
        newarg = check_object_exists(cmd_data, arg, ext - arg);
        if (!newarg) {
            printf("Can not find suitable object file for %s\n", arg);
            exit(1);
        }
        if (cmd_data->mode != mLink) {
            push_count_chars(cmd_data->arglist, newarg);
        }
        else {
            push_count_chars(cmd_data->obj_files, newarg);
        }
        return 1;
    }

    if (strcmp(ext, "la") == 0) {
        switch (cmd_data->mode) {
        case mLink:
            /* Try the .libs dir first! */
            newarg = check_library_exists(cmd_data, arg, pathlen, 1, &libtype);
            if (!newarg) {
                /* Try the normal dir next. */
                newarg = check_library_exists(cmd_data, arg, pathlen, 0, &libtype);
                if (!newarg) {
                    printf("Can not find suitable library for %s\n", arg);
                    exit(1);
                }
            }

            /* It is not ok to just add the file: a library may added with:
               1 - -L path library_name. (For *.so in Linux).
               2 - library_name.
             */
#ifdef ADD_MINUS_L
            if (libtype == type_DYNAMIC_LIB) {
                 add_minus_l(cmd_data->shared_opts.dependencies, newarg);
            } else {
                 push_count_chars(cmd_data->shared_opts.dependencies, newarg);
            }
#else
            push_count_chars(cmd_data->shared_opts.dependencies, newarg);
#endif
            if (libtype == type_DYNAMIC_LIB) {
                if (cmd_data->options.no_install) {
#ifdef RPATH
                    add_rpath_noinstall(cmd_data->shared_opts.dependencies,
                                        arg, pathlen);
#endif
#ifdef DYNAMIC_LINK_NO_INSTALL
                    /*
                     * This doesn't work as Darwin's linker has no way to
                     * override at link-time the search paths for a
                     * non-installed library.
                     */
                    /*
                    add_dylink_noinstall(cmd_data->shared_opts.dependencies,
                                         arg, pathlen, ext - arg);
                    */
#endif
                }
                else {
#ifdef RPATH
                    add_rpath_file(cmd_data->shared_opts.dependencies, arg);
#endif
                }
            }
            break;
        case mInstall:
            /* If we've already recorded a library to install, we're most
             * likely getting the .la file that we want to install as.
             * The problem is that we need to add it as the directory,
             * not the .la file itself.  Otherwise, we'll do odd things.
             */
            if (cmd_data->output == otLibrary) {
                arg[pathlen] = '\0';
                push_count_chars(cmd_data->arglist, arg);
            }
            else {
                cmd_data->output = otLibrary;
                cmd_data->output_name = arg;
                cmd_data->static_name.install = gen_install_name(arg, 0);
                cmd_data->shared_name.install = gen_install_name(arg, 1);
                cmd_data->module_name.install = gen_install_name(arg, 2);
            }
            break;
        default:
            break;
        }
        return 1;
    }

    if (strcmp(ext, "c") == 0) {
        /* If we don't already have an idea what our output name will be. */
        if (cmd_data->basename == NULL) {
            cmd_data->basename = (char *)malloc(strlen(arg) + 4);
            strcpy(cmd_data->basename, arg);
            strcpy(strrchr(cmd_data->basename, '.') + 1, "lo");

            cmd_data->fake_output_name = strrchr(cmd_data->basename, '/');
            if (cmd_data->fake_output_name) {
                cmd_data->fake_output_name++;
            }
            else {
                cmd_data->fake_output_name = cmd_data->basename;
            }
        }
    }

    return 0;
}

int parse_output_file_name(char *arg, command_t *cmd_data)
{
    char *name = strrchr(arg, '/');
    char *ext = strrchr(arg, '.');
    char *newarg = NULL;
    int pathlen;

    cmd_data->fake_output_name = arg;

    if (name) {
        name++;
    }
    else {
        name = strrchr(arg, '\\');

        if (name == NULL) {
            name = arg;
        }
        else {
            name++;
        }
    }

    if (!ext) {
        cmd_data->basename = arg;
        cmd_data->output = otProgram;
#if defined(_OSD_POSIX)
        cmd_data->options.pic_mode = pic_AVOID;
#endif
        newarg = (char *)malloc(strlen(arg) + 5);
        strcpy(newarg, arg);
#ifdef EXE_EXT
        strcat(newarg, EXE_EXT);
#endif
        cmd_data->output_name = newarg;
        return 1;
    }

    ext++;
    pathlen = name - arg;

    if (strcmp(ext, "la") == 0) {
        assert(cmd_data->mode == mLink);

        cmd_data->basename = arg;
        cmd_data->static_name.normal = gen_library_name(arg, 0);
        cmd_data->shared_name.normal = gen_library_name(arg, 1);
        cmd_data->module_name.normal = gen_library_name(arg, 2);
        cmd_data->static_name.install = gen_install_name(arg, 0);
        cmd_data->shared_name.install = gen_install_name(arg, 1);
        cmd_data->module_name.install = gen_install_name(arg, 2);

#ifdef TRUNCATE_DLL_NAME
        if (shared) {
          arg = truncate_dll_name(arg);
        }
#endif

        cmd_data->output_name = arg;
        return 1;
    }

    if (strcmp(ext, "lo") == 0) {
        cmd_data->basename = arg;
        cmd_data->output = otObject;
        newarg = (char *)malloc(strlen(arg) + 2);
        strcpy(newarg, arg);
        ext = strrchr(newarg, '.') + 1;
        strcpy(ext, OBJECT_EXT);
        cmd_data->output_name = newarg;
        return 1;
    }

    return 0;
}

/* returns just a file's name without path or extension */
char *nameof(char *fullpath)
{
    char buffer[1024];
    char *ext;
    char *name = strrchr(fullpath, '/');

    if (name == NULL) {
        name = strrchr(fullpath, '\\');
    }

    if (name == NULL) {
        name = fullpath;
    } else {
        name++;
    }

    strcpy(buffer, name);
    ext = strrchr(buffer, '.');

    if (ext) {
        *ext = 0;
        return strdup(buffer);
    }

    return name;
}

void parse_args(int argc, char *argv[], command_t *cmd_data)
{
    int a;
    char *arg;
    int argused;

    for (a = 1; a < argc; a++) {
        arg = argv[a];
        argused = 1;

        if (arg[0] == '-') {
            if (arg[1] == '-') {
                argused = parse_long_opt(arg + 2, cmd_data);
            }
            else {
                argused = parse_short_opt(arg + 1, cmd_data);
            }

            /* We haven't done anything with it yet, try some of the
             * more complicated short opts... */
            if (argused == 0 && a + 1 < argc) {
                if (arg[1] == 'o' && !arg[2]) {
                    arg = argv[++a];
                    argused = parse_output_file_name(arg, cmd_data);
                } else if (strcmp(arg+1, "MT") == 0) {
                    if (!cmd_data->options.silent) {
                        printf("Adding: %s", arg);
                    }
                    push_count_chars(cmd_data->arglist, arg);
                    arg = argv[++a];
                    if (!cmd_data->options.silent) {
                        printf(" %s\n", arg);
                    }
                    push_count_chars(cmd_data->arglist, arg);
                    argused = 1;
                } else if (strcmp(arg+1, "rpath") == 0) {
                    /* Aha, we should try to link both! */
                    cmd_data->install_path = argv[++a];
                    argused = 1;
                } else if (strcmp(arg+1, "version-info") == 0) {
                    /* Store for later deciphering */
                    cmd_data->version_info = argv[++a];
                    argused = 1;
                } else if (strcmp(arg+1, "export-symbols-regex") == 0) {
                    /* Skip the argument. */
                    ++a;
                    argused = 1;
                } else if (arg[1] == 'R' && !arg[2]) {
                    /* -R dir Add dir to runtime library search path. */
                    add_runtimedirlib(argv[++a], cmd_data);
                    argused = 1;
                }
            }
        } else {
            argused = parse_input_file_name(arg, cmd_data);
        }

        if (!argused) {
            if (!cmd_data->options.silent) {
                printf("Adding: %s\n", arg);
            }
            push_count_chars(cmd_data->arglist, arg);
        }
    }

}

int explode_static_lib(const char *lib, command_t *cmd_data)
{
    char tmpdir[1024];
    char savewd[1024];
    char cmd[1024];
    const char *name;
    DIR *dir;
    struct dirent *entry;

    /* Bah! */
    if (cmd_data->options.dry_run) {
        return 0;
    }

    strcpy(tmpdir, lib);
    strcat(tmpdir, ".exploded");

    mkdir(tmpdir, 0);
    push_count_chars(cmd_data->tmp_dirs, strdup(tmpdir));
    getcwd(savewd, sizeof(savewd));

    if (chdir(tmpdir) != 0)
        return 1;

    strcpy(cmd, LIBRARIAN " x ");
    name = strrchr(lib, '/');

    if (name) {
        name++;
    } else {
        name = lib;
    }

    strcat(cmd, "../");
    strcat(cmd, name);
    system(cmd);
    chdir(savewd);
    dir = opendir(tmpdir);

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_name[0] != '.') {
            strcpy(cmd, tmpdir);
            strcat(cmd, "/");
            strcat(cmd, entry->d_name);
            push_count_chars(cmd_data->arglist, strdup(cmd));
        }
    }

    closedir(dir);
    return 0;
}

#ifdef GEN_EXPORTS
void generate_def_file(command_t *cmd_data)
{
    char def_file[1024];
    char implib_file[1024];
    char *ext;
    FILE *hDef;
    char *export_args[1024];
    int num_export_args = 0;
    char *cmd;
    int cmd_size = 0;
    int a;

    if (cmd_data->output_name) {
        strcpy(def_file, cmd_data->output_name);
        strcat(def_file, ".def");
        hDef = fopen(def_file, "w");

        if (hDef != NULL) {
            fprintf(hDef, "LIBRARY '%s' INITINSTANCE\n", nameof(cmd_data->output_name));
            fprintf(hDef, "DATA NONSHARED\n");
            fprintf(hDef, "EXPORTS\n");
            fclose(hDef);

            for (a = 0; a < cmd_data->num_obj_files; a++) {
                cmd_size += strlen(cmd_data->obj_files[a]) + 1;
            }

            cmd_size += strlen(GEN_EXPORTS) + strlen(def_file) + 3;
            cmd = (char *)malloc(cmd_size);
            strcpy(cmd, GEN_EXPORTS);

            for (a=0; a < cmd_data->num_obj_files; a++) {
                strcat(cmd, " ");
                strcat(cmd, cmd_data->obj_files[a] );
            }

            strcat(cmd, ">>");
            strcat(cmd, def_file);
            puts(cmd);
            export_args[num_export_args++] = SHELL_CMD;
            export_args[num_export_args++] = "-c";
            export_args[num_export_args++] = cmd;
            export_args[num_export_args++] = NULL;
            external_spawn(cmd_data, export_args[0], (const char**)export_args);
            cmd_data->arglist[cmd_data->num_args++] = strdup(def_file);

            /* Now make an import library for the dll */
            num_export_args = 0;
            export_args[num_export_args++] = DEF2IMPLIB_CMD;
            export_args[num_export_args++] = "-o";

            strcpy(implib_file, ".libs/");
            strcat(implib_file, cmd_data->basename);
            ext = strrchr(implib_file, '.');

            if (ext)
                *ext = 0;

            strcat(implib_file, ".");
            strcat(implib_file, STATIC_LIB_EXT);

            export_args[num_export_args++] = implib_file;
            export_args[num_export_args++] = def_file;
            export_args[num_export_args++] = NULL;
            external_spawn(cmd_data, export_args[0], (const char**)export_args);

        }
    }
}
#endif

const char* expand_path(const char *relpath)
{
    char foo[PATH_MAX], *newpath;

    getcwd(foo, PATH_MAX-1);
    newpath = (char*)malloc(strlen(foo)+strlen(relpath)+2);
    strcat(newpath, foo);
    strcat(newpath, "/");
    strcat(newpath, relpath);
    return newpath;
}

void link_fixup(command_t *c)
{
    /* If we were passed an -rpath directive, we need to build
     * shared objects too.  Otherwise, we should only create static
     * libraries.
     */
    if (!c->install_path && (c->output == otDynamicLibraryOnly ||
        c->output == otModule || c->output == otLibrary)) {
        c->output = otStaticLibraryOnly;
    }

    if (c->output == otDynamicLibraryOnly ||
        c->output == otModule ||
        c->output == otLibrary) {

        push_count_chars(c->shared_opts.normal, "-o");
        if (c->output == otModule) {
            push_count_chars(c->shared_opts.normal, c->module_name.normal);
        }
        else {
            char *tmp;
            push_count_chars(c->shared_opts.normal, c->shared_name.normal);
#ifdef DYNAMIC_INSTALL_NAME
            push_count_chars(c->shared_opts.normal, DYNAMIC_INSTALL_NAME);

            tmp = (char*)malloc(PATH_MAX);
            strcat(tmp, c->install_path);
            strcat(tmp, strrchr(c->shared_name.normal, '/'));
            push_count_chars(c->shared_opts.normal, tmp);
#endif
        }

        append_count_chars(c->shared_opts.normal, c->obj_files);
        append_count_chars(c->shared_opts.normal, c->shared_opts.dependencies);

        if (c->options.export_all) {
#ifdef GEN_EXPORTS
            generate_def_file(c);
#endif
        }
    }

    if (c->output == otLibrary || c->output == otStaticLibraryOnly) {
        push_count_chars(c->static_opts.normal, "-o");
        push_count_chars(c->static_opts.normal, c->output_name);
    }

    if (c->output == otProgram) {
        if (c->output_name) {
            push_count_chars(c->arglist, "-o");
            push_count_chars(c->arglist, c->output_name);
            append_count_chars(c->arglist, c->obj_files);
            append_count_chars(c->arglist, c->shared_opts.dependencies);
#ifdef DYNAMIC_LINK_OPTS
            if (c->options.pic_mode != pic_AVOID) {
                push_count_chars(c->arglist, DYNAMIC_LINK_OPTS);
            }
#endif
        }
    }
}

void post_parse_fixup(command_t *cmd_data)
{
    switch (cmd_data->mode)
    {
    case mCompile:
#ifdef PIC_FLAG
        if (cmd_data->options.pic_mode != pic_AVOID) {
            push_count_chars(cmd_data->arglist, PIC_FLAG);
        }
#endif
        if (cmd_data->output_name) {
            push_count_chars(cmd_data->arglist, "-o");
            push_count_chars(cmd_data->arglist, cmd_data->output_name);
        }
        break;
    case mLink:
        link_fixup(cmd_data);
        break;
    case mInstall:
        if (cmd_data->output == otLibrary) {
            link_fixup(cmd_data);
        }
    default:
        break;
    }

#if USE_OMF
    if (cmd_data->output == otObject ||
        cmd_data->output == otProgram ||
        cmd_data->output == otLibrary ||
        cmd_data->output == otDynamicLibraryOnly) {
        push_count_chars(cmd_data->arglist, "-Zomf");
    }
#endif

    if (cmd_data->options.shared &&
            (cmd_data->output == otObject ||
             cmd_data->output == otLibrary ||
             cmd_data->output == otDynamicLibraryOnly)) {
#ifdef SHARE_SW
        push_count_chars(cmd_data->arglist, SHARE_SW);
#endif
    }
}

int run_mode(command_t *cmd_data)
{
    int rv;
    count_chars *cctemp;

    cctemp = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cctemp);

    switch (cmd_data->mode)
    {
    case mCompile:
        rv = run_command(cmd_data, cmd_data->arglist);
        if (rv) {
            return rv;
        }
        break;
    case mInstall:
        /* Well, we'll assume it's a file going to a directory... */
        /* For brain-dead install-sh based scripts, we have to repeat
         * the command N-times.  install-sh should die.
         */
        if (!cmd_data->output_name) {
            rv = run_command(cmd_data, cmd_data->arglist);
            if (rv) {
                return rv;
            }
        }
        if (cmd_data->output_name) {
            append_count_chars(cctemp, cmd_data->arglist);
            insert_count_chars(cctemp,
                               cmd_data->output_name,
                               cctemp->num - 1);
            rv = run_command(cmd_data, cctemp);
            if (rv) {
                return rv;
            }
            clear_count_chars(cctemp);
        }
        if (cmd_data->static_name.install) {
            append_count_chars(cctemp, cmd_data->arglist);
            insert_count_chars(cctemp,
                               cmd_data->static_name.install,
                               cctemp->num - 1);
            rv = run_command(cmd_data, cctemp);
            if (rv) {
                return rv;
            }
            clear_count_chars(cctemp);
        }
        if (cmd_data->shared_name.install) {
            append_count_chars(cctemp, cmd_data->arglist);
            insert_count_chars(cctemp,
                               cmd_data->shared_name.install,
                               cctemp->num - 1);
            rv = run_command(cmd_data, cctemp);
            if (rv) {
                return rv;
            }
            clear_count_chars(cctemp);
        }
        if (cmd_data->module_name.install) {
            append_count_chars(cctemp, cmd_data->arglist);
            insert_count_chars(cctemp,
                               cmd_data->module_name.install,
                               cctemp->num - 1);
            rv = run_command(cmd_data, cctemp);
            if (rv) {
                return rv;
            }
            clear_count_chars(cctemp);
        }
        break;
    case mLink:
        if (!cmd_data->options.dry_run) {
            /* Check first to see if the dir already exists! */
            mode_t old_umask;

            old_umask = umask(0);
            umask(old_umask);

            mkdir(".libs", ~old_umask);
        }

        if (cmd_data->output == otStaticLibraryOnly ||
            cmd_data->output == otLibrary) {
#ifdef RANLIB
            const char *lib_args[3];
#endif
            /* Removes compiler! */
            cmd_data->program = LIBRARIAN;
            push_count_chars(cmd_data->program_opts, LIBRARIAN_OPTS);
            push_count_chars(cmd_data->program_opts,
                             cmd_data->static_name.normal);

            rv = run_command(cmd_data, cmd_data->obj_files);
            if (rv) {
                return rv;
            }

#ifdef RANLIB
            lib_args[0] = RANLIB;
            lib_args[1] = cmd_data->static_name.normal;
            lib_args[2] = NULL;
            external_spawn(cmd_data, RANLIB, lib_args);
#endif
        }

        if (cmd_data->output == otDynamicLibraryOnly ||
            cmd_data->output == otModule ||
            cmd_data->output == otLibrary) {
            cmd_data->program = NULL;
            clear_count_chars(cmd_data->program_opts);

            append_count_chars(cmd_data->program_opts, cmd_data->arglist);
            if (cmd_data->output != otModule) {
#ifdef SHARED_OPTS
                push_count_chars(cmd_data->program_opts, SHARED_OPTS);
#endif
#ifdef dynamic_link_version_func
                push_count_chars(cmd_data->program_opts,
                             dynamic_link_version_func(cmd_data->version_info));
#endif
            }
            if (cmd_data->output == otModule) {
#ifdef MODULE_OPTS
                push_count_chars(cmd_data->program_opts, MODULE_OPTS);
#endif
            }
#ifdef DYNAMIC_LINK_OPTS
            if (cmd_data->options.pic_mode != pic_AVOID) {
                push_count_chars(cmd_data->program_opts,
                                 DYNAMIC_LINK_OPTS);
            }
#endif
            rv = run_command(cmd_data, cmd_data->shared_opts.normal);
            if (rv) {
                return rv;
            }
        }
        if (cmd_data->output == otProgram) {
            rv = run_command(cmd_data, cmd_data->arglist);
            if (rv) {
                return rv;
            }
        }
        break;
    default:
        break;
    } 

    return 0;
}

void cleanup_tmp_dir(const char *dirname)
{
    DIR *dir;
    struct dirent *entry;
    char fullname[1024];

    dir = opendir(dirname);

    if (dir == NULL)
        return;

    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_name[0] != '.') {
            strcpy(fullname, dirname);
            strcat(fullname, "/");
            strcat(fullname, entry->d_name);
            remove(fullname);
        }
    }

    rmdir(dirname);
}

void cleanup_tmp_dirs(command_t *cmd_data)
{
    int d;

    for (d = 0; d < cmd_data->tmp_dirs->num; d++) {
        cleanup_tmp_dir(cmd_data->tmp_dirs->vals[d]);
    }
}

int ensure_fake_uptodate(command_t *cmd_data)
{
    /* FIXME: could do the stat/touch here, but nah... */
    const char *touch_args[3];

    if (cmd_data->mode == mInstall) {
        return 0;
    }

    touch_args[0] = "touch";
    touch_args[1] = cmd_data->fake_output_name;
    touch_args[2] = NULL;
    return external_spawn(cmd_data, "touch", touch_args);
}

/* Store the install path in the *.la file */
int add_for_runtime(command_t *cmd_data)
{
    if (cmd_data->mode == mInstall) {
        return 0;
    }
    if (cmd_data->output == otDynamicLibraryOnly ||
        cmd_data->output == otLibrary) {
        FILE *f=fopen(cmd_data->fake_output_name,"w");
        if (f == NULL) {
            return -1;
        }
        fprintf(f,"%s\n", cmd_data->install_path);
        fclose(f);
        return(0);
    } else {
        return(ensure_fake_uptodate(cmd_data));
    }
}

int main(int argc, char *argv[])
{
    int rc;
    command_t cmd_data;

    memset(&cmd_data, 0, sizeof(cmd_data));

    cmd_data.options.pic_mode = pic_UNKNOWN;

    cmd_data.program_opts = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.program_opts);
    cmd_data.arglist = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.arglist);
    cmd_data.tmp_dirs = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.tmp_dirs);
    cmd_data.obj_files = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.obj_files);
    cmd_data.dep_rpaths = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.dep_rpaths);
    cmd_data.rpaths = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.rpaths);
    cmd_data.static_opts.normal = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.static_opts.normal);
    cmd_data.shared_opts.normal = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.shared_opts.normal);
    cmd_data.shared_opts.dependencies = (count_chars*)malloc(sizeof(count_chars));
    init_count_chars(cmd_data.shared_opts.dependencies);

    cmd_data.mode = mUnknown;
    cmd_data.output = otGeneral;

    parse_args(argc, argv, &cmd_data);
    post_parse_fixup(&cmd_data);

    if (cmd_data.mode == mUnknown) {
        exit(0);
    }

    rc = run_mode(&cmd_data);

    if (!rc) {
       add_for_runtime(&cmd_data); 
    }

    cleanup_tmp_dirs(&cmd_data);
    return rc;
}
