[net.sources] Public domain make source

caret@tictoc.UUCP (07/11/86)

Enough coaxing...  Here it is.  It will need work to work on most
other systems, but the work should be minor.  I went though the
source just now, and here is a list of things which I noticed that
will need changing.

make.c:
	* Fix dosh() for your system.
	* Call to getstat() in modtime() is like a fstat() on unix,
	  so it needs fixing.
main.c:
	* You can have fun in main().  That call to initalloc() is
	  really special to EON.
rules.c:
	* Fix makerules() to suit you compilers for the auto suffix
	  stuff.

It supports most features of the UNIX make, the notable exceptions
being libraries, and some subtleties with quoting.  I will support
problems with it to a degree.  Good luck.

P.S.  I never actually got around to writing a manual entry for
      it, so the best course of action is to refer to the UNIX
      manual entry, or the source.


Neil Russell
ACSnet:  caret@tictoc.oz
UUCP:  ...!seismo!munnari!tictoc.oz!caret
ARPA:  caret%tictoc.oz@seismo.arpa


------------------------------ snip ------------------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by caret on Fri Jul 11 20:15:58 EST 1986
# Contents:  Makefile h.h check.c input.c macro.c main.c make.c reader.c
#	rules.c
 
echo x - Makefile
sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//'
# Makefile for make!
@.SUFFIXES:	.obj

@.c.obj:
	c -c $(CFLAGS) $<


OBJS	=	check.obj input.obj macro.obj main.obj \
		make.obj reader.obj rules.obj

m:		$(OBJS)
	c -a 4000h -o m $(OBJS)

$(OBJS):	h.h
@//E*O*F Makefile//
chmod u=rw,g=r,o=r Makefile
 
echo x - h.h
sed 's/^@//' > "h.h" <<'@//E*O*F h.h//'
/*
 *	Include header for make
 */


#ifndef uchar
#define uchar		unsigned char
#endif

#define bool		uchar
#define time_t		long
#define TRUE		(1)
#define FALSE		(0)
#define max(a,b)	((a)>(b)?(a):(b))

#define DEFN1		"makefile"		/*  Default names  */
#define DEFN2		"Makefile"

#define LZ		(1024)			/*  Line size  */



/*
 *	A name.  This represents a file, either to be made, or existant
 */

struct name
{
	struct name *		n_next;		/* Next in the list of names */
	char *			n_name;		/* Called */
	struct line *		n_line;		/* Dependencies */
	time_t			n_time;		/* Modify time of this name */
	uchar			n_flag;		/* Info about the name */
};

#define N_MARK		0x01			/* For cycle check */
#define N_DONE		0x02			/* Name looked at */
#define N_TARG		0x04			/* Name is a target */
#define N_PREC		0x08			/* Target is precious */

/*
 *	Definition of a target line.
 */
struct	line
{
	struct line *		l_next;		/* Next line (for ::) */
	struct depend *		l_dep;		/* Dependents for this line */
	struct cmd *		l_cmd;		/* Commands for this line */
};


/*
 *	List of dependents for a line
 */
struct	depend
{
	struct depend *		d_next;		/* Next dependent */
	struct name *		d_name;		/* Name of dependent */
};


/*
 *	Commands for a line
 */
struct	cmd
{
	struct cmd *		c_next;		/* Next command line */
	char *			c_cmd;		/* Command line */
};


/*
 *	Macro storage
 */
struct	macro
{
	struct macro *		m_next;		/* Next variable */
	char *			m_name;		/* Called ... */
	char *			m_val;		/* Its value */
	uchar			m_flag;		/* Infinite loop check */
};

extern char *		myname;
extern struct name	namehead;
extern struct macro *	macrohead;
extern struct name *	firstname;
extern bool		silent;
extern bool		ignore;
extern bool		rules;
extern bool		dotouch;
extern bool		quest;
extern bool		domake;
extern char		str1[];
extern char		str2[];
extern int		lineno;

char *			fgets();
char *			index();
char *			rindex();
char *			malloc();
extern int		errno;

