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