summaryrefslogtreecommitdiff
path: root/appl/cmd/mk
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/mk')
-rw-r--r--appl/cmd/mk/ar.m26
-rw-r--r--appl/cmd/mk/mk.b4211
-rw-r--r--appl/cmd/mk/mkbinds2
-rw-r--r--appl/cmd/mk/mkconfig28
-rw-r--r--appl/cmd/mk/mkfile19
-rw-r--r--appl/cmd/mk/mksubdirs16
6 files changed, 4302 insertions, 0 deletions
diff --git a/appl/cmd/mk/ar.m b/appl/cmd/mk/ar.m
new file mode 100644
index 00000000..dfa686ae
--- /dev/null
+++ b/appl/cmd/mk/ar.m
@@ -0,0 +1,26 @@
+#
+# initially generated by c2l
+#
+
+Ar: module
+{
+ PATH: con "ar.dis";
+
+ ARMAG: con "!<arch>\n";
+ SARMAG: con 8;
+ ARFMAG: con "`\n";
+ SARNAME: con 16;
+
+ ar_hdr: adt{
+ name: array of byte; # SARNAME
+ date: array of byte; # 12
+ uid: array of byte; # 6
+ gid: array of byte; # 6
+ mode: array of byte; # 8
+ size: array of byte; # 10
+ fmag: array of byte; # 2
+ };
+
+ SAR_HDR: con 60;
+
+};
diff --git a/appl/cmd/mk/mk.b b/appl/cmd/mk/mk.b
new file mode 100644
index 00000000..49a5c1a2
--- /dev/null
+++ b/appl/cmd/mk/mk.b
@@ -0,0 +1,4211 @@
+#
+# initially generated by c2l
+#
+
+implement Mk;
+
+include "draw.m";
+
+Mk: module
+{
+ init: fn(nil: ref Draw->Context, argl: list of string);
+};
+
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "libc0.m";
+ libc0: Libc0;
+include "math.m";
+ math: Math;
+include "regex.m";
+ regex: Regex;
+include "ar.m";
+ ARMAG, SARMAG, ARFMAG, SARNAME, ar_hdr, SAR_HDR: import Ar;
+include "daytime.m";
+ daytime: Daytime;
+include "sh.m";
+
+init(nil: ref Draw->Context, argl: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ libc0 = load Libc0 Libc0->PATH;
+ math = load Math Math->PATH;
+ regex = load Regex Regex->PATH;
+ daytime = load Daytime Daytime->PATH;
+ main(len argl, libc0->ls2aab(argl));
+}
+
+NAMELEN: con 28;
+ERRLEN: con 64;
+PNPROC, PNGROUP : con iota;
+
+# function pointer enum for symtraverse
+ECOPY, PRINT1: con iota;
+
+Bufblock: adt{
+ next: cyclic ref Bufblock;
+ start: array of byte;
+ end: int;
+ current: int;
+};
+
+Word: adt{
+ s: array of byte;
+ next: cyclic ref Word;
+};
+
+Envy: adt{
+ name: array of byte;
+ values: ref Word;
+};
+
+Resub: adt{
+ sp: array of byte;
+ ep: array of byte;
+};
+
+Rule: adt{
+ target: array of byte; # one target
+ tail: ref Word; # constituents of targets
+ recipe: array of byte; # do it !
+ attr: int; # attributes
+ line: int; # source line
+ file: array of byte; # source file
+ alltargets: ref Word; # all the targets
+ rule: int; # rule number
+ pat: Regex->Re; # reg exp goo
+ prog: array of byte; # to use in out of date
+ chain: cyclic ref Rule; # hashed per target
+ next: cyclic ref Rule;
+};
+
+# Rule.attr
+META, SEQ, UPD, QUIET, VIR, REGEXP, NOREC, DEL, NOVIRT: con 1<<iota;
+NREGEXP: con 10;
+
+Arc: adt{
+ flag: int;
+ n: cyclic ref Node;
+ r: ref Rule;
+ stem: array of byte;
+ prog: array of byte;
+ match: array of array of byte;
+ next: cyclic ref Arc;
+};
+
+# Arc.flag
+TOGO: con 1;
+
+Node: adt{
+ name: array of byte;
+ time: int;
+ flags: int;
+ prereqs: cyclic ref Arc;
+ next: cyclic ref Node; # list for a rule
+};
+
+# Node.flags
+VIRTUAL, CYCLE, READY, CANPRETEND, PRETENDING, NOTMADE, BEINGMADE, MADE, PROBABLE, VACUOUS, NORECIPE, DELETE, NOMINUSE: con 1<<iota;
+
+Job: adt{
+ r: ref Rule; # master rule for job
+ n: ref Node; # list of node targets
+ stem: array of byte;
+ match: array of array of byte;
+ p: ref Word; # prerequistes
+ np: ref Word; # new prerequistes
+ t: ref Word; # targets
+ at: ref Word; # all targets
+ nproc: int; # slot number
+ next: cyclic ref Job;
+};
+
+Symtab: adt{
+ space: int;
+ name: array of byte;
+ svalue: array of byte;
+ ivalue: int;
+ nvalue: ref Node;
+ rvalue: ref Rule;
+ wvalue: ref Word;
+ next: cyclic ref Symtab;
+};
+
+S_VAR # variable -> value
+, S_TARGET # target -> rule
+, S_TIME # file -> time
+, S_PID # pid -> products
+, S_NODE # target name -> node
+, S_AGG # aggregate -> time
+, S_BITCH # bitched about aggregate not there
+, S_NOEXPORT # var -> noexport
+, S_OVERRIDE # can't override
+, S_OUTOFDATE # n1\377n2 -> 2(outofdate) or 1(not outofdate)
+, S_MAKEFILE # target -> node
+, S_MAKEVAR # dumpable mk variable
+, S_EXPORTED # var -> current exported value
+, S_BULKED # we have bulked this dir
+, S_WESET # variable; we set in the mkfile
+# an internal mk variable (e.g., stem, target)
+, S_INTERNAL: con iota;
+NAMEBLOCK: con 1000;
+BIGBLOCK: con 20000;
+D_PARSE, D_GRAPH, D_EXEC: con 1<<iota;
+
+MKFILE: con "mkfile";
+
+version := array[] of { byte '@', byte '(', byte '#', byte ')', byte 'm', byte 'k', byte ' ', byte 'g', byte 'e', byte 'n', byte 'e', byte 'r', byte 'a', byte 'l', byte ' ', byte 'r', byte 'e', byte 'l', byte 'e', byte 'a', byte 's', byte 'e', byte ' ', byte '4', byte ' ', byte '(', byte 'p', byte 'l', byte 'a', byte 'n', byte ' ', byte '9', byte ')', byte '\0' };
+debug: int;
+rules, metarules: ref Rule;
+nflag: int = 0;
+tflag: int = 0;
+iflag: int = 0;
+kflag: int = 0;
+aflag: int = 0;
+uflag: int = 0;
+explain: array of byte = nil;
+target1: ref Word;
+nreps: int = 1;
+jobs: ref Job;
+bout: ref Iobuf;
+patrule: ref Rule;
+
+main(argc: int, argv: array of array of byte)
+{
+ w: ref Word;
+ s, temp: array of byte;
+ files := array[256] of array of byte;
+ f: array of array of byte = files;
+ ff: int;
+ sflag: int = 1;
+ i: int;
+ tfd: ref Sys->FD = sys->fildes(-1);
+ tb: ref Iobuf;
+ buf, whatif: ref Bufblock;
+
+ #
+ # * start with a copy of the current environment variables
+ # * instead of sharing them
+ #
+ bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
+ buf = newbuf();
+ whatif = nil;
+ if(argc)
+ ;
+ for(argv = argv[1: ]; argv[0] != nil && argv[0][0] == byte '-'; argv = argv[1: ]){
+ bufcpy(buf, argv[0], libc0->strlen(argv[0]));
+ insert(buf, ' ');
+ case(int argv[0][1]){
+ 'a' =>
+ aflag = 1;
+ 'd' =>
+ if(int (s = argv[0][2: ])[0])
+ while(int s[0]){
+ case(int s[0]){
+ 'p' =>
+ debug |= D_PARSE;
+ 'g' =>
+ debug |= D_GRAPH;
+ 'e' =>
+ debug |= D_EXEC;
+ }
+ s = s[1: ];
+ }
+ else
+ debug = 16rffff;
+ 'e' =>
+ explain = argv[0][2: ];
+ 'f' =>
+ argv = argv[1: ];
+ if(argv[0] == nil)
+ badusage();
+ f[0] = argv[0];
+ f = f[1: ];
+ bufcpy(buf, argv[0], libc0->strlen(argv[0]));
+ insert(buf, ' ');
+ 'i' =>
+ iflag = 1;
+ 'k' =>
+ kflag = 1;
+ 'n' =>
+ nflag = 1;
+ 's' =>
+ sflag = 1;
+ 't' =>
+ tflag = 1;
+ 'u' =>
+ uflag = 1;
+ 'w' =>
+ if(whatif == nil)
+ whatif = newbuf();
+ else
+ insert(whatif, ' ');
+ if(int argv[0][2])
+ bufcpy(whatif, argv[0][2: ], libc0->strlen(argv[0][2: ]));
+ else{
+ argv = argv[1: ];
+ if(argv[0] == nil)
+ badusage();
+ bufcpy(whatif, argv[0][0: ], libc0->strlen(argv[0][0: ]));
+ }
+ * =>
+ badusage();
+ }
+ }
+ if(aflag)
+ iflag = 1;
+ usage();
+ syminit();
+ initenv();
+ initbind();
+ openwait();
+ usage();
+ #
+ # assignment args become null strings
+ #
+ temp = nil;
+ for(i = 0; argv[i] != nil; i++)
+ if(libc0->strchr(argv[i], '=') != nil){
+ bufcpy(buf, argv[i], libc0->strlen(argv[i]));
+ insert(buf, ' ');
+ if(tfd == nil){
+ temp = maketmp();
+ if(temp == nil){
+ perrors("temp file");
+ Exit();
+ }
+ sys->create(libc0->ab2s(temp), Sys->OWRITE, 8r600);
+ if((tfd = sys->open(libc0->ab2s(temp), 2)) == nil){
+ perror(temp);
+ Exit();
+ }
+ tb = bufio->fopen(tfd, Sys->OWRITE);
+ }
+ tb.puts(sys->sprint("%s\n", libc0->ab2s(argv[i])));
+ argv[i][0] = byte 0;
+ }
+ if(tfd != nil){
+ tb.flush();
+ sys->seek(tfd, big 0, 0);
+ parse(libc0->s2ab("command line args"), tfd, 1);
+ sys->remove(libc0->ab2s(temp));
+ }
+ if(buf.current != 0){
+ buf.current--;
+ insert(buf, 0);
+ }
+ symlookw(libc0->s2ab("MKFLAGS"), S_VAR, stow(buf.start));
+ buf.current = 0;
+ for(i = 0; argv[i] != nil; i++){
+ if(argv[i][0] == byte 0)
+ continue;
+ if(i)
+ insert(buf, ' ');
+ bufcpy(buf, argv[i], libc0->strlen(argv[i]));
+ }
+ insert(buf, 0);
+ symlookw(libc0->s2ab("MKARGS"), S_VAR, stow(buf.start));
+ freebuf(buf);
+ if(f == files){
+ if(access(libc0->s2ab(MKFILE), Sys->OREAD) == 0)
+ parse(libc0->s2ab(MKFILE), sys->open(MKFILE, 0), 0);
+ }
+ else
+ for(ff = 0; ff < len files && files[ff] != nil; ff++)
+ parse(files[ff], sys->open(libc0->ab2s(files[ff]), 0), 0);
+ if(debug&D_PARSE){
+ dumpw(libc0->s2ab("default targets"), target1);
+ dumpr(libc0->s2ab("rules"), rules);
+ dumpr(libc0->s2ab("metarules"), metarules);
+ dumpv(libc0->s2ab("variables"));
+ }
+ if(whatif != nil){
+ insert(whatif, 0);
+ timeinit(whatif.start);
+ freebuf(whatif);
+ }
+ execinit();
+ # skip assignment args
+ while(argv[0] != nil && argv[0][0] == byte 0)
+ argv = argv[1: ];
+ catchnotes();
+ if(argv[0] == nil){
+ if(target1 != nil)
+ for(w = target1; w != nil; w = w.next)
+ mk(w.s);
+ else{
+ sys->fprint(sys->fildes(2), "mk: nothing to mk\n");
+ Exit();
+ }
+ }
+ else{
+ if(sflag){
+ for(; argv[0] != nil; argv = argv[1: ])
+ if(int argv[0][0])
+ mk(argv[0]);
+ }
+ else{
+ head, tail, t: ref Word;
+
+ # fake a new rule with all the args as prereqs
+ tail = nil;
+ t = nil;
+ for(; argv[0] != nil; argv = argv[1: ])
+ if(int argv[0][0]){
+ if(tail == nil)
+ tail = t = newword(argv[0]);
+ else{
+ t.next = newword(argv[0]);
+ t = t.next;
+ }
+ }
+ if(tail.next == nil)
+ mk(tail.s);
+ else{
+ head = newword(libc0->s2ab("command line arguments"));
+ addrules(head, tail, libc0->strdup(libc0->s2ab("")), VIR, mkinline, nil);
+ mk(head.s);
+ }
+ }
+ }
+ if(uflag)
+ prusage();
+ bout.flush();
+ exit;
+}
+
+badusage()
+{
+ sys->fprint(sys->fildes(2), "Usage: mk [-f file] [-n] [-a] [-e] [-t] [-k] [-i] [-d[egp]] [targets ...]\n");
+ Exit();
+}
+
+assert(s: array of byte, n: int)
+{
+ if(!n){
+ sys->fprint(sys->fildes(2), "mk: Assertion ``%s'' failed.\n", libc0->ab2s(s));
+ Exit();
+ }
+}
+
+regerror(s: array of byte)
+{
+ if(patrule != nil)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: regular expression error; %s\n", libc0->ab2s(patrule.file), patrule.line, libc0->ab2s(s));
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: regular expression error; %s\n", libc0->ab2s(infile), mkinline, libc0->ab2s(s));
+ Exit();
+}
+
+perror(s: array of byte)
+{
+ perrors(libc0->ab2s(s));
+}
+
+perrors(s: string)
+{
+ sys->fprint(sys->fildes(2), "mk: %s: %r\n", s);
+}
+
+access(s: array of byte, mode: int): int
+{
+ fd := sys->open(libc0->ab2s(s), mode);
+ if (fd == nil)
+ return -1;
+ fd = nil;
+ return 0;
+}
+
+stob(buf: array of byte, s: string)
+{
+ b := libc0->s2ab(s);
+ libc0->strncpy(buf, b, len buf);
+}
+
+mktemp(t: array of byte)
+{
+ x := libc0->strchr(t, 'X');
+ if(x == nil)
+ return;
+ pid := libc0->s2ab(string sys->pctl(0, nil));
+ for(i := 'a'; i <= 'z'; i++){
+ x[0] = byte i;
+ x = x[1: ];
+ libc0->strncpy(x, pid, libc0->strlen(x));
+ (ok, nil) := sys->stat(libc0->ab2s(t));
+ if(ok >= 0)
+ continue;
+ }
+}
+
+postnote(t: int, pid: int, note: array of byte)
+{
+ if(pid == 0)
+ return;
+ fd := sys->open("#p/" + string pid + "/ctl", Sys->OWRITE);
+ if(fd == nil)
+ return;
+ s := libc0->ab2s(note);
+ if(t == PNGROUP)
+ s += "grp";
+ sys->fprint(fd, "%s", s);
+ fd = nil;
+}
+
+map(s: array of byte, n: int): int
+{
+ i := j := 0;
+ ls := libc0->strlen(s);
+ while(i < ls){
+ if(j == n)
+ return i;
+ (nil, l, nil) := sys->byte2char(s, i);
+ i += l;
+ j++;
+ }
+ return -1;
+}
+
+regadd(s: array of byte, m: array of (int, int), rm: array of Resub, n: int)
+{
+ k := len m;
+ for(i := 0; i < n; i++)
+ rm[i].sp = rm[i].ep= nil;
+ for(i = 0; i < k && i < n; i++){
+ (a, b) := m[i];
+ if(a >= 0 && b >= 0){
+ a = map(s, a);
+ b = map(s, b);
+ if(a >= 0 && b >= 0){
+ rm[i].sp = s[a: ];
+ rm[i].ep = s[b: ];
+ }
+ }
+ }
+}
+
+scopy(d: array of byte, j: int, m: array of Resub, k: int, n: int): int
+{
+ if(k >= n)
+ return 0;
+ sp := m[k].sp;
+ ep := m[k].ep;
+ if(sp == nil || ep == nil)
+ return 0;
+ c := ep[0];
+ ep[0] = byte 0;
+ libc0->strcpy(d[j: ], sp);
+ ep[0] = c;
+ return libc0->strlen(sp)-libc0->strlen(ep);
+}
+
+regsub(s: array of byte, d: array of byte, m: array of Resub, n: int)
+{
+ # libc0->strncpy(d, s, libc0->strlen(d));
+ ls := libc0->strlen(s);
+ j := 0;
+ for(i := 0; i < ls; i++){
+ case(int s[i]){
+ '\\' =>
+ if(i+1 < ls && s[i+1] >= byte '0' && s[i+1] <= byte '9'){
+ k := int s[++i]-'0';
+ j += scopy(d, j, m, k, n);
+ }
+ else
+ d[j++] = byte '\\';
+ '&' =>
+ j += scopy(d, j, m, 0, n);
+ * =>
+ d[j++] = s[i];
+ }
+ }
+ d[j] = byte 0;
+}
+
+wpid := -1;
+wfd : ref Sys->FD;
+wprocs := 0;
+
+openwait()
+{
+ pid := sys->pctl(0, nil);
+ w := sys->sprint("#p/%d/wait", pid);
+ fd := sys->open(w, Sys->OREAD);
+ if(fd == nil){
+ perrors("fd == nil in wait");
+ return;
+ }
+ wpid = pid;
+ wfd = fd;
+}
+
+addwait()
+{
+ if(wpid == sys->pctl(0, nil))
+ wprocs++;
+}
+
+wait(): (int, array of byte)
+{
+ n: int;
+
+ if(wpid != -1 && wpid != sys->pctl(0, nil)){
+ perrors(sys->sprint("wait: pid %d != pid %d", wpid, sys->pctl(0, nil)));
+ return (-1, nil);
+ }
+ if(wprocs == 0)
+ return (-1, nil);
+ buf := array[Sys->WAITLEN] of byte;
+ status := "";
+ for(;;){
+ if((n = sys->read(wfd, buf, len buf))<0)
+ perrors("bad read in wait");
+ status = string buf[0:n];
+ break;
+ }
+ s := "";
+ if(status[len status - 1] != ':')
+ s = status;
+ wprocs--;
+ return (int status, libc0->s2ab(s));
+}
+
+abort()
+{
+ exit;
+}
+
+execl(sh: string, name: string, a1: string, a2: string, a3: string, a4: string)
+{
+ # sys->print("execl %s : %s %s %s %s %s\n", sh, name, a1, a2, a3, a4);
+
+ c := load Command sh;
+ if(c == nil){
+ sys->fprint(sys->fildes(2), "x %s: %r\n", sh);
+ return;
+ }
+ argl: list of string;
+ if(a4 != nil)
+ argl = a4 :: argl;
+ if(a3 != nil)
+ argl = a3 :: argl;
+ if(a2 != nil)
+ argl = a2 :: argl;
+ if(a1 != nil)
+ argl = a1 :: argl;
+ # argl = "-x" :: argl;
+ argl = name :: argl;
+ # argl := list of { name, a1, a2, a3, a4 };
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "executing %s with args (%s, %s, %s, %s, %s)\n", sh, name, a1, a2, a3, a4);
+ c->init(nil, argl);
+}
+
+getuser(): string
+{
+ fd := sys->open("/dev/user", sys->OREAD);
+ if(fd == nil)
+ return "";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "";
+ return string buf[0: n];
+}
+
+initbind()
+{
+ f := sys->sprint("/usr/%s/lib/mkbinds", getuser());
+ b := bufio->open(f, Bufio->OREAD);
+ if(b == nil)
+ b = bufio->open("/appl/cmd/mk/mkbinds", Bufio->OREAD);
+ if(b == nil)
+ return;
+ while((s := b.gets('\n')) != nil){
+ m := len s;
+ if(s[m-1] == '\n')
+ s = s[0: m-1];
+ (n, l) := sys->tokenize(s, " \t");
+ if(n == 2)
+ sys->bind(hd l, hd tl l, Sys->MREPL);
+ }
+}
+
+#
+# mk
+#
+
+runerrs: int;
+
+mk(target: array of byte)
+{
+ node: ref Node;
+ did: int = 0;
+
+ nproc(); # it can be updated dynamically
+ nrep(); # it can be updated dynamically
+ runerrs = 0;
+ node = graph(target);
+ if(debug&D_GRAPH){
+ dumpn(libc0->s2ab("new target\n"), node);
+ bout.flush();
+ }
+ clrmade(node);
+ while(node.flags&NOTMADE){
+ if(work(node, nil, nil))
+ did = 1; # found something to do
+ else{
+ if(waitup(1, nil) > 0){
+ if(node.flags&(NOTMADE|BEINGMADE)){
+ assert(libc0->s2ab("must be run errors"), runerrs);
+ break; # nothing more waiting
+ }
+ }
+ }
+ }
+ if(node.flags&BEINGMADE)
+ waitup(-1, nil);
+ while(jobs != nil)
+ waitup(-2, nil);
+ assert(libc0->s2ab("target didn't get done"), runerrs || node.flags&MADE);
+ if(did == 0)
+ bout.puts(sys->sprint("mk: '%s' is up to date\n", libc0->ab2s(node.name)));
+}
+
+clrmade(n: ref Node)
+{
+ a: ref Arc;
+
+ n.flags &= ~(CANPRETEND|PRETENDING);
+ if(libc0->strchr(n.name, '(') == nil || n.time)
+ n.flags |= CANPRETEND;
+ n.flags = n.flags&~(NOTMADE|BEINGMADE|MADE)|NOTMADE;
+ for(a = n.prereqs; a != nil; a = a.next)
+ if(a.n != nil)
+ clrmade(a.n);
+}
+
+unpretend(n: ref Node)
+{
+ n.flags = n.flags&~(NOTMADE|BEINGMADE|MADE)|NOTMADE;
+ n.flags &= ~(CANPRETEND|PRETENDING);
+ n.time = 0;
+}
+
+work(node: ref Node, p: ref Node, parc: ref Arc): int
+{
+ a, ra: ref Arc;
+ weoutofdate, ready: int;
+ did: int = 0;
+
+ # print("work(%s) flags=0x%x time=%ld\n", node->name, node->flags, node->time);/*
+ if(node.flags&BEINGMADE)
+ return did;
+ if(node.flags&MADE && node.flags&PRETENDING && p != nil && outofdate(p, parc, 0)){
+ if(explain != nil)
+ sys->fprint(sys->fildes(1), "unpretending %s(%d) because %s is out of date(%d)\n", libc0->ab2s(node.name), node.time, libc0->ab2s(p.name), p.time);
+ unpretend(node);
+ }
+ #
+ # have a look if we are pretending in case
+ # someone has been unpretended out from underneath us
+ #
+ if(node.flags&MADE){
+ if(node.flags&PRETENDING){
+ node.time = 0;
+ }
+ else
+ return did;
+ }
+ # consider no prerequsite case
+ if(node.prereqs == nil){
+ if(node.time == 0){
+ sys->fprint(sys->fildes(2), "mk: don't know how to make '%s'\n", libc0->ab2s(node.name));
+ if(kflag){
+ node.flags |= BEINGMADE;
+ runerrs++;
+ }
+ else
+ Exit();
+ }
+ else
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ return did;
+ }
+ #
+ # now see if we are out of date or what
+ #
+ ready = 1;
+ weoutofdate = aflag;
+ ra = nil;
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(a.n != nil){
+ did = work(a.n, node, a) || did;
+ if(a.n.flags&(NOTMADE|BEINGMADE))
+ ready = 0;
+ if(outofdate(node, a, 0)){
+ weoutofdate = 1;
+ if(ra == nil || ra.n == nil || ra.n.time < a.n.time)
+ ra = a;
+ }
+ }
+ else{
+ if(node.time == 0){
+ if(ra == nil)
+ ra = a;
+ weoutofdate = 1;
+ }
+ }
+ if(ready == 0) # can't do anything now
+ return did;
+ if(weoutofdate == 0){
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ return did;
+ }
+ #
+ # can we pretend to be made?
+ #
+ if(iflag == 0 && node.time == 0 && node.flags&(PRETENDING|CANPRETEND) && p != nil && ra.n != nil && !outofdate(p, ra, 0)){
+ node.flags &= ~CANPRETEND;
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ if(explain != nil && (node.flags&PRETENDING) == 0)
+ sys->fprint(sys->fildes(1), "pretending %s has time %d\n", libc0->ab2s(node.name), node.time);
+ node.flags |= PRETENDING;
+ return did;
+ }
+ #
+ # node is out of date and we REALLY do have to do something.
+ # quickly rescan for pretenders
+ #
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(a.n != nil && a.n.flags&PRETENDING){
+ if(explain != nil)
+ if(ra.n != nil)
+ bout.puts(sys->sprint("unpretending %s because of %s because of %s\n", libc0->ab2s(a.n.name), libc0->ab2s(node.name), libc0->ab2s(ra.n.name)));
+ else
+ bout.puts(sys->sprint("unpretending %s because of %s because of %s\n", libc0->ab2s(a.n.name), libc0->ab2s(node.name), "rule with no prerequisites"));
+ unpretend(a.n);
+ did = work(a.n, node, a) || did;
+ ready = 0;
+ }
+ if(ready == 0) # try later unless nothing has happened for -k's sake
+ return did || work(node, p, parc);
+ did = dorecipe(node) || did;
+ return did;
+}
+
+update(fake: int, node: ref Node)
+{
+ a: ref Arc;
+
+ if(fake)
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|BEINGMADE;
+ else
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ if((node.flags&VIRTUAL) == 0 && access(node.name, 0) == 0){
+ node.time = timeof(node.name, 1);
+ node.flags &= ~(CANPRETEND|PRETENDING);
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(a.prog != nil)
+ outofdate(node, a, 1);
+ }
+ else{
+ node.time = 1;
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(a.n != nil && outofdate(node, a, 1))
+ node.time = a.n.time;
+ }
+ # print("----node %s time=%ld flags=0x%x\n", node->name, node->time, node->flags);/*
+}
+
+pcmp(prog: array of byte, p: array of byte, q: array of byte): int
+{
+ buf := array[3*NAMEBLOCK] of byte;
+ pid: int;
+
+ bout.flush();
+ stob(buf, sys->sprint("%s '%s' '%s'\n", libc0->ab2s(prog), libc0->ab2s(p), libc0->ab2s(q)));
+ pid = pipecmd(buf, nil, nil);
+ apid := array[1] of int;
+ apid[0] = pid;
+ while(waitup(-3, apid) >= 0)
+ ;
+ pid = apid[0];
+ if(pid)
+ return 2;
+ else
+ return 1;
+}
+
+outofdate(node: ref Node, arc: ref Arc, eval: int): int
+{
+ buf := array[3*NAMEBLOCK] of byte;
+ str: array of byte;
+ sym: ref Symtab;
+ ret: int;
+
+ str = nil;
+ if(arc.prog != nil){
+ stob(buf, sys->sprint("%s%c%s", libc0->ab2s(node.name), 8r377, libc0->ab2s(arc.n.name)));
+ sym = symlooki(buf, S_OUTOFDATE, 0);
+ if(sym == nil || eval){
+ if(sym == nil)
+ str = libc0->strdup(buf);
+ ret = pcmp(arc.prog, node.name, arc.n.name);
+ if(sym != nil)
+ sym.ivalue = ret;
+ else
+ symlooki(str, S_OUTOFDATE, ret);
+ }
+ else
+ ret = int sym.ivalue;
+ return ret-1;
+ }
+ else if(libc0->strchr(arc.n.name, '(') != nil && arc.n.time == 0) # missing archive member
+ return 1;
+ else
+ return node.time < arc.n.time;
+}
+
+
+#
+# recipe
+#
+
+dorecipe(node: ref Node): int
+{
+ buf := array[BIGBLOCK] of byte;
+ n: ref Node;
+ r: ref Rule = nil;
+ a, aa: ref Arc;
+ head := ref Word;
+ ahead := ref Word;
+ lp := ref Word;
+ ln := ref Word;
+ w, ww, aw: ref Word;
+ s: ref Symtab;
+ did: int = 0;
+
+ aa = nil;
+ #
+ # pick up the rule
+ #
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(int a.r.recipe[0])
+ r = (aa = a).r;
+ #
+ # no recipe? go to buggery!
+ #
+ if(r == nil){
+ if(!(node.flags&VIRTUAL) && !(node.flags&NORECIPE)){
+ sys->fprint(sys->fildes(2), "mk: no recipe to make '%s'\n", libc0->ab2s(node.name));
+ Exit();
+ }
+ if(libc0->strchr(node.name, '(') != nil && node.time == 0)
+ node.flags = node.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ else
+ update(0, node);
+ if(tflag){
+ if(!(node.flags&VIRTUAL))
+ touch(node.name);
+ else if(explain != nil)
+ bout.puts(sys->sprint("no touch of virtual '%s'\n", libc0->ab2s(node.name)));
+ }
+ return did;
+ }
+ #
+ # build the node list
+ #
+ node.next = nil;
+ head.next = nil;
+ ww = head;
+ ahead.next = nil;
+ aw = ahead;
+ if(r.attr&REGEXP){
+ ww.next = newword(node.name);
+ aw.next = newword(node.name);
+ }
+ else{
+ for(w = r.alltargets; w != nil; w = w.next){
+ if(r.attr&META)
+ subst(aa.stem, w.s, buf);
+ else
+ libc0->strcpy(buf, w.s);
+ aw.next = newword(buf);
+ aw = aw.next;
+ if((s = symlooki(buf, S_NODE, 0)) == nil)
+ continue; # not a node we are interested in
+ n = s.nvalue;
+ if(aflag == 0 && n.time){
+ for(a = n.prereqs; a != nil; a = a.next)
+ if(a.n != nil && outofdate(n, a, 0))
+ break;
+ if(a == nil)
+ continue;
+ }
+ ww.next = newword(buf);
+ ww = ww.next;
+ if(n == node)
+ continue;
+ n.next = node.next;
+ node.next = n;
+ }
+ }
+ for(n = node; n != nil; n = n.next)
+ if((n.flags&READY) == 0)
+ return did;
+ #
+ # gather the params for the job
+ #
+ lp.next = ln.next = nil;
+ for(n = node; n != nil; n = n.next){
+ for(a = n.prereqs; a != nil; a = a.next){
+ if(a.n != nil){
+ addw(lp, a.n.name);
+ if(outofdate(n, a, 0)){
+ addw(ln, a.n.name);
+ if(explain != nil)
+ sys->fprint(sys->fildes(1), "%s(%d) < %s(%d)\n", libc0->ab2s(n.name), n.time, libc0->ab2s(a.n.name), a.n.time);
+ }
+ }
+ else{
+ if(explain != nil)
+ sys->fprint(sys->fildes(1), "%s has no prerequisites\n", libc0->ab2s(n.name));
+ }
+ }
+ n.flags = n.flags&~(NOTMADE|BEINGMADE|MADE)|BEINGMADE;
+ }
+ # print("lt=%s ln=%s lp=%s\n",wtos(head.next, ' '),wtos(ln.next, ' '),wtos(lp.next, ' '));/*
+ run(newjob(r, node, aa.stem, aa.match, lp.next, ln.next, head.next, ahead.next));
+ return 1;
+}
+
+addw(w: ref Word, s: array of byte)
+{
+ lw: ref Word;
+
+ for(lw = w; (w = w.next) != nil; lw = w){
+ if(libc0->strcmp(s, w.s) == 0)
+ return;
+ }
+ lw.next = newword(s);
+}
+
+#
+# rule
+#
+
+lr, lmr: ref Rule;
+nrules: int = 0;
+
+addrule(head: array of byte, tail: ref Word, body: array of byte, ahead: ref Word, attr: int, hline: int, prog: array of byte)
+{
+ r, rr: ref Rule;
+ sym: ref Symtab;
+ reuse: int;
+
+ r = nil;
+ reuse = 0;
+ if((sym = symlooki(head, S_TARGET, 0)) != nil){
+ for(r = sym.rvalue; r != nil; r = r.chain)
+ if(rcmp(r, head, tail) == 0){
+ reuse = 1;
+ break;
+ }
+ }
+ if(r == nil)
+ r = ref Rule;
+ r.target = head;
+ r.tail = tail;
+ r.recipe = body;
+ r.line = hline;
+ r.file = infile;
+ r.attr = attr;
+ r.alltargets = ahead;
+ r.prog = prog;
+ r.rule = nrules++;
+ if(!reuse){
+ rr = symlookr(head, S_TARGET, r).rvalue;
+ if(rr != r){
+ r.chain = rr.chain;
+ rr.chain = r;
+ }
+ else
+ r.chain = nil;
+ }
+ if(!reuse)
+ r.next = nil;
+ if(attr&REGEXP || charin(head, libc0->s2ab("%&")) != nil){
+ r.attr |= META;
+ if(reuse)
+ return;
+ if(attr&REGEXP){
+ patrule = r;
+ e := "";
+ (r.pat, e) = regex->compile(libc0->ab2s(head), 1);
+ if(e != nil)
+ perrors(sys->sprint("%s: %s", libc0->ab2s(head), e));
+ }
+ if(metarules == nil)
+ metarules = lmr = r;
+ else{
+ lmr.next = r;
+ lmr = r;
+ }
+ }
+ else{
+ if(reuse)
+ return;
+ r.pat = nil;
+ if(rules == nil)
+ rules = lr = r;
+ else{
+ lr.next = r;
+ lr = r;
+ }
+ }
+}
+
+dumpr(s: array of byte, r: ref Rule)
+{
+ bout.puts(sys->sprint("%s: start=%x\n", libc0->ab2s(s), r));
+ for(; r != nil; r = r.next){
+ bout.puts(sys->sprint("\tRule %x: %s[%d] attr=%x next=%x chain=%x alltarget='%s'", r, libc0->ab2s(r.file), r.line, r.attr, r.next, r.chain, wtostr(r.alltargets, ' ')));
+ if(r.prog != nil)
+ bout.puts(sys->sprint(" prog='%s'", libc0->ab2s(r.prog)));
+ bout.puts(sys->sprint("\n\ttarget=%s: %s\n", libc0->ab2s(r.target), wtostr(r.tail, ' ')));
+ bout.puts(sys->sprint("\trecipe@%x='%s'\n", r.recipe, libc0->ab2s(r.recipe)));
+ }
+}
+
+rcmp(r: ref Rule, target: array of byte, tail: ref Word): int
+{
+ w: ref Word;
+
+ if(libc0->strcmp(r.target, target))
+ return 1;
+ for(w = r.tail; w != nil && tail != nil; (w, tail) = (w.next, tail.next))
+ if(libc0->strcmp(w.s, tail.s))
+ return 1;
+ return w != nil || tail != nil;
+}
+
+rulecnt(): array of byte
+{
+ s: array of byte;
+
+ s = array[nrules] of byte;
+ for(i := 0; i < nrules; i++)
+ s[i] = byte 0;
+ return s;
+}
+
+#
+# graph
+#
+
+
+graph(target: array of byte): ref Node
+{
+ node: ref Node;
+ cnt: array of byte;
+
+ cnt = rulecnt();
+ node = applyrules(target, cnt);
+ cnt = nil;
+ cyclechk(node);
+ node.flags |= PROBABLE; # make sure it doesn't get deleted
+ vacuous(node);
+ ambiguous(node);
+ attribute(node);
+ return node;
+}
+
+applyrules(target: array of byte, cnt: array of byte): ref Node
+{
+ sym: ref Symtab;
+ node: ref Node;
+ r: ref Rule;
+ head := ref Arc;
+ a: ref Arc = head;
+ w: ref Word;
+ stem := array[NAMEBLOCK] of byte;
+ buf := array[NAMEBLOCK] of byte;
+ rmatch := array[NREGEXP] of Resub;
+
+ # print("applyrules(%lux='%s')\n", target, target);/*
+ sym = symlooki(target, S_NODE, 0);
+ if(sym != nil)
+ return sym.nvalue;
+ target = libc0->strdup(target);
+ node = newnode(target);
+ head.n = nil;
+ head.next = nil;
+ sym = symlooki(target, S_TARGET, 0);
+ for(i := 0; i < NREGEXP; i++)
+ rmatch[i].sp = rmatch[i].ep = nil;
+ if(sym != nil)
+ tmp_1 := sym.rvalue;
+ else
+ tmp_1 = nil;
+ for(r = tmp_1; r != nil; r = r.chain){
+ if(r.attr&META)
+ continue;
+ if(libc0->strcmp(target, r.target))
+ continue;
+ if((r.recipe == nil || !int r.recipe[0]) && (r.tail == nil || r.tail.s == nil || !int r.tail.s[0])) # no effect; ignore
+ continue;
+ if(int cnt[r.rule] >= nreps)
+ continue;
+ cnt[r.rule]++;
+ node.flags |= PROBABLE;
+ # if(r->attr&VIR)
+ # * node->flags |= VIRTUAL;
+ # * if(r->attr&NOREC)
+ # * node->flags |= NORECIPE;
+ # * if(r->attr&DEL)
+ # * node->flags |= DELETE;
+ #
+ if(r.tail == nil || r.tail.s == nil || !int r.tail.s[0]){
+ a.next = newarc(nil, r, libc0->s2ab(""), rmatch);
+ a = a.next;
+ }
+ else
+ for(w = r.tail; w != nil; w = w.next){
+ a.next = newarc(applyrules(w.s, cnt), r, libc0->s2ab(""), rmatch);
+ a = a.next;
+ }
+ cnt[r.rule]--;
+ head.n = node;
+ }
+ for(r = metarules; r != nil; r = r.next){
+ if((r.recipe == nil || !int r.recipe[0]) && (r.tail == nil || r.tail.s == nil || !int r.tail.s[0])) # no effect; ignore
+ continue;
+ if(r.attr&NOVIRT && a != head && a.r.attr&VIR)
+ continue;
+ if(r.attr&REGEXP){
+ stem[0] = byte 0;
+ patrule = r;
+ for(i = 0; i < NREGEXP; i++)
+ rmatch[i].sp = rmatch[i].ep = nil;
+ m := regex->execute(r.pat, libc0->ab2s(node.name));
+ if(m == nil)
+ continue;
+ regadd(node.name, m, rmatch, NREGEXP);
+ }
+ else{
+ if(!match(node.name, r.target, stem))
+ continue;
+ }
+ if(int cnt[r.rule] >= nreps)
+ continue;
+ cnt[r.rule]++;
+ # if(r->attr&VIR)
+ # * node->flags |= VIRTUAL;
+ # * if(r->attr&NOREC)
+ # * node->flags |= NORECIPE;
+ # * if(r->attr&DEL)
+ # * node->flags |= DELETE;
+ #
+ if(r.tail == nil || r.tail.s == nil || !int r.tail.s[0]){
+ a.next = newarc(nil, r, stem, rmatch);
+ a = a.next;
+ }
+ else
+ for(w = r.tail; w != nil; w = w.next){
+ if(r.attr&REGEXP)
+ regsub(w.s, buf, rmatch, NREGEXP);
+ else
+ subst(stem, w.s, buf);
+ a.next = newarc(applyrules(buf, cnt), r, stem, rmatch);
+ a = a.next;
+ }
+ cnt[r.rule]--;
+ }
+ a.next = node.prereqs;
+ node.prereqs = head.next;
+ return node;
+}
+
+togo(node: ref Node)
+{
+ la, a: ref Arc;
+
+ # delete them now
+ la = nil;
+ for(a = node.prereqs; a != nil; (la, a) = (a, a.next))
+ if(a.flag&TOGO){
+ if(a == node.prereqs)
+ node.prereqs = a.next;
+ else
+ (la.next, a) = (a.next, la);
+ }
+}
+
+vacuous(node: ref Node): int
+{
+ la, a: ref Arc;
+ vac: int = !(node.flags&PROBABLE);
+
+ if(node.flags&READY)
+ return node.flags&VACUOUS;
+ node.flags |= READY;
+ for(a = node.prereqs; a != nil; a = a.next)
+ if(a.n != nil && vacuous(a.n) && a.r.attr&META)
+ a.flag |= TOGO;
+ else
+ vac = 0;
+ # if a rule generated arcs that DON'T go; no others from that rule go
+ for(a = node.prereqs; a != nil; a = a.next)
+ if((a.flag&TOGO) == 0)
+ for(la = node.prereqs; la != nil; la = la.next)
+ if(la.flag&TOGO && la.r == a.r){
+ la.flag &= ~TOGO;
+ }
+ togo(node);
+ if(vac)
+ node.flags |= VACUOUS;
+ return vac;
+}
+
+newnode(name: array of byte): ref Node
+{
+ node: ref Node;
+
+ node = ref Node;
+ symlookn(name, S_NODE, node);
+ node.name = name;
+ node.time = timeof(name, 0);
+ node.prereqs = nil;
+ if(node.time)
+ node.flags = PROBABLE;
+ else
+ node.flags = 0;
+ node.next = nil;
+ return node;
+}
+
+dumpn(s: array of byte, n: ref Node)
+{
+ buf := array[1024] of byte;
+ a: ref Arc;
+
+ if(s[0] == byte ' ')
+ stob(buf, sys->sprint("%s ", libc0->ab2s(s)));
+ else
+ stob(buf, sys->sprint("%s ", ""));
+ bout.puts(sys->sprint("%s%s@%x: time=%d flags=0x%x next=%x\n", libc0->ab2s(s), libc0->ab2s(n.name), n, n.time, n.flags, n.next));
+ for(a = n.prereqs; a != nil; a = a.next)
+ dumpa(buf, a);
+}
+
+trace(s: array of byte, a: ref Arc)
+{
+ sys->fprint(sys->fildes(2), "\t%s", libc0->ab2s(s));
+ while(a != nil){
+ if(a.n != nil)
+ sys->fprint(sys->fildes(2), " <-(%s:%d)- %s", libc0->ab2s(a.r.file), a.r.line, libc0->ab2s(a.n.name));
+ else
+ sys->fprint(sys->fildes(2), " <-(%s:%d)- %s", libc0->ab2s(a.r.file), a.r.line, "");
+ if(a.n != nil){
+ for(a = a.n.prereqs; a != nil; a = a.next)
+ if(int a.r.recipe[0])
+ break;
+ }
+ else
+ a = nil;
+ }
+ sys->fprint(sys->fildes(2), "\n");
+}
+
+cyclechk(n: ref Node)
+{
+ a: ref Arc;
+
+ if(n.flags&CYCLE && n.prereqs != nil){
+ sys->fprint(sys->fildes(2), "mk: cycle in graph detected at target %s\n", libc0->ab2s(n.name));
+ Exit();
+ }
+ n.flags |= CYCLE;
+ for(a = n.prereqs; a != nil; a = a.next)
+ if(a.n != nil)
+ cyclechk(a.n);
+ n.flags &= ~CYCLE;
+}
+
+ambiguous(n: ref Node)
+{
+ a: ref Arc;
+ r: ref Rule = nil;
+ la: ref Arc;
+ bad: int = 0;
+
+ la = nil;
+ for(a = n.prereqs; a != nil; a = a.next){
+ if(a.n != nil)
+ ambiguous(a.n);
+ if(a.r.recipe[0] == byte 0)
+ continue;
+ if(r == nil)
+ (r, la) = (a.r, a);
+ else{
+ if(r.recipe != a.r.recipe){
+ if(r.attr&META && !(a.r.attr&META)){
+ la.flag |= TOGO;
+ (r, la) = (a.r, a);
+ }
+ else if(!(r.attr&META) && a.r.attr&META){
+ a.flag |= TOGO;
+ continue;
+ }
+ }
+ if(r.recipe != a.r.recipe){
+ if(bad == 0){
+ sys->fprint(sys->fildes(2), "mk: ambiguous recipes for %s:\n", libc0->ab2s(n.name));
+ bad = 1;
+ trace(n.name, la);
+ }
+ trace(n.name, a);
+ }
+ }
+ }
+ if(bad)
+ Exit();
+ togo(n);
+}
+
+attribute(n: ref Node)
+{
+ a: ref Arc;
+
+ for(a = n.prereqs; a != nil; a = a.next){
+ if(a.r.attr&VIR)
+ n.flags |= VIRTUAL;
+ if(a.r.attr&NOREC)
+ n.flags |= NORECIPE;
+ if(a.r.attr&DEL)
+ n.flags |= DELETE;
+ if(a.n != nil)
+ attribute(a.n);
+ }
+ if(n.flags&VIRTUAL)
+ n.time = 0;
+}
+
+#
+# arc
+#
+
+newarc(n: ref Node, r: ref Rule, stem: array of byte, match: array of Resub): ref Arc
+{
+ a: ref Arc;
+
+ a = ref Arc;
+ a.n = n;
+ a.r = r;
+ a.stem = libc0->strdup(stem);
+ a.match = array[NREGEXP] of array of byte;
+ rcopy(a.match, match, NREGEXP);
+ a.next = nil;
+ a.flag = 0;
+ a.prog = r.prog;
+ return a;
+}
+
+dumpa(s: array of byte, a: ref Arc)
+{
+ buf := array[1024] of byte;
+
+ bout.puts(sys->sprint("%sArc@%x: n=%x r=%x flag=0x%x stem='%s'", libc0->ab2s(s), a, a.n, a.r, a.flag, libc0->ab2s(a.stem)));
+ if(a.prog != nil)
+ bout.puts(sys->sprint(" prog='%s'", libc0->ab2s(a.prog)));
+ bout.puts("\n");
+ if(a.n != nil){
+ if(s[0] == byte ' ')
+ stob(buf, sys->sprint("%s ", libc0->ab2s(s)));
+ else
+ stob(buf, sys->sprint("%s ", ""));
+ dumpn(buf, a.n);
+ }
+}
+
+nrep()
+{
+ sym: ref Symtab;
+ w: ref Word;
+
+ sym = symlooki(libc0->s2ab("NREP"), S_VAR, 0);
+ if(sym != nil){
+ w = sym.wvalue;
+ if(w != nil && w.s != nil && int w.s[0])
+ nreps = int string w.s;
+ }
+ if(nreps < 1)
+ nreps = 1;
+ if(debug&D_GRAPH)
+ bout.puts(sys->sprint("nreps = %d\n", nreps));
+}
+
+#
+# job
+#
+
+newjob(r: ref Rule, nlist: ref Node, stem: array of byte, match: array of array of byte, pre: ref Word, npre: ref Word, tar: ref Word, atar: ref Word): ref Job
+{
+ j: ref Job;
+
+ j = ref Job;
+ j.r = r;
+ j.n = nlist;
+ j.stem = stem;
+ j.match = match;
+ j.p = pre;
+ j.np = npre;
+ j.t = tar;
+ j.at = atar;
+ j.nproc = -1;
+ j.next = nil;
+ return j;
+}
+
+dumpj(s: array of byte, j: ref Job, all: int)
+{
+ bout.puts(sys->sprint("%s\n", libc0->ab2s(s)));
+ while(j != nil){
+ bout.puts(sys->sprint("job@%x: r=%x n=%x stem='%s' nproc=%d\n", j, j.r, j.n, libc0->ab2s(j.stem), j.nproc));
+ bout.puts(sys->sprint("\ttarget='%s' alltarget='%s' prereq='%s' nprereq='%s'\n", wtostr(j.t, ' '), wtostr(j.at, ' '), wtostr(j.p, ' '), wtostr(j.np, ' ')));
+ if(all)
+ j = j.next;
+ else
+ j = nil;
+ }
+}
+
+#
+# run
+#
+
+Event: adt{
+ pid: int;
+ job: ref Job;
+};
+
+events: array of Event;
+nevents, nrunning, nproclimit: int;
+
+Process: adt{
+ pid: int;
+ status: int;
+ b: cyclic ref Process;
+ f: cyclic ref Process;
+};
+
+phead, pfree: ref Process;
+
+run(j: ref Job)
+{
+ jj: ref Job;
+
+ if(jobs != nil){
+ for(jj = jobs; jj.next != nil; jj = jj.next)
+ ;
+ jj.next = j;
+ }
+ else
+ jobs = j;
+ j.next = nil;
+ # this code also in waitup after parse redirect
+ if(nrunning < nproclimit)
+ sched();
+}
+
+sched()
+{
+ flags: array of byte;
+ j: ref Job;
+ buf: ref Bufblock;
+ slot: int;
+ n: ref Node;
+ e: array of Envy;
+
+ if(jobs == nil){
+ usage();
+ return;
+ }
+ j = jobs;
+ jobs = j.next;
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "firing up job for target %s\n", libc0->ab2s(wtos(j.t, ' ')));
+ slot = nextslot();
+ events[slot].job = j;
+ buf = newbuf();
+ e = buildenv(j, slot);
+ shprint(j.r.recipe, e, buf);
+ if(!tflag && (nflag || !(j.r.attr&QUIET)))
+ bout.write(buf.start, libc0->strlen(buf.start));
+ freebuf(buf);
+ if(nflag || tflag){
+ bout.flush();
+ for(n = j.n; n != nil; n = n.next){
+ if(tflag){
+ if(!(n.flags&VIRTUAL))
+ touch(n.name);
+ else if(explain != nil)
+ bout.puts(sys->sprint("no touch of virtual '%s'\n", libc0->ab2s(n.name)));
+ }
+ n.time = daytime->now();
+ n.flags = n.flags&~(NOTMADE|BEINGMADE|MADE)|MADE;
+ }
+ }
+ else{
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "recipe='%s'", libc0->ab2s(j.r.recipe)); #
+ bout.flush();
+ if(j.r.attr&NOMINUSE)
+ flags = nil;
+ else
+ flags = libc0->s2ab("-e");
+ events[slot].pid = execsh(flags, j.r.recipe, nil, e);
+ usage();
+ nrunning++;
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "pid for target %s = %d\n", libc0->ab2s(wtos(j.t, ' ')), events[slot].pid);
+ }
+}
+
+waitup(echildok: int, retstatus: array of int): int
+{
+ e: array of Envy;
+ pid, slot: int;
+ s: ref Symtab;
+ w: ref Word;
+ j: ref Job;
+ buf := array[ERRLEN] of byte;
+ bp: ref Bufblock;
+ uarg: int = 0;
+ done: int;
+ n: ref Node;
+ p: ref Process;
+ runerrs: int;
+
+ # first check against the proces slist
+ if(retstatus != nil)
+ for(p = phead; p != nil; p = p.f)
+ if(p.pid == retstatus[0]){
+ retstatus[0] = p.status;
+ pdelete(p);
+ return -1;
+ }
+ # rogue processes
+for(;;){
+ pid = waitfor(buf);
+ if(pid == -1){
+ if(echildok > 0)
+ return 1;
+ else{
+ sys->fprint(sys->fildes(2), "mk: (waitup %d) ", echildok);
+ perrors("mk wait");
+ Exit();
+ }
+ }
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "waitup got pid=%d, status='%s'\n", pid, libc0->ab2s(buf));
+ if(retstatus != nil && pid == retstatus[0]){
+ if(int buf[0])
+ retstatus[0] = 1;
+ else
+ retstatus[0] = 0;
+ return -1;
+ }
+ slot = pidslot(pid);
+ if(slot < 0){
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(2), "mk: wait returned unexpected process %d\n", pid);
+ if(int buf[0])
+ pnew(pid, 1);
+ else
+ pnew(pid, 0);
+ continue;
+ }
+ break;
+}
+ j = events[slot].job;
+ usage();
+ nrunning--;
+ events[slot].pid = -1;
+ if(int buf[0]){
+ e = buildenv(j, slot);
+ bp = newbuf();
+ shprint(j.r.recipe, e, bp);
+ front(bp.start);
+ sys->fprint(sys->fildes(2), "mk: %s: exit status=%s", libc0->ab2s(bp.start), libc0->ab2s(buf));
+ freebuf(bp);
+ for((n, done) = (j.n, 0); n != nil; n = n.next)
+ if(n.flags&DELETE){
+ if(done++ == 0)
+ sys->fprint(sys->fildes(2), ", deleting");
+ sys->fprint(sys->fildes(2), " '%s'", libc0->ab2s(n.name));
+ delete(n.name);
+ }
+ sys->fprint(sys->fildes(2), "\n");
+ if(kflag){
+ runerrs++;
+ uarg = 1;
+ }
+ else{
+ jobs = nil;
+ Exit();
+ }
+ }
+ for(w = j.t; w != nil; w = w.next){
+ if((s = symlooki(w.s, S_NODE, 0)) == nil)
+ continue; # not interested in this node
+ update(uarg, s.nvalue);
+ }
+ if(nrunning < nproclimit)
+ sched();
+ return 0;
+}
+
+nproc()
+{
+ sym: ref Symtab;
+ w: ref Word;
+
+ if((sym = symlooki(libc0->s2ab("NPROC"), S_VAR, 0)) != nil){
+ w = sym.wvalue;
+ if(w != nil && w.s != nil && int w.s[0])
+ nproclimit = int string w.s;
+ }
+ if(1 || nproclimit < 1)
+ nproclimit = 1;
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "nprocs = %d\n", nproclimit);
+ if(nproclimit > nevents){
+ if(nevents){
+ olen := len events;
+ ne := array[nproclimit] of Event;
+ if(olen)
+ ne[0: ] = events[0: olen];
+ events = ne;
+ }
+ else
+ events = array[nproclimit] of Event;
+ while(nevents < nproclimit)
+ events[nevents++].pid = 0;
+ }
+}
+
+nextslot(): int
+{
+ i: int;
+
+ for(i = 0; i < nproclimit; i++)
+ if(events[i].pid <= 0)
+ return i;
+ assert(libc0->s2ab("out of slots!!"), 0);
+ return 0; # cyntax
+}
+
+pidslot(pid: int): int
+{
+ i: int;
+
+ for(i = 0; i < nevents; i++)
+ if(events[i].pid == pid)
+ return i;
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(2), "mk: wait returned unexpected process %d\n", pid);
+ return -1;
+}
+
+pnew(pid: int, status: int)
+{
+ p: ref Process;
+
+ if(pfree != nil){
+ p = pfree;
+ pfree = p.f;
+ }
+ else
+ p = ref Process;
+ p.pid = pid;
+ p.status = status;
+ p.f = phead;
+ phead = p;
+ if(p.f != nil)
+ p.f.b = p;
+ p.b = nil;
+}
+
+pdelete(p: ref Process)
+{
+ if(p.f != nil)
+ p.f.b = p.b;
+ if(p.b != nil)
+ p.b.f = p.f;
+ else
+ phead = p.f;
+ p.f = pfree;
+ pfree = p;
+}
+
+killchildren(msg: array of byte)
+{
+ p: ref Process;
+
+ kflag = 1; # to make sure waitup doesn't exit
+ jobs = nil; # make sure no more get scheduled
+ for(p = phead; p != nil; p = p.f)
+ expunge(p.pid, msg);
+ while(waitup(1, nil) == 0)
+ ;
+ bout.puts(sys->sprint("mk: %s\n", libc0->ab2s(msg)));
+ Exit();
+}
+
+tslot := array[1000] of int;
+tick: int;
+
+usage()
+{
+ t: int;
+
+ t = daytime->now();
+ if(tick)
+ tslot[nrunning] += t-tick;
+ tick = t;
+}
+
+prusage()
+{
+ i: int;
+
+ usage();
+ for(i = 0; i <= nevents; i++)
+ sys->fprint(sys->fildes(1), "%d: %d\n", i, tslot[i]);
+}
+
+#
+# file
+#
+
+# table-driven version in bootes dump of 12/31/96
+timeof(name: array of byte, force: int): int
+{
+ if(libc0->strchr(name, '(') != nil)
+ return atimeof(force, name); # archive
+ if(force)
+ return mtime(name);
+ return filetime(name);
+}
+
+touch(name: array of byte)
+{
+ bout.puts(sys->sprint("touch(%s)\n", libc0->ab2s(name)));
+ if(nflag)
+ return;
+ if(libc0->strchr(name, '(') != nil)
+ atouch(name); # archive
+ else if(chgtime(name) < 0){
+ perror(name);
+ Exit();
+ }
+}
+
+delete(name: array of byte)
+{
+ if(libc0->strchr(name, '(') == nil){ # file
+ if(sys->remove(libc0->ab2s(name)) < 0)
+ perror(name);
+ }
+ else
+ sys->fprint(sys->fildes(2), "hoon off; mk can'tdelete archive members\n");
+}
+
+timeinit(s: array of byte)
+{
+ t: int;
+ cp: array of byte;
+ r: int;
+ c, n: int;
+
+ t = daytime->now();
+ while(int s[0]){
+ cp = s;
+ do{
+ (r, n, nil) = sys->byte2char(s, 0);
+ if(r == ' ' || r == ',' || r == '\n')
+ break;
+ s = s[n: ];
+ }while(int s[0]);
+ c = int s[0];
+ s[0] = byte 0;
+ symlooki(libc0->strdup(cp), S_TIME, t).ivalue = t;
+ if(c){
+ s[0] = byte c;
+ s = s[1: ];
+ }
+ while(int s[0]){
+ (r, n, nil) = sys->byte2char(s, 0);
+ if(r != ' ' && r != ',' && r != '\n')
+ break;
+ s = s[n: ];
+ }
+ }
+}
+
+
+#
+# parse
+#
+
+infile: array of byte;
+mkinline: int;
+
+parse(f: array of byte, fd: ref Sys->FD, varoverride: int)
+{
+ hline, v: int;
+ body: array of byte;
+ head, tail: ref Word;
+ attr, set, pid: int;
+ prog, p: array of byte;
+ newfd: ref Sys->FD;
+ in: ref Iobuf;
+ buf: ref Bufblock;
+
+ if(fd == nil){
+ perror(f);
+ Exit();
+ }
+ ipush();
+ infile = libc0->strdup(f);
+ mkinline = 1;
+ in = bufio->fopen(fd, Sys->OREAD);
+ buf = newbuf();
+ while(assline(in, buf)){
+ hline = mkinline;
+ (v, head, tail, attr, prog) = rhead(buf.start);
+ case(v){
+ '<' =>
+ p = wtos(tail, ' ');
+ if(p[0] == byte 0){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing include file name\n");
+ Exit();
+ }
+ newfd = sys->open(libc0->ab2s(p), Sys->OREAD);
+ if(newfd == nil){
+ sys->fprint(sys->fildes(2), "warning: skipping missing include file: ");
+ perror(p);
+ }
+ else
+ parse(p, newfd, 0);
+ '|' =>
+ p = wtos(tail, ' ');
+ if(p[0] == byte 0){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing include program name\n");
+ Exit();
+ }
+ execinit();
+ anewfd := array[1] of ref Sys->FD;
+ anewfd[0] = newfd;
+ pid = pipecmd(p, envy, anewfd);
+ newfd = anewfd[0];
+ if(newfd == nil){
+ sys->fprint(sys->fildes(2), "warning: skipping missing program file: ");
+ perror(p);
+ }
+ else
+ parse(p, newfd, 0);
+ apid := array[1] of int;
+ apid[0] = pid;
+ while(waitup(-3, apid) >= 0)
+ ;
+ pid = apid[0];
+ if(pid != 0){
+ sys->fprint(sys->fildes(2), "bad include program status\n");
+ Exit();
+ }
+ ':' =>
+ body = rbody(in);
+ addrules(head, tail, body, attr, hline, prog);
+ '=' =>
+ if(head.next != nil){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "multiple vars on left side of assignment\n");
+ Exit();
+ }
+ if(symlooki(head.s, S_OVERRIDE, 0) != nil){
+ set = varoverride;
+ }
+ else{
+ set = 1;
+ if(varoverride)
+ symlooks(head.s, S_OVERRIDE, libc0->s2ab(""));
+ }
+ if(set){
+ #
+ # char *cp;
+ # dumpw("tail", tail);
+ # cp = wtos(tail, ' '); print("assign %s to %s\n", head->s, cp); free(cp);
+ #
+ setvar(head.s, tail);
+ symlooks(head.s, S_WESET, libc0->s2ab(""));
+ }
+ if(attr)
+ symlooks(head.s, S_NOEXPORT, libc0->s2ab(""));
+ * =>
+ if(hline >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), hline);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "expected one of :<=\n");
+ Exit();
+ }
+ }
+ fd = nil;
+ freebuf(buf);
+ ipop();
+}
+
+addrules(head: ref Word, tail: ref Word, body: array of byte, attr: int, hline: int, prog: array of byte)
+{
+ w: ref Word;
+
+ assert(libc0->s2ab("addrules args"), head != nil && body != nil);
+ # tuck away first non-meta rule as default target
+ if(target1 == nil && !(attr&REGEXP)){
+ for(w = head; w != nil; w = w.next)
+ if(charin(w.s, libc0->s2ab("%&")) != nil)
+ break;
+ if(w == nil)
+ target1 = wdup(head);
+ }
+ for(w = head; w != nil; w = w.next)
+ addrule(w.s, tail, body, head, attr, hline, prog);
+}
+
+rhead(line: array of byte): (int, ref Word, ref Word, int, array of byte)
+{
+ h, t: ref Word;
+ attr: int;
+ prog: array of byte;
+ p, pp: array of byte;
+ sep: int;
+ r: int;
+ n: int;
+ w: ref Word;
+
+ p = charin(line, libc0->s2ab(":=<"));
+ if(p == nil)
+ return ('?', nil, nil, 0, nil);
+ sep = int p[0];
+ p[0] = byte 0;
+ p = p[1: ];
+ if(sep == '<' && p[0] == byte '|'){
+ sep = '|';
+ p = p[1: ];
+ }
+ attr = 0;
+ prog = nil;
+ if(sep == '='){
+ pp = charin(p, termchars); # termchars is shell-dependent
+ if(pp != nil && pp[0] == byte '='){
+ while(p != pp){
+ (r, n, nil) = sys->byte2char(p, 0);
+ case(r){
+ * =>
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "unknown attribute '%c'\n", int p[0]);
+ Exit();
+ 'U' =>
+ attr = 1;
+ }
+ p = p[n: ];
+ }
+ p = p[1: ]; # skip trailing '='
+ }
+ }
+ if(sep == ':' && int p[0] && p[0] != byte ' ' && p[0] != byte '\t'){
+ while(int p[0]){
+ (r, n, nil) = sys->byte2char(p, 0);
+ if(r == ':')
+ break;
+ ea := p[n-1];
+ p = p[n: ];
+ case(r){
+ * =>
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "unknown attribute '%c'\n", int ea);
+ Exit();
+ 'D' =>
+ attr |= DEL;
+ 'E' =>
+ attr |= NOMINUSE;
+ 'n' =>
+ attr |= NOVIRT;
+ 'N' =>
+ attr |= NOREC;
+ 'P' =>
+ pp = libc0->strchr(p, ':');
+ if(pp == nil || pp[0] == byte 0){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing trailing :\n");
+ Exit();
+ }
+ pp[0] = byte 0;
+ prog = libc0->strdup(p);
+ pp[0] = byte ':';
+ p = pp;
+ 'Q' =>
+ attr |= QUIET;
+ 'R' =>
+ attr |= REGEXP;
+ 'U' =>
+ attr |= UPD;
+ 'V' =>
+ attr |= VIR;
+ }
+ }
+ if(p[0] != byte ':'){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing trailing :\n");
+ Exit();
+ }
+ p = p[1: ];
+ }
+ h = w = stow(line);
+ if(w.s[0] == byte 0 && sep != '<' && sep != '|'){
+ if(mkinline-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline-1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "no var on left side of assignment/rule\n");
+ Exit();
+ }
+ t = stow(p);
+ return (sep, h, t, attr, prog);
+}
+
+rbody(in: ref Iobuf): array of byte
+{
+ buf: ref Bufblock;
+ r, lastr: int;
+ p: array of byte;
+
+ lastr = '\n';
+ buf = newbuf();
+ for(;;){
+ r = in.getc();
+ if(r < 0)
+ break;
+ if(lastr == '\n'){
+ if(r == '#')
+ rinsert(buf, r);
+ else if(r != ' ' && r != '\t'){
+ in.ungetc();
+ break;
+ }
+ }
+ else
+ rinsert(buf, r);
+ lastr = r;
+ if(r == '\n')
+ mkinline++;
+ }
+ insert(buf, 0);
+ p = libc0->strdup(buf.start);
+ freebuf(buf);
+ return p;
+}
+
+input: adt{
+ file: array of byte;
+ line: int;
+ next: cyclic ref input;
+};
+
+inputs: ref input = nil;
+
+ipush()
+{
+ in, me: ref input;
+
+ me = ref input;
+ me.file = infile;
+ me.line = mkinline;
+ me.next = nil;
+ if(inputs == nil)
+ inputs = me;
+ else{
+ for(in = inputs; in.next != nil;)
+ in = in.next;
+ in.next = me;
+ }
+}
+
+ipop()
+{
+ in, me: ref input;
+
+ assert(libc0->s2ab("pop input list"), inputs != nil);
+ if(inputs.next == nil){
+ me = inputs;
+ inputs = nil;
+ }
+ else{
+ for(in = inputs; in.next.next != nil;)
+ in = in.next;
+ me = in.next;
+ in.next = nil;
+ }
+ infile = me.file;
+ mkinline = me.line;
+ me = nil;
+}
+
+#
+# lex
+#
+
+#
+# * Assemble a line skipping blank lines, comments, and eliding
+# * escaped newlines
+#
+assline(bp: ref Iobuf, buf: ref Bufblock): int
+{
+ c, lastc: int;
+
+ buf.current = 0;
+ while((c = nextrune(bp, 1)) >= 0){
+ case(c){
+ '\r' => # consumes CRs for Win95
+ continue;
+ '\n' =>
+ if(buf.current != 0){
+ insert(buf, 0);
+ return 1;
+ }
+ # skip empty lines
+ '\\' or '\'' or '"' =>
+ rinsert(buf, c);
+ if(escapetoken(bp, buf, 1, c) == 0)
+ Exit();
+ '`' =>
+ if(bquote(bp, buf) == 0)
+ Exit();
+ '#' =>
+ lastc = '#';
+ while((c = bp.getb()) != '\n'){
+ if(c < 0){
+ insert(buf, 0);
+ return buf.start[0] != byte 0;
+ }
+ if(c != '\r')
+ lastc = c;
+ }
+ mkinline++;
+ if(lastc == '\\')
+ break; # propagate escaped newlines??
+ if(buf.current != 0){
+ insert(buf, 0);
+ return 1;
+ }
+ * =>
+ rinsert(buf, c);
+ }
+ }
+ insert(buf, 0);
+ return buf.start[0] != byte 0;
+}
+
+#
+# * assemble a back-quoted shell command into a buffer
+#
+bquote(bp: ref Iobuf, buf: ref Bufblock): int
+{
+ c, line, term, start: int;
+
+ line = mkinline;
+ while((c = bp.getc()) == ' ' || c == '\t')
+ ;
+ if(c == '{'){
+ term = '}'; # rc style
+ while((c = bp.getc()) == ' ' || c == '\t')
+ ;
+ }
+ else
+ term = '`'; # sh style
+ start = buf.current;
+ for(; c > 0; c = nextrune(bp, 0)){
+ if(c == term){
+ insert(buf, '\n');
+ insert(buf, 0);
+ buf.current = start;
+ execinit();
+ execsh(nil, buf.start[buf.current: ], buf, envy);
+ return 1;
+ }
+ if(c == '\n')
+ break;
+ if(c == '\'' || c == '"' || c == '\\'){
+ insert(buf, c);
+ if(!escapetoken(bp, buf, 1, c))
+ return 0;
+ continue;
+ }
+ rinsert(buf, c);
+ }
+ if(line >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), line);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing closing %c after `\n", term);
+ return 0;
+}
+
+#
+# * get next character stripping escaped newlines
+# * the flag specifies whether escaped newlines are to be elided or
+# * replaced with a blank.
+#
+savec: int;
+
+nextrune(bp: ref Iobuf, elide: int): int
+{
+ c, c2: int;
+
+ if(savec){
+ c = savec;
+ savec = 0;
+ return c;
+ }
+ for(;;){
+ c = bp.getc();
+ if(c == '\\'){
+ c2 = bp.getc();
+ if(c2 == '\r'){
+ savec = c2;
+ c2 = bp.getc();
+ }
+ if(c2 == '\n'){
+ savec = 0;
+ mkinline++;
+ if(elide)
+ continue;
+ return ' ';
+ }
+ bp.ungetc();
+ }
+ if(c == '\n')
+ mkinline++;
+ return c;
+ }
+ return 0;
+}
+
+#
+# symtab
+#
+
+NHASH: con 4099;
+HASHMUL: con 79;
+
+hash := array[NHASH] of ref Symtab;
+
+syminit()
+{
+ s: ref Symtab;
+ ss, ns: ref Symtab;
+
+ for(i := 0; i < NHASH; i++){
+ s = hash[i];
+ for(ss = s; ss != nil; ss = ns){
+ ns = s.next;
+ ss = nil;
+ }
+ hash[i] = nil;
+ }
+}
+
+symval(sym: ref Symtab): int
+{
+ return sym.svalue != nil ||
+ sym.ivalue != 0 ||
+ sym.nvalue != nil ||
+ sym.rvalue != nil ||
+ sym.wvalue != nil;
+}
+
+symlooks(sym: array of byte, space: int, s: array of byte): ref Symtab
+{
+ return symlook(sym, space, s != nil, s, 0, nil, nil, nil);
+}
+
+symlooki(sym: array of byte, space: int, i: int): ref Symtab
+{
+ return symlook(sym, space, i != 0, nil, i, nil, nil, nil);
+}
+
+symlookn(sym: array of byte, space: int, n: ref Node): ref Symtab
+{
+ return symlook(sym, space, n != nil, nil, 0, n, nil, nil);
+}
+
+symlookr(sym: array of byte, space: int, r: ref Rule): ref Symtab
+{
+ return symlook(sym, space, r != nil, nil, 0, nil, r, nil);
+}
+
+symlookw(sym: array of byte, space: int, w: ref Word): ref Symtab
+{
+ return symlook(sym, space, w != nil, nil, 0, nil, nil, w);
+}
+
+symlook(sym: array of byte, space: int, install: int, sv: array of byte, iv: int, nv: ref Node, rv: ref Rule, wv: ref Word): ref Symtab
+{
+ h: int;
+ p: array of byte;
+ s: ref Symtab;
+
+ for((p, h) = (sym, space); int p[0]; ){
+ h *= HASHMUL;
+ h += int p[0];
+ p = p[1: ];
+ }
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for(s = hash[h]; s != nil; s = s.next)
+ if(s.space == space && libc0->strcmp(s.name, sym) == 0)
+ return s;
+ if(install == 0)
+ return nil;
+ s = ref Symtab;
+ s.space = space;
+ s.name = sym;
+ s.svalue = sv;
+ s.ivalue = iv;
+ s.nvalue = nv;
+ s.rvalue = rv;
+ s.wvalue = wv;
+ s.next = hash[h];
+ hash[h] = s;
+ return s;
+}
+
+symdel(sym: array of byte, space: int)
+{
+ h: int;
+ p: array of byte;
+ s, ls: ref Symtab;
+
+ # multiple memory leaks
+ for((p, h) = (sym, space); int p[0]; ){
+ h *= HASHMUL;
+ h += int p[0];
+ p = p[1: ];
+ }
+ if(h < 0)
+ h = ~h;
+ h %= NHASH;
+ for((s, ls) = (hash[h], nil); s != nil; (ls, s) = (s, s.next))
+ if(s.space == space && libc0->strcmp(s.name, sym) == 0){
+ if(ls != nil)
+ ls.next = s.next;
+ else
+ hash[h] = s.next;
+ s = nil;
+ }
+}
+
+symtraverse(space: int, fnx: int)
+{
+ s: ref Symtab;
+ ss: ref Symtab;
+
+ for(i := 0; i < NHASH; i++){
+ s = hash[i];
+ for(ss = s; ss != nil; ss = ss.next)
+ if(ss.space == space){
+ if(fnx == ECOPY)
+ ecopy(ss);
+ else if(fnx == PRINT1)
+ print1(ss);
+ }
+ }
+}
+
+symstat()
+{
+ s: ref Symtab;
+ ss: ref Symtab;
+ n: int;
+ l := array[1000] of int;
+
+ for(i := 0; i < 1000; i++)
+ l[i] = 0;
+ for(i = 0; i < NHASH; i++){
+ s = hash[i];
+ for((ss, n) = (s, 0); ss != nil; ss = ss.next)
+ n++;
+ l[n]++;
+ }
+ for(n = 0; n < 1000; n++)
+ if(l[n])
+ bout.puts(sys->sprint("%d of length %d\n", l[n], n));
+}
+
+#
+# varsub
+#
+
+varsub(s: array of byte): (ref Word, array of byte)
+{
+ b: ref Bufblock;
+ w: ref Word;
+
+ if(s[0] == byte '{') # either ${name} or ${name: A%B==C%D}
+ return expandvar(s);
+ (b, s) = varname(s);
+ if(b == nil)
+ return (nil, s);
+ (w, s) = varmatch(b.start, s);
+ freebuf(b);
+ return (w, s);
+}
+
+#
+# * extract a variable name
+#
+varname(s: array of byte): (ref Bufblock, array of byte)
+{
+ b: ref Bufblock;
+ cp: array of byte;
+ r: int;
+ n: int;
+
+ b = newbuf();
+ cp = s;
+ for(;;){
+ (r, n, nil) = sys->byte2char(cp, 0);
+ if(!(r > ' ' && libc0->strchr(libc0->s2ab("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"), r) == nil))
+ break;
+ rinsert(b, r);
+ cp = cp[n: ];
+ }
+ if(b.current == 0){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing variable name <%s>\n", libc0->ab2s(s));
+ freebuf(b);
+ return (nil, s);
+ }
+ s = cp;
+ insert(b, 0);
+ return (b, s);
+}
+
+varmatch(name: array of byte, s: array of byte): (ref Word, array of byte)
+{
+ w: ref Word;
+ sym: ref Symtab;
+ cp: array of byte;
+
+ sym = symlooki(name, S_VAR, 0);
+ if(sym != nil){
+ # check for at least one non-NULL value
+ for(w = sym.wvalue; w != nil; w = w.next)
+ if(w.s != nil && int w.s[0])
+ return (wdup(w), s);
+ }
+ for(cp = s; cp[0] == byte ' ' || cp[0] == byte '\t'; cp = cp[1: ]) # skip trailing whitespace
+ ;
+ s = cp;
+ return (nil, s);
+}
+
+expandvar(s: array of byte): (ref Word, array of byte)
+{
+ w: ref Word;
+ buf: ref Bufblock;
+ sym: ref Symtab;
+ cp, begin, end: array of byte;
+
+ begin = s;
+ s = s[1: ]; # skip the '{'
+ (buf, s) = varname(s);
+ if(buf == nil)
+ return (nil, s);
+ cp = s;
+ if(cp[0] == byte '}'){ # ${name} variant
+ s[0]++; # skip the '}'
+ (w, s) = varmatch(buf.start, s);
+ freebuf(buf);
+ return (w, s);
+ }
+ if(cp[0] != byte ':'){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "bad variable name <%s>\n", libc0->ab2s(buf.start));
+ freebuf(buf);
+ return (nil, s);
+ }
+ cp = cp[1: ];
+ end = charin(cp, libc0->s2ab("}"));
+ if(end == nil){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing '}': %s\n", libc0->ab2s(begin));
+ Exit();
+ }
+ end[0] = byte 0;
+ s = end[1: ];
+ sym = symlooki(buf.start, S_VAR, 0);
+ if(sym == nil || !symval(sym))
+ w = newword(buf.start);
+ else
+ w = subsub(sym.wvalue, cp, end);
+ freebuf(buf);
+ return (w, s);
+}
+
+extractpat(s: array of byte, r: array of byte, term: array of byte, end: array of byte): (ref Word, array of byte)
+{
+ save: int;
+ cp: array of byte;
+ w: ref Word;
+
+ cp = charin(s, term);
+ if(cp != nil){
+ r = cp;
+ if(cp == s)
+ return (nil, r);
+ save = int cp[0];
+ cp[0] = byte 0;
+ w = stow(s);
+ cp[0] = byte save;
+ }
+ else{
+ r = end;
+ w = stow(s);
+ }
+ return (w, r);
+}
+
+subsub(v: ref Word, s: array of byte, end: array of byte): ref Word
+{
+ nmid, ok: int;
+ head, tail, w, h, a, b, c, d: ref Word;
+ buf: ref Bufblock;
+ cp, enda: array of byte;
+
+ (a, cp) = extractpat(s, cp, libc0->s2ab("=%&"), end);
+ b = c = d = nil;
+ if(cp[0] == byte '%' || cp[0] == byte '&')
+ (b, cp) = extractpat(cp[1: ], cp, libc0->s2ab("="), end);
+ if(cp[0] == byte '=')
+ (c, cp) = extractpat(cp[1: ], cp, libc0->s2ab("&%"), end);
+ if(cp[0] == byte '%' || cp[0] == byte '&')
+ d = stow(cp[1: ]);
+ else if(int cp[0])
+ d = stow(cp);
+ head = tail = nil;
+ buf = newbuf();
+ for(; v != nil; v = v.next){
+ h = w = nil;
+ (ok, nmid, enda) = submatch(v.s, a, b, nmid, enda);
+ if(ok){
+ # enda points to end of A match in source;
+ # * nmid = number of chars between end of A and start of B
+ #
+ if(c != nil){
+ h = w = wdup(c);
+ while(w.next != nil)
+ w = w.next;
+ }
+ if((cp[0] == byte '%' || cp[0] == byte '&') && nmid > 0){
+ if(w != nil){
+ bufcpy(buf, w.s, libc0->strlen(w.s));
+ bufcpy(buf, enda, nmid);
+ insert(buf, 0);
+ w.s = nil;
+ w.s = libc0->strdup(buf.start);
+ }
+ else{
+ bufcpy(buf, enda, nmid);
+ insert(buf, 0);
+ h = w = newword(buf.start);
+ }
+ buf.current = 0;
+ }
+ if(d != nil && int d.s[0]){
+ if(w != nil){
+ bufcpy(buf, w.s, libc0->strlen(w.s));
+ bufcpy(buf, d.s, libc0->strlen(d.s));
+ insert(buf, 0);
+ w.s = nil;
+ w.s = libc0->strdup(buf.start);
+ w.next = wdup(d.next);
+ while(w.next != nil)
+ w = w.next;
+ buf.current = 0;
+ }
+ else
+ h = w = wdup(d);
+ }
+ }
+ if(w == nil)
+ h = w = newword(v.s);
+ if(head == nil)
+ head = h;
+ else
+ tail.next = h;
+ tail = w;
+ }
+ freebuf(buf);
+ delword(a);
+ delword(b);
+ delword(c);
+ delword(d);
+ return head;
+}
+
+submatch(s: array of byte, a: ref Word, b: ref Word, nmid: int, enda: array of byte): (int, int, array of byte)
+{
+ w: ref Word;
+ n: int;
+ end: array of byte;
+
+ n = 0;
+ for(w = a; w != nil; w = w.next){
+ n = libc0->strlen(w.s);
+ if(libc0->strncmp(s, w.s, n) == 0)
+ break;
+ }
+ if(a != nil && w == nil) # a == NULL matches everything
+ return (0, nmid, enda);
+ enda = s[n: ]; # pointer to end a A part match
+ nmid = libc0->strlen(s)-n; # size of remainder of source
+ end = enda[nmid: ];
+ onmid := nmid;
+ for(w = b; w != nil; w = w.next){
+ n = libc0->strlen(w.s);
+ if(libc0->strcmp(w.s, enda[onmid-n: ]) == 0){ # end-n
+ nmid -= n;
+ break;
+ }
+ }
+ if(b != nil && w == nil) # b == NULL matches everything
+ return (0, nmid, enda);
+ return (1, nmid, enda);
+}
+
+#
+# var
+#
+
+setvar(name: array of byte, value: ref Word)
+{
+ # s := libc0->ab2s(name);
+ # if(s == "ROOT" || s == "OBJTYPE"){
+ # if(s[0] == 'R')
+ # v := "";
+ # else
+ # v = "386";
+ # value.s = libc0->strdup(libc0->s2ab(v));
+ # }
+
+ symlookw(name, S_VAR, value).wvalue = value;
+ symlooks(name, S_MAKEVAR, libc0->s2ab(""));
+}
+
+print1(s: ref Symtab)
+{
+ w: ref Word;
+
+ bout.puts(sys->sprint("\t%s=", libc0->ab2s(s.name)));
+ for(w = s.wvalue; w != nil; w = w.next)
+ bout.puts(sys->sprint("'%s'", libc0->ab2s(w.s)));
+ bout.puts(sys->sprint("\n"));
+}
+
+dumpv(s: array of byte)
+{
+ bout.puts(sys->sprint("%s:\n", libc0->ab2s(s)));
+ symtraverse(S_VAR, PRINT1);
+}
+
+shname(a: array of byte): array of byte
+{
+ r: int;
+ n: int;
+
+ while(int a[0]){
+ (r, n, nil) = sys->byte2char(a, 0);
+ if(!(r > ' ' && libc0->strchr(libc0->s2ab("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"), r) == nil))
+ break;
+ a = a[n: ];
+ }
+ return a;
+}
+
+#
+# word
+#
+
+
+newword(s: array of byte): ref Word
+{
+ w: ref Word;
+
+ w = ref Word;
+ w.s = libc0->strdup(s);
+ w.next = nil;
+ return w;
+}
+
+stow(s: array of byte): ref Word
+{
+ head, w, new: ref Word;
+
+ w = head = nil;
+ while(int s[0]){
+ (new, s) = nextword(s);
+ if(new == nil)
+ break;
+ if(w != nil)
+ w.next = new;
+ else
+ head = w = new;
+ while(w.next != nil)
+ w = w.next;
+ }
+ if(head == nil)
+ head = newword(libc0->s2ab(""));
+ return head;
+}
+
+wtos(w: ref Word, sep: int): array of byte
+{
+ buf: ref Bufblock;
+ cp: array of byte;
+
+ buf = newbuf();
+ for(; w != nil; w = w.next){
+ for(cp = w.s; int cp[0]; cp = cp[1: ])
+ insert(buf, int cp[0]);
+ if(w.next != nil)
+ insert(buf, sep);
+ }
+ insert(buf, 0);
+ cp = libc0->strdup(buf.start);
+ freebuf(buf);
+ return cp;
+}
+
+wtostr(w: ref Word, sep: int): string
+{
+ return libc0->ab2s(wtos(w, sep));
+}
+
+wdup(w: ref Word): ref Word
+{
+ v, new, base: ref Word;
+
+ v = base = nil;
+ while(w != nil){
+ new = newword(w.s);
+ if(v != nil)
+ v.next = new;
+ else
+ base = new;
+ v = new;
+ w = w.next;
+ }
+ return base;
+}
+
+delword(w: ref Word)
+{
+ v: ref Word;
+
+ while((v = w) != nil){
+ w = w.next;
+ if(v.s != nil)
+ v.s = nil;
+ v = nil;
+ }
+}
+
+#
+# * break out a word from a string handling quotes, executions,
+# * and variable expansions.
+#
+nextword(s: array of byte): (ref Word, array of byte)
+{
+ b: ref Bufblock;
+ head, tail, w: ref Word;
+ r, n: int;
+ cp: array of byte;
+
+ cp = s;
+ b = newbuf();
+ head = tail = nil;
+ while(cp[0] == byte ' ' || cp[0] == byte '\t') # leading white space
+ cp = cp[1: ];
+ loop := 1;
+ while(loop && int cp[0]){
+ (r, n, nil) = sys->byte2char(cp, 0);
+ cp = cp[n: ];
+ case(r){
+ ' ' or '\t' or '\n' =>
+ loop = 0;
+ '\\' or '\'' or '"' =>
+ cp = expandquote(cp, r, b);
+ if(cp == nil){
+ sys->fprint(sys->fildes(2), "missing closing quote: %s\n", libc0->ab2s(s));
+ Exit();
+ }
+ '$' =>
+ (w, cp) = varsub(cp);
+ if(w == nil)
+ break;
+ if(b.current != 0){
+ bufcpy(b, w.s, libc0->strlen(w.s));
+ insert(b, 0);
+ w.s = nil;
+ w.s = libc0->strdup(b.start);
+ b.current = 0;
+ }
+ if(head != nil){
+ bufcpy(b, tail.s, libc0->strlen(tail.s));
+ bufcpy(b, w.s, libc0->strlen(w.s));
+ insert(b, 0);
+ tail.s = nil;
+ tail.s = libc0->strdup(b.start);
+ tail.next = w.next;
+ w.s = nil;
+ w = nil;
+ b.current = 0;
+ }
+ else
+ tail = head = w;
+ while(tail.next != nil)
+ tail = tail.next;
+ * =>
+ rinsert(b, r);
+ }
+ }
+ s = cp;
+ if(b.current != 0){
+ if(head != nil){
+ oc := b.current;
+ cp = b.start[b.current: ];
+ bufcpy(b, tail.s, libc0->strlen(tail.s));
+ bufcpy(b, b.start, oc);
+ insert(b, 0);
+ tail.s = nil;
+ tail.s = libc0->strdup(cp);
+ }
+ else{
+ insert(b, 0);
+ head = newword(b.start);
+ }
+ }
+ freebuf(b);
+ return (head, s);
+}
+
+dumpw(s: array of byte, w: ref Word)
+{
+ bout.puts(sys->sprint("%s", libc0->ab2s(s)));
+ for(; w != nil; w = w.next)
+ bout.puts(sys->sprint(" '%s'", libc0->ab2s(w.s)));
+ bout.putb(byte '\n');
+}
+
+#
+# match
+#
+
+match(name: array of byte, template: array of byte, stem: array of byte): int
+{
+ r: int;
+ n: int;
+
+ while(int name[0] && int template[0]){
+ (r, n, nil) = sys->byte2char(template, 0);
+ if(r == '%' || r == '&')
+ break;
+ while(n--)
+ if(name[0] != template[0])
+ return 0;
+ name = name[1: ];
+ template = template[1: ];
+ }
+ if(!(template[0] == byte '%' || template[0] == byte '&'))
+ return 0;
+ n = libc0->strlen(name)-libc0->strlen(template[1: ]);
+ if(n < 0 || libc0->strcmp(template[1: ], name[n: ]))
+ return 0;
+ libc0->strncpy(stem, name, n);
+ stem[n] = byte 0;
+ if(template[0] == byte '&')
+ return charin(stem, libc0->s2ab("./")) == nil;
+ return 1;
+}
+
+subst(stem: array of byte, template: array of byte, dest: array of byte)
+{
+ r: int;
+ s: array of byte;
+ n: int;
+
+ while(int template[0]){
+ (r, n, nil) = sys->byte2char(template, 0);
+ if(r == '%' || r == '&'){
+ template = template[n: ];
+ for(s = stem; int s[0]; s = s[1: ]){
+ dest[0] = s[0];
+ dest = dest[1: ];
+ }
+ }
+ else
+ while(n--){
+ dest[0] = template[0];
+ dest = dest[1: ];
+ template = template[1: ];
+ }
+ }
+ dest[0] = byte 0;
+}
+
+#
+# os
+#
+
+shell := "/dis/sh.dis";
+shellname := "sh";
+
+pcopy(a: array of ref Sys->FD): array of ref Sys->FD
+{
+ b := array[2] of ref Sys->FD;
+ b[0: ] = a[0: 2];
+ return b;
+}
+
+readenv()
+{
+ p: array of byte;
+ envf, f: ref Sys->FD;
+ e := array[20] of Sys->Dir;
+ nam := array[NAMELEN+5] of byte;
+ i, n, lenx: int;
+ w: ref Word;
+
+ sys->pctl(Sys->FORKENV, nil); # use copy of the current environment variables
+ envf = sys->open("/env", Sys->OREAD);
+ if(envf == nil)
+ return;
+ for(;;){
+ (n, e) = sys->dirread(envf);
+ if(n <= 0)
+ break;
+ for(i = 0; i < n; i++){
+ lenx = int e[i].length;
+ # don't import funny names, NULL values,
+ # * or internal mk variables
+ #
+ if(lenx <= 0 || shname(libc0->s2ab(e[i].name))[0] != byte '\0')
+ continue;
+ if(symlooki(libc0->s2ab(e[i].name), S_INTERNAL, 0) != nil)
+ continue;
+ stob(nam, sys->sprint("/env/%s", e[i].name));
+ f = sys->open(libc0->ab2s(nam), Sys->OREAD);
+ if(f == nil)
+ continue;
+ p = array[lenx+1] of byte;
+ if(sys->read(f, p, lenx) != lenx){
+ perror(nam);
+ f = nil;
+ continue;
+ }
+ f = nil;
+ if(p[lenx-1] == byte 0)
+ lenx--;
+ else
+ p[lenx] = byte 0;
+ w = encodenulls(p, lenx);
+ p = nil;
+ p = libc0->strdup(libc0->s2ab(e[i].name));
+ setvar(p, w);
+ symlooks(p, S_EXPORTED, libc0->s2ab("")).svalue = libc0->s2ab("");
+ }
+ }
+ envf = nil;
+}
+
+# break string of values into words at 01's or nulls
+encodenulls(s: array of byte, n: int): ref Word
+{
+ w, head: ref Word;
+ cp: array of byte;
+
+ head = w = nil;
+ while(n-- > 0){
+ for(cp = s; int cp[0] && cp[0] != byte '\u0001'; cp = cp[1: ])
+ n--;
+ cp[0] = byte 0;
+ if(w != nil){
+ w.next = newword(s);
+ w = w.next;
+ }
+ else
+ head = w = newword(s);
+ s = cp[1: ];
+ }
+ if(head == nil)
+ head = newword(libc0->s2ab(""));
+ return head;
+}
+
+# as well as 01's, change blanks to nulls, so that rc will
+# * treat the words as separate arguments
+#
+exportenv(e: array of Envy)
+{
+ f: ref Sys->FD;
+ n, hasvalue: int;
+ w: ref Word;
+ sy: ref Symtab;
+ nam := array[NAMELEN+5] of byte;
+
+ for(i := 0; e[i].name != nil; i++){
+ sy = symlooki(e[i].name, S_VAR, 0);
+ if(e[i].values == nil || e[i].values.s == nil || e[i].values.s[0] == byte 0)
+ hasvalue = 0;
+ else
+ hasvalue = 1;
+ if(sy == nil && !hasvalue) # non-existant null symbol
+ continue;
+ stob(nam, sys->sprint("/env/%s", libc0->ab2s(e[i].name)));
+ if(sy != nil && !hasvalue){ # Remove from environment
+ # we could remove it from the symbol table
+ # * too, but we're in the child copy, and it
+ # * would still remain in the parent's table.
+ #
+ sys->remove(libc0->ab2s(nam));
+ delword(e[i].values);
+ e[i].values = nil; # memory leak
+ continue;
+ }
+ f = sys->create(libc0->ab2s(nam), Sys->OWRITE, 8r666);
+ if(f == nil){
+ sys->fprint(sys->fildes(2), "can't create %s, f=%d\n", libc0->ab2s(nam), f.fd);
+ perror(nam);
+ continue;
+ }
+ for(w = e[i].values; w != nil; w = w.next){
+ n = libc0->strlen(w.s);
+ if(n){
+ if(sys->write(f, w.s, n) != n)
+ perror(nam);
+ if(w.next != nil && sys->write(f, libc0->s2ab(" "), 1) != 1)
+ perror(nam);
+ }
+ }
+ f = nil;
+ }
+}
+
+dirtime(dir: array of byte, path: array of byte)
+{
+ i: int;
+ fd: ref Sys->FD;
+ n: int;
+ t: int;
+ db := array[32] of Sys->Dir;
+ buf := array[4096] of byte;
+
+ fd = sys->open(libc0->ab2s(dir), Sys->OREAD);
+ if(fd != nil){
+ for(;;){
+ (n, db) = sys->dirread(fd);
+ if(n <= 0)
+ break;
+ for(i = 0; i < n; i++){
+ t = db[i].mtime;
+ if(t == 0) # zero mode file
+ continue;
+ stob(buf, sys->sprint("%s%s", libc0->ab2s(path), db[i].name));
+ if(symlooki(buf, S_TIME, 0) != nil)
+ continue;
+ symlooki(libc0->strdup(buf), S_TIME, t).ivalue = t;
+ }
+ }
+ fd = nil;
+ }
+}
+
+waitfor(msg: array of byte): int
+{
+ wm: array of byte;
+ pid: int;
+
+ (pid, wm) = wait();
+ if(pid > 0)
+ libc0->strncpy(msg, wm, ERRLEN);
+ return pid;
+}
+
+expunge(pid: int, msg: array of byte)
+{
+ postnote(PNPROC, pid, msg);
+}
+
+sub(cmd: array of byte, env: array of Envy): array of byte
+{
+ buf := newbuf();
+ shprint(cmd, env, buf);
+ return buf.start;
+}
+
+fork1(c1: chan of int, args: array of byte, cmd: array of byte, buf: ref Bufblock, e: array of Envy, in: array of ref Sys->FD, out: array of ref Sys->FD)
+{
+ pid: int;
+
+ c1<- = sys->pctl(Sys->FORKFD|Sys->FORKENV, nil);
+
+ {
+ if(buf != nil)
+ out[0] = nil;
+ if(sys->pipe(in) < 0){
+ perrors("pipe");
+ Exit();
+ }
+ c2 := chan of int;
+ spawn fork2(c2, cmd, pcopy(in), pcopy(out));
+ pid = <- c2;
+ addwait();
+ {
+ sys->dup(in[0].fd, 0);
+ if(buf != nil){
+ sys->dup(out[1].fd, 1);
+ out[1] = nil;
+ }
+ in[0] = nil;
+ in[1] = nil;
+ if(e != nil)
+ exportenv(e);
+ argss := libc0->ab2s(args);
+ sys->pctl(Sys->NEWFD, 0 :: 1 :: 2 :: nil);
+ if(shflags != nil)
+ execl(shell, shellname, shflags, argss, nil, nil);
+ else
+ execl(shell, shellname, argss, nil, nil, nil);
+ exit;
+ # perror(shell);
+ # exits("exec");
+ }
+ }
+}
+
+fork2(c2: chan of int, cmd: array of byte, in: array of ref Sys->FD, out: array of ref Sys->FD)
+{
+ n, p: int;
+
+ c2<- = sys->pctl(Sys->FORKFD, nil);
+
+ {
+ out[1] = nil;
+ in[0] = nil;
+ p = libc0->strlen(cmd);
+ c := 0;
+ while(c < p){ # cmd < p
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "writing '%s' to shell\n", libc0->ab2s(cmd[0: p-c]));
+ n = sys->write(in[1], cmd, p-c); # p-cmd
+ if(n < 0)
+ break;
+ cmd = cmd[n: ];
+ c += n;
+ }
+ in[1] = nil;
+ exit;
+ # exits(nil);
+ }
+}
+
+execsh(args: array of byte, cmd: array of byte, buf: ref Bufblock, e: array of Envy): int
+{
+ tot, n, pid: int;
+ in := array[2] of ref Sys->FD;
+ out := array[2] of ref Sys->FD;
+
+ cmd = sub(cmd, e);
+
+ if(buf != nil && sys->pipe(out) < 0){
+ perrors("pipe");
+ Exit();
+ }
+ c1 := chan of int;
+ spawn fork1(c1, args, cmd, buf, e, in, pcopy(out));
+ pid = <-c1;
+ addwait();
+ if(buf != nil){
+ out[1] = nil;
+ tot = 0;
+ for(;;){
+ if(buf.current >= buf.end)
+ growbuf(buf);
+ n = sys->read(out[0], buf.start[buf.current: ], buf.end-buf.current);
+ if(n <= 0)
+ break;
+ buf.current += n;
+ tot += n;
+ }
+ if(tot && buf.start[buf.current-1] == byte '\n')
+ buf.current--;
+ out[0] = nil;
+ }
+ return pid;
+}
+
+fork3(c3: chan of int, cmd: array of byte, e: array of Envy, fd: array of ref Sys->FD, pfd: array of ref Sys->FD)
+{
+ c3<- = sys->pctl(Sys->FORKFD|Sys->FORKENV, nil);
+
+ {
+ if(fd != nil){
+ pfd[0] = nil;
+ sys->dup(pfd[1].fd, 1);
+ pfd[1] = nil;
+ }
+ if(e != nil)
+ exportenv(e);
+ cmds := libc0->ab2s(cmd);
+ if(shflags != nil)
+ execl(shell, shellname, shflags, "-c", cmds, nil);
+ else
+ execl(shell, shellname, "-c", cmds, nil, nil);
+ exit;
+ # perror(shell);
+ # exits("exec");
+ }
+}
+
+pipecmd(cmd: array of byte, e: array of Envy, fd: array of ref Sys->FD): int
+{
+ pid: int;
+ pfd := array[2] of ref Sys->FD;
+
+ cmd = sub(cmd, e);
+
+ if(debug&D_EXEC)
+ sys->fprint(sys->fildes(1), "pipecmd='%s'", libc0->ab2s(cmd)); #
+ if(fd != nil && sys->pipe(pfd) < 0){
+ perrors("pipe");
+ Exit();
+ }
+ c3 := chan of int;
+ spawn fork3(c3, cmd, e, fd, pcopy(pfd));
+ pid = <- c3;
+ addwait();
+ if(fd != nil){
+ pfd[1] = nil;
+ fd[0] = pfd[0];
+ }
+ return pid;
+}
+
+Exit()
+{
+ while(wait().t0 >= 0)
+ ;
+ bout.flush();
+ exit;
+}
+
+nnote: int;
+
+notifyf(a: array of byte, msg: array of byte): int
+{
+ if(a != nil)
+ ;
+ if(++nnote > 100){ # until andrew fixes his program
+ sys->fprint(sys->fildes(2), "mk: too many notes\n");
+ # notify(nil);
+ abort();
+ }
+ if(libc0->strcmp(msg, libc0->s2ab("interrupt")) != 0 && libc0->strcmp(msg, libc0->s2ab("hangup")) != 0)
+ return 0;
+ killchildren(msg);
+ return -1;
+}
+
+catchnotes()
+{
+ # atnotify(notifyf, 1);
+}
+
+temp := array[] of { byte '/', byte 't', byte 'm', byte 'p', byte '/', byte 'm', byte 'k', byte 'a', byte 'r', byte 'g', byte 'X', byte 'X', byte 'X', byte 'X', byte 'X', byte 'X', byte '\0' };
+
+maketmp(): array of byte
+{
+ t := libc0->strdup(temp);
+ mktemp(t);
+ return t;
+}
+
+chgtime(name: array of byte): int
+{
+ (ok, nil) := sys->stat(libc0->ab2s(name));
+ if(ok >= 0){
+ sbuf := sys->nulldir;
+ sbuf.mtime = daytime->now();
+ return sys->wstat(libc0->ab2s(name), sbuf);
+ }
+ fd := sys->create(libc0->ab2s(name), Sys->OWRITE, 8r666);
+ if(fd == nil)
+ return -1;
+ fd = nil;
+ return 0;
+}
+
+rcopy(tox: array of array of byte, match: array of Resub, n: int)
+{
+ c: int;
+ p: array of byte;
+
+ i := 0;
+ tox[0] = match[0].sp; # stem0 matches complete target
+ for(i++; --n > 0; i++){
+ if(match[i].sp != nil && match[i].ep != nil){
+ p = match[i].ep;
+ c = int p[0];
+ p[0] = byte 0;
+ tox[i] = libc0->strdup(match[i].sp);
+ p[0] = byte c;
+ }
+ else
+ tox[i] = nil;
+ }
+}
+
+mkdirstat(name: array of byte): (int, Sys->Dir)
+{
+ return sys->stat(libc0->ab2s(name));
+}
+
+membername(s: array of byte, fd: ref Sys->FD, sz: int): array of byte
+{
+ if(fd == nil)
+ ;
+ if(sz)
+ ;
+ return s;
+}
+
+#
+# sh
+#
+
+termchars := array[] of { byte '\'', byte '=', byte ' ', byte '\t', byte '\0' }; # used in parse.c to isolate assignment attribute
+shflags := ""; # rc flag to force non-interactive mode - was -l
+IWS: int = '\u0001'; # inter-word separator in env - not used in plan 9
+
+#
+# * This file contains functions that depend on rc's syntax. Most
+# * of the routines extract strings observing rc's escape conventions
+#
+#
+# * skip a token in single quotes.
+#
+squote(cp: array of byte): array of byte
+{
+ r: int;
+ n, nn: int;
+
+ while(int cp[0]){
+ (r, n, nil) = sys->byte2char(cp, 0);
+ if(r == '\''){
+ (r, nn, nil) = sys->byte2char(cp[n: ], 0);
+ n += nn;
+ if(r != '\'')
+ return cp;
+ }
+ cp = cp[n: ];
+ }
+ if(-1 >= 0) # should never occur
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing closing '\n");
+ return nil;
+}
+
+#
+# * search a string for characters in a pattern set
+# * characters in quotes and variable generators are escaped
+#
+charin(cp: array of byte, pat: array of byte): array of byte
+{
+ r: int;
+ n, vargen: int;
+
+ vargen = 0;
+ while(int cp[0]){
+ (r, n, nil) = sys->byte2char(cp, 0);
+ case(r){
+ '\'' => # skip quoted string
+ cp = squote(cp[1: ]); # n must = 1
+ if(cp == nil)
+ return nil;
+ '$' =>
+ if((cp[1: ])[0] == byte '{')
+ vargen = 1;
+ '}' =>
+ if(vargen)
+ vargen = 0;
+ else if(libc0->strchr(pat, r) != nil)
+ return cp;
+ * =>
+ if(vargen == 0 && libc0->strchr(pat, r) != nil)
+ return cp;
+ }
+ cp = cp[n: ];
+ }
+ if(vargen){
+ if(-1 >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), -1);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing closing } in pattern generator\n");
+ }
+ return nil;
+}
+
+#
+# * extract an escaped token. Possible escape chars are single-quote,
+# * double-quote,and backslash. Only the first is valid for rc. the
+# * others are just inserted into the receiving buffer.
+#
+expandquote(s: array of byte, r: int, b: ref Bufblock): array of byte
+{
+ n: int;
+
+ if(r != '\''){
+ rinsert(b, r);
+ return s;
+ }
+ while(int s[0]){
+ (r, n, nil) = sys->byte2char(s, 0);
+ s = s[n: ];
+ if(r == '\''){
+ if(s[0] == byte '\'')
+ s = s[1: ];
+ else
+ return s;
+ }
+ rinsert(b, r);
+ }
+ return nil;
+}
+
+#
+# * Input an escaped token. Possible escape chars are single-quote,
+# * double-quote and backslash. Only the first is a valid escape for
+# * rc; the others are just inserted into the receiving buffer.
+#
+escapetoken(bp: ref Iobuf, buf: ref Bufblock, preserve: int, esc: int): int
+{
+ c, line: int;
+
+ if(esc != '\'')
+ return 1;
+ line = mkinline;
+ while((c = nextrune(bp, 0)) > 0){
+ if(c == '\''){
+ if(preserve)
+ rinsert(buf, c);
+ c = bp.getc();
+ if(c < 0)
+ break;
+ if(c != '\''){
+ bp.ungetc();
+ return 1;
+ }
+ }
+ rinsert(buf, c);
+ }
+ if(line >= 0)
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), line);
+ else
+ sys->fprint(sys->fildes(2), "mk: %s:%d: syntax error; ", libc0->ab2s(infile), mkinline);
+ sys->fprint(sys->fildes(2), "missing closing %c\n", esc);
+ return 0;
+}
+
+#
+# * copy a single-quoted string; s points to char after opening quote
+#
+copysingle(s: array of byte, buf: ref Bufblock): array of byte
+{
+ r, n: int;
+
+ while(int s[0]){
+ (r, n, nil) = sys->byte2char(s, 0);
+ s = s[n: ];
+ rinsert(buf, r);
+ if(r == '\'')
+ break;
+ }
+ return s;
+}
+
+#
+# * check for quoted strings. backquotes are handled here; single quotes above.
+# * s points to char after opening quote, q.
+#
+copyq(s: array of byte, q: int, buf: ref Bufblock): array of byte
+{
+ n: int;
+
+ if(q == '\'') # copy quoted string
+ return copysingle(s, buf);
+ if(q != '`') # not quoted
+ return s;
+ while(int s[0]){ # copy backquoted string
+ (q, n, nil) = sys->byte2char(s, 0);
+ s = s[n: ];
+ rinsert(buf, q);
+ if(q == '}')
+ break;
+ if(q == '\'')
+ s = copysingle(s, buf); # copy quoted string
+ }
+ return s;
+}
+
+#
+# shprint
+#
+
+shprint(s: array of byte, env: array of Envy, buf: ref Bufblock)
+{
+ n: int;
+ r: int;
+
+ while(int s[0]){
+ (r, n, nil) = sys->byte2char(s, 0);
+ if(r == '$')
+ s = vexpand(s, env, buf);
+ else{
+ rinsert(buf, r);
+ s = s[n: ];
+ s = copyq(s, r, buf); # handle quoted strings
+ }
+ }
+ insert(buf, 0);
+}
+
+mygetenv(name: array of byte, env: array of Envy): array of byte
+{
+ if(env == nil)
+ return nil;
+ if(symlooki(name, S_WESET, 0) == nil && symlooki(name, S_INTERNAL, 0) == nil)
+ return nil;
+ # only resolve internal variables and variables we've set
+ for(e := 0; env[e].name != nil; e++){
+ if(libc0->strcmp(env[e].name, name) == 0)
+ return wtos(env[e].values, ' ');
+ }
+ return nil;
+}
+
+vexpand(w: array of byte, env: array of Envy, buf: ref Bufblock): array of byte
+{
+ s: array of byte;
+ carry: byte;
+ p, q: array of byte;
+
+ assert(libc0->s2ab("vexpand no $"), w[0] == byte '$');
+ p = w[1: ]; # skip dollar sign
+ if(p[0] == byte '{'){
+ p = p[1: ];
+ q = libc0->strchr(p, '}');
+ if(q == nil)
+ q = libc0->strchr(p, 0);
+ }
+ else
+ q = shname(p);
+ carry = q[0];
+ q[0] = byte 0;
+ s = mygetenv(p, env);
+ q[0] = carry;
+ if(carry == byte '}')
+ q = q[1: ];
+ if(s != nil){
+ bufcpy(buf, s, libc0->strlen(s));
+ s = nil;
+ }
+ else
+ # copy name intact
+ bufcpy(buf, w, libc0->strlen(w)-libc0->strlen(q)); # q-w
+ return q;
+}
+
+front(s: array of byte)
+{
+ t, q: array of byte;
+ i, j: int;
+ # flds := array[512] of array of byte;
+ fields: list of string;
+
+ q = libc0->strdup(s);
+ (i, fields) = sys->tokenize(libc0->ab2s(q), " \t\n");
+ flds := array[len fields] of array of byte;
+ for(j = 0; j < len flds; j++){
+ flds[j] = libc0->s2ab(hd fields);
+ fields = tl fields;
+ }
+ if(i > 5){
+ flds[4] = flds[i-1];
+ flds[3] = libc0->s2ab("...");
+ i = 5;
+ }
+ t = s;
+ for(j = 0; j < i; j++){
+ for(s = flds[j]; int s[0]; ){
+ t[0] = s[0];
+ s = s[1: ];
+ t = t[1: ];
+ }
+ t[0] = byte ' ';
+ t = t[1: ];
+ }
+ t[0] = byte 0;
+ q = nil;
+}
+
+#
+# env
+#
+
+ENVQUANTA: con 10;
+
+envy: array of Envy;
+nextv: int;
+myenv: array of array of byte;
+
+initenv()
+{
+ p: int;
+
+ myenv = array[19] of {
+ libc0->s2ab("target"),
+ libc0->s2ab("stem"),
+ libc0->s2ab("prereq"),
+ libc0->s2ab("pid"),
+ libc0->s2ab("nproc"),
+ libc0->s2ab("newprereq"),
+ libc0->s2ab("alltarget"),
+ libc0->s2ab("newmember"),
+ libc0->s2ab("stem0"), # must be in order from here
+ libc0->s2ab("stem1"),
+ libc0->s2ab("stem2"),
+ libc0->s2ab("stem3"),
+ libc0->s2ab("stem4"),
+ libc0->s2ab("stem5"),
+ libc0->s2ab("stem6"),
+ libc0->s2ab("stem7"),
+ libc0->s2ab("stem8"),
+ libc0->s2ab("stem9"),
+ array of byte nil,
+ };
+
+ for(p = 0; myenv[p] != nil; p++)
+ symlooks(myenv[p], S_INTERNAL, libc0->s2ab(""));
+ readenv(); # o.s. dependent
+}
+
+envsize: int;
+
+envinsert(name: array of byte, value: ref Word)
+{
+ if(nextv >= envsize){
+ envsize += ENVQUANTA;
+ es := len envy;
+ ne := array[envsize] of Envy;
+ if(es)
+ ne[0: ] = envy[0: es];
+ envy = ne;
+ }
+ envy[nextv].name = name;
+ envy[nextv++].values = value;
+}
+
+envupd(name: array of byte, value: ref Word)
+{
+ e: int;
+
+ for(e = 0; envy[e].name != nil; e++)
+ if(libc0->strcmp(name, envy[e].name) == 0){
+ delword(envy[e].values);
+ envy[e].values = value;
+ return;
+ }
+ envy[e].name = name;
+ envy[e].values = value;
+ envinsert(nil, nil);
+}
+
+ecopy(s: ref Symtab)
+{
+ p: int;
+
+ if(symlooki(s.name, S_NOEXPORT, 0) != nil)
+ return;
+ for(p = 0; myenv[p] != nil; p++)
+ if(libc0->strcmp(myenv[p], s.name) == 0)
+ return;
+ envinsert(s.name, s.wvalue);
+}
+
+execinit()
+{
+ p: int;
+
+ nextv = 0;
+ for(p = 0; myenv[p] != nil; p++)
+ envinsert(myenv[p], stow(libc0->s2ab("")));
+ symtraverse(S_VAR, ECOPY);
+ envinsert(nil, nil);
+}
+
+buildenv(j: ref Job, slot: int): array of Envy
+{
+ p: int;
+ cp, qp: array of byte;
+ w, v: ref Word;
+ l: ref Word;
+ i: int;
+ buf := array[256] of byte;
+
+ envupd(libc0->s2ab("target"), wdup(j.t));
+ if(j.r.attr&REGEXP)
+ envupd(libc0->s2ab("stem"), newword(libc0->s2ab("")));
+ else
+ envupd(libc0->s2ab("stem"), newword(j.stem));
+ envupd(libc0->s2ab("prereq"), wdup(j.p));
+ stob(buf, sys->sprint("%d", sys->pctl(0, nil)));
+ envupd(libc0->s2ab("pid"), newword(buf));
+ stob(buf, sys->sprint("%d", slot));
+ envupd(libc0->s2ab("nproc"), newword(buf));
+ envupd(libc0->s2ab("newprereq"), wdup(j.np));
+ envupd(libc0->s2ab("alltarget"), wdup(j.at));
+ l = ref Word;
+ l.next = v = w = wdup(j.np);
+ while(w != nil){
+ cp = libc0->strchr(w.s, '(');
+ if(cp != nil){
+ cp = cp[1: ];
+ qp = libc0->strchr(cp, ')');
+ if(qp != nil){
+ qp[0] = byte 0;
+ libc0->strcpy(w.s, cp);
+ l.next = w;
+ l = w;
+ w = w.next;
+ continue;
+ }
+ }
+ l.next = w.next;
+ w.s = nil;
+ w = nil;
+ w = l.next;
+ }
+ v = l.next;
+ envupd(libc0->s2ab("newmember"), v);
+ # update stem0 -> stem9
+ for(p = 0; myenv[p] != nil; p++)
+ if(libc0->strcmp(myenv[p], libc0->s2ab("stem0")) == 0)
+ break;
+ for(i = 0; myenv[p] != nil; i++){
+ if(j.r.attr&REGEXP && j.match[i] != nil)
+ envupd(myenv[p], newword(j.match[i]));
+ else
+ envupd(myenv[p], newword(libc0->s2ab("")));
+ p++;
+ }
+ return envy;
+}
+
+#
+# dir
+#
+
+bulkmtime(dir: array of byte)
+{
+ buf := array[4096] of byte;
+ ss, s: array of byte;
+ db: Sys->Dir;
+ ok: int;
+
+ if(dir != nil){
+ s = dir;
+ if(libc0->strcmp(dir, libc0->s2ab("/")) == 0)
+ libc0->strcpy(buf, dir);
+ else
+ stob(buf, sys->sprint("%s/", libc0->ab2s(dir)));
+ (ok, db) = mkdirstat(dir);
+ if(ok >= 0 && (db.qid.qtype&Sys->QTDIR) == 0){
+ # bugger off
+ sys->fprint(sys->fildes(2), "mk: %s is not a directory path=%ux\n", libc0->ab2s(dir), int db.qid.path);
+ Exit();
+ }
+ }
+ else{
+ s = libc0->s2ab(".");
+ buf[0] = byte 0;
+ }
+ if(symlooki(s, S_BULKED, 0) != nil)
+ return;
+ ss = libc0->strdup(s);
+ symlooks(ss, S_BULKED, ss);
+ dirtime(s, buf);
+}
+
+mtime(name: array of byte): int
+{
+ sbuf: Sys->Dir;
+ s, ss: array of byte;
+ carry: byte;
+ ok: int;
+
+ s = libc0->strrchr(name, '/');
+ if(s == name)
+ s = s[1: ];
+ if(s != nil){
+ ss = name;
+ carry = s[0];
+ s[0] = byte 0;
+ }
+ else{
+ ss = nil;
+ carry = byte 0;
+ }
+ bulkmtime(ss);
+ if(int carry)
+ s[0] = carry;
+ (ok, sbuf) = mkdirstat(name);
+ if(ok < 0)
+ return 0;
+ return sbuf.mtime;
+}
+
+filetime(name: array of byte): int
+{
+ sym: ref Symtab;
+
+ sym = symlooki(name, S_TIME, 0);
+ if(sym != nil)
+ return sym.ivalue; # uggh
+ return mtime(name);
+}
+
+#
+# archive
+#
+
+dolong: int;
+
+atimeof(force: int, name: array of byte): int
+{
+ sym: ref Symtab;
+ t: int;
+ archive, member: array of byte;
+ buf := array[512] of byte;
+
+ (archive, member) = split(name);
+ if(archive == nil)
+ Exit();
+ t = mtime(archive);
+ sym = symlooki(archive, S_AGG, 0);
+ if(sym != nil){
+ if(force || t > sym.ivalue){
+ atimes(archive);
+ sym.ivalue = t;
+ }
+ }
+ else{
+ atimes(archive);
+ # mark the aggegate as having been done
+ symlooks(libc0->strdup(archive), S_AGG, libc0->s2ab("")).ivalue = t;
+ }
+ # truncate long member name to sizeof of name field in archive header
+ if(dolong)
+ stob(buf, sys->sprint("%s(%s)", libc0->ab2s(archive), libc0->ab2s(member)));
+ else
+ stob(buf, sys->sprint("%s(%.*s)", libc0->ab2s(archive), SARNAME, libc0->ab2s(member)));
+ sym = symlooki(buf, S_TIME, 0);
+ if(sym != nil)
+ return sym.ivalue; # uggh
+ return 0;
+}
+
+atouch(name: array of byte)
+{
+ archive, member: array of byte;
+ fd: ref Sys->FD;
+ i: int;
+ # h: ar_hdr;
+ t: int;
+
+ (archive, member) = split(name);
+ if(archive == nil)
+ Exit();
+ fd = sys->open(libc0->ab2s(archive), Sys->ORDWR);
+ if(fd == nil){
+ fd = sys->create(libc0->ab2s(archive), Sys->OWRITE, 8r666);
+ if(fd == nil){
+ perror(archive);
+ Exit();
+ }
+ sys->write(fd, libc0->s2ab(ARMAG), SARMAG);
+ }
+ if(symlooki(name, S_TIME, 0) != nil){
+ # hoon off and change it in situ
+ sys->seek(fd, big SARMAG, 0);
+ buf := array[SAR_HDR] of byte;
+ while(sys->read(fd, buf, SAR_HDR) == SAR_HDR){
+ name = buf[0: SARNAME];
+ for(i = SARNAME-1; i > 0 && name[i] == byte ' '; i--)
+ ;
+ name[i+1] = byte 0;
+ if(libc0->strcmp(member, name) == 0){
+ t = SARNAME-SAR_HDR; # ughgghh
+ sys->seek(fd, big t, 1);
+ sys->fprint(fd, "%-12d", daytime->now());
+ break;
+ }
+ t = int string buf[48: 58];
+ if(t&8r1)
+ t++;
+ sys->seek(fd, big t, 1);
+ }
+ }
+ fd = nil;
+}
+
+atimes(ar: array of byte)
+{
+ # h: ar_hdr;
+ t: int;
+ fd: ref Sys->FD;
+ i: int;
+ buf := array[BIGBLOCK] of byte;
+ n: array of byte;
+ name := array[SARNAME+1] of byte;
+
+ fd = sys->open(libc0->ab2s(ar), Sys->OREAD);
+ if(fd == nil)
+ return;
+ if(sys->read(fd, buf, SARMAG) != SARMAG){
+ fd = nil;
+ return;
+ }
+ b := array[SAR_HDR] of byte;
+ while(sys->read(fd, b, SAR_HDR) == SAR_HDR){
+ t = int string b[16: 28];
+ if(t == 0) # as it sometimes happens; thanks ken
+ t = 1;
+ hname := b[0: SARNAME];
+ libc0->strncpy(name, hname, SARNAME);
+ for(i = SARNAME-1; i > 0 && name[i] == byte ' '; i--)
+ ;
+ if(name[i] == byte '/') # system V bug
+ i--;
+ name[i+1] = byte 0;
+ n = membername(name, fd, int string b[48: 58]);
+ if(n == nil){
+ dolong = 1;
+ continue;
+ }
+ stob(buf, sys->sprint("%s(%s)", libc0->ab2s(ar), libc0->ab2s(n)));
+ symlooki(libc0->strdup(buf), S_TIME, t).ivalue = t;
+ t = int string b[48: 58];
+ if(t&8r1)
+ t++;
+ sys->seek(fd, big t, 1);
+ }
+ fd = nil;
+}
+
+typex(file: array of byte): int
+{
+ fd: ref Sys->FD;
+ buf := array[SARMAG] of byte;
+
+ fd = sys->open(libc0->ab2s(file), Sys->OREAD);
+ if(fd == nil){
+ if(symlooki(file, S_BITCH, 0) == nil){
+ bout.puts(sys->sprint("%s doesn't exist: assuming it will be an archive\n", libc0->ab2s(file)));
+ symlooks(file, S_BITCH, file);
+ }
+ return 1;
+ }
+ if(sys->read(fd, buf, SARMAG) != SARMAG){
+ fd = nil;
+ return 0;
+ }
+ fd = nil;
+ return !libc0->strncmp(libc0->s2ab(ARMAG), buf, SARMAG);
+}
+
+split(name: array of byte): (array of byte, array of byte)
+{
+ member: array of byte;
+ p, q: array of byte;
+
+ p = libc0->strdup(name);
+ q = libc0->strchr(p, '(');
+ if(q != nil){
+ q[0] = byte 0;
+ q = q[1: ];
+ member = q;
+ q = libc0->strchr(q, ')');
+ if(q != nil)
+ q[0] = byte 0;
+ if(typex(p))
+ return (p, member);
+ p = nil;
+ sys->fprint(sys->fildes(2), "mk: '%s' is not an archive\n", libc0->ab2s(name));
+ }
+ return (nil, member);
+}
+
+#
+# bufblock
+#
+
+freelist: ref Bufblock;
+
+QUANTA: con 4096;
+
+newbuf(): ref Bufblock
+{
+ p: ref Bufblock;
+
+ if(freelist != nil){
+ p = freelist;
+ freelist = freelist.next;
+ }
+ else{
+ p = ref Bufblock;
+ p.start = array[QUANTA*1] of byte;
+ p.end = QUANTA;
+ }
+ p.current = 0;
+ p.start[0] = byte 0;
+ p.next = nil;
+ return p;
+}
+
+freebuf(p: ref Bufblock)
+{
+ p.next = freelist;
+ freelist = p;
+}
+
+growbuf(p: ref Bufblock)
+{
+ n: int;
+ f: ref Bufblock;
+ cp: array of byte;
+
+ n = p.end+QUANTA;
+ # search the free list for a big buffer
+ for(f = freelist; f != nil; f = f.next){
+ if(f.end >= n){
+ f.start[0: ] = p.start[0: p.end];
+ cp = f.start;
+ f.start = p.start;
+ p.start = cp;
+ cpi := f.end;
+ f.end = p.end;
+ p.end = cpi;
+ f.current = 0;
+ break;
+ }
+ }
+ if(f == nil){ # not found - grow it
+ nps := array[n] of byte;
+ for(i := 0; i < p.end; i++)
+ nps[i] = p.start[i];
+ p.start = nps;
+ p.end = n;
+ }
+ p.current = n-QUANTA;
+}
+
+bufcpy(buf: ref Bufblock, cp: array of byte, n: int)
+{
+ i := 0;
+ while(n--)
+ insert(buf, int cp[i++]);
+}
+
+insert(buf: ref Bufblock, c: int)
+{
+ if(buf.current >= buf.end)
+ growbuf(buf);
+ buf.start[buf.current++] = byte c;
+}
+
+rinsert(buf: ref Bufblock, r: int)
+{
+ n: int;
+
+ b := array[Sys->UTFmax] of byte;
+ n = sys->char2byte(r, b, 0);
+ if(buf.current+n > buf.end)
+ growbuf(buf);
+ buf.start[buf.current: ] = b[0: n];
+ buf.current += n;
+}
+
diff --git a/appl/cmd/mk/mkbinds b/appl/cmd/mk/mkbinds
new file mode 100644
index 00000000..a5df28dc
--- /dev/null
+++ b/appl/cmd/mk/mkbinds
@@ -0,0 +1,2 @@
+/appl/cmd/mk/mkconfig /mkconfig
+/appl/cmd/mk/mksubdirs /mkfiles/mksubdirs
diff --git a/appl/cmd/mk/mkconfig b/appl/cmd/mk/mkconfig
new file mode 100644
index 00000000..17d31a93
--- /dev/null
+++ b/appl/cmd/mk/mkconfig
@@ -0,0 +1,28 @@
+#
+# Set the following 4 variables. The host system is the system where
+# the software will be built; the target system is where it will run.
+# They are almost always the same.
+
+# On Nt systems, the ROOT path MUST be of the form `drive:/path'
+ROOT=
+
+#
+# Except for building kernels, SYSTARG must always be the same as SYSHOST
+#
+SYSHOST=Plan9 # build system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris)
+SYSTARG=$SYSHOST # target system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris)
+
+#
+# specify the architecture of the target system - Inferno imports it from the
+# environment; for other systems it is usually just hard-coded
+#
+#OBJTYPE=386 # target system object type (s800, mips, 386, arm, sparc)
+OBJTYPE=386
+
+#
+# no changes required beyond this point
+#
+OBJDIR=$SYSTARG/$OBJTYPE
+
+<$ROOT/mkfiles/mkhost-$SYSHOST # variables appropriate for host system
+<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE # variables used to build target object type
diff --git a/appl/cmd/mk/mkfile b/appl/cmd/mk/mkfile
new file mode 100644
index 00000000..51d20245
--- /dev/null
+++ b/appl/cmd/mk/mkfile
@@ -0,0 +1,19 @@
+<../../../mkconfig
+
+TARG= mk.dis\
+
+MODULES=\
+ ar.m\
+
+SYSMODULES= \
+ bufio.m\
+ draw.m\
+ math.m\
+ sys.m\
+ regex.m\
+ daytime.m\
+ libc0.m\
+
+DISBIN=$ROOT/dis
+
+<$ROOT/mkfiles/mkdis
diff --git a/appl/cmd/mk/mksubdirs b/appl/cmd/mk/mksubdirs
new file mode 100644
index 00000000..3fe01c81
--- /dev/null
+++ b/appl/cmd/mk/mksubdirs
@@ -0,0 +1,16 @@
+all:V: all-$SHELLTYPE
+install:V: install-$SHELLTYPE
+uninstall:V: uninstall-$SHELLTYPE
+nuke:V: nuke-$SHELLTYPE
+clean:V: clean-$SHELLTYPE
+
+%-rc %-nt %-sh:QV:
+ load std
+ for j in $DIRS {
+ if { ftest -d $j } {
+ echo 'cd' $j '; mk' $MKFLAGS $stem
+ cd $j; mk $MKFLAGS $stem; cd ..
+ } else {
+ ! { ftest -e $j }
+ }
+ }