void showCd(string dir)
{
    printf("\n"
            "chdir ", dir, "\n");
}

string setOpt(string install_im, string envvar)
{
    list optvar;
    string ret;

    optvar = getenv(envvar);    

    if (optvar[0] == "1")
        ret = optvar[1];
    else 
        ret = install_im;

    return ret;
}

list patternList(string pattern)
{
    list in;
    list ret;
    int idx;

    in = strtok(pattern, " \t");
    for (idx = sizeof(in); idx--; )
        ret += makelist(in[idx]);

    return ret;
}

void parser()
{
    list gramfiles;
    int idx;

    chdir("parser");

    gramfiles = (list)"grammar" + makelist("inc/*");

    for (idx = sizeof(gramfiles); idx--; )
    {
        if (gramfiles[idx] younger "parse.cc")
        {
            exec("bisonc++", "-V", "grammar");
            break;
        }
    }
    chdir("..");
}


int find(string needle, list haystack)
{
    int idx;
    for (idx = sizeof(haystack); idx--; )
    {
        if (needle == haystack[idx])
            break;
    }
    return idx;
}

void touch(string dir)
{
    echo(OFF);
    if (!exists(dir + "/" "a"))
        system("touch " + dir + "/" "a");
    echo(ON);
}
            
    //  g_classes[idx] has "a" set. 
    //  find all classes that depend on g_classes[idx], 
    //  if those classes haven't been inspected yet, then set their "a" 
    //  file and inspect their dependencies.
void dependendenciesOf(string thisClass)
{
    int ret;
    int idx;
    string classLine;
    string hit;

    if (find(thisClass, g_inspected) != -1) // this class already inspected
        return;                             // then done with this class

    g_base = 1;                             // recompile the base's files

    printf("RECOMPILE: ", thisClass, "\n");
    g_inspected += (list)thisClass;         // now inspected

        // find all classes depending on thisClass
    for (idx = 0; idx != g_nClasses; ++idx)
    {
        classLine = g_classLines[idx];

        if (strfind(classLine, thisClass) > 0)
        {
            hit = strtok(classLine, " \t")[0];
            touch(hit);
            dependendenciesOf(hit);
        }
    }
}            
    
void checkALL()
{
    int idx;
    string all;

    for (idx = 0; idx != g_nClasses; ++idx)
    {
        all = g_classes[idx] + "/" "a";

        if (exists(all))
            dependendenciesOf(g_classes[idx]);
    }

    if (g_base)
        touch(".");     // touches ./a
}
    
void setClasses()
{
    string dir;
    list class;

    int idx;

    g_classes = (list)"scanner" + (list)"parser";

    while (sizeof(class = fgets("CLASSES", (int)class[1])))
    {
        dir = element(0, strtok(class[0], " \t\n"));

        if 
        (
            strlen(dir)                 // omit empty lines
            &&
            strfind(dir, "#") != 0      // omit lines starting in #
            &&
            strfind(dir, "/") != 0      // omit lines starting in //
        )
        {
            if 
            (
                dir != "scanner"          // "scanner" is already there
                &&
                dir != "parser"           // "parser" is already there
            )
                g_classes += (list)dir;     // add this dir.

            g_classLines += (list)class[0];
        }
    }

    // classLines contains the full lines of the CLASSES file, and thus
    // stores its dependencies.

    g_nClasses = sizeof(g_classes);

    // g_classes has been defined. checkALL creates the file ALL in all 
    // directories depending on classes containing the file ALL
    checkALL();
}

list inspect(string destDir, 
             int prefix, string srcDir, list srcList, string library)
{
    int idx;
    string ofile;
    string oprefix;
    string file;
    string source;
    string all;

    oprefix = destDir + "/" + (string)prefix;
    srcDir += "/";
    all = srcDir + "a";

    for (idx = sizeof(srcList); idx--; )
    {
        file  = srcList[idx];   
        source = srcDir + file;
        
        ofile   = oprefix + change_ext(file, ".o");    // make o-filename

        // A file s must be recompiled if:
        //  the ofile exists and is older than ALL
        //  the ofile doesn't exist but the lib. exists and is older than ALL
        //  neither the ofile nor the lib. exists and ALL exists

        // if ALL doesn't exist it must be recompiled if:
        //  it's newer than its object file o or newer than its target 
        //  library l, 
        //  if neither o nor l exist.

        // Since `a newer b' is true if a is newer than b, or if a exists and
        // b doesn't exist s must be compiled if s newer o and s newer l.
        // So, it doesn't have to be compiled if s older o or s older l.
                                            // redo if file has changed
        if (ofile older all && library older all)
            srcList += (list)file;
        else if (source older ofile || source older library)
            srcList -= (list)file;
    }
    return srcList;
}

void c_compile(int prefix, string destDir, string srcDir, list cfiles)
{
    int idx;
    string compiler;
    string file;

    showCd(srcDir);

    if (srcDir != "")
        srcDir += "/";

    compiler = g_compiler + " -c -o " + destDir + "/" + (string)prefix;

    for (idx = sizeof(cfiles); idx--; )
    {
        file = cfiles[idx];
        system(compiler + change_ext(file, ".o") + " " + srcDir + file);
        g_compiled = 1;
    }
}

void std_cpp(int ignoreMain, string destDir, 
            int prefix, string srcDir, string library)
{
    list files;

    chdir("");
                                                    // make list of all files
    md(destDir);
    chdir(srcDir);

    files = makelist(SOURCES);
    if (ignoreMain)
        files -= (list)MAIN;
    chdir("");

    files = inspect(destDir, prefix, srcDir, files, library);  

    if (sizeof(files))
        c_compile(prefix, destDir, srcDir, files);  // compile files
}

void static_library()
{
    chdir(TMP_DIR "/o");

    if (sizeof(makelist("*" ".o")))
    {
        showCd(TMP_DIR "/o");

        system("ar cru ../libflexc++.a *" ".o");
        system("ranlib ../libflexc++.a");
        system("rm *" ".o");
    }
    chdir("");
}

void rm_a_files()
{
    int idx;

    echo(OFF);

    for (idx = sizeof(g_inspected); idx--; )
        system("rm -f " + g_inspected[idx] + "/a");
    if (sizeof(g_inspected))
        system("rm -f a");

    echo(ON);
}

void build_library()
{
    int idx;
    string class;
    string staticLib;

    setClasses();
    special();

    g_compiler = COMPILER + setOpt(CXXFLAGS, "CXXFLAGS");

    md(TMP_DIR);

    staticLib = g_cwd + TMP_DIR "/lib" LIBRARY ".a";

    g_compiled = 0;

    if (exists("version.cc") && "VERSION" younger "version.cc")
        system("touch version.cc");

                                            // compile all files in subdirs
    for (idx = g_nClasses; idx--; )
        std_cpp(0, TMP_DIR "/o", idx + 1, g_classes[idx], staticLib);
        
                                            // compile all files in g_cwd
    std_cpp(1, TMP_DIR "/o", 0, ".", staticLib);  

    static_library();                       // make the library

    rm_a_files();

    chdir("");
}



