diff options
Diffstat (limited to 'appl/cmd/sh')
| -rw-r--r-- | appl/cmd/sh/mkfile | 5 | ||||
| -rw-r--r-- | appl/cmd/sh/mload.b | 348 | ||||
| -rw-r--r-- | appl/cmd/sh/mpexpr.b | 435 |
3 files changed, 788 insertions, 0 deletions
diff --git a/appl/cmd/sh/mkfile b/appl/cmd/sh/mkfile index 383c5ed9..1e5801eb 100644 --- a/appl/cmd/sh/mkfile +++ b/appl/cmd/sh/mkfile @@ -3,7 +3,9 @@ TARG=sh.dis\ arg.dis\ expr.dis\ + mpexpr.dis\ file2chan.dis\ + mload.dis\ regex.dis\ sexprs.dis\ std.dis\ @@ -16,6 +18,9 @@ TARG=sh.dis\ INS= $ROOT/dis/sh.dis\ $ROOT/dis/sh/arg.dis\ $ROOT/dis/sh/expr.dis\ + $ROOT/dis/sh/file2chan.dis\ + $ROOT/dis/sh/mload.dis\ + $ROOT/dis/sh/mpexpr.dis\ $ROOT/dis/sh/regex.dis\ $ROOT/dis/sh/std.dis\ $ROOT/dis/sh/string.dis\ 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); +} diff --git a/appl/cmd/sh/mpexpr.b b/appl/cmd/sh/mpexpr.b new file mode 100644 index 00000000..682c01e8 --- /dev/null +++ b/appl/cmd/sh/mpexpr.b @@ -0,0 +1,435 @@ +implement Shellbuiltin; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "keyring.m"; + keyring: Keyring; + IPint: import keyring; +include "sh.m"; + sh: Sh; + Listnode, Context: import sh; + myself: Shellbuiltin; + +Big: type ref IPint; +Zero: Big; +One: Big; +initbuiltin(ctxt: ref Context, shmod: Sh): string +{ + sys = load Sys Sys->PATH; + keyring = load Keyring Keyring->PATH; + sh = shmod; + myself = load Shellbuiltin "$self"; + if (myself == nil) + ctxt.fail("bad module", sys->sprint("expr: cannot load self: %r")); + + Zero = IPint.inttoip(0); + One = IPint.inttoip(1); + ctxt.addsbuiltin("expr", myself); + ctxt.addbuiltin("ntest", myself); + return nil; +} + +whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string +{ + return nil; +} + +getself(): Shellbuiltin +{ + return myself; +} + +EQ, GT, LT, GE, LE, PLUS, MINUS, DIVIDE, AND, TIMES, MOD, +OR, XOR, UMINUS, SHL, SHR, NOT, BNOT, NEQ, REP, SEQ, +BITS, EXPMOD, INVERT, RAND, EXP: con iota; + +runbuiltin(ctxt: ref Sh->Context, nil: Sh, + cmd: list of ref Sh->Listnode, nil: int): string +{ + case (hd cmd).word { + "ntest" => + if (len cmd != 2) + ctxt.fail("usage", "usage: ntest n"); + if(strtoip(ctxt, (hd tl cmd).word).eq(Zero)) + return "false"; + } + return nil; +} + +runsbuiltin(ctxt: ref Sh->Context, nil: Sh, + cmd: list of ref Sh->Listnode): list of ref Listnode +{ + # only one sbuiltin: expr. + stk: list of Big; + lastop := -1; + lastn := -1; + lastname := ""; + radix: int; + (cmd, radix) = opts(ctxt, tl cmd); + for (; cmd != nil; cmd = tl cmd) { + w := (hd cmd).word; + op := -1; + nops := 2; + case w { + "+" => + op = PLUS; + "-" => + op = MINUS; + "x" or "*" or "×" => + op = TIMES; + "/" => + op = DIVIDE; + "%" => + op = MOD; + "and" => + op = AND; + "or" => + op = OR; + "xor" => + op = XOR; + "_"=> + (op, nops) = (UMINUS, 1); + "<<" or "shl" => + op = SHL; + ">>" or "shr" => + op = SHR; + "=" or "==" or "eq" => + op = EQ; + "!=" or "neq" => + op = NEQ; + ">" or "gt" => + op = GT; + "<" or "lt" => + op = LT; + ">=" or "ge" => + op = GE; + "<=" or "le" => + op = LE; + "!" or "not" => + (op, nops) = (NOT, 1); + "~" => + (op, nops) = (BNOT, 1); + "rep" => + (op, nops) = (REP, 0); + "seq" => + (op, nops) = (SEQ, 2); + "bits" => + (op, nops) = (BITS, 1); + "expmod" => + (op, nops) = (EXPMOD, 3); + "invert" => + (op, nops) = (INVERT, 2); + "rand" => + (op, nops) = (RAND, 1); + "exp" or "xx" or "**" => + (op, nops) = (EXP, 2); + } + if (op == -1){ + if (w == nil || (w[0] != '-' && (w[0] < '0' || w[0] > '9'))) + ctxt.fail("usage", sys->sprint("expr: unknown operator '%s'", w)); + stk = strtoip(ctxt, w) :: stk; + }else + stk = operator(ctxt, stk, op, nops, lastop, lastn, w, lastname); + lastop = op; + lastn = nops; + lastname = w; + } + r: list of ref Listnode; + for (; stk != nil; stk = tl stk) + r = ref Listnode(nil, iptostr(hd stk, radix)) :: r; + return r; +} + +opts(ctxt: ref Context, cmd: list of ref Listnode): (list of ref Listnode, int) +{ + if (cmd == nil) + return (nil, 10); + w := (hd cmd).word; + if (len w < 2) + return (cmd, 10); + if (w[0] != '-' || (w[1] >= '0' && w[1] <= '9')) + return (cmd, 10); + if (w[1] != 'r') + ctxt.fail("usage", "usage: expr [-r radix] [arg...]"); + if (len w > 2) + w = w[2:]; + else { + if (tl cmd == nil) + ctxt.fail("usage", "usage: expr [-r radix] [arg...]"); + cmd = tl cmd; + w = (hd cmd).word; + } + r := int w; + if (r <= 0 || (r > 36 && r != 64)) + ctxt.fail("usage", "expr: invalid radix " + string r); + return (tl cmd, int w); +} + +operator(ctxt: ref Context, stk: list of Big, op, nops, lastop, lastn: int, + opname, lastopname: string): list of Big +{ + al: list of Big; + for (i := 0; i < nops; i++) { + if (stk == nil) + ctxt.fail("empty stack", + sys->sprint("expr: empty stack on op '%s'", opname)); + al = hd stk :: al; + stk = tl stk; + } + return oper(ctxt, al, op, lastop, lastn, lastopname, stk); +} + +# args are in reverse order +oper(ctxt: ref Context, args: list of Big, op, lastop, lastn: int, + lastopname: string, stk: list of Big): list of Big +{ + if (op == REP) { + if (lastop == -1 || lastop == SEQ || lastn != 2) + ctxt.fail("usage", "expr: bad operator for rep"); + if (stk == nil || tl stk == nil) + return stk; + while (tl stk != nil) + stk = operator(ctxt, stk, lastop, 2, -1, -1, lastopname, nil); + return stk; + } + n3 := Zero; + n2 := Zero; + n1 := hd args; + if (tl args != nil){ + n2 = hd tl args; + if(tl tl args != nil) + n3 = hd tl tl args; + } + r := Zero; + case op { + EQ => r = mki(n1.eq(n2)); + NEQ => r = mki(!n1.eq(n2)); + GT => r = mki(n1.cmp(n2) > 0); + LT => r = mki(n1.cmp(n2) < 0); + GE => r = mki(n1.cmp(n2) >= 0); + LE => r = mki(n1.cmp(n2) <= 0); + PLUS => r = n1.add(n2); + MINUS => r = n1.sub(n2); + NOT => r = mki(n1.eq(Zero)); + DIVIDE => + if (n2.eq(Zero)) + ctxt.fail("divide by zero", "expr: division by zero"); + (r, nil) = n1.div(n2); + MOD => + if (n2.eq(Zero)) + ctxt.fail("divide by zero", "expr: division by zero"); + (nil, r) = n1.div(n2); + TIMES => + r = n1.mul(n2); + AND => r = bitop(ipand, n1, n2); + OR => r = bitop(ipor, n1, n2); + XOR => r = bitop(ipxor, n1, n2); + UMINUS => r = n1.neg(); + BNOT => r = n1.neg().sub(One); + SHL => r = n1.shl(n2.iptoint()); + SHR => r = n1.shr(n2.iptoint()); + SEQ => return seq(n1, n2, stk); + BITS => r = mki(n1.bits()); + EXPMOD => r = n1.expmod(n2, n3); + EXP => r = n1.expmod(n2, nil); + RAND => r = IPint.random(0, n1.iptoint()); + INVERT => r = n1.invert(n2); + } + return r :: stk; +} + +# won't work if op(0, 0) != 0 +bitop(op: ref fn(n1, n2: Big): Big, n1, n2: Big): Big +{ + bits := max(n1.bits(), n2.bits()); + return signedmag(op(twoscomp(n1, bits), twoscomp(n2, bits)), bits); +} + +onebits(n: int): Big +{ + return One.shl(n).sub(One); +} + +# return a two's complement version of n, +# sign-extended to b bits if negative. +# sign bit is at 1<<b. +twoscomp(n: Big, b: int): Big +{ + if(n.cmp(Zero) >= 0) + return n; + return n.not().ori(onebits(b).xor(onebits(n.bits()))).add(One); +} + +# return conventional representation of n, +# where n is in two's complement form in b bits. +signedmag(n: Big, b: int): Big +{ + if(n.and(One.shl(b)).eq(Zero)) + return n; + return n.sub(One).not().and(onebits(b)).neg(); +} + +max(x, y: int): int +{ + if(x > y) + return x; + else + return y; +} + +seq(n1, n2: Big, stk: list of Big): list of Big +{ + incr := mki(1); + if (n2.cmp(n1) < 0) + incr = mki(-1); + for (; !n1.eq(n2); n1 = n1.add(incr)) + stk = n1 :: stk; + return n1 :: stk; +} + +strtoip(ctxt: ref Context, s: string): Big +{ + t := s; + if (neg := s[0] == '-') + s = s[1:]; + radix := 10; + for (i := 0; i < len s && i < 3; i++) { + if (s[i] == 'r') { + radix = int s; + s = s[i+1:]; + break; + } + } + if (radix == 10) + return IPint.strtoip(s, 10); + if (radix == 0 || (radix > 36 && radix != 64)) + ctxt.fail("usage", "expr: bad number " + t); + n := Zero; + case radix { + 10 or 16 or 64 => + n = IPint.strtoip(s, radix); + * => + r := mki(radix); + for (i = 0; i < len s; i++) { + if ('0' <= s[i] && s[i] <= '9') + n = n.mul(r).add(mki(s[i] - '0')); + else if ('a' <= s[i] && s[i] < 'a' + radix - 10) + n = n.mul(r).add(mki(s[i] - 'a' + 10)); + else if ('A' <= s[i] && s[i] < 'A' + radix - 10) + n = n.mul(r).add(mki(s[i] - 'A' + 10)); + else + break; + } + } + if(neg) + return n.neg(); + return n; +} + +iptostr(n: Big, radix: int): string +{ + neg := n.cmp(Zero) < 0; + t: string; + case radix { + 2 or 4 or 16 or 32 => + b := n.iptobebytes(); + rbits := log2(radix); + bits := roundup(n.bits(), rbits); + for(i := bits - rbits; i >= 0; i -= rbits){ + d := 0; + for(j := 0; j < rbits; j++) + d |= getbit(b, i+j) << j; + t[len t] = digit(d); + } + 10 => + return n.iptostr(radix); + 64 => + t = n.iptostr(radix); + if(neg) + t = t[1:]; + * => + if(neg) + n = n.neg(); + r := mki(radix); + s: string; + do{ + d: Big; + (n, d) = n.div(r); + s[len s] = digit(d.iptoint()); + }while(n.cmp(Zero) > 0); + t = s; + for (i := len s - 1; i >= 0; i--) + t[len s - 1 - i] = s[i]; + } + t = string radix + "r" + t; + if (neg) + return "-" + t; + return t; +} + +mki(i: int): Big +{ + return IPint.inttoip(i); +} + +b2s(b: array of byte): string +{ + s := ""; + for(i := 0; i < len b; i++) + s += sys->sprint("%.2x", int b[i]); + return s; +} + +# count from least significant bit. +getbit(b: array of byte, bit: int): int +{ + if((i := bit >> 3) >= len b){ + return 0; + }else{ + return (int b[len b - i -1] >> (bit&7)) & 1; + } +} + +digit(d: int): int +{ + if(d < 10) + return '0' + d; + else + return 'a' + d - 10; +} + +log2(x: int): int +{ + case x { + 2 => return 1; + 4 => return 2; + 8 => return 3; + 16 => return 4; + 32 => return 5; + } + return 0; +} + +roundup(n: int, m: int): int +{ + return m*((n+m-1)/m); +} + +# these functions are to get around the fact that the limbo compiler isn't +# currently considering ref fn(x: self X, ...) compatible with ref fn(x: X, ...). +ipand(n1, n2: Big): Big +{ + return n1.and(n2); +} + +ipor(n1, n2: Big): Big +{ + return n1.ori(n2); +} + + +ipxor(n1, n2: Big): Big +{ + return n1.xor(n2); +} |
