Browse Source

Initial revision

ceriel 36 years ago
parent
commit
a0a265736c
11 changed files with 1971 additions and 0 deletions
  1. 10 0
      util/make/.distr
  2. 29 0
      util/make/Makefile
  3. 42 0
      util/make/README
  4. 117 0
      util/make/check.c
  5. 145 0
      util/make/h.h
  6. 342 0
      util/make/input.c
  7. 167 0
      util/make/macro.c
  8. 261 0
      util/make/main.c
  9. 482 0
      util/make/make.c
  10. 119 0
      util/make/reader.c
  11. 257 0
      util/make/rules.c

+ 10 - 0
util/make/.distr

@@ -0,0 +1,10 @@
+Makefile
+README
+check.c
+h.h
+input.c
+macro.c
+main.c
+make.c
+reader.c
+rules.c

+ 29 - 0
util/make/Makefile

@@ -0,0 +1,29 @@
+# Makefile for make!
+EMHOME=../..
+
+OBJS	=	check.o input.o macro.o main.o \
+		make.o reader.o rules.o
+# unix not defined under Xenix ???
+CFLAGS = -Dunix	
+
+all:	make
+
+install: make
+	cp make $(EMHOME)/bin/make
+
+cmp:	make
+	cmp make $(EMHOME)/bin/make
+
+clean:
+	rm -f *.o make
+
+pr:
+	@pr README Makefile h.h main.c check.c input.c macro.c make.c reader.c rules.c
+
+opr:
+	make pr ^ opr
+
+make:	$(OBJS)
+	$(CC) -i -o make $(OBJS)
+
+$(OBJS):	h.h

+ 42 - 0
util/make/README

@@ -0,0 +1,42 @@
+Following is a repost of the public domain 'make' that I posted
+to net.sources a couple of months ago.  I have fixed a few bugs, and
+added some more features, and the resulting changes amounted to
+about as much text as the whole program (hence the repost).
+
+For those that missed the net.sources posting, this is a public domain
+re-implementation of the UNIX make program.  There is no manual included;
+for documentation, refer to a UNIX manual, or the source.
+
+Here is a list of the changes made:
+
+i)	If '-' (ignore) or '@' (silent) where used at the start
+	of a command, their effect was not turned off for the following
+	commands.
+ii)	A special target (.SUFFIXES, .PRECIOUS) or a rule (.c.o, .a.o),
+	if first in the file would be taken as the default target.
+	This resulted in error messages like "Don't know how to
+	make .c", because things like .SUFFIXES were being made.
+	This was further complicated by ---
+iii)	Special target lines with no dependents (ie. .SUFFIXES:\n)
+	were not clearing out the existing dependents like
+	they should.
+iv)	Default rules could not be redefined because of the error
+	checking for commands being defined twice.  Now you are
+	allowed to define a target beinging with '.', having
+	no dependents with commands.
+v)	The -q option didn't do the time comparison correctly,
+	or clear the variable used to keep track of this.  Thus
+	it didn't work very well.
+vi)	The syntax ${..} for macro's supported by UNIX make was
+	not supported.
+vii)	There wuz a couple of spelling errors.
+viii)	When make checked for implicit rules on targets without
+	a suffix, there were problems.  (Note: The ~ feature of
+	UNIX make wasn't and still isn't supported)
+ix)	The -n option did not print @ lines like it was supposed to.
+x)	:: added.  (See UNIX manual)
+xi)	$? added.  (see UNIX manual)
+
+Hacked further by Ceriel Jacobs to make it work better. Use this "make" to
+install ACK under Microsoft Xenix V3.2. Some of the makefiles are just too
+big for the Xenix "make". Strange, they work on a PDP-11 ...

+ 117 - 0
util/make/check.c

@@ -0,0 +1,117 @@
+/*
+ *	Check structures for make.
+ *
+ *	$Header$
+ */
+
+#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)
+	{
+		if (np->n_flag & N_DOUBLE)
+			fprintf(stderr, "%s::\n", np->n_name);
+		else
+			fprintf(stderr, "%s:\n", np->n_name);
+		if (np == firstname)
+			fprintf(stderr, "(MAIN NAME)\n");
+		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)
+#ifdef os9
+				fprintf(stderr, "-   %s\n", cp->c_cmd);
+#else
+				fprintf(stderr, "-\t%s\n", cp->c_cmd);
+#endif
+			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;
+}

