summaryrefslogtreecommitdiff
path: root/appl/cmd/sh/mload.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/sh/mload.b')
-rw-r--r--appl/cmd/sh/mload.b348
1 files changed, 348 insertions, 0 deletions
diff --git a/appl/cmd/sh/mload.b b/appl/cmd/sh/mload.b
new file mode 100644
index 00000000..30990ff5
--- /dev/null
+++ b/appl/cmd/sh/mload.b
@@ -0,0 +1,348 @@
+implement Sh;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "sh.m";
+ sh: Sh;
+ myself: Shellbuiltin;
+ mysh: Sh;
+
+Namespace: adt {
+ name: string;
+ madecmd: array of int;
+ mods: list of (string, Shellbuiltin);
+ builtins: array of list of (string, Shellbuiltin);
+};
+Builtin, Sbuiltin: con iota;
+
+namespaces: list of ref Namespace;
+pending: list of (string, int, Shellbuiltin);
+lock: chan of int;
+BUILTINPATH: con "/dis/sh";
+
+initbuiltin(c: ref Sh->Context, shmod: Sh): string
+{
+ sys = load Sys Sys->PATH;
+ sh = shmod;
+ mysh = load Sh "$self";
+ myself = load Shellbuiltin "$self";
+ sh->c.addbuiltin("mload", myself);
+ sh->c.addbuiltin("munload", myself);
+ lock = chan[1] of int;
+ return nil;
+}
+
+runbuiltin(ctxt: ref Sh->Context, nil: Sh,
+ argv: list of ref Sh->Listnode, last: int): string
+{
+ cmd := (hd argv).word;
+ case cmd {
+ "mload" or "munload" =>
+ if(tl argv == nil)
+ ctxt.fail("usage", "usage: "+cmd+" name [module...]");
+
+ # by doing this lock, we're relying on modules not to invoke a command
+ # in initbuiltin that calls back into mload. since they shouldn't be running
+ # any commands in initbuiltin anyway, this seems like a reasonable assumption.
+ lock <-= 1;
+ {
+ name := (hd tl argv).word;
+ for(argv = tl tl argv; argv != nil; argv = tl argv){
+ if((hd argv).cmd != nil)
+ ctxt.fail("usage", "usage: "+cmd+" namespace [module...]");
+ if(cmd == "mload")
+ mload(ctxt, name, (hd argv).word);
+ else
+ munload(ctxt, name, (hd argv).word);
+ }
+ }exception{
+ "fail:*" =>
+ <-lock;
+ raise;
+ }
+ <-lock;
+ return nil;
+ * =>
+ if(len argv < 2)
+ ctxt.fail("usage", sys->sprint("usage: %s command", (hd argv).word));
+
+ b := lookup(ctxt, (hd argv).word, (hd tl argv).word, Builtin, nil);
+ return b->runbuiltin(ctxt, mysh, tl argv, last);
+ }
+}
+
+mload(ctxt: ref Sh->Context, name, modname: string): string
+{
+ ns := nslookup(name);
+ if(ns == nil){
+ ns = ref Namespace(name, array[2] of {* => 0}, nil, array[2] of list of (string, Shellbuiltin));
+ namespaces = ns :: namespaces;
+ }
+ for(nsm := ns.mods; nsm != nil; nsm = tl nsm)
+ if((hd nsm).t0 == modname)
+ return nil;
+ path := modname;
+ if (len path < 4 || path[len path-4:] != ".dis")
+ path += ".dis";
+ if (path[0] != '/' && path[0:2] != "./")
+ path = BUILTINPATH + "/" + path;
+ mod := load Shellbuiltin path;
+ if (mod == nil)
+ ctxt.fail("bad module", sys->sprint("load: cannot load %s: %r", path));
+ s := mod->initbuiltin(ctxt, mysh);
+ if(s != nil){
+ munload(ctxt, name, modname);
+ pending = nil;
+ ctxt.fail("init", "mload: init "+modname+" failed: "+s);
+ }
+ mod = mod->getself();
+ ns.mods = (modname, mod) :: ns.mods;
+ for(; pending != nil; pending = tl pending){
+ (cmd, which, pmod) := hd pending;
+ if(pmod != mod)
+ sys->fprint(sys->fildes(2), "mload: unexpected module when loading %#q", name);
+ else
+ lookup(ctxt, name, cmd, which, mod);
+ }
+
+ return nil;
+}
+
+munload(ctxt: ref Sh->Context, name, modname: string): string
+{
+ ns := nslookup(name);
+ if(ns == nil){
+ sys->fprint(sys->fildes(2), "munload: no such namespace %#q\n", name);
+ return "fail";
+ }
+ nm: list of (string, Shellbuiltin);
+ mod: Shellbuiltin;
+ for(m := ns.mods; m != nil; m = tl m)
+ if((hd m).t0 == modname)
+ mod = (hd m).t1;
+ else
+ nm = hd m :: nm;
+ if(mod == nil){
+ sys->fprint(sys->fildes(2), "munload: no such module %#q\n", modname);
+ return "fail";
+ }
+ ns.mods = nm;
+ for(i := 0; i < 2; i++){
+ nb: list of (string, Shellbuiltin) = nil;
+ for(b := ns.builtins[i]; b != nil; b = tl b)
+ if((hd b).t1 != mod)
+ nb = hd b :: nb;
+ ns.builtins[i] = nb;
+ if(ns.builtins[i] == nil){
+ if(i == Builtin)
+ sh->ctxt.removebuiltin(name, myself);
+ else
+ sh->ctxt.removesbuiltin(name, myself);
+ }
+
+ }
+ return nil;
+}
+
+
+runsbuiltin(ctxt: ref Sh->Context, nil: Sh,
+ argv: list of ref Sh->Listnode): list of ref Sh->Listnode
+{
+ if(len argv < 2)
+ ctxt.fail("usage", sys->sprint("usage: %s command", (hd argv).word));
+ b := lookup(ctxt, (hd argv).word, (hd tl argv).word, Sbuiltin, nil);
+ return b->runsbuiltin(ctxt, mysh, tl argv);
+}
+
+searchns(mod: Shellbuiltin): string
+{
+ for(m := namespaces; m != nil; m = tl m)
+ for(b := (hd m).mods; b != nil; b = tl b)
+ if((hd b).t1 == mod)
+ return (hd m).name;
+ return nil;
+}
+
+lookup(ctxt: ref Sh->Context, name, cmd: string, which: int, sb: Shellbuiltin): Shellbuiltin
+{
+ for(m := namespaces; m != nil; m = tl m)
+ if((hd m).name == name)
+ break;
+ if(m == nil)
+ ctxt.fail("unknown", sys->sprint("unknown namespace %q", name));
+ ns := hd m;
+ for(b := ns.builtins[which]; b != nil; b = tl b)
+ if((hd b).t0 == cmd)
+ break;
+ if(b == nil){
+ if(sb != nil){
+ ns.builtins[which] = (cmd, sb) :: ns.builtins[which];
+ if(!ns.madecmd[which]){
+ if(which == Builtin)
+ sh->ctxt.addbuiltin(name, myself);
+ else
+ sh->ctxt.addsbuiltin(name, myself);
+ ns.madecmd[which] = 1;
+ }
+ return sb;
+ }
+ ctxt.fail("unknown cmd", sys->sprint("unknown command %q", cmd));
+ }
+ return (hd b).t1;
+}
+
+Context.addbuiltin(c: self ref Context, modname: string, mod: Shellbuiltin)
+{
+ name := searchns(mod);
+ if(name == nil)
+ pending = (modname, Builtin, mod) :: pending;
+ else
+ lookup(c, name, modname, Builtin, mod);
+}
+
+Context.addsbuiltin(c: self ref Context, modname: string, mod: Shellbuiltin)
+{
+ name := searchns(mod);
+ if(name == nil)
+ pending = (modname, Sbuiltin, mod) :: pending;
+ else
+ lookup(c, name, modname, Sbuiltin, mod);
+}
+
+Context.removebuiltin(c: self ref Context, nil: string, nil: Shellbuiltin)
+{
+ c.fail("nope", "mload: remove builtin not implemented");
+}
+
+Context.removesbuiltin(c: self ref Context, nil: string, nil: Shellbuiltin)
+{
+ c.fail("nope", "mload: remove sbuiltin not implemented");
+}
+
+Context.addmodule(nil: self ref Context, name: string, nil: Shellbuiltin)
+{
+ sys->fprint(sys->fildes(2), "mload: addmodule not allowed (%s)\n", name);
+}
+
+nslookup(name: string): ref Namespace
+{
+ for(m := namespaces; m != nil; m = tl m)
+ if((hd m).name == name)
+ return hd m;
+ return nil;
+}
+
+whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string
+{
+ return nil;
+}
+
+getself(): Shellbuiltin
+{
+ return myself;
+}
+
+initialise()
+{
+ return sh->initialise();
+}
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ return sh->init(ctxt, argv);
+}
+
+system(ctxt: ref Draw->Context, cmd: string): string
+{
+ return sh->system(ctxt, cmd);
+}
+
+run(ctxt: ref Draw->Context, argv: list of string): string
+{
+ return sh->run(ctxt, argv);
+}
+
+parse(s: string): (ref Cmd, string)
+{
+ return sh->parse(s);
+}
+
+cmd2string(c: ref Cmd): string
+{
+ return sh->cmd2string(c);
+}
+
+list2stringlist(nl: list of ref Listnode): list of string
+{
+ return sh->list2stringlist(nl);
+}
+
+stringlist2list(sl: list of string): list of ref Listnode
+{
+ return sh->stringlist2list(sl);
+}
+
+quoted(val: list of ref Listnode, quoteblocks: int): string
+{
+ return sh->quoted(val, quoteblocks);
+}
+
+Context.new(drawcontext: ref Draw->Context): ref Context
+{
+ return sh->Context.new(drawcontext);
+}
+
+Context.get(c: self ref Context, name: string): list of ref Listnode
+{
+ return sh->c.get(name);
+}
+
+Context.set(c: self ref Context, name: string, val: list of ref Listnode)
+{
+ return sh->c.set(name, val);
+}
+
+Context.setlocal(c: self ref Context, name: string, val: list of ref Listnode)
+{
+ return sh->c.setlocal(name, val);
+}
+
+Context.envlist(c: self ref Context): list of (string, list of ref Listnode)
+{
+ return sh->c.envlist();
+}
+
+Context.push(c: self ref Context)
+{
+ return sh->c.push();
+}
+
+Context.pop(c: self ref Context)
+{
+ return sh->c.pop();
+}
+
+Context.copy(c: self ref Context, copyenv: int): ref Context
+{
+ return sh->c.copy(copyenv);
+}
+
+Context.run(c: self ref Context, args: list of ref Listnode, last: int): string
+{
+ return sh->c.run(args, last);
+}
+
+Context.fail(c: self ref Context, ename, msg: string)
+{
+ return sh->c.fail(ename, msg);
+}
+
+Context.options(c: self ref Context): int
+{
+ return sh->c.options();
+}
+
+Context.setoptions(c: self ref Context, flags, on: int): int
+{
+ return sh->c.setoptions(flags, on);
+}