#include <stdio.h>
#include "make.h"

static FILENODE *addtolist ();
static FILENODE *afnode ();

/*
 * Return file-node for 'fname'.
 * If it doesn't exist, then create one.
 */

FILENODE *filenode (fname)
char *fname;
{
    register FILENODE *f;

    DBUG_ENTER ("filenode");
    DBUG_3 ("fnode", "locate or create node for file '%s'", fname);
    if ((f = gfile (fname)) == (FILENODE *) NULL) {
       f = afnode (fname);
    }
    DBUG_RETURN (f);
}

/*
 * Add a dependency to the node 'fnd'.
 * 'fnd' will depend on 'fname'.
 *
 * Returns dependent node associated with 'fname'.
 */

FILENODE *addfile (fnd, fname)
FILENODE *fnd;
char *fname;
{
    register NODE *n;
    register FILENODE *f;

    DBUG_ENTER ("addfile");
    if (fnd == (FILENODE *) NULL) {    /* punt if no root file */
       fputs ("No current root, can't add dependency '", stderr);
       fputs (fname, stderr);
       fputs ("%s'\n", stderr);
       DBUG_RETURN (NULL);
    }
    DBUG_4 ("fdep", "add dependency of '%s' on '%s'", fnd -> fname, fname);
    f = filenode (fname);
    if ((n = (NODE *) Calloc (1, sizeof (NODE))) == (NODE *) NULL) {
       allerr ();
    }
    n -> nnext = fnd -> fnode;
    fnd -> fnode = n;
    n -> nfile = f;
    DBUG_RETURN (f);
}


/*
 * Add a line of method-text to the node 'fnode'.
 */

void addmeth (fnode, methtext)
FILENODE *fnode;
char *methtext;
{
    register int len;
    register char *new;
    extern void free ();

    DBUG_ENTER ("addmeth");
    if (fnode != (FILENODE *) NULL && methtext != NULL) {
       len = strlen (methtext) + 2;
       if (fnode -> fmake == NULL) {
           if ((fnode -> fmake = (char *) Calloc (1, 1)) == NULL) {
               allerr ();
           }
           *(fnode -> fmake) = '\0';
       }
       len += strlen (fnode -> fmake);
       /* Lattice C does not have 'realloc()', so this kludges around it: */
       if ((new = (char *) Calloc (1, len)) == NULL) {
           allerr ();
       }
       strcpy (new, fnode -> fmake);
       free (fnode -> fmake);
       fnode -> fmake = new;
       strcat (fnode -> fmake, methtext);
       len = strlen (fnode -> fmake);
       if (len > 0 && fnode -> fmake[len - 1] != '\n') {
           strcat (fnode -> fmake, "\n");
       }
    }
    DBUG_VOID_RETURN;
}

/*
 * Add token to the parent list.  Return the pointer to the new parent.
 * If token is already on the parent list, simply return the pointer found.
 */

static FILENODE *addtolist (tok, list)
char *tok;
NODE **list;
{
    register NODE *search;
    register NODE *newnode;

    DBUG_ENTER ("addtolist");
    for (search = *list; search != (NODE *) NULL; search = search -> nnext) {
       if (STRSAME (search -> nfile -> fname, tok)) {
           DBUG_RETURN (search -> nfile);
       }
    }
    /* token not found so far... add it to list */
    if ((newnode = (NODE *) Calloc (1, sizeof (NODE))) == (NODE *) NULL) {
       allerr ();
    }
    search = *list;
    *list = newnode;
    newnode -> nnext = search;
    if ((newnode -> nfile = (FILENODE *) Calloc (1, sizeof (FILENODE))) == (FILENODE *) NULL) {
       allerr ();
    }
    if ((newnode -> nfile -> fname = (char *) Calloc (1, strlen (tok) + 1)) == NULL) {
       allerr ();
    }
    strcpy (newnode -> nfile -> fname, tok);
    newnode -> nfile -> fdate = NULL;
    newnode -> nfile -> fnode = NULL;
    newnode -> nfile -> parent = NULL;
    newnode -> nfile -> fflag = 0;
    newnode -> nfile -> fnext = NULL;
    DBUG_RETURN (newnode -> nfile);
}