+ 145 - 0
util/make/h.h

@@ -0,0 +1,145 @@
+/*
+ *	Include header for make
+ *
+ *	$Header$
+ */
+
+
+#ifndef uchar
+#ifdef os9
+#define uchar		char
+#define void		int
+#define fputc		putc
+#else
+#define uchar		unsigned char
+#endif
+#endif
+
+#define bool		int
+#define time_t		long
+#define TRUE		(1)
+#define FALSE		(0)
+#define max(a,b)	((a)>(b)?(a):(b))
+
+#define DEFN1		"makefile"		/*  Default names  */
+#ifdef unix
+#define DEFN2		"Makefile"
+#endif
+#ifdef eon
+#define DEFN2		"Makefile"
+#endif
+/* os9 is case insensitive */
+
+#define LZ		(2048)			/*  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 */
+#define N_DOUBLE	0x10			/* Double colon target */
+
+/*
+ *	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 */
+	uchar		m_prio;		/* 5 levels:
+					   - 0 for internal ($(CC), etc)
+					   - 1 (reserved for environment)
+					   - 2 for makefile
+					   - 3 for command line
+					   - 4 for special ($*,$<, etc)
+					*/
+};
+
+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();
+char *			strcpy();
+char *			strcat();
+extern int		errno;
+
+void			circh();
+char *			getmacro();
+struct macro *		setmacro();
+void			input();
+void			error();
+void			expand();
+void			fatal();
+int			make();
+void			modtime();
+struct name *		newname();
+struct depend *		newdep();
+struct cmd *		newcmd();
+void			newline();
+void			prt();
+char *			suffix();
+void			touch();
+void			makerules();
+char *			gettok();
+void			precious();

+ 342 - 0
util/make/input.c

@@ -0,0 +1,342 @@
+/*
+ *	Parse a makefile
+ *
+ *	$Header$
+ */
+
+
+#include <stdio.h>
+#include	<ctype.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((unsigned)(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 0;
+
+	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((unsigned)(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.  If flag is set,
+ *	the line is a double colon target.
+ *
+ *	Kludges:
+ *	i)  If the new name begins with a '.', and there are no dependents,
+ *	    then the target must cease to be a target.  This is for .SUFFIXES.
+ *	ii) If the new name begins with a '.', with no dependents and has
+ *	    commands, then replace the current commands.  This is for
+ *	    redefining commands for a default rule.
+ *	Neither of these free the space used by dependents or commands,
+ *	since they could be used by another target.
+ */
+void
+newline(np, dp, cp, flag)
+struct name *		np;
+struct depend *		dp;
+struct cmd *		cp;
+{
+	bool			hascmds = FALSE;  /*  Target has commands  */
+	register struct line *	rp;
+	register struct line *	rrp;
+
+
+	/* Handle the .SUFFIXES case */
+	if (! strcmp(np->n_name, ".SUFFIXES") && !dp && !cp)
+	{
+		for (rp = np->n_line; rp; rp = rrp)
+		{
+			rrp = rp->l_next;
+			free((char *)rp);
+		}
+		np->n_line = (struct line *)0;
+		np->n_flag &= ~N_TARG;
+		return;
+	}
+
+	/* This loop must happen since rrp is used later. */
+	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 && !(np->n_flag & N_DOUBLE))
+		/* Handle the implicit rules redefinition case */
+		if (np->n_name[0] == '.' && dp == (struct depend *)0)
+		{
+			np->n_line->l_cmd = cp;
+			return;
+		}
+		else
+			error("Commands defined twice for target %s", np->n_name);
+	if (np->n_flag & N_TARG)
+		if (!(np->n_flag & N_DOUBLE) != !flag)		/* like xor */
+			error("Inconsistent rules 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;
+	if (flag)
+		np->n_flag |= N_DOUBLE;
+}
+
+
+/*
+ *	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;
+	bool			dbl;
+
+
+	if (getline(str1, fd))	/*  Read the first line  */
+		return;
+
+	for(;;)
+	{
+#ifdef os9
+		if (*str1 == ' ')	/*  Rules without targets  */
+#else
+		if (*str1 == '\t')	/*  Rules without targets  */
+#endif
+			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, 2);
+
+			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  */
+
+		if (*q == ':')		/* Double colon */
+		{
+			dbl = 1;
+			q++;
+		}
+		else
+			dbl = 0;
+
+		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  */
+		{
+#ifdef os9
+			while (*str2 == ' ')
+#else
+			while (*str2 == '\t')
+#endif
+			{
+				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, dbl);
+			if (!firstname && p[0] != '.')
+				firstname = np;
+		}
+
+		if (feof(fd))				/*  EOF?  */
+			return;
+
+		strcpy(str1, str2);
+	}
+}

