#!/usr/bin/icmake -qt /tmp/icmstart

string g_confpath;
string g_home;
string g_userHome;
string g_skelpath = "/usr/share/icmake";
string g_program;
string g_defaultcommand = "";
string g_defaultcommandArg;
string g_destpath;
string g_cwd;
list   g_mkdir;

int g_confirminstall = 0;
int g_skeletons = 1;
int g_replace = 0;
int g_debug = 0;
int g_askreplace = 1;

void usage()
{
    printf("Usage: ", g_program, " [Options] dir [program|library]\n"
        "Where:\n"
        "   Options:\n"
        "     -c confpath: Use the configuration files (icmstart.rc, "
                                                                "AUTHOR,\n"
        "                  VERSION, YEARS found in `confpath' rather than\n"
        "                  if found in $HOME/.icmake or /etc/icmake\n"
        "     -d: debug: do not execute any commands, but show commands\n"
        "         that would have been executed\n"
        "     -I: do NOT install any files.\n"
        "     -r: replace existing files/directories\n"
        "     -s skelpath: Read the skeleton information from the directory\n"
        "                  `skelpath' rather than /usr/share/icmake\n"
        "   dir: the directory to install the files into\n"
        "   program, library: command passed by default to the icmbuild "
                                                                    "script\n"
        "\n");

    exit(1);
}

void die(string s)
{
    printf(g_program, ": ", s, "\n");
    exit(1);
}

int isFileOrDir(string s)
{
    return (int)stat(P_NOCHECK, s)[0] & (S_IFREG | S_IFDIR);
}

int isFile(string s)
{
    return (int)stat(P_NOCHECK, s)[0] & S_IFREG;
}

void syscall(string command)
{
    if (g_debug)
        printf(command, "\n");
    else
        system(command);
}

void md(string dir)
{
    int idx;
    for (idx = 0; idx < sizeof(g_mkdir); ++idx)
    {
        if (g_mkdir[idx] == dir)
            return;
    }
    syscall("mkdir -p " + dir);    
    g_mkdir += (list)dir;
}

string absPath(string arg)
{
    if (arg[0] != "/")
        arg = g_cwd + arg;
    if (arg[strlen(arg) - 1] != "/")
        arg += "/";
    return arg;
}

void arguments(int argc, list argv)
{
    int cmdidx = 1;
    string arg;
    list icm;
    int length;

    icm = getenv("ICM");                // ICM environment var defined?

    if ((int)icm[0] == 1)                
        g_skelpath = icm[1];            // then re-assign skelpath

    while (cmdidx < argc)
    {
        arg = argv[cmdidx];

        if (arg[0] != "-")              // no (more) options
            break;

        if (arg[1] == "c")              // -c: re-assign g_confpath
        {
            arg = substr(arg, 2, 999);  // get all beyond -c

            if (arg == "")              // or get then next argument
            {
                if (cmdidx == argc)
                    die("-c lacks configuration file specification");

                arg = argv[++cmdidx];
            }
            g_confpath = absPath(arg);  // reassign confpath
        }
        else if (arg[1] == "d")         // -d: debug
            g_debug = 1;
        else if (arg[1] == "I")         // -I: no skeletons
            g_skeletons = 0;
        else if (arg[1] == "r")         // -r: replace existing file(s)
        {
            g_askreplace = 0;
            g_replace = 1;
        }
        else if (arg[1] == "s")         // -s: re-assign g_skelpath
        {
            arg = substr(arg, 2, 999);  // get all beyond -s

            if (arg == "")              // or get then next argument
            {
                if (cmdidx == argc)
                    die("-s lacks skeleton dir specification");

                arg = argv[++cmdidx];
            }
            g_skelpath = arg;           // reassign skelpath
        }
        else
            printf("[Warning] ignoring unkown option ", arg, "\n");

        ++cmdidx;
    }

    g_skelpath = absPath(g_skelpath);
    g_destpath = argv[cmdidx];
    if (g_destpath[strlen(g_destpath) - 1] != "/")
        g_destpath += "/";
    md(g_destpath);                       // install the target dir

    if (!g_debug)
        g_destpath = chdir(g_destpath);

    if (++cmdidx < argc)
    {
        g_defaultcommandArg = argv[cmdidx];
        if 
        (
            g_defaultcommandArg == "program" 
            || 
            g_defaultcommandArg == "library"
        )
            g_defaultcommand = 
                        "#define DEFCOM \"" + g_defaultcommandArg + "\"\n";
        else
            printf("Ignored initial command `", g_defaultcommandArg, 
                                                        "' for icmbuild\n");
    }
}

