beattie@netsys.UUCP (Brian Beattie) (02/27/88)
enclosed is a shar file for a make that is, I believe is
compatible with v7 make. It comes from mod.sources. I have
been useing it for about a week. It seems to fix the problems
that have been reported with minix.
--------------------- cut-here ---------------------------
echo x - Makefile
sed 's/^X//' > Makefile << '/'
X# Makefile for make!
X
XCFLAGS = -Dunix -DMINIX
X
XOBJS = check.s input.s macro.s main.s \
X make.s reader.s rules.s
X
Xm: $(OBJS)
X cc -o m $(OBJS)
X
Xcheck.s: h.h check.c
X $(CC) -S $(CFLAGS) check.c
X
Xinput.s: h.h input.c
X $(CC) -S $(CFLAGS) input.c
X
Xmacro.s: h.h macro.c
X $(CC) -S $(CFLAGS) macro.c
X
Xmain.s: h.h main.c
X $(CC) -S $(CFLAGS) main.c
X
Xmake.s: h.h make.c
X $(CC) -S $(CFLAGS) make.c
X
Xreader.s: h.h reader.c
X $(CC) -S $(CFLAGS) reader.c
X
Xrules.s: h.h rules.c
X $(CC) -S $(CFLAGS) rules.c
/
echo x - ReadMe
sed 's/^X//' > ReadMe << '/'
XFollowing is a repost of the public domain 'make' that I posted
Xto net.sources a couple of months ago. I have fixed a few bugs, and
Xadded some more features, and the resulting changes amounted to
Xabout as much text as the whole program (hence the repost).
X
XFor those that missed the net.sources posting, this is a public domain
Xre-implementation of the UNIX make program. There is no manual included;
Xfor documentation, refer to a UNIX manual, or the source.
X
XHere is a list of the changes made:
X
Xi) If '-' (ignore) or '@' (silent) where used at the start
X of a command, their effect was not turned off for the following
X commands.
Xii) A special target (.SUFFIXES, .PRECIOUS) or a rule (.c.o, .a.o),
X if first in the file would be taken as the default target.
X This resulted in error messages like "Don't know how to
X make .c", because things like .SUFFIXES were being made.
X This was further complicated by ---
Xiii) Special target lines with no dependents (ie. .SUFFIXES:\n)
X were not clearing out the existing dependents like
X they should.
Xiv) Default rules could not be redefined because of the error
X checking for commands being defined twice. Now you are
X allowed to define a target beinging with '.', having
X no dependents with commands.
Xv) The -q option didn't do the time comparison correctly,
X or clear the variable used to keep track of this. Thus
X it didn't work very well.
Xvi) The syntax ${..} for macro's supported by UNIX make was
X not supported.
Xvii) There wuz a couple of spelling errors.
Xviii) When make checked for implicit rules on targets without
X a suffix, there were problems. (Note: The ~ feature of
X UNIX make wasn't and still isn't supported)
Xix) The -n option did not print @ lines like it was supposed to.
Xx) :: added. (See UNIX manual)
Xxi) $? added. (see UNIX manual)
/
echo x - h.h
sed 's/^X//' > h.h << '/'
X/*
X * Include header for make
X */
X
X
X#ifndef uchar
X#ifdef os9
X#define uchar char
X#define void int
X#define fputc putc
X#else
X#define uchar unsigned char
X#endif
X#endif
X
X#define bool uchar
X#define time_t long
X#define TRUE (1)
X#define FALSE (0)
X#define max(a,b) ((a)>(b)?(a):(b))
X
X#define DEFN1 "makefile" /* Default names */
X#ifdef unix
X#define DEFN2 "Makefile"
X#endif
X#ifdef eon
X#define DEFN2 "Makefile"
X#endif
X/* os9 is case insensitive */
X
X#define LZ (1024) /* Line size */
X
X
X
X/*
X * A name. This represents a file, either to be made, or existant
X */
X
Xstruct name
X{
X struct name * n_next; /* Next in the list of names */
X char * n_name; /* Called */
X struct line * n_line; /* Dependencies */
X time_t n_time; /* Modify time of this name */
X uchar n_flag; /* Info about the name */
X};
X
X#define N_MARK 0x01 /* For cycle check */
X#define N_DONE 0x02 /* Name looked at */
X#define N_TARG 0x04 /* Name is a target */
X#define N_PREC 0x08 /* Target is precious */
X#define N_DOUBLE 0x10 /* Double colon target */
X
X/*
X * Definition of a target line.
X */
Xstruct line
X{
X struct line * l_next; /* Next line (for ::) */
X struct depend * l_dep; /* Dependents for this line */
X struct cmd * l_cmd; /* Commands for this line */
X};
X
X
X/*
X * List of dependents for a line
X */
Xstruct depend
X{
X struct depend * d_next; /* Next dependent */
X struct name * d_name; /* Name of dependent */
X};
X
X
X/*
X * Commands for a line
X */
Xstruct cmd
X{
X struct cmd * c_next; /* Next command line */
X char * c_cmd; /* Command line */
X};
X
X
X/*
X * Macro storage
X */
Xstruct macro
X{
X struct macro * m_next; /* Next variable */
X char * m_name; /* Called ... */
X char * m_val; /* Its value */
X uchar m_flag; /* Infinite loop check */
X};
X
Xextern char * myname;
Xextern struct name namehead;
Xextern struct macro * macrohead;
Xextern struct name * firstname;
Xextern bool silent;
Xextern bool ignore;
Xextern bool rules;
Xextern bool dotouch;
Xextern bool quest;
Xextern bool domake;
Xextern char str1[];
Xextern char str2[];
Xextern int lineno;
X
Xchar * fgets();
Xchar * index();
Xchar * rindex();
Xchar * malloc();
Xextern int errno;
X
Xchar * getmacro();
Xstruct macro * setmacro();
Xvoid input();
Xvoid error();
Xvoid fatal();
Xint make();
Xstruct name * newname();
Xstruct depend * newdep();
Xstruct cmd * newcmd();
Xvoid newline();
Xchar * suffix();
Xvoid touch();
Xvoid makerules();
Xchar * gettok();
Xvoid precious();
/
echo x - check.c
sed 's/^X//' > check.c << '/'
X/*
X * Check structures for make.
X */
X
X#include <stdio.h>
X#include "h.h"
X
X
X/*
X * Prints out the structures as defined in memory. Good for check
X * that you make file does what you want (and for debugging make).
X */
Xvoid
Xprt()
X{
X register struct name * np;
X register struct depend * dp;
X register struct line * lp;
X register struct cmd * cp;
X register struct macro * mp;
X
X
X for (mp = macrohead; mp; mp = mp->m_next)
X fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val);
X
X fputc('\n', stderr);
X
X for (np = namehead.n_next; np; np = np->n_next)
X {
X if (np->n_flag & N_DOUBLE)
X fprintf(stderr, "%s::\n", np->n_name);
X else
X fprintf(stderr, "%s:\n", np->n_name);
X if (np == firstname)
X fprintf(stderr, "(MAIN NAME)\n");
X for (lp = np->n_line; lp; lp = lp->l_next)
X {
X fputc(':', stderr);
X for (dp = lp->l_dep; dp; dp = dp->d_next)
X fprintf(stderr, " %s", dp->d_name->n_name);
X fputc('\n', stderr);
X
X for (cp = lp->l_cmd; cp; cp = cp->c_next)
X#ifdef os9
X fprintf(stderr, "- %s\n", cp->c_cmd);
X#else
X fprintf(stderr, "-\t%s\n", cp->c_cmd);
X#endif
X fputc('\n', stderr);
X }
X fputc('\n', stderr);
X }
X}
X
X
X/*
X * Recursive routine that does the actual checking.
X */
Xvoid
Xcheck(np)
Xstruct name * np;
X{
X register struct depend * dp;
X register struct line * lp;
X
X
X if (np->n_flag & N_MARK)
X fatal("Circular dependency from %s", np->n_name);
X
X np->n_flag |= N_MARK;
X
X for (lp = np->n_line; lp; lp = lp->l_next)
X for (dp = lp->l_dep; dp; dp = dp->d_next)
X check(dp->d_name);
X
X np->n_flag &= ~N_MARK;
X}
X
X
X/*
X * Look for circular dependancies.
X * ie.
X * a: b
X * b: a
X * is a circular dep
X */
Xvoid
Xcirch()
X{
X register struct name * np;
X
X
X for (np = namehead.n_next; np; np = np->n_next)
X check(np);
X}
X
X
X/*
X * Check the target .PRECIOUS, and mark its dependentd as precious
X */
Xvoid
Xprecious()
X{
X register struct depend * dp;
X register struct line * lp;
X register struct name * np;
X
X
X if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG))
X return;
X
X for (lp = np->n_line; lp; lp = lp->l_next)
X for (dp = lp->l_dep; dp; dp = dp->d_next)
X dp->d_name->n_flag |= N_PREC;
X}
/
echo x - input.c
sed 's/^X//' > input.c << '/'
X/*
X * Parse a makefile
X */
X
X
X#include <stdio.h>
X#include <ctype.h>
X#include "h.h"
X
X
Xstruct name namehead;
Xstruct name * firstname;
X
Xchar str1[LZ]; /* General store */
Xchar str2[LZ];
X
X
X/*
X * Intern a name. Return a pointer to the name struct
X */
Xstruct name *
Xnewname(name)
Xchar * name;
X{
X register struct name * rp;
X register struct name * rrp;
X register char * cp;
X
X
X for
X (
X rp = namehead.n_next, rrp = &namehead;
X rp;
X rp = rp->n_next, rrp = rrp->n_next
X )
X if (strcmp(name, rp->n_name) == 0)
X return rp;
X
X if ((rp = (struct name *)malloc(sizeof (struct name)))
X == (struct name *)0)
X fatal("No memory for name");
X rrp->n_next = rp;
X rp->n_next = (struct name *)0;
X if ((cp = malloc(strlen(name)+1)) == (char *)0)
X fatal("No memory for name");
X strcpy(cp, name);
X rp->n_name = cp;
X rp->n_line = (struct line *)0;
X rp->n_time = (time_t)0;
X rp->n_flag = 0;
X
X return rp;
X}
X
X
X/*
X * Add a dependant to the end of the supplied list of dependants.
X * Return the new head pointer for that list.
X */
Xstruct depend *
Xnewdep(np, dp)
Xstruct name * np;
Xstruct depend * dp;
X{
X register struct depend * rp;
X register struct depend * rrp;
X
X
X if ((rp = (struct depend *)malloc(sizeof (struct depend)))
X == (struct depend *)0)
X fatal("No memory for dependant");
X rp->d_next = (struct depend *)0;
X rp->d_name = np;
X
X if (dp == (struct depend *)0)
X return rp;
X
X for (rrp = dp; rrp->d_next; rrp = rrp->d_next)
X ;
X
X rrp->d_next = rp;
X
X return dp;
X}
X
X
X/*
X * Add a command to the end of the supplied list of commands.
X * Return the new head pointer for that list.
X */
Xstruct cmd *
Xnewcmd(str, cp)
Xchar * str;
Xstruct cmd * cp;
X{
X register struct cmd * rp;
X register struct cmd * rrp;
X register char * rcp;
X
X
X if (rcp = rindex(str, '\n'))
X *rcp = '\0'; /* Loose newline */
X
X while (isspace(*str))
X str++;
X
X if (*str == '\0') /* If nothing left, the exit */
X return;
X
X if ((rp = (struct cmd *)malloc(sizeof (struct cmd)))
X == (struct cmd *)0)
X fatal("No memory for command");
X rp->c_next = (struct cmd *)0;
X if ((rcp = malloc(strlen(str)+1)) == (char *)0)
X fatal("No memory for command");
X strcpy(rcp, str);
X rp->c_cmd = rcp;
X
X if (cp == (struct cmd *)0)
X return rp;
X
X for (rrp = cp; rrp->c_next; rrp = rrp->c_next)
X ;
X
X rrp->c_next = rp;
X
X return cp;
X}
X
X
X/*
X * Add a new 'line' of stuff to a target. This check to see
X * if commands already exist for the target. If flag is set,
X * the line is a double colon target.
X *
X * Kludges:
X * i) If the new name begins with a '.', and there are no dependents,
X * then the target must cease to be a target. This is for .SUFFIXES.
X * ii) If the new name begins with a '.', with no dependents and has
X * commands, then replace the current commands. This is for
X * redefining commands for a default rule.
X * Neither of these free the space used by dependents or commands,
X * since they could be used by another target.
X */
Xvoid
Xnewline(np, dp, cp, flag)
Xstruct name * np;
Xstruct depend * dp;
Xstruct cmd * cp;
X{
X bool hascmds = FALSE; /* Target has commands */
X register struct line * rp;
X register struct line * rrp;
X
X
X /* Handle the .SUFFIXES case */
X if (np->n_name[0] == '.' && !dp && !cp)
X {
X for (rp = np->n_line; rp; rp = rrp)
X {
X rrp = rp->l_next;
X free(rp);
X }
X np->n_line = (struct line *)0;
X np->n_flag &= ~N_TARG;
X return;
X }
X
X /* This loop must happen since rrp is used later. */
X for
X (
X rp = np->n_line, rrp = (struct line *)0;
X rp;
X rrp = rp, rp = rp->l_next
X )
X if (rp->l_cmd)
X hascmds = TRUE;
X
X if (hascmds && cp && !(np->n_flag & N_DOUBLE))
X /* Handle the implicit rules redefinition case */
X if (np->n_name[0] == '.' && dp == (struct depend *)0)
X {
X np->n_line->l_cmd = cp;
X return;
X }
X else
X error("Commands defined twice for target %s", np->n_name);
X if (np->n_flag & N_TARG)
X if (!(np->n_flag & N_DOUBLE) != !flag) /* like xor */
X error("Inconsistent rules for target %s", np->n_name);
X
X if ((rp = (struct line *)malloc(sizeof (struct line)))
X == (struct line *)0)
X fatal("No memory for line");
X rp->l_next = (struct line *)0;
X rp->l_dep = dp;
X rp->l_cmd = cp;
X
X if (rrp)
X rrp->l_next = rp;
X else
X np->n_line = rp;
X
X np->n_flag |= N_TARG;
X if (flag)
X np->n_flag |= N_DOUBLE;
X}
X
X
X/*
X * Parse input from the makefile, and construct a tree structure
X * of it.
X */
Xvoid
Xinput(fd)
XFILE * fd;
X{
X char * p; /* General */
X char * q;
X struct name * np;
X struct depend * dp;
X struct cmd * cp;
X bool dbl;
X
X
X if (getline(str1, fd)) /* Read the first line */
X return;
X
X for(;;)
X {
X#ifdef os9
X if (*str1 == ' ') /* Rules without targets */
X#else
X if (*str1 == '\t') /* Rules without targets */
X#endif
X error("Rules not allowed here");
X
X p = str1;
X
X while (isspace(*p)) /* Find first target */
X p++;
X
X while (((q = index(p, '=')) != (char *)0) &&
X (p != q) && (q[-1] == '\\')) /* Find value */
X {
X register char * a;
X
X a = q - 1; /* Del \ chr; move rest back */
X p = q;
X while(*a++ = *q++)
X ;
X }
X
X if (q != (char *)0)
X {
X register char * a;
X
X *q++ = '\0'; /* Separate name and val */
X while (isspace(*q))
X q++;
X if (p = rindex(q, '\n'))
X *p = '\0';
X
X p = str1;
X if ((a = gettok(&p)) == (char *)0)
X error("No macro name");
X
X setmacro(a, q);
X
X if (getline(str1, fd))
X return;
X continue;
X }
X
X expand(str1);
X p = str1;
X
X while (((q = index(p, ':')) != (char *)0) &&
X (p != q) && (q[-1] == '\\')) /* Find dependents */
X {
X register char * a;
X
X a = q - 1; /* Del \ chr; move rest back */
X p = q;
X while(*a++ = *q++)
X ;
X }
X
X if (q == (char *)0)
X error("No targets provided");
X
X *q++ = '\0'; /* Separate targets and dependents */
X
X if (*q == ':') /* Double colon */
X {
X dbl = 1;
X q++;
X }
X else
X dbl = 0;
X
X for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);)
X /* get list of dep's */
X {
X np = newname(p); /* Intern name */
X dp = newdep(np, dp); /* Add to dep list */
X }
X
X *((q = str1) + strlen(str1) + 1) = '\0';
X /* Need two nulls for gettok (Remember separation) */
X
X cp = (struct cmd *)0;
X if (getline(str2, fd) == FALSE) /* Get commands */
X {
X#ifdef os9
X while (*str2 == ' ')
X#else
X while (*str2 == '\t')
X#endif
X {
X cp = newcmd(&str2[0], cp);
X if (getline(str2, fd))
X break;
X }
X }
X
X while ((p = gettok(&q)) != (char *)0) /* Get list of targ's */
X {
X np = newname(p); /* Intern name */
X newline(np, dp, cp, dbl);
X if (!firstname && p[0] != '.')
X firstname = np;
X }
X
X if (feof(fd)) /* EOF? */
X return;
X
X strcpy(str1, str2);
X }
X}
/
echo x - macro.c
sed 's/^X//' > macro.c << '/'
X/*
X * Macro control for make
X */
X
X
X#include "h.h"
X
X
Xstruct macro * macrohead;
X
X
Xstruct macro *
Xgetmp(name)
Xchar * name;
X{
X register struct macro * rp;
X
X for (rp = macrohead; rp; rp = rp->m_next)
X if (strcmp(name, rp->m_name) == 0)
X return rp;
X return (struct macro *)0;
X}
X
X
Xchar *
Xgetmacro(name)
Xchar * name;
X{
X struct macro * mp;
X
X if (mp = getmp(name))
X return mp->m_val;
X else
X return "";
X}
X
X
Xstruct macro *
Xsetmacro(name, val)
Xchar * name;
Xchar * val;
X{
X register struct macro * rp;
X register char * cp;
X
X
X /* Replace macro definition if it exists */
X for (rp = macrohead; rp; rp = rp->m_next)
X if (strcmp(name, rp->m_name) == 0)
X {
X free(rp->m_val); /* Free space from old */
X break;
X }
X
X if (!rp) /* If not defined, allocate space for new */
X {
X if ((rp = (struct macro *)malloc(sizeof (struct macro)))
X == (struct macro *)0)
X fatal("No memory for macro");
X
X rp->m_next = macrohead;
X macrohead = rp;
X rp->m_flag = FALSE;
X
X if ((cp = malloc(strlen(name)+1)) == (char *)0)
X fatal("No memory for macro");
X strcpy(cp, name);
X rp->m_name = cp;
X }
X
X if ((cp = malloc(strlen(val)+1)) == (char *)0)
X fatal("No memory for macro");
X strcpy(cp, val); /* Copy in new value */
X rp->m_val = cp;
X
X return rp;
X}
X
X
X/*
X * Do the dirty work for expand
X */
Xvoid
Xdoexp(to, from, len, buf)
Xchar ** to;
Xchar * from;
Xint * len;
Xchar * buf;
X{
X register char * rp;
X register char * p;
X register char * q;
X register struct macro * mp;
X
X
X rp = from;
X p = *to;
X while (*rp)
X {
X if (*rp != '$')
X {
X *p++ = *rp++;
X (*len)--;
X }
X else
X {
X q = buf;
X if (*++rp == '{')
X while (*++rp && *rp != '}')
X *q++ = *rp;
X else if (*rp == '(')
X while (*++rp && *rp != ')')
X *q++ = *rp;
X else if (!*rp)
X {
X *p++ = '$';
X break;
X }
X else
X *q++ = *rp;
X *q = '\0';
X if (*rp)
X rp++;
X if (!(mp = getmp(buf)))
X mp = setmacro(buf, "");
X if (mp->m_flag)
X fatal("Infinitely recursive macro %s", mp->m_name);
X mp->m_flag = TRUE;
X *to = p;
X doexp(to, mp->m_val, len, buf);
X p = *to;
X mp->m_flag = FALSE;
X }
X if (*len <= 0)
X error("Expanded line too line");
X }
X *p = '\0';
X *to = p;
X}
X
X
X/*
X * Expand any macros in str.
X */
Xvoid
Xexpand(str)
Xchar * str;
X{
X static char a[LZ];
X static char b[LZ];
X char * p = str;
X int len = LZ-1;
X
X strcpy(a, str);
X doexp(&p, a, &len, b);
X}
/
echo x - main.c
sed 's/^X//' > main.c << '/'
X/*
X * make [-f makefile] [-ins] [target(s) ...]
X *
X * (Better than EON mk but not quite as good as UNIX make)
X *
X * -f makefile name
X * -i ignore exit status
X * -n Pretend to make
X * -p Print all macros & targets
X * -q Question up-to-dateness of target. Return exit status 1 if not
X * -r Don't not use inbuilt rules
X * -s Make silently
X * -t Touch files instead of making them
X * -m Change memory requirements (EON only)
X */
X
X#include <stdio.h>
X#include "h.h"
X
X#ifdef unix
X#include <sys/errno.h>
X#endif
X#ifdef eon
X#include <sys/err.h>
X#endif
X#ifdef os9
X#include <errno.h>
X#endif
X
X
X#ifdef eon
X#define MEMSPACE (16384)
X#endif
X
X
Xchar * myname;
Xchar * makefile; /* The make file */
X#ifdef eon
Xunsigned memspace = MEMSPACE;
X#endif
X
XFILE * ifd; /* Input file desciptor */
Xbool domake = TRUE; /* Go through the motions option */
Xbool ignore = FALSE; /* Ignore exit status option */
Xbool silent = FALSE; /* Silent option */
Xbool print = FALSE; /* Print debuging information */
Xbool rules = TRUE; /* Use inbuilt rules */
Xbool dotouch = FALSE;/* Touch files instead of making */
Xbool quest = FALSE; /* Question up-to-dateness of file */
X
X
Xvoid
Xmain(argc, argv)
Xint argc;
Xchar ** argv;
X{
X register char * p; /* For argument processing */
X int estat = 0; /* For question */
X register struct name * np;
X
X
X myname = (argc-- < 1) ? "make" : *argv++;
X
X while ((argc > 0) && (**argv == '-'))
X {
X argc--; /* One less to process */
X p = *argv++; /* Now processing this one */
X
X while (*++p != '\0')
X {
X switch(*p)
X {
X case 'f': /* Alternate file name */
X if (*++p == '\0')
X {
X if (argc-- <= 0)
X usage();
X p = *argv++;
X }
X makefile = p;
X goto end_of_args;
X#ifdef eon
X case 'm': /* Change space requirements */
X if (*++p == '\0')
X {
X if (argc-- <= 0)
X usage();
X p = *argv++;
X }
X memspace = atoi(p);
X goto end_of_args;
X#endif
X case 'n': /* Pretend mode */
X domake = FALSE;
X break;
X case 'i': /* Ignore fault mode */
X ignore = TRUE;
X break;
X case 's': /* Silent about commands */
X silent = TRUE;
X break;
X case 'p':
X print = TRUE;
X break;
X case 'r':
X rules = FALSE;
X break;
X case 't':
X dotouch = TRUE;
X break;
X case 'q':
X quest = TRUE;
X break;
X default: /* Wrong option */
X usage();
X }
X }
X end_of_args:;
X }
X
X#ifdef eon
X if (initalloc(memspace) == 0xffff) /* Must get memory for alloc */
X fatal("Cannot initalloc memory");
X#endif
X
X if (strcmp(makefile, "-") == 0) /* Can use stdin as makefile */
X ifd = stdin;
X else
X if (!makefile) /* If no file, then use default */
X {
X if ((ifd = fopen(DEFN1, "r")) == (FILE *)0)
X#ifdef eon
X if (errno != ER_NOTF)
X fatal("Can't open %s; error %02x", DEFN1, errno);
X#endif
X#ifdef unix
X if (errno != ENOENT)
X fatal("Can't open %s; error %02x", DEFN1, errno);
X#endif
X#ifndef os9
X if ((ifd == (FILE *)0)
X && ((ifd = fopen(DEFN2, "r")) == (FILE *)0))
X fatal("Can't open %s", DEFN2);
X#else
X fatal("Can't open %s", DEFN1);
X#endif
X }
X else
X if ((ifd = fopen(makefile, "r")) == (FILE *)0)
X fatal("Can't open %s", makefile);
X
X makerules();
X
X setmacro("$", "$");
X
X while (argc && (p = index(*argv, '=')))
X {
X char c;
X
X c = *p;
X *p = '\0';
X setmacro(*argv, p+1);
X *p = c;
X
X argv++;
X argc--;
X }
X
X input(ifd); /* Input all the gunga */
X fclose(ifd); /* Finished with makefile */
X lineno = 0; /* Any calls to error now print no line number */
X
X if (print)
X prt(); /* Print out structures */
X
X np = newname(".SILENT");
X if (np->n_flag & N_TARG)
X silent = TRUE;
X
X np = newname(".IGNORE");
X if (np->n_flag & N_TARG)
X ignore = TRUE;
X
X precious();
X
X if (!firstname)
X fatal("No targets defined");
X
X circh(); /* Check circles in target definitions */
X
X if (!argc)
X estat = make(firstname, 0);
X else while (argc--)
X {
X if (!print && !silent && strcmp(*argv, "love") == 0)
X printf("Not war!\n");
X estat |= make(newname(*argv++), 0);
X }
X
X if (quest)
X exit(estat);
X else
X exit(0);
X}
X
X
Xusage()
X{
X fprintf(stderr, "Usage: %s [-f makefile] [-inpqrst] [macro=val ...] [target(s) ...]\n", myname);
X exit(1);
X}
X
X
Xvoid
Xfatal(msg, a1, a2, a3, a4, a5, a6)
Xchar *msg;
X{
X fprintf(stderr, "%s: ", myname);
X fprintf(stderr, msg, a1, a2, a3, a4, a5, a6);
X fputc('\n', stderr);
X exit(1);
X}
/
echo x - make.c
sed 's/^X//' > make.c << '/'
X/*
X * Do the actual making for make
X */
X
X#include <stdio.h>
X#ifdef unix
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/errno.h>
X#endif
X#ifdef eon
X#include <sys/stat.h>
X#include <sys/err.h>
X#endif
X#ifdef os9
X#include <time.h>
X#include <os9.h>
X#include <modes.h>
X#include <direct.h>
X#include <errno.h>
X#endif
X#include "h.h"
X
X
X
X/*
X * Exec a shell that returns exit status correctly (/bin/esh).
X * The standard EON shell returns the process number of the last
X * async command, used by the debugger (ugg).
X * [exec on eon is like a fork+exec on unix]
X */
Xint
Xdosh(string, shell)
Xchar * string;
Xchar * shell;
X{
X int number;
X
X#ifdef unix
X return system(string);
X#endif
X#ifdef eon
X return ((number = execl(shell, shell,"-c", string, 0)) == -1) ?
X -1: /* couldn't start the shell */
X wait(number); /* return its exit status */
X#endif
X#ifdef os9
X int status, pid;
X
X strcat(string, "\n");
X if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1)
X return -1; /* Couldn't start a shell */
X do
X {
X if ((pid = wait(&status)) == -1)
X return -1; /* child already died!?!? */
X } while (pid != number);
X
X return status;
X#endif
X}
X
X
X/*
X * Do commands to make a target
X */
Xvoid
Xdocmds1(np, lp)
Xstruct name * np;
Xstruct line * lp;
X{
X bool ssilent;
X bool signore;
X int estat;
X register char * q;
X register char * p;
X char * shell;
X register struct cmd * cp;
X
X
X if (*(shell = getmacro("SHELL")) == '\0')
X#ifdef eon
X shell = ":bin/esh";
X#endif
X#ifdef unix
X shell = "/bin/sh";
X#endif
X#ifdef os9
X shell = "shell";
X#endif
X
X for (cp = lp->l_cmd; cp; cp = cp->c_next)
X {
X strcpy(str1, cp->c_cmd);
X expand(str1);
X q = str1;
X ssilent = silent;
X signore = ignore;
X while ((*q == '@') || (*q == '-'))
X {
X if (*q == '@') /* Specific silent */
X ssilent = TRUE;
X else /* Specific ignore */
X signore = TRUE;
X q++; /* Not part of the command */
X }
X
X if (!domake)
X ssilent = 0;
X
X if (!ssilent)
X fputs(" ", stdout);
X
X for (p=q; *p; p++)
X {
X if (*p == '\n' && p[1] != '\0')
X {
X *p = ' ';
X if (!ssilent)
X fputs("\\\n", stdout);
X }
X else if (!ssilent)
X putchar(*p);
X }
X if (!ssilent)
X printf("\n");
X
X if (domake)
X { /* Get the shell to execute it */
X if ((estat = dosh(q, shell)) != 0)
X {
X if (estat == -1)
X fatal("Couldn't execute %s", shell);
X else
X {
X printf("%s: Error code %d", myname, estat);
X if (signore)
X fputs(" (Ignored)\n", stdout);
X else
X {
X putchar('\n');
X if (!(np->n_flag & N_PREC))
X if (unlink(np->n_name) == 0)
X printf("%s: '%s' removed.\n", myname, np->n_name);
X exit(estat);
X }
X }
X }
X }
X }
X}
X
X
Xdocmds(np)
Xstruct name * np;
X{
X register struct line * lp;
X
X
X for (lp = np->n_line; lp; lp = lp->l_next)
X docmds1(np, lp);
X}
X
X
X#ifdef os9
X/*
X * Some stuffing around to get the modified time of a file
X * in an os9 file system
X */
Xgetmdate(fd, tbp)
Xstruct sgtbuf * tbp;
X{
X struct registers regs;
X static struct fildes fdbuf;
X
X
X regs.rg_a = fd;
X regs.rg_b = SS_FD;
X regs.rg_x = &fdbuf;
X regs.rg_y = sizeof (fdbuf);
X
X if (_os9(I_GETSTT, ®s) == -1)
X {
X errno = regs.rg_b & 0xff;
X return -1;
X }
X if (tbp)
X {
X _strass(tbp, fdbuf.fd_date, sizeof (fdbuf.fd_date));
X tbp->t_second = 0; /* Files are only acurate to mins */
X }
X return 0;
X}
X
X
X/*
X * Kludge routine to return an aproximation of how many
X * seconds since 1980. Dates will be in order, but will not
X * be lineer
X */
Xtime_t
Xcnvtime(tbp)
Xstruct sgtbuf *tbp;
X{
X long acc;
X
X
X acc = tbp->t_year - 80; /* Baseyear is 1980 */
X acc = acc * 12 + tbp->t_month;
X acc = acc * 31 + tbp->t_day;
X acc = acc * 24 + tbp->t_hour;
X acc = acc * 60 + tbp->t_minute;
X acc = acc * 60 + tbp->t_second;
X
X return acc;
X}
X
X
X/*
X * Get the current time in the internal format
X */
Xtime(tp)
Xtime_t * tp;
X{
X struct sgtbuf tbuf;
X
X
X if (getime(&tbuf) < 0)
X return -1;
X
X if (tp)
X *tp = cnvtime(&tbuf);
X
X return 0;
X}
X#endif
X
X
X/*
X * Get the modification time of a file. If the first
X * doesn't exist, it's modtime is set to 0.
X */
Xvoid
Xmodtime(np)
Xstruct name * np;
X{
X#ifdef unix
X struct stat info;
X int fd;
X
X
X if (stat(np->n_name, &info) < 0)
X {
X if (errno != ENOENT)
X fatal("Can't open %s; error %d", np->n_name, errno);
X
X np->n_time = 0L;
X }
X else
X np->n_time = info.st_mtime;
X#endif
X#ifdef eon
X struct stat info;
X int fd;
X
X
X if ((fd = open(np->n_name, 0)) < 0)
X {
X if (errno != ER_NOTF)
X fatal("Can't open %s; error %02x", np->n_name, errno);
X
X np->n_time = 0L;
X }
X else if (getstat(fd, &info) < 0)
X fatal("Can't getstat %s; error %02x", np->n_name, errno);
X else
X np->n_time = info.st_mod;
X
X close(fd);
X#endif
X#ifdef os9
X struct sgtbuf info;
X int fd;
X
X
X if ((fd = open(np->n_name, 0)) < 0)
X {
X if (errno != E_PNNF)
X fatal("Can't open %s; error %02x", np->n_name, errno);
X
X np->n_time = 0L;
X }
X else if (getmdate(fd, &info) < 0)
X fatal("Can't getstat %s; error %02x", np->n_name, errno);
X else
X np->n_time = cnvtime(&info);
X
X close(fd);
X#endif
X}
X
X
X/*
X * Update the mod time of a file to now.
X */
Xvoid
Xtouch(np)
Xstruct name * np;
X{
X char c;
X int fd;
X
X
X if (!domake || !silent)
X printf(" touch(%s)\n", np->n_name);
X
X if (domake)
X {
X#ifdef unix
X long a[2];
X
X a[0] = a[1] = time(0);
X if (utime(np->n_name, &a[0]) < 0)
X printf("%s: '%s' not touched - non-existant\n",
X myname, np->n_name);
X#endif
X#ifdef eon
X if ((fd = open(np->n_name, 0)) < 0)
X printf("%s: '%s' not touched - non-existant\n",
X myname, np->n_name);
X else
X {
X uread(fd, &c, 1, 0);
X uwrite(fd, &c, 1);
X }
X close(fd);
X#endif
X#ifdef os9
X /*
X * Strange that something almost as totally useless
X * as this is easy to do in os9!
X */
X if ((fd = open(np->n_name, S_IWRITE)) < 0)
X printf("%s: '%s' not touched - non-existant\n",
X myname, np->n_name);
X close(fd);
X#endif
X }
X}
X
X
X/*
X * Recursive routine to make a target.
X */
Xint
Xmake(np, level)
Xstruct name * np;
Xint level;
X{
X register struct depend * dp;
X register struct line * lp;
X register struct depend * qdp;
X time_t dtime = 1;
X bool didsomething = 0;
X
X
X if (np->n_flag & N_DONE)
X return 0;
X
X if (!np->n_time)
X modtime(np); /* Gets modtime of this file */
X
X if (rules)
X {
X for (lp = np->n_line; lp; lp = lp->l_next)
X if (lp->l_cmd)
X break;
X if (!lp)
X dyndep(np);
X }
X
X if (!(np->n_flag & N_TARG) && np->n_time == 0L)
X fatal("Don't know how to make %s", np->n_name);
X
X for (qdp = (struct depend *)0, lp = np->n_line; lp; lp = lp->l_next)
X {
X for (dp = lp->l_dep; dp; dp = dp->d_next)
X {
X make(dp->d_name, level+1);
X if (np->n_time < dp->d_name->n_time)
X qdp = newdep(dp->d_name, qdp);
X dtime = max(dtime, dp->d_name->n_time);
X }
X if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime))
X {
X make1(np, lp, qdp); /* free()'s qdp */
X dtime = 1;
X qdp = (struct depend *)0;
X didsomething++;
X }
X }
X
X np->n_flag |= N_DONE;
X
X if (quest)
X {
X long t;
X
X t = np->n_time;
X time(&np->n_time);
X return t < dtime;
X }
X else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE))
X {
X make1(np, (struct line *)0, qdp); /* free()'s qdp */
X time(&np->n_time);
X }
X else if (level == 0 && !didsomething)
X printf("%s: '%s' is up to date\n", myname, np->n_name);
X return 0;
X}
X
X
Xmake1(np, lp, qdp)
Xregister struct depend * qdp;
Xstruct line * lp;
Xstruct name * np;
X{
X register struct depend * dp;
X
X
X if (dotouch)
X touch(np);
X else
X {
X strcpy(str1, "");
X for (dp = qdp; dp; dp = qdp)
X {
X if (strlen(str1))
X strcat(str1, " ");
X strcat(str1, dp->d_name->n_name);
X qdp = dp->d_next;
X free(dp);
X }
X setmacro("?", str1);
X setmacro("@", np->n_name);
X if (lp) /* lp set if doing a :: rule */
X docmds1(np, lp);
X else
X docmds(np);
X }
X}
/
echo x - reader.c
sed 's/^X//' > reader.c << '/'
X/*
X * Read in makefile
X */
X
X
X#include <stdio.h>
X#include <ctype.h>
X#include "h.h"
X
X
Xint lineno;
X
X
X/*
X * Syntax error handler. Print message, with line number, and exits.
X */
Xvoid
Xerror(msg, a1, a2, a3)
Xchar * msg;
X{
X fprintf(stderr, "%s: ", myname);
X fprintf(stderr, msg, a1, a2, a3);
X if (lineno)
X fprintf(stderr, " near line %d", lineno);
X fputc('\n', stderr);
X exit(1);
X}
X
X
X/*
X * Read a line into the supplied string of length LZ. Remove
X * comments, ignore blank lines. Deal with quoted (\) #, and
X * quoted newlines. If EOF return TRUE.
X */
Xbool
Xgetline(str, fd)
Xchar * str;
XFILE * fd;
X{
X register char * p;
X char * q;
X int pos = 0;
X
X
X for (;;)
X {
X if (fgets(str+pos, LZ-pos, fd) == (char *)0)
X return TRUE; /* EOF */
X
X lineno++;
X
X if ((p = index(str+pos, '\n')) == (char *)0)
X error("Line too long");
X
X if (p[-1] == '\\')
X {
X p[-1] = '\n';
X pos = p - str;
X continue;
X }
X
X p = str;
X while (((q = index(p, '#')) != (char *)0) &&
X (p != q) && (q[-1] == '\\'))
X {
X char *a;
X
X a = q - 1; /* Del \ chr; move rest back */
X p = q;
X while (*a++ = *q++)
X ;
X }
X if (q != (char *)0)
X {
X q[0] = '\n';
X q[1] = '\0';
X }
X
X p = str;
X while (isspace(*p)) /* Checking for blank */
X p++;
X
X if (*p != '\0')
X return FALSE;
X pos = 0;
X }
X}
X
X
X/*
X * Get a word from the current line, surounded by white space.
X * return a pointer to it. String returned has no white spaces
X * in it.
X */
Xchar *
Xgettok(ptr)
Xchar **ptr;
X{
X register char * p;
X
X
X while (isspace(**ptr)) /* Skip spaces */
X (*ptr)++;
X
X if (**ptr == '\0') /* Nothing after spaces */
X return NULL;
X
X p = *ptr; /* word starts here */
X
X while ((**ptr != '\0') && (!isspace(**ptr)))
X (*ptr)++; /* Find end of word */
X
X *(*ptr)++ = '\0'; /* Terminate it */
X
X return(p);
X}
/
echo x - rules.c
sed 's/^X//' > rules.c << '/'
X/*
X * Control of the implicit suffix rules
X */
X
X
X#include "h.h"
X
X
X/*
X * Return a pointer to the suffix of a name
X */
Xchar *
Xsuffix(name)
Xchar * name;
X{
X return rindex(name, '.');
X}
X
X
X/*
X * Dynamic dependency. This routine applies the suffis rules
X * to try and find a source and a set of rules for a missing
X * target. If found, np is made into a target with the implicit
X * source name, and rules. Returns TRUE if np was made into
X * a target.
X */
Xbool
Xdyndep(np)
Xstruct name * np;
X{
X register char * p;
X register char * q;
X register char * suff; /* Old suffix */
X register char * basename; /* Name without suffix */
X struct name * op; /* New dependent */
X struct name * sp; /* Suffix */
X struct line * lp;
X struct depend * dp;
X char * newsuff;
X
X
X p = str1;
X q = np->n_name;
X if (!(suff = suffix(q)))
X return FALSE; /* No suffix */
X while (q < suff)
X *p++ = *q++;
X *p = '\0';
X basename = setmacro("*", str1)->m_val;
X
X if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG))
X return FALSE;
X
X for (lp = sp->n_line; lp; lp = lp->l_next)
X for (dp = lp->l_dep; dp; dp = dp->d_next)
X {
X newsuff = dp->d_name->n_name;
X if (strlen(suff)+strlen(newsuff)+1 >= LZ)
X fatal("Suffix rule too long");
X p = str1;
X q = newsuff;
X while (*p++ = *q++)
X ;
X p--;
X q = suff;
X while (*p++ = *q++)
X ;
X sp = newname(str1);
X if (sp->n_flag & N_TARG)
X {
X p = str1;
X q = basename;
X if (strlen(basename) + strlen(newsuff)+1 >= LZ)
X fatal("Implicit name too long");
X while (*p++ = *q++)
X ;
X p--;
X q = newsuff;
X while (*p++ = *q++)
X ;
X op = newname(str1);
X if (!op->n_time)
X modtime(op);
X if (op->n_time)
X {
X dp = newdep(op, 0);
X newline(np, dp, sp->n_line->l_cmd, 0);
X setmacro("<", op->n_name);
X return TRUE;
X }
X }
X }
X return FALSE;
X}
X
X
X/*
X * Make the default rules
X */
Xvoid
Xmakerules()
X{
X struct cmd * cp;
X struct name * np;
X struct depend * dp;
X
X
X#ifdef eon
X setmacro("BDSCC", "asm");
X /* setmacro("BDSCFLAGS", ""); */
X cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", 0);
X np = newname(".c.o");
X newline(np, 0, cp, 0);
X
X setmacro("CC", "c");
X setmacro("CFLAGS", "-O");
X cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
X np = newname(".c.obj");
X newline(np, 0, cp, 0);
X
X setmacro("M80", "asm -n");
X /* setmacro("M80FLAGS", ""); */
X cp = newcmd("$(M80) $(M80FLAGS) $<", 0);
X np = newname(".mac.o");
X newline(np, 0, cp, 0);
X
X setmacro("AS", "zas");
X /* setmacro("ASFLAGS", ""); */
X cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", 0);
X np = newname(".as.obj");
X newline(np, 0, cp, 0);
X
X np = newname(".as");
X dp = newdep(np, 0);
X np = newname(".obj");
X dp = newdep(np, dp);
X np = newname(".c");
X dp = newdep(np, dp);
X np = newname(".o");
X dp = newdep(np, dp);
X np = newname(".mac");
X dp = newdep(np, dp);
X np = newname(".SUFFIXES");
X newline(np, dp, 0, 0);
X#endif
X
X/*
X * Some of the UNIX implicit rules
X */
X#ifdef unix
X setmacro("CC", "cc");
X setmacro("CFLAGS", "-O");
X#ifdef MINIX
X cp = newcmd("$(CC) $(CFLAGS) -S $<", 0);
X np = newname(".c.s");
X#else
X cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
X np = newname(".c.o");
X#endif MINIX
X newline(np, 0, cp, 0);
X
X setmacro("AS", "as");
X cp = newcmd("$(AS) -o $@ $<", 0);
X np = newname(".s.o");
X newline(np, 0, cp, 0);
X
X setmacro("YACC", "yacc");
X /* setmacro("YFLAGS", ""); */
X cp = newcmd("$(YACC) $(YFLAGS) $<", 0);
X cp = newcmd("mv y.tab.c $@", cp);
X np = newname(".y.c");
X newline(np, 0, cp, 0);
X
X cp = newcmd("$(YACC) $(YFLAGS) $<", 0);
X cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp);
X cp = newcmd("rm y.tab.c", cp);
X cp = newcmd("mv y.tab.o $@", cp);
X np = newname(".y.o");
X newline(np, 0, cp, 0);
X
X np = newname(".s");
X dp = newdep(np, 0);
X np = newname(".o");
X dp = newdep(np, dp);
X np = newname(".c");
X dp = newdep(np, dp);
X np = newname(".y");
X dp = newdep(np, dp);
X np = newname(".SUFFIXES");
X newline(np, dp, 0, 0);
X#endif
X#ifdef os9
X/*
X * Fairlight use an enhanced version of the C sub-system.
X * They have a specialised macro pre-processor.
X */
X setmacro("CC", "cc");
X setmacro("CFLAGS", "-z");
X cp = newcmd("$(CC) $(CFLAGS) -r $<", 0);
X
X np = newname(".c.r");
X newline(np, 0, cp, 0);
X np = newname(".ca.r");
X newline(np, 0, cp, 0);
X np = newname(".a.r");
X newline(np, 0, cp, 0);
X np = newname(".o.r");
X newline(np, 0, cp, 0);
X np = newname(".mc.r");
X newline(np, 0, cp, 0);
X np = newname(".mca.r");
X newline(np, 0, cp, 0);
X np = newname(".ma.r");
X newline(np, 0, cp, 0);
X np = newname(".mo.r");
X newline(np, 0, cp, 0);
X
X np = newname(".r");
X dp = newdep(np, 0);
X np = newname(".mc");
X dp = newdep(np, dp);
X np = newname(".mca");
X dp = newdep(np, dp);
X np = newname(".c");
X dp = newdep(np, dp);
X np = newname(".ca");
X dp = newdep(np, dp);
X np = newname(".ma");
X dp = newdep(np, dp);
X np = newname(".mo");
X dp = newdep(np, dp);
X np = newname(".o");
X dp = newdep(np, dp);
X np = newname(".a");
X dp = newdep(np, dp);
X np = newname(".SUFFIXES");
X newline(np, dp, 0, 0);
X#endif
X}
/
exit
--
Brian Beattie ...netsys!beattie
11525 Hickory Cluster
Reston VA 22090 (703)471-5513