summaryrefslogtreecommitdiff
path: root/appl/alphabet
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/alphabet
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/alphabet')
-rw-r--r--appl/alphabet/abc/abc.b53
-rw-r--r--appl/alphabet/abc/autoconvert.b80
-rw-r--r--appl/alphabet/abc/autodeclare.b42
-rw-r--r--appl/alphabet/abc/declare.b70
-rw-r--r--appl/alphabet/abc/declares.b124
-rw-r--r--appl/alphabet/abc/define.b52
-rw-r--r--appl/alphabet/abc/eval.b66
-rw-r--r--appl/alphabet/abc/import.b53
-rw-r--r--appl/alphabet/abc/mkfile29
-rw-r--r--appl/alphabet/abc/newtypeset.b147
-rw-r--r--appl/alphabet/abc/rewrite.b71
-rw-r--r--appl/alphabet/abc/type.b53
-rw-r--r--appl/alphabet/abc/typeset.b51
-rw-r--r--appl/alphabet/abc/undeclare.b48
-rw-r--r--appl/alphabet/alphabet.b1677
-rw-r--r--appl/alphabet/alphabet.proto29
-rw-r--r--appl/alphabet/alphabet.shmod.b413
-rw-r--r--appl/alphabet/auxi/endpoints.b105
-rw-r--r--appl/alphabet/auxi/endpointsrv.b58
-rw-r--r--appl/alphabet/auxi/fsfilter.b62
-rw-r--r--appl/alphabet/auxi/mkfile21
-rw-r--r--appl/alphabet/auxi/rexecsrv.b301
-rw-r--r--appl/alphabet/declare.sh25
-rw-r--r--appl/alphabet/eval.b757
-rw-r--r--appl/alphabet/extvalues.b49
-rw-r--r--appl/alphabet/fs/and.b70
-rw-r--r--appl/alphabet/fs/bundle.b210
-rw-r--r--appl/alphabet/fs/bundle.m9
-rw-r--r--appl/alphabet/fs/chstat.b189
-rw-r--r--appl/alphabet/fs/compose.b105
-rw-r--r--appl/alphabet/fs/depth.b54
-rw-r--r--appl/alphabet/fs/entries.b91
-rw-r--r--appl/alphabet/fs/exec.b172
-rw-r--r--appl/alphabet/fs/filter.b66
-rw-r--r--appl/alphabet/fs/ls.b107
-rw-r--r--appl/alphabet/fs/match.b84
-rw-r--r--appl/alphabet/fs/merge.b192
-rw-r--r--appl/alphabet/fs/mergewrite.b244
-rw-r--r--appl/alphabet/fs/mkext.b266
-rw-r--r--appl/alphabet/fs/mkfile55
-rw-r--r--appl/alphabet/fs/mode.b125
-rw-r--r--appl/alphabet/fs/newer.b64
-rw-r--r--appl/alphabet/fs/not.b53
-rw-r--r--appl/alphabet/fs/or.b70
-rw-r--r--appl/alphabet/fs/path.b82
-rw-r--r--appl/alphabet/fs/pipe.b230
-rw-r--r--appl/alphabet/fs/print.b61
-rw-r--r--appl/alphabet/fs/proto.b416
-rw-r--r--appl/alphabet/fs/query.b135
-rw-r--r--appl/alphabet/fs/run.b65
-rw-r--r--appl/alphabet/fs/select.b60
-rw-r--r--appl/alphabet/fs/setroot.b109
-rw-r--r--appl/alphabet/fs/size.b64
-rw-r--r--appl/alphabet/fs/unbundle.b259
-rw-r--r--appl/alphabet/fs/unbundle.m9
-rw-r--r--appl/alphabet/fs/walk.b242
-rw-r--r--appl/alphabet/fs/write.b137
-rw-r--r--appl/alphabet/fsdecl.sh13
-rwxr-xr-xappl/alphabet/getendpoint.sh13
-rw-r--r--appl/alphabet/grid/farm.b144
-rw-r--r--appl/alphabet/grid/line2rec.b91
-rw-r--r--appl/alphabet/grid/local.b86
-rw-r--r--appl/alphabet/grid/mkfile22
-rw-r--r--appl/alphabet/grid/remote.b88
-rw-r--r--appl/alphabet/grid/rexec.b112
-rw-r--r--appl/alphabet/main/auth.b157
-rw-r--r--appl/alphabet/main/cat.b78
-rw-r--r--appl/alphabet/main/create.b55
-rw-r--r--appl/alphabet/main/dial.b85
-rw-r--r--appl/alphabet/main/echo.b51
-rw-r--r--appl/alphabet/main/export.b52
-rw-r--r--appl/alphabet/main/fd.b83
-rw-r--r--appl/alphabet/main/filter.b114
-rw-r--r--appl/alphabet/main/genfilter.b79
-rw-r--r--appl/alphabet/main/mkfile36
-rw-r--r--appl/alphabet/main/mount.b80
-rw-r--r--appl/alphabet/main/par.b50
-rw-r--r--appl/alphabet/main/parse.b43
-rw-r--r--appl/alphabet/main/pretty.b116
-rw-r--r--appl/alphabet/main/print.b55
-rw-r--r--appl/alphabet/main/read.b56
-rw-r--r--appl/alphabet/main/readall.b46
-rw-r--r--appl/alphabet/main/rewrite.b97
-rw-r--r--appl/alphabet/main/rw.b50
-rw-r--r--appl/alphabet/main/seq.b66
-rw-r--r--appl/alphabet/main/unparse.b38
-rw-r--r--appl/alphabet/main/w2fd.b61
-rw-r--r--appl/alphabet/main/wait.b35
-rwxr-xr-xappl/alphabet/mkendpoint.sh14
-rw-r--r--appl/alphabet/mkfile49
-rw-r--r--appl/alphabet/newtypesets229
-rw-r--r--appl/alphabet/proxy.b304
-rw-r--r--appl/alphabet/reports.b189
-rwxr-xr-xappl/alphabet/rexecsrv.sh9
-rw-r--r--appl/alphabet/setup63
-rw-r--r--appl/alphabet/typesets/abc.b180
-rw-r--r--appl/alphabet/typesets/abctypes.b229
-rw-r--r--appl/alphabet/typesets/fs.b226
-rw-r--r--appl/alphabet/typesets/fstypes.b230
-rw-r--r--appl/alphabet/typesets/grid.b160
-rw-r--r--appl/alphabet/typesets/gridtypes.b230
-rw-r--r--appl/alphabet/typesets/mkfile34
102 files changed, 12829 insertions, 0 deletions
diff --git a/appl/alphabet/abc/abc.b b/appl/alphabet/abc/abc.b
new file mode 100644
index 00000000..b8d25342
--- /dev/null
+++ b/appl/alphabet/abc/abc.b
@@ -0,0 +1,53 @@
+implement Mkabc, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Mkabc: module {};
+types(): string
+{
+ return "A";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ nil: list of ref Value
+ ): ref Value
+{
+ alphabet := load Alphabet Alphabet->PATH;
+ if(alphabet == nil){
+ report(errorc, sys->sprint("abc: cannot load %q: %r", Alphabet->PATH));
+ return nil;
+ }
+ alphabet->init();
+ c := chan[1] of int;
+ c <-= 1;
+ return ref Value.VA((c, alphabet));
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/autoconvert.b b/appl/alphabet/abc/autoconvert.b
new file mode 100644
index 00000000..5e542c80
--- /dev/null
+++ b/appl/alphabet/abc/autoconvert.b
@@ -0,0 +1,80 @@
+implement Autoconvert, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ Cmd,
+ n_BLOCK, n_WORD, n_SEQ, n_LIST, n_ADJ, n_VAR: import Sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Autoconvert: module {};
+types(): string
+{
+ return "AAssc";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ src := (hd tl args).s().i;
+ dst := (hd tl tl args).s().i;
+ c := (hd tl tl tl args).c().i;
+
+ # {word} -> {(src); word $1}
+ if(c.ntype == n_BLOCK && c.left.ntype == n_WORD){
+ c = mk(n_BLOCK,
+ mk(n_SEQ,
+ mk(n_LIST, mkw(src), nil),
+ mk(n_ADJ,
+ c.left,
+ mk(n_VAR, mkw("1"), nil)
+ )
+ ),
+ nil
+ );
+ }
+
+ err := a->autoconvert(src, dst, c, errorc);
+ if(err != nil){
+ report(errorc, "abcautoconvert: "+err);
+ return nil;
+ }
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
+
+mk(ntype: int, left, right: ref Cmd): ref Cmd
+{
+ return ref Cmd(ntype, left, right, nil, nil);
+}
+mkw(w: string): ref Cmd
+{
+ return ref Cmd(n_WORD, nil, nil, w, nil);
+}
diff --git a/appl/alphabet/abc/autodeclare.b b/appl/alphabet/abc/autodeclare.b
new file mode 100644
index 00000000..b79009ed
--- /dev/null
+++ b/appl/alphabet/abc/autodeclare.b
@@ -0,0 +1,42 @@
+implement Autoconvert, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Autoconvert: module {};
+types(): string
+{
+ return "AAs";
+}
+
+init()
+{
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(nil: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ (hd args).A().i.alphabet->setautodeclare(int (hd tl args).s().i);
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/declare.b b/appl/alphabet/abc/declare.b
new file mode 100644
index 00000000..4c7bc020
--- /dev/null
+++ b/appl/alphabet/abc/declare.b
@@ -0,0 +1,70 @@
+implement Declare, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Declare: module {};
+types(): string
+{
+ return "AAss*-q-c";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ flags := 0;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).t0 {
+ 'q' =>
+ flags |= Alphabet->ONDEMAND;
+ 'c' =>
+ flags |= Alphabet->CHECK;
+ }
+ }
+
+ n := len args;
+ if(n > 3){
+ report(errorc, "declare: maximum of two arguments allowed");
+ return nil;
+ }
+ a := (hd args).A().i.alphabet;
+ m := (hd tl args).s().i;
+ sig := "";
+ if(n > 2)
+ sig = (hd tl tl args).s().i;
+ e := a->declare(m, sig, flags);
+ if(e != nil){
+ report(errorc, "declare: "+e);
+ return nil;
+ }
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/declares.b b/appl/alphabet/abc/declares.b
new file mode 100644
index 00000000..f4e58467
--- /dev/null
+++ b/appl/alphabet/abc/declares.b
@@ -0,0 +1,124 @@
+implement Declares, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ n_BLOCK, n_ADJ, n_VAR, n_WORD: import Sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ report, Report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+include "alphabet/abctypes.m";
+ abctypes: Abctypes;
+ Abccvt: import abctypes;
+
+cvt: ref Abccvt;
+
+types(): string
+{
+ return "AAc";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+ alphabet = checkload(load Alphabet Alphabet->PATH, Alphabet->PATH);
+ alphabet->init();
+ abctypes = checkload(load Abctypes Abctypes->PATH, Abctypes->PATH);
+ (c, nil, abccvt) := abctypes->proxy0();
+ cvt = abccvt;
+ alphabet->loadtypeset("/abc", c, nil);
+ alphabet->importtype("/abc/abc");
+ alphabet->importtype("/string");
+ alphabet->importtype("/cmd");
+ c = nil;
+ # note: it's faster if we provide the signatures, as we don't
+ # have to load the module to find out its signature just to throw
+ # it away again. pity about the maintenance.
+
+ # Edit x s:(/abc/[a-z]+) (.*):declimport("\1", "\2");
+ declimport("/abc/autoconvert", "abc string string cmd -> abc");
+ declimport("/abc/autodeclare", "abc string -> abc");
+ declimport("/abc/declare", "[-qc] abc string [string...] -> abc");
+ declimport("/abc/define", "abc string cmd -> abc");
+ declimport("/abc/import", "abc string [string...] -> abc");
+ declimport("/abc/type", "abc string [string...] -> abc");
+ declimport("/abc/typeset", "abc string -> abc");
+ declimport("/abc/undeclare", "abc string [string...] -> abc");
+}
+
+quit()
+{
+ alphabet->quit();
+}
+
+run(errorc: chan of string, r: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ (av, err) := alphabet->importvalue(cvt.int2ext((hd args).dup()), "/abc/abc");
+ if(av == nil){
+ report(errorc, sys->sprint("declares: cannot import abc value: %s", err));
+ return nil;
+ }
+ vc := chan of ref Alphabet->Value;
+ spawn alphabet->eval0((hd tl args).c().i, "/abc/abc", nil, r, r.start("evaldecl"), av :: nil, vc);
+ av = <-vc;
+ if(av == nil)
+ return nil;
+ v := cvt.ext2int(av).dup();
+ alphabet->av.free(1);
+ return v;
+}
+
+declimport(m: string, sig: string)
+{
+ if((e := alphabet->declare(m, sig, Alphabet->ONDEMAND)) != nil)
+ raise sys->sprint("fail:cannot declare %s: %s", m, e);
+ alphabet->importmodule(m);
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
+
+declares(a: Alphabet, decls: ref Sh->Cmd, errorc: chan of string, stopc: chan of int): string
+{
+ spawn reports->reportproc(reportc := chan of string, stopc, reply := chan of ref Report);
+ r := <-reply;
+ reply = nil;
+ spawn declaresproc(a, decls, r.start("declares"), r, vc := chan of ref Value);
+ r.enable();
+
+ v: ref Value;
+wait:
+ for(;;)alt{
+ v = <-vc =>
+ ;
+ msg := <-reportc =>
+ if(msg == nil)
+ break wait;
+ errorc <-= sys->sprint("declares: %s", msg);
+ }
+ if(v == nil)
+ return "declarations failed";
+ return nil;
+}
+
+declaresproc(a: Alphabet, decls: ref Sh->Cmd, errorc: chan of string, r: ref Report, vc: chan of ref Value)
+{
+ novals: list of ref Value;
+ vc <-= run(errorc, r, nil, abc->mkabc(a).dup() :: ref Value.Vc(decls) :: novals);
+ errorc <-= nil;
+}
diff --git a/appl/alphabet/abc/define.b b/appl/alphabet/abc/define.b
new file mode 100644
index 00000000..d6929b5d
--- /dev/null
+++ b/appl/alphabet/abc/define.b
@@ -0,0 +1,52 @@
+implement Define, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Define: module {};
+types(): string
+{
+ return "AAsc";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ m := (hd tl args).s().i;
+ c := (hd tl tl args).c().i;
+ if((e := a->define(m, c, errorc)) != nil){
+ report(errorc, "define: error: "+e);
+ return nil;
+ }
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/eval.b b/appl/alphabet/abc/eval.b
new file mode 100644
index 00000000..184aa0fd
--- /dev/null
+++ b/appl/alphabet/abc/eval.b
@@ -0,0 +1,66 @@
+implement Evalabc, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report, Report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Evalabc: module {};
+types(): string
+{
+ return "rAcs*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(nil: chan of string, r: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ c := (hd tl args).c().i;
+ vl, rvl: list of ref Alphabet->Value;
+ for(args = tl tl args; args != nil; args = tl args)
+ vl = ref (Alphabet->Value).Vs((hd args).s().i) :: vl;
+ for(; vl != nil; vl = tl vl)
+ rvl = hd vl :: rvl;
+ vc := chan of ref Alphabet->Value;
+ spawn a->eval0(c, "/status", nil, r, r.start("abceval"), rvl, vc);
+ v := <-vc;
+ if(v == nil)
+ return nil;
+ return ref Value.Vr(vr(v).i);
+}
+
+vr(v: ref Alphabet->Value): ref (Alphabet->Value).Vr
+{
+ pick xv := v {
+ Vr =>
+ return xv;
+ }
+ return nil;
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/import.b b/appl/alphabet/abc/import.b
new file mode 100644
index 00000000..ea6c4214
--- /dev/null
+++ b/appl/alphabet/abc/import.b
@@ -0,0 +1,53 @@
+implement Import, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Import: module {};
+types(): string
+{
+ return "AAss*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ av := (hd args);
+ a := av.A().i.alphabet;
+ for(args = tl args; args != nil; args = tl args){
+ if((e := a->importmodule((hd args).s().i)) != nil){
+ report(errorc, "import: "+(hd args).s().i+": "+e);
+ return nil;
+ }
+ }
+ return av.dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/mkfile b/appl/alphabet/abc/mkfile
new file mode 100644
index 00000000..02a918e7
--- /dev/null
+++ b/appl/alphabet/abc/mkfile
@@ -0,0 +1,29 @@
+<../../../mkconfig
+
+TARG=\
+ abc.dis\
+ autoconvert.dis\
+ autodeclare.dis\
+ declare.dis\
+ declares.dis\
+ define.dis\
+ eval.dis\
+ import.dis\
+ rewrite.dis\
+ type.dis\
+ typeset.dis\
+ undeclare.dis\
+
+SYSMODULES=\
+ alphabet.m\
+ draw.m\
+ alphabet/abc.m\
+ alphabet/reports.m\
+ sh.m\
+ string.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/alphabet/abc
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
diff --git a/appl/alphabet/abc/newtypeset.b b/appl/alphabet/abc/newtypeset.b
new file mode 100644
index 00000000..843eb116
--- /dev/null
+++ b/appl/alphabet/abc/newtypeset.b
@@ -0,0 +1,147 @@
+implement Newtypeset, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value, Vtype: import abc;
+
+# types abc -> types
+# returns a set of types defined in terms of the types and modules in $1
+# stdtypes types -> types
+# adds the standard root types to $1
+# newtype [-u] types string string cmd -> types
+# adds a new type named $2 to $1; the underlying type will be $3, and the destructor $4.
+# -u flag implies values of this type cannot be duplicated.
+# modules types -> modules
+# returns a value suitable for defining modules in terms of types defined in $1,
+# containing no module definitions.
+# module modules string string cmd -> modules
+# newtypeset abc string modules -> abc
+
+# declares adds some autoconversions:
+#
+# autoconvert abc types "{| types}
+# autoconvert types modules "{| modules}
+#
+# declares "{(abc)
+# autodeclare 1 |
+# newtypeset $1 /images {
+# abc |
+# autoconvert 1 |
+# newtype image /fd "{} |
+# newmodule read '/fd -> image' "{
+# | /filter "{canonimage}
+# } |
+# newmodule rotate 'image -> image' "{
+# | /filter "{rotate}
+# } |
+# newmodule display 'image -> /status' "{
+# | /filter "{showimage} | /create /dev/null
+# }
+# } |
+# type /images/image |
+# import /images/rotate |
+# autoconvert /string /fd "{|/read} |
+# autoconvert /fd image "{|/images/read} |
+# autoconvert image /status "{|/images/display}
+# }
+#
+# -{rotate x.bit}
+
+Newtypeset: module {};
+types(): string
+{
+ return "AAsm";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ d := (hd tl args).s().i;
+ path := "/dis/alphabet/" + d + "/alphabet"
+ iob := bufio->open(, Sys->OREAD);
+ if(iob == nil){
+ report(errorc, sys->sprint("scripttypeset: cannot open %q: %r", path));
+ return nil;
+ }
+ {
+ (types, decls) := parse(iob);
+ alphabet := load Alphabet Alphabet->PATH;
+ if(alphabet == nil){
+ report(errorc, sys->sprint("scripttypeset: cannot load %q: %r", Alphabet->PATH));
+ return nil;
+ }
+ declares := load Declares Declares->PATH;
+ if(declares == nil){
+ report(errorc, sys->sprint("scripttypeset: cannot load %q: %r", Alphabet->PATH));
+ return nil;
+ }
+ if((err := declares->declares(alphabet, decls, errorc)) != nil){
+ report(errorc, "scripttypeset: error on declarations: "+err);
+ return nil;
+ }
+ declares->quit();
+ declares = nil;
+ if(checktypes(alphabet, types, errorc) == -1)
+ return nil;
+ spawn scripttypesetproc(alphabet, types, c := chan of ref Proxy->Typescmd[ref Alphabet->Value]);
+ if((err := a->loadtypeset(d, c, errorc)) != nil){
+ c <-= nil;
+ return nil;
+ }
+ return (hd args).dup();
+ } exception e {
+ "parse:*" =>
+ report(errorc, sys->sprint("scripttypeset: error parsing %q: %s", path, e[6:]));
+ return nil;
+ }
+}
+
+checktypes(alphabet: Alphabet, types: list of ref Type, errorc: chan of string): int
+{
+ for(; types != nil; types = tl types){
+ t := hd types;
+ if(t.destructor != nil){
+ report(errorc, "destructors not supported yet");
+ }
+ }
+}
+
+scripttypesetproc(alphabet: Alphabet, types: list of ref Type, c: chan of Proxy->Typescmd[ref Alphabet->Value])
+{
+ while((gr := <-c) != nil){
+ pick r := gr {
+ Alphabet =>
+ Load =>
+
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/rewrite.b b/appl/alphabet/abc/rewrite.b
new file mode 100644
index 00000000..a9749258
--- /dev/null
+++ b/appl/alphabet/abc/rewrite.b
@@ -0,0 +1,71 @@
+implement Rewrite, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Rewrite: module {};
+types(): string
+{
+ return "cAc-ss-rs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ rtype, sig: string;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).t0 {
+ 's' =>
+ sig = (hd (hd opts).t1).s().i;
+ 'r' =>
+ rtype = (hd (hd opts).t1).s().i;
+ }
+ }
+ a := (hd args).A().i.alphabet;
+ c := (hd tl args).c().i;
+ actsig: string;
+ (c, actsig) = a->rewrite(c, rtype, errorc);
+ if(c == nil)
+ return nil;
+ if(sig != nil){
+ (ok, err) := a->typecompat(sig, actsig);
+ if(err != nil){
+ report(errorc, "rewrite: "+err);
+ return nil;
+ }
+ if(ok == 0){
+ report(errorc, sys->sprint("rewrite: %q is not compatible with %q", sig, actsig));
+ return nil;
+ }
+ }
+ return ref Value.Vc(c);
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/type.b b/appl/alphabet/abc/type.b
new file mode 100644
index 00000000..c36ceb61
--- /dev/null
+++ b/appl/alphabet/abc/type.b
@@ -0,0 +1,53 @@
+implement Type, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Type: module {};
+types(): string
+{
+ return "AAss*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ av := (hd args);
+ a := av.A().i.alphabet;
+ for(args = tl args; args != nil; args = tl args){
+ if((e := a->importtype((hd args).s().i)) != nil){
+ report(errorc, "type: "+(hd args).s().i+": "+e);
+ return nil;
+ }
+ }
+ return av.dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/typeset.b b/appl/alphabet/abc/typeset.b
new file mode 100644
index 00000000..4b9152ea
--- /dev/null
+++ b/appl/alphabet/abc/typeset.b
@@ -0,0 +1,51 @@
+implement Typeset, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Typeset: module {};
+types(): string
+{
+ return "AAs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(errorc: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ e := a->loadtypeset((hd tl args).s().i, nil, errorc);
+ if(e != nil){
+ report(errorc, "typeset: "+(hd tl args).s().i+": "+e);
+ return nil;
+ }
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/abc/undeclare.b b/appl/alphabet/abc/undeclare.b
new file mode 100644
index 00000000..c4f266b8
--- /dev/null
+++ b/appl/alphabet/abc/undeclare.b
@@ -0,0 +1,48 @@
+implement Undeclare, Abcmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+include "alphabet/abc.m";
+ abc: Abc;
+ Value: import abc;
+
+Undeclare: module {};
+types(): string
+{
+ return "AAss*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+}
+
+quit()
+{
+}
+
+run(nil: chan of string, nil: ref Reports->Report,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value
+ ): ref Value
+{
+ a := (hd args).A().i.alphabet;
+ for(al := tl args; al != nil; al = tl al)
+ a->undeclare((hd al).s().i);
+ return (hd args).dup();
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/alphabet.b b/appl/alphabet/alphabet.b
new file mode 100644
index 00000000..d56ee095
--- /dev/null
+++ b/appl/alphabet/alphabet.b
@@ -0,0 +1,1677 @@
+implement Alphabet, Copy;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "readdir.m";
+include "sh.m";
+ sh: Sh;
+ n_BLOCK, n_SEQ, n_LIST, n_ADJ, n_WORD, n_VAR, n_BQ2, n_PIPE: import Sh;
+include "sets.m";
+ sets: Sets;
+ Set: import sets;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+ Modulecmd, Typescmd: import Proxy;
+include "alphabet.m";
+ evalmod: Eval;
+ Context: import evalmod;
+
+Mainsubtypes: module {
+ proxy: fn(): chan of ref Proxy->Typescmd[ref Alphabet->Value];
+};
+
+# to do:
+# - sort out concurrent access to alphabet.
+# - if multiple options are given where only one is expected,
+# most modules ignore some values, where they should
+# discard them correctly. this could cause a malicious user
+# to hang up an alphabet expression (waiting for report to end)
+# - proper implementation of endpointsrv:
+# - resilience to failures
+# - security of endpoints
+# - no need for write(0)... (or maybe there is)
+# - proper implementation of rexecsrv:
+# - should be aware of user
+
+Debug: con 0;
+autodeclare := 0;
+
+Module: adt {
+ modname: string; # used when loading on demand.
+ typeset: ref Typeset;
+ sig: string;
+ c: chan of ref Modulecmd[ref Value];
+ m: Mainmodule;
+ def: ref Sh->Cmd;
+ defmods: ref Strhash[cyclic ref Module];
+ refcount: int;
+
+ find: fn(ctxt: ref Evalctxt, s: string): (ref Module, string);
+ typesig: fn(m: self ref Module): string;
+ run: fn(m: self ref Module, ctxt: ref Evalctxt,
+ errorc: chan of string,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value;
+ typename2c: fn(s: string): int;
+ mks: fn(ctxt: ref Evalctxt, s: string): ref Value;
+ mkc: fn(ctxt: ref Evalctxt, c: ref Sh->Cmd): ref Value;
+ ensureloaded: fn(m: self ref Module): string;
+ cvt: fn(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value;
+};
+
+Evalctxt: adt {
+ modules: ref Strhash[ref Module];
+ drawctxt: ref Draw->Context;
+ report: ref Report;
+# stopc: chan of int;
+};
+
+# used for rewriting expressions.
+Rvalue: adt {
+ i: ref Sh->Cmd;
+ tc: int;
+ refcount: int;
+ opts: list of (int, list of ref Rvalue);
+ args: list of ref Rvalue;
+
+ dup: fn(t: self ref Rvalue): ref Rvalue;
+ free: fn(v: self ref Rvalue, used: int);
+ isstring: fn(v: self ref Rvalue): int;
+ gets: fn(t: self ref Rvalue): string;
+ type2s: fn(tc: int): string;
+ typec: fn(t: self ref Rvalue): int;
+};
+
+Rmodule: adt {
+ m: ref Module;
+
+ cvt: fn(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue;
+ find: fn(nil: ref Revalctxt, s: string): (ref Rmodule, string);
+ typesig: fn(m: self ref Rmodule): string;
+ run: fn(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string,
+ opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue;
+ mks: fn(ctxt: ref Revalctxt, s: string): ref Rvalue;
+ mkc: fn(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue;
+ typename2c: fn(s: string): int;
+};
+
+Revalctxt: adt {
+ modules: ref Strhash[ref Module];
+ used: ref Strhash[ref Module];
+ defs: int;
+ vals: list of ref Rvalue;
+};
+
+Renv: adt {
+ items: list of ref Rvalue;
+ n: int;
+};
+
+Typeset: adt {
+ name: string;
+ c: chan of ref Typescmd[ref Value];
+ types: ref Table[cyclic ref Type]; # indexed by external type character
+ parent: ref Typeset;
+
+ gettype: fn(ts: self ref Typeset, tc: int): ref Type;
+};
+
+Type: adt {
+ id: int;
+ tc: int;
+ transform: list of ref Transform;
+ typeset: ref Typeset;
+ qname: string;
+ name: string;
+};
+
+Transform: adt {
+ dst: int; # which type we're transforming into.
+ all: Set; # set of all types this transformation can lead to.
+ expr: ref Sh->Cmd; # transformation operation.
+};
+
+Table: adt[T] {
+ items: array of list of (int, T);
+ nilval: T;
+
+ new: fn(nslots: int, nilval: T): ref Table[T];
+ add: fn(t: self ref Table, id: int, x: T): int;
+ del: fn(t: self ref Table, id: int): int;
+ find: fn(t: self ref Table, id: int): T;
+};
+
+Strhash: adt[T] {
+ items: array of list of (string, T);
+ nilval: T;
+
+ new: fn(nslots: int, nilval: T): ref Strhash[T];
+ add: fn(t: self ref Strhash, id: string, x: T);
+ del: fn(t: self ref Strhash, id: string);
+ find: fn(t: self ref Strhash, id: string): T;
+};
+
+Copy: module {
+ initcopy: fn(
+ typesets: list of ref Typeset,
+ roottypeset: ref Typeset,
+ modules: ref Strhash[ref Module],
+ typebyname: ref Strhash[ref Type],
+ typebyc: ref Table[ref Type],
+ types: array of ref Type,
+ currtypec: int
+ ): Alphabet;
+};
+
+typesets: list of ref Typeset;
+roottypeset: ref Typeset;
+modules: ref Strhash[ref Module];
+typebyname: ref Strhash[ref Type];
+typebyc: ref Table[ref Type]; # indexed by internal type character.
+types: array of ref Type; # indexed by id.
+currtypec := 16r25a0; # pretty graphics.
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ sys->fprint(sys->fildes(2), "alphabet: cannot load %s: %r\n", path);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sh = load Sh Sh->PATH;
+ sets = checkload(load Sets Sets->PATH, Sets->PATH);
+ evalmod = checkload(load Eval Eval->PATH, Eval->PATH);
+ evalmod->init();
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+
+ roottypeset = ref Typeset("/", nil, Table[ref Type].new(5, nil), nil);
+ typesets = roottypeset :: typesets;
+ types = array[] of {
+ ref Type(-1, 'c', nil, roottypeset, "/cmd", "cmd"),
+ ref Type(-1, 's', nil, roottypeset, "/string", "string"),
+ ref Type(-1, 'r', nil, roottypeset, "/status", "status"),
+ ref Type(-1, 'f', nil, roottypeset, "/fd", "fd"),
+ ref Type(-1, 'w', nil, roottypeset, "/wfd", "wfd"),
+ ref Type(-1, 'd', nil, roottypeset, "/data", "data"),
+ };
+ typebyname = typebyname.new(11, nil);
+ typebyc = typebyc.new(11, nil);
+ for(i := 0; i < len types; i++){
+ types[i].id = i;
+ typebyc.add(types[i].tc, types[i]);
+ typebyname.add(types[i].qname, types[i]);
+ roottypeset.types.add(types[i].tc, types[i]);
+ }
+# typebyc.add('a', ref Type(-1, 'a', nil, nil, "/any", "any")); # not sure about this anymore
+ modules = modules.new(3, nil);
+}
+
+initcopy(
+ xtypesets: list of ref Typeset,
+ xroottypeset: ref Typeset,
+ xmodules: ref Strhash[ref Module],
+ xtypebyname: ref Strhash[ref Type],
+ xtypebyc: ref Table[ref Type],
+ xtypes: array of ref Type,
+ xcurrtypec: int): Alphabet
+{
+ # XXX must do copy-on-write, and refcounting on typesets.
+ typesets = xtypesets;
+ roottypeset = xroottypeset;
+ modules = xmodules;
+ typebyname = xtypebyname;
+ typebyc = xtypebyc;
+ types = xtypes;
+ currtypec = xcurrtypec;
+ return load Alphabet "$self";
+}
+
+copy(): Alphabet
+{
+ a := load Copy Alphabet->PATH;
+ if(a == nil)
+ return nil;
+ return a->initcopy(typesets, roottypeset, modules, typebyname, typebyc, types, currtypec);
+}
+
+setautodeclare(x: int)
+{
+ autodeclare = x;
+}
+
+quit()
+{
+ for(ts := typesets; ts != nil; ts = tl ts)
+ if((hd ts).c != nil)
+ (hd ts).c <-= nil;
+ delmods(modules);
+}
+
+delmods(mods: ref Strhash[ref Module])
+{
+ for(i := 0; i < len mods.items; i++){
+ for(l := mods.items[i]; l != nil; l = tl l){
+ m := (hd l).t1;
+ if(--m.refcount == 0){
+ if(m.c != nil){
+ m.c <-= nil;
+ m.c = nil;
+ }else if(m.defmods != nil)
+ delmods(m.defmods);
+ else if(m.m != nil){
+ m.m->quit();
+ m.m = nil;
+ }
+ }
+ }
+ }
+}
+
+# XXX could do some more checking to see whether it looks vaguely like
+# a valid alphabet expression.
+parse(expr: string): (ref Sh->Cmd, string)
+{
+ return sh->parse(expr);
+}
+
+eval(expr: ref Sh->Cmd,
+ drawctxt: ref Draw->Context,
+ args: list of ref Value): string
+{
+ spawn reports->reportproc(reportc := chan of string, nil, reply := chan of ref Report);
+ r := <-reply;
+ reply = nil;
+ stderr := sys->fildes(2);
+ spawn eval0(expr, "/status", drawctxt, r, reports->r.start("eval"), args, vc := chan of ref Value);
+ reports->r.enable();
+ v: ref Value;
+wait:
+ for(;;)alt{
+ v = <-vc =>
+ if(v != nil)
+ v.r().i <-= nil;
+ msg := <-reportc =>
+ if(msg == nil)
+ break wait;
+ sys->fprint(stderr, "alphabet: %s\n", msg);
+ }
+ # we'll always get the value before the report ends.
+ if(v == nil)
+ return "no value";
+ return <-v.r().i;
+}
+
+eval0(expr: ref Sh->Cmd,
+ dsttype: string,
+ drawctxt: ref Draw->Context,
+ r: ref Report,
+ errorc: chan of string,
+ args: list of ref Value,
+ vc: chan of ref Value)
+{
+ c: Eval->Context[ref Value, ref Module, ref Evalctxt];
+ ctxt := ref Evalctxt(modules, drawctxt, r);
+ tc := -1;
+ if(dsttype != nil && (tc = Module.typename2c(dsttype)) == -1){
+ report(errorc, "error: unknown type "+dsttype);
+ vc <-= nil;
+ reports->quit(errorc);
+ }
+
+ v := c.eval(expr, ctxt, errorc, args);
+ if(tc != -1)
+ v = Module.cvt(ctxt, v, tc, errorc);
+ vc <-= v;
+ reports->quit(errorc);
+}
+
+define(name: string, expr: ref Sh->Cmd, errorc: chan of string): string
+{
+ if(name == nil || name[0] == '/')
+ return "bad module name";
+ m := modules.find(name);
+ if(m != nil)
+ return "module already declared";
+ sig: string;
+ used: ref Strhash[ref Module];
+ used = used.new(11, nil);
+ (expr, sig) = rewrite0(expr, -1, errorc, used);
+ if(sig == nil)
+ return "cannot rewrite";
+ modules.add(name, ref Module(name, roottypeset, sig, nil, nil, expr, used, 1));
+ return nil;
+}
+
+typecompat(t0, t1: string): (int, string)
+{
+ m: ref Module;
+ (sig0, err) := evalmod->usage2sig(m, t0);
+ if(err != nil)
+ return (0, sys->sprint("bad usage %q: %s", t0, err));
+ sig1: string;
+ (sig1, err) = evalmod->usage2sig(m, t1);
+ if(err != nil)
+ return (0, sys->sprint("bad usage %q: %s", t1, err));
+ return (evalmod->typecompat(sig0, sig1), nil);
+}
+
+rewrite(expr: ref Sh->Cmd, dsttype: string, errorc: chan of string): (ref Sh->Cmd, string)
+{
+ v: ref Value;
+ tc := -1;
+ if(dsttype != nil){
+ tc = Module.typename2c(dsttype);
+ if(tc == -1){
+ report(errorc, "error: unknown type "+dsttype);
+ return (nil, nil);
+ }
+ }
+ sig: string;
+ (expr, sig) = rewrite0(expr, tc, errorc, nil);
+ if(sig == nil)
+ return (nil, nil);
+
+ return (expr, evalmod->cmdusage(v, sig));
+}
+
+# XXX different kinds of rewrite:
+# could rewrite forcing all names to qualified
+# or just leave names as they are.
+
+# return (expr, sig).
+# add all modules used by the expression to mods if non-nil.
+rewrite0(expr: ref Sh->Cmd, tc: int, errorc: chan of string, used: ref Strhash[ref Module]): (ref Sh->Cmd, string)
+{
+ m: ref Rmodule;
+ ctxt := ref Revalctxt(modules, used, 1, nil);
+ (sig, err) := evalmod->blocksig(m, ctxt, expr);
+ if(sig == nil){
+ report(errorc, "error: cannot get expr type: "+err);
+ return (nil, nil);
+ }
+ args: list of ref Rvalue;
+ for(i := len sig - 1; i >= 1; i--)
+ args = ref Rvalue(mk(-1, nil, nil), sig[i], 1, nil, nil) :: args; # N.Vb. cmd node is never used.
+
+ c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
+ v := c.eval(expr, ctxt, errorc, args);
+ if(v != nil && tc != -1)
+ v = Rmodule.cvt(ctxt, v, tc, errorc);
+ if(v == nil)
+ return (nil, nil);
+ sig[0] = v.tc;
+ v.refcount++;
+ expr = gen(v, ref Renv(nil, 0));
+ if(len sig > 1){
+ t := mkw(Value.type2s(sig[1]));
+ for(i = 2; i < len sig; i++)
+ t = mk(n_ADJ, t, mkw(Value.type2s(sig[i])));
+ expr = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, t, nil), expr.left), nil);
+ }
+ return (expr, sig);
+}
+
+# generate the expression that gave rise to v.
+# it puts in parentenv any values referred to externally.
+gen(v: ref Rvalue, parentenv: ref Renv): ref Sh->Cmd
+{
+ v.refcount--;
+ if(v.refcount > 0)
+ return mk(n_VAR, mkw(string addenv(parentenv, v)), nil);
+ c := v.i;
+ (opts, args) := (v.opts, v.args);
+ if(opts == nil && args == nil)
+ return c;
+ env := parentenv;
+ if(genblock := needblock(v))
+ env = ref Renv(nil, 0);
+ for(; opts != nil; opts = tl opts){
+ c = mk(n_ADJ, c, mkw(sys->sprint("-%c", (hd opts).t0)));
+ for(a := (hd opts).t1; a != nil; a = tl a)
+ c = mk(n_ADJ, c, gen(hd a, env));
+ }
+ if(args != nil && len (hd args).i.word > 1 && (hd args).i.word[0] == '-')
+ c = mk(n_ADJ, c, mkw("--")); # XXX potentially dodgy; some sigs don't interpret "--"?
+
+ # use pipe notation when possible
+ arg0: ref Sh->Cmd;
+ if(args != nil){
+ if((arg0 = gen(hd args, env)).ntype != n_BLOCK){
+ c = mk(n_ADJ, c, arg0);
+ arg0 = nil;
+ }
+ args = tl args;
+ }
+ for(; args != nil; args = tl args)
+ c = mk(n_ADJ, c, gen(hd args, env));
+ if(arg0 != nil)
+ c = mk(n_PIPE, arg0.left, c);
+ if(genblock){
+ args = rev(env.items);
+ m := mkw(Value.type2s((hd args).tc));
+ for(a := tl args; a != nil; a = tl a)
+ m = mk(n_ADJ, m, mkw(Value.type2s((hd a).tc)));
+ c = mk(n_BLOCK, mk(n_SEQ, mk(n_LIST, m, nil), c), nil);
+ return gen(ref Rvalue(c, v.tc, 1, nil, args), parentenv);
+ }
+ return mk(n_BLOCK, c, nil);
+}
+
+addenv(env: ref Renv, v: ref Rvalue): int
+{
+ for(i := env.items; i != nil; i = tl i)
+ if(hd i == v)
+ return len i;
+ env.items = v :: env.items;
+ v.refcount++;
+ return ++env.n;
+}
+
+# need a new block if we have any duplicated values we can resolve locally.
+# i.e. for a particular value, if we're the only thing pointing to that value
+# and its refcount is > 1 to start with.
+needblock(v: ref Rvalue): int
+{
+ dups := getdups(v, nil);
+ for(d := dups; d != nil; d = tl d)
+ --(hd d).refcount;
+ r := 0;
+ for(d = dups; d != nil; d = tl d)
+ if((hd d).refcount++ == 0)
+ r = 1;
+ return r;
+}
+
+# find all values which need $ referencing (but don't go any deeper)
+getdups(v: ref Rvalue, onto: list of ref Rvalue): list of ref Rvalue
+{
+ if(v.refcount > 1)
+ return v :: onto;
+ for(o := v.opts; o != nil; o = tl o)
+ for(a := (hd o).t1; a != nil; a = tl a)
+ onto = getdups(hd a, onto);
+ for(a = v.args; a != nil; a = tl a)
+ onto = getdups(hd a, onto);
+ return onto;
+}
+
+loadtypeset(qname: string, c: chan of ref Typescmd[ref Value], errorc: chan of string): string
+{
+ tsname := canon(qname);
+ if(gettypeset(tsname) != nil)
+ return nil;
+ (parent, name) := splitqname(tsname);
+ if((pts := gettypeset(parent)) == nil)
+ return "parent typeset not found";
+
+ if(pts.c != nil){
+ if(c != nil)
+ return "typecmd channel may only be provided for top-level typesets";
+ reply := chan of (chan of ref Typescmd[ref Value], string);
+ pts.c <-= ref Typescmd[ref Value].Loadtypes(name, reply);
+ err: string;
+ (c, err) = <-reply;
+ if(c == nil)
+ return err;
+ }else if(c == nil){
+ tsmod := load Mainsubtypes "/dis/alphabet/"+name+"types.dis";
+ if(tsmod == nil)
+ return sys->sprint("cannot load %q: %r", name+"types.dis");
+ c = tsmod->proxy();
+ }
+
+ reply := chan of string;
+ c <-= ref Typescmd[ref Value].Alphabet(reply);
+ a := <-reply;
+ ts := ref Typeset(tsname, c, Table[ref Type].new(7, nil), pts);
+ typesets = ts :: typesets;
+ newtypes: list of ref Type;
+ for(i := 0; i < len a; i++){
+ tc := a[i];
+ if((t := ts.parent.gettype(tc)) == nil){
+ t = ref Type(-1, -1, nil, ts, nil, nil);
+ sreply := chan of string;
+ c <-= ref Typescmd[ref Value].Type2s(tc, sreply);
+ t.name = <-sreply;
+ # XXX check that type name is syntactically valid.
+ t.qname = mkqname(tsname, t.name);
+ if(typebyname.find(t.qname) != nil)
+ report(errorc, sys->sprint("warning: oops: typename clash on %q", t.qname));
+ else
+ typebyname.add(t.qname, t);
+ newtypes = t :: newtypes;
+ }
+ ts.types.add(tc, t);
+ }
+ id := len types;
+ types = (array[len types + len newtypes] of ref Type)[0:] = types;
+ for(; newtypes != nil; newtypes = tl newtypes){
+ types[id] = hd newtypes;
+ typebyc.add(currtypec, hd newtypes);
+ types[id].tc = currtypec++;
+ types[id].id = id;
+ id++;
+ }
+ return nil;
+}
+
+autoconvert(src, dst: string, expr: ref Sh->Cmd, errorc: chan of string): string
+{
+ tdst := typebyname.find(dst);
+ if(tdst == nil)
+ return "unknown type " + dst;
+ tsrc := typebyname.find(src);
+ if(tsrc == nil)
+ return "unknown type " + src;
+ if(tdst.typeset != tsrc.typeset && tdst.typeset != roottypeset && tsrc.typeset != roottypeset)
+ return "conversion between incompatible typesets";
+ if(expr != nil && expr.ntype == n_WORD){
+ # mod -> {(srctype); mod $1}
+ expr = mk(n_BLOCK,
+ mk(n_SEQ,
+ mk(n_LIST, mkw(src), nil),
+ mk(n_ADJ,
+ mkw(expr.word),
+ mk(n_VAR, mkw("1"), nil)
+ )
+ ),
+ nil
+ );
+ }
+
+ (e, sig) := rewrite0(expr, tdst.tc, errorc, nil);
+ if(sig == nil)
+ return "cannot rewrite transformation "+sh->cmd2string(expr);
+ if(!evalmod->typecompat(sys->sprint("%c%c", tdst.tc, tsrc.tc), sig))
+ return "incompatible module type";
+ err := addconversion(tsrc, tdst, e);
+ if(err != nil)
+ return sys->sprint("bad auto-conversion %s->%s via %s: %s",
+ tsrc.qname, tdst.qname, sh->cmd2string(expr), err);
+ return nil;
+}
+
+mk(ntype: int, left, right: ref Sh->Cmd): ref Sh->Cmd
+{
+ return ref Sh->Cmd(ntype, left, right, nil, nil);
+}
+mkw(w: string): ref Sh->Cmd
+{
+ return ref Sh->Cmd(n_WORD, nil, nil, w, nil);
+}
+
+declare(qname: string, usig: string, flags: int): string
+{
+ return declare0(qname, usig, flags).t1;
+}
+
+# declare a module.
+# if (flags&ONDEMAND), then we don't need to actually load
+# the module (although we do if (flags&CHECK) or if sig==nil,
+# in order to check or find out the type signature)
+declare0(qname: string, usig: string, flags: int): (ref Module, string)
+{
+ sig, err: string;
+ m: ref Module;
+ if(usig != nil){
+ (sig, err) = evalmod->usage2sig(m, usig);
+ if(sig == nil)
+ return (nil, "bad type sig: " + err);
+ }
+ # if not a qualified name, declare it virtually
+ if(qname != nil && qname[0] != '/'){
+ if(sig == nil)
+ return (nil, "virtual module declaration must include signature");
+ m = ref Module(qname, nil, sig, nil, nil, nil, nil, 0);
+ }else{
+ qname = canon(qname);
+ (typeset, mod) := splitqname(qname);
+ if((ts := gettypeset(typeset)) == nil)
+ return (nil, "unknown typeset");
+ if((m = modules.find(qname)) != nil){
+ if(m.typeset == ts)
+ return (m, nil);
+ return (nil, "already imported");
+ }
+ m = ref Module(mod, ts, sig, nil, nil, nil, nil, 0);
+ if(sig == nil || (flags&CHECK) || (flags&ONDEMAND)==0){
+ if((e := m.ensureloaded()) != nil)
+ return (nil, e);
+ if(flags&ONDEMAND){
+ if(m.c != nil){
+ m.c <-= nil;
+ m.c = nil;
+ }
+ m.m = nil;
+ }
+ }
+ }
+
+ modules.add(qname, m);
+ m.refcount++;
+ return (m, nil);
+}
+
+undeclare(name: string): string
+{
+ m := modules.find(name);
+ if(m == nil)
+ return "module not declared";
+ modules.del(name);
+ if(--m.refcount == 0){
+ if(m.c != nil){
+ m.c <-= nil;
+ m.c = nil;
+ }else if(m.defmods != nil){
+ delmods(m.defmods);
+ }
+ }
+ return nil;
+}
+
+# get info on a module.
+# return (qname, usage, def)
+getmodule(name: string): (string, string, ref Sh->Cmd)
+{
+ (qname, sig, def) := getmodule0(name);
+ if(sig == nil)
+ return (qname, sig, def);
+ v: ref Value;
+ return (qname, evalmod->cmdusage(v, sig), def);
+}
+
+getmodule0(name: string): (string, string, ref Sh->Cmd)
+{
+ m: ref Module;
+ if(name != nil && name[0] != '/'){
+ if((m = modules.find(name)) == nil)
+ return (nil, nil, nil);
+ # XXX could add path searching here.
+ }else{
+ name = canon(name);
+ (typeset, mod) := splitqname(name);
+ if((m = modules.find(name)) == nil){
+ if(autodeclare == 0)
+ return (nil, nil, nil);
+ ts := gettypeset(typeset);
+ if(ts == nil)
+ return (nil, nil, nil);
+ m = ref Module(mod, ts, nil, nil, nil, nil, nil, 0);
+ if((e := m.ensureloaded()) != nil)
+ return (nil, nil, nil);
+ if(m.c != nil)
+ m.c <-= nil;
+ }
+ }
+
+ qname := m.modname;
+ if(m.def == nil && m.typeset != nil)
+ qname = mkqname(m.typeset.name, qname);
+ return (qname, m.sig, m.def);
+}
+
+getmodules(): list of string
+{
+ r: list of string;
+ for(i := 0; i < len modules.items; i++)
+ for(ml := modules.items[i]; ml != nil; ml = tl ml)
+ r = (hd ml).t0 :: r;
+ return r;
+}
+
+#Cmpdeclts: adt {
+# gt: fn(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset): int
+#};
+#Cmpdeclts.gt(nil: self ref Cmpdeclts, d1, d2: ref Decltypeset)
+#{
+# return d1.name > d2.name;
+#}
+#Cmpstring: adt {
+# gt: fn(nil: self ref Cmpdeclts, d1, d2: string): int
+#};
+#Cmpstring.gt(nil: self ref Cmpstring, d1, d2: string): int
+#{
+# return d1 > d2;
+#}
+#Cmptype: adt {
+# gt: fn(nil: self ref Cmptype, d1, d2: ref Type): int
+#};
+#Cmptype.gt(nil: self ref Cmptype, d1, d2: ref Type): int
+#{
+# return d1.name > d2.name;
+#}
+#
+#getdecls(): ref Declarations
+#{
+# cmptype: ref Cmptype;
+# d := ref Declarations(array[len typesets] of ref Decltypeset);
+# i := 0;
+# ta := array[len types] of ref Type;
+# for(tsl := typesets; tsl != nil; tsl = tl tsl){
+# t := hd tsl;
+# ts := ref Decltypeset;
+# ts.name = t.name;
+#
+# # all types in the typeset, in alphabetical order.
+# j := 0;
+# for(k := 0; k < len t.types.items; k++)
+# for(tt := t.types.items[k]; tt != nil; tt = tl tt)
+# ta[j++] = hd tt;
+# sort(cmptype, ta[0:j]);
+# ts.types = array[j] of string;
+# for(k = 0; k < j; k++){
+# ts.types[k] = ta[k].name;
+# ts.alphabet[k] = ta[k].tc;
+# }
+#
+# # all modules in the typeset
+# c := gettypesetmodules(ts.name);
+# while((m := <-c) != nil){
+#
+#
+# d.types = array[len types] of string;
+# for(i := 0; i < len types; i++){
+# d.alphabet[i] = types[i].tc;
+# d.types[i] = types[i].qname;
+# }
+#
+
+gettypesetmodules(tsname: string): chan of string
+{
+ ts := gettypeset(tsname);
+ if(ts == nil)
+ return nil;
+ r := chan of string;
+ if(ts.c == nil)
+ spawn mainmodules(r);
+ else
+ ts.c <-= ref Typescmd[ref Value].Modules(r);
+ return r;
+}
+
+mainmodules(r: chan of string)
+{
+ if((readdir := load Readdir Readdir->PATH) != nil){
+ (a, nil) := readdir->init("/dis/alphabet/main", Readdir->NAME|Readdir->COMPACT);
+ for(i := 0; i < len a; i++){
+ m := a[i].name;
+ if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis")
+ r <-= m[0:len m - 4];
+ }
+ }
+ r <-= nil;
+}
+
+gettypes(ts: string): list of string
+{
+ r: list of string;
+ for(i := 0; i < len types; i++){
+ if(ts == nil)
+ r = Value.type2s(types[i].tc) :: r;
+ else if (types[i].typeset.name == ts)
+ r = types[i].name :: r;
+ }
+ return r;
+}
+
+gettypesets(): list of string
+{
+ r: list of string;
+ for(t := typesets; t != nil; t = tl t)
+ r = (hd t).name :: r;
+ return r;
+}
+
+getautoconversions(): list of (string, string, ref Sh->Cmd)
+{
+ cl: list of (string, string, ref Sh->Cmd);
+ for(i := 0; i < len types; i++){
+ if(types[i] == nil)
+ continue;
+ srct := Value.type2s(types[i].tc);
+ for(l := types[i].transform; l != nil; l = tl l)
+ cl = (srct, Value.type2s(types[(hd l).dst].tc), (hd l).expr) :: cl;
+ }
+ return cl;
+}
+
+importmodule(qname: string): string
+{
+ qname = canon(qname);
+ (typeset, mod) := splitqname(qname);
+ if(typeset == nil)
+ return "unknown typeset";
+ if((m := modules.find(mod)) != nil){
+ if(m.typeset == nil)
+ return "already defined";
+ if(m.typeset.name == typeset)
+ return nil;
+ return "already imported from "+m.typeset.name;
+ }
+ if((m = modules.find(qname)) == nil){
+ if(autodeclare == 0)
+ return "module not declared";
+ err: string;
+ (m, err) = Module.find(nil, qname);
+ if(m == nil)
+ return "cannot import: "+ err;
+ modules.add(qname, m);
+ m.refcount++;
+ }
+ modules.add(mod, m);
+ return nil;
+}
+
+
+gettypeset(name: string): ref Typeset
+{
+ name = canon(name);
+ for(l := typesets; l != nil; l = tl l)
+ if((hd l).name == name)
+ break;
+ if(l == nil)
+ return nil;
+ return hd l;
+}
+
+importtype(qname: string): string
+{
+ qname = canon(qname);
+ (typeset, tname) := splitqname(qname);
+ if((ts := gettypeset(typeset)) == nil)
+ return "unknown typeset";
+ t := typebyname.find(tname);
+ if(t != nil){
+ if(t.typeset == ts)
+ return nil;
+ return "type already imported from " + t.typeset.name;
+ }
+ t = typebyname.find(qname);
+ if(t == nil)
+ return sys->sprint("%s does not hold type %s", typeset, tname);
+ typebyname.add(tname, t);
+ return nil;
+}
+
+importvalue(v: ref Value, tname: string): (ref Value, string)
+{
+ if(v == nil || tagof v != tagof Value.Vz)
+ return (v, nil);
+ if(tname == nil || tname[0] == '/')
+ tname = canon(tname);
+ t := typebyname.find(tname);
+ if(t == nil)
+ return (nil, "no such type");
+ pick xv := v {
+ Vz =>
+ if(t.typeset.types.find(xv.i.typec) != t)
+ return (nil, "value appears to be of different type");
+ xv.i.typec = t.tc;
+ }
+ return (v, nil);
+}
+
+gettype(tc: int): ref Type
+{
+ return typebyc.find(tc);
+}
+
+Typeset.gettype(ts: self ref Typeset, tc: int): ref Type
+{
+ return ts.types.find(tc);
+}
+
+Module.find(ctxt: ref Evalctxt, name: string): (ref Module, string)
+{
+ mods := modules;
+ if(ctxt != nil)
+ mods = ctxt.modules;
+ m := mods.find(name);
+ if(m == nil){
+ if(autodeclare == 0 || name == nil || name[0] != '/')
+ return (nil, "module not declared");
+ err: string;
+ (m, err) = declare0(name, nil, 0);
+ if(m == nil)
+ return (nil, err);
+ }else if((err := m.ensureloaded()) != nil)
+ return (nil, err);
+ return (m, nil);
+}
+
+Module.ensureloaded(m: self ref Module): string
+{
+ if(m.c != nil || m.m != nil || m.def != nil || m.typeset == nil)
+ return nil;
+
+ sig: string;
+ if(m.typeset.c == nil){
+ p := "/dis/alphabet/main/" + m.modname + ".dis";
+ mod := load Mainmodule p;
+ if(mod == nil)
+ return sys->sprint("cannot load %q: %r", p);
+ {
+ mod->init();
+ } exception e {
+ "fail:*" =>
+ return sys->sprint("init %q failed: %s", m.modname, e[5:]);
+ }
+ m.m = mod;
+ sig = mod->typesig();
+ }else{
+ reply := chan of (chan of ref Modulecmd[ref Value], string);
+ m.typeset.c <-= ref Typescmd[ref Value].Load(m.modname, reply);
+ (mc, err) := <-reply;
+ if(mc == nil)
+ return sys->sprint("cannot load: %s", err);
+ m.c = mc;
+ sig = gettypesig(m);
+ }
+ if(m.sig == nil)
+ m.sig = sig;
+ else if(!evalmod->typecompat(m.sig, sig)){
+ v: ref Value;
+ if(m.c != nil){
+ m.c <-= nil;
+ m.c = nil;
+ }
+ m.m = nil;
+ return sys->sprint("%q not compatible with %q (%q vs %q, %d)",
+ m.modname+" "+evalmod->cmdusage(v, sig),
+ evalmod->cmdusage(v, m.sig), m.sig, sig, m.sig==sig);
+ }
+ return nil;
+}
+
+Module.typesig(m: self ref Module): string
+{
+ return m.sig;
+}
+
+# get the type signature of a module in its native typeset.
+# it's not valid to call this on defined or virtually declared modules.
+gettypesig(m: ref Module): string
+{
+ reply := chan of string;
+ m.c <-= ref Modulecmd[ref Value].Typesig(reply);
+ sig := <-reply;
+ origsig := sig;
+ for(i := 0; i < len sig; i++){
+ tc := sig[i];
+ if(tc == '-'){
+ i++;
+ continue;
+ }
+ if(tc != '*'){
+ t := m.typeset.gettype(sig[i]);
+ if(t == nil){
+sys->print("no type found for '%c' in sig %q\n", sig[i], origsig);
+ return nil; # XXX is it alright to break here?
+ }
+ sig[i] = t.tc;
+ }
+ }
+ return sig;
+}
+
+Module.run(m: self ref Module, ctxt: ref Evalctxt, errorc: chan of string, opts: list of (int, list of ref Value), args: list of ref Value): ref Value
+{
+ if(m.c != nil){
+ reply := chan of ref Value;
+ m.c <-= ref Modulecmd[ref Value].Run(ctxt.drawctxt, ctxt.report, errorc, opts, args, reply);
+ if((v := <-reply) != nil){
+ pick xv := v {
+ Vz =>
+ xv.i.typec = m.typeset.types.find(xv.i.typec).tc;
+ }
+ }
+ return v;
+ }else if(m.def != nil){
+ c: Eval->Context[ref Value, ref Module, ref Evalctxt];
+ return c.eval(m.def, ref Evalctxt(m.defmods, ctxt.drawctxt, ctxt.report), errorc, args);
+ }else if(m.typeset != nil){
+ v := m.m->run(ctxt.drawctxt, ctxt.report, errorc, opts, args);
+ free(opts, args, v != nil);
+ return v;
+ }
+ report(errorc, "error: cannot run a virtually declared module");
+ return nil;
+}
+
+free[V](opts: list of (int, list of V), args: list of V, used: int)
+ for{
+ V =>
+ free: fn(v: self V, used: int);
+ }
+{
+ for(; args != nil; args = tl args)
+ (hd args).free(used);
+ for(; opts != nil; opts = tl opts)
+ for(args = (hd opts).t1; args != nil; args = tl args)
+ (hd args).free(used);
+}
+
+Module.typename2c(s: string): int
+{
+ if((t := typebyname.find(s)) == nil)
+ return -1;
+ return t.tc;
+}
+
+Module.cvt(ctxt: ref Evalctxt, v: ref Value, tc: int, errorc: chan of string): ref Value
+{
+ if(v == nil)
+ return nil;
+ srctc := v.typec();
+ dstid := gettype(tc).id;
+ while((vtc := v.typec()) != tc){
+ # XXX assumes v always returns a valid typec: might that be dangerous?
+ for(l := gettype(vtc).transform; l != nil; l = tl l)
+ if((hd l).all.holds(dstid))
+ break;
+ if(l == nil){
+ report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname,
+ types[dstid].qname));
+ v.free(0);
+ return nil; # should only happen the first time.
+ }
+ t := hd l;
+ c: Eval->Context[ref Value, ref Module, ref Evalctxt];
+ nv := c.eval(t.expr, ctxt, errorc, v::nil);
+ if(nv == nil){
+ report(errorc, sys->sprint("error: autoconvert %q failed", sh->cmd2string(t.expr)));
+ return nil;
+ }
+ v = nv;
+ }
+ return v;
+}
+
+Module.mks(nil: ref Evalctxt, s: string): ref Value
+{
+ return ref Value.Vs(s);
+}
+
+Module.mkc(nil: ref Evalctxt, c: ref Sh->Cmd): ref Value
+{
+ return ref Value.Vc(c);
+}
+
+show()
+{
+ for(i := 0; i < len types; i++){
+ if(types[i] == nil)
+ continue;
+ sys->print("%s =>\n", types[i].qname);
+ for(l := types[i].transform; l != nil; l = tl l)
+ sys->print("\t%s -> %s {%s}\n", set2s((hd l).all), types[(hd l).dst].qname, sh->cmd2string((hd l).expr));
+ }
+}
+
+set2s(set: Set): string
+{
+ s := "{";
+ for(i := 0; i < len types; i++){
+ if(set.holds(i)){
+ if(len s > 1)
+ s[len s] = ' ';
+ s += types[i].qname;
+ }
+ }
+ return s + "}";
+}
+
+Value.dup(v: self ref Value): ref Value
+{
+ if(v == nil)
+ return nil;
+ pick xv := v {
+ Vr =>
+ return nil;
+ Vd =>
+ return nil;
+ Vf or
+ Vw =>
+ return nil;
+ Vz =>
+ rc := chan of ref Value;
+ gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Dup(xv, rc);
+ nv := <-rc;
+ if(nv == nil)
+ return nil;
+ if(nv == v)
+ return v;
+ pick nxv := nv {
+ Vz =>
+ if(nxv.i.typec == xv.i.typec)
+ return nxv;
+ }
+ sys->print("oh dear, invalid duplicated value from typeset %s\n", gettype(xv.i.typec).typeset.name);
+ return nil;
+ }
+ return v;
+}
+
+Value.typec(v: self ref Value): int
+{
+ pick xv := v {
+ Vc =>
+ return 'c';
+ Vs =>
+ return 's';
+ Vr =>
+ return 'r';
+ Vf =>
+ return 'f';
+ Vw =>
+ return 'w';
+ Vd =>
+ return 'd';
+ Vz =>
+ return xv.i.typec;
+ }
+}
+
+Value.typename(v: self ref Value): string
+{
+ return Value.type2s(v.typec());
+}
+
+Value.free(v: self ref Value, used: int)
+{
+ if(v == nil)
+ return;
+ pick xv := v {
+ Vr =>
+ if(!used)
+ xv.i <-= "stop";
+ Vf or
+ Vw=>
+ if(!used){
+ <-xv.i;
+ xv.i <-= nil;
+ }
+ Vd =>
+ if(!used){
+ alt{
+ xv.i.stop <-= 1 =>
+ ;
+ * =>
+ ;
+ }
+ }
+ Vz =>
+ gettype(xv.i.typec).typeset.c <-= ref Typescmd[ref Value].Free(xv, used, reply := chan of int);
+ <-reply;
+ }
+}
+
+Value.isstring(v: self ref Value): int
+{
+ return tagof v == tagof Value.Vs;
+}
+Value.gets(v: self ref Value): string
+{
+ return v.s().i;
+}
+Value.c(v: self ref Value): ref Value.Vc
+{
+ pick xv :=v {Vc => return xv;}
+ raise "type error";
+}
+Value.s(v: self ref Value): ref Value.Vs
+{
+ pick xv :=v {Vs => return xv;}
+ raise "type error";
+}
+Value.r(v: self ref Value): ref Value.Vr
+{
+ pick xv :=v {Vr => return xv;}
+ raise "type error";
+}
+Value.f(v: self ref Value): ref Value.Vf
+{
+ pick xv :=v {Vf => return xv;}
+ raise "type error";
+}
+Value.w(v: self ref Value): ref Value.Vw
+{
+ pick xv :=v {Vw => return xv;}
+ raise "type error";
+}
+Value.d(v: self ref Value): ref Value.Vd
+{
+ pick xv :=v {Vd => return xv;}
+ raise "type error";
+}
+Value.z(v: self ref Value): ref Value.Vz
+{
+ pick xv :=v {Vz => return xv;}
+ raise "type error";
+}
+
+Value.type2s(tc: int): string
+{
+ t := gettype(tc);
+ if(t == nil)
+ return "unknown";
+ if(typebyname.find(t.name) == t)
+ return t.name;
+ return t.qname;
+}
+
+Rmodule.find(ctxt: ref Revalctxt, s: string): (ref Rmodule, string)
+{
+ m := ctxt.modules.find(s);
+ if(m == nil){
+ if(autodeclare == 0 || s == nil || s[0] != '/')
+ return (nil, "module not declared");
+ if(ctxt.modules != modules)
+ return (nil, "shouldn't happen: module not found in defined block");
+ err: string;
+ (m, err) = declare0(s, nil, ONDEMAND);
+ if(m == nil)
+ return (nil, err);
+ }
+ return (ref Rmodule(m), nil);
+}
+
+Rmodule.cvt(ctxt: ref Revalctxt, v: ref Rvalue, tc: int, errorc: chan of string): ref Rvalue
+{
+ if(v == nil)
+ return nil;
+ srctc := v.typec();
+ dstid := gettype(tc).id;
+ while((vtc := v.typec()) != tc){
+ # XXX assumes v always returns a valid typec: might that be dangerous?
+ for(l := gettype(vtc).transform; l != nil; l = tl l)
+ if((hd l).all.holds(dstid))
+ break;
+ if(l == nil){
+ report(errorc, sys->sprint("error: no way to get from %s to %s", gettype(v.typec()).qname,
+ types[dstid].qname));
+ return nil; # should only happen the first time.
+ }
+ t := hd l;
+ c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
+ v = c.eval(t.expr, ctxt, errorc, v::nil);
+ }
+ return v;
+}
+
+Rmodule.typesig(m: self ref Rmodule): string
+{
+ return m.m.sig;
+}
+
+Rmodule.typename2c(name: string): int
+{
+ return Module.typename2c(name);
+}
+
+Rmodule.mks(ctxt: ref Revalctxt, s: string): ref Rvalue
+{
+ v := ref Rvalue(mkw(s), 's', 0, nil, nil);
+ ctxt.vals = v :: ctxt.vals;
+ return v;
+}
+
+Rmodule.mkc(ctxt: ref Revalctxt, c: ref Sh->Cmd): ref Rvalue
+{
+ v := ref Rvalue(mk(n_BQ2, c, nil), 'c', 0, nil, nil);
+ ctxt.vals = v :: ctxt.vals;
+ return v;
+}
+
+Rmodule.run(m: self ref Rmodule, ctxt: ref Revalctxt, errorc: chan of string,
+ opts: list of (int, list of ref Rvalue), args: list of ref Rvalue): ref Rvalue
+{
+ if(ctxt.defs && m.m.def != nil){
+ c: Eval->Context[ref Rvalue, ref Rmodule, ref Revalctxt];
+ nctxt := ref Revalctxt(m.m.defmods, ctxt.used, ctxt.defs, ctxt.vals);
+ v := c.eval(m.m.def, nctxt, errorc, args);
+ ctxt.vals = nctxt.vals;
+ return v;
+ }
+ name := mkqname(m.m.typeset.name, m.m.modname);
+ if(ctxt.used != nil){
+ ctxt.used.add(name, m.m);
+ m.m.refcount++;
+ }
+ v := ref Rvalue(mkw(name), m.m.sig[0], 0, opts, args);
+ if(args == nil && opts == nil)
+ v.i = mk(n_BLOCK, v.i, nil);
+ for(; args != nil; args = tl args)
+ (hd args).refcount++;
+ for(; opts != nil; opts = tl opts)
+ for(args = (hd opts).t1; args != nil; args = tl args)
+ (hd args).refcount++;
+ ctxt.vals = v :: ctxt.vals;
+ return v;
+}
+
+Rvalue.dup(v: self ref Rvalue): ref Rvalue
+{
+ return v;
+}
+
+Rvalue.free(nil: self ref Rvalue, nil: int)
+{
+ # XXX perhaps there should be some way of finding out whether a particular
+ # type will allow duplication of values or not.
+}
+
+Rvalue.isstring(v: self ref Rvalue): int
+{
+ return v.tc == 's';
+}
+
+Rvalue.gets(t: self ref Rvalue): string
+{
+ return t.i.word;
+}
+
+Rvalue.type2s(tc: int): string
+{
+ return Value.type2s(tc);
+}
+
+Rvalue.typec(t: self ref Rvalue): int
+{
+ return t.tc;
+}
+
+addconversion(src, dst: ref Type, expr: ref Sh->Cmd): string
+{
+ # allow the same transform to be added again
+ for(l := src.transform; l != nil; l = tl l)
+ if((hd l).all.holds(dst.id)){
+ if((hd l).dst == dst.id && sh->cmd2string((hd l).expr) == sh->cmd2string(expr))
+ return nil;
+ }
+
+ reached := array[len types/8+1] of {* => byte 0};
+ if((at := ambiguous(dst, reached)) != nil)
+ return sys->sprint("ambiguity: %s", at);
+
+ src.transform = ref Transform(dst.id, sets->bytes2set(reached), expr) :: src.transform;
+ # check we haven't created ambiguity in nodes that point to src.
+ for(i := 0; i < len types; i++){
+ for(l = types[i].transform; l != nil; l = tl l){
+ if((hd l).all.holds(src.id) && (at = ambiguous(types[i], array[len types/8+1] of {* => byte 0})) != nil){
+ src.transform = tl src.transform;
+ return sys->sprint("ambiguity: %s", at);
+ }
+ }
+ }
+ all := (Sets->None).add(dst.id);
+ for(l = types[dst.id].transform; l != nil; l = tl l)
+ all = all.X(Sets->A|Sets->B, (hd l).all);
+ # add everything pointed to by dst to the all sets of those types
+ # that had previously pointed (indirectly) to src
+ for(i = 0; i < len types; i++)
+ for(l = types[i].transform; l != nil; l = tl l)
+ if((hd l).all.holds(src.id))
+ (hd l).all = (hd l).all.X(Sets->A|Sets->B, all);
+ return nil;
+}
+
+ambiguous(t: ref Type, reached: array of byte): string
+{
+ if((dt := ambiguous1(t, reached)) == nil)
+ return nil;
+ (nil, at) := findambiguous(t, dt, array[len reached] of {* =>byte 0}, "self "+types[t.id].qname);
+ s := hd at;
+ for(at = tl at; at != nil; at = tl at)
+ s += ", " + hd at;
+ return s;
+}
+
+# a conversion is ambiguous if there's more than one
+# way of reaching the same type.
+# return the type at which the ambiguity is found.
+ambiguous1(t: ref Type, reached: array of byte): ref Type
+{
+ if(bsetholds(reached, t.id))
+ return t;
+ bsetadd(reached, t.id);
+ for(l := t.transform; l != nil; l = tl l)
+ if((at := ambiguous1(types[(hd l).dst], reached)) != nil)
+ return at;
+ return nil;
+}
+
+findambiguous(t: ref Type, dt: ref Type, reached: array of byte, s: string): (int, list of string)
+{
+ a: list of string;
+ if(t == dt)
+ a = s :: nil;
+ if(bsetholds(reached, t.id))
+ return (1, a);
+ bsetadd(reached, t.id);
+ for(l := t.transform; l != nil; l = tl l){
+ (found, at) := findambiguous(types[(hd l).dst], dt, reached,
+ sys->sprint("%s|%s", s, sh->cmd2string((hd l).expr))); # XXX rewite correctly
+ for(; at != nil; at = tl at)
+ a = hd at :: a;
+ if(found)
+ return (1, a);
+ }
+ return (0, a);
+}
+
+bsetholds(x: array of byte, n: int): int
+{
+ return int x[n >> 3] & (1 << (n & 7));
+}
+
+bsetadd(x: array of byte, n: int)
+{
+ x[n >> 3] |= byte (1 << (n & 7));
+}
+
+mkqname(parent, child: string): string
+{
+ if(parent == "/")
+ return parent+child;
+ return parent+"/"+child;
+}
+
+# splits a canonical qname into typeset and name components.
+splitqname(name: string): (string, string)
+{
+ if(name == nil)
+ return (nil, nil);
+ for(i := len name - 1; i >= 0; i--)
+ if(name[i] == '/')
+ break;
+ if(i == 0)
+ return ("/", name[1:]);
+ return (name[0:i], name[i+1:]);
+}
+
+# compress multiple slashes into single; remove trailing slashes.
+canon(name: string): string
+{
+ if(name == nil || name[0] != '/')
+ return nil;
+
+ slash := nonslash := 0;
+ s := "";
+ for(i := 0; i < len name; i++){
+ c := name[i];
+ if(c == '/')
+ slash = 1;
+ else{
+ if(slash){
+ s[len s] = '/';
+ nonslash++;
+ slash = 0;
+ }
+ s[len s] = c;
+ }
+ }
+ if(slash && !nonslash)
+ s[len s] = '/';
+ return s;
+}
+
+report(errorc: chan of string, s: string)
+{
+ if(Debug || errorc == nil)
+ sys->fprint(sys->fildes(2), "%s\n", s);
+ if(errorc != nil)
+ errorc <-= s;
+}
+
+Table[T].new(nslots: int, nilval: T): ref Table[T]
+{
+ if(nslots == 0)
+ nslots = 13;
+ return ref Table[T](array[nslots] of list of (int, T), nilval);
+}
+
+Table[T].add(t: self ref Table[T], id: int, x: T): int
+{
+ slot := id % len t.items;
+ for(q := t.items[slot]; q != nil; q = tl q)
+ if((hd q).t0 == id)
+ return 0;
+ t.items[slot] = (id, x) :: t.items[slot];
+ return 1;
+}
+
+Table[T].del(t: self ref Table[T], id: int): int
+{
+ slot := id % len t.items;
+
+ p: list of (int, T);
+ r := 0;
+ for(q := t.items[slot]; q != nil; q = tl q){
+ if((hd q).t0 == id){
+ p = joinip(p, tl q);
+ r = 1;
+ break;
+ }
+ p = hd q :: p;
+ }
+ t.items[slot] = p;
+ return r;
+}
+
+Table[T].find(t: self ref Table[T], id: int): T
+{
+ for(p := t.items[id % len t.items]; p != nil; p = tl p)
+ if((hd p).t0 == id)
+ return (hd p).t1;
+ return t.nilval;
+}
+
+hashfn(s: string, n: int): int
+{
+ h := 0;
+ m := len s;
+ for(i:=0; i<m; i++){
+ h = 65599*h+s[i];
+ }
+ return (h & 16r7fffffff) % n;
+}
+
+Strhash[T].new(nslots: int, nilval: T): ref Strhash[T]
+{
+ if(nslots == 0)
+ nslots = 13;
+ return ref Strhash[T](array[nslots] of list of (string, T), nilval);
+}
+
+Strhash[T].add(t: self ref Strhash, id: string, x: T)
+{
+ slot := hashfn(id, len t.items);
+ t.items[slot] = (id, x) :: t.items[slot];
+}
+
+Strhash[T].del(t: self ref Strhash, id: string)
+{
+ slot := hashfn(id, len t.items);
+
+ p: list of (string, T);
+ for(q := t.items[slot]; q != nil; q = tl q)
+ if((hd q).t0 != id)
+ p = hd q :: p;
+ t.items[slot] = p;
+}
+
+Strhash[T].find(t: self ref Strhash, id: string): T
+{
+ for(p := t.items[hashfn(id, len t.items)]; p != nil; p = tl p)
+ if((hd p).t0 == id)
+ return (hd p).t1;
+ return t.nilval;
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
+
+# join x to y, leaving result in arbitrary order.
+join[T](x, y: list of T): list of T
+{
+ if(len x > len y)
+ (x, y) = (y, x);
+ for(; x != nil; x = tl x)
+ y = hd x :: y;
+ return y;
+}
+
+# join x to y, leaving result in arbitrary order.
+joinip[T](x, y: list of (int, T)): list of (int, T)
+{
+ if(len x > len y)
+ (x, y) = (y, x);
+ for(; x != nil; x = tl x)
+ y = hd x :: y;
+ return y;
+}
+
+sort[S, T](s: S, a: array of T)
+ for{
+ S =>
+ gt: fn(s: self S, x, y: T): int;
+ }
+{
+ mergesort(s, a, array[len a] of T);
+}
+
+mergesort[S, T](s: S, a, b: array of T)
+ for{
+ S =>
+ gt: fn(s: self S, x, y: T): int;
+ }
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(s, a[0:m], b[0:m]);
+ mergesort(s, a[m:], b[m:]);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if(s.gt(b[i], b[j]))
+ 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];
+ }
+}
diff --git a/appl/alphabet/alphabet.proto b/appl/alphabet/alphabet.proto
new file mode 100644
index 00000000..2c005396
--- /dev/null
+++ b/appl/alphabet/alphabet.proto
@@ -0,0 +1,29 @@
+# -{/fs/proto alphabet.proto | /fs/filter {/fs/or {/fs/path /dis} {/fs/not {/fs/or *.dis *.sbl}}} | /fs/write /tmp/blah}
+# -{/fs/proto alphabet.proto | /fs/filter {/fs/not {/fs/or *.dis *.sbl}} | /fs/select {/fs/mode -d}}
+module
+ alphabet.m
+ alphabet
+ +
+dis
+ sh
+ alphabet.dis
+ alphabet
+ +
+ scheduler
+ workflowgen.dis
+appl
+ alphabet
+ +
+ cmd
+ scheduler
+ workflowgen.b
+ tgsimple.b
+ mkfile
+man
+ 1
+ sh-alphabet
+ alphabet-main
+ alphabet-grid
+ alphabet-fs
+ 2
+ alphabet-intro
diff --git a/appl/alphabet/alphabet.shmod.b b/appl/alphabet/alphabet.shmod.b
new file mode 100644
index 00000000..cbe2cbd9
--- /dev/null
+++ b/appl/alphabet/alphabet.shmod.b
@@ -0,0 +1,413 @@
+implement Alphabetsh, Shellbuiltin;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context, Listnode: import sh;
+ n_WORD: import sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ report, Report: import reports;
+include "readdir.m";
+ readdir: Readdir;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value, CHECK, ONDEMAND: import alphabet;
+include "alphabet/abc.m";
+
+Alphabetsh: module {};
+
+myself: Shellbuiltin;
+
+initbuiltin(ctxt: ref Sh->Context, shmod: Sh): string
+{
+ sys = load Sys Sys->PATH;
+ myself = load Shellbuiltin "$self";
+ sh = shmod;
+ if (myself == nil)
+ ctxt.fail("bad module", sys->sprint("file2chan: cannot load self: %r"));
+
+ alphabet = load Alphabet Alphabet->PATH;
+ if(alphabet == nil)
+ ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH));
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Reports->PATH));
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil)
+ ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Readdir->PATH));
+
+ alphabet->init();
+ alphabet->setautodeclare(1);
+
+ if((decls := ctxt.get("autodeclares")) != nil){
+ for(; decls != nil; decls = tl decls){
+ d := hd decls;
+ if(d.cmd == nil){
+ err: string;
+ (d.cmd, err) = sh->parse(d.word);
+ if(err != nil){
+ sys->fprint(sys->fildes(2), "alphabet: warning: bad autodeclaration: %s\n", err);
+ continue;
+ }
+ }
+ {
+ declares(ctxt, nil::d::nil);
+ }exception{
+ "fail:*" =>
+ ;
+ }
+ }
+ }
+
+ ctxt.addbuiltin("declare", myself);
+ ctxt.addbuiltin("declares", myself);
+ ctxt.addbuiltin("undeclare", myself);
+ ctxt.addbuiltin("define", myself);
+ ctxt.addbuiltin("import", myself);
+ ctxt.addbuiltin("autodeclare", myself);
+ ctxt.addbuiltin("type", myself);
+ ctxt.addbuiltin("typeset", myself);
+ ctxt.addbuiltin("autoconvert", myself);
+ ctxt.addbuiltin("-", myself);
+ ctxt.addbuiltin("info", myself);
+ ctxt.addbuiltin("clear", myself);
+
+# ctxt.addsbuiltin("-", myself);
+ ctxt.addsbuiltin("rewrite", myself);
+ ctxt.addsbuiltin("modules", myself);
+ ctxt.addsbuiltin("types", myself);
+ ctxt.addsbuiltin("usage", myself);
+ return nil;
+}
+
+runbuiltin(c: ref Sh->Context, nil: Sh,
+ cmd: list of ref Listnode, nil: int): string
+{
+ case (hd cmd).word {
+ "declare" =>
+ return declare(c, cmd);
+ "declares" =>
+ return declares(c, cmd);
+ "undeclare" =>
+ return undeclare(c, cmd);
+ "define" =>
+ return define(c, cmd);
+ "import" =>
+ return importf(c, cmd);
+ "type" =>
+ return importtype(c, cmd);
+ "typeset" =>
+ return typeset(c, cmd);
+ "autoconvert" =>
+ return autoconvert(c, cmd);
+ "autodeclare" =>
+ if(len cmd != 2)
+ usage(c, "usage: autodeclare 0/1");
+ alphabet->setautodeclare(int word(hd tl cmd));
+ "info" =>
+ return info(c, cmd);
+ "clear" =>
+ a := load Alphabet Alphabet->PATH;
+ if(a == nil)
+ c.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Alphabet->PATH));
+ alphabet->quit();
+ alphabet = a;
+ alphabet->init();
+ alphabet->setautodeclare(1);
+ "-" =>
+ return eval(c, cmd);
+ }
+ return nil;
+}
+
+whatis(nil: ref Sh->Context, nil: Sh, mod: string, wtype: int): string
+{
+ if(wtype == OTHER){
+ (qname, sig, def) := alphabet->getmodule(mod);
+ if(qname == nil)
+ return nil;
+ s := sys->sprint("declare %q %q", qname, sig);
+ if(def != nil){
+ for(i := len sig-1; i >= 0; i--){
+ if(sig[i] == '>'){
+ sig = sig[0:i-1];
+ break;
+ }
+ }
+ s += sys->sprint("; define %q {(%s); %s}", qname, sig, sh->cmd2string(def));
+ }
+ return s;
+ }
+ return nil;
+}
+
+getself(): Shellbuiltin
+{
+ return myself;
+}
+
+runsbuiltin(ctxt: ref Context, nil: Sh,
+ argv: list of ref Listnode): list of ref Listnode
+{
+ case (hd argv).word {
+ "rewrite" =>
+ return rewrite(ctxt, argv);
+ "modules" =>
+ return sh->stringlist2list(alphabet->getmodules());
+ "types" =>
+ ts := "";
+ if(tl argv != nil)
+ ts = word(hd tl argv);
+ r := sh->stringlist2list(alphabet->gettypes(ts));
+ if(r == nil)
+ ctxt.fail("error", sys->sprint("unknown typeset %q", ts));
+ return r;
+ "usage" =>
+ if(len argv != 2)
+ usage(ctxt, "usage qname");
+ (qname, u, nil) := alphabet->getmodule(word(hd tl argv));
+ if(qname == nil)
+ ctxt.fail("error", "module not declared");
+ return ref Listnode(nil, u) :: nil;
+ }
+ return nil;
+}
+
+usage(ctxt: ref Context, s: string)
+{
+ ctxt.fail("usage", "usage: " + s);
+}
+
+declares(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(argv == nil || (hd argv).cmd == nil)
+ ctxt.fail("usage", "usage: declares decls");
+ decls := (hd argv).cmd;
+ declares := load Declares Declares->PATH;
+ if(declares == nil)
+ ctxt.fail("bad module", sys->sprint("alphabet: cannot load %q: %r", Declares->PATH));
+ {
+ declares->init();
+ } exception e {
+ "fail:*" =>
+ ctxt.fail("declares init", e[5:]);
+ }
+
+ spawn printerrors(errorc := chan of string);
+ e := declares->declares(alphabet, decls, errorc, nil);
+ declares->quit();
+ if(e != nil)
+ ctxt.fail("bad declaration", sys->sprint("alphabet: declaration failed: %s", e));
+ return nil;
+}
+
+rewrite(ctxt: ref Sh->Context, argv: list of ref Listnode): list of ref Listnode
+{
+ argv = tl argv;
+ n := len argv;
+ if(n != 1 && n != 2 || (hd argv).cmd == nil)
+ usage(ctxt, "rewrite {expr} [desttype]");
+ spawn printerrors(errorc := chan of string);
+ desttype := "";
+ if(n == 2)
+ desttype = word(hd tl argv);
+ (c, usage) := alphabet->rewrite((hd argv).cmd, desttype, errorc);
+ errorc <-= nil;
+ if(c == nil)
+ raise "fail:bad expression";
+ return (ref Listnode(c, nil) :: ref Listnode(nil, usage) :: nil);
+}
+
+# XXX add support for optional ONDEMAND and CHECK flags
+declare(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ n := len argv;
+ if(n < 1 || n > 2)
+ usage(ctxt, "declare qname [type]");
+ decltype := "";
+ if(n == 2)
+ decltype = word(hd tl argv);
+ e := alphabet->declare(word(hd argv), decltype, 0);
+ if(e != nil)
+ ctxt.fail("error", sys->sprint("cannot declare %s: %s", word(hd argv), e));
+ return nil;
+}
+
+undeclare(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(argv == nil)
+ usage(ctxt, "undeclare name...");
+ for(; argv != nil; argv = tl argv){
+ if((e := alphabet->undeclare(word(hd argv))) != nil)
+ sys->fprint(sys->fildes(2), "alphabet: cannot undeclare %q: %s\n", word(hd argv), e);
+ }
+ return nil;
+}
+
+# usage define name expr
+define(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(len argv != 2 || (hd tl argv).cmd == nil)
+ usage(ctxt, "define name {expr}");
+
+ spawn printerrors(errorc := chan of string);
+
+ err := alphabet->define((hd argv).word, (hd tl argv).cmd, errorc);
+ errorc <-= nil;
+ if(err != nil)
+ raise "fail:bad define: "+err;
+ return nil;
+}
+
+importf(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(argv == nil)
+ usage(ctxt, "import qname...");
+ errs := 0;
+ for(; argv != nil; argv = tl argv){
+ e := alphabet->importmodule(word(hd argv));
+ if(e != nil){
+ sys->fprint(sys->fildes(2), "alphabet: cannot import %s: %s\n", word(hd argv), e);
+ errs++;
+ }
+ }
+ if(errs)
+ raise "fail:import error";
+ return nil;
+}
+
+importtype(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(argv == nil)
+ usage(ctxt, "type qname...");
+ errs := 0;
+ for(; argv != nil; argv = tl argv){
+ e := alphabet->importtype(word(hd argv));
+ if(e != nil){
+ sys->fprint(sys->fildes(2), "alphabet: cannot import type %s: %s\n", word(hd argv), e);
+ errs++;
+ }
+ }
+ if(errs)
+ raise "fail:type declare error";
+ return nil;
+}
+
+typeset(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(len argv != 1)
+ usage(ctxt, "typeset qname");
+ spawn printerrors(errorc := chan of string);
+ e := alphabet->loadtypeset(word(hd argv), nil, errorc); # XXX errorc?
+ errorc <-= nil;
+ if(e != nil)
+ ctxt.fail("error", sys->sprint("cannot load typeset %q: %s", word(hd argv), e));
+ return nil;
+}
+
+autoconvert(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(len argv != 3)
+ usage(ctxt, "autoconvert src dst fn");
+ src := word(hd argv);
+ dst := word(hd tl argv);
+ expr := (hd tl tl argv).cmd;
+ if(expr == nil)
+ expr = ref Sh->Cmd(Sh->n_WORD, nil, nil, (hd tl tl argv).word, nil);
+ spawn printerrors(errorc := chan of string);
+ e := alphabet->autoconvert(src, dst, expr, errorc);
+ errorc <-= nil;
+ if(e != nil)
+ ctxt.fail("error", sys->sprint("cannot autoconvert %s to %s via %s: %s",
+ src, dst, word(hd tl tl argv), e));
+ return nil;
+}
+
+info(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ first := 1;
+ if(tl argv != nil)
+ usage(ctxt, "info");
+ for(tsl := alphabet->gettypesets(); tsl != nil; tsl = tl tsl){
+ ts := hd tsl;
+ r := alphabet->gettypesetmodules(ts);
+ if(r == nil)
+ continue;
+ if(first == 0)
+ sys->print("\n");
+ sys->print("typeset %s\n", ts);
+ while((mod := <-r) != nil){
+ (qname, u, nil) := alphabet->getmodule(ts+"/"+mod);
+ if(qname != nil)
+ sys->print("%s %s\n", qname, u);
+ }
+ first = 0;
+ }
+ acl := alphabet->getautoconversions();
+ if(acl != nil)
+ sys->print("\n");
+
+ for(; acl != nil; acl = tl acl){
+ (src, dst, via) := hd acl;
+ sys->print("autoconvert %q %q %s\n", src, dst, sh->cmd2string(via));
+ }
+ return nil;
+}
+
+eval(ctxt: ref Sh->Context, argv: list of ref Listnode): string
+{
+ argv = tl argv;
+ if(argv == nil || (hd argv).cmd == nil)
+ usage(ctxt, "- {expr} [arg...]");
+ c := (hd argv).cmd;
+ if(c == nil)
+ c = mkw((hd argv).word);
+
+
+ args: list of ref Value;
+ for(argv = tl argv; argv != nil; argv = tl argv){
+ if((hd argv).cmd != nil)
+ args = ref Value.Vc((hd argv).cmd) :: args;
+ else
+ args = ref Value.Vs((hd argv).word) :: args;
+ }
+ return alphabet->eval(c, ctxt.drawcontext, rev(args));
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
+
+word(n: ref Listnode): string
+{
+ if (n.word != nil)
+ return n.word;
+ if (n.cmd != nil)
+ n.word = sh->cmd2string(n.cmd);
+ return n.word;
+}
+
+printerrors(c: chan of string)
+{
+ while((s := <-c) != nil)
+ sys->fprint(sys->fildes(2), "e: %s\n", s);
+}
+
+mkw(w: string): ref Sh->Cmd
+{
+ return ref Sh->Cmd(n_WORD, nil, nil, w, nil);
+}
diff --git a/appl/alphabet/auxi/endpoints.b b/appl/alphabet/auxi/endpoints.b
new file mode 100644
index 00000000..5544da8b
--- /dev/null
+++ b/appl/alphabet/auxi/endpoints.b
@@ -0,0 +1,105 @@
+implement Endpoints;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "string.m";
+ str: String;
+include "sh.m";
+ sh: Sh;
+include "alphabet/endpoints.m";
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+ str = load String String->PATH;
+}
+
+DIR: con "/n/endpoint";
+
+new(nil, addr: string, force: int): string # XXX don't ignore net directory
+{
+ if(!force && sys->stat(DIR+"/"+addr+"/clone").t0 != -1)
+ return nil;
+ if((e := sh->run(nil, "mount"::"{mntgen}"::DIR::nil)) != nil)
+ return "mount mntgen failed: "+e;
+ if((e = sh->run(nil, "endpointsrv"::addr::DIR+"/"+addr::nil)) != nil)
+ return "endpoint failed: "+e;
+ if((e = sh->run(nil, "listen"::addr::"export"::DIR+"/"+addr::nil)) != nil){
+ sys->unmount(nil, DIR+"/"+addr);
+ return "listen failed: "+e;
+ }
+ return nil;
+}
+
+err(e: string): Endpoint
+{
+ return (nil, nil, e);
+}
+
+create(addr: string): (ref Sys->FD, Endpoint)
+{
+ d := DIR+"/"+addr;
+ fd := sys->open(d+"/clone", Sys->OREAD);
+ if(fd == nil)
+ return (nil, err(sys->sprint("cannot open %s/clone: %r", d)));
+
+ buf := array[1024] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n <= 0)
+ return (nil, err("read id failed"));
+ s := string buf[0:n];
+ (nt, toks) := sys->tokenize(s, " ");
+ if(nt != 2)
+ return (nil, err(sys->sprint("invalid id read %q", s)));
+ id: string;
+ (addr, id) = (hd toks, hd tl toks);
+ fd = sys->open(d+"/"+id+".in", Sys->OWRITE);
+ if(fd == nil)
+ return (nil, err(sys->sprint("cannot write to %s/%s: %r", d, id)));
+ return (fd, Endpoint(addr, id, nil));
+}
+
+open(net: string, ep: Endpoint): (ref Sys->FD, string)
+{
+ if(hasslash(ep.addr))
+ return (nil, "bad address");
+ if(hasslash(ep.id))
+ return (nil, "bad id");
+ d := DIR+"/"+ep.addr;
+ fd := sys->open(d+"/"+ep.id, Sys->OREAD);
+ if(fd != nil)
+ return (fd, nil);
+ e := sys->sprint("%r");
+ if(sys->stat(d+"/clone").t0 != -1)
+ return (nil, sys->sprint("endpoint does not exist: %s", e));
+ if((e = sh->run(nil, "mount"::"-A"::net+ep.addr::d::nil)) != nil)
+ return (nil, e);
+ fd = sys->open(d+"/"+ep.id, Sys->OREAD);
+ if(fd == nil)
+ return (nil, sys->sprint("endpoint does not exist: %r"));
+ return (fd, nil);
+}
+
+Endpoint.text(ep: self Endpoint): string
+{
+ return sys->sprint("%q %q %q", ep.addr, ep.id, ep.about);
+}
+
+Endpoint.mk(s: string): Endpoint
+{
+ t := str->unquoted(s);
+ if(len t != 3)
+ return err("invalid endpoint string");
+ # XXX could do more validation than this.
+ return (hd t, hd tl t, hd tl tl t);
+}
+
+hasslash(s: string): int
+{
+ for(i := 0; i < len s; i++)
+ if(s[i] == '/')
+ return 1;
+ return 0;
+}
diff --git a/appl/alphabet/auxi/endpointsrv.b b/appl/alphabet/auxi/endpointsrv.b
new file mode 100644
index 00000000..ea5391e5
--- /dev/null
+++ b/appl/alphabet/auxi/endpointsrv.b
@@ -0,0 +1,58 @@
+implement Endpointsrv;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+
+Endpointsrv: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ if(len argv != 3)
+ fatal("usage: endpointsrv addr [dir]");
+ addr := hd tl argv;
+ dir := hd tl tl argv;
+ if(sys->bind("#s", dir, Sys->MREPL) == -1)
+ fatal(sys->sprint("cannot bind #s onto %q: %r", dir));
+
+ fio := sys->file2chan(dir, "clone");
+ spawn endpointproc(addr, dir, fio);
+}
+
+endpointproc(addr, dir: string, fio: ref Sys->FileIO)
+{
+ n := 0;
+ for(;;) alt {
+ (offset, nil, nil, rc) := <-fio.read =>
+ if(rc != nil){
+ if(offset > 0)
+ rc <-= (nil, nil);
+ else{
+ mkpipe(dir, string n);
+ rc <-= (array of byte (addr+" "+string n++), nil);
+ }
+ }
+ (nil, nil, nil, wc) := <-fio.write =>
+ if(wc != nil)
+ wc <-= (0, "cannot write");
+ }
+}
+
+mkpipe(dir: string, p: string)
+{
+ sys->bind("#|", "/tmp", Sys->MREPL);
+ d := Sys->nulldir;
+ d.name = p;
+ sys->wstat("/tmp/data", d);
+ d.name = p + ".in";
+ sys->wstat("/tmp/data1", d);
+ sys->bind("/tmp", dir, Sys->MBEFORE);
+}
+
+fatal(e: string)
+{
+ sys->fprint(sys->fildes(2), "endpointsrv: %s\n", e);
+ raise "fail:error";
+}
diff --git a/appl/alphabet/auxi/fsfilter.b b/appl/alphabet/auxi/fsfilter.b
new file mode 100644
index 00000000..a83a5c5b
--- /dev/null
+++ b/appl/alphabet/auxi/fsfilter.b
@@ -0,0 +1,62 @@
+implement Fsfilter;
+include "sys.m";
+include "draw.m";
+include "sh.m";
+include "fslib.m";
+ Fschan, Next, Quit, Skip, Down: import Fslib;
+
+filter[T](t: T, src, dst: Fschan)
+ for{
+ T =>
+ query: fn(t: self T, d: ref Sys->Dir, name: string, depth: int): int;
+ }
+{
+ names: list of string;
+ name: string;
+ indent := 0;
+ myreply := chan of int;
+loop:
+ for(;;){
+ (d, reply) := <-src;
+ if(d.dir != nil){
+ p := name;
+ if(indent > 0){
+ if(p != nil && p[len p - 1] != '/')
+ p[len p] = '/';
+ }
+ if(t.query(d.dir, p + d.dir.name, indent) == 0 && indent > 0){
+ reply <-= Next;
+ continue;
+ }
+ }
+ dst <-= (d, myreply);
+ case reply <-= <-myreply {
+ Quit =>
+ break loop;
+ Next =>
+ if(d.dir == nil && d.data == nil){
+ if(--indent == 0)
+ break loop;
+ (name, names) = (hd names, tl names);
+ }
+ Skip =>
+ if(--indent == 0)
+ break loop;
+ (name, names) = (hd names, tl names);
+ Down =>
+ if(d.dir != nil){
+ names = name :: names;
+ if(d.dir.mode & Sys->DMDIR){
+ if(indent == 0)
+ name = d.dir.name;
+ else{
+ if(name[len name - 1] != '/')
+ name[len name] = '/';
+ name += d.dir.name;
+ }
+ }
+ indent++;
+ }
+ }
+ }
+}
diff --git a/appl/alphabet/auxi/mkfile b/appl/alphabet/auxi/mkfile
new file mode 100644
index 00000000..db753a46
--- /dev/null
+++ b/appl/alphabet/auxi/mkfile
@@ -0,0 +1,21 @@
+<../../../mkconfig
+
+TARG=\
+ endpoints.dis\
+ endpointsrv.dis\
+ rexecsrv.dis\
+ fsfilter.dis\
+
+SYSMODULES=\
+ alphabet.m\
+ alphabet/endpoints.m\
+ alphabet/reports.m\
+ draw.m\
+ sh.m\
+ string.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/alphabet
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
diff --git a/appl/alphabet/auxi/rexecsrv.b b/appl/alphabet/auxi/rexecsrv.b
new file mode 100644
index 00000000..9412d617
--- /dev/null
+++ b/appl/alphabet/auxi/rexecsrv.b
@@ -0,0 +1,301 @@
+implement Rexecsrv;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+include "alphabet/endpoints.m";
+ endpoints: Endpoints;
+ Endpoint: import endpoints;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+include "alphabet/abc.m";
+include "alphabet/abctypes.m";
+include "string.m";
+ str: String;
+
+Rexecsrv: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+drawctxt: ref Draw->Context;
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ endpoints = load Endpoints Endpoints->PATH;
+ if(endpoints == nil)
+ fatal(sys->sprint("cannot load %s: %r", Endpoints->PATH));
+ endpoints->init();
+ sh = load Sh Sh->PATH;
+ if(sh == nil)
+ fatal(sys->sprint("cannot load %s: %r", Sh->PATH));
+ sh->initialise();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ fatal(sys->sprint("cannot load %s: %r", Reports->PATH));
+ str = load String String->PATH;
+ if(str == nil)
+ fatal(sys->sprint("cannot load %s: %r", String->PATH));
+ if(len argv != 3)
+ fatal("usage: rexecsrv dir {decls}");
+ drawctxt = ctxt;
+ if(sys->stat("/n/endpoint/local/clone").t0 == -1)
+ fatal("no local endpoints available");
+ dir := hd tl argv;
+ decls := parse(hd tl tl argv);
+ if(sys->bind("#s", dir, Sys->MREPL) == -1)
+ fatal(sys->sprint("cannot bind #s onto %q: %r", dir));
+
+ alphabet = declares(decls);
+
+ fio := sys->file2chan(dir, "exec");
+ sync := chan of int;
+ spawn rexecproc(sync, fio);
+ <-sync;
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+# use one alphabet module to bootstrap another
+# with the desired declarations that we can use to
+# execute external commands.
+declares(decls: ref Sh->Cmd): Alphabet
+{
+ alphabet0 := load Alphabet Alphabet->PATH;
+ if(alphabet0 == nil)
+ fatal(sys->sprint("cannot load %s: %r", Alphabet->PATH));
+ alphabet0->init();
+ abctypes := load Abctypes Abctypes->PATH;
+ if(abctypes == nil)
+ fatal(sys->sprint("cannot load %s: %r", Abctypes->PATH));
+ Abccvt: import abctypes;
+ abc := load Abc Abc->PATH;
+ if(abc == nil)
+ fatal(sys->sprint("cannot load %s: %r", Abc->PATH));
+ abc->init();
+ Value: import abc;
+
+ (c, nil, abccvt) := abctypes->proxy0();
+
+ spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report);
+ r := <-reply;
+ if((err := alphabet0->loadtypeset("/abc", c, nil)) != nil)
+ fatal("cannot load typeset /abc: "+err);
+ alphabet0->setautodeclare(1);
+ spawn alphabet0->eval0(
+ parse("{(/cmd);"+
+ "/abc/abc |"+
+ "/abc/declares $1"+
+ "}"
+ ),
+ "/abc/abc",
+ nil,
+ r,
+ r.start("evaldecls"),
+ ref (Alphabet->Value).Vc(decls) :: nil,
+ vc := chan of ref Alphabet->Value
+ );
+ r.enable();
+ av: ref Alphabet->Value;
+wait:
+ for(;;)alt{
+ av = <-vc =>
+ ;
+ msg := <-errorc =>
+ if(msg == nil)
+ break wait;
+ sys->fprint(stderr(), "rexecsrv: %s\n", msg);
+ }
+ if(av == nil)
+ fatal("declarations failed");
+ v := abccvt.ext2int(av).dup();
+ alphabet0->av.free(1);
+ pick xv := v {
+ VA =>
+ return xv.i.alphabet;
+ }
+ return nil;
+}
+
+parse(s: string): ref Sh->Cmd
+{
+ (c, err) := sh->parse(s);
+ if(c== nil)
+ fatal(sys->sprint("cannot parse %q: %s", s, err));
+ return c;
+}
+
+lc(cmd: ref Sh->Cmd): ref Sh->Listnode
+{
+ return ref Sh->Listnode(cmd, nil);
+}
+
+lw(word: string): ref Sh->Listnode
+{
+ return ref Sh->Listnode(nil, word);
+}
+
+# write endpoints, cmd
+# read endpoints
+rexecproc(sync: chan of int, fio: ref Sys->FileIO)
+{
+ sys->pctl(Sys->FORKNS, nil);
+ pending: list of (int, string);
+ sync <-= 1;
+ for(;;) alt {
+ (nil, data, fid, wc) := <-fio.write =>
+ if(wc == nil)
+ break;
+ req := string data;
+ l := str->unquoted(req);
+ if(len l != 2 || Endpoint.mk(hd l).addr == nil){
+ wc <-= (0, "bad request");
+ break;
+ }
+ pending = (fid, req) :: pending;
+ wc <-= (0, nil);
+ (offset, nil, fid, rc) := <-fio.read =>
+ if(rc == nil){
+ (pending, nil) = removefid(fid, pending);
+ break;
+ }
+ if(offset > 0){
+ rc <-= (nil, nil);
+ break;
+ }
+ req: string;
+ (pending, req) = removefid(fid, pending);
+ if(req == nil){
+ rc <-= (nil, "no pending exec");
+ break;
+ }
+ l := str->unquoted(req);
+ spawn exec(sync1 := chan of int, Endpoint.mk(hd l), hd tl l, rc);
+ <-sync1;
+ }
+}
+
+gather(errorc: chan of string)
+{
+ s := "";
+ while((e := <-errorc) != nil)
+ s += e + "\n";
+ errorc <-= s;
+}
+
+exec(sync: chan of int, ep: Endpoint, expr: string,
+ rc: chan of (array of byte, string))
+{
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= 1;
+
+ spawn gather(errorc := chan of string);
+ (c, err) := alphabet->parse(expr);
+ if(c == nil){
+ rc <-= (nil, "parse error: "+err);
+ return;
+ }
+ usage: string;
+ (c, usage) = alphabet->rewrite(c, "/fd", errorc);
+ errorc <-= nil;
+ err = <-errorc;
+ if(c == nil){
+ rc <-= (nil, err);
+ return;
+ }
+ if(!alphabet->typecompat("/fd -> /fd", usage).t0)
+ rc <-= (nil, "incompatible type: "+usage);
+
+ fd0: ref Sys->FD;
+ (fd0, err) = endpoints->open(nil, ep);
+ if(fd0 == nil){
+ rc <-= (nil, err);
+ return;
+ }
+ (fd1, ep1) := endpoints->create("local");
+ if(fd1 == nil){
+ rc <-= (nil, "cannot make endpoints: "+ep1.about);
+ return;
+ }
+ rc <-= (array of byte ep1.text(), nil);
+
+ runcmd(c, fd0, fd1);
+}
+
+fdproc(f: chan of ref Sys->FD, fd0: ref Sys->FD)
+{
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ exit;
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0)
+ if(sys->write(fd1, buf, n) == -1)
+ break;
+}
+
+runcmd(c: ref Sh->Cmd, fd0, fd1: ref Sys->FD)
+{
+ f := chan of ref Sys->FD;
+ spawn fdproc(f, fd0);
+
+ spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report);
+ r := <-reply;
+ spawn alphabet->eval0(
+ c,
+ "/fd",
+ drawctxt,
+ r,
+ r.start("evalcmd"),
+ ref (Alphabet->Value).Vf(f) :: nil,
+ vc := chan of ref Alphabet->Value
+ );
+ r.enable();
+ av: ref Alphabet->Value;
+wait:
+ for(;;)alt{
+ av = <-vc =>
+ if(av == nil){
+ sys->fprint(stderr(), "rexecsrv: no value received\n");
+ break;
+ }
+ pick v := av {
+ Vf =>
+ <-v.i;
+ v.i <-= fd1;
+ * =>
+ sys->fprint(stderr(), "rexecsrv: can't happen: expression has wrong type '%c'\n",
+ alphabet->v.typec());
+ }
+ msg := <-errorc =>
+ if(msg == nil)
+ break wait;
+ # XXX could queue diagnostics back to caller here.
+ sys->fprint(stderr(), "rexecsrv: %s\n", msg);
+ }
+ sys->write(fd1, array[0] of byte, 0);
+}
+
+removefid(fid: int, l: list of (int, string)): (list of (int, string), string)
+{
+ if(l == nil)
+ return (nil, nil);
+ if((hd l).t0 == fid)
+ return (removefid(fid, tl l).t0, (hd l).t1);
+ (rl, d) := removefid(fid, tl l);
+ return (hd l :: rl, d);
+}
+
+fatal(e: string)
+{
+ sys->fprint(sys->fildes(2), "rexecsrv: %s\n", e);
+ raise "fail:error";
+}
+
diff --git a/appl/alphabet/declare.sh b/appl/alphabet/declare.sh
new file mode 100644
index 00000000..7ea84062
--- /dev/null
+++ b/appl/alphabet/declare.sh
@@ -0,0 +1,25 @@
+load std alphabet
+
+type /string /fd /status /cmd /wfd
+
+typeset /fs
+type /fs/fs /fs/entries /fs/gate /fs/selector
+
+typeset /grid
+type /grid/endpoint
+
+autoconvert fd status {(fd); /print $1 1}
+autoconvert string fd /read
+autoconvert cmd string /unparse
+autoconvert wfd fd /w2fd
+
+autoconvert fs entries /fs/entries
+autoconvert string gate /fs/match
+autoconvert entries fd /fs/print
+autoconvert endpoint fd {(endpoint); /grid/local -v $1}
+
+fn pretty {
+ -{
+ /echo {/pretty $1}
+ } ${rewrite $1 /status}
+}
diff --git a/appl/alphabet/eval.b b/appl/alphabet/eval.b
new file mode 100644
index 00000000..b63b9f7e
--- /dev/null
+++ b/appl/alphabet/eval.b
@@ -0,0 +1,757 @@
+implement Eval;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ n_BLOCK, n_VAR, n_BQ, n_BQ2, n_REDIR,
+ n_DUP, n_LIST, n_SEQ, n_CONCAT, n_PIPE, n_ADJ,
+ n_WORD, n_NOWAIT, n_SQUASH, n_COUNT,
+ n_ASSIGN, n_LOCAL: import sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+
+# XXX /usr/inferno/appl/alphabet/eval.b:189: function call type mismatch
+# ... a remarkably uninformative error message!
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ sys->fprint(sys->fildes(2), "eval: cannot load %s: %r\n", path);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ sh = checkload(load Sh Sh->PATH, Sh->PATH);
+}
+
+WORD, VALUE: con iota;
+
+# to do:
+# - change value letters to more appropriate (e.g. fs->f, entries->e, gate->g).
+# - allow shell $variable expansions
+
+Evalstate: adt[V, M, C]
+ for {
+ V =>
+ dup: fn(t: self V): V;
+ free: fn(t: self V, used: int);
+ gets: fn(t: self V): string;
+ isstring: fn(t: self V): int;
+ type2s: fn(tc: int): string;
+ typec: fn(t: self V): int;
+ M =>
+ find: fn(c: C, s: string): (M, string);
+ typesig: fn(m: self M): string;
+ run: fn(m: self M, c: C,
+ errorc: chan of string,
+ opts: list of (int, list of V), args: list of V): V;
+ mks: fn(c: C, s: string): V;
+ mkc: fn(c: C, cmd: ref Sh->Cmd): V;
+ typename2c: fn(s: string): int;
+ cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V;
+ }
+{
+ ctxt: C;
+ errorc: chan of string;
+
+ expr: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V;
+ runcmd: fn(e: self ref Evalstate, cmd: ref Sh->Cmd, arg0: V, args: list of V): V;
+ getargs: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): (ref Sh->Cmd, list of V);
+ getvar: fn(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V;
+};
+
+Env: adt[V]
+ for {
+ V =>
+ free: fn(v: self V, used: int);
+ dup: fn(v: self V): V;
+ }
+{
+ items: array of V;
+
+ new: fn(args: list of V, nilval: V): Env[V];
+ get: fn(t: self Env, id: int): V;
+ discard: fn(t: self Env);
+};
+
+Context[V, M, Ectxt].eval(expr: ref Sh->Cmd, ctxt: Ectxt, errorc: chan of string,
+ args: list of V): V
+{
+ if(expr == nil){
+ discardlist(nil, args);
+ return nil;
+ }
+ nilv: V;
+ e := ref Evalstate[V, M, Ectxt](ctxt, errorc);
+ {
+ return e.runcmd(expr, nilv, args);
+ } exception x {
+ "error:*" =>
+ report(e.errorc, x);
+ return nil;
+ }
+}
+
+Evalstate[V,M,C].expr(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V
+{
+ op: ref Sh->Cmd;
+ args: list of V;
+ arg0: V;
+ case c.ntype {
+ n_PIPE =>
+ if(c.left == nil){
+ # N.B. side effect on env.
+ arg0 = env.items[0];
+ env.items[0] = nil;
+ env.items = env.items[1:];
+ }else
+ arg0 = e.expr(c.left, env);
+ {
+ (op, args) = e.getargs(c.right, env);
+ } exception {
+ "error:*" =>
+ arg0.free(0);
+ raise;
+ }
+ n_ADJ or
+ n_WORD or
+ n_BLOCK or
+ n_BQ2 =>
+ (op, args) = e.getargs(c, env);
+ * =>
+ raise "error: expected pipe, adj or word, got " + sh->cmd2string(c);
+ }
+
+ return e.runcmd(op, arg0, args);
+}
+
+# a b c -> adj(adj('a', 'b'), 'c')
+Evalstate[V,M,C].getargs(e: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): (ref Sh->Cmd, list of V)
+{
+ # do a quick sanity check of module/command-block type
+ for(d := c; d.ntype == n_ADJ; d = d.left)
+ ;
+ if(d.ntype != n_WORD && d.ntype != n_BLOCK)
+ raise "error: expected word or block, got "+sh->cmd2string(d);
+ args: list of V;
+ for(; c.ntype == n_ADJ; c = c.left){
+ r: V;
+ case c.right.ntype {
+ n_VAR =>
+ r = e.getvar(c.right.left, env);
+ n_BLOCK =>
+ r = e.expr(c.right.left, env);
+ n_WORD =>
+ r = M.mks(e.ctxt, deglob(c.right.word));
+ n_BQ2 =>
+ r = M.mkc(e.ctxt, c.right.left);
+ * =>
+ discardlist(nil, args);
+ raise "error: syntax error: expected var, block or word. got "+sh->cmd2string(c);
+ }
+ args = r :: args;
+ }
+ return (c, args);
+}
+
+Evalstate[V,M,C].getvar(nil: self ref Evalstate, c: ref Sh->Cmd, env: Env[V]): V
+{
+ if(c == nil || c.ntype != n_WORD)
+ raise "error: bad variable name";
+ var := deglob(c.word);
+ v := env.get(int var);
+ if(v == nil)
+ raise sys->sprint("error: $%q not defined or cannot be reused", var);
+ return v;
+}
+
+# get rid of GLOB characters left there by the shell.
+deglob(s: string): string
+{
+ j := 0;
+ for (i := 0; i < len s; i++) {
+ if (s[i] != Sh->GLOB) {
+ if (i != j) # a worthy optimisation???
+ s[j] = s[i];
+ j++;
+ }
+ }
+ if (i == j)
+ return s;
+ return s[0:j];
+}
+
+Evalstate[V,M,C].runcmd(e: self ref Evalstate, cmd: ref Sh->Cmd, arg0: V, args: list of V): V
+{
+ m: M;
+ sig: string;
+ err: string;
+ if(cmd.ntype == n_WORD){
+ (m, err) = M.find(e.ctxt, cmd.word);
+ if(err != nil){
+ discardlist(nil, arg0::args);
+ raise sys->sprint("error: cannot load %q: %s", cmd.word, err);
+ }
+ sig = m.typesig();
+ }else{
+ (sig, cmd, err) = blocksig0(m, e.ctxt, cmd);
+ if(sig == nil){
+ discardlist(nil, arg0::args);
+ raise sys->sprint("error: invalid command: %s", err);
+ }
+ }
+ ok: int;
+ opts: list of (int, list of V);
+ x: M;
+ (ok, opts, args) = cvtargs(x, e.ctxt, sig, cmd, arg0, args, e.errorc);
+ if(ok == -1){
+ x: V;
+ discardlist(opts, args);
+ raise "error: usage: " + sh->cmd2string(cmd)+" "+cmdusage(x, sig);
+ }
+ if(m != nil){
+ r := m.run(e.ctxt, e.errorc, opts, args);
+ if(r == nil)
+ raise "error: command failed";
+ return r;
+ }else{
+ v: V; # XXX prevent spurious (?) compiler error message: "type polymorphic type does not have a 'discard' function"
+ env := Env[V].new(args, v);
+ {
+ v = e.expr(cmd, env);
+ env.discard();
+ return v;
+ } exception ex {
+ "error:*" =>
+ env.discard();
+ raise;
+ }
+ }
+}
+
+# {(fd string); walk $2 | merge {unbundle $1}}
+blocksig[M, Ectxt](nilm: M, ctxt: Ectxt, e: ref Sh->Cmd): (string, string)
+ for{
+ M =>
+ typename2c: fn(s: string): int;
+ find: fn(c: Ectxt, s: string): (M, string);
+ typesig: fn(m: self M): string;
+ }
+{
+ (sig, nil, err) := blocksig0(nilm, ctxt, e);
+ return (sig, err);
+}
+
+# {(fd string); walk $2 | merge {unbundle $1}}
+blocksig0[M, Ectxt](nilm: M, ctxt, e: ref Sh->Cmd): (string, ref Sh->Cmd, string)
+ for{
+ M =>
+ typename2c: fn(s: string): int;
+ find: fn(c: Ectxt, s: string): (M, string);
+ typesig: fn(m: self M): string;
+ }
+{
+ if(e == nil || e.ntype != n_BLOCK)
+ return (nil, nil, "expected block, got "+sh->cmd2string(e));
+ e = e.left;
+
+
+ if(e == nil || e.ntype != n_SEQ || e.left == nil || e.left.ntype != n_LIST){
+ (ptc, err) := pipesig(nilm, ctxt, e);
+ if(err != nil)
+ return (nil, nil, err);
+ sig := "a";
+ if(ptc != -1)
+ sig[len sig] = ptc;
+ return (sig, e, nil);
+ }
+
+ r := e.right;
+ e = e.left.left;
+ if(e == nil)
+ return ("a", r, nil);
+ argt: list of string;
+ while(e.ntype == n_ADJ){
+ if(e.right.ntype != n_WORD)
+ return (nil, nil, "bad declaration: expected word, got "+sh->cmd2string(e.right));
+ argt = deglob(e.right.word) :: argt;
+ e = e.left;
+ }
+ if(e.ntype != n_WORD)
+ return (nil, nil, "bad declaration: expected word, got "+sh->cmd2string(e));
+ argt = e.word :: argt;
+ i := 1;
+ sig := "a";
+ (ptc, err) := pipesig(nilm, ctxt, r);
+ if(err != nil)
+ return (nil, nil, err);
+ if(ptc != -1)
+ sig[len sig] = ptc;
+
+ for(a := argt; a != nil; a = tl a){
+ tc := M.typename2c(hd a);
+ if(tc == -1)
+ return (nil, nil, sys->sprint("unknown type %q", hd a));
+ sig[len sig] = tc;
+ i++;
+ }
+ return (sig, r, nil);
+}
+
+# if e represents an expression with an empty first pipe element,
+# return the type of its first argument (-1 if it doesn't).
+# string represents error if module doesn't have a first argument.
+pipesig[M, Ectxt](nilm: M, ctxt: Ectxt, e: ref Sh->Cmd): (int, string)
+ for{
+ M =>
+ typename2c: fn(s: string): int;
+ find: fn(c: Ectxt, s: string): (M, string);
+ typesig: fn(m: self M): string;
+ }
+{
+ if(e == nil)
+ return (-1, nil);
+ for(; e.ntype == n_PIPE; e = e.left){
+ if(e.left == nil){
+ # find actual module that's being called.
+ for(e = e.right; e.ntype == n_ADJ; e = e.left)
+ ;
+ sig: string;
+ if(e.ntype == n_WORD){
+ (m, err) := M.find(ctxt, e.word);
+ if(m == nil)
+ return (-1, err);
+ sig = m.typesig();
+ }
+ else if(e.ntype == n_BLOCK){
+ err: string;
+ (sig, nil, err) = blocksig0(nilm, ctxt, e);
+ if(sig == nil)
+ return (-1, err);
+ }else
+ return (-1, "expected word or block, got "+sh->cmd2string(e));
+ if(len sig < 2)
+ return (-1, "cannot pipe into "+sh->cmd2string(e));
+ return (sig[1], nil);
+ }
+ }
+ return (-1, nil);
+}
+
+cvtargs[M,V,C](nil: M, ctxt: C, otype: string, cmd: ref Sh->Cmd, arg0: V, args: list of V, errorc: chan of string): (int, list of (int, list of V), list of V)
+ for{
+ V =>
+ typec: fn(v: self V): int;
+ isstring: fn(v: self V): int;
+ type2s: fn(tc: int): string;
+ gets: fn(v: self V): string;
+ M =>
+ cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V;
+ mks: fn(c: C, s: string): V;
+ }
+{
+ ok: int;
+ opts: list of (int, list of V);
+ (nil, at, t) := splittype(otype);
+ x: M;
+ (ok, opts, args) = cvtopts(x, ctxt, t, cmd, args, errorc);
+ if(arg0 != nil)
+ args = arg0 :: args;
+ if(ok == -1)
+ return (-1, opts, args);
+ if(len at > 0 && at[0] == '*'){
+ report(errorc, sys->sprint("error: invalid type descriptor %#q for %s", at, sh->cmd2string(cmd)));
+ return (-1, opts, args);
+ }
+ n := len args;
+ if(at != nil && at[len at - 1] == '*'){
+ tc := at[len at - 2];
+ at = at[0:len at - 2];
+ for(i := len at; i < n; i++)
+ at[i] = tc;
+ }
+ if(n != len at){
+ report(errorc, sys->sprint("error: wrong number of arguments (%d/%d) to %s", n, len at, sh->cmd2string(cmd)));
+ return (-1, opts, args);
+ }
+ d: list of V;
+ (ok, args, d) = cvtvalues(x, ctxt, at, cmd, args, errorc);
+ if(ok == -1)
+ args = join(args, d);
+ return (ok, opts, args);
+}
+
+cvtvalues[M,V,C](nil: M, ctxt: C, t: string, cmd: ref Sh->Cmd, args: list of V, errorc: chan of string): (int, list of V, list of V)
+ for{
+ V =>
+ type2s: fn(tc: int): string;
+ typec: fn(v: self V): int;
+ M =>
+ cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V;
+ }
+{
+ cargs: list of V;
+ for(i := 0; i < len t; i++){
+ tc := t[i];
+ if(args == nil){
+ report(errorc, sys->sprint("error: missing argument of type %s for %s", V.type2s(tc), sh->cmd2string(cmd)));
+ return (-1, cargs, args);
+ }
+ v := M.cvt(ctxt, hd args, tc, errorc);
+ if(v == nil){
+ report(errorc, "error: conversion failed for "+sh->cmd2string(cmd));
+ return (-1, cargs, tl args);
+ }
+ cargs = v :: cargs;
+ args = tl args;
+ }
+ return (0, rev(cargs), args);
+}
+
+cvtopts[M,V,C](nil: M, ctxt: C, opttype: string, cmd: ref Sh->Cmd, args: list of V, errorc: chan of string): (int, list of (int, list of V), list of V)
+ for{
+ V =>
+ type2s: fn(tc: int): string;
+ isstring: fn(v: self V): int;
+ typec: fn(v: self V): int;
+ gets: fn(v: self V): string;
+ M =>
+ cvt: fn(c: C, v: V, tc: int, errorc: chan of string): V;
+ mks: fn(c: C, s: string): V;
+ }
+{
+ if(opttype == nil)
+ return (0, nil, args);
+ opts: list of (int, list of V);
+getopts:
+ while(args != nil){
+ s := "";
+ if((hd args).isstring()){
+ s = (hd args).gets();
+ if(s == nil || s[0] != '-' || len s == 1)
+ s = nil;
+ else if(s == "--"){
+ args = tl args;
+ s = nil;
+ }
+ }
+ if(s == nil)
+ return (0, opts, args);
+ s = s[1:];
+ while(len s > 0){
+ opt := s[0];
+ if(((ok, t) := opttypes(opt, opttype)).t0 == -1){
+ report(errorc, sys->sprint("error: unknown option -%c for %s", opt, sh->cmd2string(cmd)));
+ return (-1, opts, args);
+ }
+ if(t == nil){
+ s = s[1:];
+ opts = (opt, nil) :: opts;
+ }else{
+ if(len s > 1)
+ args = M.mks(ctxt, s[1:]) :: tl args;
+ else
+ args = tl args;
+ vl: list of V;
+ x: M;
+ (ok, vl, args) = cvtvalues(x, ctxt, t, cmd, args, errorc);
+ if(ok == -1)
+ return (-1, opts, join(vl, args));
+ opts = (opt, vl) :: opts;
+ continue getopts;
+ }
+ }
+ args = tl args;
+ }
+ return (0, opts, args);
+}
+
+discardlist[V](ol: list of (int, list of V), vl: list of V)
+ for{
+ V =>
+ free: fn(v: self V, used: int);
+ }
+{
+ for(; ol != nil; ol = tl ol)
+ for(ovl := (hd ol).t1; ovl != nil; ovl = tl ovl)
+ vl = (hd ovl) :: vl;
+ for(; vl != nil; vl = tl vl)
+ (hd vl).free(0);
+}
+
+# true if a module with type sig t1 is compatible with a caller that expects t0
+typecompat(t0, t1: string): int
+{
+ (rt0, at0, ot0) := splittype(t0);
+ (rt1, at1, ot1) := splittype(t1);
+
+ if((rt0 != rt1 && rt0 != 'a') || at0 != at1) # XXX could do better for repeated args.
+ return 0;
+
+ for(i := 1; i < len ot0; i++){
+ for(j := i; j < len ot0; j++)
+ if(ot0[j] == '-')
+ break;
+ (ok, t) := opttypes(ot0[i], ot1);
+ if(ok == -1 || ot0[i+1:j] != t)
+ return 0;
+ i = j;
+ }
+ return 1;
+}
+
+splittype(t: string): (int, string, string)
+{
+ if(t == nil)
+ return (-1, nil, nil);
+ for(i := 1; i < len t; i++)
+ if(t[i] == '-')
+ break;
+ return (t[0], t[1:i], t[i:]);
+}
+
+opttypes(opt: int, opts: string): (int, string)
+{
+ for(i := 1; i < len opts; i++){
+ if(opts[i] == opt && opts[i-1] == '-'){
+ for(j := i+1; j < len opts; j++)
+ if(opts[j] == '-')
+ break;
+ return (0, opts[i+1:j]);
+ }
+ }
+ return (-1, nil);
+}
+
+usage2sig[V](nil: V, u: string): (string, string)
+ for{
+ V =>
+ typename2c: fn(s: string): int;
+ }
+{
+ u[len u] = '\0';
+
+ i := 0;
+ t: int;
+ tok: string;
+
+ # options
+ opts: string;
+ for(;;){
+ (t, tok, i) = optstok(u, i);
+ if(t != '[')
+ break;
+ o := i;
+ (t, tok, i) = optstok(u, i);
+ if(t != '-'){
+ i = o;
+ t = '[';
+ break;
+ }
+ for(j := 0; j < len tok; j++){
+ opts[len opts] = '-';
+ opts[len opts] = tok[j];
+ }
+ for(;;){
+ (t, tok, i) = optstok(u, i);
+ if(t == ']')
+ break;
+ if(t != 't')
+ return (nil, sys->sprint("bad option syntax, got '%c'", t));
+ tc := V.typename2c(tok);
+ if(tc == -1)
+ return (nil, "unknown type: "+tok);
+ opts[len opts] = tc;
+ }
+ }
+
+ # arguments
+ args: string;
+parseargs:
+ for(;;){
+ case t {
+ '>' =>
+ break parseargs;
+ '[' =>
+ (t, tok, i) = optstok(u, i);
+ if(t != 't')
+ return (nil, "bad argument syntax");
+ tc := V.typename2c(tok);
+ if(tc == -1)
+ return (nil, "unknown type: "+tok);
+ if(((t, nil, i) = optstok(u, i)).t0 != '*')
+ return (nil, "bad argument syntax");
+ if(((t, nil, i) = optstok(u, i)).t0 != ']')
+ return (nil, "bad argument syntax");
+ if(((t, nil, i) = optstok(u, i)).t0 != '>')
+ return (nil, "bad argument syntax");
+ args[len args] = tc;
+ args[len args] = '*';
+ break parseargs;
+ 't' =>
+ tc := V.typename2c(tok);
+ if(tc == -1)
+ return (nil, "unknown type: "+tok);
+ args[len args] = tc;
+ (t, tok, i) = optstok(u, i);
+ * =>
+ return (nil, "no return type");
+ }
+ }
+
+ # return type
+ (t, tok, i) = optstok(u, i);
+ if(t != 't')
+ return (nil, "expected return type");
+ tc := V.typename2c(tok);
+ if(tc == -1)
+ return (nil, "unknown type: "+tok);
+ r: string;
+ r[0] = tc;
+ r += args;
+ r += opts;
+ return (r, nil);
+}
+
+optstok(u: string, i: int): (int, string, int)
+{
+ while(u[i] == ' ')
+ i++;
+ case u[i] {
+ '\0' =>
+ return (-1, nil, i);
+ '-' =>
+ i++;
+ if(u[i] == '>')
+ return ('>', nil, i+1);
+ start := i;
+ while((c := u[i]) != '\0'){
+ if(c == ']' || c == ' ')
+ break;
+ i++;
+ }
+ return ('-', u[start:i], i);
+ '[' =>
+ return (u[i], nil, i+1);
+ ']' =>
+ return (u[i], nil, i+1);
+ '.' =>
+ start := i;
+ while(u[i] == '.')
+ i++;
+ if(i - start < 3)
+ raise "parse:error at '.'";
+ return ('*', nil, i);
+ * =>
+ start := i;
+ while((c := u[i]) != '\0'){
+ if(c == ' ' || c == ']' || c == '-' || (c == '.' && u[i+1] == '.'))
+ return ('t', u[start:i], i);
+ i++;
+ }
+ return ('t', u[start:i], i);
+ }
+}
+
+cmdusage[V](nil: V, t: string): string
+ for{
+ V =>
+ type2s: fn(c: int): string;
+ }
+{
+ if(t == nil)
+ return "-> bad";
+ for(oi := 0; oi < len t; oi++)
+ if(t[oi] == '-')
+ break;
+ s := "";
+ if(oi < len t){
+ single, multi: string;
+ for(i := oi; i < len t - 1;){
+ for(j := i + 1; j < len t; j++)
+ if(t[j] == '-')
+ break;
+
+ optargs := t[i+2:j];
+ if(optargs == nil)
+ single[len single] = t[i+1];
+ else{
+ multi += sys->sprint(" [-%c", t[i+1]);
+ for (k := 0; k < len optargs; k++)
+ multi += " " + V.type2s(optargs[k]);
+ multi += "]";
+ }
+ i = j;
+ }
+ if(single != nil)
+ s += " [-" + single + "]";
+ s += multi;
+ }
+ multi := 0;
+ if(oi > 2 && t[oi - 1] == '*'){
+ multi = 1;
+ oi -= 2;
+ }
+ for(k := 1; k < oi; k++)
+ s += " " + V.type2s(t[k]);
+ if(multi)
+ s += " [" + V.type2s(t[k]) + "...]";
+ s += " -> " + V.type2s(t[0]);
+ if(s[0] == ' ')
+ s=s[1:];
+ return s;
+}
+
+Env[V].new(args: list of V, nilval: V): Env[V]
+{
+ if(args == nil)
+ return Env(nil);
+ e := Env[V](array[len args] of {* => nilval});
+ for(i := 0; args != nil; args = tl args)
+ e.items[i++] = hd args;
+ return e;
+}
+
+Env[V].get(t: self Env, id: int): V
+{
+ id--;
+ if(id < 0 || id >= len t.items)
+ return nil;
+ x := t.items[id];
+ if((y := x.dup()) == nil){
+ t.items[id] = nil;
+ y = x;
+ }
+ return y;
+}
+
+Env[V].discard(t: self Env)
+{
+ for(i := 0; i < len t.items; i++)
+ t.items[i].free(0);
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
+
+# join x to y, leaving result in arbitrary order.
+join[T](x, y: list of T): list of T
+{
+ if(len x > len y)
+ (x, y) = (y, x);
+ for(; x != nil; x = tl x)
+ y = hd x :: y;
+ return y;
+}
diff --git a/appl/alphabet/extvalues.b b/appl/alphabet/extvalues.b
new file mode 100644
index 00000000..67d4833f
--- /dev/null
+++ b/appl/alphabet/extvalues.b
@@ -0,0 +1,49 @@
+implement Extvalues;
+include "sys.m";
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+
+Values[V].new(): ref Values[V]
+{
+ v: V;
+ return ref Values[V](chan[1] of int, array[4] of {* => (0, v)}, 0::1::2::3::nil);
+}
+
+Values[V].add(vals: self ref Values, v: V): int
+{
+ vals.lock <-= 1;
+ if(vals.freeids == nil){
+ n := len vals.v;
+ vals.v = (array[len vals.v * 3 / 2] of (int, V))[0:] = vals.v;
+ for(; n < len vals.v; n++)
+ vals.freeids = n :: vals.freeids;
+ }
+ id := hd vals.freeids;
+ vals.freeids = tl vals.freeids;
+ vals.v[id] = (1, v);
+#(load Sys Sys->PATH)->print("add %d\n", id);
+ <-vals.lock;
+ return id;
+}
+
+Values[V].inc(vals: self ref Values, id: int)
+{
+ vals.lock <-= 1;
+ vals.v[id].t0++;
+#(load Sys Sys->PATH)->print("inc %d -> %d\n", id, vals.v[id].t0);
+ <-vals.lock;
+}
+
+Values[V].del(vals: self ref Values, id: int)
+{
+ vals.lock <-= 1;
+ if(--vals.v[id].t0 == 0){
+ vals.v[id].t1 = nil;
+ vals.freeids = id :: vals.freeids;
+ }
+#(load Sys Sys->PATH)->print("del %d -> %d\n", id, vals.v[id].t0);
+ <-vals.lock;
+}
+
diff --git a/appl/alphabet/fs/and.b b/appl/alphabet/fs/and.b
new file mode 100644
index 00000000..da180978
--- /dev/null
+++ b/appl/alphabet/fs/and.b
@@ -0,0 +1,70 @@
+implement And,Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+And: module {};
+
+types(): string
+{
+ return "pppp*";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ c := chan of Gatequery;
+ spawn andgate(c, args);
+ return ref Value.Vp(c);
+}
+
+andgate(c: Gatechan, args: list of ref Value)
+{
+ sub: list of Gatechan;
+ for(; args != nil; args = tl args)
+ sub = (hd args).p().i :: sub;
+ sub = rev(sub);
+ myreply := chan of int;
+ while(((d, reply) := <-c).t0.t0 != nil){
+ for(l := sub; l != nil; l = tl l){
+ (hd l) <-= (d, myreply);
+ if(<-myreply == 0)
+ break;
+ }
+ reply <-= l == nil;
+ }
+ for(; sub != nil; sub = tl sub)
+ hd sub <-= (Nilentry, nil);
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
diff --git a/appl/alphabet/fs/bundle.b b/appl/alphabet/fs/bundle.b
new file mode 100644
index 00000000..2c692f1a
--- /dev/null
+++ b/appl/alphabet/fs/bundle.b
@@ -0,0 +1,210 @@
+implement Bundle, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "readdir.m";
+ readdir: Readdir;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, quit, report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Bundle: module {};
+
+# XXX if we can't open a directory, is it ever worth passing its metadata
+# through anyway?
+
+EOF: con "end of archive\n";
+
+types(): string
+{
+ return "fx";
+}
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: bundle: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil)
+ badmod(Readdir->PATH);
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ badmod(Readdir->PATH);
+ bufio->fopen(nil, Sys->OREAD); # XXX no bufio->init!
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Readdir->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+}
+
+run(nil: ref Draw->Context, r: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ spawn bundleproc((hd args).x().i, f, r.start("bundle"));
+ return ref Value.Vf(f);
+}
+
+#bundle(r: ref Report, iob: ref Iobuf, c: Fschan): chan of string
+bundle(nil: ref Report, nil: ref Iobuf, nil: Fschan): chan of string
+{
+ return nil;
+# sync := chan[1] of string;
+# spawn bundleproc(c, sync, iob, r.start("bundle"));
+# return sync;
+}
+
+bundleproc(c: Fschan, f: chan of ref Sys->FD, errorc: chan of string)
+{
+ f <-= nil;
+ if((fd := <-f) == nil){
+ (<-c).t1 <-= Quit;
+ quit(errorc);
+ }
+ iob := bufio->fopen(fd, Sys->OWRITE);
+ fd = nil;
+ (d, reply) := <-c;
+ if(d.dir == nil){
+ report(errorc, "no root directory");
+ endarchive(iob, errorc);
+ }
+ if(puts(iob, dir2header(d.dir), errorc) == -1){
+ reply <-= Quit;
+ quit(errorc);
+ }
+ reply <-= Down;
+ bundledir(d.dir.name, d, c, iob, errorc);
+ endarchive(iob, errorc);
+}
+
+endarchive(iob: ref Iobuf, errorc: chan of string)
+{
+ {
+ if(puts(iob, EOF, errorc) != -1)
+ iob.flush();
+ sys->fprint(iob.fd, "");
+ } exception {
+ "write on closed pipe" =>
+ ;
+ }
+ quit(errorc);
+}
+
+bundledir(path: string, d: Fsdata,
+ c: Fschan,
+ iob: ref Iobuf, errorc: chan of string)
+{
+ if(d.dir.mode & Sys->DMDIR){
+ path[len path] = '/';
+ for(;;){
+ (ent, reply) := <-c;
+ if(ent.dir == nil){
+ reply <-= Skip;
+ break;
+ }
+ if(puts(iob, dir2header(ent.dir), errorc) == -1){
+ reply <-= Quit;
+ quit(errorc);
+ }
+ reply <-= Down;
+ bundledir(path + ent.dir.name, ent, c, iob, errorc);
+ }
+ iob.putc('\n');
+ }else{
+ buf: array of byte;
+ reply: chan of int;
+ length := big d.dir.length;
+ n := big 0;
+ for(;;){
+ ((nil, buf), reply) = <-c;
+ if(buf == nil){
+ reply <-= Skip;
+ break;
+ }
+ if(write(iob, buf, len buf, errorc) != len buf){
+ reply <-= Quit;
+ quit(errorc);
+ }
+ n += big len buf;
+ if(n > length){ # should never happen
+ report(errorc, sys->sprint("%q is longer than expected (fatal)", path));
+ reply <-= Quit;
+ quit(errorc);
+ }
+ if(n == length){
+ reply <-= Skip;
+ break;
+ }
+ reply <-= Next;
+ }
+ if(n < length){
+ report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd); adding null bytes", path, n, length));
+ buf = array[Sys->ATOMICIO] of {* => byte 0};
+ while(n < length){
+ nb := len buf;
+ if(length - n < big len buf)
+ nb = int (length - n);
+ if(write(iob, buf, nb, errorc) != nb){
+ (<-c).t1 <-= Quit;
+ quit(errorc);
+ }
+ report(errorc, sys->sprint("added %d null bytes", nb));
+ n += big nb;
+ }
+ }
+ }
+}
+
+dir2header(d: ref Sys->Dir): string
+{
+ return sys->sprint("%q %uo %q %q %ud %bd\n", d.name, d.mode, d.uid, d.gid, d.mtime, d.length);
+}
+
+puts(iob: ref Iobuf, s: string, errorc: chan of string): int
+{
+ {
+ if(iob.puts(s) == -1)
+ report(errorc, sys->sprint("write error: %r"));
+ return 0;
+ } exception {
+ "write on closed pipe" =>
+ report(errorc, sys->sprint("write on closed pipe"));
+ return -1;
+ }
+}
+
+write(iob: ref Iobuf, buf: array of byte, n: int, errorc: chan of string): int
+{
+ {
+ nw := iob.write(buf, n);
+ if(nw < n){
+ if(nw >= 0)
+ report(errorc, "short write");
+ else{
+ report(errorc, sys->sprint("write error: %r"));
+ }
+ }
+ return nw;
+ } exception {
+ "write on closed pipe" =>
+ report(errorc, "write on closed pipe");
+ return -1;
+ }
+}
diff --git a/appl/alphabet/fs/bundle.m b/appl/alphabet/fs/bundle.m
new file mode 100644
index 00000000..905b8288
--- /dev/null
+++ b/appl/alphabet/fs/bundle.m
@@ -0,0 +1,9 @@
+Bundle: module {
+ PATH: con "/dis/fs/bundle.dis";
+
+ types: fn(): string;
+ init: fn();
+ run: fn(nil: ref Draw->Context, report: ref Reports->Report,
+ nil: list of Fs->Option, args: list of ref Fs->Value): ref Fs->Value;
+ bundle: fn(r: ref Reports->Report, iob: ref Bufio->Iobuf, c: Fs->Fschan): chan of string;
+};
diff --git a/appl/alphabet/fs/chstat.b b/appl/alphabet/fs/chstat.b
new file mode 100644
index 00000000..b53ca784
--- /dev/null
+++ b/appl/alphabet/fs/chstat.b
@@ -0,0 +1,189 @@
+implement Chstat, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet/fs.m";
+ fsfilter: Fsfilter;
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Chstat: module {};
+
+Query: adt {
+ gate: Gatechan;
+ stat: Sys->Dir;
+ mask: int;
+ cflag: int;
+ reply: chan of int;
+
+ query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int;
+};
+
+types(): string
+{
+ return "xx-pp-ms-us-gs-ts-as-c";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+
+ fsfilter = load Fsfilter Fsfilter->PATH;
+ if(fsfilter == nil)
+ badmod(Fsfilter->PATH);
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ ws := Sys->nulldir;
+ mask := 0;
+ gate: ref Value;
+ cflag := 0;
+ for(; opts != nil; opts = tl opts){
+ o := (hd opts).args;
+ case (hd opts).opt {
+ 'p' =>
+ gate.free(0);
+ gate = hd o;
+ 'm' =>
+ ok: int;
+ m := (hd o).s().i;
+ (ok, mask, ws.mode) = parsemode(m);
+ mask &= ~Sys->DMDIR;
+ if(ok == 0){
+ sys->fprint(sys->fildes(2), "fs: chstat: bad mode %#q\n", m);
+ gate.free(0);
+ return nil;
+ }
+ 'u' =>
+ ws.uid = (hd o).s().i;
+ 'g' =>
+ ws.gid = (hd o).s().i;
+ 't' =>
+ ws.mtime = int (hd o).s().i;
+ 'a' =>
+ ws.atime = int (hd o).s().i;
+ 'c' =>
+ cflag++;
+ }
+ }
+
+ dst := chan of (Fsdata, chan of int);
+ p: Gatechan;
+ if(gate != nil)
+ p = gate.p().i;
+ spawn chstatproc((hd args).x().i, dst, p, ws, mask, cflag);
+ return ref Value.Vx(dst);
+}
+
+chstatproc(src, dst: Fschan, gate: Gatechan, stat: Sys->Dir, mask: int, cflag: int)
+{
+ fsfilter->filter(ref Query(gate, stat, mask, cflag, chan of int), src, dst);
+ if(gate != nil)
+ gate <-= ((nil, nil, 0), nil);
+}
+
+Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int
+{
+ c := 1;
+ if(q.gate != nil){
+ q.gate <-= ((d, name, depth), q.reply);
+ c = <-q.reply;
+ }
+ if(c){
+ if(q.cflag){
+ m := d.mode & 8r700;
+ d.mode = (d.mode & ~8r77)|(m>>3)|(m>>6);
+ }
+ stat := q.stat;
+ d.mode = (d.mode & ~q.mask) | (stat.mode & q.mask);
+ if(stat.uid != nil)
+ d.uid = stat.uid;
+ if(stat.gid != nil)
+ d.gid = stat.gid;
+ if(stat.mtime != ~0)
+ d.mtime = stat.mtime;
+ if(stat.atime != ~0)
+ d.atime = stat.atime;
+ }
+ return 1;
+}
+
+# stolen from /appl/cmd/chmod.b
+User: con 8r700;
+Group: con 8r070;
+Other: con 8r007;
+All: con User | Group | Other;
+
+Read: con 8r444;
+Write: con 8r222;
+Exec: con 8r111;
+parsemode(spec: string): (int, int, int)
+{
+ mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH;
+loop:
+ for(i := 0; i < len spec; i++){
+ case spec[i] {
+ 'u' =>
+ mask |= User;
+ 'g' =>
+ mask |= Group;
+ 'o' =>
+ mask |= Other;
+ 'a' =>
+ mask |= All;
+ * =>
+ break loop;
+ }
+ }
+ if(i == len spec)
+ return (0, 0, 0);
+ if(i == 0)
+ mask |= All;
+
+ op := spec[i++];
+ if(op != '+' && op != '-' && op != '=')
+ return (0, 0, 0);
+
+ mode := 0;
+ for(; i < len spec; i++){
+ case spec[i]{
+ 'r' =>
+ mode |= Read;
+ 'w' =>
+ mode |= Write;
+ 'x' =>
+ mode |= Exec;
+ 'a' =>
+ mode |= Sys->DMAPPEND;
+ 'l' =>
+ mode |= Sys->DMEXCL;
+ 'd' =>
+ mode |= Sys->DMDIR;
+ 'A' =>
+ mode |= Sys->DMAUTH;
+ * =>
+ return (0, 0, 0);
+ }
+ }
+ if(op == '+' || op == '-')
+ mask &= mode;
+ if(op == '-')
+ mode = ~mode;
+ return (1, mask, mode);
+}
diff --git a/appl/alphabet/fs/compose.b b/appl/alphabet/fs/compose.b
new file mode 100644
index 00000000..09bef812
--- /dev/null
+++ b/appl/alphabet/fs/compose.b
@@ -0,0 +1,105 @@
+implement Compose, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Cmpchan,
+ Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Compose: module {};
+
+AinB: con 1<<3;
+BinA: con 1<<2;
+AoutB: con 1<<1;
+BoutA: con 1<<0;
+
+A: con AinB|AoutB;
+AoverB: con AinB|AoutB|BoutA;
+AatopB: con AinB|BoutA;
+AxorB: con AoutB|BoutA;
+
+B: con BinA|BoutA;
+BoverA: con BinA|BoutA|AoutB;
+BatopA: con BinA|AoutB;
+BxorA: con BoutA|AoutB;
+
+ops := array[] of {
+ AinB => "AinB",
+ BinA => "BinA",
+ AoutB => "AoutB",
+ BoutA => "BoutA",
+ A => "A",
+ AoverB => "AoverB",
+ AatopB => "AatopB",
+ AxorB => "AxorB",
+ B => "B",
+ BoverA => "BoverA",
+ BatopA => "BatopA",
+};
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+types(): string
+{
+ return "ms-d";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ c := chan of (ref Sys->Dir, ref Sys->Dir, chan of int);
+ s := (hd args).s().i;
+ for(i := 0; i < len ops; i++)
+ if(ops[i] == s)
+ break;
+ if(i == len ops){
+ sys->fprint(sys->fildes(2), "fs: join: bad op %q\n", s);
+ return nil;
+ }
+ spawn compose(c, i, opts != nil);
+ return ref Value.Vm(c);
+}
+
+compose(c: Cmpchan, op: int, dflag: int)
+{
+ t := array[4] of {* => 0};
+ if(op & AinB)
+ t[2r11] = 2r01;
+ if(op & BinA)
+ t[2r11] = 2r10;
+ if(op & AoutB)
+ t[2r01] = 2r01;
+ if(op & BoutA)
+ t[2r10] = 2r10;
+ if(dflag){
+ while(((d0, d1, reply) := <-c).t2 != nil){
+ x := (d1 != nil) << 1 | d0 != nil;
+ r := t[d0 != nil | (d1 != nil) << 1];
+ if(r == 0 && x == 2r11 && (d0.mode & d1.mode & Sys->DMDIR))
+ r = 2r11;
+ reply <-= r;
+ }
+ }else{
+ while(((d0, d1, reply) := <-c).t2 != nil)
+ reply <-= t[(d1 != nil) << 1 | d0 != nil];
+ }
+}
diff --git a/appl/alphabet/fs/depth.b b/appl/alphabet/fs/depth.b
new file mode 100644
index 00000000..9787fd54
--- /dev/null
+++ b/appl/alphabet/fs/depth.b
@@ -0,0 +1,54 @@
+implement Depth, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Depth: module {};
+
+types(): string
+{
+ return "ps";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ d := int (hd args).s().i;
+ if(d <= 0){
+ sys->fprint(sys->fildes(2), "fs: depth: invalid depth\n");
+ return nil;
+ }
+ c := chan of Gatequery;
+ spawn depthgate(c, d);
+ return ref Value.Vp(c);
+}
+
+depthgate(c: Gatechan, d: int)
+{
+ while((((dir, nil, depth), reply) := <-c).t0.t0 != nil)
+ reply <-= depth <= d;
+}
diff --git a/appl/alphabet/fs/entries.b b/appl/alphabet/fs/entries.b
new file mode 100644
index 00000000..6fbf78d0
--- /dev/null
+++ b/appl/alphabet/fs/entries.b
@@ -0,0 +1,91 @@
+implement Entries, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Entries: module {};
+
+types(): string
+{
+ return "tx";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ sc := Entrychan(chan of int, chan of Entry);
+ spawn entriesproc((hd args).x().i, sc);
+ return ref Value.Vt(sc);
+}
+
+entriesproc(c: Fschan, sc: Entrychan)
+{
+ if(<-sc.sync == 0){
+ (<-c).t1 <-= Quit;
+ exit;
+ }
+ indent := 0;
+ names: list of string;
+ name: string;
+loop:
+ for(;;){
+ (d, reply) := <-c;
+ if(d.dir != nil){
+ p: string;
+ depth := indent;
+ if(d.dir.mode & Sys->DMDIR){
+ names = name :: names;
+ if(indent == 0)
+ name = d.dir.name;
+ else{
+ if(name[len name - 1] != '/')
+ name[len name] = '/';
+ name += d.dir.name;
+ }
+ indent++;
+ reply <-= Down;
+ p = name;
+ }else{
+ p = name;
+ if(p[len p - 1] != '/')
+ p[len p] = '/';
+ p += d.dir.name;
+ reply <-= Next;
+ }
+ if(p != nil)
+ sc.c <-= (d.dir, p, depth);
+ }else{
+ reply <-= Next;
+ if(d.dir == nil && d.data == nil){
+ if(--indent == 0)
+ break loop;
+ (name, names) = (hd names, tl names);
+ }
+ }
+ }
+ sc.c <-= Nilentry;
+}
diff --git a/appl/alphabet/fs/exec.b b/appl/alphabet/fs/exec.b
new file mode 100644
index 00000000..a7aa7460
--- /dev/null
+++ b/appl/alphabet/fs/exec.b
@@ -0,0 +1,172 @@
+implement Exec, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Option, Value, Entrychan: import fs;
+
+Exec: module {};
+
+# usage: exec [-n nfiles] [-t endcmd] [-pP] command entries
+types(): string
+{
+ return "rtc-ns-tc-p-P";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+ sh = load Sh Sh->PATH;
+ if(sh == nil)
+ badmod(Sh->PATH);
+ sh->initialise();
+}
+
+run(drawctxt: ref Draw->Context, report: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ n := 1;
+ pflag := 0;
+ tcmd: ref Sh->Cmd;
+ for(; opts != nil; opts = tl opts){
+ o := hd opts;
+ case o.opt {
+ 'n' =>
+ if((n = int (hd o.args).s().i) <= 0){
+ sys->fprint(sys->fildes(2), "fs: exec: invalid argument to -n\n");
+ return nil;
+ }
+ 't' =>
+ tcmd = (hd o.args).c().i;
+ 'p' =>
+ pflag = 1;
+ 'P' =>
+ pflag = 2;
+ }
+ }
+ if(pflag && n > 1){
+ sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -n %d\n", n);
+ return nil;
+ }
+ cmd := (hd tl args).c().i;
+ c := (hd args).t().i;
+ sync := chan of string;
+ spawn execproc(drawctxt, sync, n, pflag, c, cmd, tcmd, report.start("exec"));
+ sync <-= nil;
+ return ref Value.Vr(sync);
+}
+
+execproc(drawctxt: ref Draw->Context, sync: chan of string, n, pflag: int,
+ c: Entrychan, cmd, tcmd: ref Sh->Cmd, errorc: chan of string)
+{
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ ctxt := Context.new(drawctxt);
+ <-sync;
+ if(<-sync != nil){
+ c.sync <-= 0;
+ errorc <-= nil;
+ exit;
+ }
+ c.sync <-= 1;
+ argv := ref Sh->Listnode(cmd, nil) :: nil;
+
+ fl: list of ref Sh->Listnode;
+ nf := 0;
+ while(((d, p, nil) := <-c.c).t0 != nil){
+ fl = ref Sh->Listnode(nil, p) :: fl;
+ if(++nf >= n){
+ ctxt.set("file", rev(fl));
+ if(pflag)
+ setstatenv(ctxt, d, pflag);
+ fl = nil;
+ nf = 0;
+ {ctxt.run(argv, 0);} exception {"fail:*" =>;}
+ }
+ }
+ if(nf > 0){
+ ctxt.set("file", rev(fl));
+ {ctxt.run(argv, 0);} exception {"fail:*" =>;}
+ }
+ if(tcmd != nil){
+ ctxt.set("file", nil);
+ {ctxt.run(ref Sh->Listnode(tcmd, nil) :: nil, 0);} exception {"fail:*" =>;}
+ }
+ errorc <-= nil;
+ sync <-= nil; # XXX should return result here...
+}
+
+setenv(ctxt: ref Context, var: string, val: list of string)
+{
+ ctxt.set(var, sh->stringlist2list(val));
+}
+
+setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int)
+{
+ setenv(ctxt, "mode", modes(dir.mode) :: nil);
+ setenv(ctxt, "uid", dir.uid :: nil);
+ setenv(ctxt, "mtime", string dir.mtime :: nil);
+ setenv(ctxt, "length", string dir.length :: nil);
+
+ if(pflag > 1){
+ setenv(ctxt, "name", dir.name :: nil);
+ setenv(ctxt, "gid", dir.gid :: nil);
+ setenv(ctxt, "muid", dir.muid :: nil);
+ setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil);
+ setenv(ctxt, "atime", string dir.atime :: nil);
+ setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil);
+ setenv(ctxt, "dev", string dir.dev :: nil);
+ }
+}
+
+mtab := array[] of {
+ "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx"
+};
+
+modes(mode: int): string
+{
+ s: string;
+
+ if(mode & Sys->DMDIR)
+ s = "d";
+ else if(mode & Sys->DMAPPEND)
+ s = "a";
+ else if(mode & Sys->DMAUTH)
+ s = "A";
+ else
+ s = "-";
+ if(mode & Sys->DMEXCL)
+ s += "l";
+ else
+ s += "-";
+ s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7];
+ return s;
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
diff --git a/appl/alphabet/fs/filter.b b/appl/alphabet/fs/filter.b
new file mode 100644
index 00000000..af696e2f
--- /dev/null
+++ b/appl/alphabet/fs/filter.b
@@ -0,0 +1,66 @@
+implement Filter, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet/fs.m";
+ fsfilter: Fsfilter;
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Filter: module {};
+
+Query: adt {
+ gate: Gatechan;
+ dflag: int;
+ reply: chan of int;
+ query: fn(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int;
+};
+
+types(): string
+{
+ return "xxp-d";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fsfilter = load Fsfilter Fsfilter->PATH;
+ if(fsfilter == nil)
+ badmod(Fsfilter->PATH);
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ dst := chan of (Fsdata, chan of int);
+ spawn filterproc((hd args).x().i, dst, (hd tl args).p().i, opts != nil);
+ return ref Value.Vx(dst);
+}
+
+filterproc(src, dst: Fschan, gate: Gatechan, dflag: int)
+{
+ fsfilter->filter(ref Query(gate, dflag, chan of int), src, dst);
+ gate <-= ((nil, nil, 0), nil);
+}
+
+Query.query(q: self ref Query, d: ref Sys->Dir, name: string, depth: int): int
+{
+ if(depth == 0 || (q.dflag && (d.mode & Sys->DMDIR)))
+ return 1;
+ q.gate <-= ((d, name, depth), q.reply);
+ return <-q.reply;
+}
diff --git a/appl/alphabet/fs/ls.b b/appl/alphabet/fs/ls.b
new file mode 100644
index 00000000..fdc2ddb0
--- /dev/null
+++ b/appl/alphabet/fs/ls.b
@@ -0,0 +1,107 @@
+implement Ls, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "daytime.m";
+ daytime: Daytime;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Option, Value, Entrychan: import fs;
+
+Ls: module {};
+
+types(): string
+{
+ return "ft-u-m";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: ls: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ daytime = load Daytime Daytime->PATH;
+ if(daytime == nil)
+ badmod(Daytime->PATH);
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ spawn lsproc(f, opts, (hd args).t().i, report.start("/fs/ls"));
+ return ref Value.Vf(f);
+}
+
+lsproc(f: chan of ref Sys->FD, opts: list of Option, c: Entrychan, errorc: chan of string)
+{
+ f <-= nil;
+ if((fd := <-f) == nil){
+ c.sync <-= 0;
+ reports->quit(errorc);
+ }
+ now := daytime->now();
+ mflag := uflag := 0;
+ c.sync <-= 1;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).opt {
+ 'm' =>
+ mflag = 1;
+ 'u' =>
+ uflag = 1;
+ }
+ }
+ while(((dir, p, nil) := <-c.c).t0 != nil){
+ t := dir.mtime;
+ if(uflag)
+ t = dir.atime;
+ s := sys->sprint("%s %c %d %s %s %bud %s %s\n",
+ modes(dir.mode), dir.dtype, dir.dev,
+ dir.uid, dir.gid, dir.length,
+ daytime->filet(now, dir.mtime), p);
+ if(mflag)
+ s = "[" + dir.muid + "] " + s;
+ sys->fprint(fd, "%s", s);
+ }
+ reports->quit(errorc);
+}
+
+mtab := array[] of {
+ "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx"
+};
+
+modes(mode: int): string
+{
+ s: string;
+
+ if(mode & Sys->DMDIR)
+ s = "d";
+ else if(mode & Sys->DMAPPEND)
+ s = "a";
+ else if(mode & Sys->DMAUTH)
+ s = "A";
+ else
+ s = "-";
+ if(mode & Sys->DMEXCL)
+ s += "l";
+ else
+ s += "-";
+ s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7];
+ return s;
+}
diff --git a/appl/alphabet/fs/match.b b/appl/alphabet/fs/match.b
new file mode 100644
index 00000000..3a82de49
--- /dev/null
+++ b/appl/alphabet/fs/match.b
@@ -0,0 +1,84 @@
+implement Match, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "filepat.m";
+ filepat: Filepat;
+include "regex.m";
+ regex: Regex;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Match: module {};
+
+types(): string
+{
+ return "ps-a-r";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ regex = load Regex Regex->PATH;
+ if(regex == nil)
+ badmod(Regex->PATH);
+ filepat = load Filepat Filepat->PATH;
+ if(filepat == nil)
+ badmod(Filepat->PATH);
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ pat := (hd args).s().i;
+ aflag := rflag := 0;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).opt {
+ 'a' =>
+ aflag = 1;
+ 'r' =>
+ rflag = 1;
+ }
+ }
+ v := ref Value.Vp(chan of Gatequery);
+ re: Regex->Re;
+ if(rflag){
+ err: string;
+ (re, err) = regex->compile(pat, 0);
+ if(re == nil){
+ sys->fprint(sys->fildes(2), "fs: match: regex error on %#q: %s\n", pat, err);
+ return nil;
+ }
+ }
+ spawn matchproc(v.i, aflag, pat, re);
+ return v;
+}
+
+matchproc(c: Gatechan, all: int, pat: string, re: Regex->Re)
+{
+ while((((d, name, nil), reply) := <-c).t0.t0 != nil){
+ if(all == 0)
+ name = d.name;
+ if(re != nil)
+ reply <-= regex->execute(re, name) != nil; # XXX should anchor it?
+ else
+ reply <-= filepat->match(pat, name);
+ }
+}
diff --git a/appl/alphabet/fs/merge.b b/appl/alphabet/fs/merge.b
new file mode 100644
index 00000000..62524de5
--- /dev/null
+++ b/appl/alphabet/fs/merge.b
@@ -0,0 +1,192 @@
+implement Merge, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Cmpchan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Merge: module {};
+
+# e.g....
+# fs select {mode -d} {merge -c {compose -d AoutB} {filter {not {path /chan /dev /usr/rog /n/local /net}} /} {merge {proto FreeBSD} {proto Hp} {proto Irix} {proto Linux} {proto MacOSX} {proto Nt} {proto Nt.ti} {proto Nt.ti925} {proto Plan9} {proto Plan9.ti} {proto Plan9.ti925} {proto Solaris} {proto authsrv} {proto dl} {proto dlsrc} {proto ep7} {proto inferno} {proto inferno.ti} {proto ipaqfs} {proto minitel} {proto os} {proto scheduler.client} {proto scheduler.server} {proto sds} {proto src} {proto src.ti} {proto sword} {proto ti925.ti} {proto ti925bin} {proto tipaq} {proto umec} {proto utils} {proto utils.ti}}} >[2] /dev/null
+
+types(): string
+{
+ return "xxxx*-1-cm";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil){
+ sys->fprint(sys->fildes(2), "fs: cannot load %s: %r\n", Fs->PATH);
+ raise "fail:bad module";
+ }
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ recurse := 1;
+ cmp: Cmpchan;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).opt {
+ '1' =>
+ recurse = 0;
+ 'c' =>
+ cmp = (hd (hd opts).args).m().i;
+ }
+ }
+ dst := chan of (Fsdata, chan of int);
+ spawn mergeproc((hd args).x().i, (hd tl args).x().i, dst, recurse, cmp, tl tl args == nil);
+ for(args = tl tl args; args != nil; args = tl args){
+ dst1 := chan of (Fsdata, chan of int);
+ spawn mergeproc(dst, (hd args).x().i, dst1, recurse, cmp, tl args == nil);
+ dst = dst1;
+ }
+ return ref Value.Vx(dst);
+}
+
+# merge two trees; assume directories are alphabetically sorted.
+mergeproc(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan, killcmp: int)
+{
+ myreply := chan of int;
+ ((d0, nil), reply0) := <-c0;
+ ((d1, nil), reply1) := <-c1;
+
+ if(compare(cmp, d0, d1) == 2r10)
+ dst <-= ((d1, nil), myreply);
+ else
+ dst <-= ((d0, nil), myreply);
+ r := <-myreply;
+ reply0 <-= r;
+ reply1 <-= r;
+ if(r == Down){
+ {
+ mergedir(c0, c1, dst, recurse, cmp);
+ } exception {"exit" =>;}
+ }
+ if(cmp != nil && killcmp)
+ cmp <-= (nil, nil, nil);
+}
+
+mergedir(c0, c1, dst: Fschan, recurse: int, cmp: Cmpchan)
+{
+ myreply := chan of int;
+ reply0, reply1: chan of int;
+ d0, d1: ref Sys->Dir;
+ eof0 := eof1 := 0;
+ for(;;){
+ if(!eof0 && d0 == nil){
+ ((d0, nil), reply0) = <-c0;
+ if(d0 == nil){
+ reply0 <-= Next;
+ eof0 = 1;
+ }
+ }
+ if(!eof1 && d1 == nil){
+ ((d1, nil), reply1) = <-c1;
+ if(d1 == nil){
+ reply1 <-= Next;
+ eof1 = 1;
+ }
+ }
+ if(eof0 && eof1)
+ break;
+
+ (wd0, wd1) := (d0, d1);
+ if(d0 != nil && d1 != nil && d0.name != d1.name){
+ if(d0.name < d1.name)
+ wd1 = nil;
+ else
+ wd0 = nil;
+ }
+
+ wc0, wc1: Fschan;
+ wreply0, wreply1: chan of int;
+ weof0, weof1: int;
+
+ c := compare(cmp, wd0, wd1);
+ if(wd0 != nil && wd1 != nil){
+ if(c != 0 && recurse && (wd0.mode & wd1.mode & Sys->DMDIR) != 0){
+ dst <-= ((wd0, nil), myreply);
+ r := <-myreply;
+ reply0 <-= r;
+ reply1 <-= r;
+ d0 = d1 = nil;
+ case r {
+ Quit =>
+ raise "exit";
+ Skip =>
+ return;
+ Down =>
+ mergedir(c0, c1, dst, 1, cmp);
+ }
+ continue;
+ }
+ # when we can't merge and there's a clash, choose c0 over c1, unless cmp says otherwise
+ if(c == 2r10){
+ reply0 <-= Next;
+ d0 = nil;
+ }else{
+ reply1 <-= Next;
+ d1 = nil;
+ }
+ }
+ if(c & 2r01){
+ (wd0, wc0, wreply0, weof0) = (d0, c0, reply0, eof0);
+ (wd1, wc1, wreply1, weof1) = (d1, c1, reply1, eof1);
+ d0 = nil;
+ }else if(c & 2r10){
+ (wd0, wc0, wreply0, weof0) = (d1, c1, reply1, eof1);
+ (wd1, wc1, wreply1, weof1) = (d0, c0, reply0, eof0);
+ d1 = nil;
+ }else{
+ if(wd0 == nil){
+ reply1 <-= Next;
+ d1 = nil;
+ }else{
+ reply0 <-= Next;
+ d0 = nil;
+ }
+ continue;
+ }
+ dst <-= ((wd0, nil), myreply);
+ r := <-myreply;
+ wreply0 <-= r;
+ if(r == Down)
+ r = fs->copy(wc0, dst); # XXX hmm, maybe this should be a mergedir()
+ case r {
+ Quit or
+ Skip =>
+ if(wd1 == nil && !weof1)
+ (nil, wreply1) = <-wc1;
+ wreply1 <-= r;
+ if(r == Quit)
+ raise "exit";
+ return;
+ }
+ }
+ dst <-= ((nil, nil), myreply);
+ if(<-myreply == Quit)
+ raise "exit";
+}
+
+compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int
+{
+ mask := (d0 != nil) | (d1 != nil) << 1;
+ if(cmp == nil)
+ return mask;
+ reply := chan of int;
+ cmp <-= (d0, d1, reply);
+ return <-reply & mask;
+}
diff --git a/appl/alphabet/fs/mergewrite.b b/appl/alphabet/fs/mergewrite.b
new file mode 100644
index 00000000..04e8cebb
--- /dev/null
+++ b/appl/alphabet/fs/mergewrite.b
@@ -0,0 +1,244 @@
+implement Mergewrite, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "readdir.m";
+ readdir: Readdir;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report, quit: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Cmpchan, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Mergewrite: module {};
+
+types(): string
+{
+ return "rxsm-v-n";
+}
+
+VERBOSE, NOWRITE, ASSUME: con 1<<iota;
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil){
+ sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Readdir->PATH);
+ raise "fail:bad module";
+ }
+ readdir->init(nil, 0);
+
+ fs = load Fs Fs->PATH;
+ if(fs == nil){
+ sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Fs->PATH);
+ raise "fail:bad module";
+ }
+ reports = load Reports Reports->PATH;
+ if(reports == nil){
+ sys->fprint(sys->fildes(2), "fs: mergewrite: cannot load %s: %r\n", Reports->PATH);
+ raise "fail:bad module";
+ }
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ sync := chan of string;
+ flags := 0;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).opt {
+ 'n' =>
+ flags |= NOWRITE;
+ 'v' =>
+ flags |= VERBOSE;
+ }
+ }
+
+ spawn fswriteproc(sync, flags, (hd args).x().i, (hd tl args).s().i, (hd tl tl args).m().i, report.start("mergewrite"));
+ sync <-= nil;
+ return ref Value.Vr(sync);
+}
+
+fswriteproc(sync: chan of string, flags: int, c: Fschan, root: string, cmp: Cmpchan, errorc: chan of string)
+{
+ sys->pctl(Sys->FORKNS, nil);
+ <-sync;
+ if(<-sync != nil){
+ (<-c).t1 <-= Quit;
+ quit(errorc);
+ }
+
+ ((d, nil), reply) := <-c;
+ if(root != nil){
+ d = ref *d;
+ d.name = root;
+ }
+ fswritedir(d.name, cmp, d, reply, c, errorc, flags);
+ errorc <-= nil;
+ sync <-= nil; # XXX should return result here...
+}
+
+fswritedir(path: string, cmp: Cmpchan, dir: ref Sys->Dir, dreply: chan of int, c: Fschan,
+ errorc: chan of string, flags: int)
+{
+ fd: ref Sys->FD;
+ if(dir.mode & Sys->DMDIR){
+ made := 0;
+ if(flags&VERBOSE)
+ report(errorc, sys->sprint("create %q %uo", path, dir.mode));
+ if(flags&NOWRITE){
+ if(flags&ASSUME)
+ made = 1;
+ else{
+ fd = sys->open(dir.name, Sys->OREAD);
+ if(fd == nil){
+ made = 1;
+ flags |= ASSUME;
+ }else if(sys->chdir(dir.name) == -1){
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot cd to %q: %r", path));
+ return;
+ }
+ }
+ }else{
+ fd = sys->create(dir.name, Sys->OREAD, dir.mode|8r300);
+ made = fd != nil;
+ if(fd == nil && (fd = sys->open(dir.name, Sys->OREAD)) == nil){
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300));
+ return;
+ }
+ # XXX if we haven't just made it, we should chmod the old entry u+w to enable writing.
+ if(sys->chdir(dir.name) == -1){ # XXX beware of names starting with '#'
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot cd to %q: %r", path));
+ fd = nil;
+ sys->remove(dir.name);
+ return;
+ }
+ }
+ dreply <-= Down;
+ entries: array of ref Sys->Dir;
+ if(made == 0)
+ entries = readdir->readall(fd, Readdir->NAME|Readdir->COMPACT).t0;
+ i := 0;
+ eod := 0;
+ d0, d1: ref Sys->Dir;
+ reply: chan of int;
+ path[len path] = '/';
+ for(;;){
+ if(!eod && d0 == nil){
+ ((d0, nil), reply) = <-c;
+ if(d0 == nil){
+ reply <-= Next;
+ eod = 1;
+ }
+ }
+ if(d1 == nil && i < len entries)
+ d1 = entries[i++];
+ if(d0 == nil && d1 == nil)
+ break;
+
+ (wd0, wd1) := (d0, d1);
+ if(d0 != nil && d1 != nil && d0.name != d1.name){
+ if(d0.name < d1.name)
+ wd1 = nil;
+ else
+ wd0 = nil;
+ }
+ r := compare(cmp, wd0, wd1);
+ if(wd1 != nil){
+ if((r & 2r10) == 0){
+ if(flags&VERBOSE)
+ report(errorc, "removing "+path+wd1.name);
+ if((flags&NOWRITE)==0){
+ if(wd1.mode & Sys->DMDIR)
+ rmdir(wd1.name);
+ else
+ remove(wd1.name);
+ }
+ }
+ d1 = nil;
+ }
+ if(wd0 != nil){
+ if((r & 2r01) == 0)
+ reply <-= Next;
+ else
+ fswritedir(path + wd0.name, cmp, d0, reply, c, errorc, flags);
+ d0 = nil;
+ }
+ }
+ if((flags&ASSUME)==0)
+ sys->chdir("..");
+ if((flags&NOWRITE)==0){
+ if((dir.mode & 8r300) != 8r300){
+ ws := Sys->nulldir;
+ ws.mode = dir.mode;
+ if(sys->fwstat(fd, ws) == -1)
+ report(errorc, sys->sprint("cannot wstat %q: %r", path));
+ }
+ }
+ }else{
+ if(flags&VERBOSE)
+ report(errorc, sys->sprint("create %q %uo", path, dir.mode));
+ if(flags&NOWRITE){
+ dreply <-= Next;
+ return;
+ }
+ fd = sys->create(dir.name, Sys->OWRITE, dir.mode);
+ if(fd == nil){
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, dir.mode|8r300));
+ return;
+ }
+ dreply <-= Down;
+ while((((nil, buf), reply) := <-c).t0.data != nil){
+ nw := sys->write(fd, buf, len buf);
+ if(nw < len buf){
+ if(nw == -1)
+ errorc <-= sys->sprint("error writing %q: %r", path);
+ else
+ errorc <-= sys->sprint("short write");
+ reply <-= Skip;
+ break;
+ }
+ reply <-= Next;
+ }
+ reply <-= Next;
+ }
+}
+
+rmdir(name: string)
+{
+ (d, n) := readdir->init(name, Readdir->NONE|Readdir->COMPACT);
+ for(i := 0; i < n; i++){
+ path := name+"/"+d[i].name;
+ if(d[i].mode & Sys->DMDIR)
+ rmdir(path);
+ else
+ remove(path);
+ }
+ remove(name);
+}
+
+remove(name: string)
+{
+ if(sys->remove(name) < 0)
+ sys->fprint(sys->fildes(2), "mergewrite: cannot remove %q: %r\n", name);
+}
+
+compare(cmp: Cmpchan, d0, d1: ref Sys->Dir): int
+{
+ mask := (d0 != nil) | (d1 != nil) << 1;
+ if(cmp == nil)
+ return mask;
+ reply := chan of int;
+ cmp <-= (d0, d1, reply);
+ return <-reply & mask;
+}
diff --git a/appl/alphabet/fs/mkext.b b/appl/alphabet/fs/mkext.b
new file mode 100644
index 00000000..b916aebc
--- /dev/null
+++ b/appl/alphabet/fs/mkext.b
@@ -0,0 +1,266 @@
+implement Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "string.m";
+ str: String;
+include "bundle.m";
+ bundle: Bundle;
+include "draw.m";
+include "sh.m";
+include "alphabet/fs.m";
+ fslib: Fs;
+ Report, Value,quit, report: import fslib;
+ Fschan, Fsdata, Entrychan, Entry,
+ Quit, Next, Skip, Down,
+ Option: import Fs;
+
+to do...
+read file. if non-seekable, make temporary copy.
+record offsets of all files
+sort by filename
+output in proper order.
+
+
+types(): string
+{
+ return "xs";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fslib = load Fs Fs->PATH;
+ if(fslib == nil)
+ badmod(Fs->PATH);
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ badmod(Bufio->PATH);
+ str = load String String->PATH;
+ if(str == nil)
+ badmod(String->PATH);
+ bundle = load Bundle Bundle->PATH;
+ if(bundle == nil)
+ badmod(Bundle->PATH);
+ bundle->init();
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ p := (hd args).s().i;
+ iob: ref Bufio->Iobuf;
+ if(p == "-")
+ iob = bufio->fopen(sys->fildes(0), Sys->OREAD);
+ else
+ iob = bufio->open(p, Sys->OREAD);
+ if(iob == nil){
+ sys->fprint(sys->fildes(2), "fs: unbundle: cannot open %q: %r\n", p);
+ return nil;
+ }
+ seekable := p != "-";
+ if(seekable)
+ seekable = isseekable(iob.fd);
+ if(
+ return ref Value.Vx(mkext(report, iob, seekable, Sys->ATOMICIO));
+}
+
+# dodgy heuristic... avoid, or using the stat-length of pipes and net connections
+isseekable(fd: ref Sys->FD): int
+{
+ (ok, stat) := sys->fstat(fd);
+ if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I')
+ return 0;
+ return 1;
+}
+
+mkext(r: ref Report, iob: ref Iobuf, seekable, blocksize: int): Fschan
+{
+ c := chan of (Fsdata, chan of int);
+ spawn unbundleproc(iob, c, seekable, blocksize, r.start("bundle"));
+ return c;
+}
+
+EOF: con "end of archive\n";
+
+unbundleproc(iob: ref Iobuf, c: Fschan, seekable, blocksize: int, errorc: chan of string)
+{
+ reply := chan of int;
+ p := iob.gets('\n');
+ # XXX overall header?
+ if(p == nil || p == EOF){
+ fslib->sendnulldir(c);
+ quit(errorc);
+ }
+ d := header2dir(p);
+ if(d == nil){
+ fslib->sendnulldir(c);
+ report(errorc, "invalid first header");
+ quit(errorc);
+ }
+ if((d.mode & Sys->DMDIR) == 0){
+ fslib->sendnulldir(c);
+ report(errorc, "first entry is not a directory");
+ quit(errorc);
+ }
+ c <-= ((d, nil), reply);
+ case r := <-reply {
+ Down =>
+ unbundledir(iob, c, 0, seekable, blocksize, errorc);
+ c <-= ((nil, nil), reply);
+ <-reply;
+ Skip or
+ Next =>
+ unbundledir(iob, c, 1, seekable, blocksize, errorc);
+ Quit =>
+ break;
+ }
+ quit(errorc);
+}
+
+unbundledir(iob: ref Iobuf, c: Fschan,
+ skipping, seekable, blocksize: int, errorc: chan of string): int
+{
+ reply := chan of int;
+ while((p := iob.gets('\n')) != nil){
+ if(p == EOF)
+ break;
+ if(p[0] == '\n')
+ break;
+ d := header2dir(p);
+ if(d == nil){
+ report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1]));
+ return -1;
+ }
+ if(d.mode & Sys->DMDIR){
+ if(skipping)
+ continue;
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Down =>
+ r := unbundledir(iob, c, 0, seekable, blocksize, errorc);
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ if(r == -1)
+ return -1;
+ Skip =>
+ if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)
+ return -1;
+ skipping = 1;
+ Next =>
+ if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)
+ return -1;
+ }
+ }else{
+ if(skipping){
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ }else{
+ case unbundlefile(iob, d, c, errorc, seekable, blocksize) {
+ -1 =>
+ return -1;
+ Skip =>
+ skipping = 1;
+ }
+ }
+ }
+ }
+ if(p == nil)
+ report(errorc, "unexpected eof");
+ return 0;
+}
+
+skipdata(iob: ref Iobuf, length: big, seekable: int): int
+{
+ if(seekable){
+ iob.seek(big length, Sys->SEEKRELA);
+ return 0;
+ }
+ buf := array[Sys->ATOMICIO] of byte;
+ for(n := big 0; n < length; ){
+ nb := Sys->ATOMICIO;
+ if(length - n < big Sys->ATOMICIO)
+ nb = int (length - n);
+ nb = iob.read(buf, nb);
+ if(nb <= 0)
+ return -1;
+ n += big nb;
+ }
+ return 0;
+}
+
+unbundlefile(iob: ref Iobuf, d: ref Sys->Dir,
+ c: Fschan, errorc: chan of string, seekable, blocksize: int): int
+{
+ reply := chan of int;
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ return Skip;
+ Next =>
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ return Next;
+ }
+ length := d.length;
+ for(n := big 0; n < length; ){
+ nr := blocksize;
+ if(n + big blocksize > length)
+ nr = int (length - n);
+ buf := array[nr] of byte;
+ nr = iob.read(buf, nr);
+ if(nr <= 0){
+ if(nr < 0)
+ report(errorc, sys->sprint("read error: %r"));
+ else
+ report(errorc, sys->sprint("premature eof"));
+ return -1;
+ }else if(nr < len buf)
+ buf = buf[0:nr];
+ c <-= ((nil, buf), reply);
+ n += big nr;
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ if(skipdata(iob, length - n, seekable) == -1)
+ return -1;
+ return Next;
+ }
+ }
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+}
+
+header2dir(s: string): ref Sys->Dir
+{
+ toks := str->unquoted(s);
+ nf := len toks;
+ if(nf != 6)
+ return nil;
+ d := ref Sys->nulldir;
+ (d.name, toks) = (hd toks, tl toks);
+ (d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks);
+ (d.uid, toks) = (hd toks, tl toks);
+ (d.gid, toks) = (hd toks, tl toks);
+ (d.mtime, toks) = (int hd toks, tl toks);
+ (d.length, toks) = (big hd toks, tl toks);
+ return d;
+}
diff --git a/appl/alphabet/fs/mkfile b/appl/alphabet/fs/mkfile
new file mode 100644
index 00000000..684a3650
--- /dev/null
+++ b/appl/alphabet/fs/mkfile
@@ -0,0 +1,55 @@
+<../../../mkconfig
+
+TARG=\
+ and.dis\
+ bundle.dis\
+ chstat.dis\
+ compose.dis\
+ depth.dis\
+ entries.dis\
+ exec.dis\
+ filter.dis\
+ ls.dis\
+ match.dis\
+ merge.dis\
+ mergewrite.dis\
+ mode.dis\
+ newer.dis\
+ not.dis\
+ or.dis\
+ path.dis\
+ pipe.dis\
+ print.dis\
+ proto.dis\
+ query.dis\
+ run.dis\
+ select.dis\
+ setroot.dis\
+ size.dis\
+ unbundle.dis\
+ walk.dis\
+ write.dis\
+
+MODULES=\
+ bundle.m\
+ unbundle.m\
+
+SYSMODULES=\
+ alphabet/fs.m\
+ alphabet/reports.m\
+ bufio.m\
+ bundle.m\
+ daytime.m\
+ draw.m\
+ filepat.m\
+ readdir.m\
+ regex.m\
+ sh.m\
+ string.m\
+ sys.m\
+ unbundle.m\
+
+DISBIN=$ROOT/dis/alphabet/fs
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
diff --git a/appl/alphabet/fs/mode.b b/appl/alphabet/fs/mode.b
new file mode 100644
index 00000000..974885c7
--- /dev/null
+++ b/appl/alphabet/fs/mode.b
@@ -0,0 +1,125 @@
+implement Mode, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Mode: module {};
+
+# XXX implement octal modes.
+
+User: con 8r700;
+Group: con 8r070;
+Other: con 8r007;
+All: con User | Group | Other;
+
+Read: con 8r444;
+Write: con 8r222;
+Exec: con 8r111;
+
+types(): string
+{
+ return "ps";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ spec := (hd args).s().i;
+ (ok, mask, mode) := parsemode(spec);
+ if(ok == 0){
+ sys->fprint(sys->fildes(2), "fs: mode: bad mode %#q\n", spec);
+ return nil;
+ }
+ c := chan of Gatequery;
+ spawn modegate(c, mask, mode);
+ return ref Value.Vp(c);
+}
+
+modegate(c: Gatechan, mask, mode: int)
+{
+ m := mode & mask;
+ while((((d, nil, nil), reply) := <-c).t0.t0 != nil)
+ reply <-= ((d.mode & mask) ^ m) == 0;
+}
+
+# stolen from /appl/cmd/chmod.b
+parsemode(spec: string): (int, int, int)
+{
+ mask := Sys->DMAPPEND | Sys->DMEXCL | Sys->DMDIR | Sys->DMAUTH;
+loop:
+ for(i := 0; i < len spec; i++){
+ case spec[i] {
+ 'u' =>
+ mask |= User;
+ 'g' =>
+ mask |= Group;
+ 'o' =>
+ mask |= Other;
+ 'a' =>
+ mask |= All;
+ * =>
+ break loop;
+ }
+ }
+ if(i == len spec)
+ return (0, 0, 0);
+ if(i == 0)
+ mask |= All;
+
+ op := spec[i++];
+ if(op != '+' && op != '-' && op != '=')
+ return (0, 0, 0);
+
+ mode := 0;
+ for(; i < len spec; i++){
+ case spec[i]{
+ 'r' =>
+ mode |= Read;
+ 'w' =>
+ mode |= Write;
+ 'x' =>
+ mode |= Exec;
+ 'a' =>
+ mode |= Sys->DMAPPEND;
+ 'l' =>
+ mode |= Sys->DMEXCL;
+ 'd' =>
+ mode |= Sys->DMDIR;
+ 'A' =>
+ mode |= Sys->DMAUTH;
+ * =>
+ return (0, 0, 0);
+ }
+ }
+ if(op == '+' || op == '-')
+ mask &= mode;
+ if(op == '-')
+ mode = ~mode;
+ return (1, mask, mode);
+}
+
+
diff --git a/appl/alphabet/fs/newer.b b/appl/alphabet/fs/newer.b
new file mode 100644
index 00000000..a278f70a
--- /dev/null
+++ b/appl/alphabet/fs/newer.b
@@ -0,0 +1,64 @@
+implement Newer, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Cmpchan, Option: import Fs;
+
+Newer: module {};
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+types(): string
+{
+ return "m-d";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+# select those items in A that are newer than those in B
+# or those that exist in A that don't in B.
+# if -d flag is given, select all directories in A too.
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, nil: list of ref Value): ref Value
+{
+ c := chan of (ref Sys->Dir, ref Sys->Dir, chan of int);
+ spawn newer(c, opts != nil);
+ return ref Value.Vm(c);
+}
+
+newer(c: Cmpchan, dflag: int)
+{
+ while(((d0, d1, reply) := <-c).t2 != nil){
+ r: int;
+ if(d0 == nil)
+ r = 2r10;
+ else if(d1 == nil)
+ r = 2r01;
+ else if(dflag && (d0.mode & Sys->DMDIR))
+ r = 2r11;
+ else {
+ if(d0.mtime > d1.mtime)
+ r = 2r01;
+ else
+ r= 2r10;
+ }
+ reply <-= r;
+ }
+}
diff --git a/appl/alphabet/fs/not.b b/appl/alphabet/fs/not.b
new file mode 100644
index 00000000..571f18a3
--- /dev/null
+++ b/appl/alphabet/fs/not.b
@@ -0,0 +1,53 @@
+implement Not, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Not: module {};
+
+types(): string
+{
+ return "pp";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ c := chan of Gatequery;
+ spawn notgate(c, (hd args).p().i);
+ return ref Value.Vp(c);
+}
+
+notgate(c, sub: Gatechan)
+{
+ myreply := chan of int;
+ while(((d, reply) := <-c).t0.t0 != nil){
+ sub <-= (d, myreply);
+ reply <-= !<-myreply;
+ }
+ sub <-= (Nilentry, nil);
+}
diff --git a/appl/alphabet/fs/or.b b/appl/alphabet/fs/or.b
new file mode 100644
index 00000000..7a103a0b
--- /dev/null
+++ b/appl/alphabet/fs/or.b
@@ -0,0 +1,70 @@
+implement Or, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Or: module {};
+
+types(): string
+{
+ return "pppp*";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ c := chan of Gatequery;
+ spawn orgate(c, args);
+ return ref Value.Vp(c);
+}
+
+orgate(c: Gatechan, args: list of ref Value)
+{
+ sub: list of Gatechan;
+ for(; args != nil; args = tl args)
+ sub = (hd args).p().i :: sub;
+ sub = rev(sub);
+ myreply := chan of int;
+ while(((d, reply) := <-c).t0.t0 != nil){
+ for(l := sub; l != nil; l = tl l){
+ (hd l) <-= (d, myreply);
+ if(<-myreply)
+ break;
+ }
+ reply <-= l != nil;
+ }
+ for(; sub != nil; sub = tl sub)
+ hd sub <-= (Nilentry, nil);
+}
+
+rev[T](x: list of T): list of T
+{
+ l: list of T;
+ for(; x != nil; x = tl x)
+ l = hd x :: l;
+ return l;
+}
diff --git a/appl/alphabet/fs/path.b b/appl/alphabet/fs/path.b
new file mode 100644
index 00000000..1ed48378
--- /dev/null
+++ b/appl/alphabet/fs/path.b
@@ -0,0 +1,82 @@
+implement Path, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Path: module {};
+
+types(): string
+{
+ return "pss*-x";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ # XXX cleanname all paths?
+ c := chan of Gatequery;
+ p: list of string;
+ for(; args != nil; args = tl args)
+ p = (hd args).s().i :: p;
+ spawn pathgate(c, opts != nil, p);
+ return ref Value.Vp(c);
+}
+
+pathgate(c: Gatechan, xflag: int, paths: list of string)
+{
+ if(xflag){
+ while((((d, path, nil), reply) := <-c).t0.t0 != nil){
+ for(q := paths; q != nil; q = tl q){
+ r := 1;
+ p := hd q;
+ if(len path > len p)
+ r = path[len p] != '/' || path[0:len p] != p;
+ else if(len path == len p)
+ r = path != p;
+ if(r == 0)
+ break;
+ }
+ reply <-= q == nil;
+ }
+ }else{
+ while((((d, path, nil), reply) := <-c).t0.t0 != nil){
+ for(q := paths; q != nil; q = tl q){
+ r := 0;
+ p := hd q;
+ if(len path > len p)
+ r = path[len p] == '/' && path[0:len p] == p;
+ else if(len path == len p)
+ r = path == p;
+ else
+ r = p[len path] == '/' && p[0:len path] == path;
+ if(r)
+ break;
+ }
+ reply <-= q != nil;
+ }
+ }
+}
diff --git a/appl/alphabet/fs/pipe.b b/appl/alphabet/fs/pipe.b
new file mode 100644
index 00000000..9fe36ec7
--- /dev/null
+++ b/appl/alphabet/fs/pipe.b
@@ -0,0 +1,230 @@
+implement Pipe, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Option, Value, Fschan: import fs;
+ Skip, Next, Down, Quit: import fs;
+
+Pipe: module {};
+
+# pipe the contents of the files in a filesystem through
+# a command. -1 causes one command only to be executed.
+# -p and -P (exclusive to -1) cause stat modes to be set in the shell environment.
+types(): string
+{
+ return "rxc-1-p-P";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ sh = load Sh Sh->PATH;
+ if(sh == nil)
+ badmod(Sh->PATH);
+ sh->initialise();
+}
+
+run(drawctxt: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ n := 1;
+ oneflag := pflag := 0;
+ for(; opts != nil; opts = tl opts){
+ o := hd opts;
+ case o.opt {
+ '1' =>
+ oneflag = 1;
+ 'p' =>
+ pflag = 1;
+ 'P' =>
+ pflag = 2;
+ }
+ }
+ if(pflag && oneflag){
+ sys->fprint(sys->fildes(2), "fs: exec: cannot specify -p with -1\n");
+ return nil;
+ }
+ c := (hd args).x().i;
+ cmd := (hd tl args).c().i;
+ sync := chan of string;
+ spawn execproc(drawctxt, sync, oneflag, pflag, c, cmd);
+ sync <-= nil;
+ return ref Value.Vr(sync);
+}
+
+execproc(drawctxt: ref Draw->Context, sync: chan of string, oneflag, pflag: int,
+ c: Fschan, cmd: ref Sh->Cmd)
+{
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ ctxt := Context.new(drawctxt);
+ <-sync;
+ if(<-sync != nil){
+ (<-c).t1 <-= Quit;
+ exit;
+ }
+ argv := ref Sh->Listnode(cmd, nil) :: nil;
+ fd: ref Sys->FD;
+ result := chan of string;
+ if(oneflag){
+ fd = popen(ctxt, argv, result);
+ if(fd == nil){
+ (<-c).t1 <-= Quit;
+ sync <-= "cannot make pipe";
+ exit;
+ }
+ }
+
+ names: list of string;
+ name: string;
+ indent := 0;
+ for(;;){
+ (d, reply) := <-c;
+ if(d.dir == nil){
+ reply <-= Next;
+ if(--indent == 0){
+ break;
+ }
+ (name, names) = (hd names, tl names);
+ continue;
+ }
+ if((d.dir.mode & Sys->DMDIR) != 0){
+ reply <-= Down;
+ names = name :: names;
+ if(indent > 0 && name != nil && name[len name - 1] != '/')
+ name[len name] = '/';
+ name += d.dir.name;
+ indent++;
+ continue;
+ }
+ if(!oneflag){
+ p := name;
+ if(p != nil && p[len p - 1] != '/')
+ p[len p] = '/';
+ setenv(ctxt, "file", p + d.dir.name :: nil);
+ if(pflag)
+ setstatenv(ctxt, d.dir, pflag);
+ fd = popen(ctxt, argv, result);
+ }
+ if(fd == nil){
+ reply <-= Next;
+ continue;
+ }
+ reply <-= Down;
+ for(;;){
+ data: array of byte;
+ ((nil, data), reply) = <-c;
+ reply <-= Next;
+ if(data == nil)
+ break;
+ n := -1;
+ {n = sys->write(fd, data, len data);}exception {"write on closed pipe" => ;}
+ if(n != len data){
+ if(oneflag){
+ (<-c).t1 <-= Quit;
+ sync <-= "truncated write";
+ exit;
+ }
+ (<-c).t1 <-= Skip;
+ break;
+ }
+ }
+ if(!oneflag){
+ fd = nil;
+ <-result;
+ }
+ }
+ fd = nil;
+ if(oneflag)
+ sync <-= <-result;
+ else
+ sync <-= nil;
+}
+
+popen(ctxt: ref Context, argv: list of ref Sh->Listnode, result: chan of string): ref Sys->FD
+{
+ sync := chan of int;
+ fds := array[2] of ref Sys->FD;
+ sys->pipe(fds);
+ spawn runcmd(ctxt, argv, fds[0], sync, result);
+ <-sync;
+ return fds[1];
+}
+
+runcmd(ctxt: ref Context, argv: list of ref Sh->Listnode, stdin: ref Sys->FD, sync: chan of int, result: chan of string)
+{
+ sys->pctl(Sys->FORKFD, nil);
+ sys->dup(stdin.fd, 0);
+ stdin = nil;
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ ctxt = ctxt.copy(0);
+ sync <-= 0;
+ r := ctxt.run(argv, 0);
+ ctxt = nil;
+ sys->pctl(Sys->NEWFD, nil);
+ result <-=r;
+}
+
+setenv(ctxt: ref Context, var: string, val: list of string)
+{
+ ctxt.set(var, sh->stringlist2list(val));
+}
+
+setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int)
+{
+ setenv(ctxt, "mode", modes(dir.mode) :: nil);
+ setenv(ctxt, "uid", dir.uid :: nil);
+ setenv(ctxt, "mtime", string dir.mtime :: nil);
+ setenv(ctxt, "length", string dir.length :: nil);
+
+ if(pflag > 1){
+ setenv(ctxt, "name", dir.name :: nil);
+ setenv(ctxt, "gid", dir.gid :: nil);
+ setenv(ctxt, "muid", dir.muid :: nil);
+ setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil);
+ setenv(ctxt, "atime", string dir.atime :: nil);
+ setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil);
+ setenv(ctxt, "dev", string dir.dev :: nil);
+ }
+}
+
+mtab := array[] of {
+ "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx"
+};
+
+modes(mode: int): string
+{
+ s: string;
+
+ if(mode & Sys->DMDIR)
+ s = "d";
+ else if(mode & Sys->DMAPPEND)
+ s = "a";
+ else if(mode & Sys->DMAUTH)
+ s = "A";
+ else
+ s = "-";
+ if(mode & Sys->DMEXCL)
+ s += "l";
+ else
+ s += "-";
+ s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7];
+ return s;
+}
diff --git a/appl/alphabet/fs/print.b b/appl/alphabet/fs/print.b
new file mode 100644
index 00000000..4c9bee59
--- /dev/null
+++ b/appl/alphabet/fs/print.b
@@ -0,0 +1,61 @@
+implement Print, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Print: module {};
+
+types(): string
+{
+ return "ft";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ spawn printproc(f, (hd args).t().i, report.start("/fs/print"));
+ return ref Value.Vf(f);
+}
+
+printproc(f: chan of ref Sys->FD, c: Entrychan, errorc: chan of string)
+{
+ f <-= nil;
+ if((fd := <-f) == nil){
+ c.sync <-= 0;
+ reports->quit(errorc);
+ }
+ c.sync <-= 1;
+ while(((d, p, nil) := <-c.c).t0 != nil)
+ sys->fprint(fd, "%s\n", p);
+ sys->fprint(fd, "");
+ reports->quit(errorc);
+}
diff --git a/appl/alphabet/fs/proto.b b/appl/alphabet/fs/proto.b
new file mode 100644
index 00000000..48e46d18
--- /dev/null
+++ b/appl/alphabet/fs/proto.b
@@ -0,0 +1,416 @@
+implement Proto, 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 "alphabet/reports.m";
+ reports: Reports;
+ Report, quit, report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Proto: module {};
+
+File: adt {
+ name: string;
+ mode: int;
+ owner: string;
+ group: string;
+ old: string;
+ flags: int;
+ sub: cyclic array of ref File;
+};
+
+Protof: adt {
+ indent: int;
+ lastline: string;
+ iob: ref Iobuf;
+};
+
+Star, Plus: con 1<<iota;
+
+types(): string
+{
+ return "xf-rs";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: proto: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->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
+{
+ f := (hd args).f().i;
+ rootpath: string;
+ if(opts != nil)
+ rootpath = (hd (hd opts).args).s().i;
+ if(rootpath == nil)
+ rootpath = "/";
+
+ root := ref File(rootpath, ~0, nil, nil, nil, 0, nil);
+ c := chan of (Fsdata, chan of int);
+ spawn protowalk(c, f, root, report.start("proto"));
+ return ref Value.Vx(c);
+}
+
+protowalk(c: Fschan, f: chan of ref Sys->FD, root: ref File, errorc: chan of string)
+{
+ fd := <-f;
+ if(fd != nil)
+ f <-= nil;
+ else{
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f <-= p[1];
+ fd = p[0];
+ }
+ proto := ref Protof(0, nil, nil);
+ proto.iob = bufio->fopen(fd, Sys->OREAD);
+ (root.flags, root.sub) = readproto(proto, -1);
+
+ d: ref Sys->Dir;
+ (ok, rd) := sys->stat(root.name);
+ if(ok != -1)
+ d = ref rd;
+
+ protowalk1(c, root.flags, root.name, file2dir(root, d), root.sub, errorc);
+ quit(errorc);
+}
+
+protowalk1(c: Fschan, flags: int, path: string, d: ref Sys->Dir,
+ sub: array of ref File, errorc: chan of string): int
+{
+ reply := chan of int;
+ c <-= ((d, nil), reply);
+ case r := <-reply {
+ Quit =>
+ quit(errorc);
+ Next or
+ Skip =>
+ return r;
+ }
+ (a, n) := readdir->init(path, Readdir->NAME|Readdir->COMPACT);
+ if(len a == 0){
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+ }
+ j := 0;
+ preventry: string;
+ useentry: int;
+ for(i := 0; i < n; i += useentry){
+ useentry = 1;
+ for(; j < len sub; j++){
+ s := sub[j].name;
+ if(s == preventry){
+ report(errorc, sys->sprint("duplicate entry %s", pathconcat(path, s)));
+ continue; # eliminate duplicates in proto
+ }
+ if(s >= a[i].name)
+ break;
+ # entry has not been found, but we've got a substitute version,
+ # so save the rest of the entries to match the rest of sub.
+ if(sub[j].old != nil){
+ useentry = 0;
+ 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++];
+ preventry = 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, errorc);
+ else if(flags & Plus)
+ r = protowalk1(c, Plus, p, d, nil, errorc);
+ else
+ r = protowalk1(c, f.flags, p, d, f.sub, errorc);
+ if(r == Skip)
+ return Next;
+ }
+ }
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+}
+
+pathconcat(p, name: string): string
+{
+ if(p != nil && p[len p - 1] != '/')
+ p[len p] = '/';
+ p += name;
+ return p;
+}
+
+# from(ish) walk.b
+walkfile(c: Fschan, path: string, d: ref Sys->Dir, errorc: chan of string): int
+{
+ reply := chan of int;
+ fd := sys->open(path, Sys->OREAD);
+ if(fd == nil){
+ report(errorc, sys->sprint("cannot open %q: %r", path));
+ return Next;
+ }
+ c <-= ((d, nil), reply);
+ case r := <-reply {
+ Quit =>
+ quit(errorc);
+ Next or
+ Skip =>
+ return r;
+ }
+ length := d.length;
+ for(n := big 0; n < length; ){
+ nr := Sys->ATOMICIO;
+ if(n + big Sys->ATOMICIO > length)
+ nr = int (length - n);
+ buf := array[nr] of byte;
+ nr = sys->read(fd, buf, nr);
+ if(nr <= 0){
+ if(nr < 0)
+ report(errorc, sys->sprint("error reading %q: %r", path));
+ else
+ report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)",
+ path, n, length));
+ break;
+ }else if(nr < len buf)
+ buf = buf[0:nr];
+ c <-= ((nil, buf), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ return Next;
+ }
+ n += big nr;
+ }
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+}
+
+readproto(proto: ref Protof, 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 Protof, 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;
+ (n, 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] == '$')
+ return getenv(s[1:]);
+ return s;
+}
+
+getenv(s: string): string
+{
+ # XXX implement env variables
+ 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;
+}
diff --git a/appl/alphabet/fs/query.b b/appl/alphabet/fs/query.b
new file mode 100644
index 00000000..8d230707
--- /dev/null
+++ b/appl/alphabet/fs/query.b
@@ -0,0 +1,135 @@
+implement Query, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Option, Value, Gatechan, Gatequery, Nilentry: import fs;
+
+Query: module {};
+
+types(): string
+{
+ return "pc-p-P";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: query: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ sh = load Sh Sh->PATH;
+ if(sh == nil)
+ badmod(Sh->PATH);
+}
+
+run(drawctxt: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ pflag := 0;
+ for(; opts != nil; opts = tl opts){
+ o := hd opts;
+ case o.opt {
+ 'p' =>
+ pflag = 1;
+ 'P' =>
+ pflag = 2;
+ }
+ }
+
+ v := ref Value.Vp(chan of Gatequery);
+ spawn querygate(drawctxt, v.i, (hd args).c().i, pflag);
+ v.i <-= (Nilentry, nil);
+ return v;
+}
+
+querygate(drawctxt: ref Draw->Context, c: Gatechan, cmd: ref Sh->Cmd, pflag: int)
+{
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ ctxt := Context.new(drawctxt);
+ <-c;
+ argv := ref Sh->Listnode(cmd, nil) :: nil;
+ while((((d, p, nil), reply) := <-c).t0.t0 != nil){
+ ctxt.set("file", ref Sh->Listnode(nil, p) :: nil);
+ if(pflag)
+ setstatenv(ctxt, d, pflag);
+ err := "";
+ {
+ err = ctxt.run(argv, 0);
+ } exception e {
+ "fail:*" =>
+ err = e;
+ }
+ reply <-= (err == nil);
+ }
+}
+
+# XXX shouldn't duplicate this...
+
+setenv(ctxt: ref Context, var: string, val: list of string)
+{
+ ctxt.set(var, sh->stringlist2list(val));
+}
+
+setstatenv(ctxt: ref Context, dir: ref Sys->Dir, pflag: int)
+{
+ setenv(ctxt, "mode", modes(dir.mode) :: nil);
+ setenv(ctxt, "uid", dir.uid :: nil);
+ setenv(ctxt, "mtime", string dir.mtime :: nil);
+ setenv(ctxt, "length", string dir.length :: nil);
+
+ if(pflag > 1){
+ setenv(ctxt, "name", dir.name :: nil);
+ setenv(ctxt, "gid", dir.gid :: nil);
+ setenv(ctxt, "muid", dir.muid :: nil);
+ setenv(ctxt, "qid", sys->sprint("16r%ubx", dir.qid.path) :: string dir.qid.vers :: nil);
+ setenv(ctxt, "atime", string dir.atime :: nil);
+ setenv(ctxt, "dtype", sys->sprint("%c", dir.dtype) :: nil);
+ setenv(ctxt, "dev", string dir.dev :: nil);
+ }
+}
+
+start(startc: chan of (string, chan of string), name: string): chan of string
+{
+ c := chan of string;
+ startc <-= (name, c);
+ return c;
+}
+
+mtab := array[] of {
+ "---", "--x", "-w-", "-wx",
+ "r--", "r-x", "rw-", "rwx"
+};
+
+modes(mode: int): string
+{
+ s: string;
+
+ if(mode & Sys->DMDIR)
+ s = "d";
+ else if(mode & Sys->DMAPPEND)
+ s = "a";
+ else if(mode & Sys->DMAUTH)
+ s = "A";
+ else
+ s = "-";
+ if(mode & Sys->DMEXCL)
+ s += "l";
+ else
+ s += "-";
+ s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7];
+ return s;
+}
diff --git a/appl/alphabet/fs/run.b b/appl/alphabet/fs/run.b
new file mode 100644
index 00000000..6f6a38bb
--- /dev/null
+++ b/appl/alphabet/fs/run.b
@@ -0,0 +1,65 @@
+implement Run, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Run: module {};
+
+types(): string
+{
+ return "sc";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ sh = load Sh Sh->PATH;
+ if(sh == nil)
+ badmod(Sh->PATH);
+ sh->initialise();
+}
+
+run(drawctxt: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ c := (hd args).c().i;
+ ctxt := Context.new(drawctxt);
+ ctxt.setlocal("s", nil);
+ {
+ ctxt.run(ref Sh->Listnode(c, nil)::nil, 0);
+ } exception e {
+ "fail:*" =>
+ sys->fprint(sys->fildes(2), "fs: run: exception %q raised in %s\n", e[5:], sh->cmd2string(c));
+ return nil;
+ }
+ sl := ctxt.get("s");
+ if(sl == nil || tl sl != nil){
+ sys->fprint(sys->fildes(2), "fs: run: $s has %d members; exactly one is required\n", len sl);
+ return nil;
+ }
+ s := (hd sl).word;
+ if(s == nil && (hd sl).cmd != nil)
+ s = sh->cmd2string((hd sl).cmd);
+ return ref Value.Vs(s);
+}
diff --git a/appl/alphabet/fs/select.b b/appl/alphabet/fs/select.b
new file mode 100644
index 00000000..8a44104d
--- /dev/null
+++ b/appl/alphabet/fs/select.b
@@ -0,0 +1,60 @@
+implement Select, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Select: module {};
+types(): string
+{
+ return "ttp";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ dst := Entrychan(chan of int, chan of Entry);
+ spawn selectproc((hd args).t().i, dst, (hd tl args).p().i);
+ return ref Value.Vt(dst);
+}
+
+selectproc(src, dst: Entrychan, query: Gatechan)
+{
+ if(<-dst.sync == 0){
+ query <-= (Nilentry, nil);
+ src.sync <-= 0;
+ exit;
+ }
+ src.sync <-= 1;
+ reply := chan of int;
+ while((d := <-src.c).t0 != nil){
+ query <-= (d, reply);
+ if(<-reply)
+ dst.c <-= d;
+ }
+ dst.c <-= Nilentry;
+ query <-= (Nilentry, nil);
+}
diff --git a/appl/alphabet/fs/setroot.b b/appl/alphabet/fs/setroot.b
new file mode 100644
index 00000000..d04b7de5
--- /dev/null
+++ b/appl/alphabet/fs/setroot.b
@@ -0,0 +1,109 @@
+implement Setroot, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ Report: import Reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Setroot: module {};
+
+# set the root
+types(): string
+{
+ return "xxs-c";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, nil: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ root := (hd tl args).s().i;
+ if(root == nil && opts == nil){
+ sys->fprint(sys->fildes(2), "fs: setroot: empty path\n");
+ return nil;
+ }
+ v := ref Value.Vx(chan of (Fsdata, chan of int));
+ spawn setroot((hd args).x().i, v.i, root, opts != nil);
+ return v;
+}
+
+setroot(src, dst: Fschan, root: string, cflag: int)
+{
+ ((d, nil), reply) := <-src;
+ if(cflag){
+ createroot(src, dst, root, d, reply);
+ }else{
+ myreply := chan of int;
+ rd := ref *d;
+ rd.name = root;
+ dst <-= ((rd, nil), myreply);
+ if(<-myreply == Down){
+ reply <-= Down;
+ fs->copy(src, dst);
+ }
+ }
+}
+
+createroot(src, dst: Fschan, root: string, d: ref Sys->Dir, reply: chan of int)
+{
+ if(root == nil)
+ root = d.name;
+ (n, elems) := sys->tokenize(root, "/"); # XXX should really do a cleanname first
+ if(root[0] == '/'){
+ elems = "/" :: elems;
+ n++;
+ }
+ myreply := chan of int;
+ lev := 0;
+ r := -1;
+ for(; elems != nil; elems = tl elems){
+ rd := ref *d;
+ rd.name = hd elems;
+ dst <-= ((rd, nil), myreply);
+ case r = <-myreply {
+ Quit =>
+ (<-src).t1 <-= Quit;
+ exit;
+ Skip =>
+ break;
+ Next =>
+ lev++;
+ break;
+ }
+ lev++;
+ }
+ if(r == Down){
+ reply <-= Down;
+ if(fs->copy(src, dst) == Quit)
+ exit;
+ }else
+ reply <-= Quit;
+ while(lev-- > 1){
+ dst <-= ((nil, nil), myreply);
+ if(<-myreply == Quit){
+ (<-src).t1 <-= Quit;
+ exit;
+ }
+ }
+}
diff --git a/appl/alphabet/fs/size.b b/appl/alphabet/fs/size.b
new file mode 100644
index 00000000..966bf957
--- /dev/null
+++ b/appl/alphabet/fs/size.b
@@ -0,0 +1,64 @@
+implement Size, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Size: module {};
+
+types(): string
+{
+ return "ft";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: size: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ spawn sizeproc(f, (hd args).t().i, report.start("size"));
+ return ref Value.Vf(f);
+}
+
+sizeproc(f: chan of ref Sys->FD, c: Entrychan, errorc: chan of string)
+{
+ f <-= nil;
+ if((fd := <-f) == nil){
+ c.sync <-= 0;
+ exit;
+ }
+ c.sync <-= 1;
+
+ size := big 0;
+ while(((d, nil, nil) := <-c.c).t0 != nil)
+ size += d.length;
+ sys->fprint(fd, "%bd\n", size);
+ sys->fprint(fd, "");
+ errorc <-= nil;
+}
diff --git a/appl/alphabet/fs/unbundle.b b/appl/alphabet/fs/unbundle.b
new file mode 100644
index 00000000..3de7e7e2
--- /dev/null
+++ b/appl/alphabet/fs/unbundle.b
@@ -0,0 +1,259 @@
+implement Unbundle, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "string.m";
+ str: String;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, quit, report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Quit, Next, Skip, Down,
+ Option: import Fs;
+
+Unbundle: module {};
+types(): string
+{
+ return "xf";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: exec: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->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,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ f := (hd args).f().i;
+ c := ref Value.Vx(chan of (Fsdata, chan of int));
+ spawn unbundleproc((hd args).f().i, nil, c.i, -1, Sys->ATOMICIO, report.start("unbundle"));
+ return c;
+}
+
+# dodgy heuristic... avoid, or using the stat-length of pipes and net connections
+isseekable(fd: ref Sys->FD): int
+{
+ (ok, stat) := sys->fstat(fd);
+ if(ok != -1 && stat.dtype == '|' || stat.dtype == 'I')
+ return 0;
+ return 1;
+}
+
+EOF: con "end of archive\n";
+
+unbundleproc(f: chan of ref Sys->FD, iob: ref Iobuf, c: Fschan,
+ seekable, blocksize: int, errorc: chan of string)
+{
+ if(f != nil){
+ fd := <-f;
+ if(fd == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f <-= p[1];
+ p[1] = nil;
+ fd = p[0];
+ }else
+ f <-= nil;
+ if(seekable == -1)
+ seekable = isseekable(fd);
+ iob = bufio->fopen(fd, Sys->OREAD);
+ f = nil;
+ }
+
+ reply := chan of int;
+ p := iob.gets('\n');
+ # XXX overall header?
+ if(p == nil || p == EOF){
+ fs->sendnulldir(c);
+ quit(errorc);
+ }
+ d := header2dir(p);
+ if(d == nil){
+ fs->sendnulldir(c);
+ report(errorc, sys->sprint("invalid first header %q", p[0:len p - 1]));
+ quit(errorc);
+ }
+ if((d.mode & Sys->DMDIR) == 0){
+ fs->sendnulldir(c);
+ report(errorc, "first entry is not a directory");
+ quit(errorc);
+ }
+ c <-= ((d, nil), reply);
+ case r := <-reply {
+ Down =>
+ unbundledir(iob, c, 0, seekable, blocksize, errorc);
+ c <-= ((nil, nil), reply);
+ <-reply;
+ Skip or
+ Next =>
+ unbundledir(iob, c, 1, seekable, blocksize, errorc);
+ Quit =>
+ break;
+ }
+ quit(errorc);
+}
+
+unbundledir(iob: ref Iobuf, c: Fschan,
+ skipping, seekable, blocksize: int, errorc: chan of string): int
+{
+ reply := chan of int;
+ while((p := iob.gets('\n')) != nil){
+ if(p == EOF)
+ break;
+ if(p[0] == '\n')
+ break;
+ d := header2dir(p);
+ if(d == nil){
+ report(errorc, sys->sprint("invalid bundle header %q", p[0:len p - 1]));
+ return -1;
+ }
+ if(d.mode & Sys->DMDIR){
+ if(skipping)
+ continue;
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Down =>
+ r := unbundledir(iob, c, 0, seekable, blocksize, errorc);
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ if(r == -1)
+ return -1;
+ Skip =>
+ if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)
+ return -1;
+ skipping = 1;
+ Next =>
+ if(unbundledir(iob, c, 1, seekable, blocksize, errorc) == -1)
+ return -1;
+ }
+ }else{
+ if(skipping){
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ }else{
+ case unbundlefile(iob, d, c, errorc, seekable, blocksize) {
+ -1 =>
+ return -1;
+ Skip =>
+ skipping = 1;
+ }
+ }
+ }
+ }
+ if(p == nil)
+ report(errorc, "unexpected eof");
+ return 0;
+}
+
+skipdata(iob: ref Iobuf, length: big, seekable: int): int
+{
+ if(seekable){
+ iob.seek(big length, Sys->SEEKRELA);
+ return 0;
+ }
+ buf := array[Sys->ATOMICIO] of byte;
+ for(n := big 0; n < length; ){
+ nb := Sys->ATOMICIO;
+ if(length - n < big Sys->ATOMICIO)
+ nb = int (length - n);
+ nb = iob.read(buf, nb);
+ if(nb <= 0)
+ return -1;
+ n += big nb;
+ }
+ return 0;
+}
+
+unbundlefile(iob: ref Iobuf, d: ref Sys->Dir,
+ c: Fschan, errorc: chan of string, seekable, blocksize: int): int
+{
+ reply := chan of int;
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ return Skip;
+ Next =>
+ if(skipdata(iob, d.length, seekable) == -1)
+ return -1;
+ return Next;
+ }
+ length := d.length;
+ for(n := big 0; n < length; ){
+ nr := blocksize;
+ if(n + big blocksize > length)
+ nr = int (length - n);
+ buf := array[nr] of byte;
+ nr = iob.read(buf, nr);
+ if(nr <= 0){
+ if(nr < 0)
+ report(errorc, sys->sprint("read error: %r"));
+ else
+ report(errorc, sys->sprint("premature eof"));
+ return -1;
+ }else if(nr < len buf)
+ buf = buf[0:nr];
+ c <-= ((nil, buf), reply);
+ n += big nr;
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ if(skipdata(iob, length - n, seekable) == -1)
+ return -1;
+ return Next;
+ }
+ }
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+}
+
+header2dir(s: string): ref Sys->Dir
+{
+ toks := str->unquoted(s);
+ nf := len toks;
+ if(nf != 6)
+ return nil;
+ d := ref Sys->nulldir;
+ (d.name, toks) = (hd toks, tl toks);
+ (d.mode, toks) = (str->toint(hd toks, 8).t0, tl toks);
+ (d.uid, toks) = (hd toks, tl toks);
+ (d.gid, toks) = (hd toks, tl toks);
+ (d.mtime, toks) = (int hd toks, tl toks);
+ (d.length, toks) = (big hd toks, tl toks);
+ return d;
+}
diff --git a/appl/alphabet/fs/unbundle.m b/appl/alphabet/fs/unbundle.m
new file mode 100644
index 00000000..cb93005b
--- /dev/null
+++ b/appl/alphabet/fs/unbundle.m
@@ -0,0 +1,9 @@
+Unbundle: module {
+ PATH: con "/dis/fs/bundle.dis";
+
+ types: fn(): string;
+ init: fn();
+ run: fn(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value;
+ unbundle: fn(r: ref Reports->Report, iob: ref Bufio->Iobuf, seekable: int, blocksize: int): Fs->Fschan;
+};
diff --git a/appl/alphabet/fs/walk.b b/appl/alphabet/fs/walk.b
new file mode 100644
index 00000000..d99d0e9f
--- /dev/null
+++ b/appl/alphabet/fs/walk.b
@@ -0,0 +1,242 @@
+implement Walk, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "readdir.m";
+ readdir: Readdir;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, quit, report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Nilentry, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Walk: module {};
+
+Loopcheck: adt {
+ a: array of list of ref Sys->Dir;
+
+ new: fn(): ref Loopcheck;
+ enter: fn(l: self ref Loopcheck, d: ref Sys->Dir): int;
+ leave: fn(l: self ref Loopcheck, d: ref Sys->Dir);
+};
+
+types(): string
+{
+ return "xs-bs";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: walk: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil)
+ badmod(Readdir->PATH);
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ nil: list of Option, args: list of ref Value): ref Value
+{
+ path := (hd args).s().i;
+ (ok, d) := sys->stat(path);
+ if(ok== -1){
+ sys->fprint(sys->fildes(2), "fs: walk: cannot stat %q: %r\n", path);
+ return nil;
+ }
+ if((d.mode & Sys->DMDIR) == 0){
+ # XXX could produce an fs containing just the single file.
+ # would have to split the path though.
+ sys->fprint(sys->fildes(2), "fs: walk: %q is not a directory\n", path);
+ return nil;
+ }
+ sync := chan of int;
+ c := chan of (Fsdata, chan of int);
+ spawn fswalkproc(sync, path, c, Sys->ATOMICIO, report.start("walk"));
+ <-sync;
+ return ref Value.Vx(c);
+}
+
+# XXX need to avoid loops in the filesystem...
+fswalkproc(sync: chan of int, path: string, c: Fschan, blocksize: int, errorc: chan of string)
+{
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= 1;
+ # XXX could allow a single root file?
+ if(sys->chdir(path) == -1){
+ report(errorc, sys->sprint("cannot cd to %q: %r", path));
+ fs->sendnulldir(c);
+ quit(errorc);
+ }
+ (ok, d) := sys->stat(".");
+ if(ok == -1){
+ report(errorc, sys->sprint("cannot stat %q: %r", path));
+ fs->sendnulldir(c);
+ quit(errorc);
+ }
+ d.name = path;
+ reply := chan of int;
+ c <-= ((ref d, nil), reply);
+ if(<-reply == Down){
+ loopcheck := Loopcheck.new();
+ loopcheck.enter(ref d);
+ if(path[len path - 1] != '/')
+ path[len path] = '/';
+ fswalkdir(path, c, blocksize, loopcheck, errorc);
+ c <-= ((nil, nil), reply);
+ <-reply;
+ }
+ quit(errorc);
+}
+
+fswalkdir(path: string, c: Fschan, blocksize: int, loopcheck: ref Loopcheck, errorc: chan of string)
+{
+ reply := chan of int;
+ (a, n) := readdir->init(".", Readdir->NAME|Readdir->COMPACT);
+ if(n == -1){
+ report(errorc, sys->sprint("cannot readdir %q: %r", path));
+ return;
+ }
+ for(i := 0; i < n; i++)
+ if(a[i].mode & Sys->DMDIR)
+ if(loopcheck.enter(a[i]) == 0)
+ a[i].dtype = ~0;
+directory:
+ for(i = 0; i < n; i++){
+ if(a[i].mode & Sys->DMDIR){
+ d := a[i];
+ if(d.dtype == ~0){
+ report(errorc, sys->sprint("filesystem loop at %#q", path + d.name));
+ continue;
+ }
+ if(sys->chdir("./" + d.name) == -1){
+ report(errorc, sys->sprint("cannot cd to %#q: %r", path + a[i].name));
+ continue;
+ }
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Down =>
+ fswalkdir(path + a[i].name + "/", c, blocksize, loopcheck, errorc);
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ Skip =>
+ sys->chdir("..");
+ i++;
+ break directory;
+ Next =>
+ break;
+ }
+ if(sys->chdir("..") == -1) # XXX what should we do if this fails?
+ report(errorc, sys->sprint("failed to cd .. from %#q: %r\n", path + a[i].name));
+
+ } else {
+ if(fswalkfile(path, a[i], c, blocksize, errorc) == Skip)
+ break directory;
+ }
+ }
+ for(i = n - 1; i >= 0; i--)
+ if(a[i].mode & Sys->DMDIR && a[i].dtype != ~0)
+ loopcheck.leave(a[i]);
+}
+
+fswalkfile(path: string, d: ref Sys->Dir, c: Fschan, blocksize: int, errorc: chan of string): int
+{
+ reply := chan of int;
+ fd := sys->open(d.name, Sys->OREAD);
+ if(fd == nil){
+ report(errorc, sys->sprint("cannot open %q: %r", path+d.name));
+ return Next;
+ }
+ c <-= ((d, nil), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ return Skip;
+ Next =>
+ return Next;
+ Down =>
+ break;
+ }
+ length := d.length;
+ for(n := big 0; n < length; ){
+ nr := blocksize;
+ if(n + big blocksize > length)
+ nr = int (length - n);
+ buf := array[nr] of byte;
+ nr = sys->read(fd, buf, nr);
+ if(nr <= 0){
+ if(nr < 0)
+ report(errorc, sys->sprint("error reading %q: %r", path + d.name));
+ else
+ report(errorc, sys->sprint("%q is shorter than expected (%bd/%bd)",
+ path + d.name, n, length));
+ break;
+ }else if(nr < len buf)
+ buf = buf[0:nr];
+ c <-= ((nil, buf), reply);
+ case <-reply {
+ Quit =>
+ quit(errorc);
+ Skip =>
+ return Next;
+ }
+ n += big nr;
+ }
+ c <-= ((nil, nil), reply);
+ if(<-reply == Quit)
+ quit(errorc);
+ return Next;
+}
+
+HASHSIZE: con 32;
+
+issamedir(d0, d1: ref Sys->Dir): int
+{
+ (q0, q1) := (d0.qid, d1.qid);
+ return q0.path == q1.path &&
+ q0.qtype == q1.qtype &&
+ d0.dtype == d1.dtype &&
+ d0.dev == d1.dev;
+}
+
+Loopcheck.new(): ref Loopcheck
+{
+ return ref Loopcheck(array[HASHSIZE] of list of ref Sys->Dir);
+}
+
+# XXX we're assuming no-one modifies the values in d behind our back...
+Loopcheck.enter(l: self ref Loopcheck, d: ref Sys->Dir): int
+{
+ slot := int d.qid.path & (HASHSIZE-1);
+ for(ll := l.a[slot]; ll != nil; ll = tl ll)
+ if(issamedir(d, hd ll))
+ return 0;
+ l.a[slot] = d :: l.a[slot];
+ return 1;
+}
+
+Loopcheck.leave(l: self ref Loopcheck, d: ref Sys->Dir)
+{
+ slot := int d.qid.path & (HASHSIZE-1);
+ l.a[slot] = tl l.a[slot];
+}
diff --git a/appl/alphabet/fs/write.b b/appl/alphabet/fs/write.b
new file mode 100644
index 00000000..272c2f71
--- /dev/null
+++ b/appl/alphabet/fs/write.b
@@ -0,0 +1,137 @@
+implement Write, Fsmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet/fs.m";
+ fs: Fs;
+ Value: import fs;
+ Fschan, Fsdata, Entrychan, Entry,
+ Gatechan, Gatequery, Option,
+ Next, Down, Skip, Quit: import Fs;
+
+Write: module {};
+types(): string
+{
+ return "rxs-v";
+}
+
+badmod(p: string)
+{
+ sys->fprint(sys->fildes(2), "fs: write: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ fs = load Fs Fs->PATH;
+ if(fs == nil)
+ badmod(Fs->PATH);
+ fs->init();
+ reports = load Reports Reports->PATH;
+ if(reports == nil)
+ badmod(Reports->PATH);
+}
+
+run(nil: ref Draw->Context, report: ref Report,
+ opts: list of Option, args: list of ref Value): ref Value
+{
+ sync := chan of string;
+ spawn fswriteproc(sync, (hd tl args).s().i, (hd args).x().i, report.start("fswrite"), opts!=nil);
+ <-sync;
+ return ref Value.Vr(sync);
+}
+
+fswriteproc(sync: chan of string, root: string, c: Fschan, errorc: chan of string, verbose: int)
+{
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= nil;
+ if(<-sync != nil){
+ (<-c).t1 <-= Quit;
+ quit(sync, errorc);
+ }
+
+ (d, reply) := <-c;
+ if(root != nil){
+ d.dir = ref *d.dir;
+ d.dir.name = root;
+ }
+ fswritedir(d.dir.name, d, reply, c, errorc, verbose);
+ quit(sync, errorc);
+}
+
+quit(sync: chan of string, errorc: chan of string)
+{
+ errorc <-= nil;
+ sync <-= nil;
+ exit;
+}
+
+fswritedir(path: string, d: Fsdata, dreply: chan of int, c: Fschan, errorc: chan of string, verbose: int)
+{
+ fd: ref Sys->FD;
+ if(verbose)
+ report(errorc, sys->sprint("create %q %uo", path, d.dir.mode));
+ if(d.dir.mode & Sys->DMDIR){
+ created := 1;
+ fd = sys->create(d.dir.name, Sys->OREAD, d.dir.mode|8r777);
+ if(fd == nil){
+ err := sys->sprint("%r");
+ if((fd = sys->open(d.dir.name, Sys->OREAD)) == nil){
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot create %q, mode %uo: %s", path, d.dir.mode|8r300, err));
+ return;
+ }else
+ created = 0;
+ }
+ if(sys->chdir(d.dir.name) == -1){ # XXX beware of names starting with '#'
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot cd to %q: %r", path));
+ fd = nil;
+ sys->remove(d.dir.name);
+ return;
+ }
+ dreply <-= Down;
+ path[len path] = '/';
+ for(;;){
+ (ent, reply) := <-c;
+ if(ent.dir == nil){
+ reply <-= Next;
+ break;
+ }
+ fswritedir(path + ent.dir.name, ent, reply, c, errorc, verbose);
+ }
+ sys->chdir("..");
+ if(created && (d.dir.mode & 8r777) != 8r777){
+ ws := Sys->nulldir;
+ ws.mode = d.dir.mode;
+ if(sys->fwstat(fd, ws) == -1)
+ report(errorc, sys->sprint("cannot wstat %q: %r", path));
+ }
+ }else{
+ fd = sys->create(d.dir.name, Sys->OWRITE, d.dir.mode);
+ if(fd == nil){
+ dreply <-= Next;
+ report(errorc, sys->sprint("cannot create %q, mode %uo: %r", path, d.dir.mode|8r300));
+ return;
+ }
+ dreply <-= Down;
+ while((((nil, buf), reply) := <-c).t0.data != nil){
+ nw := sys->write(fd, buf, len buf);
+ if(nw < len buf){
+ if(nw == -1)
+ errorc <-= sys->sprint("error writing %q: %r", path);
+ else
+ errorc <-= sys->sprint("short write");
+ reply <-= Skip;
+ break;
+ }
+ reply <-= Next;
+ }
+ reply <-= Next;
+ }
+}
diff --git a/appl/alphabet/fsdecl.sh b/appl/alphabet/fsdecl.sh
new file mode 100644
index 00000000..9c63ff1d
--- /dev/null
+++ b/appl/alphabet/fsdecl.sh
@@ -0,0 +1,13 @@
+load alphabet std
+
+typeset /fs
+
+declare /fs/walk
+declare /fs/entries
+declare /fs/match
+declare /fs/print
+
+autoconvert /string /fs/fs /fs/walk
+autoconvert /fs/fs /fs/entries /fs/entries
+autoconvert /string /fs/gate /fs/match
+autoconvert /fs/entries /fd /fs/print
diff --git a/appl/alphabet/getendpoint.sh b/appl/alphabet/getendpoint.sh
new file mode 100755
index 00000000..8d6cabd4
--- /dev/null
+++ b/appl/alphabet/getendpoint.sh
@@ -0,0 +1,13 @@
+#!/dis/sh -n
+autoload=std
+load std
+if{! ~ $#* 1}{
+ echo usage: getendpoint addr >[1=2]
+ raise usage
+}
+addr:=$1
+if{! ftest -e /n/endpoint/dsgdsfgeafreqeq}{
+ mount {mntgen} /n/endpoint
+}
+mount -A $addr /n/endpoint/$addr
+bind /n/endpoint/$addr /n/endpoint/local
diff --git a/appl/alphabet/grid/farm.b b/appl/alphabet/grid/farm.b
new file mode 100644
index 00000000..4aaa8758
--- /dev/null
+++ b/appl/alphabet/grid/farm.b
@@ -0,0 +1,144 @@
+implement Farm, Gridmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+include "string.m";
+ str: String;
+include "alphabet/reports.m";
+ reports: Reports;
+ report, Report, quit: import reports;
+include "alphabet/endpoints.m";
+ endpoints: Endpoints;
+ Endpoint: import endpoints;
+include "alphabet/grid.m";
+ grid: Grid;
+ Value: import grid;
+
+Farm: module {};
+
+types(): string
+{
+ return "eesss*-A-k-a-v-bs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH);
+ endpoints->init();
+ grid = checkload(load Grid Grid->PATH, Grid->PATH);
+ grid->init();
+ sh = checkload(load Sh Sh->PATH, Sh->PATH);
+ sh->initialise();
+ str = checkload(load String String->PATH, String->PATH);
+}
+
+run(nil: chan of string, r: ref Reports->Report,
+ opt: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value
+{
+ ec0 := (hd args).e().i;
+ addr := (hd tl args).s().i;
+ job, opts: string;
+ noauth := 0;
+ for(; opt != nil; opt = tl opt){
+ c := (hd opt).t0;
+ case (hd opt).t0 {
+ 'A' =>
+ noauth = 1;
+ 'b' =>
+ opts += " -b "+(hd (hd opt).t1).s().i;
+ * =>
+ opts += sys->sprint(" -%c", (hd opt).t0);
+ }
+ }
+ for(args = tl tl args; args != nil; args = tl args)
+ job += sys->sprint(" %q", (hd args).s().i);
+
+ spawn farmproc(sync := chan of int, addr, ec0, opts, job, noauth, r.start("farm"), ec := chan of Endpoint);
+ <-sync;
+ return ref Value.Ve(ec);
+}
+
+farmproc(sync: chan of int,
+ addr: string,
+ ec0: chan of Endpoint,
+ opts: string,
+ job: string,
+ noauth: int,
+ errorc: chan of string,
+ ec1: chan of Endpoint)
+{
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= 1;
+ ep0 := <-ec0;
+ if(ep0.addr == nil){
+ ec1 <-= ep0;
+ quit(errorc);
+ }
+ (v, e) := farm(addr, ep0, opts, job, noauth, errorc);
+ if(e != nil){
+ endpoints->open(nil, ep0);
+ report(errorc, "error: "+e);
+ }
+ ec1 <-= v;
+ quit(errorc);
+}
+
+Nope: con Endpoint(nil, nil, nil);
+
+farm(addr: string,
+ ep0: Endpoint,
+ opts: string,
+ job: string,
+ noauth: int,
+ errorc: chan of string): (Endpoint, string)
+{
+ args := addr::"/n/remote"::nil;
+ if(noauth)
+ args = "-A"::args;
+ if((e := sh->run(nil, "mount"::args)) != nil)
+ return (Nope, sys->sprint("cannot mount scheduler at %q: %s, args %s", addr, e, str->quoted(args)));
+
+ fd := sys->open("/n/remote/admin/clone", Sys->ORDWR);
+ if(fd == nil)
+ return (Nope, sys->sprint("cannot open clone: %r"));
+ if((d := gets(fd)) == nil)
+ return (Nope, "read clone failed");
+ dir := "/n/remote/admin/"+d;
+ if(sys->fprint(fd, "load workflow%s %q %s", opts, ep0.text(), job) == -1)
+ return (Nope, sys->sprint("job load failed: %r"));
+ if(sys->fprint(fd, "start") == -1)
+ return (Nope, sys->sprint("job start failed: %r"));
+ dfd := sys->open(dir+"/data", Sys->OREAD);
+ if(dfd == nil){
+ sys->fprint(fd, "delete");
+ return (Nope, sys->sprint("cannot open job data file: %r"));
+ }
+ s := gets(dfd);
+ ep1 := Endpoint.mk(s);
+ if(ep1.addr == nil)
+ return (Nope, sys->sprint("bad remote endpoint %q", s));
+ report(errorc, sys->sprint("job %s started, id %s", d, gets(sys->open(dir+"/id", Sys->OREAD))));
+ # XXX how is the job going to be deleted eventually
+ ep1.about = sys->sprint("%s | farm%s %s%s", ep0.about, opts, addr, job);
+ return (ep1, nil);
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
+
+gets(fd: ref Sys->FD): string
+{
+ d := array[8192] of byte;
+ n := sys->read(fd, d, len d);
+ if(n <= 0)
+ return nil;
+ return string d[0:n];
+}
diff --git a/appl/alphabet/grid/line2rec.b b/appl/alphabet/grid/line2rec.b
new file mode 100644
index 00000000..2429a67d
--- /dev/null
+++ b/appl/alphabet/grid/line2rec.b
@@ -0,0 +1,91 @@
+implement Line2rec, Gridmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet/endpoints.m";
+include "alphabet/grid.m";
+ grid: Grid;
+ Value: import grid;
+
+Line2rec: module {};
+
+types(): string
+{
+ return "bf";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ grid = load Grid Grid->PATH;
+ reports = load Reports Reports->PATH;
+ bufio = load Bufio Bufio->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: chan of string, r: ref Report,
+ nil: list of (int, list of ref Value), args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ spawn line2recproc((hd args).f().i, f, r.start("line2rec"));
+ return ref Value.Vb(f);
+}
+
+line2recproc(
+ f0,
+ f1: chan of ref Sys->FD,
+ errorc: chan of string)
+{
+ (fd0, fd1) := startfilter(f0, f1, errorc);
+ iob0 := bufio->fopen(fd0, Sys->OREAD);
+ iob1 := bufio->fopen(fd1, Sys->OWRITE);
+ {
+ while((s := iob0.gets('\n')) != nil){
+ d := array of byte s;
+ if(iob1.puts("data "+string len d) < 0)
+ break;
+ if(iob1.write(d, len d) != len d)
+ break;
+ }
+ iob1.flush();
+ sys->fprint(fd1, "");
+ }exception{
+ "write on closed pipe" =>
+ ;
+ }
+ reports->quit(errorc);
+}
+
+# read side (when it's an argument):
+# read proposed new fd
+# write actual fd for them to write to (creating pipe in necessary)
+#
+# write side (when you're returning it):
+# write a proposed new fd (or nil if no suggestion)
+# read actual fd for writing
+startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD)
+{
+ f1 <-= nil;
+ if((fd1 := <-f1) == nil){
+ <-f0;
+ f0 <-= nil;
+ reports->quit(errorc);
+ }
+ if((fd0 := <-f0) == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f0 <-= p[1];
+ fd0 = p[0];
+ }else
+ f0 <-= nil;
+ return (fd0, fd1);
+}
diff --git a/appl/alphabet/grid/local.b b/appl/alphabet/grid/local.b
new file mode 100644
index 00000000..2fca7b95
--- /dev/null
+++ b/appl/alphabet/grid/local.b
@@ -0,0 +1,86 @@
+implement Local,Gridmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report, quit, Report: import reports;
+include "alphabet/endpoints.m";
+ endpoints: Endpoints;
+ Endpoint: import endpoints;
+include "alphabet/grid.m";
+ grid: Grid;
+ Value: import grid;
+
+Local: module {};
+types(): string
+{
+ return "fe-v";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH);
+ endpoints->init();
+ grid = checkload(load Grid Grid->PATH, Grid->PATH);
+ grid->init();
+}
+
+run(nil: chan of string, r: ref Reports->Report,
+ opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value
+{
+
+ spawn localproc((hd args).e().i, f := chan of ref Sys->FD, opts!=nil, r.start("local"));
+ return ref Value.Vf(f);
+}
+
+localproc(ec: chan of Endpoint, f: chan of ref Sys->FD, verbose: int, errorc: chan of string)
+{
+ ep := <-ec;
+ if(ep.addr == nil){
+ # error should already have been printed (XXX is that the right way to do it?)
+ f <-= nil;
+ <-f;
+ quit(errorc);
+ }
+ if(verbose)
+ report(errorc, sys->sprint("endpoint %q at %q: %s", ep.id, ep.addr, ep.about));
+ (fd0, err) := endpoints->open(nil, ep);
+ if(fd0 == nil){
+ report(errorc, sys->sprint("error: local: cannot open endpoint (%q %q): %s", ep.addr, ep.id, err));
+ f <-= nil;
+ <-f;
+ quit(errorc);
+ }
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ quit(errorc);
+
+ buf := array[Sys->ATOMICIO] of byte;
+ {
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+#sys->print("local read %d bytes\n", n);
+ sys->write(fd1, buf, n);
+ }
+#sys->print("local eof %d\n", n);
+ sys->write(fd1, array[0] of byte, 0);
+ if(n < 0)
+ report(errorc, sys->sprint("read error: %r"));
+ } exception e {
+ "write on closed pipe" =>
+ report(errorc, "write on closed pipe");
+ ;
+ }
+ quit(errorc);
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/grid/mkfile b/appl/alphabet/grid/mkfile
new file mode 100644
index 00000000..c8e78ffb
--- /dev/null
+++ b/appl/alphabet/grid/mkfile
@@ -0,0 +1,22 @@
+<../../../mkconfig
+
+TARG=\
+ farm.dis\
+ line2rec.dis\
+ local.dis\
+ remote.dis\
+ rexec.dis\
+
+SYSMODULES=\
+ draw.m\
+ alphabet/endpoints.m\
+ alphabet/grid.m\
+ alphabet/reports.m\
+ sh.m\
+ string.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/alphabet/grid
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
diff --git a/appl/alphabet/grid/remote.b b/appl/alphabet/grid/remote.b
new file mode 100644
index 00000000..dbdc86ef
--- /dev/null
+++ b/appl/alphabet/grid/remote.b
@@ -0,0 +1,88 @@
+implement Remote, Gridmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ report, quit, Report: import reports;
+include "alphabet/endpoints.m";
+ endpoints: Endpoints;
+ Endpoint: import endpoints;
+include "alphabet/grid.m";
+ grid: Grid;
+ Value: import grid;
+
+Remote: module {};
+
+types(): string
+{
+ return "ef-as";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH);
+ endpoints->init();
+ grid = checkload(load Grid Grid->PATH, Grid->PATH);
+ grid->init();
+}
+
+run(nil: chan of string, r: ref Reports->Report,
+ opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value
+{
+ addr := "local";
+ if(opts != nil)
+ addr = (hd (hd opts).t1).s().i;
+ f := (hd args).f().i;
+ spawn remoteproc(ec := chan of Endpoint, f, addr, r.start("remote"));
+ return ref Value.Ve(ec);
+}
+
+Noendpoint: con Endpoint(nil, nil, nil);
+
+remoteproc(ec: chan of Endpoint, f: chan of ref Sys->FD, addr: string, errorc: chan of string)
+{
+ (fd1, ep) := endpoints->create(addr);
+ if(fd1 == nil){
+ report(errorc, "error: remote: cannot create endpoint at "+addr+": "+ep.about);
+ ec <-= Noendpoint;
+ <-f;
+ f <-= nil;
+ quit(errorc);
+ }
+ fd0 := <-f;
+ if(fd0 != nil)
+ ep.about = sys->sprint("local(%#q)", sys->fd2path(fd0));
+ else
+ ep.about = "local(pipe)";
+ ec <-= ep;
+ f <-= fd1;
+ quit(errorc);
+}
+
+# sys->pipe(p := array[2] of ref Sys->FD);
+# f <-= p[1];
+# p[1] = nil;
+# buf := array[Sys->ATOMICIO] of byte;
+# while((n := sys->read(p[0], buf, len buf)) > 0){
+# if(sys->write(fd, buf, n) == -1){
+# report(errorc, sys->sprint("write error: %r"));
+# break;
+# }
+# }exception{
+# "write on closed pipe" =>
+# report(errorc, "got write on closed pipe");
+# }
+# sys->write(fd, array[0] of byte, 0);
+# quit(errorc);
+#}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/grid/rexec.b b/appl/alphabet/grid/rexec.b
new file mode 100644
index 00000000..02869659
--- /dev/null
+++ b/appl/alphabet/grid/rexec.b
@@ -0,0 +1,112 @@
+implement Rexec, Gridmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+include "string.m";
+ str: String;
+include "alphabet/reports.m";
+ reports: Reports;
+ report, Report, quit: import reports;
+include "alphabet/endpoints.m";
+ endpoints: Endpoints;
+ Endpoint: import endpoints;
+include "alphabet/grid.m";
+ grid: Grid;
+ Value: import grid;
+
+Rexec: module {};
+
+types(): string
+{
+ return "eesc-A";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ reports = checkload(load Reports Reports->PATH, Reports->PATH);
+ endpoints = checkload(load Endpoints Endpoints->PATH, Endpoints->PATH);
+ endpoints->init();
+ grid = checkload(load Grid Grid->PATH, Grid->PATH);
+ grid->init();
+ sh = checkload(load Sh Sh->PATH, Sh->PATH);
+ sh->initialise();
+ str = checkload(load String String->PATH, String->PATH);
+}
+
+run(nil: chan of string, r: ref Reports->Report,
+ opts: list of (int, list of ref Grid->Value), args: list of ref Grid->Value): ref Grid->Value
+{
+ ec0 := (hd args).e().i;
+ addr := (hd tl args).s().i;
+ cmd := (hd tl tl args).c().i;
+
+ spawn rexecproc(sync := chan of int, addr, ec0, cmd, r.start("rexec"), opts != nil, ec1 := chan of Endpoint);
+ <-sync;
+ return ref Value.Ve(ec1);
+}
+
+rexecproc(sync: chan of int,
+ addr: string,
+ ec0: chan of Endpoint,
+ cmd: ref Sh->Cmd,
+ errorc: chan of string,
+ noauth: int,
+ ec1: chan of Endpoint
+ )
+{
+ sys->pctl(Sys->FORKNS, nil);
+ sync <-= 1;
+
+ ep0 := <-ec0;
+ if(ep0.addr == nil){
+ ec1 <-= ep0;
+ quit(errorc);
+ }
+
+ (ep1, err) := exec(addr, ep0, cmd, noauth);
+ if(err != nil){
+ endpoints->open(nil, ep0); # discard
+ report(errorc, err);
+ }
+ ec1 <-= ep1;
+ quit(errorc);
+}
+
+Nope: con Endpoint(nil, nil, nil);
+
+exec(addr: string, ep0: Endpoint, cmd: ref Sh->Cmd, noauth: int): (Endpoint, string)
+{
+ args := addr::"/n/remote"::nil;
+ if(noauth)
+ args = "-A"::args;
+ if((e := sh->run(nil, "mount"::args)) != nil)
+ return (Nope, sys->sprint("cannot mount rexec at %q: %s", addr, e));
+
+ fd := sys->open("/n/remote/exec", Sys->ORDWR);
+ if(fd == nil)
+ return (Nope, sys->sprint("cannot open exec at %q: %r", addr));
+ if(sys->fprint(fd, "%q %q", ep0.text(), sh->cmd2string(cmd)) == -1)
+ return (Nope, sys->sprint("exec write failed: %r"));
+ buf := array[1024] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return (Nope, sys->sprint("error reading endpoint: %r"));
+ if(n == 0)
+ return (Nope, "eof reading endpoint");
+ s := string buf[0:n];
+ ep1 := Endpoint.mk(s);
+ if(ep1.addr == nil)
+ return (Nope, sys->sprint("bad endpoint %#q: %s", s, ep1.about));
+ ep1.about = sys->sprint("%s | rexec %q %s", ep0.about, addr, sh->cmd2string(cmd));
+ return (ep1, nil);
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ raise sys->sprint("fail:cannot load %s: %r", path);
+}
diff --git a/appl/alphabet/main/auth.b b/appl/alphabet/main/auth.b
new file mode 100644
index 00000000..d05ecb46
--- /dev/null
+++ b/appl/alphabet/main/auth.b
@@ -0,0 +1,157 @@
+implement Authenticate, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ auth: Auth;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Authenticate: module {};
+
+typesig(): string
+{
+ return "ww-ks-Cs-v";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+ keyring = load Keyring Keyring->PATH;
+ auth = load Auth Auth->PATH;
+ auth->init();
+}
+
+quit()
+{
+}
+
+After, Before, Create: con 1<<iota;
+
+run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ keyfile: string;
+ alg: string;
+ verbose: int;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).t0 {
+ 'k' =>
+ keyfile = (hd (hd opts).t1).s().i;
+ if (keyfile != nil && ! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./")))
+ keyfile = "/usr/" + user() + "/keyring/" + keyfile;
+ 'C' =>
+ alg = (hd (hd opts).t1).s().i;
+ 'v' =>
+ verbose = 1;
+ }
+ }
+ if(keyfile == nil)
+ keyfile = "/usr/" + user() + "/keyring/default";
+ cert := keyring->readauthinfo(keyfile);
+ if (cert == nil) {
+ report(errorc, sys->sprint("auth: cannot read %q: %r", keyfile));
+ return nil;
+ }
+ w := chan of ref Sys->FD;
+ spawn authproc((hd args).w().i, w, cert, verbose, alg, r.start("auth"));
+ return ref Value.Vw(w);
+}
+
+authproc(f0, f1: chan of ref Sys->FD, cert: ref Keyring->Authinfo,
+ verbose: int, alg: string, errorc: chan of string)
+{
+ fd0 := <-f0;
+ if(fd0 == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f0 <-= p[1];
+ fd0 = p[0];
+ }else
+ f0 <-= nil;
+
+ eu: string;
+ (fd0, eu) = auth->client(alg, cert, fd0);
+ if(fd0 == nil){
+ report(errorc, "authentication failed: "+eu);
+ f1 <-= nil;
+ <-f1;
+ reports->quit(errorc);
+ }
+ if(verbose)
+ report(errorc, sys->sprint("remote user %q", eu));
+ f1 <-= fd0;
+ fd1 := <-f1;
+ if(fd1 == nil)
+ reports->quit(errorc);
+ wstream(fd0, fd1, errorc);
+ reports->quit(errorc);
+}
+
+wstream(fd0, fd1: ref Sys->FD, errorc: chan of string)
+{
+ sync := chan[2] of int;
+ qc := chan of int;
+ spawn stream(fd0, fd1, sync, qc, errorc);
+ spawn stream(fd1, fd0, sync, qc, errorc);
+ <-qc;
+ kill(<-sync);
+ kill(<-sync);
+}
+
+stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string)
+{
+ sync <-= sys->pctl(0, nil);
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+ if(sys->write(fd1, buf, n) == -1){
+ report(errorc, sys->sprint("write error: %r"));
+ break;
+ }
+ }
+ qc <-= 1;
+ exit;
+}
+
+kill(pid: int)
+{
+ sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
+}
+
+
+exists(f: string): int
+{
+ (ok, nil) := sys->stat(f);
+ return ok != -1;
+}
+
+user(): string
+{
+ u := readfile("/dev/user");
+ if (u == nil)
+ return "nobody";
+ return u;
+}
+
+readfile(f: string): string
+{
+ fd := sys->open(f, sys->OREAD);
+ if(fd == nil)
+ return nil;
+
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return nil;
+
+ return string buf[0:n];
+}
diff --git a/appl/alphabet/main/cat.b b/appl/alphabet/main/cat.b
new file mode 100644
index 00000000..b19b1fd9
--- /dev/null
+++ b/appl/alphabet/main/cat.b
@@ -0,0 +1,78 @@
+implement Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+typesig(): string
+{
+ return "ff*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ fds: list of chan of ref Sys->FD;
+ for(; args != nil; args = tl args)
+ fds = (hd args).f().i :: fds;
+ f := chan of ref Sys->FD;
+ spawn catproc(f, rev(fds), r.start("print"));
+ return ref Value.Vf(f);
+}
+
+catproc(f: chan of ref Sys->FD, fds: list of chan of ref Sys->FD, reportc: chan of string)
+{
+ f <-= nil;
+ if((fd1 := <-f) == nil){
+ for(; fds != nil; fds = tl fds){
+ <-hd fds;
+ hd fds <-= nil;
+ }
+ reports->quit(reportc);
+ }
+ buf := array[8192] of byte;
+ for(; fds != nil; fds = tl fds){
+ fd0 := <-hd fds;
+ if(fd0 == nil){
+ p := array[2] of ref Sys->FD;
+ sys->pipe(p);
+ fd0 = p[0];
+ hd fds <-= p[1];
+ }else
+ hd fds <-= nil;
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+ sys->write(fd1, buf, n);
+ }exception{
+ "write on closed pipe" =>
+ ;
+ }
+ }
+ sys->write(fd1, array[0] of byte, 0);
+ reports->quit(reportc);
+}
+
+rev[T](l: list of T): list of T
+{
+ r: list of T;
+ for(; l != nil; l = tl l)
+ r = hd l :: r;
+ return r;
+} \ No newline at end of file
diff --git a/appl/alphabet/main/create.b b/appl/alphabet/main/create.b
new file mode 100644
index 00000000..bb1601dc
--- /dev/null
+++ b/appl/alphabet/main/create.b
@@ -0,0 +1,55 @@
+implement Create,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Create: module {};
+
+typesig(): string
+{
+ return "rfs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ r := chan of string;
+ fd := sys->create((hd tl args).s().i, Sys->OWRITE, 8r666);
+ if(fd == nil){
+ report(errorc, sys->sprint("error: cannot create %q: %r", (hd tl args).s().i));
+ return nil;
+ }
+ spawn createproc(r, (hd args).f().i, fd);
+ return ref Value.Vr(r);
+}
+
+createproc(r: chan of string, f: chan of ref Sys->FD, fd: ref Sys->FD)
+{
+ if(<-r != nil){
+ <-f;
+ f <-= nil;
+ exit;
+ }
+ <-f;
+ f <-= fd;
+ r <-= nil;
+}
diff --git a/appl/alphabet/main/dial.b b/appl/alphabet/main/dial.b
new file mode 100644
index 00000000..e8521b45
--- /dev/null
+++ b/appl/alphabet/main/dial.b
@@ -0,0 +1,85 @@
+implement Dial,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Dial: module {};
+
+typesig(): string
+{
+ return "ws";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ w := chan of ref Sys->FD;
+ addr := (hd args).s().i;
+ (ok, c) := sys->dial(addr, nil);
+ if(ok == -1){
+ report(errorc, sys->sprint("dial: cannot dial %q: %r", addr));
+ return nil;
+ }
+ f := chan of ref Sys->FD;
+ spawn dialproc(f, c.dfd, r.start("dial"));
+ return ref Value.Vw(f);
+}
+
+dialproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string)
+{
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ reports->quit(errorc);
+ wstream(fd0, fd1, errorc);
+ reports->quit(errorc);
+}
+
+wstream(fd0, fd1: ref Sys->FD, errorc: chan of string)
+{
+ sync := chan[2] of int;
+ qc := chan of int;
+ spawn stream(fd0, fd1, sync, qc, errorc);
+ spawn stream(fd1, fd0, sync, qc, errorc);
+ <-qc;
+ kill(<-sync);
+ kill(<-sync);
+}
+
+stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string)
+{
+ sync <-= sys->pctl(0, nil);
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+ if(sys->write(fd1, buf, n) == -1){
+ report(errorc, sys->sprint("write error: %r"));
+ break;
+ }
+ }
+ qc <-= 1;
+ exit;
+}
+
+kill(pid: int)
+{
+ sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
+}
diff --git a/appl/alphabet/main/echo.b b/appl/alphabet/main/echo.b
new file mode 100644
index 00000000..3b01c951
--- /dev/null
+++ b/appl/alphabet/main/echo.b
@@ -0,0 +1,51 @@
+implement Echo, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Echo: module {};
+
+typesig(): string
+{
+ return "fs-n";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ opts: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ f := chan of ref Sys->FD;
+ s := (hd args).s().i;
+ if(opts == nil)
+ s[len s] = '\n';
+ spawn echoproc(f, s);
+ return ref Value.Vf(f);
+}
+
+echoproc(f: chan of ref Sys->FD, s: string)
+{
+ f <-= nil;
+ fd := <-f;
+ if(fd == nil)
+ exit;
+ sys->fprint(fd, "%s", s);
+ sys->write(fd, array[0] of byte, 0);
+}
diff --git a/appl/alphabet/main/export.b b/appl/alphabet/main/export.b
new file mode 100644
index 00000000..5e9b986e
--- /dev/null
+++ b/appl/alphabet/main/export.b
@@ -0,0 +1,52 @@
+implement Export,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Export: module {};
+
+typesig(): string
+{
+ return "ws";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ w := chan of ref Sys->FD;
+ addr := (hd args).s().i;
+ f := chan of ref Sys->FD;
+ spawn exportproc(f, (hd args).s().i, r.start("export"));
+ return ref Value.Vw(f);
+}
+
+exportproc(f: chan of ref Sys->FD, dir: string, errorc: chan of string)
+{
+ f <-= nil;
+ fd := <-f;
+ if(fd == nil)
+ reports->quit(errorc);
+ errorc <-= nil;
+ if(sys->export(fd, dir, Sys->EXPASYNC) == -1)
+ report(errorc, sys->sprint("cannot export: %r"));
+ reports->quit(errorc);
+}
diff --git a/appl/alphabet/main/fd.b b/appl/alphabet/main/fd.b
new file mode 100644
index 00000000..11d10828
--- /dev/null
+++ b/appl/alphabet/main/fd.b
@@ -0,0 +1,83 @@
+implement Fd, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Fd: module {};
+
+typesig(): string
+{
+ return "ws";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ fd := sys->fildes(int (hd args).s().i);
+ if(fd == nil){
+ report(errorc, sys->sprint("error: no such file descriptor %q", (hd args).s().i));
+ return nil;
+ }
+ f := chan of ref Sys->FD;
+ spawn readfdproc(f, fd, r.start("stdin"));
+ return ref Value.Vw(f);
+}
+
+readfdproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string)
+{
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ reports->quit(errorc);
+ wstream(fd0, fd1, errorc);
+ reports->quit(errorc);
+}
+
+wstream(fd0, fd1: ref Sys->FD, errorc: chan of string)
+{
+ sync := chan[2] of int;
+ qc := chan of int;
+ spawn stream(fd0, fd1, sync, qc, errorc);
+ spawn stream(fd1, fd0, sync, qc, errorc);
+ <-qc;
+ kill(<-sync);
+ kill(<-sync);
+}
+
+stream(fd0, fd1: ref Sys->FD, sync, qc: chan of int, errorc: chan of string)
+{
+ sync <-= sys->pctl(0, nil);
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+ if(sys->write(fd1, buf, n) == -1){
+ report(errorc, sys->sprint("write error: %r"));
+ break;
+ }
+ }
+ qc <-= 1;
+ exit;
+}
+
+kill(pid: int)
+{
+ sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
+}
diff --git a/appl/alphabet/main/filter.b b/appl/alphabet/main/filter.b
new file mode 100644
index 00000000..f532f529
--- /dev/null
+++ b/appl/alphabet/main/filter.b
@@ -0,0 +1,114 @@
+implement Filter, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Filter: module {};
+
+typesig(): string
+{
+ return "ffcs*"; # XXX option to suppress stderr?
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+ bufio = load Bufio Bufio->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+}
+
+quit()
+{
+}
+
+run(drawctxt: ref Draw->Context, report: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ f := chan of ref Sys->FD;
+ a: list of ref Sh->Listnode;
+ for(al := tl tl args; al != nil; al = tl al)
+ a = ref Sh->Listnode(nil, (hd al).s().i) :: a;
+ spawn filterproc(drawctxt, (hd args).f().i, f, (hd tl args).c().i, rev(a), report.start("filter"));
+ return ref Value.Vf(f);
+}
+
+filterproc(drawctxt: ref Draw->Context,
+ f0,
+ f1: chan of ref Sys->FD,
+ c: ref Sh->Cmd,
+ args: list of ref Sh->Listnode,
+ errorc: chan of string)
+{
+ (fd0, fd1) := startfilter(f0, f1, errorc);
+ sys->pipe(p := array[2] of ref Sys->FD);
+ spawn stderrproc(p[0], errorc);
+ p[0] = nil;
+
+ # i hate this stuff.
+ sys->pctl(Sys->FORKFD, nil);
+ sys->dup(fd0.fd, 0);
+ sys->dup(fd1.fd, 1);
+ sys->dup(p[1].fd, 2);
+ fd0 = fd1 = nil;
+ p = nil;
+ sys->pctl(Sys->NEWFD, 0::1::2::nil);
+ Context.new(drawctxt).run(ref Sh->Listnode(c, nil)::args, 0);
+ sys->fprint(sys->fildes(2), "");
+}
+
+# read side (when it's an argument):
+# read proposed new fd
+# write actual fd for them to write to (creating pipe in necessary)
+#
+# write side (when you're returning it):
+# write a proposed new fd (or nil if no suggestion)
+# read actual fd for writing
+startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD)
+{
+ f1 <-= nil;
+ if((fd1 := <-f1) == nil){
+ <-f0;
+ f0 <-= nil;
+ reports->quit(errorc);
+ }
+ if((fd0 := <-f0) == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f0 <-= p[1];
+ fd0 = p[0];
+ }else
+ f0 <-= nil;
+ return (fd0, fd1);
+}
+
+stderrproc(fd: ref Sys->FD, errorc: chan of string)
+{
+ iob := bufio->fopen(fd, Sys->OREAD);
+ while((s := iob.gets('\n')) != nil)
+ if(len s > 1)
+ errorc <-= s[0:len s - 1];
+ errorc <-= nil;
+}
+
+rev[T](l: list of T): list of T
+{
+ r: list of T;
+ for(; l != nil; l = tl l)
+ r = hd l :: r;
+ return r;
+} \ No newline at end of file
diff --git a/appl/alphabet/main/genfilter.b b/appl/alphabet/main/genfilter.b
new file mode 100644
index 00000000..9a920d7e
--- /dev/null
+++ b/appl/alphabet/main/genfilter.b
@@ -0,0 +1,79 @@
+implement Myfilter, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Myfilter: module {};
+
+typesig(): string
+{
+ return "ff";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+ bufio = load Bufio Bufio->PATH;
+}
+
+quit()
+{
+}
+
+run(drawctxt: ref Draw->Context, report: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ f := chan of ref Sys->FD;
+ spawn filterproc(drawctxt, (hd args).f().i, f, report.start("myfilter"));
+ return ref Value.Vf(f);
+}
+
+filterproc(nil: ref Draw->Context, f0, f1: chan of ref Sys->FD, errorc: chan of string)
+{
+ (fd0, fd1) := startfilter(f0, f1, errorc);
+ iob0 := bufio->fopen(fd0, Sys->OREAD);
+ iob1 := bufio->fopen(fd1, Sys->OWRITE);
+
+ # XXX your filter here!
+ while((s := iob0.gets('\n')) != nil){
+ d := array of byte s;
+ iob1.puts("data "+string len d+"\n");
+ iob1.write(d, len d);
+ }exception{
+ "write on closed pipe" =>
+ ;
+ }
+ iob1.flush();
+ sys->fprint(fd1, "");
+ reports->quit(errorc);
+}
+
+startfilter(f0, f1: chan of ref Sys->FD, errorc: chan of string): (ref Sys->FD, ref Sys->FD)
+{
+ f1 <-= nil;
+ if((fd1 := <-f1) == nil){
+ <-f0;
+ f0 <-= nil;
+ reports->quit(errorc);
+ }
+ if((fd0 := <-f0) == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f0 <-= p[1];
+ fd0 = p[0];
+ }else
+ f0 <-= nil;
+ return (fd0, fd1);
+}
diff --git a/appl/alphabet/main/mkfile b/appl/alphabet/main/mkfile
new file mode 100644
index 00000000..ff2d95fb
--- /dev/null
+++ b/appl/alphabet/main/mkfile
@@ -0,0 +1,36 @@
+<../../../mkconfig
+
+TARG=\
+ auth.dis\
+ cat.dis\
+ create.dis\
+ dial.dis\
+ echo.dis\
+ env.dis\
+ export.dis\
+ fd.dis\
+ filter.dis\
+ mount.dis\
+ par.dis\
+ parse.dis\
+ pretty.dis\
+ print.dis\
+ read.dis\
+ readall.dis\
+ rewrite.dis\
+ seq.dis\
+ unparse.dis\
+ w2fd.dis\
+ wait.dis\
+
+SYSMODULES=\
+ alphabet.m\
+ alphabet/reports.m\
+ draw.m\
+ sh.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/alphabet/main
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
diff --git a/appl/alphabet/main/mount.b b/appl/alphabet/main/mount.b
new file mode 100644
index 00000000..1b27617d
--- /dev/null
+++ b/appl/alphabet/main/mount.b
@@ -0,0 +1,80 @@
+implement Mount,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Mount: module {};
+
+typesig(): string
+{
+ return "rws-a-b-c-xs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+After, Before, Create: con 1<<iota;
+
+run(nil: ref Draw->Context, report: ref Reports->Report, nil: chan of string,
+ opts: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ flag := Sys->MREPL;
+ aname := "";
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).t0 {
+ 'a' =>
+ flag = After & (flag&Sys->MCREATE);
+ 'b' =>
+ flag = Before & (flag&Sys->MCREATE);
+ 'c' =>
+ flag |= Create;
+ 'x' =>
+ aname = (hd (hd opts).t1).s().i;
+ }
+ }
+ r := chan of string;
+ spawn mountproc(r, (hd args).w().i, (hd tl args).s().i, aname, flag, report.start("mount"));
+ return ref Value.Vr(r);
+}
+
+mountproc(r: chan of string, w: chan of ref Sys->FD, dir, aname: string, flag: int, errorc: chan of string)
+{
+ if(<-r != nil){
+ errorc <-= nil;
+ <-w;
+ w <-= nil;
+ exit;
+ }
+ fd := <-w;
+ if(fd == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ w <-= p[0];
+ fd = p[1];
+ }else
+ w <-= nil;
+ if(sys->mount(fd, nil, dir, flag, aname) == -1){
+ e := sys->sprint("mount error on %#q: %r", dir);
+ report(errorc, e);
+ r <-= e;
+ exit;
+ }
+
+ errorc <-= nil;
+ r <-= nil;
+}
diff --git a/appl/alphabet/main/par.b b/appl/alphabet/main/par.b
new file mode 100644
index 00000000..d2b47ef3
--- /dev/null
+++ b/appl/alphabet/main/par.b
@@ -0,0 +1,50 @@
+implement Seq, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Seq: module {};
+
+typesig(): string
+{
+ return "rr*";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ spawn parproc(r := chan of string, args);
+ return ref Value.Vr(r);
+}
+
+parproc(r: chan of string, args: list of ref Alphabet->Value)
+{
+ if(<-r != nil){
+ for(; args != nil; args = tl args)
+ (hd args).r().i <-= "die!";
+ }else{
+ status := "";
+ for(a := args; a != nil; a = tl a)
+ (hd a).r().i <-= nil;
+ for(; args != nil; args = tl args)
+ if((e := <-(hd args).r().i) != nil)
+ status = e;
+ r <-= status;
+ }
+}
diff --git a/appl/alphabet/main/parse.b b/appl/alphabet/main/parse.b
new file mode 100644
index 00000000..c02ba124
--- /dev/null
+++ b/appl/alphabet/main/parse.b
@@ -0,0 +1,43 @@
+implement Parse, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Parse: module {};
+
+typesig(): string
+{
+ return "cs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ (c, err) := sh->parse((hd args).s().i);
+ if(c == nil){
+ report(errorc, sys->sprint("parse: parse %q failed: %s", (hd args).s().i, err));
+ return nil;
+ }
+ return ref Value.Vc(c);
+}
diff --git a/appl/alphabet/main/pretty.b b/appl/alphabet/main/pretty.b
new file mode 100644
index 00000000..52d8b32f
--- /dev/null
+++ b/appl/alphabet/main/pretty.b
@@ -0,0 +1,116 @@
+implement Pretty, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ n_BLOCK, n_VAR, n_BQ, n_BQ2, n_REDIR,
+ n_DUP, n_LIST, n_SEQ, n_CONCAT, n_PIPE, n_ADJ,
+ n_WORD, n_NOWAIT, n_SQUASH, n_COUNT,
+ n_ASSIGN, n_LOCAL,
+ GLOB: import Sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Pretty: module {};
+
+typesig(): string
+{
+ return "sc";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ {
+ return ref Value.Vs(pretty((hd args).c().i, 0));
+ }exception{
+ "bad expr" =>
+ return nil;
+ }
+}
+
+pretty(n: ref Sh->Cmd, depth: int): string
+{
+ if (n == nil)
+ return nil;
+ s: string;
+ case n.ntype {
+ n_BLOCK =>
+ s = "{\n"+tabs(depth+1)+pretty(n.left,depth+1) + "\n"+tabs(depth)+"}";
+ n_VAR =>
+ s = "$" + pretty(n.left, depth);
+ n_LIST =>
+ s = "(" + pretty(n.left, depth) + ")";
+ n_SEQ =>
+ s = pretty(n.left, depth) + "\n"+tabs(depth)+pretty(n.right, depth);
+ n_PIPE =>
+ s = pretty(n.left, depth) + " |\n"+tabs(depth)+pretty(n.right, depth);
+ n_ADJ =>
+ s = pretty(n.left, depth) + " " + pretty(n.right, depth);
+ n_WORD =>
+ s = quote(n.word, 1);
+ n_BQ2 =>
+ # if we can't do it, revert to ugliness.
+ {
+ s = "\"" + pretty(n.left, depth);
+ } exception {
+ "bad expr" =>
+ s = sh->cmd2string(n);
+ }
+ * =>
+ raise "bad expr";
+ }
+ return s;
+}
+
+tabs(n: int): string
+{
+ s: string;
+ while(n-- > 0)
+ s[len s] = '\t';
+ return s;
+}
+
+# stolen from sh.y
+quote(s: string, glob: int): string
+{
+ needquote := 0;
+ t := "";
+ for (i := 0; i < len s; i++) {
+ case s[i] {
+ '{' or '}' or '(' or ')' or '`' or '&' or ';' or '=' or '>' or '<' or '#' or
+ '|' or '*' or '[' or '?' or '$' or '^' or ' ' or '\t' or '\n' or '\r' =>
+ needquote = 1;
+ '\'' =>
+ t[len t] = '\'';
+ needquote = 1;
+ GLOB =>
+ if (glob) {
+ if (i < len s - 1)
+ i++;
+ }
+ }
+ t[len t] = s[i];
+ }
+ if (needquote || t == nil)
+ t = "'" + t + "'";
+ return t;
+}
diff --git a/appl/alphabet/main/print.b b/appl/alphabet/main/print.b
new file mode 100644
index 00000000..12327934
--- /dev/null
+++ b/appl/alphabet/main/print.b
@@ -0,0 +1,55 @@
+implement Print,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Print: module {};
+
+typesig(): string
+{
+ return "rfs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ r := chan of string;
+ fd := sys->fildes(int (hd tl args).s().i);
+ if(fd == nil){
+ report(errorc, sys->sprint("error: no such fd %q", (hd tl args).s().i));
+ return nil;
+ }
+ spawn printproc(r, (hd args).f().i, fd);
+ return ref Value.Vr(r);
+}
+
+printproc(r: chan of string, f: chan of ref Sys->FD, fd: ref Sys->FD)
+{
+ if(<-r != nil){
+ <-f;
+ f <-= nil;
+ exit;
+ }
+ <-f;
+ f <-= fd;
+ r <-= nil;
+}
diff --git a/appl/alphabet/main/read.b b/appl/alphabet/main/read.b
new file mode 100644
index 00000000..ebc4d156
--- /dev/null
+++ b/appl/alphabet/main/read.b
@@ -0,0 +1,56 @@
+implement Read,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Read: module{};
+
+typesig(): string
+{
+ return "fs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ f := chan of ref Sys->FD;
+ file := (hd args).s().i;
+ if((fd0 := sys->open(file, Sys->OREAD)) == nil){
+ report(errorc, sys->sprint("cannot open %q: %r", file));
+ return nil;
+ }
+ spawn readproc(f, fd0, r.start("read"));
+ return ref Value.Vf(f);
+}
+
+readproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string)
+{
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ reports->quit(errorc);
+ buf := array[8192] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0)
+ sys->write(fd1, buf, n);
+ sys->write(fd1, array[0] of byte, 0);
+ reports->quit(errorc);
+}
diff --git a/appl/alphabet/main/readall.b b/appl/alphabet/main/readall.b
new file mode 100644
index 00000000..b8697332
--- /dev/null
+++ b/appl/alphabet/main/readall.b
@@ -0,0 +1,46 @@
+implement F2s, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+F2s: module {};
+
+typesig(): string
+{
+ return "sf";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ f := (hd args).f().i;
+ fd := <-f;
+ if(fd == nil){
+ sys->pipe(p := array[2] of ref Sys->FD);
+ f <-= p[1];
+ fd = p[0];
+ }
+ s: string;
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd, buf, len buf)) > 0)
+ s += string buf[0:n];
+ return ref Value.Vs(s);
+}
diff --git a/appl/alphabet/main/rewrite.b b/appl/alphabet/main/rewrite.b
new file mode 100644
index 00000000..96a6e205
--- /dev/null
+++ b/appl/alphabet/main/rewrite.b
@@ -0,0 +1,97 @@
+implement Rewrite, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ Context: import sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ report: import reports;
+include "alphabet.m";
+ Value: import Alphabet;
+
+Rewrite: module {};
+
+typesig(): string
+{
+ return "ccc-ds";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(drawctxt: ref Draw->Context, nil: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ c := chan of ref Value;
+ spawn rewriteproc(drawctxt, errorc, opts, args, c);
+ return <-c;
+}
+
+# we need a separate process so that we can create a shell context
+# without worrying about opening an already-opened wait file.
+rewriteproc(drawctxt: ref Draw->Context, errorc: chan of string,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value,
+ c: chan of ref Value)
+{
+ c <-= rewrite(drawctxt, errorc, opts, args);
+}
+
+rewrite(drawctxt: ref Draw->Context, errorc: chan of string,
+ opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ alphabet := load Alphabet Alphabet->PATH;
+ if(alphabet == nil){
+ report(errorc, sys->sprint("rewrite: cannot load %q: %r", Alphabet->PATH));
+ return nil;
+ }
+ Value: import alphabet;
+ alphabet->init();
+ expr := (hd args).c().i;
+ decls := (hd tl args).c().i;
+ ctxt := Context.new(drawctxt);
+ {
+ ctxt.run(w("load")::w("alphabet")::nil, 0);
+ ctxt.run(c(decls) :: nil, 0);
+ dstarg: list of ref Sh->Listnode;
+ if(opts != nil)
+ dstarg = w((hd (hd opts).t1).s().i) :: nil;
+ ctxt.run(w("{x=${rewrite $1 $2}}") :: c(expr) :: dstarg, 0);
+ } exception e {
+ "fail:*" =>
+ ctxt.run(w("clear")::nil, 0);
+ report(errorc, "rewrite failed: "+e[5:]);
+ return nil;
+ }
+ r := ctxt.get("x");
+ if(len r != 2 || (hd r).cmd == nil){
+ ctxt.run(w("clear")::nil, 0);
+ report(errorc, "rewrite not available, strange... (len "+string len r+")");
+ return nil;
+ }
+ ctxt.run(w("clear")::nil, 0);
+ return ref Value.Vc((hd r).cmd);
+}
+
+c(c: ref Sh->Cmd): ref Sh->Listnode
+{
+ return ref Sh->Listnode(c, nil);
+}
+
+w(w: string): ref Sh->Listnode
+{
+ return ref Sh->Listnode(nil, w);
+}
diff --git a/appl/alphabet/main/rw.b b/appl/alphabet/main/rw.b
new file mode 100644
index 00000000..74ede534
--- /dev/null
+++ b/appl/alphabet/main/rw.b
@@ -0,0 +1,50 @@
+implement Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report, quit: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+typesig(): string
+{
+ return "fs";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ f := chan of ref Sys->FD;
+ file := (hd args).s().i;
+ if((fd0 := sys->open(file, Sys->OREAD)) == nil){
+ report(errorc, sys->sprint("cannot open %q: %r", file));
+ return nil;
+ }
+ spawn readproc(f, fd0, r.start("read"));
+ return ref Value.F(f);
+}
+
+readproc(f: chan of ref Sys->FD, fd0: ref Sys->FD, errorc: chan of string)
+{
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil)
+ quit(errorc);
+ buf := array[8192] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0)
+ sys->write(fd1, buf, n);
+ sys->write(fd1, array[0] of byte, 0);
+ quit(errorc);
+}
diff --git a/appl/alphabet/main/seq.b b/appl/alphabet/main/seq.b
new file mode 100644
index 00000000..8e062738
--- /dev/null
+++ b/appl/alphabet/main/seq.b
@@ -0,0 +1,66 @@
+implement Seq, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Seq: module {};
+
+typesig(): string
+{
+ return "rr*-a-o";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ opts: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ stop := -1;
+ for(; opts != nil; opts = tl opts){
+ case (hd opts).t0 {
+ 'a' =>
+ stop = 0;
+ 'o' =>
+ stop = 1;
+ }
+ }
+ spawn seqproc(r := chan of string, args, stop);
+ return ref Value.Vr(r);
+}
+
+seqproc(r: chan of string, args: list of ref Alphabet->Value, stop: int)
+{
+ status := "";
+ if(<-r == nil){
+pid := sys->pctl(0, nil);
+sys->print("%d. seq %d args\n", pid, len args);
+ for(; args != nil; args = tl args){
+ sr := (hd args).r().i;
+sys->print("%d. started\n", pid);
+ sr <-= nil;
+ status = <-sr;
+sys->print("%d. got status\n", pid);
+ if((status == nil) == stop)
+ break;
+ }
+ }else
+ r = nil;
+ for(; args != nil; args = tl args)
+ (hd args).r().i <-= "die!";
+ if(r != nil)
+ r <-= status;
+}
diff --git a/appl/alphabet/main/unparse.b b/appl/alphabet/main/unparse.b
new file mode 100644
index 00000000..11aa0d48
--- /dev/null
+++ b/appl/alphabet/main/unparse.b
@@ -0,0 +1,38 @@
+implement Unparse, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+include "alphabet/reports.m";
+ reports: Reports;
+ Report, report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Unparse: module {};
+
+typesig(): string
+{
+ return "sc";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+ sh = load Sh Sh->PATH;
+ sh->initialise();
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Value),
+ args: list of ref Value): ref Value
+{
+ return ref Value.Vs(sh->cmd2string((hd args).c().i));
+}
diff --git a/appl/alphabet/main/w2fd.b b/appl/alphabet/main/w2fd.b
new file mode 100644
index 00000000..ab7e3a70
--- /dev/null
+++ b/appl/alphabet/main/w2fd.b
@@ -0,0 +1,61 @@
+implement ToFD,Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+ reports: Reports;
+ Report: import reports;
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+ToFD: module {};
+
+typesig(): string
+{
+ return "fw";
+}
+
+init()
+{
+ alphabet = load Alphabet Alphabet->PATH;
+ reports = load Reports Reports->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, r: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ sys = load Sys Sys->PATH;
+ f := chan of ref Sys->FD;
+ spawn tofdproc(f, (hd args).w().i, r.start("2fd"));
+ return ref Value.Vf(f);
+}
+
+tofdproc(f, w: chan of ref Sys->FD, errorc: chan of string)
+{
+ fd0 := <-w;
+ f <-= fd0;
+ fd1 := <-f;
+ if(fd1 == nil) # asked to quit? tell w to quit too.
+ w <-= nil;
+ else
+ if(fd0 == nil) # no proposed fd? give 'em the one we've just got.
+ w <-= fd1;
+ else{ # otherwise one-way stream from w to f.
+ w <-= nil;
+ buf := array[Sys->ATOMICIO] of byte;
+ while((n := sys->read(fd0, buf, len buf)) > 0){
+ if(sys->write(fd1, buf, n) == -1){
+ reports->report(errorc, sys->sprint("write error: %r"));
+ break;
+ }
+ }
+ }
+ reports->quit(errorc);
+}
diff --git a/appl/alphabet/main/wait.b b/appl/alphabet/main/wait.b
new file mode 100644
index 00000000..04bf88c6
--- /dev/null
+++ b/appl/alphabet/main/wait.b
@@ -0,0 +1,35 @@
+implement Wait, Mainmodule;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+ alphabet: Alphabet;
+ Value: import alphabet;
+
+Wait: module {};
+
+typesig(): string
+{
+ return "sr";
+}
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ alphabet = load Alphabet Alphabet->PATH;
+}
+
+quit()
+{
+}
+
+run(nil: ref Draw->Context, nil: ref Reports->Report, nil: chan of string,
+ nil: list of (int, list of ref Alphabet->Value),
+ args: list of ref Alphabet->Value): ref Alphabet->Value
+{
+ r := (hd args).r().i;
+ r <-= nil;
+ return ref Value.Vs(<-r);
+}
diff --git a/appl/alphabet/mkendpoint.sh b/appl/alphabet/mkendpoint.sh
new file mode 100755
index 00000000..bfd4800b
--- /dev/null
+++ b/appl/alphabet/mkendpoint.sh
@@ -0,0 +1,14 @@
+#!/dis/sh -n
+autoload=std
+load std
+if{! ~ $#* 1}{
+ echo usage: mkendpoint addr >[1=2]
+ raise usage
+}
+addr:=$1
+if{! ftest -e /n/endpoint/dsgdsfgeafreqeq}{
+ mount {mntgen} /n/endpoint
+}
+mount {pctl forkns; alphabet/endpointsrv $addr /n; export /n} /n/endpoint/$addr
+bind /n/endpoint/$addr /n/endpoint/local
+styxlisten -A $addr {export /n/endpoint/local}
diff --git a/appl/alphabet/mkfile b/appl/alphabet/mkfile
new file mode 100644
index 00000000..36465f49
--- /dev/null
+++ b/appl/alphabet/mkfile
@@ -0,0 +1,49 @@
+<../../mkconfig
+DIRS=\
+ typesets\
+ auxi\
+ abc\
+ fs\
+ grid\
+ main\
+
+TARG=\
+ alphabet.dis\
+ alphabet.shmod.dis\
+ eval.dis\
+ extvalues.dis\
+ proxy.dis\
+ reports.dis\
+
+INS=${TARG:%=$ROOT/dis/alphabet/%} \
+ $ROOT/dis/sh/alphabet.dis
+
+MODULES=\
+
+SYSMODULES=\
+ alphabet.m\
+ alphabet/abc.m\
+ alphabet/reports.m\
+ draw.m\
+ readdir.m\
+ sets.m\
+ sh.m\
+ sys.m\
+
+DISBIN=$ROOT/dis/alphabet
+
+<$ROOT/mkfiles/mkdis
+LIMBOFLAGS=-F $LIMBOFLAGS
+install:V: $INS
+
+nuke:V: clean
+ rm -f $INS
+
+uninstall:V:
+ rm -f $INS
+
+$ROOT/dis/sh/alphabet.dis: alphabet.shmod.dis
+ rm -f $ROOT/dis/sh/alphabet.dis && cp alphabet.shmod.dis $ROOT/dis/sh/alphabet.dis
+
+<$ROOT/mkfiles/mksubdirs
+
diff --git a/appl/alphabet/newtypesets b/appl/alphabet/newtypesets
new file mode 100644
index 00000000..29048a6a
--- /dev/null
+++ b/appl/alphabet/newtypesets
@@ -0,0 +1,229 @@
+arithmetic typeset:
+
+ int
+ big
+ real
+
+ i+i int int -> int
+
+ {(int); {i+i $1 10}}
+
+
+
+ {i+i 12 34} | {i*i 10} | {
+
+ Expr: adt {
+ pick {
+ Op =>
+ op: int;
+ l: ref Expr;
+ r: ref Expr;
+ Int =>
+ i: int;
+ Big =>
+ i: big;
+ Real =>
+ i: real;
+ }
+ };
+
+ + {int 12} {
+
+ when we come to run the expression, say in module
+ generate limbo code containing function
+
+ gen(hd args);
+ gen("+");
+ gen(hd tl args);
+ compile();
+
+ output limbo code might look like:
+
+ implement M;
+ M: module {
+ f: fn(a, b: int): int;
+ };
+
+ f(a, b: int): int
+ {
+ return (a +
+
+graphics:
+
+ rect point point -> rect
+ point string string -> point
+
+ x point -> string
+ y point -> string
+
+ r string [string...] -> rect
+ r.canon rect -> rect
+ r.min rect -> point
+ r.max rect -> point
+ r.dx rect -> string
+ r.dy rect -> string
+ r.combine rect rect -> rect
+ r+p rect point -> rect
+ r-p rect point -> rect
+ r.inset rect string -> rect
+
+ image [-r] [-b string] [-c string] rect -> image
+ draw [-o string] image point image -> image
+ win [-t string] rect -> image
+
+ tkwin [-t string] rect -> tk
+ tk tk string -> tk
+
+{(rect); r {min $1|x} {min $1|y} {max $1|x} {max $1|y}}
+
+if we wish to be at all efficient, we need to deal with chans
+not single values.
+
+ r: chan of Rect;
+
+or do we?
+if we had some way of expressing combinations
+of external modules, then perhaps an external
+typeset could do a reasonable job of interpreting stuff.
+
+if a typeset can build expressions bottom-up, incrementally
+out of its own components...
+
+when we're rewriting an expression, we could rewrite it
+in terms of module units provided by the underlying
+typeset... when we ask to find a module, the typeset
+can return some other info as well
+
+we can give the underlying typeset an opportunity
+to optimise the expression, if some of its arguments are
+modules from the same typeset, or from a parent/grandparent
+typeset.
+
+on Load, the typeset could be given expressions representing
+each of its arguments. it then has the opportunity to rewrite
+this whole expression, yielding a module customised for the
+particular arguments it's been given.
+
+perhaps a typeset could assign ids to each module it has returned,
+so that it could easily look up...
+of course, the arguments to the expression consist either
+of modules external to the typeset (no optimisation possible),
+or of modules that have already been loaded by the typeset
+(or by its parent), in which case we can retrieve info on them
+and decide what sort of optimisation might be possible.
+
+there's a moment when you should actually have
+the opportunity to compile optimised code
+(when the expression is passed to another typeset's module?)
+
+---
+
+what about expression types, and allowing access to expressions
+from within the context of a particular typeset.
+
+perhaps any typeset could be treated as the root
+typeset for the purposes of a particular expression evaluation:
+
+what about
+
+ {(/grid/data /fs/fs)
+ /grid/local $1 |
+ /fs/unbundle |
+ /fs/merge $2
+ }
+
+when we wish to pass $1 and $2 from our own program?
+
+rewritten:
+ /fs/merge {/fs/unbundle {/grid/local $1}} $2}
+
+so reduces to
+
+ fd := {grid/local $1} # in /grid/typeset
+ result := {/fs/merge {/fs/unbundle $fd} $2} # in /fs typeset
+
+maybe not possible. (bollocks)
+
+---
+
+typeset for the control library.
+
+
+decl {
+ declare read (string >> fd)
+ define hello (string >> fd) {(string); read $1}
+
+
+abc typeset
+
+ declare [-t string] abc string string -> abc
+ typeset abc string -> abc
+ define abc string cmd -> abc
+ eval abc cmd -> any
+
+{
+ abc |
+ declare read (string >> fd) |
+ define wc (fd >> fd) |
+ define readfile {(>>fd); read /tmp/blah}
+} | {(abc);
+ eval $1 "{
+ read
+
+compile string >> expr
+
+compile string >> (abc string >> expr)
+
+compile '100 + 12 * sin($1)'
+
+
+transform fd (string >> string) >> fd
+
+
+----
+
+descendant typesets problem...
+
+we can't tell which types are identical.
+
+when we load a typeset, we have to look at its parent
+typeset and use its types if the typec characters are contained there.
+
+
+----
+
+if we allow expression types, we have to be very careful...
+can get recursion (equivalent to Y-combinator in λ calculus):
+
+declare eval (cmd->cmd) [(cmd->cmd)...] -> (cmd->cmd)
+
+{((cmd->cmd)->(cmd->cmd))
+ {((cmd->cmd)->(cmd->cmd))
+ eval $1 $1
+ } "{((cmd->cmd)->(cmd->cmd))
+ eval $1 $1
+ }
+}
+
+note this isn't possible without an eval operator and/or
+something that admits a cyclic expression type evaluation.
+
+note also that if this was done in the current implementation,
+it would just hang, as two runs can't be outstanding at the same time (monitor channel).
+
+-----
+
+records:
+
+apply1 records (data -> status) -> records
+apply records (data -> status) -> status
+
+filter records (data -> data) -> records
+filter1 records (data -> data) -> records
+
+discard records -> status
+
+| apply1 "{
+ | data2fd | /fs/unbundle | /fs/write somewhere
+} | apply "{
+ | \ No newline at end of file
diff --git a/appl/alphabet/proxy.b b/appl/alphabet/proxy.b
new file mode 100644
index 00000000..4d90145b
--- /dev/null
+++ b/appl/alphabet/proxy.b
@@ -0,0 +1,304 @@
+implement Proxy;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+
+Debug: con 0;
+
+proxy[Ctxt,Cvt,M,V,EV](ctxt: Ctxt): (
+ chan of ref Typescmd[EV],
+ chan of (string, chan of ref Typescmd[V])
+ ) for {
+ M =>
+ typesig: fn(m: self M): string;
+ run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of V), args: list of V): V;
+ quit: fn(m: self M);
+ Ctxt =>
+ loadtypes: fn(ctxt: self Ctxt, name: string): (chan of ref Proxy->Typescmd[V], string);
+ type2s: fn(ctxt: self Ctxt, tc: int): string;
+ alphabet: fn(ctxt: self Ctxt): string;
+ modules: fn(ctxt: self Ctxt, r: chan of string);
+ find: fn(ctxt: self Ctxt, s: string): (M, string);
+ getcvt: fn(ctxt: self Ctxt): Cvt;
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ free: fn(cvt: self Cvt, v: EV, used: int);
+ dup: fn(cvt: self Cvt, v: EV): EV;
+ }
+{
+ sys = load Sys Sys->PATH;
+ t := chan of ref Typescmd[EV];
+ newts := chan of (string, chan of ref Typescmd[V]);
+ spawn proxyproc(ctxt, t, newts);
+ return (t, newts);
+}
+
+proxyproc[Ctxt,Cvt,M,V,EV](
+ ctxt: Ctxt,
+ t: chan of ref Typescmd[EV],
+ newts: chan of (string, chan of ref Typescmd[V])
+ )
+ for{
+ M =>
+ typesig: fn(m: self M): string;
+ run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of V), args: list of V): V;
+ quit: fn(m: self M);
+ Ctxt =>
+ loadtypes: fn(ctxt: self Ctxt, name: string): (chan of ref Proxy->Typescmd[V], string);
+ type2s: fn(ctxt: self Ctxt, tc: int): string;
+ alphabet: fn(ctxt: self Ctxt): string;
+ modules: fn(ctxt: self Ctxt, r: chan of string);
+ find: fn(ctxt: self Ctxt, s: string): (M, string);
+ getcvt: fn(ctxt: self Ctxt): Cvt;
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ free: fn(cvt: self Cvt, v: EV, used: int);
+ dup: fn(cvt: self Cvt, v: EV): EV;
+ }
+{
+ typesets: list of (string, chan of ref Typescmd[V]);
+ cvt := ctxt.getcvt();
+ for(;;)alt{
+ gr := <-t =>
+ if(gr == nil){
+ for(; typesets != nil; typesets = tl typesets)
+ (hd typesets).t1 <-= nil;
+ exit;
+ }
+ pick r := gr {
+ Load =>
+ (m, err) := ctxt.find(r.cmd);
+ if(m == nil){
+ r.reply <-= (nil, err);
+ }else{
+ c := chan of ref Modulecmd[EV];
+ spawn modproxyproc(cvt, m, c);
+ r.reply <-= (c, nil);
+ }
+ Alphabet =>
+ r.reply <-= ctxt.alphabet();
+ Free =>
+ cvt.free(r.v, r.used);
+ r.reply <-= 0;
+ Dup =>
+ r.reply <-= cvt.dup(r.v);
+ Type2s =>
+ r.reply <-= ctxt.type2s(r.tc);
+ Loadtypes =>
+ ts := typesets;
+ typesets = nil;
+ c: chan of ref Typescmd[V];
+ for(; ts != nil; ts = tl ts){
+ if((hd ts).t0 == r.name)
+ c = (hd ts).t1;
+ else
+ typesets = hd ts :: typesets;
+ }
+ err: string;
+ if(c == nil)
+ (c, err) = ctxt.loadtypes(r.name);
+ if(c == nil)
+ r.reply <-= (nil, err);
+ else{
+ et := chan of ref Typescmd[EV];
+ spawn extproxyproc(ctxt, ctxt.alphabet(), c, et);
+ r.reply <-= (et, nil);
+ }
+ Modules =>
+ spawn ctxt.modules(r.reply);
+ * =>
+ sys->fprint(sys->fildes(2), "unknown type of proxy request %d\n", tagof gr);
+ raise "unknown type proxy request";
+ }
+ typesets = <-newts :: typesets =>
+ ;
+ }
+}
+
+modproxyproc[Cvt,V,EV,M](cvt: Cvt, m: M, c: chan of ref Modulecmd[EV])
+ for{
+ M =>
+ typesig: fn(m: self M): string;
+ run: fn(m: self M, ctxt: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of V), args: list of V): V;
+ quit: fn(m: self M);
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ free: fn(cvt: self Cvt, ev: EV, used: int);
+ }
+{
+ while((gr := <-c) != nil){
+ pick r := gr {
+ Typesig =>
+ r.reply <-= m.typesig();
+ Run =>
+ # XXX could start (or invoke) a new process so that we don't potentially
+ # block concurrency while we're starting the command.
+ {
+ iopts: list of (int, list of V);
+ for(o := r.opts; o != nil; o = tl o){
+ il := extlist2intlist(cvt, (hd o).t1);
+ iopts = ((hd o).t0, il) :: iopts;
+ }
+ iopts = revip(iopts);
+ v := cvt.int2ext(m.run(r.ctxt, r.report, r.errorc, iopts, extlist2intlist(cvt, r.args)));
+ free(cvt, r.opts, r.args, v != nil);
+ r.reply <-= v;
+ } exception {
+ "type error" =>
+ if(Debug)
+ sys->fprint(sys->fildes(2), "error: type conversion failed");
+ if(r.errorc != nil)
+ r.errorc <-= "error: type conversion failed";
+ r.reply <-= nil;
+ }
+ }
+ }
+ m.quit();
+}
+
+extproxyproc[Ctxt,Cvt,V,EV](ctxt: Ctxt, alphabet: string, t: chan of ref Typescmd[V], et: chan of ref Typescmd[EV])
+ for{
+ Ctxt =>
+ type2s: fn(ctxt: self Ctxt, tc: int): string;
+ getcvt: fn(ctxt: self Ctxt): Cvt;
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ free: fn(cvt: self Cvt, ev: EV, used: int);
+ dup: fn(cvt: self Cvt, ev: EV): EV;
+ }
+{
+ cvt := ctxt.getcvt();
+ for(;;){
+ gr := <-et;
+ if(gr == nil)
+ break;
+ pick r := gr {
+ Load =>
+ reply := chan of (chan of ref Modulecmd[V], string);
+ t <-= ref Typescmd[V].Load(r.cmd, reply);
+ (c, err) := <-reply;
+ if(c == nil){
+ r.reply <-= (nil, err);
+ }else{
+ ec := chan of ref Modulecmd[EV];
+ spawn extmodproxyproc(cvt, c, ec);
+ r.reply <-= (ec, nil);
+ }
+ Alphabet =>
+ t <-= ref Typescmd[V].Alphabet(r.reply);
+ Free =>
+ cvt.free(r.v, r.used);
+ Dup =>
+ r.reply <-= cvt.dup(r.v);
+ Type2s =>
+ for(i := 0; i < len alphabet; i++)
+ if(alphabet[i] == r.tc)
+ break;
+ if(i == len alphabet)
+ t <-= ref Typescmd[V].Type2s(r.tc, r.reply);
+ else
+ r.reply <-= ctxt.type2s(r.tc);
+ Loadtypes =>
+ reply := chan of (chan of ref Typescmd[V], string);
+ t <-= ref Typescmd[V].Loadtypes(r.name, reply);
+ (c, err) := <-reply;
+ if(c == nil)
+ r.reply <-= (nil, err);
+ else{
+ t <-= ref Typescmd[V].Alphabet(areply := chan of string);
+ ec := chan of ref Typescmd[EV];
+ spawn extproxyproc(ctxt, <-areply, c, ec);
+ r.reply <-= (ec, nil);
+ }
+ Modules =>
+ t <-= ref Typescmd[V].Modules(r.reply);
+ * =>
+ sys->fprint(sys->fildes(2), "unknown type of proxy request %d\n", tagof gr);
+ raise "unknown type proxy request";
+ }
+ }
+ et <-= nil;
+}
+
+extmodproxyproc[Cvt,V,EV](cvt: Cvt, c: chan of ref Modulecmd[V], ec: chan of ref Modulecmd[EV])
+ for{
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ free: fn(cvt: self Cvt, ev: EV, used: int);
+ }
+{
+ while((gr := <-ec) != nil){
+ pick r := gr {
+ Typesig =>
+ c <-= ref Modulecmd[V].Typesig(r.reply);
+ Run =>
+ {
+ iopts: list of (int, list of V);
+ for(o := r.opts; o != nil; o = tl o){
+ il := extlist2intlist(cvt, (hd o).t1);
+ iopts = ((hd o).t0, il) :: iopts;
+ }
+ iopts = revip(iopts);
+ c <-= ref Modulecmd[V].Run(
+ r.ctxt,
+ r.report,
+ r.errorc,
+ iopts,
+ extlist2intlist(cvt, r.args),
+ reply := chan of V
+ );
+ v := cvt.int2ext(<-reply);
+ free(cvt, r.opts, r.args, v != nil);
+ r.reply <-= v;
+ }
+ }
+ }
+}
+
+
+revip[V](l: list of (int, V)): list of (int, V)
+{
+ m: list of (int, V);
+ for(; l != nil; l = tl l)
+ m = hd l :: m;
+ return m;
+}
+
+extlist2intlist[V,EV,Cvt](cvt: Cvt, vl: list of EV): list of V
+ for{
+ Cvt =>
+ int2ext: fn(cvt: self Cvt, v: V): EV;
+ ext2int: fn(cvt: self Cvt, ev: EV): V;
+ }
+{
+ l, m: list of V;
+ for(; vl != nil; vl = tl vl)
+ l = cvt.ext2int(hd vl) :: l;
+ for(; l != nil; l = tl l)
+ m = hd l :: m;
+ return m;
+}
+
+free[V,Cvt](cvt: Cvt, opts: list of (int, list of V), args: list of V, used: int)
+ for{
+ Cvt =>
+ free: fn(cvt: self Cvt, ev: V, used: int);
+ }
+{
+ for(; args != nil; args = tl args)
+ cvt.free(hd args, used);
+ for(; opts != nil; opts = tl opts)
+ for(args = (hd opts).t1; args != nil; args = tl args)
+ cvt.free(hd args, used);
+}
diff --git a/appl/alphabet/reports.b b/appl/alphabet/reports.b
new file mode 100644
index 00000000..d9336d8b
--- /dev/null
+++ b/appl/alphabet/reports.b
@@ -0,0 +1,189 @@
+implement Reports;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+
+Reporter: adt {
+ id: int;
+ name: string;
+ stopc: chan of int;
+};
+
+reportproc(errorc: chan of string, stopc: chan of int, reply: chan of ref Report)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ r := ref Report(chan of (string, chan of string, chan of int), chan of int);
+ if(stopc == nil)
+ stopc = chan of int;
+ else
+ sys->pctl(Sys->NEWPGRP, nil);
+ reply <-= r;
+ reportproc0(stopc, errorc, r.startc, r.enablec);
+}
+
+Report.start(r: self ref Report, name: string): chan of string
+{
+ if(r == nil)
+ return nil;
+ errorc := chan of string;
+ r.startc <-= (name, errorc, nil);
+ return errorc;
+}
+
+Report.add(r: self ref Report, name: string, errorc: chan of string, stopc: chan of int)
+{
+ r.startc <-= (name, errorc, stopc);
+}
+
+Report.enable(r: self ref Report)
+{
+ r.enablec <-= 0;
+}
+
+reportproc0(
+ stopc: chan of int,
+ reportc: chan of string,
+ startc: chan of (string, chan of string, chan of int),
+ enablec: chan of int
+ )
+{
+ realc := array[2] of chan of string;
+ p := array[len realc] of Reporter;
+ a := array[0] of chan of string;
+ id := n := 0;
+ stopped := 0;
+out:
+ for(;;) alt{
+ <-stopc =>
+ stopped = 1;
+ break out;
+ (prefix, c, stop) := <-startc =>
+ if(n == len realc){
+ if(realc == a)
+ a = nil;
+ realc = (array[n * 2] of chan of string)[0:] = realc;
+ p = (array[n * 2] of Reporter)[0:] = p;
+ if(a == nil)
+ a = realc;
+ }
+ realc[n] = c;
+ p[n] = (id++, prefix, stop);
+ n++;
+ <-enablec =>
+ if(n == 0)
+ break out;
+ a = realc;
+ (x, msg) := <-a =>
+ if(msg == nil){
+ if(--n == 0)
+ break out;
+ if(n != x){
+ a[x] = a[n];
+ a[n] = nil;
+ p[x] = p[n];
+ p[n] = (-1, nil, nil);
+ }
+ }else{
+ if(reportc != nil){
+ alt{
+ reportc <-= sys->sprint("%d. %s: %s", p[x].id, p[x].name, msg) =>
+ ;
+ <-stopc =>
+ stopped = 1;
+ break out;
+ }
+ }
+ }
+ }
+ if(stopped == 0){
+ if(reportc != nil){
+ alt{
+ reportc <-= nil =>
+ ;
+ <-stopc =>
+ stopped = 1;
+ }
+ }
+ }
+ if(stopped){
+ for(i := 0; i < n; i++)
+ note(p[i].stopc);
+ note(stopc);
+ }
+}
+
+quit(errorc: chan of string)
+{
+ if(errorc != nil)
+ errorc <-= nil;
+ exit;
+}
+
+report(errorc: chan of string, err: string)
+{
+ if(errorc != nil)
+ errorc <-= err;
+}
+
+newpgrp(stopc: chan of int, flags: int): chan of int
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(flags&PROPAGATE){
+ if(stopc == nil)
+ stopc = chan[1] of int;
+ sys->pipe(p := array[2] of ref Sys->FD);
+ spawn deadman(p[1]);
+ sys->pctl(Sys->NEWPGRP, nil);
+ spawn watchproc(p[0], stopc);
+ }else
+ sys->pctl(Sys->NEWPGRP, nil);
+ spawn grpproc(stopc, newstopc := chan[1] of int, flags&KILL);
+ return newstopc;
+}
+
+grpproc(noteparent, noteself: chan of int, kill: int)
+{
+ if(noteparent == nil)
+ noteparent = chan of int;
+ alt{
+ <-noteparent =>
+ note(noteparent);
+ <-noteself =>
+ ;
+ }
+ note(noteself);
+ if(kill){
+ pid := sys->pctl(0, nil);
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd == nil)
+ fd = sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);
+ sys->fprint(fd, "killgrp");
+ }
+}
+
+note(c: chan of int)
+{
+ if(c != nil){
+ alt {
+ c <-= 1 =>
+ ;
+ * =>
+ ;
+ }
+ }
+}
+
+deadman(nil: ref Sys->FD)
+{
+ <-chan of int;
+}
+
+watchproc(fd: ref Sys->FD, stopc: chan of int)
+{
+ sys->read(fd, array[1] of byte, 1);
+ note(stopc);
+}
diff --git a/appl/alphabet/rexecsrv.sh b/appl/alphabet/rexecsrv.sh
new file mode 100755
index 00000000..45987e9f
--- /dev/null
+++ b/appl/alphabet/rexecsrv.sh
@@ -0,0 +1,9 @@
+#!/dis/sh
+if{! ~ $#* 2}{
+ echo usage rexecsrv net!addr decls >[1=2]
+ raise usage
+}
+(addr decls) := $*
+/appl/alphabet/mkendpoint.sh $addr!2222
+alphabet/rexecsrv /n/cd $decls
+listen -v $addr!2223 {export /n/cd&}
diff --git a/appl/alphabet/setup b/appl/alphabet/setup
new file mode 100644
index 00000000..3eb9a037
--- /dev/null
+++ b/appl/alphabet/setup
@@ -0,0 +1,63 @@
+/appl/alphabet/rexecsrv.sh tcp!rogero {typeset /fs; import /fs/unbundle /fs/entries /fs/print}
+
+####################
+addr=tcp!rogero!1234
+run /appl/alphabet/declare.sh
+/appl/alphabet/mkendpoint.sh $addr
+echo ${rewrite {
+ /echo hello |
+ /grid/remote |
+ /grid/rexec tcp!rogero!1235 "{(/fd);/filter $1 "{wc}}
+ }
+}
+- {
+ /echo hello |
+ /grid/remote |
+ /grid/rexec tcp!rogero!1235 "{(/fd);/filter $1 "{wc}} |
+ /grid/local
+ }
+# - {remote /n/local/lib/words | farm rogero!1235 "{tr -d e} } | /grid/local}
+######################
+
+/appl/alphabet/mkendpoint.sh tcp!rogero!9998
+load alphabet
+run /appl/alphabet/declare.sh
+- {
+ /fs/walk /tmp |
+ /fs/bundle |
+ /grid/remote |
+ /grid/rexec tcp!rogero!1235 "{
+ (/fd)
+ /fs/unbundle $1 |
+ /fs/entries |
+ /fs/print
+ }
+}
+
+- {
+ /fs/walk /tmp |
+ /fs/bundle |
+ /grid/remote |
+ /grid/local |
+ /fs/unbundle |
+ /fs/print
+}
+
+###############
+
+the below script generates:
+
+alphabet: 2. bundle: write error: i/o on hungup channel
+and a much truncated file.
+
+-{
+ /fs/walk /tmp |
+ /fs/bundle |
+ /grid/remote |
+ /grid/rexec tcp!127.1!1235 "{
+ (/fd)
+ /fs/unbundle $1 |
+ /fs/filter -d {/fs/match '*.b'} |
+ /fs/bundle
+ } | /create xx
+}
diff --git a/appl/alphabet/typesets/abc.b b/appl/alphabet/typesets/abc.b
new file mode 100644
index 00000000..d2a060fd
--- /dev/null
+++ b/appl/alphabet/typesets/abc.b
@@ -0,0 +1,180 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead
+implement Abc;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet.m";
+include "abc.m";
+mkabc(a: Alphabet): ref Value.VA
+{
+ r := chan[1] of int;
+ r <-= 1;
+ return ref Value.VA((r, a));
+}
+
+valuec := array[] of {
+ tagof(Value.Vm) => 'm',
+ tagof(Value.Vt) => 't',
+ tagof(Value.VA) => 'A',
+ tagof(Value.Vw) => 'w',
+ tagof(Value.Vc) => 'c',
+ tagof(Value.Vr) => 'r',
+ tagof(Value.Vf) => 'f',
+ tagof(Value.Vs) => 's',
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+Value.type2s(c: int): string
+{
+ case c {
+ 'm' =>
+ return "vmods";
+ 't' =>
+ return "vtypes";
+ 'A' =>
+ return "abc";
+ 'w' =>
+ return "wfd";
+ 'c' =>
+ return "cmd";
+ 'r' =>
+ return "status";
+ 'f' =>
+ return "fd";
+ 's' =>
+ return "string";
+ * =>
+ return sys->sprint("unknowntype('%c')", c);
+ }
+}
+
+typeerror(tc: int, v: ref Value): string
+{
+ sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v]));
+ return "type conversion error";
+}
+
+Value.m(v: self ref Value): ref Value.Vm
+{
+ pick xv := v {Vm => return xv;}
+ raise typeerror('m', v);
+}
+
+Value.t(v: self ref Value): ref Value.Vt
+{
+ pick xv := v {Vt => return xv;}
+ raise typeerror('t', v);
+}
+
+Value.A(v: self ref Value): ref Value.VA
+{
+ pick xv := v {VA => return xv;}
+ raise typeerror('A', v);
+}
+
+Value.w(v: self ref Value): ref Value.Vw
+{
+ pick xv := v {Vw => return xv;}
+ raise typeerror('w', v);
+}
+
+Value.c(v: self ref Value): ref Value.Vc
+{
+ pick xv := v {Vc => return xv;}
+ raise typeerror('c', v);
+}
+
+Value.r(v: self ref Value): ref Value.Vr
+{
+ pick xv := v {Vr => return xv;}
+ raise typeerror('r', v);
+}
+
+Value.f(v: self ref Value): ref Value.Vf
+{
+ pick xv := v {Vf => return xv;}
+ raise typeerror('f', v);
+}
+
+Value.s(v: self ref Value): ref Value.Vs
+{
+ pick xv := v {Vs => return xv;}
+ raise typeerror('s', v);
+}
+
+Value.typec(v: self ref Value): int
+{
+ return valuec[tagof v];
+}
+
+Value.dup(xv: self ref Value): ref Value
+{
+ if(xv == nil)
+ return nil;
+ pick v := xv {
+ Vm =>
+ v = nil;
+ xv = v;
+ Vt =>
+ v = nil;
+ xv = v;
+ VA =>
+ a := v.A().i;
+ a.refcount <-= <-a.refcount + 1;
+ xv = v;
+ Vw =>
+ v = nil;
+ xv = v;
+ Vr =>
+ v = nil;
+ xv = v;
+ Vf =>
+ v = nil;
+ xv = v;
+ }
+ return xv;
+}
+
+Value.free(xv: self ref Value, used: int)
+{
+ if(xv == nil)
+ return;
+ pick v := xv {
+ Vm =>
+ if(!used){
+ v.i.abc.free(0);
+ }
+ Vt =>
+ if(!used){
+ v.i.abc.free(0);
+ }
+ VA =>
+ r := v.i.refcount <-= <-v.i.refcount - 1;
+ if(r == 0){
+ v.i.alphabet->quit();
+ v.i.alphabet = nil;
+ v.i.refcount = nil;
+ }
+ Vw =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ Vr =>
+ if(!used){
+ v.i <-= "stop";
+ }
+ Vf =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ }
+}
+
diff --git a/appl/alphabet/typesets/abctypes.b b/appl/alphabet/typesets/abctypes.b
new file mode 100644
index 00000000..4fd5d24d
--- /dev/null
+++ b/appl/alphabet/typesets/abctypes.b
@@ -0,0 +1,229 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or abc.b instead
+implement Abctypes;
+include "sys.m";
+ sys: Sys;
+include "alphabet/reports.m";
+include "draw.m";
+include "sh.m";
+include "alphabet.m";
+ extvalues: Extvalues;
+ Values: import extvalues;
+ proxymod: Proxy;
+ Typescmd, Modulecmd: import Proxy;
+include "abc.m";
+ abc: Abc;
+ Value: import abc;
+include "abctypes.m";
+
+Pcontext: adt {
+ cvt: ref Abccvt;
+ ctxt: ref Context;
+
+ loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string);
+ type2s: fn(ctxt: self ref Pcontext, tc: int): string;
+ alphabet: fn(ctxt: self ref Pcontext): string;
+ modules: fn(ctxt: self ref Pcontext, r: chan of string);
+ find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string);
+ getcvt: fn(ctxt: self ref Pcontext): ref Abccvt;
+};
+
+proxy(): chan of ref Typescmd[ref Alphabet->Value]
+{
+ return proxy0().t0;
+}
+
+proxy0(): (
+ chan of ref Typescmd[ref Alphabet->Value],
+ chan of (string, chan of ref Typescmd[ref Abc->Value]),
+ ref Abccvt
+ )
+{
+ sys = load Sys Sys->PATH;
+ extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH);
+ proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH);
+ abc = checkload(load Abc Abc->PATH, Abc->PATH);
+ abc->init();
+ cvt := ref Abccvt(Values[ref Value].new());
+ (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new()));
+ return (t, newts, cvt);
+}
+
+include "readdir.m";
+Context: adt {
+ modules: fn(ctxt: self ref Context, r: chan of string);
+ loadtypes: fn(ctxt: self ref Context, name: string)
+ : (chan of ref Proxy->Typescmd[ref Value], string);
+ find: fn(ctxt: self ref Context, s: string): (ref Module, string);
+ new: fn(): ref Context;
+};
+Module: adt {
+ m: Abcmodule;
+ run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report,
+ errorc: chan of string, opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value;
+ typesig: fn(m: self ref Module): string;
+ quit: fn(m: self ref Module);
+};
+Context.new(): ref Context
+{
+ return nil;
+}
+Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ p := "/dis/alphabet/abc/"+name+"types.dis";
+ types := load Abcsubtypes p;
+ if(types == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ return (types->proxy(), nil);
+}
+Context.modules(nil: self ref Context, r: chan of string)
+{
+ if((readdir := load Readdir Readdir->PATH) != nil){
+ (a, nil) := readdir->init("/dis/alphabet/abc", Readdir->NAME|Readdir->COMPACT);
+ for(i := 0; i < len a; i++){
+ m := a[i].name;
+ if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis")
+ r <-= m[0:len m - 4];
+ }
+ }
+ r <-= nil;
+}
+Context.find(nil: self ref Context, s: string): (ref Module, string)
+{
+ p := "/dis/alphabet/abc/"+s+".dis";
+ m := load Abcmodule p;
+ if(m == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ {
+ m->init();
+ } exception e {
+ "fail:*" =>
+ return (nil, "init failed: " + e[5:]);
+ }
+ return (ref Module(m), nil);
+}
+Module.run(m: self ref Module, nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of ref Value), args: list of ref Value): ref Value
+{
+ return m.m->run(errorc, r, opts, args);
+}
+Module.typesig(m: self ref Module): string
+{
+ return m.m->types();
+}
+Module.quit(nil: self ref Module)
+{
+}
+Pcontext.type2s(nil: self ref Pcontext, tc: int): string
+{
+ return Value.type2s(tc);
+}
+
+Pcontext.alphabet(nil: self ref Pcontext): string
+{
+ return "mtAwcrfs";
+}
+
+Pcontext.getcvt(ctxt: self ref Pcontext): ref Abccvt
+{
+ return ctxt.cvt;
+}
+
+Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string)
+{
+ return ctxt.ctxt.find(s);
+}
+
+Pcontext.modules(ctxt: self ref Pcontext, r: chan of string)
+{
+ ctxt.ctxt.modules(r);
+}
+
+Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ return ctxt.ctxt.loadtypes(name);
+}
+
+Abccvt.int2ext(cvt: self ref Abccvt, gv: ref Value): ref Alphabet->Value
+{
+ if(gv == nil)
+ return nil;
+ pick v := gv {
+ Vw =>
+ return ref (Alphabet->Value).Vw(v.i);
+ Vf =>
+ return ref (Alphabet->Value).Vf(v.i);
+ Vr =>
+ return ref (Alphabet->Value).Vr(v.i);
+ Vs =>
+ return ref (Alphabet->Value).Vs(v.i);
+ Vc =>
+ return ref (Alphabet->Value).Vc(v.i);
+ * =>
+ id := cvt.values.add(gv);
+ return ref (Alphabet->Value).Vz((gv.typec(), id));
+ }
+}
+
+Abccvt.ext2int(cvt: self ref Abccvt, ev: ref Alphabet->Value): ref Value
+{
+ if(ev == nil)
+ return nil;
+ pick v := ev {
+ Vd =>
+ return nil; # can't happen
+ Vw =>
+ return ref Value.Vw(v.i);
+ Vf =>
+ return ref Value.Vf(v.i);
+ Vr =>
+ return ref Value.Vr(v.i);
+ Vs =>
+ return ref Value.Vs(v.i);
+ Vc =>
+ return ref Value.Vc(v.i);
+ Vz =>
+ x := cvt.values.v[v.i.id].t1;
+ if(x == nil){
+ sys->print("abctypes: bad id %d, type %c\n", v.i.id, v.i.typec);
+ return nil;
+ }
+ return x;
+ }
+}
+
+Abccvt.free(cvt: self ref Abccvt, gv: ref Alphabet->Value, used: int)
+{
+ pick v := gv {
+ Vz =>
+ id := v.i.id;
+ cvt.values.v[id].t1.free(used);
+ cvt.values.del(id);
+ }
+}
+
+Abccvt.dup(cvt: self ref Abccvt, gv: ref Alphabet->Value): ref Alphabet->Value
+{
+ pick ev := gv {
+ Vz =>
+ id := ev.i.id;
+ v := cvt.values.v[id].t1;
+ nv := v.dup();
+ if(nv == nil)
+ return nil;
+ if(nv != v)
+ return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv)));
+ cvt.values.inc(id);
+ return ev;
+ * =>
+ return nil;
+ }
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ sys->fprint(sys->fildes(2), "abctypes: cannot load %s: %r\n", path);
+ raise "fail:bad module";
+}
diff --git a/appl/alphabet/typesets/fs.b b/appl/alphabet/typesets/fs.b
new file mode 100644
index 00000000..9b50d13f
--- /dev/null
+++ b/appl/alphabet/typesets/fs.b
@@ -0,0 +1,226 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead
+implement Fs;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "fs.m";
+sendnulldir(c: Fschan): int
+{
+ reply := chan of int;
+ c <-= ((ref Sys->nulldir, nil), reply);
+ if((r := <-reply) == Down){
+ c <-= ((nil, nil), reply);
+ if(<-reply != Quit)
+ return Quit;
+ return Next;
+ }
+ return r;
+}
+# copy the contents (not the entry itself) of a directory from src to dst.
+copy(src, dst: Fschan): int
+{
+ indent := 1;
+ myreply := chan of int;
+ for(;;){
+ (d, reply) := <-src;
+ dst <-= (d, myreply);
+ r := <-myreply;
+ case reply <-= r {
+ Quit =>
+ return Quit;
+ Next =>
+ if(d.dir == nil && d.data == nil)
+ if(--indent == 0)
+ return Next;
+ Skip =>
+ if(--indent == 0)
+ return Next;
+ Down =>
+ if(d.dir != nil || d.data != nil)
+ indent++;
+ }
+ }
+}
+
+valuec := array[] of {
+ tagof(Value.Vr) => 'r',
+ tagof(Value.Vd) => 'd',
+ tagof(Value.Vc) => 'c',
+ tagof(Value.Vf) => 'f',
+ tagof(Value.Vs) => 's',
+ tagof(Value.Vm) => 'm',
+ tagof(Value.Vp) => 'p',
+ tagof(Value.Vt) => 't',
+ tagof(Value.Vx) => 'x',
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+}
+
+Value.type2s(c: int): string
+{
+ case c {
+ 'r' =>
+ return "status";
+ 'd' =>
+ return "data";
+ 'c' =>
+ return "command";
+ 'f' =>
+ return "fd";
+ 's' =>
+ return "string";
+ 'm' =>
+ return "selector";
+ 'p' =>
+ return "gate";
+ 't' =>
+ return "entries";
+ 'x' =>
+ return "fs";
+ * =>
+ return sys->sprint("unknowntype('%c')", c);
+ }
+}
+
+typeerror(tc: int, v: ref Value): string
+{
+ sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v]));
+ return "type conversion error";
+}
+
+Value.r(v: self ref Value): ref Value.Vr
+{
+ pick xv := v {Vr => return xv;}
+ raise typeerror('r', v);
+}
+
+Value.d(v: self ref Value): ref Value.Vd
+{
+ pick xv := v {Vd => return xv;}
+ raise typeerror('d', v);
+}
+
+Value.c(v: self ref Value): ref Value.Vc
+{
+ pick xv := v {Vc => return xv;}
+ raise typeerror('c', v);
+}
+
+Value.f(v: self ref Value): ref Value.Vf
+{
+ pick xv := v {Vf => return xv;}
+ raise typeerror('f', v);
+}
+
+Value.s(v: self ref Value): ref Value.Vs
+{
+ pick xv := v {Vs => return xv;}
+ raise typeerror('s', v);
+}
+
+Value.m(v: self ref Value): ref Value.Vm
+{
+ pick xv := v {Vm => return xv;}
+ raise typeerror('m', v);
+}
+
+Value.p(v: self ref Value): ref Value.Vp
+{
+ pick xv := v {Vp => return xv;}
+ raise typeerror('p', v);
+}
+
+Value.t(v: self ref Value): ref Value.Vt
+{
+ pick xv := v {Vt => return xv;}
+ raise typeerror('t', v);
+}
+
+Value.x(v: self ref Value): ref Value.Vx
+{
+ pick xv := v {Vx => return xv;}
+ raise typeerror('x', v);
+}
+
+Value.typec(v: self ref Value): int
+{
+ return valuec[tagof v];
+}
+
+Value.dup(xv: self ref Value): ref Value
+{
+ if(xv == nil)
+ return nil;
+ pick v := xv {
+ Vr =>
+ v = nil;
+ xv = v;
+ Vd =>
+ v = nil;
+ xv = v;
+ Vf =>
+ v = nil;
+ xv = v;
+ Vm =>
+ v = nil;
+ xv = v;
+ Vp =>
+ v = nil;
+ xv = v;
+ Vt =>
+ v = nil;
+ xv = v;
+ Vx =>
+ v = nil;
+ xv = v;
+ }
+ return xv;
+}
+
+Value.free(xv: self ref Value, used: int)
+{
+ if(xv == nil)
+ return;
+ pick v := xv {
+ Vr =>
+ if(!used){
+ v.i <-= "stop";
+ }
+ Vd =>
+ if(!used){
+ alt{
+ v.i.stop <-= 1 =>
+ ;
+ * =>
+ ;
+ }
+ }
+ Vf =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ Vm =>
+ if(!used){
+ v.i <-= (nil, nil, nil);
+ }
+ Vp =>
+ if(!used){
+ v.i <-= (Nilentry, nil);
+ }
+ Vt =>
+ if(!used){
+ v.i.sync <-= 0;
+ }
+ Vx =>
+ if(!used){
+ (<-v.i).t1 <-= Quit;
+ }
+ }
+}
+
diff --git a/appl/alphabet/typesets/fstypes.b b/appl/alphabet/typesets/fstypes.b
new file mode 100644
index 00000000..086b2089
--- /dev/null
+++ b/appl/alphabet/typesets/fstypes.b
@@ -0,0 +1,230 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or fs.b instead
+implement Fstypes;
+include "sys.m";
+ sys: Sys;
+include "alphabet/reports.m";
+include "draw.m";
+include "sh.m";
+include "alphabet.m";
+ extvalues: Extvalues;
+ Values: import extvalues;
+ proxymod: Proxy;
+ Typescmd, Modulecmd: import Proxy;
+include "fs.m";
+ fs: Fs;
+ Value: import fs;
+include "fstypes.m";
+
+Pcontext: adt {
+ cvt: ref Fscvt;
+ ctxt: ref Context;
+
+ loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string);
+ type2s: fn(ctxt: self ref Pcontext, tc: int): string;
+ alphabet: fn(ctxt: self ref Pcontext): string;
+ modules: fn(ctxt: self ref Pcontext, r: chan of string);
+ find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string);
+ getcvt: fn(ctxt: self ref Pcontext): ref Fscvt;
+};
+
+proxy(): chan of ref Typescmd[ref Alphabet->Value]
+{
+ return proxy0().t0;
+}
+
+proxy0(): (
+ chan of ref Typescmd[ref Alphabet->Value],
+ chan of (string, chan of ref Typescmd[ref Fs->Value]),
+ ref Fscvt
+ )
+{
+ sys = load Sys Sys->PATH;
+ extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH);
+ proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH);
+ fs = checkload(load Fs Fs->PATH, Fs->PATH);
+ fs->init();
+ cvt := ref Fscvt(Values[ref Value].new());
+ (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new()));
+ return (t, newts, cvt);
+}
+
+include "readdir.m";
+Context: adt {
+ modules: fn(ctxt: self ref Context, r: chan of string);
+ loadtypes: fn(ctxt: self ref Context, name: string)
+ : (chan of ref Proxy->Typescmd[ref Value], string);
+ find: fn(ctxt: self ref Context, s: string): (ref Module, string);
+ new: fn(): ref Context;
+};
+Module: adt {
+ m: Fsmodule;
+ run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report,
+ errorc: chan of string, opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value;
+ typesig: fn(m: self ref Module): string;
+ quit: fn(m: self ref Module);
+};
+Context.new(): ref Context
+{
+ return nil;
+}
+Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ p := "/dis/alphabet/fs/"+name+"types.dis";
+ types := load Fssubtypes p;
+ if(types == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ return (types->proxy(), nil);
+}
+Context.modules(nil: self ref Context, r: chan of string)
+{
+ if((readdir := load Readdir Readdir->PATH) != nil){
+ (a, nil) := readdir->init("/dis/alphabet/fs", Readdir->NAME|Readdir->COMPACT);
+ for(i := 0; i < len a; i++){
+ m := a[i].name;
+ if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis")
+ r <-= m[0:len m - 4];
+ }
+ }
+ r <-= nil;
+}
+Context.find(nil: self ref Context, s: string): (ref Module, string)
+{
+ p := "/dis/alphabet/fs/"+s+".dis";
+ m := load Fsmodule p;
+ if(m == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ {
+ m->init();
+ } exception e {
+ "fail:*" =>
+ return (nil, "init failed: " + e[5:]);
+ }
+ return (ref Module(m), nil);
+}
+Module.run(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report, nil: chan of string,
+ opts: list of (int, list of ref Value), args: list of ref Value): ref Value
+{
+ # add errorc
+ return m.m->run(ctxt, r, opts, args);
+}
+Module.typesig(m: self ref Module): string
+{
+ return m.m->types();
+}
+Module.quit(nil: self ref Module)
+{
+}
+Pcontext.type2s(nil: self ref Pcontext, tc: int): string
+{
+ return Value.type2s(tc);
+}
+
+Pcontext.alphabet(nil: self ref Pcontext): string
+{
+ return "rdcfsmptx";
+}
+
+Pcontext.getcvt(ctxt: self ref Pcontext): ref Fscvt
+{
+ return ctxt.cvt;
+}
+
+Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string)
+{
+ return ctxt.ctxt.find(s);
+}
+
+Pcontext.modules(ctxt: self ref Pcontext, r: chan of string)
+{
+ ctxt.ctxt.modules(r);
+}
+
+Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ return ctxt.ctxt.loadtypes(name);
+}
+
+Fscvt.int2ext(cvt: self ref Fscvt, gv: ref Value): ref Alphabet->Value
+{
+ if(gv == nil)
+ return nil;
+ pick v := gv {
+ Vd =>
+ return ref (Alphabet->Value).Vd(v.i);
+ Vf =>
+ return ref (Alphabet->Value).Vf(v.i);
+ Vr =>
+ return ref (Alphabet->Value).Vr(v.i);
+ Vs =>
+ return ref (Alphabet->Value).Vs(v.i);
+ Vc =>
+ return ref (Alphabet->Value).Vc(v.i);
+ * =>
+ id := cvt.values.add(gv);
+ return ref (Alphabet->Value).Vz((gv.typec(), id));
+ }
+}
+
+Fscvt.ext2int(cvt: self ref Fscvt, ev: ref Alphabet->Value): ref Value
+{
+ if(ev == nil)
+ return nil;
+ pick v := ev {
+ Vd =>
+ return ref Value.Vd(v.i);
+ Vw =>
+ return nil; # can't happen
+ Vf =>
+ return ref Value.Vf(v.i);
+ Vr =>
+ return ref Value.Vr(v.i);
+ Vs =>
+ return ref Value.Vs(v.i);
+ Vc =>
+ return ref Value.Vc(v.i);
+ Vz =>
+ x := cvt.values.v[v.i.id].t1;
+ if(x == nil){
+ sys->print("fstypes: bad id %d, type %c\n", v.i.id, v.i.typec);
+ return nil;
+ }
+ return x;
+ }
+}
+
+Fscvt.free(cvt: self ref Fscvt, gv: ref Alphabet->Value, used: int)
+{
+ pick v := gv {
+ Vz =>
+ id := v.i.id;
+ cvt.values.v[id].t1.free(used);
+ cvt.values.del(id);
+ }
+}
+
+Fscvt.dup(cvt: self ref Fscvt, gv: ref Alphabet->Value): ref Alphabet->Value
+{
+ pick ev := gv {
+ Vz =>
+ id := ev.i.id;
+ v := cvt.values.v[id].t1;
+ nv := v.dup();
+ if(nv == nil)
+ return nil;
+ if(nv != v)
+ return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv)));
+ cvt.values.inc(id);
+ return ev;
+ * =>
+ return nil;
+ }
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ sys->fprint(sys->fildes(2), "fstypes: cannot load %s: %r\n", path);
+ raise "fail:bad module";
+}
diff --git a/appl/alphabet/typesets/grid.b b/appl/alphabet/typesets/grid.b
new file mode 100644
index 00000000..cba8366c
--- /dev/null
+++ b/appl/alphabet/typesets/grid.b
@@ -0,0 +1,160 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead
+implement Grid;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+include "alphabet/reports.m";
+include "alphabet/endpoints.m";
+include "grid.m";
+endpoints: Endpoints;
+
+valuec := array[] of {
+ tagof(Value.Vb) => 'b',
+ tagof(Value.Ve) => 'e',
+ tagof(Value.Vw) => 'w',
+ tagof(Value.Vc) => 'c',
+ tagof(Value.Vr) => 'r',
+ tagof(Value.Vf) => 'f',
+ tagof(Value.Vs) => 's',
+};
+
+init()
+{
+ sys = load Sys Sys->PATH;
+ endpoints = load Endpoints Endpoints->PATH;
+ endpoints->init();
+}
+
+Value.type2s(c: int): string
+{
+ case c {
+ 'b' =>
+ return "records";
+ 'e' =>
+ return "endpoint";
+ 'w' =>
+ return "wfd";
+ 'c' =>
+ return "cmd";
+ 'r' =>
+ return "status";
+ 'f' =>
+ return "fd";
+ 's' =>
+ return "string";
+ * =>
+ return sys->sprint("unknowntype('%c')", c);
+ }
+}
+
+typeerror(tc: int, v: ref Value): string
+{
+ sys->fprint(sys->fildes(2), "fs: bad type conversion, expected %s, was actually %s\n", Value.type2s(tc), Value.type2s(valuec[tagof v]));
+ return "type conversion error";
+}
+
+Value.b(v: self ref Value): ref Value.Vb
+{
+ pick xv := v {Vb => return xv;}
+ raise typeerror('b', v);
+}
+
+Value.e(v: self ref Value): ref Value.Ve
+{
+ pick xv := v {Ve => return xv;}
+ raise typeerror('e', v);
+}
+
+Value.w(v: self ref Value): ref Value.Vw
+{
+ pick xv := v {Vw => return xv;}
+ raise typeerror('w', v);
+}
+
+Value.c(v: self ref Value): ref Value.Vc
+{
+ pick xv := v {Vc => return xv;}
+ raise typeerror('c', v);
+}
+
+Value.r(v: self ref Value): ref Value.Vr
+{
+ pick xv := v {Vr => return xv;}
+ raise typeerror('r', v);
+}
+
+Value.f(v: self ref Value): ref Value.Vf
+{
+ pick xv := v {Vf => return xv;}
+ raise typeerror('f', v);
+}
+
+Value.s(v: self ref Value): ref Value.Vs
+{
+ pick xv := v {Vs => return xv;}
+ raise typeerror('s', v);
+}
+
+Value.typec(v: self ref Value): int
+{
+ return valuec[tagof v];
+}
+
+Value.dup(xv: self ref Value): ref Value
+{
+ if(xv == nil)
+ return nil;
+ pick v := xv {
+ Vb =>
+ v = nil;
+ xv = v;
+ Ve =>
+ v = nil;
+ xv = v;
+ Vw =>
+ v = nil;
+ xv = v;
+ Vr =>
+ v = nil;
+ xv = v;
+ Vf =>
+ v = nil;
+ xv = v;
+ }
+ return xv;
+}
+
+Value.free(xv: self ref Value, used: int)
+{
+ if(xv == nil)
+ return;
+ pick v := xv {
+ Vb =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ Ve =>
+ if(!used){
+ ep := <-v.i;
+ if(ep.addr != nil)
+ endpoints->open(nil, ep); # open and discard
+ }
+ Vw =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ Vr =>
+ if(!used){
+ v.i <-= "stop";
+ }
+ Vf =>
+ if(!used){
+ <-v.i;
+ v.i <-= nil;
+ }
+ }
+}
+
diff --git a/appl/alphabet/typesets/gridtypes.b b/appl/alphabet/typesets/gridtypes.b
new file mode 100644
index 00000000..05fc1bce
--- /dev/null
+++ b/appl/alphabet/typesets/gridtypes.b
@@ -0,0 +1,230 @@
+# warning: autogenerated code; don't bother to change this, change mktypeset.b or grid.b instead
+implement Gridtypes;
+include "sys.m";
+ sys: Sys;
+include "alphabet/reports.m";
+include "draw.m";
+include "sh.m";
+include "alphabet.m";
+ extvalues: Extvalues;
+ Values: import extvalues;
+ proxymod: Proxy;
+ Typescmd, Modulecmd: import Proxy;
+include "alphabet/endpoints.m";
+include "grid.m";
+ grid: Grid;
+ Value: import grid;
+include "gridtypes.m";
+
+Pcontext: adt {
+ cvt: ref Gridcvt;
+ ctxt: ref Context;
+
+ loadtypes: fn(ctxt: self ref Pcontext, name: string): (chan of ref Proxy->Typescmd[ref Value], string);
+ type2s: fn(ctxt: self ref Pcontext, tc: int): string;
+ alphabet: fn(ctxt: self ref Pcontext): string;
+ modules: fn(ctxt: self ref Pcontext, r: chan of string);
+ find: fn(ctxt: self ref Pcontext, s: string): (ref Module, string);
+ getcvt: fn(ctxt: self ref Pcontext): ref Gridcvt;
+};
+
+proxy(): chan of ref Typescmd[ref Alphabet->Value]
+{
+ return proxy0().t0;
+}
+
+proxy0(): (
+ chan of ref Typescmd[ref Alphabet->Value],
+ chan of (string, chan of ref Typescmd[ref Grid->Value]),
+ ref Gridcvt
+ )
+{
+ sys = load Sys Sys->PATH;
+ extvalues = checkload(load Extvalues Extvalues->PATH, Extvalues->PATH);
+ proxymod = checkload(load Proxy Proxy->PATH, Proxy->PATH);
+ grid = checkload(load Grid Grid->PATH, Grid->PATH);
+ grid->init();
+ cvt := ref Gridcvt(Values[ref Value].new());
+ (t, newts) := proxymod->proxy(ref Pcontext(cvt, Context.new()));
+ return (t, newts, cvt);
+}
+
+include "readdir.m";
+Context: adt {
+ modules: fn(ctxt: self ref Context, r: chan of string);
+ loadtypes: fn(ctxt: self ref Context, name: string)
+ : (chan of ref Proxy->Typescmd[ref Value], string);
+ find: fn(ctxt: self ref Context, s: string): (ref Module, string);
+ new: fn(): ref Context;
+};
+Module: adt {
+ m: Gridmodule;
+ run: fn(m: self ref Module, ctxt: ref Draw->Context, r: ref Reports->Report,
+ errorc: chan of string, opts: list of (int, list of ref Value),
+ args: list of ref Value): ref Value;
+ typesig: fn(m: self ref Module): string;
+ quit: fn(m: self ref Module);
+};
+Context.new(): ref Context
+{
+ return nil;
+}
+Context.loadtypes(nil: self ref Context, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ p := "/dis/alphabet/grid/"+name+"types.dis";
+ types := load Gridsubtypes p;
+ if(types == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ return (types->proxy(), nil);
+}
+Context.modules(nil: self ref Context, r: chan of string)
+{
+ if((readdir := load Readdir Readdir->PATH) != nil){
+ (a, nil) := readdir->init("/dis/alphabet/grid", Readdir->NAME|Readdir->COMPACT);
+ for(i := 0; i < len a; i++){
+ m := a[i].name;
+ if((a[i].mode & Sys->DMDIR) == 0 && len m > 4 && m[len m - 4:] == ".dis")
+ r <-= m[0:len m - 4];
+ }
+ }
+ r <-= nil;
+}
+Context.find(nil: self ref Context, s: string): (ref Module, string)
+{
+ p := "/dis/alphabet/grid/"+s+".dis";
+ m := load Gridmodule p;
+ if(m == nil)
+ return (nil, sys->sprint("cannot load %q: %r", p));
+ {
+ m->init();
+ } exception e {
+ "fail:*" =>
+ return (nil, "init failed: " + e[5:]);
+ }
+ return (ref Module(m), nil);
+}
+Module.run(m: self ref Module, nil: ref Draw->Context, r: ref Reports->Report, errorc: chan of string,
+ opts: list of (int, list of ref Value), args: list of ref Value): ref Value
+{
+ return m.m->run(errorc, r, opts, args);
+}
+Module.typesig(m: self ref Module): string
+{
+ return m.m->types();
+}
+Module.quit(nil: self ref Module)
+{
+}
+Pcontext.type2s(nil: self ref Pcontext, tc: int): string
+{
+ return Value.type2s(tc);
+}
+
+Pcontext.alphabet(nil: self ref Pcontext): string
+{
+ return "bewcrfs";
+}
+
+Pcontext.getcvt(ctxt: self ref Pcontext): ref Gridcvt
+{
+ return ctxt.cvt;
+}
+
+Pcontext.find(ctxt: self ref Pcontext, s: string): (ref Module, string)
+{
+ return ctxt.ctxt.find(s);
+}
+
+Pcontext.modules(ctxt: self ref Pcontext, r: chan of string)
+{
+ ctxt.ctxt.modules(r);
+}
+
+Pcontext.loadtypes(ctxt: self ref Pcontext, name: string): (chan of ref Typescmd[ref Value], string)
+{
+ return ctxt.ctxt.loadtypes(name);
+}
+
+Gridcvt.int2ext(cvt: self ref Gridcvt, gv: ref Value): ref Alphabet->Value
+{
+ if(gv == nil)
+ return nil;
+ pick v := gv {
+ Vw =>
+ return ref (Alphabet->Value).Vw(v.i);
+ Vf =>
+ return ref (Alphabet->Value).Vf(v.i);
+ Vr =>
+ return ref (Alphabet->Value).Vr(v.i);
+ Vs =>
+ return ref (Alphabet->Value).Vs(v.i);
+ Vc =>
+ return ref (Alphabet->Value).Vc(v.i);
+ * =>
+ id := cvt.values.add(gv);
+ return ref (Alphabet->Value).Vz((gv.typec(), id));
+ }
+}
+
+Gridcvt.ext2int(cvt: self ref Gridcvt, ev: ref Alphabet->Value): ref Value
+{
+ if(ev == nil)
+ return nil;
+ pick v := ev {
+ Vd =>
+ return nil; # can't happen
+ Vw =>
+ return ref Value.Vw(v.i);
+ Vf =>
+ return ref Value.Vf(v.i);
+ Vr =>
+ return ref Value.Vr(v.i);
+ Vs =>
+ return ref Value.Vs(v.i);
+ Vc =>
+ return ref Value.Vc(v.i);
+ Vz =>
+ x := cvt.values.v[v.i.id].t1;
+ if(x == nil){
+ sys->print("gridtypes: bad id %d, type %c\n", v.i.id, v.i.typec);
+ return nil;
+ }
+ return x;
+ }
+}
+
+Gridcvt.free(cvt: self ref Gridcvt, gv: ref Alphabet->Value, used: int)
+{
+ pick v := gv {
+ Vz =>
+ id := v.i.id;
+ cvt.values.v[id].t1.free(used);
+ cvt.values.del(id);
+ }
+}
+
+Gridcvt.dup(cvt: self ref Gridcvt, gv: ref Alphabet->Value): ref Alphabet->Value
+{
+ pick ev := gv {
+ Vz =>
+ id := ev.i.id;
+ v := cvt.values.v[id].t1;
+ nv := v.dup();
+ if(nv == nil)
+ return nil;
+ if(nv != v)
+ return ref (Alphabet->Value).Vz((ev.i.typec, cvt.values.add(nv)));
+ cvt.values.inc(id);
+ return ev;
+ * =>
+ return nil;
+ }
+}
+
+checkload[T](m: T, path: string): T
+{
+ if(m != nil)
+ return m;
+ sys->fprint(sys->fildes(2), "gridtypes: cannot load %s: %r\n", path);
+ raise "fail:bad module";
+}
diff --git a/appl/alphabet/typesets/mkfile b/appl/alphabet/typesets/mkfile
new file mode 100644
index 00000000..dc0a0a81
--- /dev/null
+++ b/appl/alphabet/typesets/mkfile
@@ -0,0 +1,34 @@
+<../../../mkconfig
+
+TARG=\
+ mktypeset.dis\
+ abc.dis\
+ abctypes.dis\
+ fs.dis\
+ fstypes.dis\
+ grid.dis\
+ gridtypes.dis\
+
+SYSMODULES=\
+ alphabet.m\
+ alphabet/endpoints.m\
+ alphabet/reports.m\
+ draw.m\
+ readdir.m\
+ sh.m\
+ sys.m\
+ alphabet/abc.m\
+ alphabet/abctypes.m\
+ alphabet/fs.m\
+ alphabet/fstypes.m\
+ alphabet/grid.m\
+ alphabet/gridtypes.m\
+
+DISBIN=$ROOT/dis/alphabet
+
+<$ROOT/mkfiles/mkdis
+
+LIMBOFLAGS=$LIMBOFLAGS -i -F
+
+#%.b %types.b %.m %types.m: %.typeset
+# mktypeset $stem.typeset