char *			getmacro();
struct macro *		setmacro();
void			input();
void			error();
void			fatal();
int			make();
struct name *		newname();
struct depend *		newdep();
struct cmd *		newcmd();
void			newline();
char *			suffix();
void			touch();
void			makerules();
char *			gettok();
void			precious();
@//E*O*F h.h//
chmod u=rw,g=r,o=r h.h
 
echo x - check.c
sed 's/^@//' > "check.c" <<'@//E*O*F check.c//'
/*
 *	Check structures for make.
 */

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


/*
 *	Prints out the structures as defined in memory.  Good for check
 *	that you make file does what you want (and for debugging make).
 */
void
prt()
{
	register struct name *		np;
	register struct depend *	dp;
	register struct line *		lp;
	register struct cmd *		cp;
	register struct macro *		mp;


	for (mp = macrohead; mp; mp = mp->m_next)
		fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val);

	fputc('\n', stderr);

	for (np = namehead.n_next; np; np = np->n_next)
	{
		fprintf(stderr, "%s:\n", np->n_name);
		for (lp = np->n_line; lp; lp = lp->l_next)
		{
			fputc(':', stderr);
			for (dp = lp->l_dep; dp; dp = dp->d_next)
				fprintf(stderr, " %s", dp->d_name->n_name);
			fputc('\n', stderr);

			for (cp = lp->l_cmd; cp; cp = cp->c_next)
				fprintf(stderr, "-\t%s\n", cp->c_cmd);
			fputc('\n', stderr);
		}
		fputc('\n', stderr);
	}
}


/*
 *	Recursive routine that does the actual checking.
 */
void
check(np)
struct name *		np;
{
	register struct depend *	dp;
	register struct line *		lp;


	if (np->n_flag & N_MARK)
		fatal("Circular dependency from %s", np->n_name);

	np->n_flag |= N_MARK;

	for (lp = np->n_line; lp; lp = lp->l_next)
		for (dp = lp->l_dep; dp; dp = dp->d_next)
			check(dp->d_name);

	np->n_flag &= ~N_MARK;
}


/*
 *	Look for circular dependancies.
 *	ie.
 *		a: b
 *		b: a
 *	is a circular dep
 */
void
circh()
{
	register struct name *	np;


	for (np = namehead.n_next; np; np = np->n_next)
		check(np);
}


/*
 *	Check the target .PRECIOUS, and mark its dependentd as precious
 */
void
precious()
{
	register struct depend *	dp;
	register struct line *		lp;
	register struct name *		np;


	if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG))
		return;

	for (lp = np->n_line; lp; lp = lp->l_next)
		for (dp = lp->l_dep; dp; dp = dp->d_next)
			dp->d_name->n_flag |= N_PREC;
}
@//E*O*F check.c//
chmod u=rw,g=r,o=r check.c
 
echo x - input.c
sed 's/^@//' > "input.c" <<'@//E*O*F input.c//'
/*
 *	Parse a makefile
 */


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


struct name		namehead;
struct name *		firstname;

char 			str1[LZ];		/*  General store  */
char			str2[LZ];


/*
 *	Intern a name.  Return a pointer to the name struct
 */
struct name *
newname(name)
char *			name;
{
	register struct name *	rp;
	register struct name *	rrp;
	register char *		cp;


	for
	(
		rp = namehead.n_next, rrp = &namehead;
		rp;
		rp = rp->n_next, rrp = rrp->n_next
	)
		if (strcmp(name, rp->n_name) == 0)
			return rp;

	if ((rp = (struct name *)malloc(sizeof (struct name)))
				== (struct name *)0)
		fatal("No memory for name");
	rrp->n_next = rp;
	rp->n_next = (struct name *)0;
	if ((cp = malloc(strlen(name)+1)) == (char *)0)
		fatal("No memory for name");
	strcpy(cp, name);
	rp->n_name = cp;
	rp->n_line = (struct line *)0;
	rp->n_time = (time_t)0;
	rp->n_flag = 0;

	return rp;
}


/*
 *	Add a dependant to the end of the supplied list of dependants.
 *	Return the new head pointer for that list.
 */
struct depend *
newdep(np, dp)
struct name *		np;
struct depend *		dp;
{
	register struct depend *	rp;
	register struct depend *	rrp;


