#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 ((FILENODE *) 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 = (DATE) NULL;
    newnode -> nfile -> fnode = (NODE *) NULL;
    newnode -> nfile -> parent = (FILENODE *) NULL;
    newnode -> nfile -> fflag = 0;
    newnode -> nfile -> fnext = NULL;
    DBUG_RETURN (newnode -> nfile);
}

static  NODE *parentlist = (NODE *) 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 = (FILENODE *) 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 == (FILENODE *) NULL) {
	if ((librarylist = (FILENODE *) Calloc (1, sizeof (FILENODE))) == (FILENODE *) NULL) {
	    allerr ();
	}
	librarylist -> fnode = (NODE *) NULL;
    }
    if ((n = (NODE *) Calloc (1, sizeof (NODE))) == (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 == (FILENODE *) NULL) {
	DBUG_RETURN (FAILURE);
    }
    for (lib = librarylist->fnode; lib != (NODE *)NULL; lib = lib->nnext) {
	for (dep = lib->nfile->fnode; dep != (NODE *)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 = (NODE *) 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 != (NODE *)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 != (FILENODE *) 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))) == (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 = (DATE) NULL;
    f -> fnode = (NODE *) NULL;
    f -> parent = (FILENODE *) 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 != (FILENODE *) 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 != (FILENODE *) 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 != (NODE *) NULL; n = n -> nnext) {
	    fputc ('\t', stdout);
	    puts ((n -> nfile) -> fname);
	}
	fputc ('\n', stdout);
    }
    DBUG_VOID_RETURN;
}
