[comp.sources.amiga] PD Make for the Amiga

doc@j.cc.purdue.edu (Craig Norborg) (04/24/87)

    This is the source to the public domain 'make' that was posted to
the net earlier this year.  Steve Walton has done the port to the Amiga.
Binaries available in comp.binaries.amiga.
    Craig Norborg
    comp.sources.amiga moderator	j.cc.purdue.edu!amiga-sources-request

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	Makefile.AZTEC
#	README
#	README.amiga
#	check.c
#	h.h
#	input.c
#	macro.c
#	main.c
#	make.c
#	reader.c
#	rules.c
# This archive created: Fri Apr 24 15:24:37 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > Makefile.AZTEC
# Makefile for make (Amiga Aztec version)

CFLAGS=-Damiga -Daztec

OBJS = main.o make.o rules.o reader.o input.o macro.o check.o

m : $(OBJS)
	ln -o m $(OBJS) -lc
SHAR_EOF
cat << \SHAR_EOF > README
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)
SHAR_EOF
cat << \SHAR_EOF > README.amiga
This program is the public domain Make program which appeared on
mod.sources, Volume 7, number 91.  I have ported it to the Amiga under
Manx Aztec C version 3.40a.  This short document assumes you know
Make, and simply points out the Amiga specific features of the
program.  The supported switches are listed in the comment block at
the beginning of module main.c.  I offer no apologies for the fact that
I ran the code through "indent -i4 -nfc1" on the 4.3BSD system at work
before I started working on it.
   The program has been compiled under Manx Aztec C Version 3.40.
It uses Manx's fexecv() function to execute commands and get
their return value, and the Manx dos_packet() function to send an
AmigaDOS packet to the file system for touch'ing purposes.  Lattice
recompilers need to change these (at least).  Peculiar features
of the Amiga version are:

(1) The Amiga-specific sections of the code are #ifdef'd on the symbol
    amiga (note the lower case).  I endorse Fred Fish's effort to
    have system and compiler-supplied #define's in lower case to
    ensure no collisions with user-supplied ones.
(2) The file rules.c, routine makerules(), contains the definitions of
    the default built-in rules. For the Amiga, these are equivalent to
    the Makefile:

	CC = cc
	CFLAGS = 
	AS = as
	
	.c.o:
		$(CC) $(CFLAGS) $<
	.asm.o:
		$(AS) -o $@ $<

    (indented for clarity only).  Thus, one could conceivably do:
    	make CC=lc CFLAGS= AS=asm
    to run this make under Lattice C.
(3) If the file S:builtins.make exists, its contents are read in
    instead of the built-in rules defined in makerules().  Thus, you
    can use different default rules on different disks and change
    the default rules without recompiling make.
(4) A Control-D typed during execution of a command by make will cause
    an abort with exit status 1 AFTER the completion of that command.
    Control-C is an immediate abort, as you might expect.
(5) Not really Amiga specific, but worth mentioning, is that characters
    special both to the local operating system (such as : in AmigaDOS) and
    to make may be used in the makefile by preceding them with \
    to temporarily override their special meaning.  For example, to tell
    make that RAM:foo depends on AC:foo.c, write:

    	RAM\:foo : AC\:foo.c

(6) The Aztec fexecv() function, which is used by make to execute its
    commands, only works on programs in directories stored along the
    AmigaDOS PATH, so make sure your PATH includes the appropriate
    directories.

    Finally, I added one new feature in the non-machine-specific code:
the name of the makefile can be a single dash "-", in which case a
makefile is read from the standard input.
    About the only feature of "real" make missing here is the
semicolon construct which allows a pair of lines such as the .c.o:
rule and command above to be written as one line, viz:
	.c.o: ; $(CC) $(CFLAGS) $<

Enjoy!  Bug reports in the Amiga-specific stuff should be directed to
me;  others should go to caret@fairlight.OZ, the author of the rest of it.
By the way, this code is superior to the Manx-supplied make--more switches
and a better parser;  in fact, this make will handle the Makefile for
MicroGnuEmacs while Manx make chokes on the ln command.

				Steve Walton
				ametek!walton@csvax.caltech.edu (ARPA)
				WALTON@CALTECH (BITNET)
				...!ucbvax!sun!megatest!ametek!walton

SHAR_EOF
cat << \SHAR_EOF > 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) {
	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;
}
SHAR_EOF
cat << \SHAR_EOF > h.h
/*
 *	Include header for make
 */


#ifndef uchar
#ifdef os9
#define uchar		char
#define void		int
#define fputc		putc
#else
#define uchar		unsigned char
#endif
#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  */
#ifdef unix
#define DEFN2		"Makefile"
#endif
#ifdef eon
#define DEFN2		"Makefile"
#endif
/* os9 is case insensitive */

#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 */
#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 */
};

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();
SHAR_EOF
cat << \SHAR_EOF > input.c
/*
 *	Parse a makefile
 */