	if ((rp = (struct depend *)malloc(sizeof (struct depend)))
				== (struct depend *)0)
		fatal("No memory for dependant");
	rp->d_next = (struct depend *)0;
	rp->d_name = np;

	if (dp == (struct depend *)0)
		return rp;

	for (rrp = dp; rrp->d_next; rrp = rrp->d_next)
		;

	rrp->d_next = rp;

	return dp;
}


/*
 *	Add a command to the end of the supplied list of commands.
 *	Return the new head pointer for that list.
 */
struct cmd *
newcmd(str, cp)
char *			str;
struct cmd *		cp;
{
	register struct cmd *	rp;
	register struct cmd *	rrp;
	register char *		rcp;


	if (rcp = rindex(str, '\n'))
		*rcp = '\0';		/*  Loose newline  */

	while (isspace(*str))
		str++;

	if (*str == '\0')		/*  If nothing left, the exit  */
		return;

	if ((rp = (struct cmd *)malloc(sizeof (struct cmd)))
				== (struct cmd *)0)
		fatal("No memory for command");
	rp->c_next = (struct cmd *)0;
	if ((rcp = malloc(strlen(str)+1)) == (char *)0)
		fatal("No memory for command");
	strcpy(rcp, str);
	rp->c_cmd = rcp;

	if (cp == (struct cmd *)0)
		return rp;

	for (rrp = cp; rrp->c_next; rrp = rrp->c_next)
		;

	rrp->c_next = rp;

	return cp;
}


/*
 *	Add a new 'line' of stuff to a target.  This check to see
 *	if commands already exist for the target.
 */
void
newline(np, dp, cp)
struct name *		np;
struct depend *		dp;
struct cmd *		cp;
{
	bool			hascmds = FALSE;  /*  Target has commands  */
	register struct line *	rp;
	register struct line *	rrp;


	for
	(
		rp = np->n_line, rrp = (struct line *)0;
		rp;
		rrp = rp, rp = rp->l_next
	)
		if (rp->l_cmd)
			hascmds = TRUE;

	if (hascmds && cp)
		error("Commands defined twice for target %s", np->n_name);

	if ((rp = (struct line *)malloc(sizeof (struct line)))
				== (struct line *)0)
		fatal("No memory for line");
	rp->l_next = (struct line *)0;
	rp->l_dep = dp;
	rp->l_cmd = cp;

	if (rrp)
		rrp->l_next = rp;
	else
		np->n_line = rp;

	np->n_flag |= N_TARG;
}


/*
 *	Parse input from the makefile, and construct a tree structure
 *	of it.
 */
void
input(fd)
FILE *			fd;
{
	char *			p;		/*  General  */
	char *			q;
	struct name *		np;
	struct depend *		dp;
	struct cmd *		cp;


	if (getline(str1, fd))	/*  Read the first line  */
		return;

	for(;;)
	{
		if (*str1 == '\t')	/*  Rules without targets  */
			error("Rules not allowed here");

		p = str1;

		while (isspace(*p))	/*  Find first target  */
			p++;

		while (((q = index(p, '=')) != (char *)0) &&
		    (p != q) && (q[-1] == '\\'))	/*  Find value */
		{
			register char *		a;

			a = q - 1;	/*  Del \ chr; move rest back  */
			p = q;
			while(*a++ = *q++)
				;
		}

		if (q != (char *)0)
		{
			register char *		a;

			*q++ = '\0';		/*  Separate name and val  */
			while (isspace(*q))
				q++;
			if (p = rindex(q, '\n'))
				*p = '\0';

			p = str1;
			if ((a = gettok(&p)) == (char *)0)
				error("No macro name");

			setmacro(a, q);

			if (getline(str1, fd))
				return;
			continue;
		}

		expand(str1);
		p = str1;

		while (((q = index(p, ':')) != (char *)0) &&
		    (p != q) && (q[-1] == '\\'))	/*  Find dependents  */
		{
			register char *		a;

			a = q - 1;	/*  Del \ chr; move rest back  */
			p = q;
			while(*a++ = *q++)
				;
		}

		if (q == (char *)0)
			error("No targets provided");

		*q++ = '\0';	/*  Separate targets and dependents  */

		for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);)
					/*  get list of dep's */
		{
			np = newname(p);		/*  Intern name  */
			dp = newdep(np, dp);		/*  Add to dep list */
		}

		*((q = str1) + strlen(str1) + 1) = '\0';
			/*  Need two nulls for gettok (Remember separation)  */

		cp = (struct cmd *)0;
		if (getline(str2, fd) == FALSE)		/*  Get commands  */
		{
			while (*str2 == '\t')
			{
				cp = newcmd(&str2[0], cp);
				if (getline(str2, fd))
					break;
			}
		}

		while ((p = gettok(&q)) != (char *)0)	/* Get list of targ's */
		{
			np = newname(p);		/*  Intern name  */
			newline(np, dp, cp);
			if (!firstname)
				firstname = np;
		}

		if (feof(fd))				/*  EOF?  */
			return;

		strcpy(str1, str2);
	}
}
@//E*O*F input.c//
chmod u=rw,g=r,o=r input.c
 