static  NODE *parentlist = NULL;

FILENODE *addparent (tok)
char *tok;
{
    FILENODE *np;
    
    DBUG_ENTER ("addparent");
    np = addtolist (tok, &parentlist);
    DBUG_RETURN (np);
}

#ifdef FUNNYLIBS

isonlibrary (f)                        /* return SUCCEED if f is a library. */
FILENODE *f;                   /* set f->fdate to parent's date */
{
    DBUG_ENTER ("isonlibrary");
    if (f -> fflag & LIBRARY) {
       getdate (f -> parent);
       f -> fdate = f -> parent -> fdate;
       DBUG_RETURN (SUCCEED);
    }
    DBUG_RETURN (FAILURE);
}

#else

/*
 * Add file node fnd to library list.
 */

static FILENODE *librarylist = NULL;

void AddToLibrary (fnd)
FILENODE *fnd;
{
    register NODE *n;

    DBUG_ENTER ("AddToLibrary");
    DBUG_3 ("lib", "add node for '%s' to library list", fnd -> fname);
    if (librarylist == NULL) {
       if ((librarylist = (FILENODE *) Calloc (1, sizeof (FILENODE))) == (FILENODE *) NULL) {
           allerr ();
       }
       librarylist -> fnode = NULL;
    }
    if ((n = (NODE *) Calloc (1, sizeof (NODE))) == NULL) {
       allerr ();
    }
    n -> nnext = librarylist -> fnode;
    librarylist -> fnode = n;
    n -> nfile = fnd;
    DBUG_VOID_RETURN;
}

/*
 * Return SUCCEED if filenode f is a direct descendant of a library;
 * set f->fdate to parent's time.
 */

isonlibrary (f)
FILENODE *f;
{
    register NODE *lib;
    register NODE *dep;

    DBUG_ENTER ("isonlibrary");
    DBUG_3 ("isonlib", "Searching for: %s", f -> fname);
    if (librarylist == NULL) {
       DBUG_RETURN (FAILURE);
    }
    for (lib = librarylist->fnode; lib != NULL; lib = lib->nnext) {
       for (dep = lib->nfile->fnode; dep != NULL; dep = dep->nnext) {
           DBUG_3 ("dep", "Examining: %s", dep -> nfile -> fname);
           if (f == dep -> nfile) {            /* found it!! */
               DBUG_3 ("dep", "found %s", dep -> nfile -> fname);
               DBUG_3 ("dep", "depends on %s", lib -> nfile -> fname);
               f -> fdate = lib -> nfile -> fdate;     /* update time */
               DBUG_RETURN (SUCCEED);
           }
       }
    }
    DBUG_RETURN (FAILURE);
}
#endif

isanarchive (f)                        /* return SUCCEED if f is an archive */
FILENODE *f;                   /* set f->fdate to date in parent's */
{                              /* archive directory */
    DATE getarchdate ();

    DBUG_ENTER ("isanarchive");
    if (f -> fflag & ARCHIVE) {
       f -> fdate = getarchdate (f -> parent -> fname, f -> fname);
       DBUG_RETURN (SUCCEED);
    }
    DBUG_RETURN (FAILURE);
}

NODE *deletelist = NULL;

#ifdef LAR
extract (f)
FILENODE *f;
{
    DBUG_ENTER ("extract");
    DBUG_3 ("extr", "extracting %s for archivehood", f -> fname);
    if (f -> fflag & ARCHIVE) {
       DBUG_3 ("extr", "copying %s for archivehood", f -> fname);
#ifndef NOREALEXTRACT
       copyfile (f -> parent -> fname, f -> fname);
#endif
       /* delete f->fname at end of run */
       (void) addtolist (f -> fname, &deletelist);
       DBUG_RETURN (SUCCEED);
    }
    DBUG_RETURN (FAILURE);
}
#endif