int replace(string target)
{
    string ret;

    while (g_askreplace)
    {
        printf("`", target, "' exists.\n"
               "Replace [?akNy] ? ");
        ret = getch();

        if (ret == "a")
        {
            g_replace = 1;
            g_askreplace = 0;
            break;
        }

        if (ret == "k")
        {
            g_askreplace = 0;
            break;
        }

        if (ret == "y")
            return 1;

        if (ret == "n" || ret == "\n")
            return 0;

        printf("Press `a' to replace all,\n"
               "      `k' to keep all current versions\n"
               "      `n' to keep current version (default)\n"
               "      `?' shows this help\n");
    }
    return g_replace;
}

int install(string target)
{
    return (int)(!exists(target) || replace(target));
}

int install_dest(string source, string dest)
{
    int idx;

    if (!isFileOrDir(source))
        die("Can't find `" + source + "'\n");

    if (dest == "")
        dest = change_path(source, g_destpath);
    else    
        dest = g_destpath + dest;

    idx = strlen(dest) - 1;

    if (dest[idx] == "/")
        dest = substr(dest, 0, idx);

    if (install(dest))
    {
        md(get_path(dest));
        syscall("cp -rd " + source + " " + dest);
        return 1;
    }
    return 0;
}

    // a line in the conffile may be organized as follows (except for empty
    // lines and comment lines starting at # in column 0)
    // name is either a file or a directory; directories are copied
    // completely. 
    //  name                - skelpath/name is installed as destpath/name
    //  ./path/name         - $cwd/path/name is installed as destpath/name
    //  /path/name          - /path/name is installed as destpath/name
    // a second entry may be dest (path OK): destpath/dest will be the
    // destination. E.g.:
    //  name  one/two       - skelpath/name is installed as destpath/one/two
    // All lines may start with a P (+space) or L (+space).
    // A source at a P-line is not installed when using 'icmstart xxx library'
    // A source at an L-line is not installed when using 'icmstart xxx
    // program' If omitted the source is installed unconditionally.
    // Following P or L (+space) an optional ? (+ space) may be specificed 
    // in which case the installation must be confirmed by the user
int install_file(string source, string dest)
{
    int idx;
    string rawDest;
    
    if (source[0] == ".")
        source = g_cwd + source;
    else
    {
        idx = strfind(source, "/");

        if (idx == -1)
            source = g_skelpath + source;
        else if (idx > 0)
            source = g_cwd + source;
    }
    return install_dest(source, dest);
}

string find(string path, string file)
{
    string ret;

    ret = path + "/" + file;
    if (exists(ret))
        return ret;

    return "";
}

string findFile(string file)
{
    string ret;

    if (g_confpath != "")                   // locate file in -c path
        ret = find(g_confpath, file);

    if (ret == "")                          // not found, locate in $HOME
        ret = find(g_home, file);

    if (ret == "")                          // not found, locate in CONFDIR
        ret = find("/etc/icmake", file);

    return ret;
}

void install_conf(string file, string default)
{
    string ret;
    ret = findFile(file);

    if (g_skeletons)
    {
        if (ret != "")
            system("cat " + ret + " >> " + g_destpath + "VERSION");
        else
            fprintf(g_destpath + "VERSION", 
                        "#define ", file, " \"", default, "\"\n");
    }
}    