echo x - macro.c
sed 's/^@//' > "macro.c" <<'@//E*O*F macro.c//'
/*
 *	Macro control for make
 */


#include "h.h"


struct macro *		macrohead;


struct macro *
getmp(name)
char *			name;
{
	register struct macro *	rp;

	for (rp = macrohead; rp; rp = rp->m_next)
		if (strcmp(name, rp->m_name) == 0)
			return rp;
	return (struct macro *)0;
}


char *
getmacro(name)
char *			name;
{
	struct macro *		mp;

	if (mp = getmp(name))
		return mp->m_val;
	else
		return "";
}


struct macro *
setmacro(name, val)
char *			name;
char *			val;
{
	register struct macro *	rp;
	register char *		cp;


			/*  Replace macro definition if it exists  */
	for (rp = macrohead; rp; rp = rp->m_next)
		if (strcmp(name, rp->m_name) == 0)
		{
			free(rp->m_val);	/*  Free space from old  */
			break;
		}

	if (!rp)		/*  If not defined, allocate space for new  */
	{
		if ((rp = (struct macro *)malloc(sizeof (struct macro)))
					 == (struct macro *)0)
			fatal("No memory for macro");

		rp->m_next = macrohead;
		macrohead = rp;
		rp->m_flag = FALSE;

		if ((cp = malloc(strlen(name)+1)) == (char *)0)
			fatal("No memory for macro");
		strcpy(cp, name);
		rp->m_name = cp;
	}

	if ((cp = malloc(strlen(val)+1)) == (char *)0)
		fatal("No memory for macro");
	strcpy(cp, val);		/*  Copy in new value  */
	rp->m_val = cp;

	return rp;
}


/*
 *	Do the dirty work for expand
 */
void
doexp(to, from, len, buf)
char **			to;
char *			from;
int *			len;
char *			buf;
{
	register char *		rp;
	register char *		p;
	register char *		q;
	register struct macro *	mp;


	rp = from;
	p = *to;
	while (*rp)
	{
		if (*rp != '$')
		{
			*p++ = *rp++;
			(*len)--;
		}
		else
		{
			q = buf;
			if (*++rp == '(')
				while (*++rp && *rp != ')')
					*q++ = *rp;
			else if (!*rp)
			{
				*p++ = '$';
				break;
			}
			else
				*q++ = *rp;
			*q = '\0';
			if (*rp)
				rp++;
			if (!(mp = getmp(buf)))
				mp = setmacro(buf, "");
			if (mp->m_flag)
				fatal("Infinitely recursive macro %s", mp->m_name);
			mp->m_flag = TRUE;
			*to = p;
			doexp(to, mp->m_val, len, buf);
			p = *to;
			mp->m_flag = FALSE;
		}
		if (*len <= 0)
			error("Expanded line too line");
	}
	*p = '\0';
	*to = p;
}


/*
 *	Expand any macros in str.
 */
void
expand(str)
char *		str;
{
	static char		a[LZ];
	static char		b[LZ];
	char *			p = str;
	int			len = LZ-1;

	strcpy(a, str);
	doexp(&p, a, &len, b);
}
@//E*O*F macro.c//
chmod u=rw,g=r,o=r macro.c
 