void cleanuparchives ()
{
    register NODE *search;

    DBUG_ENTER ("cleanuparchives");
    for (search=deletelist; search != NULL; search = search->nnext) {
       fputs ("Purging ", stdout);
       puts (search -> nfile -> fname);
#ifndef NOREALDELETE
       unlink (search -> nfile -> fname);
#endif
    }
    DBUG_VOID_RETURN;
}


/*
 * Get a filenode for the file called 'fn'.
 * Returns (FILENODE *) NULL if the node doesn't exist.
 */

FILENODE *gfile (fn)
char *fn;
{
    register FILENODE *f;

    DBUG_ENTER ("gfile");
    DBUG_3 ("fnode", "look for file node '%s'", fn);
    for (f = froot; f != NULL; f = f -> fnext) {
       if (STRSAME (fn, f -> fname)) {
           DBUG_2 ("fnode", "found it");
           break;
       }
    }
    DBUG_RETURN (f);
}


/*
 * Alloc space for a new file node.
 *
 * Note that when this routine is called, it has already been
 * determined that there is no existing node with this name, so
 * we don't need to check again.
 *
 * Also note that the method string pointer is set to a dynamically
 * allocated null string, rather than a static null string (""), because
 * it is latter freed when expanding the method string.
 *
 */

static FILENODE *afnode (name)
char *name;
{
    FILENODE *f;

    DBUG_ENTER ("afnode");
    DBUG_3 ("fnode", "allocate a new node for file '%s'", name);
    if ((f = (FILENODE *) Calloc (1, sizeof (FILENODE))) == NULL) {
       allerr ();
    }
    if ((f -> fname = (char *) Calloc (1, strlen (name) + 1)) == NULL) {
       allerr ();
    }
    strcpy (f -> fname, name);
    if ((f -> fmake = (char *) Calloc (1, 1)) == NULL) {
       allerr ();
    }
    *(f -> fmake) = '\0';
    f -> fdate = NULL;
    f -> fnode = NULL;
    f -> parent = NULL;
    f -> fflag = 0;
    f -> fnext = froot;
    froot = f;
    DBUG_RETURN (f);
}

/*
 * Print dependency tree.
 */

void prtree ()
{
    register FILENODE *f;
    register NODE *n;
    extern char *printdate ();

    DBUG_ENTER ("prtree");
    for (f = froot; f != NULL; f = f -> fnext) {
       fputs (f -> fname, stdout);
       fputs ((f -> fflag & ROOTP) ? " (root)" : "", stdout);
       fputs ((f -> fflag & REBUILT) ? " (rebuilt)" : "", stdout);
       fputs ((f -> fflag & LIBRARY) ? " (library)" : "", stdout);
       fputs ((f -> fflag & EXTRACT) ? " (extracted)" : "", stdout);
       fputs ((f -> fflag & ARCHIVE) ? " (archive)" : "", stdout);
       fputs (printdate (f -> fdate), stdout);
       fputc ('\n', stdout);
       if (f -> parent != NULL) {
           fputs ("Parent is: ", stdout);
           fputs (f -> parent -> fname, stdout);
           fputc ('\n', stdout);
           fputs ("Parental Date:", stdout);
           fputs (printdate (f -> parent -> fdate), stdout);
           fputc ('\n', stdout);
       }
       if (f -> fmake != NULL) {
           puts (f -> fmake);
       }
       puts ("Dependents: ");
       for (n = f -> fnode; n != NULL; n = n -> nnext) {
           fputc ('\t', stdout);
           puts ((n -> nfile) -> fname);
       }
       fputc ('\n', stdout);
    }
    DBUG_VOID_RETURN;
}
