diff options
Diffstat (limited to 'appl/cmd/mk/mk.b')
| -rw-r--r-- | appl/cmd/mk/mk.b | 4211 |
1 files changed, 4211 insertions, 0 deletions
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®EXP){ + 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®EXP || charin(head, libc0->s2ab("%&")) != nil){ + r.attr |= META; + if(reuse) + return; + if(attr®EXP){ + 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®EXP){ + 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®EXP) + 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®EXP)){ + 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®EXP) + 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®EXP && 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; +} + |
