ref: 9f76a7f6819ac04552b4fb6588156f3e4089d1d7
dir: /appl/cmd/mk/mk.b/
#
# 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 "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;
regex = load Regex Regex->PATH;
daytime = load Daytime Daytime->PATH;
sys->pctl(Sys->FORKNS, nil);
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: 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: string;
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, tfd) = tmpfile("/tmp/mkarg");
if(tfd == nil){
perror(array of byte 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(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();
}
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);
}
tmpfile(basename: string): (string, ref Sys->FD)
{
pid := sys->pctl(0, nil);
for(i := 0; i < 100; i++){
t := basename+sys->sprint("%8.8d.%.2d", pid, i);
fd := sys->create(t, Sys->OEXCL|Sys->ORDWR, 8r600);
if(fd != nil)
return (t, fd);
}
return (nil, nil);
}
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);
raise "fail:execl";
}
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("/lib/mk/binds", 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
if(sys->open("/env/autoload", Sys->OREAD) == nil){
fd := sys->create("/env/autoload", Sys->OWRITE, 8r666);
if(fd != nil)
sys->fprint(fd, "std");
}
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;
}
}
}
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;
}
}
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;
}
}
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();
raise "fail:error";
}
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);
}
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;
}