summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2008-06-10 15:06:19 +0000
committerCharles.Forsyth <devnull@localhost>2008-06-10 15:06:19 +0000
commitcd48a233840576d6782a2be4745d44badcb1bbeb (patch)
treee99527feb19b45c282350d79d07f4f819ea475b2
parent13c8260ff19b8d186b77c30ae42db83c4f29e0b6 (diff)
20080910-1604
-rw-r--r--CHANGES2
-rw-r--r--appl/cmd/sh/mkfile5
-rw-r--r--appl/cmd/sh/mload.b348
-rw-r--r--appl/cmd/sh/mpexpr.b435
-rw-r--r--dis/sh/mload.disbin0 -> 5580 bytes
-rw-r--r--dis/sh/mpexpr.disbin0 -> 8331 bytes
-rw-r--r--include/version.h2
-rw-r--r--man/1/sh-expr50
-rw-r--r--man/1/sh-mload68
-rw-r--r--man/2/INDEX52
10 files changed, 921 insertions, 41 deletions
diff --git a/CHANGES b/CHANGES
index 438aecf1..96b95581 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+20080610
+ push sh-mload(1) and changes to sh-expr(1)
20080609
_tas type changed from ulong to int
20080530
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);
+}
diff --git a/dis/sh/mload.dis b/dis/sh/mload.dis
new file mode 100644
index 00000000..fb114990
--- /dev/null
+++ b/dis/sh/mload.dis
Binary files differ
diff --git a/dis/sh/mpexpr.dis b/dis/sh/mpexpr.dis
new file mode 100644
index 00000000..41fc53e3
--- /dev/null
+++ b/dis/sh/mpexpr.dis
Binary files differ
diff --git a/include/version.h b/include/version.h
index 80e0c58d..dbbd4e07 100644
--- a/include/version.h
+++ b/include/version.h
@@ -1 +1 @@
-#define VERSION "Fourth Edition (20080609)"
+#define VERSION "Fourth Edition (20080610)"
diff --git a/man/1/sh-expr b/man/1/sh-expr
index 976e6c04..be9d2b97 100644
--- a/man/1/sh-expr
+++ b/man/1/sh-expr
@@ -1,8 +1,10 @@
.TH SH-EXPR 1
.SH NAME
-expr, ntest \- shell module for simple arithmetic.
+expr, ntest, mpexpr \- shell module for simple arithmetic.
.SH SYNOPSIS
.B load expr
+OR
+.B load mpexpr
.B ${expr
[
@@ -19,10 +21,17 @@ expr, ntest \- shell module for simple arithmetic.
.br
.SH DESCRIPTION
.I Expr
-is a loadable module for
+and
+.I mpexpr
+are loadable modules for
.IR sh (1)
-that provides support for simple integer arithmetic.
-It provides one command,
+that provide support for integer arithmetic.
+.I Expr
+uses 64-bit signed integers;
+.I mpexpr
+uses arbitrary-precision signed integers.
+They each provide the same interface:
+a command
.IR ntest ,
which performs a simple boolean test
on its integer argument, and the
@@ -54,15 +63,16 @@ Alternative names are given for some operators;
this is to avoid the necessity of quoting operators
that contain
.IR sh (1)
-metacharacters. All operations use 64-bit signed
-integers; integers are given in the same form acceptable
+metacharacters. Integers are given in the same form acceptable
to Limbo. The relational operators yield either
1 (true) or 0 (false). If the
.B -r
option is given,
.I radix
-specifies an output base for numbers yielded by
-.IR expr .
+specifies an output base for printed numbers.
+It may be from 2 to 36;
+.I mpexpr
+also allows 64 to specify base64 notation.
Numbers are printed in a form suitable for re-interpretation
by
.IR expr .
@@ -70,8 +80,9 @@ by
When all its arguments have been evaluated,
.B expr
yields all the values remaining on its stack, first pushed
-first. The operators supported by expr are as follows (the number
-of operands required in is given parentheses):
+first. Note that bitwise operators treat their operands as if they
+were stored in two's complement form. The operators supported by expr are as follows (the number
+of operands required in is given parentheses).
.TP 15
.BR + \ (2)
Addition
@@ -148,13 +159,30 @@ pushes on the stack a sequence of numbers ranging
numerically from its first argument up to and including
its second argument. If its second argument is
less than its first, the sequence will descend.
+.TP
+.BR rand \ (1)
+(\fImpexpr\fP only). Push a secure random number;
+the argument value gives the size of the number, in bits.
+.TP
+.BR bits \ (1)
+(\fImpexpr\fP only). Push the size, in bits, of the argument.
+.TP
+.BR expmod ", " invert " (2)"
+(\fImpexpr\fP only). See
+.IR ipint (2).
+.TP
+.BR exp ", " xx ", " **
+(\fImpexpr\fP only). Exponentiation.
.SH SOURCE
.B /appl/cmd/sh/expr.b
.SH SEE ALSO
.IR sh (1),
.IR sh-std (1),
-.IR sh-tk (1)
+.IR sh-tk (1),
+.IR keyring-ipint (2)
.SH BUGS
Postfix notation can be confusing.
Any operators that contain shell metacharacters (e.g. ``*'', ``>'')
must be quoted to avoid interpretation by the shell.
+Base64 notation can contain # characters, which need
+quoting to avoid interpretation by the shell.
diff --git a/man/1/sh-mload b/man/1/sh-mload
new file mode 100644
index 00000000..e56cb92a
--- /dev/null
+++ b/man/1/sh-mload
@@ -0,0 +1,68 @@
+.TH SH-MLOAD 1
+.SH NAME
+mload, munload \- namespace separation for shell modules
+.SH SYNOPSIS
+.B load mload
+
+.B mload
+.I name
+[
+.IR path ...
+]
+.br
+.B munload
+.I name
+[
+.IR path ...
+]
+.br
+.SH DESCRIPTION
+.I Mload
+is a loadable module for
+.IR sh (1)
+that allows the simultaneous use of shell modules with
+potentially clashing command name spaces.
+.B Mload
+creates a new namespace
+.I name
+and loads each
+.I path
+as a builtin module in the same way as
+.B load
+(see
+.IR sh (1)).
+Any commands or substitution builtins defined
+by the modules are accessible by giving
+the command and its arguments as arguments to
+the
+.I name
+command.
+.PP
+.B Munload
+unloads a module from the namespace
+.IR name .
+If no modules remain in the namespace,
+.I name
+is undefined as a command.
+.SH EXAMPLE
+Load
+.I mpexpr
+in a different namespace from
+.I expr
+(see
+.IR sh-expr (1)):
+.EX
+load expr
+mload mp mpexpr
+echo ${expr 1 2 +}
+echo ${mp expr 2 300 xx}
+.EE
+.SH SOURCE
+.B /appl/cmd/sh/mload.b
+.SH SEE ALSO
+.IR sh (1),
+.SH BUGS
+Because of the way shell modules are implemented,
+the namespaces are global across all processes that
+share an instance of
+.IR mload .
diff --git a/man/2/INDEX b/man/2/INDEX
index 07b31546..c90cf393 100644
--- a/man/2/INDEX
+++ b/man/2/INDEX
@@ -150,19 +150,6 @@ readjson json
writejson json
keyring intro keyring-0intro
keyring-intro keyring-0intro
-auth keyring-auth
-keyring keyring-auth
-keyring-auth keyring-auth
-readauthinfo keyring-auth
-writeauthinfo keyring-auth
-certtostr keyring-certtostr
-keyring keyring-certtostr
-keyring-certtostr keyring-certtostr
-pktostr keyring-certtostr
-sktostr keyring-certtostr
-strtocert keyring-certtostr
-strtopk keyring-certtostr
-strtosk keyring-certtostr
aescbc keyring-crypt
aessetup keyring-crypt
descbc keyring-crypt
@@ -173,24 +160,22 @@ ideaecb keyring-crypt
ideasetup keyring-crypt
keyring keyring-crypt
keyring-crypt keyring-crypt
+dsagen keyring-dsagen
+eggen keyring-dsagen
+keyring keyring-dsagen
+keyring-dsagen keyring-dsagen
+rsadecrypt keyring-dsagen
+rsaencrypt keyring-dsagen
+rsafill keyring-dsagen
+rsagen keyring-dsagen
dhparams keyring-gensk
gensk keyring-gensk
genskfrompk keyring-gensk
keyring keyring-gensk
keyring-gensk keyring-gensk
+sign keyring-gensk
sktopk keyring-gensk
-getmsg keyring-getmsg
-keyring keyring-getmsg
-keyring-getmsg keyring-getmsg
-senderrmsg keyring-getmsg
-sendmsg keyring-getmsg
-getbytearray keyring-getstring
-getstring keyring-getstring
-keyring keyring-getstring
-keyring-getstring keyring-getstring
-putbytearray keyring-getstring
-puterror keyring-getstring
-putstring keyring-getstring
+verify keyring-gensk
ipint keyring-ipint
keyring keyring-ipint
keyring-ipint keyring-ipint
@@ -207,8 +192,6 @@ keyring-sha1 keyring-sha1
md4 keyring-sha1
md5 keyring-sha1
sha1 keyring-sha1
-sign keyring-sha1
-verify keyring-sha1
keyset keyset
allsat lists
anysat lists
@@ -346,12 +329,23 @@ secstore secstore
intro security-0intro
security-intro security-0intro
auth security-auth
-client security-auth
+fauth security-auth
init security-auth
security-auth security-auth
-server security-auth
login security-login
security-login security-login
+certtostr security-oldauth
+oldauth security-oldauth
+pktostr security-oldauth
+readauthinfo security-oldauth
+security-oldauth security-oldauth
+sign security-oldauth
+sktostr security-oldauth
+strtocert security-oldauth
+strtopk security-oldauth
+strtosk security-oldauth
+verify security-oldauth
+writeauthinfo security-oldauth
random security-random
randombuf security-random
randomint security-random