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, ®s) == -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