/*
 * mkdep - generate make dependency
 */

static char rcsID[] = "$Header: RCS/mkdep.c 1.3 90/07/04 18:05:03 kmori Exp $";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


typedef	char	bool;
#define	YES	1
#define	NO	0

#define	MAXLINE	1000
#define	MAXFN	100


static char *Progname = "mkdep";



void usage()
{
	fprintf(stderr,
		"Usage: %s [-c] [-s] [-f makefile] [-o .obj] sourcefile...\n",
		Progname);
}



char *ealloc(unsigned size, int count)
{
	char *p;

	if ((p = calloc(size, count)) == NULL) {
		fprintf(stderr, "%s: Out of memory\n", Progname);
		exit(1);
	}
	return (p);
}



char *remdir(char *s)
{
	char *p, c;

	p = s;
	while (c = *s++) {
		if (c == ':' || c == '/' || c == '\\')
			p = s;
	}
	return (p);
}



char *suffix(char *s)
{
	char c, *p;

	p = NULL;
	while (c = *s) {
		if (c == '.')
			p = s;
		else if (c == '/' || c == '\\' || c == ':')
			p = NULL;
		s++;
	}
	return (p ? p : s);
}



char *addsl(char *buf)
{
	if (buf[0]) {
		switch(buf[strlen(buf) - 1]) {
		case '/': case '\\': case ':':
			break;
		default:
			strcat(buf, "/");
			break;
		}
	}
	return (buf);
}



char *skipsp(char *p)
{
	while (isspace(*p))
		p++;
	return (p);
}


bool strmatch(char *text, char *pat)
{
	return (strncmp(text, pat, strlen(pat)) == 0);
}



typedef	struct source {
	struct source	*next;
	char		name[0];
} Source;

#define	MAXPATH	100
char *Ipaths[MAXPATH];
char *Makefile = "Makefile";
char *Objext = ".obj";
bool Cflag = NO;
bool Sflag = NO;




void findfile(char *buf, char *file, char *org)
{
	char **p;

	if (*file != '/' && *file != '\\' && (!file[0] || file[1] != ':')) {
		strcpy(remdir(strcpy(buf, org)), file);
		if (access(buf, 0) == 0)
			return;
		for (p = Ipaths; *p; p++) {
			strcat(addsl(strcpy(buf, *p)), file);
			if (access(buf, 0) == 0)
				return;
		}
	}
	strcpy(buf, file);
}



Source *lookup(char *fn, Source *p)
{
	for (; p != NULL; p = p->next) {
		if (strcmp(p->name, fn) == 0)
			break;
	}
	return (p);
}



Source *searchInclude(char *file, Source *list, char *target, FILE *ofp)
{
	static char line[MAXLINE];
	char buf[MAXFN];
	FILE *fp;
	Source *src;
	char *p, *s;

	if ((fp = fopen(file, "r")) == NULL) {
		fprintf(stderr, "%s: ", Progname);
		perror(file);
		return (list);
	}
	while (fgets(line, MAXLINE, fp)) {
		p = skipsp(line);
		if (*p++ != '#')
			continue;
		p = skipsp(p);
		if (strncmp(p, "include", 7) != 0)
			continue;
		p = skipsp(p + 7);
		if (*p == '"')
			s = strchr(++p, '"');
		else if (Sflag && *p == '<')
			s = strchr(++p, '>');
		else
			continue;
		if (s == NULL)
			continue;
		*s = '\0';
		findfile(buf, p, file);
		if (lookup(buf, list))
			continue;
		if (list == NULL)
			fprintf(ofp, "%s:", target);
		fprintf(ofp, " %s", buf);
		src = (Source *)ealloc(sizeof(Source) + strlen(buf) + 1, 1);
		src->next = list;
		strcpy(src->name, buf);
		list = searchInclude(src->name, src, target, ofp);
	}
	fclose(fp);
	return (list);
}



int main(int argc, char *argv[])
{
	char mkdepwork[MAXFN];
	char buf[MAXFN];
	static char line[MAXLINE];
	FILE *ifp, *ofp;
	int np;

	np = 0;
	if (*argv)
		++argv;
	for (; *argv && **argv == '-'; argv++) {
		if (strcmp(*argv, "-f") == 0) {
			if (*++argv == NULL) {
				usage();
				exit(1);
			}
			Makefile = *argv;
		} else if (strcmp(*argv, "-o") == 0) {
			if (*++argv == NULL) {
				usage();
				exit(1);
			}
			Objext = *argv;
		} else if (strcmp(*argv, "-c") == 0) {
			Cflag = YES;
		} else if (strcmp(*argv, "-s") == 0) {
			Sflag = YES;
		} else if (argv[0][1] == 'I') {
			if (np >= MAXPATH) {
				fprintf(stderr, "%s: Too may -I\n", Progname);
				exit(1);
			}
			Ipaths[np++] = argv[0] + 2;
		} else {
			usage();
			exit(1);
		}
	}
	Ipaths[np] = NULL;
	if (*argv == NULL) {
		usage();
		exit(1);
	}
	if ((ifp = fopen(Makefile, "r")) == NULL) {
		perror(Makefile);
		exit(1);
	}
	strcpy(suffix(strcpy(mkdepwork, Makefile)), ".$$$");
	if ((ofp = fopen(mkdepwork, "w")) == NULL) {
		perror(mkdepwork);
		exit(1);
	}
	while (fgets(line, sizeof(line), ifp)
			&& !strmatch(line, "# DO NOT DELETE THIS LINE"))
		fputs(line, ofp);
	fclose(ifp);
	fputs("# DO NOT DELETE THIS LINE -- mkdep uses it.\n", ofp);
	fputs("# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.\n\n", ofp);
	for (; *argv; argv++) {
	/* Process a file */
		if (Cflag)
			strcpy(buf, remdir(*argv));
		else
			strcpy(buf, *argv);
		strcpy(suffix(buf), Objext);
		strlwr(buf);
		if (searchInclude(*argv, NULL, buf, ofp))
			putc('\n', ofp);
	}
	fputs("\n# IF YOU PUT ANYTHING HERE IT WILL GO AWAY\n", ofp);
	fclose(ofp);
	strcpy(suffix(strcpy(buf, Makefile)), ".bak");
	remove(buf);
	rename(Makefile, buf);
	rename(mkdepwork, Makefile);
	exit(0);
}
