summaryrefslogtreecommitdiff
path: root/sys/src/ape/cmd/make
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/ape/cmd/make
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/ape/cmd/make')
-rwxr-xr-xsys/src/ape/cmd/make/defs.h213
-rwxr-xr-xsys/src/ape/cmd/make/doname.c380
-rwxr-xr-xsys/src/ape/cmd/make/dosys.c287
-rwxr-xr-xsys/src/ape/cmd/make/files.c552
-rwxr-xr-xsys/src/ape/cmd/make/gram.y441
-rwxr-xr-xsys/src/ape/cmd/make/ident.c125
-rwxr-xr-xsys/src/ape/cmd/make/main.c423
-rwxr-xr-xsys/src/ape/cmd/make/misc.c504
-rwxr-xr-xsys/src/ape/cmd/make/mkfile26
9 files changed, 2951 insertions, 0 deletions
diff --git a/sys/src/ape/cmd/make/defs.h b/sys/src/ape/cmd/make/defs.h
new file mode 100755
index 000000000..d7ce537f5
--- /dev/null
+++ b/sys/src/ape/cmd/make/defs.h
@@ -0,0 +1,213 @@
+/* defs 4.2 85/10/28 */
+#define _POSIX_SOURCE
+#define _RESEARCH_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#ifndef SHELLCOM
+#define SHELLCOM "/bin/sh"
+#endif
+
+typedef char flag; /* represent a few bit flag */
+
+#define NO 0
+#define YES 1
+
+#define equal(a,b) (! strcmp(a,b))
+#define HASHSIZE 1021
+#define NLEFTS 512
+#define NCHARS 500
+#define NINTS 250
+#define INMAX 20000
+#define OUTMAX 20000
+#define QBUFMAX 20000
+#define MAXDIR 10
+#define MAXPROC 100
+#define MAXINCLUDE 17
+#define PROCLIMIT 3
+
+#define ALLDEPS 1
+#define SOMEDEPS 2
+
+#define META 01
+#define TERMINAL 02
+extern char funny[128];
+
+
+#define ALLOC(x) (struct x *) ckalloc(sizeof(struct x))
+#define CHNULL (char *) NULL
+
+extern int sigivalue;
+extern int sigqvalue;
+extern int dbgflag;
+extern int prtrflag;
+extern int silflag;
+extern int noexflag;
+extern int keepgoing;
+extern int noruleflag;
+extern int touchflag;
+extern int questflag;
+extern int oldflag;
+extern int ndocoms;
+extern int ignerr;
+extern int okdel;
+extern int forceshell;
+extern int inarglist;
+extern char **envpp; /* points to slot in environment vector */
+extern char *prompt;
+extern int nopdir;
+
+typedef struct nameblock *nameblkp;
+typedef struct depblock *depblkp;
+typedef struct lineblock *lineblkp;
+typedef struct chain *chainp;
+
+struct nameblock
+ {
+ nameblkp nxtnameblock;
+ char *namep;
+ lineblkp linep;
+ flag done;
+ flag septype;
+ flag isarch;
+ flag isdir;
+ time_t modtime;
+ };
+
+extern nameblkp mainname;
+extern nameblkp firstname;
+extern nameblkp *hashtab;
+extern int nhashed;
+extern int hashsize;
+extern int hashthresh;
+
+struct lineblock
+ {
+ lineblkp nxtlineblock;
+ struct depblock *depp;
+ struct shblock *shp;
+ };
+extern lineblkp sufflist;
+
+struct depblock
+ {
+ depblkp nxtdepblock;
+ nameblkp depname;
+ char nowait;
+ } ;
+
+struct shblock
+ {
+ struct shblock *nxtshblock;
+ char *shbp;
+ };
+
+struct varblock
+ {
+ struct varblock *nxtvarblock;
+ char *varname;
+ char *varval;
+ char **export;
+ flag noreset;
+ flag used;
+ };
+extern struct varblock *firstvar;
+
+struct pattern
+ {
+ struct pattern *nxtpattern;
+ char *patval;
+ };
+extern struct pattern *firstpat;
+
+struct dirhd
+ {
+ struct dirhd *nxtdirhd;
+ time_t dirtime;
+ int dirok;
+ DIR * dirfc;
+ char *dirn;
+ };
+extern struct dirhd *firstod;
+
+
+struct chain
+ {
+ chainp nextp;
+ char *datap;
+ };
+
+struct wild
+ {
+ struct wild *next;
+ lineblkp linep;
+ char *left;
+ char *right;
+ int llen;
+ int rlen;
+ int totlen;
+ };
+
+typedef struct wild *wildp;
+extern wildp firstwild;
+extern wildp lastwild;
+
+
+/* date for processes */
+extern int proclimit; /* maximum spawned processes allowed alive at one time */
+extern int proclive; /* number of spawned processes awaited */
+extern int nproc; /* next slot in process stack to use */
+extern struct process
+ {
+ int pid;
+ flag nohalt;
+ flag nowait;
+ flag done;
+ } procstack[ ];
+
+extern void intrupt(int);
+extern void enbint(void (*)(int));
+extern int doname(nameblkp, int, time_t *, int);
+extern int docom(struct shblock *, int, int);
+extern int dosys(char *, int, int, char *);
+extern int waitstack(int);
+extern void touch(int, char*);
+extern time_t exists(char *);
+extern time_t prestime(void);
+extern depblkp srchdir(char*, int, depblkp);
+extern time_t lookarch(char *);
+extern void dirsrch(char *);
+extern void baddirs(void);
+extern nameblkp srchname(char *);
+extern nameblkp makename(char *);
+extern int hasparen(char *);
+extern void newhash(int);
+extern nameblkp chkname(char *);
+extern char *copys(char *);
+extern char *concat(char *, char *, char *);
+extern int suffix(char *, char *, char *);
+extern int *ckalloc(int);
+extern char *subst(char *, char *);
+extern void setvar(char *, char *, int);
+extern void set3var(char *, char *);
+extern int eqsign(char *);
+extern struct varblock *varptr(char *);
+extern int dynmacro(char *);
+extern void fatal1(char *, char *);
+extern void fatal(char *);
+extern chainp appendq(chainp, char *);
+extern char *mkqlist(chainp, char *);
+extern wildp iswild(char *);
+extern char *wildmatch(wildp, char *, int);
+extern char *wildsub(char *, char *);
+extern int parse(char *);
+extern int yylex(void);
diff --git a/sys/src/ape/cmd/make/doname.c b/sys/src/ape/cmd/make/doname.c
new file mode 100755
index 000000000..5d8048bf2
--- /dev/null
+++ b/sys/src/ape/cmd/make/doname.c
@@ -0,0 +1,380 @@
+#include "defs.h"
+
+static int docom1(char *, int, int, int, int);
+static void expand(depblkp);
+
+/* BASIC PROCEDURE. RECURSIVE. */
+
+/*
+p->done = 0 don't know what to do yet
+p->done = 1 file in process of being updated
+p->done = 2 file already exists in current state
+p->done = 3 file make failed
+*/
+
+int
+doname(nameblkp p, int reclevel, time_t *tval, int nowait)
+{
+int errstat;
+int okdel1;
+int didwork;
+int len;
+time_t td, td1, tdep, ptime, ptime1;
+depblkp q;
+depblkp qtemp, suffp, suffp1;
+nameblkp p1, p2;
+struct shblock *implcom, *explcom;
+lineblkp lp;
+lineblkp lp1, lp2;
+char sourcename[100], prefix[100], temp[100], concsuff[20];
+char *stem;
+char *pnamep, *p1namep;
+chainp allchain, qchain;
+char qbuf[QBUFMAX], tgsbuf[QBUFMAX];
+wildp wp;
+int nproc1;
+char *lastslash, *s;
+
+if(p == 0)
+ {
+ *tval = 0;
+ return 0;
+ }
+
+if(dbgflag)
+ {
+ printf("doname(%s,%d)\n",p->namep,reclevel);
+ fflush(stdout);
+ }
+
+if(p->done > 0)
+ {
+ *tval = p->modtime;
+ return (p->done == 3);
+ }
+
+errstat = 0;
+tdep = 0;
+implcom = 0;
+explcom = 0;
+ptime = exists(p->namep);
+ptime1 = 0;
+didwork = NO;
+p->done = 1; /* avoid infinite loops */
+nproc1 = nproc; /* current depth of process stack */
+
+qchain = NULL;
+allchain = NULL;
+
+/* define values of Bradford's $$@ and $$/ macros */
+for(s = lastslash = p->namep; *s; ++s)
+ if(*s == '/')
+ lastslash = s;
+setvar("$@", p->namep, YES);
+setvar("$/", lastslash, YES);
+
+
+/* expand any names that have embedded metacharacters */
+
+for(lp = p->linep ; lp ; lp = lp->nxtlineblock)
+ for(q = lp->depp ; q ; q=qtemp )
+ {
+ qtemp = q->nxtdepblock;
+ expand(q);
+ }
+
+/* make sure all dependents are up to date */
+
+for(lp = p->linep ; lp ; lp = lp->nxtlineblock)
+ {
+ td = 0;
+ for(q = lp->depp ; q ; q = q->nxtdepblock)
+ if(q->depname)
+ {
+ errstat += doname(q->depname, reclevel+1, &td1, q->nowait);
+ if(dbgflag)
+ printf("TIME(%s)=%ld\n",q->depname->namep, td1);
+ if(td1 > td)
+ td = td1;
+ if(ptime < td1)
+ qchain = appendq(qchain, q->depname->namep);
+ allchain = appendq(allchain, q->depname->namep);
+ }
+ if(p->septype == SOMEDEPS)
+ {
+ if(lp->shp)
+ if( ptime<td || (ptime==0 && td==0) || lp->depp==0)
+ {
+ okdel1 = okdel;
+ okdel = NO;
+ set3var("@", p->namep);
+ setvar("?", mkqlist(qchain,qbuf), YES);
+ setvar("^", mkqlist(allchain,tgsbuf), YES);
+ qchain = NULL;
+ if( !questflag )
+ errstat += docom(lp->shp, nowait, nproc1);
+ set3var("@", CHNULL);
+ okdel = okdel1;
+ ptime1 = prestime();
+ didwork = YES;
+ }
+ }
+
+ else {
+ if(lp->shp != 0)
+ {
+ if(explcom)
+ fprintf(stderr, "Too many command lines for `%s'\n",
+ p->namep);
+ else explcom = lp->shp;
+ }
+
+ if(td > tdep) tdep = td;
+ }
+ }
+
+
+
+/* Look for implicit dependents, using suffix rules */
+
+for(lp = sufflist ; lp ; lp = lp->nxtlineblock)
+ for(suffp = lp->depp ; suffp ; suffp = suffp->nxtdepblock)
+ {
+ pnamep = suffp->depname->namep;
+ if(suffix(p->namep , pnamep , prefix))
+ {
+ (void)srchdir(concat(prefix,"*",temp), NO, (depblkp) NULL);
+ for(lp1 = sufflist ; lp1 ; lp1 = lp1->nxtlineblock)
+ for(suffp1=lp1->depp; suffp1 ; suffp1 = suffp1->nxtdepblock)
+ {
+ p1namep = suffp1->depname->namep;
+ if( (p1=srchname(concat(p1namep, pnamep ,concsuff))) &&
+ (p2=srchname(concat(prefix, p1namep ,sourcename))) )
+ {
+ errstat += doname(p2, reclevel+1, &td, NO);
+ if(ptime < td)
+ qchain = appendq(qchain, p2->namep);
+if(dbgflag) printf("TIME(%s)=%ld\n", p2->namep, td);
+ if(td > tdep) tdep = td;
+ set3var("*", prefix);
+ set3var("<", copys(sourcename));
+ for(lp2=p1->linep ; lp2 ; lp2 = lp2->nxtlineblock)
+ if(implcom = lp2->shp) break;
+ goto endloop;
+ }
+ }
+ }
+ }
+
+/* Look for implicit dependents, using pattern matching rules */
+
+len = strlen(p->namep);
+for(wp = firstwild ; wp ; wp = wp->next)
+ if(stem = wildmatch(wp, p->namep, len) )
+ {
+ lp = wp->linep;
+ for(q = lp->depp; q; q = q->nxtdepblock)
+ {
+ if(dbgflag>1 && q->depname)
+ fprintf(stderr,"check dep of %s on %s\n", p->namep,
+ wildsub(q->depname->namep,stem));
+ if(q->depname &&
+ ! chkname(wildsub(q->depname->namep,stem)))
+ break;
+ }
+
+ if(q) /* some name not found, go to next line */
+ continue;
+
+ for(q = lp->depp; q; q = q->nxtdepblock)
+ {
+ nameblkp tamep;
+ if(q->depname == NULL)
+ continue;
+ tamep = srchname( wildsub(q->depname->namep,stem));
+/*TEMP fprintf(stderr,"check dep %s on %s =>%s\n",p->namep,q->depname->namep,tamep->namep);*/
+/*TEMP*/if(dbgflag) printf("%s depends on %s. stem=%s\n", p->namep,tamep->namep, stem);
+ errstat += doname(tamep, reclevel+1, &td, q->nowait);
+ if(ptime < td)
+ qchain = appendq(qchain, tamep->namep);
+ allchain = appendq(allchain, tamep->namep);
+ if(dbgflag) printf("TIME(%s)=%ld\n", tamep->namep, td);
+ if(td > tdep)
+ tdep = td;
+ set3var("<", copys(tamep->namep) );
+ }
+ set3var("*", stem);
+ setvar("%", stem, YES);
+ implcom = lp->shp;
+ goto endloop;
+ }
+
+endloop:
+
+
+if(errstat==0 && (ptime<tdep || (ptime==0 && tdep==0) ) )
+ {
+ ptime = (tdep>0 ? tdep : prestime() );
+ set3var("@", p->namep);
+ setvar("?", mkqlist(qchain,qbuf), YES);
+ setvar("^", mkqlist(allchain,tgsbuf), YES);
+ if(explcom)
+ errstat += docom(explcom, nowait, nproc1);
+ else if(implcom)
+ errstat += docom(implcom, nowait, nproc1);
+ else if(p->septype == 0)
+ if(p1=srchname(".DEFAULT"))
+ {
+ set3var("<", p->namep);
+ for(lp2 = p1->linep ; lp2 ; lp2 = lp2->nxtlineblock)
+ if(implcom = lp2->shp)
+ {
+ errstat += docom(implcom, nowait,nproc1);
+ break;
+ }
+ }
+ else if(keepgoing)
+ {
+ printf("Don't know how to make %s\n", p->namep);
+ ++errstat;
+ }
+ else
+ fatal1(" Don't know how to make %s", p->namep);
+
+ set3var("@", CHNULL);
+ if(noexflag || nowait || (ptime = exists(p->namep)) == 0 )
+ ptime = prestime();
+ }
+
+else if(errstat!=0 && reclevel==0)
+ printf("`%s' not remade because of errors\n", p->namep);
+
+else if(!questflag && reclevel==0 && didwork==NO)
+ printf("`%s' is up to date.\n", p->namep);
+
+if(questflag && reclevel==0)
+ exit(ndocoms>0 ? -1 : 0);
+
+p->done = (errstat ? 3 : 2);
+if(ptime1 > ptime)
+ ptime = ptime1;
+p->modtime = ptime;
+*tval = ptime;
+return errstat;
+}
+
+docom(struct shblock *q, int nowait, int nproc1)
+{
+char *s;
+int ign, nopr, doit;
+char string[OUTMAX];
+
+++ndocoms;
+if(questflag)
+ return NO;
+
+if(touchflag)
+ {
+ s = varptr("@")->varval;
+ if(!silflag)
+ printf("touch(%s)\n", s);
+ if(!noexflag)
+ touch(YES, s);
+ return NO;
+ }
+
+if(nproc1 < nproc)
+ waitstack(nproc1);
+
+for( ; q ; q = q->nxtshblock )
+ {
+ subst(q->shbp,string);
+ ign = ignerr;
+ nopr = NO;
+ doit = NO;
+ for(s = string ; ; ++s)
+ {
+ switch(*s)
+ {
+ case '-':
+ ign = YES;
+ continue;
+ case '@':
+ nopr = YES;
+ continue;
+ case '+':
+ doit = YES;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+
+ if( docom1(s, ign, nopr, doit||!noexflag, nowait&&!q->nxtshblock) && !ign)
+ return YES;
+ }
+return NO;
+}
+
+
+static int
+docom1(char *comstring, int nohalt, int noprint, int doit, int nowait)
+{
+int status;
+char *prefix;
+
+if(comstring[0] == '\0')
+ return 0;
+
+if(!silflag && (!noprint || !doit) )
+ prefix = doit ? prompt : "" ;
+else
+ prefix = CHNULL;
+
+if(dynmacro(comstring) || !doit)
+ {
+ if(prefix)
+ {
+ fputs(prefix, stdout);
+ puts(comstring); /* with a newline */
+ fflush(stdout);
+ }
+ return 0;
+ }
+
+status = dosys(comstring, nohalt, nowait, prefix);
+baddirs(); /* directories may have changed */
+return status;
+}
+
+
+/*
+ If there are any Shell meta characters in the name,
+ expand into a list, after searching directory
+*/
+
+static void
+expand(depblkp q)
+{
+char *s;
+char *s1;
+depblkp p;
+
+s1 = q->depname->namep;
+for(s=s1 ; ;) switch(*s++)
+ {
+ case '\0':
+ return;
+
+ case '*':
+ case '?':
+ case '[':
+ if( p = srchdir(s1 , YES, q->nxtdepblock) )
+ {
+ q->nxtdepblock = p;
+ q->depname = 0;
+ }
+ return;
+ }
+}
diff --git a/sys/src/ape/cmd/make/dosys.c b/sys/src/ape/cmd/make/dosys.c
new file mode 100755
index 000000000..7d28eaa4d
--- /dev/null
+++ b/sys/src/ape/cmd/make/dosys.c
@@ -0,0 +1,287 @@
+#include "defs.h"
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static int metas(char *);
+static int waitproc(int *);
+static int doshell(char *, int);
+static int doexec(char *);
+
+int
+dosys(char *comstring, int nohalt, int nowait, char *prefix)
+{
+int status;
+struct process *procp;
+
+/* make sure there is room in the process stack */
+if(nproc >= MAXPROC)
+ waitstack(MAXPROC-1);
+
+/* make sure fewer than proclimit processes are running */
+while(proclive >= proclimit)
+ {
+ enbint(SIG_IGN);
+ waitproc(&status);
+ enbint(intrupt);
+ }
+
+if(prefix)
+ {
+ fputs(prefix, stdout);
+ fputs(comstring, stdout);
+ }
+
+procp = procstack + nproc;
+procp->pid = (forceshell || metas(comstring) ) ?
+ doshell(comstring,nohalt) : doexec(comstring);
+if(procp->pid == -1)
+ fatal("fork failed");
+procstack[nproc].nohalt = nohalt;
+procstack[nproc].nowait = nowait;
+procstack[nproc].done = NO;
+++proclive;
+++nproc;
+
+if(nowait)
+ {
+ printf(" &%d\n", procp->pid);
+ fflush(stdout);
+ return 0;
+ }
+if(prefix)
+ {
+ putchar('\n');
+ fflush(stdout);
+ }
+return waitstack(nproc-1);
+}
+
+static int
+metas(char *s) /* Are there are any Shell meta-characters? */
+{
+char c;
+
+while( (funny[c = *s++] & META) == 0 )
+ ;
+return( c );
+}
+
+static void
+doclose(void) /* Close open directory files before exec'ing */
+{
+struct dirhd *od;
+
+for (od = firstod; od; od = od->nxtdirhd)
+ if(od->dirfc)
+ closedir(od->dirfc);
+}
+
+/* wait till none of the processes in the stack starting at k is live */
+int
+waitstack(int k)
+{
+int npending, status, totstatus;
+int i;
+
+totstatus = 0;
+npending = 0;
+for(i=k ; i<nproc; ++i)
+ if(! procstack[i].done)
+ ++npending;
+enbint(SIG_IGN);
+if(dbgflag > 1)
+ printf("waitstack(%d)\n", k);
+
+while(npending>0 && proclive>0)
+ {
+ if(waitproc(&status) >= k)
+ --npending;
+ totstatus |= status;
+ }
+
+if(nproc > k)
+ nproc = k;
+enbint(intrupt);
+return totstatus;
+}
+
+static int
+waitproc(int *statp)
+{
+pid_t pid;
+int status;
+int i;
+struct process *procp;
+char junk[50];
+static int inwait = NO;
+
+if(inwait) /* avoid infinite recursions on errors */
+ return MAXPROC;
+inwait = YES;
+
+pid = wait(&status);
+if(dbgflag > 1)
+ fprintf(stderr, "process %d done, status = %d\n", pid, status);
+if(pid == -1)
+ {
+ if(errno == ECHILD) /* multiple deaths, no problem */
+ {
+ if(proclive)
+ {
+ for(i=0, procp=procstack; i<nproc; ++i, ++procp)
+ procp->done = YES;
+ proclive = nproc = 0;
+ }
+ return MAXPROC;
+ }
+ fatal("bad wait code");
+ }
+for(i=0, procp=procstack; i<nproc; ++i, ++procp)
+ if(procp->pid == pid)
+ {
+ --proclive;
+ procp->done = YES;
+
+ if(status)
+ {
+ if(procp->nowait)
+ printf("%d: ", pid);
+ if( WEXITSTATUS(status) )
+ printf("*** Error code %d", WEXITSTATUS(status) );
+ else printf("*** Termination code %d", WTERMSIG(status));
+
+ printf(procp->nohalt ? "(ignored)\n" : "\n");
+ fflush(stdout);
+ if(!keepgoing && !procp->nohalt)
+ fatal(CHNULL);
+ }
+ *statp = status;
+ inwait = NO;
+ return i;
+ }
+
+sprintf(junk, "spurious return from process %d", pid);
+fatal(junk);
+/*NOTREACHED*/
+}
+
+static int
+doshell(char *comstring, int nohalt)
+{
+pid_t pid;
+
+if((pid = fork()) == 0)
+ {
+ enbint(SIG_DFL);
+ doclose();
+
+ execl(SHELLCOM, "sh", (nohalt ? "-c" : "-ce"), comstring, NULL);
+ fatal("Couldn't load Shell");
+ }
+
+return pid;
+}
+
+static int
+doexec(char *str)
+{
+char *t, *tend;
+char **argv;
+char **p;
+int nargs;
+pid_t pid;
+
+while( *str==' ' || *str=='\t' )
+ ++str;
+if( *str == '\0' )
+ return(-1); /* no command */
+
+nargs = 1;
+for(t = str ; *t ; )
+ {
+ ++nargs;
+ while(*t!=' ' && *t!='\t' && *t!='\0')
+ ++t;
+ if(*t) /* replace first white space with \0, skip rest */
+ for( *t++ = '\0' ; *t==' ' || *t=='\t' ; ++t)
+ ;
+ }
+
+/* now allocate args array, copy pointer to start of each string,
+ then terminate array with a null
+*/
+p = argv = (char **) ckalloc(nargs*sizeof(char *));
+tend = t;
+for(t = str ; t<tend ; )
+ {
+ *p++ = t;
+ while( *t )
+ ++t;
+ do {
+ ++t;
+ } while(t<tend && (*t==' ' || *t=='\t') );
+ }
+*p = NULL;
+/*TEMP for(p=argv; *p; ++p)printf("arg=%s\n", *p);*/
+
+if((pid = fork()) == 0)
+ {
+ enbint(SIG_DFL);
+ doclose();
+ enbint(intrupt);
+ execvp(str, argv);
+ printf("\n");
+ fatal1("Cannot load %s",str);
+ }
+
+free( (char *) argv);
+return pid;
+}
+
+void
+touch(int force, char *name)
+{
+struct stat stbuff;
+char junk[1];
+int fd;
+
+if( stat(name,&stbuff) < 0)
+ if(force)
+ goto create;
+ else
+ {
+ fprintf(stderr, "touch: file %s does not exist.\n", name);
+ return;
+ }
+
+if(stbuff.st_size == 0)
+ goto create;
+
+if( (fd = open(name, O_RDWR)) < 0)
+ goto bad;
+
+if( read(fd, junk, 1) < 1)
+ {
+ close(fd);
+ goto bad;
+ }
+lseek(fd, 0L, SEEK_SET);
+if( write(fd, junk, 1) < 1 )
+ {
+ close(fd);
+ goto bad;
+ }
+close(fd);
+return;
+
+bad:
+ fprintf(stderr, "Cannot touch %s\n", name);
+ return;
+
+create:
+ if( (fd = creat(name, 0666)) < 0)
+ goto bad;
+ close(fd);
+}
diff --git a/sys/src/ape/cmd/make/files.c b/sys/src/ape/cmd/make/files.c
new file mode 100755
index 000000000..59b8ddf84
--- /dev/null
+++ b/sys/src/ape/cmd/make/files.c
@@ -0,0 +1,552 @@
+/* POSIX DEPENDENT PROCEDURES */
+#include "defs.h"
+#include <sys/stat.h>
+#include <ar.h>
+
+#define NAMESPERBLOCK 32
+
+/* DEFAULT RULES FOR POSIX */
+
+char *dfltmacro[] =
+ {
+ ".SUFFIXES : .o .c .y .l .a .sh .f",
+ "MAKE=make",
+ "AR=ar",
+ "ARFLAGS=rv",
+ "YACC=yacc",
+ "YFLAGS=",
+ "LEX=lex",
+ "LFLAGS=",
+ "LDFLAGS=",
+ "CC=c89",
+ "CFLAGS=-O",
+ "FC=fort77",
+ "FFLAGS=-O 1",
+ 0 };
+
+char *dfltpat[] =
+ {
+ "%.o : %.c",
+ "\t$(CC) $(CFLAGS) -c $<",
+
+ "%.o : %.y",
+ "\t$(YACC) $(YFLAGS) $<",
+ "\t$(CC) $(CFLAGS) -c y.tab.c",
+ "\trm y.tab.c",
+ "\tmv y.tab.o $@",
+
+ "%.o : %.l",
+ "\t$(LEX) $(LFLAGS) $<",
+ "\t$(CC) $(CFLAGS) -c lex.yy.c",
+ "\trm lex.yy.c",
+ "\tmv lex.yy.o $@",
+
+ "%.c : %.y",
+ "\t$(YACC) $(YFLAGS) $<",
+ "\tmv y.tab.c $@",
+
+ "%.c : %.l",
+ "\t$(LEX) $(LFLAGS) $<",
+ "\tmv lex.yy.c $@",
+
+ "% : %.o",
+ "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<",
+
+ "% : %.c",
+ "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<",
+
+ 0 };
+
+
+
+char *dfltsuff[] =
+ {
+ ".SUFFIXES : .o .c .y .l .a .sh .f",
+ ".c.o :",
+ "\t$(CC) $(CFLAGS) -c $<",
+
+ ".f.o :",
+ "\t$(FC) $(FFLAGS) -c $<",
+
+ ".y.o :",
+ "\t$(YACC) $(YFLAGS) $<",
+ "\t$(CC) $(CFLAGS) -c y.tab.c",
+ "\trm -f y.tab.c",
+ "\tmv y.tab.o $@",
+
+ ".l.o :",
+ "\t$(LEX) $(LFLAGS) $<",
+ "\t$(CC) $(CFLAGS) -c lex.yy.c",
+ "\trm -f lex.yy.c",
+ "\tmv lex.yy.o $@",
+
+ ".y.c :",
+ "\t$(YACC) $(YFLAGS) $<",
+ "\tmv y.tab.c $@",
+
+ ".l.c :",
+ "\t$(LEX) $(LFLAGS) $<",
+ "\tmv lex.yy.c $@",
+
+ ".c.a:",
+ "\t$(CC) -c $(CFLAGS) $<",
+ "\t$(AR) $(ARFLAGS) $@ $*.o",
+ "\trm -f $*.o",
+
+ ".f.a:",
+ "\t$(FC) -c $(FFLAGS) $<",
+ "\t$(AR) $(ARFLAGS) $@ $*.o",
+ "\trm -f $*.o",
+
+ ".c:",
+ "\t$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<",
+
+ ".f:",
+ "\t$(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<",
+
+ ".sh:",
+ "\tcp $< $@",
+ "\tchmod a+x $@",
+
+ 0 };
+
+
+static struct dirhd *opdir(char *, int);
+static void cldir(struct dirhd *, int);
+static int amatch(char *, char *);
+static int umatch(char *, char *);
+static void clarch(void);
+static int openarch(char *);
+static int getarch(void);
+
+time_t
+exists(char *filename)
+{
+struct stat buf;
+char *s;
+
+for(s = filename ; *s!='\0' && *s!='(' && *s!=')' ; ++s)
+ ;
+
+if(*s != '\0')
+ return lookarch(filename);
+
+if(stat(filename,&buf) < 0)
+ return 0;
+else return buf.st_mtime;
+}
+
+
+time_t
+prestime(void)
+{
+time_t t;
+time(&t);
+return t;
+}
+
+static char nmtemp[MAXNAMLEN+1]; /* guarantees a null after the name */
+static char *tempend = nmtemp + MAXNAMLEN;
+
+
+
+depblkp
+srchdir(char *pat, int mkchain, depblkp nextdbl)
+{
+DIR *dirf;
+struct dirhd *dirptr;
+char *dirname, *dirpref, *endir, *filepat, *p, temp[100];
+char fullname[100];
+nameblkp q;
+depblkp thisdbl;
+struct pattern *patp;
+
+struct dirent *dptr;
+
+thisdbl = 0;
+
+if(mkchain == NO)
+ for(patp=firstpat ; patp ; patp = patp->nxtpattern)
+ if(equal(pat, patp->patval)) return 0;
+
+patp = ALLOC(pattern);
+patp->nxtpattern = firstpat;
+firstpat = patp;
+patp->patval = copys(pat);
+
+endir = 0;
+
+for(p=pat; *p!='\0'; ++p)
+ if(*p=='/') endir = p;
+
+if(endir==0)
+ {
+ dirname = ".";
+ dirpref = "";
+ filepat = pat;
+ }
+else {
+ dirname = pat;
+ *endir = '\0';
+ dirpref = concat(dirname, "/", temp);
+ filepat = endir+1;
+ }
+
+dirptr = opdir(dirname,YES);
+dirf = dirptr->dirfc;
+
+for( dptr = readdir(dirf) ; dptr ; dptr = readdir(dirf) )
+ {
+ char *p1, *p2;
+ p1 = dptr->d_name;
+ p2 = nmtemp;
+ while( (p2<tempend) && (*p2++ = *p1++)!='\0')
+ ;
+ if( amatch(nmtemp,filepat) )
+ {
+ concat(dirpref,nmtemp,fullname);
+ if( (q=srchname(fullname)) ==0)
+ q = makename(copys(fullname));
+ if(mkchain)
+ {
+ thisdbl = ALLOC(depblock);
+ thisdbl->nxtdepblock = nextdbl;
+ thisdbl->depname = q;
+ nextdbl = thisdbl;
+ }
+ }
+ }
+
+
+if(endir)
+ *endir = '/';
+
+cldir(dirptr, YES);
+
+return thisdbl;
+}
+
+static struct dirhd *
+opdir(char *dirname, int stopifbad)
+{
+struct dirhd *od;
+
+for(od = firstod; od; od = od->nxtdirhd)
+ if(equal(dirname, od->dirn) )
+ break;
+
+if(od == NULL)
+ {
+ ++nopdir;
+ od = ALLOC(dirhd);
+ od->nxtdirhd = firstod;
+ firstod = od;
+ od->dirn = copys(dirname);
+ }
+
+if(od->dirfc==NULL && (od->dirfc = opendir(dirname)) == NULL && stopifbad)
+ {
+ fprintf(stderr, "Directory %s: ", dirname);
+ fatal("Cannot open");
+ }
+
+return od;
+}
+
+
+static void
+cldir(struct dirhd *dp, int used)
+{
+if(nopdir >= MAXDIR)
+ {
+ closedir(dp->dirfc);
+ dp->dirfc = NULL;
+ }
+else if(used)
+ rewinddir(dp->dirfc); /* start over at the beginning */
+}
+
+/* stolen from glob through find */
+
+static int
+amatch(char *s, char *p)
+{
+ int cc, scc, k;
+ int c, lc;
+
+ scc = *s;
+ lc = 077777;
+ switch (c = *p) {
+
+ case '[':
+ k = 0;
+ while (cc = *++p) {
+ switch (cc) {
+
+ case ']':
+ if (k)
+ return amatch(++s, ++p);
+ else
+ return 0;
+
+ case '-':
+ k |= (lc <= scc) & (scc <= (cc=p[1]) ) ;
+ }
+ if (scc==(lc=cc)) k++;
+ }
+ return 0;
+
+ case '?':
+ caseq:
+ if(scc) return amatch(++s, ++p);
+ return 0;
+ case '*':
+ return umatch(s, ++p);
+ case 0:
+ return !scc;
+ }
+ if (c==scc) goto caseq;
+ return 0;
+}
+
+static int
+umatch(char *s, char *p)
+{
+ if(*p==0) return 1;
+ while(*s)
+ if (amatch(s++,p)) return 1;
+ return 0;
+}
+
+#ifdef METERFILE
+#include <pwd.h>
+int meteron = 0; /* default: metering off */
+
+extern void meter(char *file)
+{
+time_t tvec;
+char *p;
+FILE * mout;
+struct passwd *pwd;
+
+if(file==0 || meteron==0) return;
+
+pwd = getpwuid(getuid());
+
+time(&tvec);
+
+if( mout = fopen(file,"a") )
+ {
+ p = ctime(&tvec);
+ p[16] = '\0';
+ fprintf(mout, "User %s, %s\n", pwd->pw_name, p+4);
+ fclose(mout);
+ }
+}
+#endif
+
+
+/* look inside archives for notation a(b)
+ a(b) is file member b in archive a
+*/
+
+static long arflen;
+static long arfdate;
+static char arfname[16];
+FILE *arfd;
+long int arpos, arlen;
+
+time_t
+lookarch(char *filename)
+{
+char *p, *q, *send, s[15], pad;
+int i, nc, nsym;
+
+for(p = filename; *p!= '(' ; ++p)
+ ;
+
+*p = '\0';
+if( ! openarch(filename) )
+ {
+ *p = '(';
+ return 0L;
+ }
+*p++ = '(';
+nc = 14;
+pad = ' ';
+
+send = s + nc;
+for( q = s ; q<send && *p!='\0' && *p!=')' ; *q++ = *p++ )
+ ;
+if(p[0]==')' && p[1]!='\0') /* forbid stuff after the paren */
+ {
+ clarch();
+ return 0L;
+ }
+while(q < send)
+ *q++ = pad;
+while(getarch())
+ {
+ if( !strncmp(arfname, s, nc))
+ {
+ clarch();
+/*TEMP fprintf(stderr, "found archive member %14s, time=%d\n", s, arfdate); */
+ return arfdate;
+ }
+ }
+
+clarch();
+return 0L;
+}
+
+static void
+clarch(void)
+{
+fclose( arfd );
+}
+
+static int
+openarch(char *f)
+{
+char magic[SARMAG];
+int word;
+struct stat buf;
+nameblkp p;
+
+stat(f, &buf);
+arlen = buf.st_size;
+
+arfd = fopen(f, "r");
+if(arfd == NULL)
+ return NO;
+ /* fatal1("cannot open %s", f); */
+
+fread( (char *) &word, sizeof(word), 1, arfd);
+
+fseek(arfd, 0L, 0);
+fread(magic, SARMAG, 1, arfd);
+arpos = SARMAG;
+if( strncmp(magic, ARMAG, SARMAG) )
+ fatal1("%s is not an archive", f);
+
+if( !(p = srchname(f)) )
+ p = makename( copys(f) );
+p->isarch = YES;
+arflen = 0;
+return YES;
+}
+
+
+static int
+getarch(void)
+{
+struct ar_hdr arhead;
+
+arpos += (arflen + 1) & ~1L; /* round archived file length up to even */
+if(arpos >= arlen)
+ return 0;
+fseek(arfd, arpos, 0);
+
+fread( (char *) &arhead, sizeof(arhead), 1, arfd);
+arpos += sizeof(arhead);
+arflen = atol(arhead.ar_size);
+arfdate = atol(arhead.ar_date);
+strncpy(arfname, arhead.ar_name, sizeof(arhead.ar_name));
+return 1;
+}
+
+/* find the directory containing name.
+ read it into the hash table if it hasn't been used before or if
+ if might have changed since last reference
+*/
+
+void
+dirsrch(char *name)
+{
+DIR *dirf;
+struct dirhd *dirp;
+time_t dirt, objt;
+int dirused, hasparen;
+char *dirname, *lastslash;
+char *fullname, *filepart, *fileend, *s;
+struct dirent *dptr;
+
+lastslash = NULL;
+hasparen = NO;
+
+for(s=name; *s; ++s)
+ if(*s == '/')
+ lastslash = s;
+ else if(*s=='(' || *s==')')
+ hasparen = YES;
+
+if(hasparen)
+ {
+ if(objt = lookarch(name))
+ makename(name)->modtime = objt;
+ return;
+ }
+
+if(lastslash)
+ {
+ dirname = name;
+ *lastslash = '\0';
+ }
+else
+ dirname = ".";
+
+dirused = NO;
+dirp = opdir(dirname, NO);
+dirf = dirp->dirfc;
+if(dirp->dirok || !dirf)
+ goto ret;
+dirt = exists(dirname);
+if(dirp->dirtime == dirt)
+ goto ret;
+
+dirp->dirok = YES;
+dirp->dirtime = dirt;
+dirused = YES;
+
+/* allocate buffer to hold full file name */
+if(lastslash)
+ {
+ fullname = (char *) ckalloc(strlen(dirname)+MAXNAMLEN+2);
+ concat(dirname, "/", fullname);
+ filepart = fullname + strlen(fullname);
+ }
+else
+ filepart = fullname = (char *) ckalloc(MAXNAMLEN+1);
+
+
+fileend = filepart + MAXNAMLEN;
+*fileend = '\0';
+for(dptr = readdir(dirf) ; dptr ; dptr = readdir(dirf) )
+ {
+ char *p1, *p2;
+ p1 = dptr->d_name;
+ p2 = filepart;
+ while( (p2<fileend) && (*p2++ = *p1++)!='\0')
+ ;
+ if( ! srchname(fullname) )
+ (void) makename(copys(fullname));
+ }
+
+free(fullname);
+
+ret:
+ cldir(dirp, dirused);
+ if(lastslash)
+ *lastslash = '/';
+}
+
+
+
+void
+baddirs(void)
+{
+struct dirhd *od;
+
+for(od = firstod; od; od = od->nxtdirhd)
+ od->dirok = NO;
+}
diff --git a/sys/src/ape/cmd/make/gram.y b/sys/src/ape/cmd/make/gram.y
new file mode 100755
index 000000000..db83c5148
--- /dev/null
+++ b/sys/src/ape/cmd/make/gram.y
@@ -0,0 +1,441 @@
+%{#include "defs.h"
+%}
+
+%term NAME SHELLINE START MACRODEF COLON DOUBLECOLON GREATER AMPER AMPERAMPER
+%union
+ {
+ struct shblock *yshblock;
+ depblkp ydepblock;
+ nameblkp ynameblock;
+ }
+
+%type <yshblock> SHELLINE, shlist, shellist
+%type <ynameblock> NAME, namelist
+%type <ydepblock> deplist, dlist
+
+
+%%
+
+%{
+struct depblock *pp;
+static struct shblock *prevshp;
+
+static struct nameblock *lefts[NLEFTS];
+struct nameblock *leftp;
+static int nlefts;
+
+struct lineblock *lp, *lpp;
+static struct depblock *prevdep;
+static int sepc;
+static int allnowait;
+
+static struct fstack
+ {
+ FILE *fin;
+ char *fname;
+ int lineno;
+ } filestack[MAXINCLUDE];
+static int ninclude = 0;
+%}
+
+
+file:
+ | file comline
+ ;
+
+comline: START
+ | MACRODEF
+ | START namelist deplist shellist = {
+ while( --nlefts >= 0)
+ {
+ wildp wp;
+
+ leftp = lefts[nlefts];
+ if(wp = iswild(leftp->namep))
+ {
+ leftp->septype = SOMEDEPS;
+ if(lastwild)
+ lastwild->next = wp;
+ else
+ firstwild = wp;
+ lastwild = wp;
+ }
+
+ if(leftp->septype == 0)
+ leftp->septype = sepc;
+ else if(leftp->septype != sepc)
+ {
+ if(! wp)
+ fprintf(stderr,
+ "Inconsistent rules lines for `%s'\n",
+ leftp->namep);
+ }
+ else if(sepc==ALLDEPS && leftp->namep[0]!='.' && $4!=0)
+ {
+ for(lp=leftp->linep; lp->nxtlineblock; lp=lp->nxtlineblock)
+ if(lp->shp)
+ fprintf(stderr,
+ "Multiple rules lines for `%s'\n",
+ leftp->namep);
+ }
+
+ lp = ALLOC(lineblock);
+ lp->nxtlineblock = NULL;
+ lp->depp = $3;
+ lp->shp = $4;
+ if(wp)
+ wp->linep = lp;
+
+ if(equal(leftp->namep, ".SUFFIXES") && $3==0)
+ leftp->linep = 0;
+ else if(leftp->linep == 0)
+ leftp->linep = lp;
+ else {
+ for(lpp = leftp->linep; lpp->nxtlineblock;
+ lpp = lpp->nxtlineblock) ;
+ if(sepc==ALLDEPS && leftp->namep[0]=='.')
+ lpp->shp = 0;
+ lpp->nxtlineblock = lp;
+ }
+ }
+ }
+ | error
+ ;
+
+namelist: NAME = { lefts[0] = $1; nlefts = 1; }
+ | namelist NAME = { lefts[nlefts++] = $2;
+ if(nlefts>=NLEFTS) fatal("Too many lefts"); }
+ ;
+
+deplist:
+ {
+ char junk[100];
+ sprintf(junk, "%s:%d", filestack[ninclude-1].fname, yylineno);
+ fatal1("Must be a separator on rules line %s", junk);
+ }
+ | dlist
+ ;
+
+dlist: sepchar = { prevdep = 0; $$ = 0; allnowait = NO; }
+ | sepchar AMPER = { prevdep = 0; $$ = 0; allnowait = YES; }
+ | dlist NAME = {
+ pp = ALLOC(depblock);
+ pp->nxtdepblock = NULL;
+ pp->depname = $2;
+ pp->nowait = allnowait;
+ if(prevdep == 0) $$ = pp;
+ else prevdep->nxtdepblock = pp;
+ prevdep = pp;
+ }
+ | dlist AMPER = { if(prevdep) prevdep->nowait = YES; }
+ | dlist AMPERAMPER
+ ;
+
+sepchar: COLON = { sepc = ALLDEPS; }
+ | DOUBLECOLON = { sepc = SOMEDEPS; }
+ ;
+
+shellist: = {$$ = 0; }
+ | shlist = { $$ = $1; }
+ ;
+
+shlist: SHELLINE = { $$ = $1; prevshp = $1; }
+ | shlist SHELLINE = { $$ = $1;
+ prevshp->nxtshblock = $2;
+ prevshp = $2;
+ }
+ ;
+
+%%
+
+static char *zznextc; /* null if need another line;
+ otherwise points to next char */
+static int yylineno;
+static FILE * fin;
+static int retsh(char *);
+static int nextlin(void);
+static int isinclude(char *);
+
+int yyparse(void);
+
+int
+parse(char *name)
+{
+FILE *stream;
+
+if(name == CHNULL)
+ {
+ stream = NULL;
+ name = "(builtin-rules)";
+ }
+else if(equal(name, "-"))
+ {
+ stream = stdin;
+ name = "(stdin)";
+ }
+else if( (stream = fopen(name, "r")) == NULL)
+ return NO;
+filestack[0].fname = copys(name);
+ninclude = 1;
+fin = stream;
+yylineno = 0;
+zznextc = 0;
+
+if( yyparse() )
+ fatal("Description file error");
+
+if(fin)
+ fclose(fin);
+return YES;
+}
+
+int
+yylex(void)
+{
+char *p;
+char *q;
+char word[INMAX];
+
+if(! zznextc )
+ return nextlin() ;
+
+while( isspace(*zznextc) )
+ ++zznextc;
+switch(*zznextc)
+ {
+ case '\0':
+ return nextlin() ;
+
+ case '|':
+ if(zznextc[1]==':')
+ {
+ zznextc += 2;
+ return DOUBLECOLON;
+ }
+ break;
+ case ':':
+ if(*++zznextc == ':')
+ {
+ ++zznextc;
+ return DOUBLECOLON;
+ }
+ return COLON;
+ case '>':
+ ++zznextc;
+ return GREATER;
+ case '&':
+ if(*++zznextc == '&')
+ {
+ ++zznextc;
+ return AMPERAMPER;
+ }
+ return AMPER;
+ case ';':
+ return retsh(zznextc) ;
+ }
+
+p = zznextc;
+q = word;
+
+while( ! ( funny[*p] & TERMINAL) )
+ *q++ = *p++;
+
+if(p != zznextc)
+ {
+ *q = '\0';
+ if((yylval.ynameblock=srchname(word))==0)
+ yylval.ynameblock = makename(word);
+ zznextc = p;
+ return NAME;
+ }
+
+else {
+ char junk[100];
+ sprintf(junk, "Bad character %c (octal %o), line %d of file %s",
+ *zznextc, *zznextc, yylineno, filestack[ninclude-1].fname);
+ fatal(junk);
+ }
+return 0; /* never executed */
+}
+
+
+
+
+static int
+retsh(char *q)
+{
+register char *p;
+struct shblock *sp;
+
+for(p=q+1 ; *p==' '||*p=='\t' ; ++p) ;
+
+sp = ALLOC(shblock);
+sp->nxtshblock = NULL;
+sp->shbp = (fin ? copys(p) : p );
+yylval.yshblock = sp;
+zznextc = 0;
+return SHELLINE;
+}
+
+static int
+nextlin(void)
+{
+static char yytext[INMAX];
+static char *yytextl = yytext+INMAX;
+char *text, templin[INMAX];
+char c;
+char *p, *t;
+char lastch, *lastchp;
+extern char **linesptr;
+int incom;
+int kc;
+
+again:
+
+ incom = NO;
+ zznextc = 0;
+
+if(fin == NULL)
+ {
+ if( (text = *linesptr++) == 0)
+ return 0;
+ ++yylineno;
+ }
+
+else {
+ for(p = text = yytext ; p<yytextl ; *p++ = kc)
+ switch(kc = getc(fin))
+ {
+ case '\t':
+ if(p == yytext)
+ incom = YES;
+ break;
+
+ case ';':
+ incom = YES;
+ break;
+
+ case '#':
+ if(! incom)
+ kc = '\0';
+ break;
+
+ case '\n':
+ ++yylineno;
+ if(p==yytext || p[-1]!='\\')
+ {
+ *p = '\0';
+ goto endloop;
+ }
+ p[-1] = ' ';
+ while( (kc=getc(fin))=='\t' || kc==' ' || kc=='\n')
+ if(kc == '\n')
+ ++yylineno;
+
+ if(kc != EOF)
+ break;
+ case EOF:
+ *p = '\0';
+ if(ninclude > 1)
+ {
+ register struct fstack *stp;
+ fclose(fin);
+ --ninclude;
+ stp = filestack + ninclude;
+ fin = stp->fin;
+ yylineno = stp->lineno;
+ free(stp->fname);
+ goto again;
+ }
+ return 0;
+ }
+
+ fatal("line too long");
+ }
+
+endloop:
+
+ if((c = text[0]) == '\t')
+ return retsh(text) ;
+
+ if(isalpha(c) || isdigit(c) || c==' ' || c=='.'|| c=='_')
+ for(p=text+1; *p!='\0'; )
+ if(*p == ':')
+ break;
+ else if(*p++ == '=')
+ {
+ eqsign(text);
+ return MACRODEF;
+ }
+
+/* substitute for macros on dependency line up to the semicolon if any */
+
+for(t = yytext ; *t!='\0' && *t!=';' ; ++t)
+ ;
+
+lastchp = t;
+lastch = *t;
+*t = '\0'; /* replace the semi with a null so subst will stop */
+
+subst(yytext, templin); /* Substitute for macros on dependency lines */
+
+if(lastch) /* copy the stuff after the semicolon */
+ {
+ *lastchp = lastch;
+ strcat(templin, lastchp);
+ }
+
+strcpy(yytext, templin);
+
+/* process include files after macro substitution */
+if(strncmp(text, "include", 7) == 0) {
+ if (isinclude(text+7))
+ goto again;
+}
+
+for(p = zznextc = text ; *p ; ++p )
+ if(*p!=' ' && *p!='\t')
+ return START;
+goto again;
+}
+
+
+static int
+isinclude(char *s)
+{
+char *t;
+struct fstack *p;
+
+for(t=s; *t==' ' || *t=='\t' ; ++t)
+ ;
+if(t == s)
+ return NO;
+
+for(s = t; *s!='\n' && *s!='#' && *s!='\0' ; ++s)
+ if(*s == ':')
+ return NO;
+*s = '\0';
+
+if(ninclude >= MAXINCLUDE)
+ fatal("include depth exceeded");
+p = filestack + ninclude;
+p->fin = fin;
+p->lineno = yylineno;
+p->fname = copys(t);
+if( (fin = fopen(t, "r")) == NULL)
+ fatal1("Cannot open include file %s", t);
+yylineno = 0;
+++ninclude;
+return YES;
+}
+
+
+int
+yyerror(char *s, ...)
+{
+char buf[100];
+
+sprintf(buf, "line %d of file %s: %s",
+ yylineno, filestack[ninclude-1].fname, s);
+fatal(buf);
+}
diff --git a/sys/src/ape/cmd/make/ident.c b/sys/src/ape/cmd/make/ident.c
new file mode 100755
index 000000000..00df0b9de
--- /dev/null
+++ b/sys/src/ape/cmd/make/ident.c
@@ -0,0 +1,125 @@
+char *xxxvers = "\n@(#) MAKE. VERSION 2.78 22 MAY 1986\n" ;
+static char *sccsid = "@(#)ident.c 8th Edition (Bell Labs) 85/10/28";
+
+/*
+2.1 4/24/76 Base version
+
+2.2 4/26/76 Error found by SRB in overriding pattern rules;
+ corrected gram.y
+
+2.3 4/27/76 Further correction for overriding pattern rules;
+ corrected doname.c
+
+2.4 Removed .CLEAR name, added .IGNORE.
+ A .SUFFIXES rule without dependents clears the list
+
+2.5 Stripped output
+
+2.6 Changed doshell to accomodate new shell.
+
+2.7 Following SRB's sugestion, added ${...} as
+ alternate macro name
+
+2.8 Defined macros AS and DTGEN in files.c.
+
+2.9 Put in a fix to prevent removal of files
+ upon interrupt in a :: rule.
+
+2.10 Fixed bugs involving messages for ::
+ and closing standard input
+
+2.11 Changed time test from <= to <
+ (equal times are considered in sync)
+
+2.12 Installed -t flag (touch and update time of
+ files rather than issue commands)
+ Fixed bug in dosys
+
+2.13 Fixed lex.c to allow sharps (#) in commands
+
+2.14 Added .DEFAULT rule
+
+2.15 Changed to <lS> I/O System (stdio.h)
+
+2.16 Removed references to double floats and macro HAVELONGS;
+ committed to use of long ints for times.
+2.17 Corrected metacharacter list in dosys.c.
+2.18 Miscellaneous fixes
+2.19 Updated files.c to use include file stat.h
+2.20 Added -q flag for Mike Lesk
+2.21 Added AWK rules and .w suffix to files.c
+2.22 Added colon to the list of metacharacters
+2.23 Macro substitutions on dependency lines.
+ Redid argument and macro setting.
+ Close files before exec'ing.
+ Print > at beginning of command lines.
+ No printing of commands beginnng with @.
+2.24 Parametrized propt sequence in doname.c (4/1/77)
+2.25 Added $? facility
+2.26 Fixed bug in macro expansion
+2.27 Repaired interrupt handling
+2.28 Repaired bug in -n
+2.29 Repaired bug in file closing and $? string creation
+2.30 Repaired bug in grammar about command lines
+2.31 Added -k flag, modified doname.c and defs
+2.32 Made "keepgoing" the default, added -S flag,
+ changed handling of funny characters internally
+2.3 Small fixups to interrupt and quit handling.
+ Changed default back to -k.
+2.34 Added .PRECIOUS rule for interrupts
+2.35 Added references to include files (due to TLL)
+2.36 Fixed bug in lex.c so = permitted in rules on :; line
+2.37 Miscellaneous code cleanups
+2.38 Sleep one second after each touch in -t mode
+2.39 Extended string[] declaration in doname.c
+2.40 Permit recursive macro references
+2.41 Separated YYLMAX into INMAX and OUTMAX macros, specifying longest
+ input and output lines respectively.
+2.42 Fixed bug involving :: lines without dependents
+2.43 Main name is first name that contains a slash or doesn't
+ begin with a dot
+2.44 Fixed bug involving $$ on command line
+2.45 Changed files.c to put .f before .e, .r and to use f77 instead of fc.
+2.46 Changed dosys.c to eliminate copying and to call execvp.
+2.47 Changed files.c to add ".out" suffix and rules.
+2.48 Changed misc.c to permit tabs preceding = in macro definition
+2.49 Added reference to <ctyp.h>. Removed -lS references from files.c
+2.50 General cleanup to reduce lint messages. (changes in declarations
+ and in uses of variables)
+2.51 Further cleanup making use of new Yacc features.
+2.52
+2.53 Changed handling of "touch"
+2.54 Fixed bug involving comments in lexical analyzer.
+2.55 Ignore commands that begin with a # are comments.
+2.56 Added = to list of META characters (to permit shell commands)
+2.57 Changed lookarch and getobj to fix bugs.
+2.58 Fixed interrupt handling.
+2.59 Changed references to sprintf to accomodate new function definition
+ Also fixed extern declarations.
+2.60 Limited the number of open directories.
+2.61 Added code to handle archives with ascii headers.
+2.62 Joe Condon Fixes to archive formats
+2.63 Pattern Matching (%) stuff.
+2.64 Reinstalled $(TGS) as $^ from other version
+2.65 Installed dynamic macros ( := commands).
+2.66 Sped up pattern matching code
+2.67 Changed pattern matching code to permit multiple dependents
+2.68 Added + (do it despite -n) prefix to command lines.
+ Fixed bug involving metacharacter expansions on dependency lines.
+2.69 Added & to dependency lines and new background process spawning
+2.70 Added Bradford's macros: $/, $@, *D, *F, <D, <F, @D, @F.
+2.71 Added include stack to input.
+ Added check for sccs makefiles: s.[Mm]akefile
+2.72 Load environment into macro tables. Added Bradford's -e flag.
+2.73 Pass changed environment macros out to commands.
+2.74 Fixed limit on args in dosys.c.
+ Non-existent archives now treated as other non-existent files.
+2.75 Fixed bug in rehash.
+2.76 Fixed bug when pattern searching in non-existent directory
+ Fixed infinite loop when awaiting failed process
+ Now wait till all subjobs finish before returning
+ make, unless a subjob fails
+2.77 Added -z option that always forces shell invocation
+ rather than direct fork-exec
+2.78 Check for error (-1) returned from fork
+*/
diff --git a/sys/src/ape/cmd/make/main.c b/sys/src/ape/cmd/make/main.c
new file mode 100755
index 000000000..26853e86b
--- /dev/null
+++ b/sys/src/ape/cmd/make/main.c
@@ -0,0 +1,423 @@
+# include "defs.h"
+/*
+command make to update programs.
+Posix Flags:
+ 'e' use environment macros after rather than before makefiles
+ 'f' the next argument is the name of the description file;
+ "makefile" is the default
+ 'i' ignore error codes from the shell
+ 'k' continue to update other targets that don't depend
+ on target if error occurs making a target
+ 'n' don't issue, just print, commands
+ 'p' print out a version of the input graph
+ 'q' don't do anything, but check if object is up to date;
+ returns exit code 0 if up to date, 1 if not
+ 'r' clear the builtin suffix list and don't use built-in rules
+ 's' silent mode--don't print out commands
+ 'S' stop after any command fails (default; opposite of -k)
+ 't' touch (update time of) files but don't issue command
+
+Nonposix Flags:
+ 'd' print out debugging comments
+ 'N' use % patterns instead of old suffix rules
+ 'Pn' set process limit to n
+ 'z' always use shell, never issue commands directly
+
+*/
+
+nameblkp mainname = NULL;
+nameblkp firstname = NULL;
+lineblkp sufflist = NULL;
+struct varblock *firstvar = NULL;
+struct pattern *firstpat = NULL;
+struct dirhd *firstod = NULL;
+wildp firstwild = NULL;
+wildp lastwild = NULL;
+nameblkp *hashtab;
+int nhashed;
+int hashsize;
+int hashthresh;
+
+int proclimit = PROCLIMIT;
+int nproc = 0;
+int proclive = 0;
+struct process procstack[MAXPROC];
+
+int sigivalue = 0;
+int sigqvalue = 0;
+
+int dbgflag = NO;
+int prtrflag = NO;
+int silflag = NO;
+int noexflag = NO;
+int keepgoing = NO;
+int noruleflag = NO;
+int touchflag = NO;
+int questflag = NO;
+int oldflag = YES;
+int ndocoms = NO;
+int ignerr = NO; /* default is to stop on error */
+int forceshell = NO;
+int okdel = YES;
+int envlast = NO;
+int inarglist = NO;
+char **envpp = NULL;
+
+extern char *dfltmacro[];
+extern char *dfltpat[];
+extern char *dfltsuff[];
+extern char **environ;
+char **linesptr;
+
+char *prompt = "";
+int nopdir = 0;
+char funny[128];
+
+static void loadenv(void);
+static int isprecious(char *);
+static int rddescf(char *);
+static void rdarray(char **);
+static void printdesc(int);
+
+void
+main(int argc, char **argv)
+{
+nameblkp p;
+int i, j;
+int descset, nfargs;
+int nowait = NO;
+time_t tjunk;
+char c, *s, *mkflagp;
+static char makeflags[30] = "-";
+static char onechar[2] = "X";
+
+descset = 0;
+mkflagp = makeflags+1;
+
+funny['\0'] = (META | TERMINAL);
+for(s = "=|^();&<>*?[]:$`'\"\\\n" ; *s ; ++s)
+ funny[*s] |= META;
+for(s = "\n\t :;&>|" ; *s ; ++s)
+ funny[*s] |= TERMINAL;
+
+
+newhash(HASHSIZE);
+
+inarglist = YES;
+for(i=1; i<argc; ++i)
+ if(argv[i]!=0 && argv[i][0]!='-' && eqsign(argv[i]))
+ argv[i] = 0;
+
+setvar("$", "$", NO);
+inarglist = NO;
+
+for(i=1; i<argc; ++i)
+ if(argv[i]!=0 && argv[i][0]=='-')
+ {
+ for(j=1 ; (c=argv[i][j])!='\0' ; ++j) switch(c)
+ {
+ case 'd':
+ ++dbgflag;
+ *mkflagp++ = 'd';
+ break;
+
+ case 'e':
+ envlast = YES;
+ *mkflagp++ = 'e';
+ break;
+
+ case 'f':
+ if(i >= argc-1)
+ fatal("No description argument after -f flag");
+ if( ! rddescf(argv[i+1]) )
+ fatal1("Cannot open %s", argv[i+1]);
+ argv[i+1] = 0;
+ ++descset;
+ break;
+
+ case 'i':
+ ignerr = YES;
+ *mkflagp++ = 'i';
+ break;
+
+ case 'k':
+ keepgoing = YES;
+ *mkflagp++ = 'k';
+ break;
+
+ case 'n':
+ noexflag = YES;
+ *mkflagp++ = 'n';
+ break;
+
+ case 'N':
+ oldflag = NO;
+ *mkflagp++ = 'N';
+ break;
+
+ case 'p':
+ prtrflag = YES;
+ break;
+
+ case 'P':
+ if(isdigit(argv[i][j+1]))
+ {
+ proclimit = argv[i][++j] - '0';
+ if(proclimit < 1)
+ proclimit = 1;
+ }
+ else
+ fatal("illegal proclimit parameter");
+ *mkflagp++ = 'P';
+ *mkflagp++ = argv[i][j];
+ break;
+
+ case 'q':
+ questflag = YES;
+ *mkflagp++ = 'q';
+ break;
+
+ case 'r':
+ noruleflag = YES;
+ *mkflagp++ = 'r';
+ break;
+
+ case 's':
+ silflag = YES;
+ *mkflagp++ = 's';
+ break;
+
+ case 'S':
+ keepgoing = NO;
+ *mkflagp++ = 'S';
+ break;
+
+ case 't':
+ touchflag = YES;
+ *mkflagp++ = 't';
+ break;
+
+ case 'z':
+ forceshell = YES;
+ *mkflagp++ = 'z';
+ break;
+
+ default:
+ onechar[0] = c; /* to make lint happy */
+ fatal1("Unknown flag argument %s", onechar);
+ }
+
+ argv[i] = NULL;
+ }
+
+if(mkflagp > makeflags+1)
+ setvar("MAKEFLAGS", makeflags, NO);
+
+if( !descset )
+if( !rddescf("makefile") &&
+ !rddescf("Makefile") &&
+ (exists(s = "s.makefile") || exists(s = "s.Makefile")) )
+ {
+ char junk[20];
+ concat("get ", s, junk);
+ (void) dosys(junk, NO, NO, junk);
+ rddescf(s+2);
+ unlink(s+2);
+ }
+
+
+if(envlast)
+ loadenv();
+if(!noruleflag && !oldflag)
+ rdarray(dfltpat);
+
+if(prtrflag) printdesc(NO);
+
+if( srchname(".IGNORE") )
+ ignerr = YES;
+if( srchname(".SILENT") )
+ silflag = YES;
+if( srchname(".OLDFLAG") )
+ oldflag = YES;
+if( p=srchname(".SUFFIXES") )
+ sufflist = p->linep;
+if( !sufflist && !firstwild)
+ fprintf(stderr,"No suffix or %% pattern list.\n");
+/*
+if(sufflist && !oldflag)
+ fprintf(stderr, "Suffix lists are old-fashioned. Use %% patterns\n);
+*/
+
+ sigivalue = (int) signal(SIGINT, SIG_IGN);
+ sigqvalue = (int) signal(SIGQUIT, SIG_IGN);
+ enbint(intrupt);
+
+nfargs = 0;
+
+for(i=1; i<argc; ++i)
+ if(s = argv[i])
+ {
+ if((p=srchname(s)) == NULL)
+ p = makename(s);
+ ++nfargs;
+ if(i+1<argc && argv[i+1] != 0 && equal(argv[i+1], "&") )
+ {
+ ++i;
+ nowait = YES;
+ }
+ else
+ nowait = NO;
+ doname(p, 0, &tjunk, nowait);
+ if(dbgflag) printdesc(YES);
+ }
+
+/*
+If no file arguments have been encountered, make the first
+name encountered that doesn't start with a dot
+*/
+
+if(nfargs == 0)
+ if(mainname == 0)
+ fatal("No arguments or description file");
+ else {
+ doname(mainname, 0, &tjunk, NO);
+ if(dbgflag) printdesc(YES);
+ }
+
+if(!nowait)
+ waitstack(0);
+exit(0);
+}
+
+
+void
+intrupt(int sig)
+{
+char *p;
+
+if(okdel && !noexflag && !touchflag &&
+ (p = varptr("@")->varval) && exists(p)>0 && !isprecious(p) )
+ {
+ fprintf(stderr, "\n*** %s removed.", p);
+ remove(p);
+ }
+
+fprintf(stderr, "\n");
+exit(2);
+}
+
+
+
+static int
+isprecious(char *p)
+{
+lineblkp lp;
+depblkp dp;
+nameblkp np;
+
+if(np = srchname(".PRECIOUS"))
+ for(lp = np->linep ; lp ; lp = lp->nxtlineblock)
+ for(dp = lp->depp ; dp ; dp = dp->nxtdepblock)
+ if(equal(p, dp->depname->namep))
+ return YES;
+
+return NO;
+}
+
+
+void
+enbint(void (*k)(int))
+{
+if(sigivalue == 0)
+ signal(SIGINT,k);
+if(sigqvalue == 0)
+ signal(SIGQUIT,k);
+}
+
+static int
+rddescf(char *descfile)
+{
+static int firstrd = YES;
+
+/* read and parse description */
+
+if(firstrd)
+ {
+ firstrd = NO;
+ if( !noruleflag )
+ {
+ rdarray(dfltmacro);
+ if(oldflag)
+ rdarray(dfltsuff);
+ }
+ if(!envlast)
+ loadenv();
+
+ }
+
+return parse(descfile);
+}
+
+static void
+rdarray(char **s)
+{
+linesptr = s;
+parse(CHNULL);
+}
+
+static void
+loadenv(void)
+{
+for(envpp = environ ; *envpp ; ++envpp)
+ eqsign(*envpp);
+envpp = NULL;
+}
+
+static void
+printdesc(int prntflag)
+{
+nameblkp p;
+depblkp dp;
+struct varblock *vp;
+struct dirhd *od;
+struct shblock *sp;
+lineblkp lp;
+
+if(prntflag)
+ {
+ printf("Open directories:\n");
+ for (od = firstod; od; od = od->nxtdirhd)
+ printf("\t%s\n", od->dirn);
+ }
+
+if(firstvar != 0) printf("Macros:\n");
+for(vp = firstvar; vp ; vp = vp->nxtvarblock)
+ printf("\t%s = %s\n" , vp->varname , vp->varval ? vp->varval : "(null)");
+
+for(p = firstname; p; p = p->nxtnameblock)
+ {
+ printf("\n\n%s",p->namep);
+ if(p->linep != 0) printf(":");
+ if(prntflag) printf(" done=%d",p->done);
+ if(p==mainname) printf(" (MAIN NAME)");
+ for(lp = p->linep ; lp ; lp = lp->nxtlineblock)
+ {
+ if( dp = lp->depp )
+ {
+ printf("\n depends on:");
+ for(; dp ; dp = dp->nxtdepblock)
+ if(dp->depname != 0)
+ printf(" %s ", dp->depname->namep);
+ }
+
+ if(sp = lp->shp)
+ {
+ printf("\n commands:\n");
+ for( ; sp ; sp = sp->nxtshblock)
+ printf("\t%s\n", sp->shbp);
+ }
+ }
+ }
+printf("\n");
+fflush(stdout);
+}
diff --git a/sys/src/ape/cmd/make/misc.c b/sys/src/ape/cmd/make/misc.c
new file mode 100755
index 000000000..cd7a990d2
--- /dev/null
+++ b/sys/src/ape/cmd/make/misc.c
@@ -0,0 +1,504 @@
+#include "defs.h"
+
+static int hasslash(char *);
+static int haspercent(char *);
+static void rehash(void);
+
+/* simple linear hash. hash function is sum of
+ characters mod hash table size.
+*/
+static int
+hashloc(char *s)
+{
+int i;
+int hashval;
+char *t;
+
+hashval = 0;
+
+for(t=s; *t!='\0' ; ++t)
+ hashval += *t;
+
+hashval %= hashsize;
+
+for(i=hashval;
+ hashtab[i]!=0 && !equal(s,hashtab[i]->namep);
+ i = i >= hashsize-1 ? 0 : i+1) ;
+
+return i;
+}
+
+
+nameblkp
+srchname(char *s)
+{
+return hashtab[hashloc(s)] ;
+}
+
+
+
+nameblkp
+makename(char *s)
+{
+nameblkp p;
+
+if(nhashed > hashthresh)
+ rehash();
+
+++nhashed;
+hashtab[hashloc(s)] = p = ALLOC(nameblock);
+p->nxtnameblock = firstname;
+p->namep = copys(s); /* make a fresh copy of the string s */
+/* p->linep = 0; p->done = 0; p->septype = 0; p->modtime = 0; */
+
+firstname = p;
+if(mainname==NULL && !haspercent(s) && (*s!='.' || hasslash(s)) )
+ mainname = p;
+
+return p;
+}
+
+
+static int
+hasslash(char *s)
+{
+for( ; *s ; ++s)
+ if(*s == '/')
+ return YES;
+return NO;
+}
+
+static int
+haspercent(char *s)
+{
+for( ; *s ; ++s)
+ if(*s == '%')
+ return YES;
+return NO;
+}
+
+int
+hasparen(char *s)
+{
+for( ; *s ; ++s)
+ if(*s == '(')
+ return YES;
+return NO;
+}
+
+static void
+rehash(void)
+{
+nameblkp *ohash;
+nameblkp p, *hp, *endohash;
+hp = ohash = hashtab;
+endohash = hashtab + hashsize;
+
+newhash(2*hashsize);
+
+while( hp<endohash )
+ if(p = *hp++)
+ hashtab[hashloc(p->namep)] = p;
+
+free( (char *) ohash);
+}
+
+
+void
+newhash(int newsize)
+{
+hashsize = newsize;
+hashtab = (nameblkp *) ckalloc(hashsize * sizeof(nameblkp));
+hashthresh = (2*hashsize)/3;
+}
+
+
+
+nameblkp chkname(char *s)
+{
+nameblkp p;
+time_t k;
+/*TEMP NEW */
+if(hasparen(s))
+ {
+ k = lookarch(s);
+/*TEMP fprintf(stderr, "chkname(%s): look=%d\n", s, k); */
+ if(k == 0)
+ return NULL;
+ }
+if(p = srchname(s))
+ return p;
+dirsrch(s);
+return srchname(s);
+}
+
+
+
+char *
+copys(char *s)
+{
+char *t;
+
+if( (t = malloc( strlen(s)+1 ) ) == NULL)
+ fatal("out of memory");
+strcpy(t, s);
+return t;
+}
+
+
+
+char *
+concat(char *a, char *b, char *c) /* c = concatenation of a and b */
+{
+char *t;
+t = c;
+
+while(*t = *a++) t++;
+while(*t++ = *b++);
+return c;
+}
+
+
+int
+suffix(char *a, char *b, char *p) /* is b the suffix of a? if so, set p = prefix */
+{
+char *a0,*b0;
+a0 = a;
+b0 = b;
+
+while(*a++);
+while(*b++);
+
+if( (a-a0) < (b-b0) ) return 0;
+
+while(b>b0)
+ if(*--a != *--b) return 0;
+
+while(a0<a) *p++ = *a0++;
+*p = '\0';
+
+return 1;
+}
+
+int *
+ckalloc(int n)
+{
+int *p;
+
+if( p = (int *) calloc(1,n) )
+ return p;
+
+fatal("out of memory");
+/* NOTREACHED */
+}
+
+/* copy string a into b, substituting for arguments */
+char *
+subst(char *a, char *b)
+{
+static depth = 0;
+char *s;
+char vname[100];
+struct varblock *vbp;
+char closer;
+
+if(++depth > 100)
+ fatal("infinitely recursive macro?");
+if(a) while(*a)
+ {
+ if(*a!='$' || a[1]=='\0' || *++a=='$')
+ /* if a non-macro character copy it. if $$ or $\0, copy $ */
+ *b++ = *a++;
+ else {
+ s = vname;
+ if( *a=='(' || *a=='{' )
+ {
+ closer = ( *a=='(' ? ')' : '}');
+ ++a;
+ while(*a == ' ') ++a;
+ while(*a!=' ' && *a!=closer && *a!='\0') *s++ = *a++;
+ while(*a!=closer && *a!='\0') ++a;
+ if(*a == closer) ++a;
+ }
+ else *s++ = *a++;
+
+ *s = '\0';
+ if( (vbp = varptr(vname)) ->varval != 0)
+ {
+ b = subst(vbp->varval, b);
+ vbp->used = YES;
+ }
+ }
+ }
+
+*b = '\0';
+--depth;
+return b;
+}
+
+void
+setvar(char *v, char *s, int dyn)
+{
+struct varblock *p;
+
+p = varptr(v);
+if( ! p->noreset )
+ {
+ p->varval = s;
+ p->noreset = inarglist;
+ if(p->used && !dyn)
+ fprintf(stderr, "Warning: %s changed after being used\n",v);
+ if(p->export)
+ {
+ /* change string pointed to by environment to new v=s */
+ char *t;
+ int lenv;
+ lenv = strlen(v);
+ *(p->export) = t = (char *) ckalloc(lenv + strlen(s) + 2);
+ strcpy(t,v);
+ t[lenv] = '=';
+ strcpy(t+lenv+1, s);
+ }
+ else
+ p->export = envpp;
+ }
+}
+
+
+/* for setting Bradford's *D and *F family of macros whens setting * etc */
+void
+set3var(char *macro, char *value)
+{
+char *s;
+char macjunk[8], *lastslash, *dirpart, *filepart;
+
+setvar(macro, value, YES);
+if(value == CHNULL)
+ dirpart = filepart = CHNULL;
+else
+ {
+ lastslash = CHNULL;
+ for(s = value; *s; ++s)
+ if(*s == '/')
+ lastslash = s;
+ if(lastslash)
+ {
+ dirpart = copys(value);
+ filepart = dirpart + (lastslash-value);
+ filepart[-1] = '\0';
+ }
+ else
+ {
+ dirpart = "";
+ filepart = value;
+ }
+ }
+setvar(concat(macro, "D", macjunk), dirpart, YES);
+setvar(concat(macro, "F", macjunk), filepart, YES);
+}
+
+
+int
+eqsign(char *a) /*look for arguments with equal signs but not colons */
+{
+char *s, *t;
+char c;
+
+while(*a == ' ') ++a;
+for(s=a ; *s!='\0' && *s!=':' ; ++s)
+ if(*s == '=')
+ {
+ for(t = a ; *t!='=' && *t!=' ' && *t!='\t' ; ++t );
+ c = *t;
+ *t = '\0';
+
+ for(++s; *s==' ' || *s=='\t' ; ++s);
+ setvar(a, copys(s), NO);
+ *t = c;
+ return YES;
+ }
+
+return NO;
+}
+
+struct varblock *
+varptr(char *v)
+{
+struct varblock *vp;
+
+/* for compatibility, $(TGS) = $^ */
+if(equal(v, "TGS") )
+ v = "^";
+for(vp = firstvar; vp ; vp = vp->nxtvarblock)
+ if(equal(v , vp->varname))
+ return vp;
+
+vp = ALLOC(varblock);
+vp->nxtvarblock = firstvar;
+firstvar = vp;
+vp->varname = copys(v);
+vp->varval = 0;
+return vp;
+}
+
+int
+dynmacro(char *line)
+{
+char *s;
+char endc, *endp;
+if(!isalpha(line[0]))
+ return NO;
+for(s=line+1 ; *s && (isalpha(*s) | isdigit(*s)) ; ++s)
+ ;
+endp = s;
+while( isspace(*s) )
+ ++s;
+if(s[0]!=':' || s[1]!='=')
+ return NO;
+
+endc = *endp;
+*endp = '\0';
+setvar(line, copys(s+2), YES);
+*endp = endc;
+
+return YES;
+}
+
+
+void
+fatal1(char *s, char *t)
+{
+char buf[100];
+sprintf(buf, s, t);
+fatal(buf);
+}
+
+
+void
+fatal(char *s)
+{
+fflush(stdout);
+if(s)
+ fprintf(stderr, "Make: %s. Stop.\n", s);
+else
+ fprintf(stderr, "\nStop.\n");
+
+waitstack(0);
+exit(1);
+}
+
+
+
+/* appends to the chain for $? and $^ */
+chainp
+appendq(chainp head, char *tail)
+{
+chainp p, q;
+
+p = ALLOC(chain);
+p->datap = tail;
+
+if(head)
+ {
+ for(q = head ; q->nextp ; q = q->nextp)
+ ;
+ q->nextp = p;
+ return head;
+ }
+else
+ return p;
+}
+
+
+
+
+
+/* builds the value for $? and $^ */
+char *
+mkqlist(chainp p, char *qbuf)
+{
+char *qbufp, *s;
+
+if(p == NULL)
+ return "";
+
+qbufp = qbuf;
+
+for( ; p ; p = p->nextp)
+ {
+ s = p->datap;
+ if(qbufp+strlen(s) > &qbuf[QBUFMAX-3])
+ {
+ fprintf(stderr, "$? list too long\n");
+ break;
+ }
+ while (*s)
+ *qbufp++ = *s++;
+ *qbufp++ = ' ';
+ }
+*--qbufp = '\0';
+return qbuf;
+}
+
+wildp
+iswild(char *name)
+{
+char *s;
+wildp p;
+
+for(s=name; *s; ++s)
+ if(*s == '%')
+ {
+ p = ALLOC(wild);
+ *s = '\0';
+ p->left = copys(name);
+ *s = '%';
+ p->right = copys(s+1);
+ p->llen = strlen(p->left);
+ p->rlen = strlen(p->right);
+ p->totlen = p->llen + p->rlen;
+ return p;
+ }
+return NULL;
+}
+
+
+char *
+wildmatch(wildp p, char *name, int len)
+{
+char *stem;
+char *s;
+char c;
+
+if(len < p->totlen ||
+ strncmp(name, p->left, p->llen) ||
+ strncmp(s = name+len-p->rlen, p->right, p->rlen) )
+ return CHNULL;
+
+/*TEMP fprintf(stderr, "wildmatch(%s)=%s%%%s)\n", name,p->left,p->right); */
+c = *s;
+*s = '\0';
+stem = copys(name + p->llen);
+*s = c;
+return stem;
+}
+
+
+
+/* substitute stem for any % marks */
+char *
+wildsub(char *pat, char *stem)
+{
+static char temp[100];
+char *s, *t;
+
+s = temp;
+for(; *pat; ++pat)
+ if(*pat == '%')
+ for(t = stem ; *t; )
+ *s++ = *t++;
+ else
+ *s++ = *pat;
+*s = '\0';
+return temp;
+}
diff --git a/sys/src/ape/cmd/make/mkfile b/sys/src/ape/cmd/make/mkfile
new file mode 100755
index 000000000..b7669386c
--- /dev/null
+++ b/sys/src/ape/cmd/make/mkfile
@@ -0,0 +1,26 @@
+APE=/sys/src/ape
+<$APE/config
+
+TARG=make
+OFILES=ident.$O\
+ main.$O\
+ doname.$O\
+ dosys.$O\
+ gram.$O\
+ misc.$O\
+ files.$O\
+
+HFILES=defs.h
+YFILES=gram.y
+
+BIN=$APEBIN
+</sys/src/cmd/mkone
+
+CFLAGS=-c -DSHELLCOM'="/bin/sh"'
+YFLAGS=-S
+
+gram.c: y.tab.c
+ mv $prereq $target
+
+nuke clean:V:
+ rm -f *.[$OS] [$OS].out y.tab.? y.debug y.output $TARG gram.c