diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/alphabet | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/alphabet')
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 |