echo x - main.c
sed 's/^@//' > "main.c" <<'@//E*O*F main.c//'
/*
 *	make [-f makefile] [-ins] [target(s) ...]
 *
 *	(Better than EON mk but not quite as good as UNIX make)
 *
 *	-f makefile name
 *	-i ignore exit status
 *	-n Pretend to make
 *	-p Print all macros & targets
 *	-q Question up-to-dateness of target.  Return exit status 1 if not
 *	-r Don't not use inbuilt rules
 *	-s Make silently
 *	-t Touch files instead of making them
 *	-m Change memory requirements
 */

#include <stdio.h>
#include <sys/stat.h>
#include <sys/err.h>
#include "h.h"


#define MEMSPACE	(16384)


char *			myname;
char *			makefile;	/*  The make file  */
unsigned		memspace = MEMSPACE;

FILE *			ifd;		/*  Input file desciptor  */
bool			domake = TRUE;	/*  Go through the motions option  */
bool			ignore = FALSE;	/*  Ignore exit status option  */
bool			silent = FALSE;	/*  Silent option  */
bool			print = FALSE;	/*  Print debuging information  */
bool			rules = TRUE;	/*  Use inbuilt rules  */
bool			dotouch = FALSE;/*  Touch files instead of making  */
bool			quest = FALSE;	/*  Question up-to-dateness of file  */


void
main(argc, argv)
char **			argv;
int			argc;
{
	register char *		p;	/*  For argument processing  */
	int			estat;	/*  For question  */
	register struct name *	np;


	myname = (argc-- < 1) ? "make" : *argv++;

	while ((argc > 0) && (**argv == '-'))
	{
		argc--;		/*  One less to process  */
		p = *argv++;	/*  Now processing this one  */

		while (*++p != '\0')
		{
			switch(*p)
			{
			case 'f':	/*  Alternate file name  */
				if (*++p == '\0')
				{
					if (argc-- <= 0)
						usage();
					p = *argv++;
				}
				makefile = p;
				goto end_of_args;
			case 'm':	/*  Change space requirements  */
				if (*++p == '\0')
				{
					if (argc-- <= 0)
						usage();
					p = *argv++;
				}
				memspace = atoi(p);
				goto end_of_args;
			case 'n':	/*  Pretend mode  */
				domake = FALSE;
				break;
			case 'i':	/*  Ignore fault mode  */
				ignore = TRUE;
				break;
			case 's':	/*  Silent about commands  */
				silent = TRUE;
				break;
			case 'p':
				print = TRUE;
				break;
			case 'r':
				rules = FALSE;
				break;
			case 't':
				dotouch = TRUE;
				break;
			case 'q':
				quest = TRUE;
				break;
			default:	/*  Wrong option  */
				usage();
			}
		}
	end_of_args:;
	}

	if (initalloc(memspace) == 0xffff)  /*  Must get memory for alloc  */
		fatal("Cannot initalloc memory");

	if (strcmp(makefile, "-") == 0)	/*  Can use stdin as makefile  */
		ifd = stdin;
	else
		if (!makefile)		/*  If no file, then use default */
		{
			if ((ifd = fopen(DEFN1, "r")) == (FILE *)0)
				if (errno != ER_NOTF)
					fatal("Can't open %s; error %02x", DEFN1, errno);
			if ((ifd == (FILE *)0)
				  && ((ifd = fopen(DEFN2, "r")) == (FILE *)0))
				fatal("Can't open %s", DEFN2);
		}
		else
			if ((ifd = fopen(makefile, "r")) == (FILE *)0)
				fatal("Can't open %s", makefile);

	makerules();

	setmacro("$", "$");

	while (argc && (p = index(*argv, '=')))
	{
		char		c;

		c = *p;
		*p = '\0';
		setmacro(*argv, p+1);
		*p = c;

		argv++;
		argc--;
	}

	input(ifd);	/*  Input all the gunga  */
	fclose(ifd);	/*  Finished with makefile  */
	lineno = 0;	/*  Any calls to error now print no line number */

	if (print)
		prt();	/*  Print out structures  */

	np = newname(".SILENT");
	if (np->n_flag & N_TARG)
		silent = TRUE;

	np = newname(".IGNORE");
	if (np->n_flag & N_TARG)
		ignore = TRUE;

	precious();

	if (!domake)
		silent = FALSE;

	if (!firstname)
		fatal("No targets defined");

	circh();	/*  Check circles in target definitions  */

	if (!argc)
		estat = make(firstname, 0);
	else while (argc--)
	{
		if (!print && !silent && strcmp(*argv, "love") == 0)
			printf("Not war!\n");
		estat |= make(newname(*argv++), 0);
	}

	if (quest)
		exit(estat);
	else
		exit(0);
}


