summaryrefslogtreecommitdiff
path: root/appl/cmd/mash/xeq.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/mash/xeq.b')
-rw-r--r--appl/cmd/mash/xeq.b543
1 files changed, 543 insertions, 0 deletions
diff --git a/appl/cmd/mash/xeq.b b/appl/cmd/mash/xeq.b
new file mode 100644
index 00000000..fd2f1e6f
--- /dev/null
+++ b/appl/cmd/mash/xeq.b
@@ -0,0 +1,543 @@
+#
+# Command execution.
+#
+
+#
+# Entry from parser.
+#
+Cmd.xeq(c: self ref Cmd, e: ref Env)
+{
+ if (e.flags & EDumping) {
+ s := c.text();
+ f := e.outfile();
+ f.puts(s);
+ if (s != nil && s[len s - 1] != '&')
+ f.putc(';');
+ f.putc('\n');
+ f.close();
+ f = nil;
+ }
+ if ((e.flags & ENoxeq) == 0)
+ c.xeqit(e, 1);
+}
+
+#
+# Execute a command. Tail recursion.
+#
+Cmd.xeqit(c: self ref Cmd, e: ref Env, wait: int)
+{
+tail: for (;;) {
+ if (c == nil)
+ return;
+ case c.op {
+ Csimple =>
+ c.simple(e, wait);
+ Casync =>
+ e = e.clone();
+ e.in = e.devnull();
+ e.wait = nil;
+ spawn c.left.xeqit(e, 1);
+ Cgroup =>
+ if (c.redirs != nil) {
+ (ok, in, out) := mkredirs(e, c.redirs);
+ if (!ok)
+ return;
+ e = e.copy();
+ e.in = in;
+ e.out = out;
+ c.left.xeqit(e, 1);
+ } else {
+ c = c.left;
+ continue tail;
+ }
+ Csubgroup =>
+ e = e.clone();
+ if (c.redirs != nil) {
+ (ok, in, out) := mkredirs(e, c.redirs);
+ if (!ok)
+ return;
+ e.in = in;
+ e.out = out;
+ }
+ c = c.left;
+ continue tail;
+ Cseq =>
+ c.left.xeqit(e, 1);
+ c = c.right;
+ continue tail;
+ Cpipe =>
+ do {
+ fds := e.pipe();
+ if (fds == nil)
+ return;
+ n := e.clone();
+ n.out = fds[0];
+ c.left.xeqit(n, 0);
+ n = nil;
+ e = e.clone();
+ e.in = fds[1];
+ fds = nil;
+ c = c.right;
+ } while (c.op == Cpipe);
+ continue tail;
+ Cif =>
+ t := c.left.truth(e);
+ if (c.right.op == Celse) {
+ if (t)
+ c.right.left.xeqit(e, wait);
+ else
+ c.right.right.xeqit(e, wait);
+ } else if (t)
+ c.right.xeqit(e, wait);
+ Celse =>
+ panic("unexpected else");
+ Cwhile =>
+ while (c.left.truth(e))
+ c.right.xeqit(e, wait);
+ Cfor =>
+ (ok, l) := evalw(c.words, e);
+ if (!ok)
+ return;
+ s := c.item.word.text;
+ c = c.left;
+ while (l != nil) {
+ e.let(s, (hd l) :: nil);
+ c.xeqit(e, 1);
+ l = tl l;
+ }
+ Ccase =>
+ (s1, l1) := c.left.eeval(e);
+ r := c.right;
+ while (r != nil) {
+ l := r.left;
+ (s2, l2) := l.left.eeval(e);
+ if (match2(e, s1, l1, s2, l2)) {
+ c = l.right;
+ continue tail;
+ }
+ r = r.right;
+ }
+ Ceq =>
+ c.assign(e, 0);
+ Cdefeq =>
+ c.assign(e, 1);
+ Cfn =>
+ (s, nil, nil) := c.item.ieval(e);
+ if (!ident(s)) {
+ e.report("bad function name");
+ return;
+ }
+ e.define(s, c.left);
+ Crescue =>
+ e.report("rescue not implemented");
+ Cdepend =>
+ c.depend(e);
+ Crule =>
+ c.rule(e);
+ * =>
+ sys->print("number %d\n", c.op);
+ } return; } # tail recursion
+}
+
+#
+# Execute quote or backquote generator. Return generated item.
+#
+Cmd.quote(c: self ref Cmd, e: ref Env, back: int): ref Item
+{
+ e = e.copy();
+ fds := e.pipe();
+ if (fds == nil)
+ return nil;
+ e.out = fds[0];
+ in := bufio->fopen(fds[1], Bufio->OREAD);
+ if (in == nil)
+ e.couldnot("fopen", "pipe");
+ c.xeqit(e, 0);
+ fds = nil;
+ e = nil;
+ if (back) {
+ l: list of string;
+ while ((s := in.gets('\n')) != nil) {
+ (nil, r) := sys->tokenize(s, " \t\r\n");
+ l = prepend(l, r);
+ }
+ return Item.iteml(revstrs(l));
+ } else {
+ s := in.gets('\n');
+ if (s != nil && s[len s - 1] == '\n')
+ s = s[:len s - 1];
+ return Item.itemw(s);
+ }
+}
+
+#
+# Execute serve generator.
+#
+Cmd.serve(c: self ref Cmd, e: ref Env, write: int): ref Item
+{
+ e = e.clone();
+ fds := e.pipe();
+ if (fds == nil)
+ return nil;
+ if (write)
+ e.in = fds[0];
+ else
+ e.out = fds[0];
+ s := e.servefd(fds[1], write);
+ if (s == nil)
+ return nil;
+ c.xeqit(e, 0);
+ return Item.itemw(s);
+}
+
+#
+# Expression evaluation, first pass.
+# Parse tree is copied and word items are evaluated.
+# nil return for error is propagated.
+#
+Cmd.eeval1(c: self ref Cmd, e: ref Env): ref Cmd
+{
+ case c.op {
+ Cword =>
+ l := c.item.ieval1(e);
+ if (l == nil)
+ return nil;
+ return Cmd.cmd1i(Cword, nil, l);
+ Chd or Ctl or Clen or Cnot =>
+ l := c.left.eeval1(e);
+ if (l == nil)
+ return nil;
+ return Cmd.cmd1(c.op, l);
+ Ccaret or Ccons or Ceqeq or Cnoteq or Cmatch =>
+ l := c.left.eeval1(e);
+ r := c.right.eeval1(e);
+ if (l == nil || r == nil)
+ return nil;
+ return Cmd.cmd2(c.op, l, r);
+ }
+ panic("expr1: bad op");
+ return nil;
+}
+
+#
+# Expression evaluation, second pass.
+# Returns a tuple (singleton, list, expand flag).
+#
+Cmd.eeval2(c: self ref Cmd, e: ref Env): (string, list of string, int)
+{
+ case c.op {
+ Cword =>
+ return c.item.ieval2(e);
+ Clist =>
+ return (nil, c.value, 0);
+ Ccaret =>
+ (s1, l1, x1) := c.left.eeval2(e);
+ (s2, l2, x2) := c.right.eeval2(e);
+ return caret(s1, l1, x1, s2, l2, x2);
+ Chd =>
+ (s, l, x) := c.left.eeval2(e);
+ if (s != nil)
+ return (s, nil, x);
+ if (l != nil)
+ return (hd l, nil, 0);
+ Ctl =>
+ (s, l, nil) := c.left.eeval2(e);
+ if (s != nil)
+ break;
+ if (l != nil)
+ return (nil, tl l, 0);
+ Clen =>
+ (s, l, nil) := c.left.eeval2(e);
+ if (s != nil)
+ return ("1", nil, 0);
+ return (string len l, nil, 0);
+ Cnot =>
+ (s, l, nil) := c.left.eeval2(e);
+ if (s == nil && l == nil)
+ return (TRUE, nil, 0);
+ Ccons =>
+ (s1, l1, nil) := c.left.eeval2(e);
+ (s2, l2, nil) := c.right.eeval2(e);
+ if (s1 != nil) {
+ if (s2 != nil)
+ return (nil, s1 :: s2 :: nil, 0);
+ if (l2 != nil)
+ return (nil, s1 :: l2, 0);
+ return (s1, nil, 0);
+ } else if (l1 != nil) {
+ if (s2 != nil)
+ return (nil, prepend(s2 :: nil, revstrs(l1)), 0);
+ if (l2 != nil)
+ return (nil, prepend(l2, revstrs(l1)), 0);
+ return (nil, l1, 0);
+ } else
+ return (s2, l2, 0);
+ Ceqeq =>
+ if (c.evaleq(e))
+ return (TRUE, nil, 0);
+ Cnoteq =>
+ if (!c.evaleq(e))
+ return (TRUE, nil, 0);
+ Cmatch =>
+ if (c.evalmatch(e))
+ return (TRUE, nil, 0);
+ * =>
+ panic("expr2: bad op");
+ }
+ return (nil, nil, 0);
+}
+
+#
+# Evaluate expression. 1st pass, 2nd pass, maybe glob.
+#
+Cmd.eeval(c: self ref Cmd, e: ref Env): (string, list of string)
+{
+ c = c.eeval1(e);
+ if (c == nil)
+ return (nil, nil);
+ (s, l, x) := c.eeval2(e);
+ if (x && s != nil)
+ (s, l) = glob(e, s);
+ return (s, l);
+}
+
+#
+# Assignment - let or set.
+#
+Cmd.assign(c: self ref Cmd, e: ref Env, def: int)
+{
+ i := c.item;
+ if (i == nil)
+ return;
+ (ok, v) := evalw(c.words, e);
+ if (!ok)
+ return;
+ s := c.item.word.text;
+ if (def)
+ e.let(s, v);
+ else
+ e.set(s, v);
+}
+
+#
+# Evaluate command and test for non-empty.
+#
+Cmd.truth(c: self ref Cmd, e: ref Env): int
+{
+ (s, l) := c.eeval(e);
+ return s != nil || l != nil;
+}
+
+#
+# Evaluate word.
+#
+evalw(l: list of ref Item, e: ref Env): (int, list of string)
+{
+ if (l == nil)
+ return (1, nil);
+ w := pass1(e, l);
+ if (w == nil)
+ return (0, nil);
+ return (1, pass2(e, w));
+}
+
+#
+# Evaluate list of items, pass 1 - reverses.
+#
+pass1(e: ref Env, l: list of ref Item): list of ref Item
+{
+ r: list of ref Item;
+ while (l != nil) {
+ i := (hd l).ieval1(e);
+ if (i == nil)
+ return nil;
+ r = i :: r;
+ l = tl l;
+ }
+ return r;
+}
+
+#
+# Evaluate list of items, pass 2 with globbing - reverses (restores order).
+#
+pass2(e: ref Env, l: list of ref Item): list of string
+{
+ r: list of string;
+ while (l != nil) {
+ (s, t, x) := (hd l).ieval2(e);
+ if (x && s != nil)
+ (s, t) = glob(e, s);
+ if (s != nil)
+ r = s :: r;
+ else if (t != nil)
+ r = prepend(r, revstrs(t));
+ l = tl l;
+ }
+ return r;
+}
+
+#
+# Simple command. Maybe a function.
+#
+Cmd.simple(c: self ref Cmd, e: ref Env, wait: int)
+{
+ w := pass1(e, c.words);
+ if (w == nil)
+ return;
+ s := pass2(e, w);
+ if (s == nil)
+ return;
+ if (e.flags & EEcho)
+ echo(e, s);
+ (ok, in, out) := mkredirs(e, c.redirs);
+ if (ok)
+ e.runit(s, in, out, wait);
+}
+
+#
+# Cmd name and arglist. Maybe a function.
+#
+Env.runit(e: self ref Env, s: list of string, in, out: ref Sys->FD, wait: int)
+{
+ d := e.func(hd s);
+ if (d != nil) {
+ if (e.level >= MAXELEV) {
+ e.report(hd s + ": function nesting too deep");
+ return;
+ }
+ e = e.copy();
+ e.level++;
+ e.in = in;
+ e.out = out;
+ e.local = Stab.new();
+ e.local.assign(ARGS, tl s);
+ d.xeqit(e, wait);
+ } else
+ exec(s, e, in, out, wait);
+}
+
+#
+# Item evaluation, first pass. Copy parse tree. Expand variables.
+# Call first pass of expression evaluation. Execute generators.
+#
+Item.ieval1(i: self ref Item, e: ref Env): ref Item
+{
+ if (i == nil)
+ return nil;
+ case i.op {
+ Icaret or Iicaret =>
+ l := i.left.ieval1(e);
+ r := i.right.ieval1(e);
+ if (l == nil || r == nil)
+ return nil;
+ return Item.item2(i.op, l, r);
+ Idollar or Idollarq=>
+ s := e.dollar(i.word.text);
+ if (s == nil) {
+ e.undefined(i.word.text);
+ return nil;
+ }
+ if (s.value == empty)
+ return Item.itemw(nil);
+ if (i.op == Idollar)
+ return Item.iteml(s.value);
+ else
+ return Item.itemw(concat(s.value));
+ Iword or Imatch =>
+ return i;
+ Iexpr =>
+ l := i.cmd.eeval1(e);
+ if (l == nil)
+ return nil;
+ return Item.itemc(Iexpr, l);
+ Ibackq =>
+ return i.cmd.quote(e, 1);
+ Iquote =>
+ return i.cmd.quote(e, 0);
+ Iinpipe =>
+ return i.cmd.serve(e, 0);
+ Ioutpipe =>
+ return i.cmd.serve(e, 1);
+ }
+ panic("ieval1: bad op");
+ return nil;
+}
+
+#
+# Item evaluation, second pass. Outer level carets. Expand matches.
+# Call second pass of expression evaluation.
+#
+Item.ieval2(i: self ref Item, e: ref Env): (string, list of string, int)
+{
+ case i.op {
+ Icaret or Iicaret =>
+ return i.caret(e);
+ Imatch =>
+ return (e.arg(i.word.text), nil, 0);
+ Idollar or Idollarq =>
+ panic("ieval2: unexpected $");
+ Iword =>
+ return (i.word.text, nil, i.word.flags & Wexpand);
+ Iexpr =>
+ return i.cmd.eeval2(e);
+ Ibackq or Iinpipe or Ioutpipe =>
+ panic("ieval2: unexpected generator");
+ }
+ panic("ieval2: bad op");
+ return (nil, nil, 0);
+}
+
+#
+# Item evaluation.
+#
+Item.ieval(i: self ref Item, e: ref Env): (string, list of string, int)
+{
+ i = i.ieval1(e);
+ if (i == nil)
+ return (nil, nil, 0);
+ return i.ieval2(e);
+}
+
+#
+# Redirection item evaluation.
+#
+Item.reval(i: self ref Item, e: ref Env): (int, string)
+{
+ (s, l, nil) := i.ieval(e);
+ if (s == nil) {
+ if (l == nil)
+ e.report("null redirect");
+ else
+ e.report("list for redirect");
+ return (0, nil);
+ }
+ return (1, s);
+}
+
+#
+# Make redirection names.
+#
+mkrdnames(e: ref Env, l: list of ref Redir): (int, array of string)
+{
+ f := array[Rcount] of string;
+ while (l != nil) {
+ r := hd l;
+ (ok, s) := r.word.reval(e);
+ if (!ok)
+ return (0, nil);
+ f[r.op] = s;
+ l = tl l;
+ }
+ return (1, f);
+}
+
+#
+# Perform redirections.
+#
+mkredirs(e: ref Env, l: list of ref Redir): (int, ref Sys->FD, ref Sys->FD)
+{
+ (ok, f) := mkrdnames(e, l);
+ if (!ok)
+ return (0, nil, nil);
+ return redirect(e, f, e.in, e.out);
+}