v07i071: Public-domain MAKE
sources-request at mirror.UUCP
sources-request at mirror.UUCP
Thu Dec 4 05:44:49 AEST 1986
Submitted by: caret at fairlight.oz
Mod.sources: Volume 7, Issue 71
Archive-name: make
[ This seems to be fairly complete for the V7 make. People with
source licenses will appreciate the appearance of this code. --r$ ]
#!/bin/sh
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If all goes well, you will see the message "No problems found."
# Exit status; set to 1 on "wc" errors or if would overwrite.
STATUS=0
# Contents: README Makefile h.h check.c input.c macro.c main.c
# make.c reader.c rules.c
echo x - README
if test -f README ; then
echo README exists, putting output in $$README
OUT=$$README
STATUS=1
else
OUT=README
fi
sed 's/^X//' > $OUT <<'@//E*O*F 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).
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.
XHere is a list of the changes made:
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)
@//E*O*F README//
chmod u=rw,g=rw,o=rw $OUT
echo x - Makefile
if test -f Makefile ; then
echo Makefile exists, putting output in $$Makefile
OUT=$$Makefile
STATUS=1
else
OUT=Makefile
fi
sed 's/^X//' > $OUT <<'@//E*O*F Makefile//'
X# Makefile for make!
XOBJS = check.o input.o macro.o main.o \
X make.o reader.o rules.o
Xm: $(OBJS)
X cc -o m $(OBJS)
X$(OBJS): h.h
@//E*O*F Makefile//
chmod u=rw,g=rw,o=rw $OUT
echo x - h.h
if test -f h.h ; then
echo h.h exists, putting output in $$h.h
OUT=$$h.h
STATUS=1
else
OUT=h.h
fi
sed 's/^X//' > $OUT <<'@//E*O*F h.h//'
X/*
X * Include header for make
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#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#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#define LZ (1024) /* Line size */
X/*
X * A name. This represents a file, either to be made, or existant
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#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 * 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 * 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 * 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 * 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};
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;
Xchar * fgets();
Xchar * index();
Xchar * rindex();
Xchar * malloc();
Xextern int errno;
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();
@//E*O*F h.h//
chmod u=rw,g=rw,o=rw $OUT
echo x - check.c
if test -f check.c ; then
echo check.c exists, putting output in $$check.c
OUT=$$check.c
STATUS=1
else
OUT=check.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F check.c//'
X/*
X * Check structures for make.
X */
X#include <stdio.h>
X#include "h.h"
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 for (mp = macrohead; mp; mp = mp->m_next)
X fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val);
X fputc('\n', stderr);
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 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 * 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 if (np->n_flag & N_MARK)
X fatal("Circular dependency from %s", np->n_name);
X np->n_flag |= N_MARK;
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 np->n_flag &= ~N_MARK;
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 for (np = namehead.n_next; np; np = np->n_next)
X check(np);
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 if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG))
X return;
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}
@//E*O*F check.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - input.c
if test -f input.c ; then
echo input.c exists, putting output in $$input.c
OUT=$$input.c
STATUS=1
else
OUT=input.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F input.c//'
X/*
X * Parse a makefile
X */
X#include <stdio.h>
X#include <ctype.h>
X#include "h.h"
Xstruct name namehead;
Xstruct name * firstname;
Xchar str1[LZ]; /* General store */
Xchar str2[LZ];
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 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 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 return rp;
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 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 if (dp == (struct depend *)0)
X return rp;
X for (rrp = dp; rrp->d_next; rrp = rrp->d_next)
X ;
X rrp->d_next = rp;
X return dp;
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 if (rcp = rindex(str, '\n'))
X *rcp = '\0'; /* Loose newline */
X while (isspace(*str))
X str++;
X if (*str == '\0') /* If nothing left, the exit */
X return;
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 if (cp == (struct cmd *)0)
X return rp;
X for (rrp = cp; rrp->c_next; rrp = rrp->c_next)
X ;
X rrp->c_next = rp;
X return cp;
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 /* 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 /* 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 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 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 if (rrp)
X rrp->l_next = rp;
X else
X np->n_line = rp;
X np->n_flag |= N_TARG;
X if (flag)
X np->n_flag |= N_DOUBLE;
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 if (getline(str1, fd)) /* Read the first line */
X return;
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 p = str1;
X while (isspace(*p)) /* Find first target */
X p++;
X while (((q = index(p, '=')) != (char *)0) &&
X (p != q) && (q[-1] == '\\')) /* Find value */
X {
X register char * a;
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 register char * a;
X *q++ = '\0'; /* Separate name and val */
X while (isspace(*q))
X q++;
X if (p = rindex(q, '\n'))
X *p = '\0';
X p = str1;
X if ((a = gettok(&p)) == (char *)0)
X error("No macro name");
X setmacro(a, q);
X if (getline(str1, fd))
X return;
X continue;
X }
X expand(str1);
X p = str1;
X while (((q = index(p, ':')) != (char *)0) &&
X (p != q) && (q[-1] == '\\')) /* Find dependents */
X {
X register char * a;
X a = q - 1; /* Del \ chr; move rest back */
X p = q;
X while(*a++ = *q++)
X ;
X }
X if (q == (char *)0)
X error("No targets provided");
X *q++ = '\0'; /* Separate targets and dependents */
X if (*q == ':') /* Double colon */
X {
X dbl = 1;
X q++;
X }
X else
X dbl = 0;
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 *((q = str1) + strlen(str1) + 1) = '\0';
X /* Need two nulls for gettok (Remember separation) */
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 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 if (feof(fd)) /* EOF? */
X return;
X strcpy(str1, str2);
X }
X}
@//E*O*F input.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - macro.c
if test -f macro.c ; then
echo macro.c exists, putting output in $$macro.c
OUT=$$macro.c
STATUS=1
else
OUT=macro.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F macro.c//'
X/*
X * Macro control for make
X */
X#include "h.h"
Xstruct macro * macrohead;
Xstruct macro *
Xgetmp(name)
Xchar * name;
X{
X register struct macro * rp;
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}
Xchar *
Xgetmacro(name)
Xchar * name;
X{
X struct macro * mp;
X if (mp = getmp(name))
X return mp->m_val;
X else
X return "";
X}
Xstruct macro *
Xsetmacro(name, val)
Xchar * name;
Xchar * val;
X{
X register struct macro * rp;
X register char * cp;
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 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 rp->m_next = macrohead;
X macrohead = rp;
X rp->m_flag = FALSE;
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 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 return rp;
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 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 * 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 strcpy(a, str);
X doexp(&p, a, &len, b);
X}
@//E*O*F macro.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - main.c
if test -f main.c ; then
echo main.c exists, putting output in $$main.c
OUT=$$main.c
STATUS=1
else
OUT=main.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F 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#include <stdio.h>
X#include "h.h"
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#ifdef eon
X#define MEMSPACE (16384)
X#endif
Xchar * myname;
Xchar * makefile; /* The make file */
X#ifdef eon
Xunsigned memspace = MEMSPACE;
X#endif
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 */
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 myname = (argc-- < 1) ? "make" : *argv++;
X while ((argc > 0) && (**argv == '-'))
X {
X argc--; /* One less to process */
X p = *argv++; /* Now processing this one */
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#ifdef eon
X if (initalloc(memspace) == 0xffff) /* Must get memory for alloc */
X fatal("Cannot initalloc memory");
X#endif
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 makerules();
X setmacro("$", "$");
X while (argc && (p = index(*argv, '=')))
X {
X char c;
X c = *p;
X *p = '\0';
X setmacro(*argv, p+1);
X *p = c;
X argv++;
X argc--;
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 if (print)
X prt(); /* Print out structures */
X np = newname(".SILENT");
X if (np->n_flag & N_TARG)
X silent = TRUE;
X np = newname(".IGNORE");
X if (np->n_flag & N_TARG)
X ignore = TRUE;
X precious();
X if (!firstname)
X fatal("No targets defined");
X circh(); /* Check circles in target definitions */
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 if (quest)
X exit(estat);
X else
X exit(0);
X}
Xusage()
X{
X fprintf(stderr, "Usage: %s [-f makefile] [-inpqrst] [macro=val ...] [target(s) ...]\n", myname);
X exit(1);
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}
@//E*O*F main.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - make.c
if test -f make.c ; then
echo make.c exists, putting output in $$make.c
OUT=$$make.c
STATUS=1
else
OUT=make.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F make.c//'
X/*
X * Do the actual making for make
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 * 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#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 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 return status;
X#endif
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 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 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 if (!domake)
X ssilent = 0;
X if (!ssilent)
X fputs(" ", stdout);
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 putchar('\n');
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}
Xdocmds(np)
Xstruct name * np;
X{
X register struct line * lp;
X for (lp = np->n_line; lp; lp = lp->l_next)
X docmds1(np, lp);
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 regs.rg_a = fd;
X regs.rg_b = SS_FD;
X regs.rg_x = &fdbuf;
X regs.rg_y = sizeof (fdbuf);
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 * 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 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 return acc;
X}
X/*
X * Get the current time in the internal format
X */
Xtime(tp)
Xtime_t * tp;
X{
X struct sgtbuf tbuf;
X if (getime(&tbuf) < 0)
X return -1;
X if (tp)
X *tp = cnvtime(&tbuf);
X return 0;
X}
X#endif
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 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 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 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 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 close(fd);
X#endif
X#ifdef os9
X struct sgtbuf info;
X int fd;
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 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 close(fd);
X#endif
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 if (!domake || !silent)
X printf(" touch(%s)\n", np->n_name);
X if (domake)
X {
X#ifdef unix
X long a[2];
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 * 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 if (np->n_flag & N_DONE)
X return 0;
X if (!np->n_time)
X modtime(np); /* Gets modtime of this file */
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 if (!(np->n_flag & N_TARG) && np->n_time == 0L)
X fatal("Don't know how to make %s", np->n_name);
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 np->n_flag |= N_DONE;
X if (quest)
X {
X long t;
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}
Xmake1(np, lp, qdp)
Xregister struct depend * qdp;
Xstruct line * lp;
Xstruct name * np;
X{
X register struct depend * dp;
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}
@//E*O*F make.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - reader.c
if test -f reader.c ; then
echo reader.c exists, putting output in $$reader.c
OUT=$$reader.c
STATUS=1
else
OUT=reader.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F reader.c//'
X/*
X * Read in makefile
X */
X#include <stdio.h>
X#include <ctype.h>
X#include "h.h"
Xint lineno;
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 * 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 for (;;)
X {
X if (fgets(str+pos, LZ-pos, fd) == (char *)0)
X return TRUE; /* EOF */
X lineno++;
X if ((p = index(str+pos, '\n')) == (char *)0)
X error("Line too long");
X if (p[-1] == '\\')
X {
X p[-1] = '\n';
X pos = p - str;
X continue;
X }
X p = str;
X while (((q = index(p, '#')) != (char *)0) &&
X (p != q) && (q[-1] == '\\'))
X {
X char *a;
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 p = str;
X while (isspace(*p)) /* Checking for blank */
X p++;
X if (*p != '\0')
X return FALSE;
X pos = 0;
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 while (isspace(**ptr)) /* Skip spaces */
X (*ptr)++;
X if (**ptr == '\0') /* Nothing after spaces */
X return NULL;
X p = *ptr; /* word starts here */
X while ((**ptr != '\0') && (!isspace(**ptr)))
X (*ptr)++; /* Find end of word */
X *(*ptr)++ = '\0'; /* Terminate it */
X return(p);
X}
@//E*O*F reader.c//
chmod u=rw,g=rw,o=rw $OUT
echo x - rules.c
if test -f rules.c ; then
echo rules.c exists, putting output in $$rules.c
OUT=$$rules.c
STATUS=1
else
OUT=rules.c
fi
sed 's/^X//' > $OUT <<'@//E*O*F rules.c//'
X/*
X * Control of the implicit suffix rules
X */
X#include "h.h"
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 * 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 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 if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG))
X return FALSE;
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 * Make the default rules
X */
Xvoid
Xmakerules()
X{
X struct cmd * cp;
X struct name * np;
X struct depend * dp;
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 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 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 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 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 * Some of the UNIX implicit rules
X */
X#ifdef unix
X setmacro("CC", "cc");
X setmacro("CFLAGS", "-O");
X cp = newcmd("$(CC) $(CFLAGS) -c $<", 0);
X np = newname(".c.o");
X newline(np, 0, cp, 0);
X setmacro("AS", "as");
X cp = newcmd("$(AS) -o $@ $<", 0);
X np = newname(".s.o");
X newline(np, 0, cp, 0);
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 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 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 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 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}
@//E*O*F rules.c//
chmod u=rw,g=rw,o=rw $OUT
echo Inspecting for damage in transit...
temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
38 313 1803 README
11 22 134 Makefile
130 414 2457 h.h
115 316 2100 check.c
340 1124 6577 input.c
156 396 2366 macro.c
222 683 4336 main.c
452 1202 7682 make.c
116 318 1795 reader.c
234 740 4819 rules.c
1814 5528 34069 total
!!!
wc README Makefile h.h check.c input.c macro.c main.c make.c reader.c rules.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if test -s $dtemp ; then
echo "Ouch [diff of wc output]:"
cat $dtemp
STATUS=1
elif test $STATUS = 0 ; then
echo "No problems found."
else
echo "WARNING -- PROBLEMS WERE FOUND..."
fi
exit $STATUS
More information about the Mod.sources
mailing list