doc@j.cc.purdue.edu (Craig Norborg) (04/24/87)
This is the source to the public domain 'make' that was posted to the net earlier this year. Steve Walton has done the port to the Amiga. Binaries available in comp.binaries.amiga. Craig Norborg comp.sources.amiga moderator j.cc.purdue.edu!amiga-sources-request # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # Makefile.AZTEC # README # README.amiga # check.c # h.h # input.c # macro.c # main.c # make.c # reader.c # rules.c # This archive created: Fri Apr 24 15:24:37 1987 # By: Craig Norborg (Purdue University Computing Center) cat << \SHAR_EOF > Makefile.AZTEC # Makefile for make (Amiga Aztec version) CFLAGS=-Damiga -Daztec OBJS = main.o make.o rules.o reader.o input.o macro.o check.o m : $(OBJS) ln -o m $(OBJS) -lc SHAR_EOF cat << \SHAR_EOF > README Following is a repost of the public domain 'make' that I posted to net.sources a couple of months ago. I have fixed a few bugs, and added some more features, and the resulting changes amounted to about as much text as the whole program (hence the repost). For those that missed the net.sources posting, this is a public domain re-implementation of the UNIX make program. There is no manual included; for documentation, refer to a UNIX manual, or the source. Here is a list of the changes made: i) If '-' (ignore) or '@' (silent) where used at the start of a command, their effect was not turned off for the following commands. ii) A special target (.SUFFIXES, .PRECIOUS) or a rule (.c.o, .a.o), if first in the file would be taken as the default target. This resulted in error messages like "Don't know how to make .c", because things like .SUFFIXES were being made. This was further complicated by --- iii) Special target lines with no dependents (ie. .SUFFIXES:\n) were not clearing out the existing dependents like they should. iv) Default rules could not be redefined because of the error checking for commands being defined twice. Now you are allowed to define a target beinging with '.', having no dependents with commands. v) The -q option didn't do the time comparison correctly, or clear the variable used to keep track of this. Thus it didn't work very well. vi) The syntax ${..} for macro's supported by UNIX make was not supported. vii) There wuz a couple of spelling errors. viii) When make checked for implicit rules on targets without a suffix, there were problems. (Note: The ~ feature of UNIX make wasn't and still isn't supported) ix) The -n option did not print @ lines like it was supposed to. x) :: added. (See UNIX manual) xi) $? added. (see UNIX manual) SHAR_EOF cat << \SHAR_EOF > README.amiga This program is the public domain Make program which appeared on mod.sources, Volume 7, number 91. I have ported it to the Amiga under Manx Aztec C version 3.40a. This short document assumes you know Make, and simply points out the Amiga specific features of the program. The supported switches are listed in the comment block at the beginning of module main.c. I offer no apologies for the fact that I ran the code through "indent -i4 -nfc1" on the 4.3BSD system at work before I started working on it. The program has been compiled under Manx Aztec C Version 3.40. It uses Manx's fexecv() function to execute commands and get their return value, and the Manx dos_packet() function to send an AmigaDOS packet to the file system for touch'ing purposes. Lattice recompilers need to change these (at least). Peculiar features of the Amiga version are: (1) The Amiga-specific sections of the code are #ifdef'd on the symbol amiga (note the lower case). I endorse Fred Fish's effort to have system and compiler-supplied #define's in lower case to ensure no collisions with user-supplied ones. (2) The file rules.c, routine makerules(), contains the definitions of the default built-in rules. For the Amiga, these are equivalent to the Makefile: CC = cc CFLAGS = AS = as .c.o: $(CC) $(CFLAGS) $< .asm.o: $(AS) -o $@ $< (indented for clarity only). Thus, one could conceivably do: make CC=lc CFLAGS= AS=asm to run this make under Lattice C. (3) If the file S:builtins.make exists, its contents are read in instead of the built-in rules defined in makerules(). Thus, you can use different default rules on different disks and change the default rules without recompiling make. (4) A Control-D typed during execution of a command by make will cause an abort with exit status 1 AFTER the completion of that command. Control-C is an immediate abort, as you might expect. (5) Not really Amiga specific, but worth mentioning, is that characters special both to the local operating system (such as : in AmigaDOS) and to make may be used in the makefile by preceding them with \ to temporarily override their special meaning. For example, to tell make that RAM:foo depends on AC:foo.c, write: RAM\:foo : AC\:foo.c (6) The Aztec fexecv() function, which is used by make to execute its commands, only works on programs in directories stored along the AmigaDOS PATH, so make sure your PATH includes the appropriate directories. Finally, I added one new feature in the non-machine-specific code: the name of the makefile can be a single dash "-", in which case a makefile is read from the standard input. About the only feature of "real" make missing here is the semicolon construct which allows a pair of lines such as the .c.o: rule and command above to be written as one line, viz: .c.o: ; $(CC) $(CFLAGS) $< Enjoy! Bug reports in the Amiga-specific stuff should be directed to me; others should go to caret@fairlight.OZ, the author of the rest of it. By the way, this code is superior to the Manx-supplied make--more switches and a better parser; in fact, this make will handle the Makefile for MicroGnuEmacs while Manx make chokes on the ln command. Steve Walton ametek!walton@csvax.caltech.edu (ARPA) WALTON@CALTECH (BITNET) ...!ucbvax!sun!megatest!ametek!walton SHAR_EOF cat << \SHAR_EOF > check.c /* * Check structures for make. */ #include <stdio.h> #include "h.h" /* * Prints out the structures as defined in memory. Good for check * that you make file does what you want (and for debugging make). */ void prt() { register struct name *np; register struct depend *dp; register struct line *lp; register struct cmd *cp; register struct macro *mp; for (mp = macrohead; mp; mp = mp->m_next) fprintf(stderr, "%s = %s\n", mp->m_name, mp->m_val); fputc('\n', stderr); for (np = namehead.n_next; np; np = np->n_next) { if (np->n_flag & N_DOUBLE) fprintf(stderr, "%s::\n", np->n_name); else fprintf(stderr, "%s:\n", np->n_name); if (np == firstname) fprintf(stderr, "(MAIN NAME)\n"); for (lp = np->n_line; lp; lp = lp->l_next) { fputc(':', stderr); for (dp = lp->l_dep; dp; dp = dp->d_next) fprintf(stderr, " %s", dp->d_name->n_name); fputc('\n', stderr); for (cp = lp->l_cmd; cp; cp = cp->c_next) #ifdef os9 fprintf(stderr, "- %s\n", cp->c_cmd); #else fprintf(stderr, "-\t%s\n", cp->c_cmd); #endif fputc('\n', stderr); } fputc('\n', stderr); } } /* * Recursive routine that does the actual checking. */ void check(np) struct name *np; { register struct depend *dp; register struct line *lp; if (np->n_flag & N_MARK) fatal("Circular dependency from %s", np->n_name); np->n_flag |= N_MARK; for (lp = np->n_line; lp; lp = lp->l_next) for (dp = lp->l_dep; dp; dp = dp->d_next) check(dp->d_name); np->n_flag &= ~N_MARK; } /* * Look for circular dependancies. * ie. * a: b * b: a * is a circular dep */ void circh() { register struct name *np; for (np = namehead.n_next; np; np = np->n_next) check(np); } /* * Check the target .PRECIOUS, and mark its dependentd as precious */ void precious() { register struct depend *dp; register struct line *lp; register struct name *np; if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG)) return; for (lp = np->n_line; lp; lp = lp->l_next) for (dp = lp->l_dep; dp; dp = dp->d_next) dp->d_name->n_flag |= N_PREC; } SHAR_EOF cat << \SHAR_EOF > h.h /* * Include header for make */ #ifndef uchar #ifdef os9 #define uchar char #define void int #define fputc putc #else #define uchar unsigned char #endif #endif #define bool uchar #define time_t long #define TRUE (1) #define FALSE (0) #define max(a,b) ((a)>(b)?(a):(b)) #define DEFN1 "makefile" /* Default names */ #ifdef unix #define DEFN2 "Makefile" #endif #ifdef eon #define DEFN2 "Makefile" #endif /* os9 is case insensitive */ #define LZ (1024) /* Line size */ /* * A name. This represents a file, either to be made, or existant */ struct name { struct name *n_next; /* Next in the list of names */ char *n_name; /* Called */ struct line *n_line; /* Dependencies */ time_t n_time; /* Modify time of this name */ uchar n_flag; /* Info about the name */ }; #define N_MARK 0x01 /* For cycle check */ #define N_DONE 0x02 /* Name looked at */ #define N_TARG 0x04 /* Name is a target */ #define N_PREC 0x08 /* Target is precious */ #define N_DOUBLE 0x10 /* Double colon target */ /* * Definition of a target line. */ struct line { struct line *l_next; /* Next line (for ::) */ struct depend *l_dep; /* Dependents for this line */ struct cmd *l_cmd; /* Commands for this line */ }; /* * List of dependents for a line */ struct depend { struct depend *d_next; /* Next dependent */ struct name *d_name; /* Name of dependent */ }; /* * Commands for a line */ struct cmd { struct cmd *c_next; /* Next command line */ char *c_cmd; /* Command line */ }; /* * Macro storage */ struct macro { struct macro *m_next; /* Next variable */ char *m_name; /* Called ... */ char *m_val; /* Its value */ uchar m_flag; /* Infinite loop check */ }; extern char *myname; extern struct name namehead; extern struct macro *macrohead; extern struct name *firstname; extern bool silent; extern bool ignore; extern bool rules; extern bool dotouch; extern bool quest; extern bool domake; extern char str1[]; extern char str2[]; extern int lineno; char *fgets(); char *index(); char *rindex(); char *malloc(); extern int errno; char *getmacro(); struct macro *setmacro(); void input(); void error(); void fatal(); int make(); struct name *newname(); struct depend *newdep(); struct cmd *newcmd(); void newline(); char *suffix(); void touch(); void makerules(); char *gettok(); void precious(); SHAR_EOF cat << \SHAR_EOF > input.c /* * Parse a makefile */ #include <stdio.h> #include <ctype.h> #include "h.h" struct name namehead; struct name *firstname; char str1[LZ]; /* General store */ char str2[LZ]; /* * Intern a name. Return a pointer to the name struct */ struct name * newname(name) char *name; { register struct name *rp; register struct name *rrp; register char *cp; for ( rp = namehead.n_next, rrp = &namehead; rp; rp = rp->n_next, rrp = rrp->n_next ) if (strcmp(name, rp->n_name) == 0) return rp; if ((rp = (struct name *) malloc(sizeof(struct name))) == (struct name *) 0) fatal("No memory for name"); rrp->n_next = rp; rp->n_next = (struct name *) 0; if ((cp = malloc((unsigned) strlen(name) + 1)) == (char *) 0) fatal("No memory for name"); strcpy(cp, name); rp->n_name = cp; rp->n_line = (struct line *) 0; rp->n_time = (time_t) 0; rp->n_flag = 0; return rp; } /* * Add a dependant to the end of the supplied list of dependants. * Return the new head pointer for that list. */ struct depend * newdep(np, dp) struct name *np; struct depend *dp; { register struct depend *rp; register struct depend *rrp; if ((rp = (struct depend *) malloc(sizeof(struct depend))) == (struct depend *) 0) fatal("No memory for dependant"); rp->d_next = (struct depend *) 0; rp->d_name = np; if (dp == (struct depend *) 0) return rp; for (rrp = dp; rrp->d_next; rrp = rrp->d_next); rrp->d_next = rp; return dp; } /* * Add a command to the end of the supplied list of commands. * Return the new head pointer for that list. */ struct cmd * newcmd(str, cp) char *str; struct cmd *cp; { register struct cmd *rp; register struct cmd *rrp; register char *rcp; if (rcp = rindex(str, '\n')) *rcp = '\0'; /* Loose newline */ while (isspace(*str)) str++; if (*str == '\0') /* If nothing left, the exit */ return (struct cmd *) 0; if ((rp = (struct cmd *) malloc(sizeof(struct cmd))) == (struct cmd *) 0) fatal("No memory for command"); rp->c_next = (struct cmd *) 0; if ((rcp = malloc((unsigned) strlen(str) + 1)) == (char *) 0) fatal("No memory for command"); strcpy(rcp, str); rp->c_cmd = rcp; if (cp == (struct cmd *) 0) return rp; for (rrp = cp; rrp->c_next; rrp = rrp->c_next); rrp->c_next = rp; return cp; } /* * Add a new 'line' of stuff to a target. This check to see * if commands already exist for the target. If flag is set, * the line is a double colon target. * * Kludges: * i) If the new name begins with a '.', and there are no dependents, * then the target must cease to be a target. This is for .SUFFIXES. * ii) If the new name begins with a '.', with no dependents and has * commands, then replace the current commands. This is for * redefining commands for a default rule. * Neither of these free the space used by dependents or commands, * since they could be used by another target. */ void newline(np, dp, cp, flag) struct name *np; struct depend *dp; struct cmd *cp; bool flag; { bool hascmds = FALSE; /* Target has commands */ register struct line *rp; register struct line *rrp; /* Handle the .SUFFIXES case */ if (np->n_name[0] == '.' && !dp && !cp) { for (rp = np->n_line; rp; rp = rrp) { rrp = rp->l_next; free((char *) rp); } np->n_line = (struct line *) 0; np->n_flag &= ~N_TARG; return; } /* This loop must happen since rrp is used later. */ for ( rp = np->n_line, rrp = (struct line *) 0; rp; rrp = rp, rp = rp->l_next ) if (rp->l_cmd) hascmds = TRUE; if (hascmds && cp && !(np->n_flag & N_DOUBLE)) /* Handle the implicit rules redefinition case */ if (np->n_name[0] == '.' && dp == (struct depend *) 0) { np->n_line->l_cmd = cp; return; } else error("Commands defined twice for target %s", np->n_name); if (np->n_flag & N_TARG) if (!(np->n_flag & N_DOUBLE) != !flag) /* like xor */ error("Inconsistent rules for target %s", np->n_name); if ((rp = (struct line *) malloc(sizeof(struct line))) == (struct line *) 0) fatal("No memory for line"); rp->l_next = (struct line *) 0; rp->l_dep = dp; rp->l_cmd = cp; if (rrp) rrp->l_next = rp; else np->n_line = rp; np->n_flag |= N_TARG; if (flag) np->n_flag |= N_DOUBLE; } /* * Parse input from the makefile, and construct a tree structure * of it. */ void input(fd) FILE *fd; { char *p; /* General */ char *q; struct name *np; struct depend *dp; struct cmd *cp; bool dbl, getline(); if (getline(str1, fd)) /* Read the first line */ return; for (;;) { #ifdef os9 if (*str1 == ' ') /* Rules without targets */ # else if (*str1 == '\t') /* Rules without targets */ # endif error("Rules not allowed here"); p = str1; while (isspace(*p)) /* Find first target */ p++; while (((q = index(p, '=')) != (char *) 0) && (p != q) && (q[-1] == '\\')) { /* Find value */ register char *a; a = q - 1; /* Del \ chr; move rest back */ p = q; while (*a++ = *q++); } if (q != (char *) 0) { register char *a; *q++ = '\0'; /* Separate name and val */ while (isspace(*q)) q++; if (p = rindex(q, '\n')) *p = '\0'; p = str1; if ((a = gettok(&p)) == (char *) 0) error("No macro name"); setmacro(a, q); if (getline(str1, fd)) return; continue; } expand(str1); p = str1; while (((q = index(p, ':')) != (char *) 0) && (p != q) && (q[-1] == '\\')) { /* Find dependents */ register char *a; a = q - 1; /* Del \ chr; move rest back */ p = q; while (*a++ = *q++); } if (q == (char *) 0) error("No targets provided"); *q++ = '\0'; /* Separate targets and dependents */ if (*q == ':') { /* Double colon */ dbl = 1; q++; } else dbl = 0; for (dp = (struct depend *) 0; ((p = gettok(&q)) != (char *) 0);) /* get list of dep's */ { np = newname(p); /* Intern name */ dp = newdep(np, dp);/* Add to dep list */ } *((q = str1) + strlen(str1) + 1) = '\0'; /* Need two nulls for gettok (Remember separation) */ cp = (struct cmd *) 0; if (getline(str2, fd) == FALSE) { /* Get commands */ #ifdef os9 while (*str2 == ' ') #else while (*str2 == '\t') #endif { cp = newcmd(&str2[0], cp); if (getline(str2, fd)) break; } } while ((p = gettok(&q)) != (char *) 0) { /* Get list of targ's */ np = newname(p); /* Intern name */ newline(np, dp, cp, dbl); if (!firstname && p[0] != '.') firstname = np; } if (feof(fd)) /* EOF? */ return; strcpy(str1, str2); } } SHAR_EOF cat << \SHAR_EOF > macro.c /* * Macro control for make */ #include "h.h" struct macro *macrohead; struct macro * getmp(name) char *name; { register struct macro *rp; for (rp = macrohead; rp; rp = rp->m_next) if (strcmp(name, rp->m_name) == 0) return rp; return (struct macro *) 0; } char * getmacro(name) char *name; { struct macro *mp; if (mp = getmp(name)) return mp->m_val; else return ""; } struct macro * setmacro(name, val) char *name; char *val; { register struct macro *rp; register char *cp; /* Replace macro definition if it exists */ for (rp = macrohead; rp; rp = rp->m_next) if (strcmp(name, rp->m_name) == 0) { free(rp->m_val); /* Free space from old */ break; } if (!rp) { /* If not defined, allocate space for new */ if ((rp = (struct macro *) malloc(sizeof(struct macro))) == (struct macro *) 0) fatal("No memory for macro"); rp->m_next = macrohead; macrohead = rp; rp->m_flag = FALSE; if ((cp = malloc((unsigned) strlen(name) + 1)) == (char *) 0) fatal("No memory for macro"); strcpy(cp, name); rp->m_name = cp; } if ((cp = malloc((unsigned) strlen(val) + 1)) == (char *) 0) fatal("No memory for macro"); strcpy(cp, val); /* Copy in new value */ rp->m_val = cp; return rp; } /* * Do the dirty work for expand */ void doexp(to, from, len, buf) char **to; char *from; int *len; char *buf; { register char *rp; register char *p; register char *q; register struct macro *mp; rp = from; p = *to; while (*rp) { if (*rp != '$') { *p++ = *rp++; (*len)--; } else { q = buf; if (*++rp == '{') while (*++rp && *rp != '}') *q++ = *rp; else if (*rp == '(') while (*++rp && *rp != ')') *q++ = *rp; else if (!*rp) { *p++ = '$'; break; } else *q++ = *rp; *q = '\0'; if (*rp) rp++; if (!(mp = getmp(buf))) mp = setmacro(buf, ""); if (mp->m_flag) fatal("Infinitely recursive macro %s", mp->m_name); mp->m_flag = TRUE; *to = p; doexp(to, mp->m_val, len, buf); p = *to; mp->m_flag = FALSE; } if (*len <= 0) error("Expanded line too line"); } *p = '\0'; *to = p; } /* * Expand any macros in str. */ void expand(str) char *str; { static char a[LZ]; static char b[LZ]; char *p = str; int len = LZ - 1; strcpy(a, str); doexp(&p, a, &len, b); } SHAR_EOF cat << \SHAR_EOF > main.c /* * make [-f makefile] [-ins] [target(s) ...] * * (Better than EON mk but not quite as good as UNIX make) * * -f makefile name * -i ignore exit status * -n Pretend to make * -p Print all macros & targets * -q Question up-to-dateness of target. Return exit status 1 if not * -r Don't not use inbuilt rules * -s Make silently * -t Touch files instead of making them * -m Change memory requirements (EON only) */ #include <stdio.h> #include "h.h" #ifdef unix #include <sys/errno.h> #endif #ifdef eon #include <sys/err.h> #endif #ifdef os9 #include <errno.h> #endif #ifdef amiga #include <errno.h> #endif #ifdef eon #define MEMSPACE (16384) #endif char *myname; char *makefile; /* The make file */ #ifdef eon unsigned memspace = MEMSPACE; #endif FILE *ifd; /* Input file desciptor */ bool domake = TRUE; /* Go through the motions option */ bool ignore = FALSE; /* Ignore exit status option */ bool silent = FALSE; /* Silent option */ bool print = FALSE; /* Print debuging information */ bool rules = TRUE; /* Use inbuilt rules */ bool dotouch = FALSE;/* Touch files instead of making */ bool quest = FALSE; /* Question up-to-dateness of file */ void main(argc, argv) int argc; char **argv; { register char *p; /* For argument processing */ int estat = 0; /* For question */ register struct name *np; void prt(), circh(); myname = (argc-- < 1) ? "make" : *argv++; while ((argc > 0) && (**argv == '-')) { argc--; /* One less to process */ p = *argv++; /* Now processing this one */ while (*++p != '\0') { switch (*p) { case 'f': /* Alternate file name */ if (*++p == '\0') { if (argc-- <= 0) usage(); p = *argv++; } makefile = p; goto end_of_args; #ifdef eon case 'm': /* Change space requirements */ if (*++p == '\0') { if (argc-- <= 0) usage(); p = *argv++; } memspace = atoi(p); goto end_of_args; #endif case 'n': /* Pretend mode */ domake = FALSE; break; case 'i': /* Ignore fault mode */ ignore = TRUE; break; case 's': /* Silent about commands */ silent = TRUE; break; case 'p': print = TRUE; break; case 'r': rules = FALSE; break; case 't': dotouch = TRUE; break; case 'q': quest = TRUE; break; default: /* Wrong option */ usage(); } } end_of_args:; } #ifdef amiga if ((ifd = fopen("s:builtins.make", "r")) != (FILE *) 0) { input(ifd); fclose(ifd); } else #endif makerules(); #ifdef eon if (initalloc(memspace) == 0xffff) /* Must get memory for alloc */ fatal("Cannot initalloc memory"); #endif if (!makefile) { /* If no file, then use default */ if ((ifd = fopen(DEFN1, "r")) == (FILE *) 0) #ifdef eon if (errno != ER_NOTF) fatal("Can't open %s; error %02x", DEFN1, errno); #endif #ifdef unix if (errno != ENOENT) fatal("Can't open %s; error %02x", DEFN1, errno); #endif #ifdef amiga if (errno != ENOENT) fatal("Can't open %s; error %02x", DEFN1, errno); #endif #ifdef DEFN2 if ((ifd == (FILE *) 0) && ((ifd = fopen(DEFN2, "r")) == (FILE *) 0)) fatal("Can't open %s", DEFN2); #else else fatal("Can't open %s", DEFN1); #endif } else if (strcmp(makefile, "-") == 0) /* Can use stdin as makefile */ ifd = stdin; else if ((ifd = fopen(makefile, "r")) == (FILE *) 0) fatal("Can't open %s", makefile); setmacro("$", "$"); while (argc && (p = index(*argv, '='))) { char c; c = *p; *p = '\0'; setmacro(*argv, p + 1); *p = c; argv++; argc--; } input(ifd); /* Input all the gunga */ fclose(ifd); /* Finished with makefile */ lineno = 0; /* Any calls to error now print no line * number */ if (print) prt(); /* Print out structures */ np = newname(".SILENT"); if (np->n_flag & N_TARG) silent = TRUE; np = newname(".IGNORE"); if (np->n_flag & N_TARG) ignore = TRUE; precious(); if (!firstname) fatal("No targets defined"); circh(); /* Check circles in target definitions */ if (!argc) estat = make(firstname, 0); else while (argc--) { if (!print && !silent && strcmp(*argv, "love") == 0) printf("Not war!\n"); estat |= make(newname(*argv++), 0); } if (quest) exit(estat); else exit(0); } usage() { fprintf(stderr, "Usage: %s [-f makefile] [-inpqrst] [macro=val ...] [target(s) ...]\n", myname); exit(1); } void fatal(msg, a1, a2, a3, a4, a5, a6) char *msg; { fprintf(stderr, "%s: ", myname); fprintf(stderr, msg, a1, a2, a3, a4, a5, a6); fputc('\n', stderr); exit(1); } SHAR_EOF cat << \SHAR_EOF > make.c /* * Do the actual making for make */ #include <stdio.h> #ifdef unix #include <sys/types.h> #include <sys/stat.h> #include <sys/errno.h> #endif #ifdef eon #include <sys/stat.h> #include <sys/err.h> #endif #ifdef os9 #include <time.h> #include <os9.h> #include <modes.h> #include <direct.h> #include <errno.h> #endif #ifdef amiga #include <ctype.h> #include <errno.h> #include <libraries/dosextens.h> #include <exec/memory.h> #include <exec/io.h> #include <exec/ports.h> #include <functions.h> #endif #include "h.h" /* * Exec a shell that returns exit status correctly (/bin/esh). * The standard EON shell returns the process number of the last * async command, used by the debugger (ugg). * [exec on eon is like a fork+exec on unix] */ int dosh(string, shell) char *string; char *shell; { int number; #ifdef amiga char *av[3], *s, *p; int i; p = s = malloc((unsigned) strlen(string) + 1); strcpy(p, string); /* make a copy of the string */ i = 0; av[0] = gettok(&p); /* get first argument */ av[1] = p; /* get rest of command line */ av[2] = NULL; if (fexecv(av[0], av) == -1) fatal("couldn't execute command '%s', error return %02x\n", av[0], errno); number = wait(); free(s); return number; #endif #ifdef unix return system(string); #endif #ifdef eon return ((number = execl(shell, shell, "-c", string, 0)) == -1) ? -1 : /* couldn't start the shell */ wait(number); /* return its exit status */ #endif #ifdef os9 int status, pid; strcat(string, "\n"); if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1) return -1; /* Couldn't start a shell */ do { if ((pid = wait(&status)) == -1) return -1; /* child already died!?!? */ } while (pid != number); return status; #endif } /* * Do commands to make a target */ void docmds1(np, lp) struct name *np; struct line *lp; { bool ssilent; bool signore; int estat; register char *q; register char *p; char *shell; register struct cmd *cp; #ifdef amiga long SetSignal(); #endif if (*(shell = getmacro("SHELL")) == '\0') #ifdef eon shell = ":bin/esh"; #endif #ifdef unix shell = "/bin/sh"; #endif #ifdef os9 shell = "shell"; #endif #ifdef amiga shell = NULL; #endif for (cp = lp->l_cmd; cp; cp = cp->c_next) { strcpy(str1, cp->c_cmd); expand(str1); q = str1; ssilent = silent; signore = ignore; while ((*q == '@') || (*q == '-')) { if (*q == '@') /* Specific silent */ ssilent = TRUE; else /* Specific ignore */ signore = TRUE; q++; /* Not part of the command */ } if (!domake) ssilent = 0; if (!ssilent) fputs(" ", stdout); for (p = q; *p; p++) { if (*p == '\n' && p[1] != '\0') { *p = ' '; if (!ssilent) fputs("\\\n", stdout); } else if (!ssilent) putchar(*p); } if (!ssilent) putchar('\n'); if (domake) { /* Get the shell to execute it */ if ((estat = dosh(q, shell)) != 0) { if (estat == -1) fatal("Couldn't execute %s", shell); else { printf("%s: Error code %d", myname, estat); if (signore) fputs(" (Ignored)\n", stdout); else { putchar('\n'); if (!(np->n_flag & N_PREC)) if (unlink(np->n_name) == 0) printf("%s: '%s' removed.\n", myname, np->n_name); exit(estat); } } } #ifdef amiga if ((SetSignal(0L, SIGBREAKF_CTRL_D) & SIGBREAKF_CTRL_D) != 0) { fputs("make: abort due to ^D\n", stdout); exit(1); } #endif } } } docmds(np) struct name *np; { register struct line *lp; for (lp = np->n_line; lp; lp = lp->l_next) docmds1(np, lp); } #ifdef os9 /* * Some stuffing around to get the modified time of a file * in an os9 file system */ getmdate(fd, tbp) struct sgtbuf *tbp; { struct registers regs; static struct fildes fdbuf; regs.rg_a = fd; regs.rg_b = SS_FD; regs.rg_x = &fdbuf; regs.rg_y = sizeof(fdbuf); if (_os9(I_GETSTT, ®s) == -1) { errno = regs.rg_b & 0xff; return -1; } if (tbp) { _strass(tbp, fdbuf.fd_date, sizeof(fdbuf.fd_date)); tbp->t_second = 0; /* Files are only acurate to mins */ } return 0; } /* * Kludge routine to return an aproximation of how many * seconds since 1980. Dates will be in order, but will not * be lineer */ time_t cnvtime(tbp) struct sgtbuf *tbp; { long acc; acc = tbp->t_year - 80; /* Baseyear is 1980 */ acc = acc * 12 + tbp->t_month; acc = acc * 31 + tbp->t_day; acc = acc * 24 + tbp->t_hour; acc = acc * 60 + tbp->t_minute; acc = acc * 60 + tbp->t_second; return acc; } /* * Get the current time in the internal format */ time(tp) time_t *tp; { struct sgtbuf tbuf; if (getime(&tbuf) < 0) return -1; if (tp) *tp = cnvtime(&tbuf); return 0; } #endif /* * Get the modification time of a file. If the first * doesn't exist, it's modtime is set to 0. */ void modtime(np) struct name *np; { #ifdef unix struct stat info; int fd; if (stat(np->n_name, &info) < 0) { if (errno != ENOENT) fatal("Can't open %s; error %d", np->n_name, errno); np->n_time = 0L; } else np->n_time = info.st_mtime; #endif #ifdef eon struct stat info; int fd; if ((fd = open(np->n_name, 0)) < 0) { if (errno != ER_NOTF) fatal("Can't open %s; error %02x", np->n_name, errno); np->n_time = 0L; } else if (getstat(fd, &info) < 0) fatal("Can't getstat %s; error %02x", np->n_name, errno); else np->n_time = info.st_mod; close(fd); #endif #ifdef os9 struct sgtbuf info; int fd; if ((fd = open(np->n_name, 0)) < 0) { if (errno != E_PNNF) fatal("Can't open %s; error %02x", np->n_name, errno); np->n_time = 0L; } else if (getmdate(fd, &info) < 0) fatal("Can't getstat %s; error %02x", np->n_name, errno); else np->n_time = cnvtime(&info); close(fd); #endif #ifdef amiga struct FileInfoBlock *fib; struct Lock *myLock; long ioErr; fib = (struct FileInfoBlock *) malloc((unsigned) sizeof(struct FileInfoBlock)); if ((myLock = Lock(np->n_name, ACCESS_READ)) == NULL) { if ((ioErr = IoErr()) != ERROR_OBJECT_NOT_FOUND) fatal("Can't Lock '%s'; error %3ld", np->n_name, ioErr); np->n_time = 0L; } else if (!Examine(myLock, fib)) { UnLock(myLock); fatal("Can't Examine '%s'; error %3ld", np->n_name, IoErr()); } else { np->n_time = fib->fib_Date.ds_Tick/TICKS_PER_SECOND + 60*fib->fib_Date.ds_Minute + 86400*fib->fib_Date.ds_Days; UnLock(myLock); } free((char *) fib); #endif } /* * Update the mod time of a file to now. */ void touch(np) struct name *np; { char c; int fd; if (!domake || !silent) printf(" touch(%s)\n", np->n_name); if (domake) { #ifdef unix long a[2]; a[0] = a[1] = time(0); if (utime(np->n_name, &a[0]) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); #endif #ifdef eon if ((fd = open(np->n_name, 0)) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); else { uread(fd, &c, 1, 0); uwrite(fd, &c, 1); } close(fd); #endif #ifdef os9 /* * Strange that something almost as totally useless as this is easy * to do in os9! */ if ((fd = open(np->n_name, S_IWRITE)) < 0) printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); close(fd); #endif #ifdef amiga struct MsgPort *task; ULONG dateStamp[3]; struct Lock *lock, *plock; UBYTE *pointer; if(!(pointer= (UBYTE *)AllocMem(64L,MEMF_PUBLIC))) fatal("Can't get 64 bytes for pointer"); if(!(task=(struct MsgPort *)DeviceProc(np->n_name))) { printf("%s: can't get MsgPort for '%s'\n", myname, np->n_name); FreeMem((void *) pointer, 64L); return; } if(!(lock = Lock(np->n_name,SHARED_LOCK))) { printf("%s: '%s' not touched - non-existant\n", myname, np->n_name); FreeMem((void *) pointer, 64L); return; } plock = ParentDir(lock); UnLock(lock); strcpy((pointer + 1),np->n_name); *pointer = strlen(np->n_name); dos_packet(task, ACTION_SET_DATE, NULL, plock, (ULONG) &pointer[0] >> 2, (ULONG) DateStamp(dateStamp), 0L, 0L, 0L); UnLock(plock); FreeMem((void *) pointer, 64L); #endif } } /* * Recursive routine to make a target. */ int make(np, level) struct name *np; int level; { register struct depend *dp; register struct line *lp; register struct depend *qdp; time_t dtime = 1, time(); bool didsomething = 0; if (np->n_flag & N_DONE) return 0; if (!np->n_time) modtime(np); /* Gets modtime of this file */ if (rules) { for (lp = np->n_line; lp; lp = lp->l_next) if (lp->l_cmd) break; if (!lp) dyndep(np); } if (!(np->n_flag & N_TARG) && np->n_time == 0L) fatal("Don't know how to make %s", np->n_name); for (qdp = (struct depend *) 0, lp = np->n_line; lp; lp = lp->l_next) { for (dp = lp->l_dep; dp; dp = dp->d_next) { make(dp->d_name, level + 1); if (np->n_time < dp->d_name->n_time) qdp = newdep(dp->d_name, qdp); dtime = max(dtime, dp->d_name->n_time); } if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime)) { make1(np, lp, qdp); /* free()'s qdp */ dtime = 1; qdp = (struct depend *) 0; didsomething++; } } np->n_flag |= N_DONE; if (quest) { long t; t = np->n_time; time(&np->n_time); return t < dtime; } else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE)) { make1(np, (struct line *) 0, qdp); /* free()'s qdp */ time(&np->n_time); } else if (level == 0 && !didsomething) printf("%s: '%s' is up to date\n", myname, np->n_name); return 0; } make1(np, lp, qdp) register struct depend *qdp; struct line *lp; struct name *np; { register struct depend *dp; if (dotouch) touch(np); else { strcpy(str1, ""); for (dp = qdp; dp; dp = qdp) { if (strlen(str1)) strcat(str1, " "); strcat(str1, dp->d_name->n_name); qdp = dp->d_next; free(dp); } setmacro("?", str1); setmacro("@", np->n_name); if (lp) /* lp set if doing a :: rule */ docmds1(np, lp); else docmds(np); } } #ifdef amiga /* * Replace the Aztec-provided time function with one which returns something * easy to find and compare, namely the number of seconds since the Amiga's * reference date. This is the same thing returned by modtime() above. */ time_t time(v) time_t *v; { long t[3]; DateStamp(t); t[0] = t[2]/TICKS_PER_SECOND + 60*t[1] + 86400*t[0]; if (v) *v = t[0]; return t[0]; } #endif SHAR_EOF cat << \SHAR_EOF > reader.c /* * Read in makefile */ #include <stdio.h> #include <ctype.h> #include "h.h" int lineno; /* * Syntax error handler. Print message, with line number, and exits. */ void error(msg, a1, a2, a3) char *msg; { fprintf(stderr, "%s: ", myname); fprintf(stderr, msg, a1, a2, a3); if (lineno) fprintf(stderr, " near line %d", lineno); fputc('\n', stderr); exit(1); } /* * Read a line into the supplied string of length LZ. Remove * comments, ignore blank lines. Deal with quoted (\) #, and * quoted newlines. If EOF return TRUE. */ bool getline(str, fd) char *str; FILE *fd; { register char *p; char *q; int pos = 0; for (;;) { if (fgets(str + pos, LZ - pos, fd) == (char *) 0) return TRUE; /* EOF */ lineno++; if ((p = index(str + pos, '\n')) == (char *) 0) error("Line too long"); if (p[-1] == '\\') { p[-1] = '\n'; pos = p - str; continue; } p = str; while (((q = index(p, '#')) != (char *) 0) && (p != q) && (q[-1] == '\\')) { char *a; a = q - 1; /* Del \ chr; move rest back */ p = q; while (*a++ = *q++); } if (q != (char *) 0) { q[0] = '\n'; q[1] = '\0'; } p = str; while (isspace(*p)) /* Checking for blank */ p++; if (*p != '\0') return FALSE; pos = 0; } } /* * Get a word from the current line, surounded by white space. * return a pointer to it. String returned has no white spaces * in it. */ char * gettok(ptr) char **ptr; { register char *p; while (isspace(**ptr)) /* Skip spaces */ (*ptr)++; if (**ptr == '\0') /* Nothing after spaces */ return NULL; p = *ptr; /* word starts here */ while ((**ptr != '\0') && (!isspace(**ptr))) (*ptr)++; /* Find end of word */ *(*ptr)++ = '\0'; /* Terminate it */ return (p); } SHAR_EOF cat << \SHAR_EOF > rules.c /* * Control of the implicit suffix rules */ #include "h.h" #ifndef NULL #define NULL ((void *) 0) #endif /* * Return a pointer to the suffix of a name */ char * suffix(name) char *name; { return rindex(name, '.'); } /* * Dynamic dependency. This routine applies the suffis rules * to try and find a source and a set of rules for a missing * target. If found, np is made into a target with the implicit * source name, and rules. Returns TRUE if np was made into * a target. */ bool dyndep(np) struct name *np; { register char *p; register char *q; register char *suff; /* Old suffix */ register char *basename; /* Name without suffix */ struct name *op; /* New dependent */ struct name *sp; /* Suffix */ struct line *lp; struct depend *dp; char *newsuff; void modtime(); p = str1; q = np->n_name; if (!(suff = suffix(q))) return FALSE; /* No suffix */ while (q < suff) *p++ = *q++; *p = '\0'; basename = setmacro("*", str1)->m_val; if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG)) return FALSE; for (lp = sp->n_line; lp; lp = lp->l_next) for (dp = lp->l_dep; dp; dp = dp->d_next) { newsuff = dp->d_name->n_name; if (strlen(suff) + strlen(newsuff) + 1 >= LZ) fatal("Suffix rule too long"); p = str1; q = newsuff; while (*p++ = *q++); p--; q = suff; while (*p++ = *q++); sp = newname(str1); if (sp->n_flag & N_TARG) { p = str1; q = basename; if (strlen(basename) + strlen(newsuff) + 1 >= LZ) fatal("Implicit name too long"); while (*p++ = *q++); p--; q = newsuff; while (*p++ = *q++); op = newname(str1); if (!op->n_time) modtime(op); if (op->n_time) { dp = newdep(op, NULL); newline(np, dp, sp->n_line->l_cmd, 0); setmacro("<", op->n_name); return TRUE; } } } return FALSE; } /* * Make the default rules */ void makerules() { struct cmd *cp; struct name *np; struct depend *dp; #ifdef eon setmacro("BDSCC", "asm"); /* setmacro("BDSCFLAGS", ""); */ cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", 0); np = newname(".c.o"); newline(np, 0, cp, 0); setmacro("CC", "c"); setmacro("CFLAGS", "-O"); cp = newcmd("$(CC) $(CFLAGS) -c $<", 0); np = newname(".c.obj"); newline(np, 0, cp, 0); setmacro("M80", "asm -n"); /* setmacro("M80FLAGS", ""); */ cp = newcmd("$(M80) $(M80FLAGS) $<", 0); np = newname(".mac.o"); newline(np, 0, cp, 0); setmacro("AS", "zas"); /* setmacro("ASFLAGS", ""); */ cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", 0); np = newname(".as.obj"); newline(np, 0, cp, 0); np = newname(".as"); dp = newdep(np, 0); np = newname(".obj"); dp = newdep(np, dp); np = newname(".c"); dp = newdep(np, dp); np = newname(".o"); dp = newdep(np, dp); np = newname(".mac"); dp = newdep(np, dp); np = newname(".SUFFIXES"); newline(np, dp, 0, 0); #endif /* * Some of the UNIX implicit rules */ #ifdef unix setmacro("CC", "cc"); setmacro("CFLAGS", "-O"); cp = newcmd("$(CC) $(CFLAGS) -c $<", 0); np = newname(".c.o"); newline(np, 0, cp, 0); setmacro("AS", "as"); cp = newcmd("$(AS) -o $@ $<", 0); np = newname(".s.o"); newline(np, 0, cp, 0); setmacro("YACC", "yacc"); /* setmacro("YFLAGS", ""); */ cp = newcmd("$(YACC) $(YFLAGS) $<", 0); cp = newcmd("mv y.tab.c $@", cp); np = newname(".y.c"); newline(np, 0, cp, 0); cp = newcmd("$(YACC) $(YFLAGS) $<", 0); cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp); cp = newcmd("rm y.tab.c", cp); cp = newcmd("mv y.tab.o $@", cp); np = newname(".y.o"); newline(np, 0, cp, 0); np = newname(".s"); dp = newdep(np, 0); np = newname(".o"); dp = newdep(np, dp); np = newname(".c"); dp = newdep(np, dp); np = newname(".y"); dp = newdep(np, dp); np = newname(".SUFFIXES"); newline(np, dp, 0, 0); #endif #ifdef os9 /* * Fairlight use an enhanced version of the C sub-system. * They have a specialised macro pre-processor. */ setmacro("CC", "cc"); setmacro("CFLAGS", "-z"); cp = newcmd("$(CC) $(CFLAGS) -r $<", 0); np = newname(".c.r"); newline(np, 0, cp, 0); np = newname(".ca.r"); newline(np, 0, cp, 0); np = newname(".a.r"); newline(np, 0, cp, 0); np = newname(".o.r"); newline(np, 0, cp, 0); np = newname(".mc.r"); newline(np, 0, cp, 0); np = newname(".mca.r"); newline(np, 0, cp, 0); np = newname(".ma.r"); newline(np, 0, cp, 0); np = newname(".mo.r"); newline(np, 0, cp, 0); np = newname(".r"); dp = newdep(np, 0); np = newname(".mc"); dp = newdep(np, dp); np = newname(".mca"); dp = newdep(np, dp); np = newname(".c"); dp = newdep(np, dp); np = newname(".ca"); dp = newdep(np, dp); np = newname(".ma"); dp = newdep(np, dp); np = newname(".mo"); dp = newdep(np, dp); np = newname(".o"); dp = newdep(np, dp); np = newname(".a"); dp = newdep(np, dp); np = newname(".SUFFIXES"); newline(np, dp, 0, 0); #endif #ifdef amiga setmacro("CC", "cc"); setmacro("CFLAGS", ""); cp = newcmd("$(CC) $(CFLAGS) $<", NULL); np = newname(".c.o"); newline(np, NULL, cp, 0); setmacro("AS", "as"); cp = newcmd("$(AS) -o $@ $<", NULL); np = newname(".s.o"); newline(np, NULL, cp, NULL); np = newname(".o"); dp = newdep(np, NULL); np = newname(".s"); dp = newdep(np, dp); np = newname(".c"); dp = newdep(np, dp); np = newname(".SUFFIXES"); newline(np, dp, NULL, 0); #endif } SHAR_EOF # End of shell archive exit 0