+ 167 - 0
util/make/macro.c

@@ -0,0 +1,167 @@
+/*
+ *	Macro control for make
+ *
+ *	$Header$
+ */
+
+
+#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, prio)
+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)
+		{
+			if (prio < rp->m_prio)
+				return rp;
+			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((unsigned)(strlen(name)+1))) == (char *)0)
+			fatal("No memory for macro");
+		strcpy(cp, name);
+		rp->m_name = cp;
+	}
+
+	if ((cp = malloc((unsigned)(strlen(val)+1))) == (char *)0)
+		fatal("No memory for macro");
+	strcpy(cp, val);		/*  Copy in new value  */
+	rp->m_val = cp;
+	rp->m_prio = prio;
+
+	return rp;
+}
+
+#define MBUFSIZ	128
+
+/*
+ *	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 != '}') {
+					if (q < &buf[MBUFSIZ-1]) *q++ = *rp;
+				}
+			else if (*rp == '(')
+				while (*++rp && *rp != ')') {
+					if (q < &buf[MBUFSIZ-1]) *q++ = *rp;
+				}
+			else if (!*rp)
+			{
+				*p++ = '$';
+				break;
+			}
+			else
+				*q++ = *rp;
+			*q = '\0';
+			if (*rp)
+				rp++;
+			if (!(mp = getmp(buf)))
+				mp = setmacro(buf, "", 2);
+			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;
+{
+	char			*a;
+	static char		b[MBUFSIZ];	/* temp storage for macroname */
+	char *			p = str;
+	int			len = LZ-1;
+
+	a = malloc((unsigned)(strlen(str)+1));
+	if (!a) fatal("No memory for expand");
+	strcpy(a, str);
+	doexp(&p, a, &len, b);
+	free(a);
+}

+ 261 - 0
util/make/main.c

@@ -0,0 +1,261 @@
+/*
+ *	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 (EON only)
+ *	-k For the time being: accept but ignore
+ *
+ *	$Header$
+ */
+
+#include <stdio.h>
+#include "h.h"
+
+#ifdef unix
+#include <errno.h>
+#endif
+#ifdef eon
+#include <sys/err.h>
+#endif
+#ifdef os9
+#include <errno.h>
+#endif
+
+
+#ifdef eon
+#define MEMSPACE	(16384)
+#endif
+
+
+char *			myname;
+char *			makefile;	/*  The make file  */
+#ifdef eon
+unsigned		memspace = MEMSPACE;
+#endif
+
+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)
+int			argc;
+char **			argv;
+{
+	register char *		p;		/*  For argument processing  */
+	int			estat = 0;	/*  For question  */
+	register struct name *	np;
+	int			nargc = 0;
+	char			**nargv;
+	int			fflag = 0;
+
+
+	myname = (argc-- < 1) ? "make" : *argv++;
+	nargv = argv;
+
+	while (argc > 0)
+	{
+		argc--;		/*  One less to process  */
+		p = *argv++;	/*  Now processing this one  */
+
+		if (*p == '-') while (*++p != '\0')
+		{
+			switch(*p)
+			{
+			case 'f':	/*  Alternate file name  */
+				fflag = 1;
+				break;
+#ifdef eon
+			case 'm':	/*  Change space requirements  */
+				if (*++p == '\0')
+				{
+					if (argc-- <= 0)
+						usage();
+					p = *argv++;
+				}
+				memspace = atoi(p);
+				goto end_of_args;
+#endif
+			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;
+			case 'k':
+				break;
+			default:	/*  Wrong option  */
+				usage();
+			}
+		}
+		else {
+			if (fflag) {
+				if (argc <= 0) usage();
+				makefile = p;
+				fflag = 0;
+			}
+			else {
+				nargc++;
+				*nargv++ = p;
+			}
+		}
+	end_of_args:;
+	}
+	argv = nargv - nargc;
+	argc = nargc;
+
+#ifdef eon
+	if (initalloc(memspace) == 0xffff)  /*  Must get memory for alloc  */
+		fatal("Cannot initalloc memory");
+#endif
+
+	if (makefile && 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)
+#ifdef eon
+				if (errno != ER_NOTF)
+					fatal("Can't open %s; error %02x", DEFN1, errno);
+#endif
+#ifdef unix
+				if (errno != ENOENT)
+					fatal("Can't open %s; error %02x", DEFN1, errno);
+#endif
+#ifndef os9
+			if ((ifd == (FILE *)0)
+				  && ((ifd = fopen(DEFN2, "r")) == (FILE *)0))
+				fatal("Can't open %s", DEFN2);
+#else
+				fatal("Can't open %s", DEFN1);
+#endif
+		}
+		else
+			if ((ifd = fopen(makefile, "r")) == (FILE *)0)
+				fatal("Can't open %s", makefile);
+
+	makerules();
+
+	setmacro("$", "$", 4);
+
+	while (argc && (p = index(*argv, '=')))
+	{
+		char		c;
+
+		c = *p;
+		*p = '\0';
+		setmacro(*argv, p+1, 3);
+		*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 (!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);
+}
+
+
+/*VARARGS1*/
+void
+fatal(msg, a1, a2, a3, a4, a5, a6)
+char	*msg;
+{
+	fprintf(stderr, "%s: ", myname);
+	fprintf(stderr, msg, a1, a2, a3, a4, a5, a6);
+	fputc('\n', stderr);
+	exit(1);
+}
+
+char *
+index(s, c)
+	register char *s, c;
+{
+	while (*s)
+		if (*s++ == c)
+			return --s;
+	return (char *)0;
+}
+
+char *
+rindex(str, chr)
+	register char *str, chr;
+{
+	register char *retptr = 0;
+
+	while (*str)
+		if (*str++ == chr)
+			retptr = &str[-1];
+	return retptr;
+}