#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 (struct cmd *) 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	    flag;
{
    bool            hascmds = FALSE;	/* Target has commands  */
    register struct line *rp;
    register struct line *rrp;


    /* Handle the .SUFFIXES case */
    if (np->n_name[0] == '.' && !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, getline();


    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);

	    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);
    }
}
SHAR_EOF
cat << \SHAR_EOF > 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((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;

    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 == '(')
		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);
}
SHAR_EOF
cat << \SHAR_EOF > 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 (EON only)
 */

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

#ifdef unix
#include <sys/errno.h>
#endif
#ifdef eon
#include <sys/err.h>
#endif
#ifdef os9
#include <errno.h>
#endif
#ifdef amiga
#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;
    void            prt(), circh();


    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;
#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;
	    default:		/* Wrong option  */
		usage();
	    }
	}
end_of_args:;
    }

#ifdef amiga
    if ((ifd = fopen("s:builtins.make", "r")) != (FILE *) 0) {
	input(ifd);
	fclose(ifd);
    } else
#endif
    makerules();

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

    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
#ifdef amiga
	if (errno != ENOENT)
	    fatal("Can't open %s; error %02x", DEFN1, errno);
#endif
#ifdef DEFN2
	if ((ifd == (FILE *) 0)
	    && ((ifd = fopen(DEFN2, "r")) == (FILE *) 0))
	    fatal("Can't open %s", DEFN2);
#else
	else
	    fatal("Can't open %s", DEFN1);
#endif
    } else if (strcmp(makefile, "-") == 0)	/* Can use stdin as makefile  */
	ifd = stdin;
    else if ((ifd = fopen(makefile, "r")) == (FILE *) 0)
	fatal("Can't open %s", makefile);

    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 (!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, a4, a5, a6)
    char           *msg;
{
    fprintf(stderr, "%s: ", myname);
    fprintf(stderr, msg, a1, a2, a3, a4, a5, a6);
    fputc('\n', stderr);
    exit(1);
}
SHAR_EOF
cat << \SHAR_EOF > make.c
/*
 *	Do the actual making for make
 */

#include <stdio.h>
#ifdef unix
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/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
#ifdef amiga
#include <ctype.h>
#include <errno.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/ports.h>
#include <functions.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 amiga
    char           *av[3], *s, *p;
    int             i;

    p = s = malloc((unsigned) strlen(string) + 1);
    strcpy(p, string);			/* make a copy of the string */
    i = 0;
    av[0] = gettok(&p);			/* get first argument */
    av[1] = p;				/* get rest of command line */
    av[2] = NULL;
    if (fexecv(av[0], av) == -1)
	fatal("couldn't execute command '%s', error return %02x\n",
	      av[0], errno);
    number = wait();
    free(s);
    return number;
#endif

#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;
#ifdef amiga
    long SetSignal();
#endif


    if (*(shell = getmacro("SHELL")) == '\0')
#ifdef eon
	shell = ":bin/esh";
#endif
#ifdef unix
    shell = "/bin/sh";
#endif
#ifdef os9
    shell = "shell";
#endif
#ifdef amiga
    shell = NULL;
#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');

	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);
		    }
		}
	    }
#ifdef amiga
	    if ((SetSignal(0L, SIGBREAKF_CTRL_D) & SIGBREAKF_CTRL_D) != 0) {
		fputs("make: abort due to ^D\n", stdout);
		exit(1);
	    }
#endif	    
	}
    }
}


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;
    int             fd;


    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
#ifdef amiga
    struct FileInfoBlock *fib;
    struct Lock *myLock;
    long ioErr;

    fib = (struct FileInfoBlock *) malloc((unsigned) sizeof(struct FileInfoBlock));
    if ((myLock = Lock(np->n_name, ACCESS_READ)) == NULL) {
	if ((ioErr = IoErr()) != ERROR_OBJECT_NOT_FOUND)
	    fatal("Can't Lock '%s'; error %3ld", np->n_name, ioErr);
	np->n_time = 0L;
    } else if (!Examine(myLock, fib)) {
	UnLock(myLock);
	fatal("Can't Examine '%s'; error %3ld", np->n_name, IoErr());
    } else {
 	np->n_time = fib->fib_Date.ds_Tick/TICKS_PER_SECOND +
		 60*fib->fib_Date.ds_Minute + 86400*fib->fib_Date.ds_Days;
    	UnLock(myLock);
    }
    free((char *) fib);