usage()
{
	fprintf(stderr, "Usage: %s [-f makefile] [-inpqrst] [macro=val ...] [target(s) ...]\n", myname);
	exit(1);
}


void
fatal(msg, a1, a2, a3)
char	*msg;
{
	fprintf(stderr, "%s: ", myname);
	fprintf(stderr, msg, a1, a2, a3);
	fputc('\n', stderr);
	exit(1);
}

@//E*O*F main.c//
chmod u=rw,g=r,o=r main.c
 
echo x - make.c
sed 's/^@//' > "make.c" <<'@//E*O*F make.c//'
/*
 *	Do the actual making for make
 */

#include <stdio.h>
#include <sys/stat.h>
#include <sys/err.h>
#include "h.h"



/*
 *	Exec a shell that returns exit status correctly (/bin/esh).
 *	The standard EON shell returns the process number of the last
 *	async command, used by the debugger (ugg).
 *	[exec on eon is like a fork+exec on unix]
 */
int
dosh(string, shell)
char *			string;
char *			shell;
{
	int	number;

	return ((number = execl(shell, shell,"-c", string, 0)) == -1) ?
		-1:	/* couldn't start the shell */
		wait(number);	/* return its exit status */
}


/*
 *	Do commands to make a target
 */
void
docmds(np)
struct name *		np;
{
	bool			ssilent = silent;
	bool			signore = ignore;
	int			estat;
	register char *		q;
	register char *		p;
	char *			shell;
	register struct line *	lp;
	register struct cmd *	cp;


	if (*(shell = getmacro("SHELL")) == '\0')
		shell = ":bin/esh";

	for (lp = np->n_line; lp; lp = lp->l_next)
		for (cp = lp->l_cmd; cp; cp = cp->c_next)
		{
			strcpy(str1, cp->c_cmd);
			expand(str1);
			q = str1;
			while ((*q == '@') || (*q == '-'))
			{
				if (*q == '@')	   /*  Specific silent  */
					ssilent = TRUE;
				else		   /*  Specific ignore  */
					signore = TRUE;
				q++;		   /*  Not part of the command  */
			}

			if (!ssilent)
				fputs("    ", stdout);

			for (p=q; *p; p++)
			{
				if (*p == '\n' && p[1] != '\0')
				{
					*p = ' ';
					if (!ssilent)
						fputs("\\\n", stdout);
				}
				else if (!ssilent)
					putchar(*p);
			}
			if (!ssilent)
				putchar('\n');

			if (domake)
			{			/*  Get the shell to execute it  */
				if ((estat = dosh(q, shell)) != 0)
				{
					if (estat == -1)
						fatal("Couldn't execute %s", shell);
					else
					{
						printf("%s: Error code %d", myname, estat);
						if (signore)
							fputs(" (Ignored)\n", stdout);
						else
						{
							putchar('\n');
							if (!(np->n_flag & N_PREC))
								if (unlink(np->n_name) == 0)
									printf("%s: '%s' removed.\n", myname, np->n_name);
							exit(estat);
						}
					}
				}
			}
		}
}


/*
 *	Get the modification time of a file.  If the first
 *	doesn't exist, it's modtime is set to 0.
 */
void
modtime(np)
struct name *		np;
{
	struct stat		info;
	int			fd;


	if ((fd = open(np->n_name, 0)) < 0)
	{
		if (errno != ER_NOTF)
			fatal("Can't open %s; error %02x", np->n_name, errno);

		np->n_time = 0L;
	}
	else if (getstat(fd, &info) < 0)
		fatal("Can't getstat %s; error %02x", np->n_name, errno);
	else
		np->n_time = info.st_mod;