+ 482 - 0
util/make/make.c

@@ -0,0 +1,482 @@
+/*
+ *	Do the actual making for make
+ *
+ *	$Header$
+ */
+
+#include <stdio.h>
+#ifdef unix
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#endif
+#ifdef eon
+#include <sys/stat.h>
+#include <sys/err.h>
+#endif
+#ifdef os9
+#include <time.h>
+#include <os9.h>
+#include <modes.h>
+#include <direct.h>
+#include <errno.h>
+#endif
+#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;
+
+#ifdef unix
+	return system(string);
+#endif
+#ifdef eon
+	return ((number = execl(shell, shell,"-c", string, 0)) == -1) ?
+		-1:	/* couldn't start the shell */
+		wait(number);	/* return its exit status */
+#endif
+#ifdef os9
+	int	status, pid;
+
+	strcat(string, "\n");
+	if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1)
+		return -1;		/* Couldn't start a shell */
+	do
+	{
+		if ((pid = wait(&status)) == -1)
+			return -1;	/* child already died!?!? */
+	} while (pid != number);
+
+	return status;
+#endif
+}
+
+
+/*
+ *	Do commands to make a target
+ */
+void
+docmds1(np, lp)
+struct name *		np;
+struct line *		lp;
+{
+	bool			ssilent;
+	bool			signore;
+	int			estat;
+	register char *		q;
+	register char *		p;
+	char *			shell;
+	register struct cmd *	cp;
+
+
+	if (*(shell = getmacro("SHELL")) == '\0')
+#ifdef eon
+		shell = ":bin/esh";
+#endif
+#ifdef unix
+		shell = "/bin/sh";
+#endif
+#ifdef os9
+		shell = "shell";
+#endif
+
+	for (cp = lp->l_cmd; cp; cp = cp->c_next)
+	{
+		strcpy(str1, cp->c_cmd);
+		expand(str1);
+		q = str1;
+		ssilent = silent;
+		signore = ignore;
+		while ((*q == '@') || (*q == '-'))
+		{
+			if (*q == '@')	   /*  Specific silent  */
+				ssilent = TRUE;
+			else		   /*  Specific ignore  */
+				signore = TRUE;
+			q++;		   /*  Not part of the command  */
+		}
+
+		if (!domake)
+			ssilent = 0;
+
+		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');
+			fflush(stdout);
+		}
+
+		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(1);
+					}
+				}
+				fflush(stdout);
+			}
+		}
+	}
+}
+
+
+docmds(np)
+struct name *		np;
+{
+	register struct line *	lp;
+
+
+	for (lp = np->n_line; lp; lp = lp->l_next)
+		docmds1(np, lp);
+}
+
+
+#ifdef os9
+/*
+ *	Some stuffing around to get the modified time of a file
+ *	in an os9 file system
+ */
+getmdate(fd, tbp)
+struct sgtbuf *		tbp;
+{
+	struct registers	regs;
+	static struct fildes	fdbuf;
+
+
+	regs.rg_a = fd;
+	regs.rg_b = SS_FD;
+	regs.rg_x = &fdbuf;
+	regs.rg_y = sizeof (fdbuf);
+
+	if (_os9(I_GETSTT, &regs) == -1)
+	{
+		errno = regs.rg_b & 0xff;
+		return -1;
+	}
+	if (tbp)
+	{
+		_strass(tbp, fdbuf.fd_date, sizeof (fdbuf.fd_date));
+		tbp->t_second = 0;	/* Files are only acurate to mins */
+	}
+	return 0;
+}
+
+
+/*
+ *	Kludge routine to return an aproximation of how many
+ *	seconds since 1980.  Dates will be in order, but will not
+ *	be lineer
+ */
+time_t
+cnvtime(tbp)
+struct sgtbuf		*tbp;
+{
+	long			acc;
+
+
+	acc = tbp->t_year - 80;		/* Baseyear is 1980 */
+	acc = acc * 12 + tbp->t_month;
+	acc = acc * 31 + tbp->t_day;
+	acc = acc * 24 + tbp->t_hour;
+	acc = acc * 60 + tbp->t_minute;
+	acc = acc * 60 + tbp->t_second;
+
+	return acc;
+}
+
+
+/*
+ *	Get the current time in the internal format
+ */
+time(tp)
+time_t *		tp;
+{
+	struct sgtbuf		tbuf;
+
+
+	if (getime(&tbuf) < 0)
+		return -1;
+
+	if (tp)
+		*tp = cnvtime(&tbuf);
+
+	return 0;
+}
+#endif
+
+
+/*
+ *	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;
+{
+#ifdef unix
+	struct stat		info;
+
+
+	if (stat(np->n_name, &info) < 0)
+	{
+		if (errno != ENOENT)
+			fatal("Can't open %s; error %d", np->n_name, errno);
+
+		np->n_time = 0L;
+	}
+	else
+		np->n_time = info.st_mtime;
+#endif
+#ifdef eon
+	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);
+#endif
+#ifdef os9
+	struct sgtbuf		info;
+	int			fd;
+
+
+	if ((fd = open(np->n_name, 0)) < 0)
+	{
+		if (errno != E_PNNF)
+			fatal("Can't open %s; error %02x", np->n_name, errno);
+
+		np->n_time = 0L;
+	}
+	else if (getmdate(fd, &info) < 0)
+		fatal("Can't getstat %s; error %02x", np->n_name, errno);
+	else
+		np->n_time = cnvtime(&info);
+
+	close(fd);
+#endif
+}
+
+
+/*
+ *	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)
+	{
+#ifdef unix
+		long		a[2];
+		long		time();
+
+		a[0] = a[1] = time((long *)0);
+		if (utime(np->n_name, &a[0]) < 0)
+			printf("%s: '%s' not touched - non-existant\n",
+					myname, np->n_name);
+#endif
+#ifdef eon
+		if ((fd = open(np->n_name, 0)) < 0)
+			printf("%s: '%s' not touched - non-existant\n",
+					myname, np->n_name);
+		else
+		{
+			uread(fd, &c, 1, 0);
+			uwrite(fd, &c, 1);
+		}
+		close(fd);
+#endif
+#ifdef os9
+		/*
+		 *	Strange that something almost as totally useless
+		 *	as this is easy to do in os9!
+		 */
+		if ((fd = open(np->n_name, S_IWRITE)) < 0)
+			printf("%s: '%s' not touched - non-existant\n",
+					myname, np->n_name);
+		close(fd);
+#endif
+	}
+}
+
+
+/*
+ *	Recursive routine to make a target.
+ */
+int
+make(np, level)
+struct name *		np;
+int			level;
+{
+	register struct depend *	dp;
+	register struct line *		lp;
+	register struct depend *	qdp;
+	time_t				dtime = 1;
+	bool				didsomething = 0;
+	int				dynamic = 0;
+
+
+	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);
+			dynamic = 1;
+		}
+	}
+
+	if (!(np->n_flag & N_TARG) && np->n_time == 0L)
+		fatal("Don't know how to make %s", np->n_name);
+
+	for (qdp = (struct depend *)0, lp = np->n_line; lp; lp = lp->l_next)
+	{
+		for (dp = lp->l_dep; dp; dp = dp->d_next)
+		{
+			char *sv = 0;
+			if (dynamic) {
+				char *s = getmacro("<");
+
+				if (s) {
+					sv = malloc((unsigned)(strlen(s)+1));
+					if (!sv) {
+						fatal("no space for saved $<");
+					}
+					strcpy(sv, s);
+				}
+			}
+			make(dp->d_name, level+1);
+			if (dynamic && sv) {
+				setmacro("<", sv, 4);
+				free(sv);
+			}
+			if (np->n_time < dp->d_name->n_time)
+				qdp = newdep(dp->d_name, qdp);
+			dtime = max(dtime, dp->d_name->n_time);
+		}
+		if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime))
+		{
+			make1(np, lp, qdp);	/* free()'s qdp */
+			dtime = 1;
+			qdp = (struct depend *)0;
+			didsomething++;
+		}
+	}
+
+	np->n_flag |= N_DONE;
+
+	if (quest)
+	{
+		long		t;
+
+		t = np->n_time;
+		time(&np->n_time);
+		return t < dtime;
+	}
+	else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE))
+	{
+		make1(np, (struct line *)0, qdp);	/* free()'s qdp */
+		time(&np->n_time);
+	}
+	else if (level == 0 && !didsomething)
+		printf("%s: '%s' is up to date\n", myname, np->n_name);
+	return 0;
+}
+
+
+make1(np, lp, qdp)
+register struct depend *	qdp;
+struct line *			lp;
+struct name *			np;
+{
+	register struct depend *	dp;
+	register char *p;
+	char *rindex();
+
+
+	if (dotouch)
+		touch(np);
+	else
+	{
+		strcpy(str1, "");
+		for (dp = qdp; dp; dp = qdp)
+		{
+			if (strlen(str1))
+				strcat(str1, " ");
+			strcat(str1, dp->d_name->n_name);
+			qdp = dp->d_next;
+			free((char *)dp);
+		}
+		setmacro("?", str1, 4);
+		setmacro("@", np->n_name, 4);
+		p = rindex(np->n_name, '.');
+		if (p) *p = 0;
+		setmacro("*", np->n_name, 4);
+		if (p) *p = '.';
+		if (lp)		/* lp set if doing a :: rule */
+			docmds1(np, lp);
+		else
+			docmds(np);
+	}
+}