#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];

	a[0] = a[1] = time(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
#ifdef amiga
	struct MsgPort *task;
	ULONG dateStamp[3];
	struct Lock *lock, *plock;
	UBYTE *pointer;

	if(!(pointer= (UBYTE *)AllocMem(64L,MEMF_PUBLIC)))
		fatal("Can't get 64 bytes for pointer");
	if(!(task=(struct MsgPort *)DeviceProc(np->n_name))) {
		printf("%s: can't get MsgPort for '%s'\n", myname, np->n_name);
		FreeMem((void *) pointer, 64L);
		return;
	}
	if(!(lock = Lock(np->n_name,SHARED_LOCK))) {
		printf("%s: '%s' not touched - non-existant\n",
			myname, np->n_name);
		FreeMem((void *) pointer, 64L);
		return;
	}
	plock = ParentDir(lock);
	UnLock(lock);

	strcpy((pointer + 1),np->n_name);
	*pointer = strlen(np->n_name);
 
	dos_packet(task, ACTION_SET_DATE, NULL, plock, (ULONG) &pointer[0] >> 2,
		(ULONG) DateStamp(dateStamp), 0L, 0L, 0L);

	UnLock(plock);
	FreeMem((void *) pointer, 64L);
#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, time();
    bool            didsomething = 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);
    }
    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) {
	    make(dp->d_name, level + 1);
	    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;


    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(dp);
	}
	setmacro("?", str1);
	setmacro("@", np->n_name);
	if (lp)			/* lp set if doing a :: rule */
	    docmds1(np, lp);
	else
	    docmds(np);
    }
}
#ifdef amiga
/*
 * Replace the Aztec-provided time function with one which returns something
 * easy to find and compare, namely the number of seconds since the Amiga's
 * reference date.  This is the same thing returned by modtime() above.
 */
time_t
time(v)
    time_t *v;
{
    long t[3];

    DateStamp(t);
    t[0] = t[2]/TICKS_PER_SECOND + 60*t[1] + 86400*t[0];
    if (v)
	*v = t[0];
    return t[0];
}
#endif
SHAR_EOF
cat << \SHAR_EOF > reader.c
/*
 *	Read in makefile
 */


#include <stdio.h>
#include	<ctype.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, " 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);
}
SHAR_EOF
cat << \SHAR_EOF > rules.c
/*
 *	Control of the implicit suffix rules
 */


#include "h.h"
#ifndef NULL
#define NULL ((void *) 0)
#endif

/*
 * 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;
    void	    modtime();


    p = str1;
    q = np->n_name;
    if (!(suff = suffix(q)))
	return FALSE;		/* No suffix */
    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, NULL);
		    newline(np, dp, sp->n_line->l_cmd, 0);
		    setmacro("<", op->n_name);
		    return TRUE;
		}
	    }
	}
    return FALSE;
}


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


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

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

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

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

    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, 0);
#endif

/*
 *	Some of the UNIX implicit rules
 */
#ifdef unix
    setmacro("CC", "cc");
    setmacro("CFLAGS", "-O");
    cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
    np = newname(".c.o");
    newline(np, 0, cp, 0);

    setmacro("AS", "as");
    cp = newcmd("$(AS) -o $@ $<", 0);
    np = newname(".s.o");
    newline(np, 0, cp, 0);

    setmacro("YACC", "yacc");
    /* setmacro("YFLAGS", "");	 */
    cp = newcmd("$(YACC) $(YFLAGS) $<", 0);
    cp = newcmd("mv y.tab.c $@", cp);
    np = newname(".y.c");
    newline(np, 0, cp, 0);

    cp = newcmd("$(YACC) $(YFLAGS) $<", 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, 0, cp, 0);

    np = newname(".s");
    dp = newdep(np, 0);
    np = newname(".o");
    dp = newdep(np, dp);
    np = newname(".c");
    dp = newdep(np, dp);
    np = newname(".y");
    dp = newdep(np, dp);
    np = newname(".SUFFIXES");
    newline(np, dp, 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");
    setmacro("CFLAGS", "-z");
    cp = newcmd("$(CC) $(CFLAGS) -r $<", 0);

    np = newname(".c.r");
    newline(np, 0, cp, 0);
    np = newname(".ca.r");
    newline(np, 0, cp, 0);
    np = newname(".a.r");
    newline(np, 0, cp, 0);
    np = newname(".o.r");
    newline(np, 0, cp, 0);
    np = newname(".mc.r");
    newline(np, 0, cp, 0);
    np = newname(".mca.r");
    newline(np, 0, cp, 0);
    np = newname(".ma.r");
    newline(np, 0, cp, 0);
    np = newname(".mo.r");
    newline(np, 0, cp, 0);

    np = newname(".r");
    dp = newdep(np, 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, 0, 0);
#endif
#ifdef amiga
    setmacro("CC", "cc");
    setmacro("CFLAGS", "");
    cp = newcmd("$(CC) $(CFLAGS) $<", NULL);
    np = newname(".c.o");
    newline(np, NULL, cp, 0);

    setmacro("AS", "as");
    cp = newcmd("$(AS) -o $@ $<", NULL);
    np = newname(".s.o");
    newline(np, NULL, cp, NULL);

    np = newname(".o");
    dp = newdep(np, NULL);
    np = newname(".s");
    dp = newdep(np, dp);
    np = newname(".c");
    dp = newdep(np, dp);
    np = newname(".SUFFIXES");
    newline(np, dp, NULL, 0);
#endif
}
SHAR_EOF
#	End of shell archive
exit 0