	close(fd);
}


/*
 *	Update the mod time of a file to now.
 */
void
touch(np)
struct name *		np;
{
	char			c;
	int			fd;


	if (!domake || !silent)
		printf("    touch %s\n", np->n_name);

	if (domake)
	{
		if ((fd = open(np->n_name, 0)) < 0)
			printf("%s: '%s' no touched - non-existant\n",
					myname, np->n_name);
		else
		{
			uread(fd, &c, 1, 0);
			uwrite(fd, &c, 1);
		}
		close(fd);
	}
}


/*
 *	Recursive routine to make a target.
 */
int
make(np, level)
struct name *		np;
int			level;
{
	register struct depend *	dp;
	register struct line *		lp;
	time_t				dtime = 1;


	if (np->n_flag & N_DONE)
		return 0;

	if (!np->n_time)
		modtime(np);		/*  Gets modtime of this file  */

	if (rules)
	{
		for (lp = np->n_line; lp; lp = lp->l_next)
			if (lp->l_cmd)
				break;
		if (!lp)
			dyndep(np);
	}

	if (!(np->n_flag & N_TARG) && np->n_time == 0L)
		fatal("Don't know how to make %s", np->n_name);

	for (lp = np->n_line; lp; lp = lp->l_next)
		for (dp = lp->l_dep; dp; dp = dp->d_next)
		{
			make(dp->d_name, level+1);
			dtime = max(dtime, dp->d_name->n_time);
		}

	np->n_flag |= N_DONE;

	if (quest)
	{
		rtime(&np->n_time);
		return np->n_time < dtime;
	}
	else if (np->n_time < dtime)
	{
		if (dotouch)
			touch(np);
		else
		{
			setmacro("@", np->n_name);
			docmds(np);
		}
		rtime(&np->n_time);
	}
	else if (level == 0)
		printf("%s: '%s' is up to date\n", myname, np->n_name);
	return 0;
}
@//E*O*F make.c//
chmod u=rw,g=r,o=r make.c
 
echo x - reader.c
sed 's/^@//' > "reader.c" <<'@//E*O*F reader.c//'
/*
 *	Read in makefile
 */


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


int			lineno;


/*
 *	Syntax error handler.  Print message, with line number, and exits.
 */
void
error(msg, a1, a2, a3)
char *			msg;
{
	fprintf(stderr, "%s: ", myname);
	fprintf(stderr, msg, a1, a2, a3);
	if (lineno)
		fprintf(stderr, " on line %d", lineno);
	fputc('\n', stderr);
	exit(1);
}


/*
 *	Read a line into the supplied string of length LZ.  Remove
 *	comments, ignore blank lines. Deal with	quoted (\) #, and
 *	quoted newlines.  If EOF return TRUE.
 */
bool
getline(str, fd)
char *		str;
FILE *		fd;
{
	register char *		p;
	char *			q;
	int			pos = 0;


	for (;;)
	{
		if (fgets(str+pos, LZ-pos, fd) == (char *)0)
			return TRUE;		/*  EOF  */

		lineno++;

		if ((p = index(str+pos, '\n')) == (char *)0)
			error("Line too long");

		if (p[-1] == '\\')
		{
			p[-1] = '\n';
			pos = p - str;
			continue;
		}

		p = str;
		while (((q = index(p, '#')) != (char *)0) &&
		    (p != q) && (q[-1] == '\\'))
		{
			char	*a;

			a = q - 1;	/*  Del \ chr; move rest back  */
			p = q;
			while (*a++ = *q++)
				;
		}
		if (q != (char *)0)
		{
			q[0] = '\n';
			q[1] = '\0';
		}

		p = str;
		while (isspace(*p))	/*  Checking for blank  */
			p++;

		if (*p != '\0')
			return FALSE;
		pos = 0;
	}
}


/*
 *	Get a word from the current line, surounded by white space.
 *	return a pointer to it. String returned has no white spaces
 *	in it.
 */
