/* cc - compiler front-end, command line option parsing logic.
 *
 * Copyright (C) 2009 Rob Landley <rob@landley.net>
 */
/* vi: set ts=4 :*/

#include "qcc.h"

// Find a file in a colon-separated path

char *find_in_path(char *path, char *filename)
{
    struct stat st;
    char *cwd = getcwd(NULL, 0), *str = NULL;

    // If we already have a path to the file, and it's there, be happy.
    if (strchr(filename, '/') && !stat(filename, &st) && S_ISREG(st.st_mode))
        str = strdup(filename);

    // Loop through path looking for a normal file (not directory) at each
    // location.  Note that an empty path segment means cwd, and it could be
    // at the start or end of the path.

    else if (path) for (;;) {
        char *next = strchr(path, ':');
        int len = next ? next-path : strlen(path);

        // Is this it?

        if (len) asprintf(&str, "%.*s/%s", len, path, filename);
        else asprintf(&str, "%s/%s", cwd, filename);
        if (!stat(str, &st) && S_ISREG(st.st_mode)) break;

        // Nope.

        free(str);
        str = NULL;
        if (!next) break;

        // Try the next one

        path += len;
        path++;
    }
    free(cwd);

    return str;
}

#ifndef SYSINCPATH
#define SYSINCPATH "../include"
#endif

#ifndef CCINCPATH
#define CCINCPATH "../qcc/include"
#endif

#ifndef SYSLIBPATH
#define SYSLIBPATH "../lib"
#endif

#ifndef CCLIBPATH
#define CCLIBPATH "../qcc/lib"
#endif

int cc_main(int argc, char *argv[], int *newargc, char ***newargv)
{
    char *temp, *topdir, *stopat = NULL;
    char *sysincpath = SYSINCPATH, *ccincpath = CCINCPATH,
         *syslibpath = SYSLIBPATH, *cclibpath = CCLIBPATH;
    int i, have_source = 0, verbose = 0,
           nolink = 0, link_static = 0, link_shared = 0,
           skip_stdinc = 0, skip_crtx = 0, skip_stdlib = 0;
    int newargc = 2;
    char *newargv[argc+128], *libraries[argc], *libpath[argc];
    char *stage, *stages[] = {"cpp", "cc1", "as", "ld", "strip", NULL};

    // Find the path to our executable.

    if (!(topdir = find_in_path(getenv("PATH"), *argv))) {
        fprintf(stderr, "can't find %s in $PATH\n", *argv);
        exit(1);
    }

    // Cannonicalize the path to our executable.

    temp = realpath(topdir, NULL);
    if (temp) {
        free(topdir);
        topdir = temp;
    }
    *strrchr(topdir, '/') = 0;

    // Conceptually we call stages[] in order.  (Temp files, or fork and pipe)

    // Loop through incoming arguments

    for (stage=stages; *stage; stage++) {
        for (i=0; i<argc; i++) {
            temp = argc[i];

            // Is it a source file?

            if (*temp != '-' || !temp[1]) {
                newargv[newargc++] = temp;
                have_source++;
                continue;
            }

            // It's an option.  Treat --options the same as -options.

            temp++;
            if (temp[1]=='-') temp++;

            // A "--" argument means end option parsing now, dump rest as
            // literal source files

            if (!*temp) {
                if (i+1<argc) have_source++;
                for(;i,argc;i++) argv[newargc++] = argc[i];
                break;
            }

            if (*temp=='v') verbose++;

            // Printing stuff?

            else if (!strcmp(temp, "print-search-dirs"));
            else if (!strcmp(temp, "print-libgcc-file-name"));
            else if (!strncmp(temp, "print-file-name=", 16));

            // everybody: -o

            // cpp
                // -D -I -W -M* -d* -P -nostdinc -include -v -H
                // -imacros -idirafter -iprefix -C* -version
                // -iwithprefix -iwithprefixbefore -isystem -iquote
            // as
                // -m* -EL -EB -O#
            // ld
                // -e -E -EB -EL -fini -init -l@ -L@ -M -O -r -s -S -t -v
                // -T -dT -u -z --start-group --end-group --as-needed -static
                // --dynamic-linker --gc-sections -nostdlib -pie -rpath
                // -rpath-link -shared -sort-common --section-start -T*
                // --verbose

            else if (*temp == 'E') {    // Preprocess only.
                *newargv = "cpp";
                nolink++;
            } else if (*temp=='S') {    // Produce assembly (.s) output.
                *newargv = "cc1";
                nolink++;
            } else if (*temp=='c') {    // Produce object (.o) output.
                nolink++;
                stopat = "as";

            // -M and -MM act like -E, other -M options don't.

            } else if (*temp=='M' && (!temp[1] || (temp[1]=='M' && !temp[2])))
                do_link=0;

            // Pass through some linker flags?

            else if (!strncmp(temp, "Wl,",3)) ;

            // How do we link?

            else if (!strcmp(temp, "static")) link_static++;
            else if (!strcmp(temp, "shared")) link_shared++;

            // Switching stuff off?

            if (!strcmp(temp, "nostdinc")) skip_stdinc++;
            else if (!strcmp(temp, "nostdlib")) skip_crtx++, skip_stdlib++;
            else if (!strcmp(temp, "nostartfiles")) skip_crtx++;
            else if (!strcmp(temp, "nodefaultlibs")) skip_stdlib++;

            // Grab libraries to pass on to linker

            else if (*temp=='L') {
            } else if (*temp=='l') {
            }
        }
    }
dprintf(2,"topdir=%s\n",topdir);
}