void install_version()
{
    string str;
    str = g_destpath + "VERSION";

    if (install(str))
    {
        system(P_NOCHECK, "rm -f " + g_destpath + "VERSION");
        install_conf("AUTHOR",  "");
        install_conf("VERSION", "0.00.00");

        str = "date '+%Y'";
        str = `str`[0];
        install_conf("YEARS", substr(str, 0, strlen(str) - 1));
    }
}

void install_std()
{
    install_version();
                                        // install a default command if 
                                        // provided as last arg.
    if (install_file("icmconf", "") && g_defaultcommand != "")
        fprintf(g_destpath + "icmconf", g_defaultcommand);
}

int confirminstall(string source, string dest)
{
    if (g_confirminstall)
    {
        if (source == dest)
            printf("Install `", dest, "' [yN] ? ");
        else
            printf("Install `", source, "' as `", dest, "' [yN] ? ");
        return getch() == "y";
    }

    return 1;
}

string check_home(string source)
{
    if (source[0] != "~")
        return source;

    return g_userHome + substr(source, 1, strlen(source));
}

list shift(list source)     // shift away the first element
{
    list ret;
    int idx;

    for (idx = 1; idx != sizeof(source); ++idx)
        ret += (list)source[idx];

    return ret;
}

    
    // a line in the conffile may be organized as follows (name is either
    //  a file or a directory; directories are copied completely):
    //  name        - name is located in skelpath and installed at destpath
    //  name  dest  - name is located in skelpath and installed at destpath/name
    //                if name is a directory then the destination will be
    //                destpath/dest/name. dest may also be /dest
    //  path/name   - relative or absolute path's name will be installed at
    //                destpath. Relative is relative to the startup directory
    //  path/name dest - relative or absolute path's name will be installed
    //                at destpath/dest. dest may also be /dest
    // All lines may start with a P (+space) or L (+space).
    // A source at a P-line is not installed when using 'icmstart xxx library'
    // A source at an L-line is not installed when using 'icmstart xxx
    // program' If omitted the source is installed unconditionally.
    // Following P or L (+space) an optional ? (+ space) may be specificed 
    // in which case the installation must be confirmed by the user

void install_line(string confline)
{
    int idx;
    list fields;
    string source;
    string dest;
    string plFlag;
    string install;

    fields = strtok(confline, " \t\n");
    source = fields[0];

                                            // empty line or comment
    if (sizeof(fields) == 0 || source[0] == "#")
        return;

    install = source;
    if (install == "P" || install == "L")
    {
        fields = shift(fields);             // rm the P/L-flag
        source = fields[0];
    }
    else
        install = "";

    g_confirminstall = source == "?";       // need confirmation ?

    if (g_confirminstall)
    {
        fields = shift(fields);             // rm the ?-mark
        source = fields[0];
    }

    if 
    (
        g_defaultcommandArg == "library" && install == "P"
        ||
        g_defaultcommandArg == "program" && install == "L"
    )
        return;                             // no P with libs, no L with progs


    if (sizeof(fields) > 1)
        dest = fields[1];
    else
        dest = source;

    source = check_home(source);
    dest   = check_home(dest);

    if (!g_replace && !g_askreplace && exists(g_destpath + dest))
        return;

    if (!confirminstall(source, dest))
            return;

    install_file(source, dest);
}

void install_rc()
{
    list line;
    string conffile;

    conffile = findFile("icmstart.rc");

    if (!isFile(conffile))
        die("Can't find configuration file `icmstart.rc'");
    
    while (1)
    {
        line = fgets(conffile, (int)line[1]);
        if (!line)
            break;
        install_line(line[0]);
    }
}

int main(int argc, list argv)
{
    echo(OFF);

    g_program = get_base(argv[0]);
    g_userHome = getenv("HOME")[1];
    g_home = g_userHome + "/.icmake";

    if (argc == 1)
        usage();

    g_cwd = chdir("");

    arguments(argc, argv);

    install_std();              // install CLASSES VERSION and icmconf
    install_rc();               // install .rc file elements

    printf("Done. Don't forget to inspect the #defines in\n"
            "'", g_destpath, "icmconf'\n");

    return 0;
}
