summaryrefslogtreecommitdiff
path: root/appl/cmd/install/eproto.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/install/eproto.b')
-rw-r--r--appl/cmd/install/eproto.b357
1 files changed, 357 insertions, 0 deletions
diff --git a/appl/cmd/install/eproto.b b/appl/cmd/install/eproto.b
new file mode 100644
index 00000000..b87a4390
--- /dev/null
+++ b/appl/cmd/install/eproto.b
@@ -0,0 +1,357 @@
+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;
+}