char *
gettok(ptr)
char	**ptr;
{
	register char *		p;


	while (isspace(**ptr))	/*  Skip spaces  */
		(*ptr)++;

	if (**ptr == '\0')	/*  Nothing after spaces  */
		return NULL;

	p = *ptr;		/*  word starts here  */

	while ((**ptr != '\0') && (!isspace(**ptr)))
		(*ptr)++;	/*  Find end of word  */

	*(*ptr)++ = '\0';	/*  Terminate it  */

	return(p);
}
@//E*O*F reader.c//
chmod u=rw,g=r,o=r reader.c
 
echo x - rules.c
sed 's/^@//' > "rules.c" <<'@//E*O*F rules.c//'
/*
 *	Control of the implicit suffix rules
 */


#include "h.h"


/*
 *	Return a pointer to the suffix of a name
 */
char *
suffix(name)
char *			name;
{
	return rindex(name, '.');
}


/*
 *	Dynamic dependency.  This routine applies the suffis rules
 *	to try and find a source and a set of rules for a missing
 *	target.  If found, np is made into a target with the implicit
 *	source name, and rules.  Returns TRUE if np was made into
 *	a target.
 */
bool
dyndep(np)
struct name *		np;
{
	register char *		p;
	register char *		q;
	register char *		suff;		/*  Old suffix  */
	register char *		basename;	/*  Name without suffix  */
	struct name *		op;		/*  New dependent  */
	struct name *		sp;		/*  Suffix  */
	struct line *		lp;
	struct depend *		dp;
	char *			newsuff;


	p = str1;
	q = np->n_name;
	suff = suffix(q);
	while (q < suff)
		*p++ = *q++;
	*p = '\0';
	basename = setmacro("*", str1)->m_val;

	if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG))
		return FALSE;

	for (lp = sp->n_line; lp; lp = lp->l_next)
		for (dp = lp->l_dep; dp; dp = dp->d_next)
		{
			newsuff = dp->d_name->n_name;
			if (strlen(suff)+strlen(newsuff)+1 >= LZ)
				fatal("Suffix rule too long");
			p = str1;
			q = newsuff;
			while (*p++ = *q++)
				;
			p--;
			q = suff;
			while (*p++ = *q++)
				;
			sp = newname(str1);
			if (sp->n_flag & N_TARG)
			{
				p = str1;
				q = basename;
				if (strlen(basename) + strlen(newsuff)+1 >= LZ)
					fatal("Implicit name too long");
				while (*p++ = *q++)
					;
				p--;
				q = newsuff;
				while (*p++ = *q++)
					;
				op = newname(str1);
				if (!op->n_time)
					modtime(op);
				if (op->n_time)
				{
					dp = newdep(op, 0);
					newline(np, dp, sp->n_line->l_cmd);
					setmacro("<", op->n_name);
					return TRUE;
				}
			}
		}
	return FALSE;
}


/*
 *	Make the default rules
 */
void
makerules()
{
	struct cmd *		cp;
	struct name *		np;
	struct depend *		dp;


	setmacro("BDSCC", "asm");
	/*	setmacro("BDSCFLAGS", "");	*/
	cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", 0);
	np = newname(".c.o");
	newline(np, 0, cp);

	setmacro("CC", "c");
	setmacro("CFLAGS", "-O");
	cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
	np = newname(".c.obj");
	newline(np, 0, cp);

	setmacro("M80", "asm -n");
	/*	setmacro("M80FLAGS", "");	*/
	cp = newcmd("$(M80) $(M80FLAGS) $<", 0);
	np = newname(".mac.o");
	newline(np, 0, cp);

	setmacro("AS", "zas");
	/*	setmacro("ASFLAGS", "");	*/
	cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", 0);
	np = newname(".as.obj");
	newline(np, 0, cp);

	np = newname(".as");
	dp = newdep(np, 0);
	np = newname(".obj");
	dp = newdep(np, dp);
	np = newname(".c");
	dp = newdep(np, dp);
	np = newname(".o");
	dp = newdep(np, dp);
	np = newname(".mac");
	dp = newdep(np, dp);
	np = newname(".SUFFIXES");
	newline(np, dp, 0);
}
@//E*O*F rules.c//
chmod u=rw,g=r,o=r rules.c
 
exit 0