implement Fsmodule;include "sys.m"; sys: Sys;include "readdir.m"; readdir: Readdir;include "bufio.m"; bufio: Bufio; Iobuf: import bufio;include "string.m"; str: String;include "draw.m";include "sh.m";include "fslib.m"; fslib: Fslib; Report, Value, type2s, report, quit: import fslib; Fschan, Fsdata, Entrychan, Entry, Gatechan, Gatequery, Nilentry, Option, Next, Down, Skip, Quit: import Fslib; File: adt { name: string; mode: int; owner: string; group: string; old: string; flags: int; sub: cyclic array of ref File;}; Proto: adt { indent: int; lastline: string; iob: ref Iobuf;}; Star, Plus: con 1<<iota; types(): string{ return "ts-rs";} badmod(p: string){ sys->fprint(sys->fildes(2), "fs: eproto: cannot load %s: %r\n", p); raise "fail:bad module";} init(){ sys = load Sys Sys->PATH; fslib = load Fslib Fslib->PATH; if(fslib == nil) badmod(Fslib->PATH); readdir = load Readdir Readdir->PATH; if(readdir == nil) badmod(Readdir->PATH); bufio = load Bufio Bufio->PATH; if(bufio == nil) badmod(Bufio->PATH); str = load String String->PATH; if(str == nil) badmod(String->PATH);} run(nil: ref Draw->Context, report: ref Report, opts: list of Option, args: list of ref Value): ref Value{ protofile := (hd args).s().i; rootpath: string; if(opts != nil) rootpath = (hd (hd opts).args).s().i; if(rootpath == nil) rootpath = "/"; proto := ref Proto(0, nil, nil); if((proto.iob = bufio->open(protofile, Sys->OREAD)) == nil){ sys->fprint(sys->fildes(2), "fs: eproto: cannot open %q: %r\n", protofile); return nil; } root := ref File(rootpath, ~0, nil, nil, nil, 0, nil); (root.flags, root.sub) = readproto(proto, -1); c := Entrychan(chan of int, chan of Entry); spawn protowalk(c, root, report.start("proto")); return ref Value.T(c);} protowalk(c: Entrychan, root: ref File, errorc: chan of string){ if(<-c.sync == 0){ quit(errorc); exit; } protowalk1(c, root.flags, root.name, file2dir(root, nil), root.sub, -1, errorc); c.c <-= (nil, nil, 0); quit(errorc);} protowalk1(c: Entrychan, flags: int, path: string, d: ref Sys->Dir, sub: array of ref File, depth: int, errorc: chan of string): int{ if(depth >= 0) c.c <-= (d, path, depth); depth++; (a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT); j := 0; prevsub: string; for(i := 0; i < n; i++){ for(; j < len sub; j++){ s := sub[j].name; if(s == prevsub){ report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s))); continue; # eliminate duplicates in proto } if(s >= a[i].name || sub[j].old != nil) break; report(errorc, sys->sprint("%s not found", pathconcat(path, s))); } foundsub := j < len sub && (sub[j].name == a[i].name || sub[j].old != nil); if(foundsub || flags&Plus || (flags&Star && (a[i].mode & Sys->DMDIR)==0)){ f: ref File; if(foundsub){ f = sub[j++]; prevsub = f.name; } p: string; d: ref Sys->Dir; if(foundsub && f.old != nil){ p = f.old; (ok, xd) := sys->stat(p); if(ok == -1){ report(errorc, sys->sprint("cannot stat %q: %r", p)); continue; } d = ref xd; }else{ p = pathconcat(path, a[i].name); d = a[i]; } d = file2dir(f, d); r: int; if((d.mode & Sys->DMDIR) == 0) r = walkfile(c, p, d, depth, errorc); else if(flags & Plus) r = protowalk1(c, Plus, p, d, nil, depth, errorc); else r = protowalk1(c, f.flags, p, d, f.sub, depth, errorc); if(r == Skip) return Next; } } return Next;} pathconcat(p, name: string): string{ if(p != nil && p[len p - 1] != '/') p[len p] = '/'; return p+name;} # from(ish) walk.bwalkfile(c: Entrychan, path: string, d: ref Sys->Dir, depth: int, errorc: chan of string): int{ fd := sys->open(path, Sys->OREAD); if(fd == nil) report(errorc, sys->sprint("cannot open %q: %r", path)); else c.c <-= (d, path, depth); return Next;} readproto(proto: ref Proto, indent: int): (int, array of ref File){ a := array[10] of ref File; n := 0; flags := 0; while((f := readline(proto, indent)) != nil){ if(f.name == "*") flags |= Star; else if(f.name == "+") flags |= Plus; else{ (f.flags, f.sub) = readproto(proto, proto.indent); if(n == len a) a = (array[n * 2] of ref File)[0:] = a; a[n++] = f; } } if(n < len a) a = (array[n] of ref File)[0:] = a[0:n]; mergesort(a, array[n] of ref File); return (flags, a);} readline(proto: ref Proto, indent: int): ref File{ s: string; if(proto.lastline != nil){ s = proto.lastline; proto.lastline = nil; }else if(proto.indent == -1) return nil; else if((s = proto.iob.gets('\n')) == nil){ proto.indent = -1; return nil; } spc := 0; for(i := 0; i < len s; i++){ c := s[i]; if(c == ' ') spc++; else if(c == '\t') spc += 8; else break; } if(i == len s || s[i] == '#' || s[i] == '\n') return readline(proto, indent); # XXX sort out tail recursion! if(spc <= indent){ proto.lastline = s; return nil; } proto.indent = spc; (nil, toks) := sys->tokenize(s, " \t\n"); f := ref File(nil, ~0, nil, nil, nil, 0, nil); (f.name, toks) = (getname(hd toks, 0), tl toks); if(toks == nil) return f; (f.mode, toks) = (getmode(hd toks), tl toks); if(toks == nil) return f; (f.owner, toks) = (getname(hd toks, 1), tl toks); if(toks == nil) return f; (f.group, toks) = (getname(hd toks, 1), tl toks); if(toks == nil) return f; (f.old, toks) = (hd toks, tl toks); return f;} mergesort(a, b: array of ref File){ r := len a; if (r > 1) { m := (r-1)/2 + 1; mergesort(a[0:m], b[0:m]); mergesort(a[m:], b[m:]); b[0:] = a; for ((i, j, k) := (0, m, 0); i < m && j < r; k++) { if(b[i].name > b[j].name) a[k] = b[j++]; else a[k] = b[i++]; } if (i < m) a[k:] = b[i:m]; else if (j < r) a[k:] = b[j:r]; }} getname(s: string, allowminus: int): string{ if(s == nil) return nil; if(allowminus && s == "-") return nil; if(s[0] == '$'){ s = getenv(s[1:]); if(s == nil) ; # TO DO: w.warn(sys->sprint("can't read environment variable %s", s)); return s; } return s;} getenv(s: string): string{ if(s == "user") return readfile("/dev/user"); # more accurate? return readfile("/env/"+s);} readfile(f: string): string{ fd := sys->open(f, Sys->OREAD); if(fd != nil){ a := array[256] of byte; n := sys->read(fd, a, len a); if(n > 0) return string a[0:n]; } return nil;} getmode(s: string): int{ s = getname(s, 1); if(s == nil) return ~0; m := 0; i := 0; if(s[i] == 'd'){ m |= Sys->DMDIR; i++; } if(i < len s && s[i] == 'a'){ m |= Sys->DMAPPEND; i++; } if(i < len s && s[i] == 'l'){ m |= Sys->DMEXCL; i++; } (xmode, t) := str->toint(s, 8); if(t != nil){ # report(aux.errorc, "bad mode specification %q", s); return ~0; } return xmode | m;} file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir{ d := ref Sys->nulldir; if(old != nil){ if(old.dtype != 'M'){ d.uid = "sys"; d.gid = "sys"; xmode := (old.mode >> 6) & 7; d.mode = old.mode | xmode | (xmode << 3); }else{ d.uid = old.uid; d.gid = old.gid; d.mode = old.mode; } d.length = old.length; d.mtime = old.mtime; d.atime = old.atime; d.muid = old.muid; d.name = old.name; } if(f != nil){ d.name = f.name; if(f.owner != nil) d.uid = f.owner; if(f.group != nil) d.gid = f.group; if(f.mode != ~0) d.mode = f.mode; } return d;}
implement Fsmodule;
include "sys.m";
sys: Sys;
include "readdir.m";
readdir: Readdir;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "string.m";
str: String;
include "draw.m";
include "sh.m";
include "fslib.m";
fslib: Fslib;
Report, Value, type2s, report, quit: import fslib;
Fschan, Fsdata, Entrychan, Entry,
Gatechan, Gatequery, Nilentry, Option,
Next, Down, Skip, Quit: import Fslib;
File: adt {
name: string;
mode: int;
owner: string;
group: string;
old: string;
flags: int;
sub: cyclic array of ref File;
};
Proto: adt {
indent: int;
lastline: string;
iob: ref Iobuf;
};
Star, Plus: con 1<<iota;
types(): string
{
return "ts-rs";
}
badmod(p: string)
{
sys->fprint(sys->fildes(2), "fs: eproto: cannot load %s: %r\n", p);
raise "fail:bad module";
}
init()
{
sys = load Sys Sys->PATH;
fslib = load Fslib Fslib->PATH;
if(fslib == nil)
badmod(Fslib->PATH);
readdir = load Readdir Readdir->PATH;
if(readdir == nil)
badmod(Readdir->PATH);
bufio = load Bufio Bufio->PATH;
if(bufio == nil)
badmod(Bufio->PATH);
str = load String String->PATH;
if(str == nil)
badmod(String->PATH);
}
run(nil: ref Draw->Context, report: ref Report,
opts: list of Option, args: list of ref Value): ref Value
{
protofile := (hd args).s().i;
rootpath: string;
if(opts != nil)
rootpath = (hd (hd opts).args).s().i;
if(rootpath == nil)
rootpath = "/";
proto := ref Proto(0, nil, nil);
if((proto.iob = bufio->open(protofile, Sys->OREAD)) == nil){
sys->fprint(sys->fildes(2), "fs: eproto: cannot open %q: %r\n", protofile);
return nil;
}
root := ref File(rootpath, ~0, nil, nil, nil, 0, nil);
(root.flags, root.sub) = readproto(proto, -1);
c := Entrychan(chan of int, chan of Entry);
spawn protowalk(c, root, report.start("proto"));
return ref Value.T(c);
}
protowalk(c: Entrychan, root: ref File, errorc: chan of string)
{
if(<-c.sync == 0){
quit(errorc);
exit;
}
protowalk1(c, root.flags, root.name, file2dir(root, nil), root.sub, -1, errorc);
c.c <-= (nil, nil, 0);
quit(errorc);
}
protowalk1(c: Entrychan, flags: int, path: string, d: ref Sys->Dir,
sub: array of ref File, depth: int, errorc: chan of string): int
{
if(depth >= 0)
c.c <-= (d, path, depth);
depth++;
(a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT);
j := 0;
prevsub: string;
for(i := 0; i < n; i++){
for(; j < len sub; j++){
s := sub[j].name;
if(s == prevsub){
report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s)));
continue; # eliminate duplicates in proto
}
if(s >= a[i].name || sub[j].old != nil)
break;
report(errorc, sys->sprint("%s not found", pathconcat(path, s)));
}
foundsub := j < len sub && (sub[j].name == a[i].name || sub[j].old != nil);
if(foundsub || flags&Plus ||
(flags&Star && (a[i].mode & Sys->DMDIR)==0)){
f: ref File;
if(foundsub){
f = sub[j++];
prevsub = f.name;
}
p: string;
d: ref Sys->Dir;
if(foundsub && f.old != nil){
p = f.old;
(ok, xd) := sys->stat(p);
if(ok == -1){
report(errorc, sys->sprint("cannot stat %q: %r", p));
continue;
}
d = ref xd;
}else{
p = pathconcat(path, a[i].name);
d = a[i];
}
d = file2dir(f, d);
r: int;
if((d.mode & Sys->DMDIR) == 0)
r = walkfile(c, p, d, depth, errorc);
else if(flags & Plus)
r = protowalk1(c, Plus, p, d, nil, depth, errorc);
else
r = protowalk1(c, f.flags, p, d, f.sub, depth, errorc);
if(r == Skip)
return Next;
}
}
return Next;
}
pathconcat(p, name: string): string
{
if(p != nil && p[len p - 1] != '/')
p[len p] = '/';
return p+name;
}
# from(ish) walk.b
walkfile(c: Entrychan, path: string, d: ref Sys->Dir, depth: int, errorc: chan of string): int
{
fd := sys->open(path, Sys->OREAD);
if(fd == nil)
report(errorc, sys->sprint("cannot open %q: %r", path));
else
c.c <-= (d, path, depth);
return Next;
}
readproto(proto: ref Proto, indent: int): (int, array of ref File)
{
a := array[10] of ref File;
n := 0;
flags := 0;
while((f := readline(proto, indent)) != nil){
if(f.name == "*")
flags |= Star;
else if(f.name == "+")
flags |= Plus;
else{
(f.flags, f.sub) = readproto(proto, proto.indent);
if(n == len a)
a = (array[n * 2] of ref File)[0:] = a;
a[n++] = f;
}
}
if(n < len a)
a = (array[n] of ref File)[0:] = a[0:n];
mergesort(a, array[n] of ref File);
return (flags, a);
}
readline(proto: ref Proto, indent: int): ref File
{
s: string;
if(proto.lastline != nil){
s = proto.lastline;
proto.lastline = nil;
}else if(proto.indent == -1)
return nil;
else if((s = proto.iob.gets('\n')) == nil){
proto.indent = -1;
return nil;
}
spc := 0;
for(i := 0; i < len s; i++){
c := s[i];
if(c == ' ')
spc++;
else if(c == '\t')
spc += 8;
else
break;
}
if(i == len s || s[i] == '#' || s[i] == '\n')
return readline(proto, indent); # XXX sort out tail recursion!
if(spc <= indent){
proto.lastline = s;
return nil;
}
proto.indent = spc;
(nil, toks) := sys->tokenize(s, " \t\n");
f := ref File(nil, ~0, nil, nil, nil, 0, nil);
(f.name, toks) = (getname(hd toks, 0), tl toks);
if(toks == nil)
return f;
(f.mode, toks) = (getmode(hd toks), tl toks);
if(toks == nil)
return f;
(f.owner, toks) = (getname(hd toks, 1), tl toks);
if(toks == nil)
return f;
(f.group, toks) = (getname(hd toks, 1), tl toks);
if(toks == nil)
return f;
(f.old, toks) = (hd toks, tl toks);
return f;
}
mergesort(a, b: array of ref File)
{
r := len a;
if (r > 1) {
m := (r-1)/2 + 1;
mergesort(a[0:m], b[0:m]);
mergesort(a[m:], b[m:]);
b[0:] = a;
for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
if(b[i].name > b[j].name)
a[k] = b[j++];
else
a[k] = b[i++];
}
if (i < m)
a[k:] = b[i:m];
else if (j < r)
a[k:] = b[j:r];
}
}
getname(s: string, allowminus: int): string
{
if(s == nil)
return nil;
if(allowminus && s == "-")
return nil;
if(s[0] == '$'){
s = getenv(s[1:]);
if(s == nil)
; # TO DO: w.warn(sys->sprint("can't read environment variable %s", s));
return s;
}
return s;
}
getenv(s: string): string
{
if(s == "user")
return readfile("/dev/user"); # more accurate?
return readfile("/env/"+s);
}
readfile(f: string): string
{
fd := sys->open(f, Sys->OREAD);
if(fd != nil){
a := array[256] of byte;
n := sys->read(fd, a, len a);
if(n > 0)
return string a[0:n];
}
return nil;
}
getmode(s: string): int
{
s = getname(s, 1);
if(s == nil)
return ~0;
m := 0;
i := 0;
if(s[i] == 'd'){
m |= Sys->DMDIR;
i++;
}
if(i < len s && s[i] == 'a'){
m |= Sys->DMAPPEND;
i++;
}
if(i < len s && s[i] == 'l'){
m |= Sys->DMEXCL;
i++;
}
(xmode, t) := str->toint(s, 8);
if(t != nil){
# report(aux.errorc, "bad mode specification %q", s);
return ~0;
}
return xmode | m;
}
file2dir(f: ref File, old: ref Sys->Dir): ref Sys->Dir
{
d := ref Sys->nulldir;
if(old != nil){
if(old.dtype != 'M'){
d.uid = "sys";
d.gid = "sys";
xmode := (old.mode >> 6) & 7;
d.mode = old.mode | xmode | (xmode << 3);
}else{
d.uid = old.uid;
d.gid = old.gid;
d.mode = old.mode;
}
d.length = old.length;
d.mtime = old.mtime;
d.atime = old.atime;
d.muid = old.muid;
d.name = old.name;
}
if(f != nil){
d.name = f.name;
if(f.owner != nil)
d.uid = f.owner;
if(f.group != nil)
d.gid = f.group;
if(f.mode != ~0)
d.mode = f.mode;
}
return d;
}