+ 119 - 0
util/make/reader.c

@@ -0,0 +1,119 @@
+/*
+ *	Read in makefile
+ *
+ *	$Header$
+ */
+
+
+#include <stdio.h>
+#include	<ctype.h>
+#include "h.h"
+
+
+int			lineno;
+
+
+/*
+ *	Syntax error handler.  Print message, with line number, and exits.
+ */
+/*VARARGS1*/
+void
+error(msg, a1, a2, a3)
+char *			msg;
+{
+	fprintf(stderr, "%s: ", myname);
+	fprintf(stderr, msg, a1, a2, a3);
+	if (lineno)
+		fprintf(stderr, " near 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);
+}

+ 257 - 0
util/make/rules.c

@@ -0,0 +1,257 @@
+/*
+ *	Control of the implicit suffix rules
+ *
+ *	$Header$
+ */
+
+
+#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;
+	if (!(suff = suffix(q)))
+		return FALSE;		/* No suffix */
+	while (q < suff)
+		*p++ = *q++;
+	*p = '\0';
+	basename = setmacro("*", str1, 4)->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 || (op->n_flag & N_TARG))
+				{
+					dp = newdep(op, (struct depend *)0);
+					newline(np, dp, sp->n_line->l_cmd, 0);
+					setmacro("<", op->n_name, 4);
+					return TRUE;
+				}
+			}
+		}
+	return FALSE;
+}
+
+
+/*
+ *	Make the default rules
+ */
+void
+makerules()
+{
+	struct cmd *		cp;
+	struct name *		np;
+	struct depend *		dp;
+
+
+#ifdef eon
+	setmacro("BDSCC", "asm", 0);
+	/*	setmacro("BDSCFLAGS", "", 0);	*/
+	cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", (struct cmd *)0);
+	np = newname(".c.o");
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("CC", "c", 0);
+	setmacro("CFLAGS", "-O", 0);
+	cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0);
+	np = newname(".c.obj");
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("M80", "asm -n", 0);
+	/*	setmacro("M80FLAGS", "", 0);	*/
+	cp = newcmd("$(M80) $(M80FLAGS) $<", (struct cmd *)0);
+	np = newname(".mac.o");
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("AS", "zas", 0);
+	/*	setmacro("ASFLAGS", "", 0);	*/
+	cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", (struct cmd *)0);
+	np = newname(".as.obj");
+	newline(np, (struct depend *)0, cp, 0);
+
+	np = newname(".as");
+	dp = newdep(np, (struct depend *)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, (struct cmd *)0, 0);
+#endif
+
+/*
+ *	Some of the UNIX implicit rules
+ */
+#ifdef unix
+	setmacro("CC", "cc", 0);
+	setmacro("CFLAGS", "-O", 0);
+#ifdef MINIX
+	cp = newcmd("$(CC) $(CFLAGS) -S $<", (struct cmd *)0);
+	np = newname(".c.s");
+#else
+	cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0);
+	np = newname(".c.o");
+#endif MINIX
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("AS", "as", 0);
+	cp = newcmd("$(AS) -o $@ $<", (struct cmd *)0);
+	np = newname(".s.o");
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("YACC", "yacc", 0);
+	/*	setmacro("YFLAGS", "", 0);	*/
+	cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0);
+	cp = newcmd("mv y.tab.c $@", cp);
+	np = newname(".y.c");
+	newline(np, (struct depend *)0, cp, 0);
+
+	cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0);
+	cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp);
+	cp = newcmd("rm y.tab.c", cp);
+	cp = newcmd("mv y.tab.o $@", cp);
+	np = newname(".y.o");
+	newline(np, (struct depend *)0, cp, 0);
+
+	setmacro("LEX", "lex", 0);
+	/*	setmacro("LFLAGS", "", 0);	*/
+	cp = newcmd("$(LEX) $(LFLAGS) $<", (struct cmd *)0);
+	cp = newcmd("mv lex.yy.c $@", cp);
+	np = newname(".l.c");
+	newline(np, (struct depend *)0, cp, 0);
+
+	cp = newcmd("$(LEX) $(LFLAGS) $<", (struct cmd *)0);
+	cp = newcmd("$(CC) $(CFLAGS) -c lex.yy.c", cp);
+	cp = newcmd("rm lex.yy.c", cp);
+	cp = newcmd("mv lex.yy.o $@", cp);
+	np = newname(".l.o");
+	newline(np, (struct depend *)0, cp, 0);
+
+	np = newname(".s");
+	dp = newdep(np, (struct depend *)0);
+	np = newname(".o");
+	dp = newdep(np, dp);
+	np = newname(".c");
+	dp = newdep(np, dp);
+	np = newname(".y");
+	dp = newdep(np, dp);
+	np = newname(".l");
+	dp = newdep(np, dp);
+	np = newname(".SUFFIXES");
+	newline(np, dp, (struct cmd *)0, 0);
+#endif
+#ifdef os9
+/*
+ *	Fairlight use an enhanced version of the C sub-system.
+ *	They have a specialised macro pre-processor.
+ */
+	setmacro("CC", "cc", 0);
+	setmacro("CFLAGS", "-z", 0);
+	cp = newcmd("$(CC) $(CFLAGS) -r $<", (struct cmd *)0);
+
+	np = newname(".c.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".ca.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".a.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".o.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".mc.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".mca.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".ma.r");
+	newline(np, (struct depend *)0, cp, 0);
+	np = newname(".mo.r");
+	newline(np, (struct depend *)0, cp, 0);
+
+	np = newname(".r");
+	dp = newdep(np, (struct depend *)0);
+	np = newname(".mc");
+	dp = newdep(np, dp);
+	np = newname(".mca");
+	dp = newdep(np, dp);
+	np = newname(".c");
+	dp = newdep(np, dp);
+	np = newname(".ca");
+	dp = newdep(np, dp);
+	np = newname(".ma");
+	dp = newdep(np, dp);
+	np = newname(".mo");
+	dp = newdep(np, dp);
+	np = newname(".o");
+	dp = newdep(np, dp);
+	np = newname(".a");
+	dp = newdep(np, dp);
+	np = newname(".SUFFIXES");
+	newline(np, dp, (struct cmd *)0, 0);
+#endif
+}