
/*
 * Update files, given dependency list.
 * Based on a programme originally written
 * by Tom Duff (NYIT).
 * More closely based on a version of the
 * above tarted up by Charles Forsyth.
 */
#include <stdio.h>

#define  NFB	32
#define	 NANTI	20

typedef	long	time_t;

struct	dep
{
	struct	dep *d_fp;		/* Forward pointer */
	struct	file *d_cons;		/* Consequent */
	char	*d_work;		/* Command lines */
	int	d_nanti;		/* Number of anticedents */
	struct	file *d_anti[];		/* Antidecents */
};

struct	file
{
	struct	file *f_fp;		/* Forward pointer */
	char	*f_name;		/* Name */
	char	f_level;		/* Level */
	char	f_mark;			/* Loop detect flag */
	time_t	f_time;			/* Modification time */
};

struct	file 	*file;
struct	dep	*dep;

int	vflag;
int	line;
int	maxlevel;
char	*mfn;
FILE	*mfp;

main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;

	mfn = "makefile";
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			++p;
			while (c = *p++)
				switch (c) {

				case 'V':
				case 'v':
					++vflag;
					break;

				default:
					usage();
				}
		} else
			mfn = p;
	}
	if ((mfp = fopen(mfn, "r")) == NULL) {
		fprintf(stderr, "%s: cannot open.\n", mfn);
		exit(1);
	}
	readin();
	dowork();
}

readin()
{
	register struct file *fp;
	register struct dep  *dp;
	register na;
	struct file *anti[NANTI], *cfp;

	while ((cfp = getfile(1)) != NULL) {
		na = 0;
		while ((fp = getfile(0)) != NULL) {
			if (na >= NANTI)
				err("Too many anticedents");
			anti[na++] = fp;
		}
		if (na == 0)
			err("No anticedents");
		dp = newspace(sizeof(*dp) + na*sizeof(dp->d_anti[0]));
		dp->d_cons  = cfp;
		dp->d_nanti = na;
		while (na--)
			dp->d_anti[na] = anti[na];
		dp->d_work = getwork();
	}
}

struct file *
getfile(f)
{
	register struct file *fp;
	register char *cp;
	register c;
	char fb[NFB];

	while ((c = getc(ifp)) == ' ' || c == '\t') {
		if (c == '\n') {
			++line;
			if (f == 0)
				break;
		}
	}
	if (c == EOF || c == '\n')
		return (NULL);
	cp = fb;
	do {
		if (cp < &fb[NFB-1]) {
			if (c>='A' && c<='Z')
				c += 'a'-'A';
			*cp++ = c;
		}
	} while ((c = getc(ifp)) != ' ' && c!='\t' && c!='\n' && c!=EOF);
	*cp = 0;
	ungetc(c, mfp);

	fp = file;
	while (fp != NULL) {
		if (strcmp(fb, fp->f_name) == 0)
			return (fp);
		fp = fp->f_fp;
	}
	fp = newspace(sizeof(*fp));
	fp->f_fp = file;
	file = fp;
	fp->f_name = strsave(fb);
	fp->f_mark = 0;
	fp->f_level = -1;
	settime(fp);
	return (fp);
}

char *
getwork()
{
	cp = work;
	while ((c = getc(mfp)) == '\t') {
		while ((c = getc(ifp)) != EOF && c != '\n') {
			if (cp >= &work[NWORK])
				err("Too many commands");
			*cp++ = c;
		}
		*cp++ = '\n';
	}
	ungetc(c, mfp);
	if (cp == work)
		return (NULL);
	*cp = 0;
	return (strsave(work));
}

dowork()
{
	register struct file *fp;
	register struct dep  *dp;
	register i;

	setlevel();
	for (i=1; i<=maxlevel; ++i) {
		dp = dep;
		while (dp != NULL) {
			if (dp->d_cons->f_level==i && youngest(dp->d_group)
				>= dp->d_cons->f_time)
				domcr(dp);
			dp = dp->d_fp;
		}
		dp = dep;
		while (dp != NULL) {
			if (dp->d_cons->f_level == n) {
				t = youngest(dp);
				if (t < dp->d_cons->f_time)
					continue;
				if (dp->d_work != NULL)
					setime(dp->d_cons); else
					dp->d_cons->f_time = t;
			}
			dp = dp->d_fp;
		}
	}
}

domcr(dp)
register struct dep *dp;
{
	if (dp->d_work == NULL)
		return;
	if (vflag)
		printf("%s", dp->d_work);
	/* ... */
}

time_t
youngest(dp)
register struct dep *dp;
{
	register struct file *fp;
	register na;
	time_t t;

	t = 0;
	na = dp->d_nanti;
	while (na--) {
		fp = dp->d_anti[na];
		if (fp->f_time > t)
			t = fp->f_time;
	}
	return (t);
}

setlevel()
{
	register struct file *fp;

	fp = file;
	while (fp != NULL) {
		follow(fp, 0);
		fp = fp->f_fp;
	}
}

follow(fp, level)
register struct file *fp;
{
	register struct dep *dp;
	register struct file **ap;
	int na;

	if (fp->f_mark) {
		fprintf(stderr, "Circularity on %s!\n", fp->f_name);
		exit(1);
	}
	if (fp->f_level < level) {
		if (level > maxlevel)
			maxlevel = level;
		fp->f_level = level;
		fp->f_mark = 1;
		dp = dep;
		while (dp != NULL) {
			na = dp->d_nanti;
			ap = dp->d_anti;
			while (na--) 
				if (*ap++ == fp) {
					follow(dp->d_cons, level+1);
					break;
				}
			dp = dp->d_fp;
		}
		fp->f_mark = 0;
	}
}

err(s)
char *s;
{
	if (line != 0)
		fprintf(stderr, "%d: ", line);
	fprintf(stderr, "%s\n", s);
	exit(1);
}

char *
newspace(n)
{
	register char *p;

	p = alloc(n);
	if (p == (char *)-1)
		err("Out of space");
	return (p);
}

char *
strsave(s)
char *s;
{
	register char *p;

	p = newspace(strlen(s) + sizeof(char));
	strcpy(p, s);
	return (p);
}

usage()
{
	fprintf(stderr, "Usage: make [-v] [file]\n");
	exit(1);
}
