commit a005c00d82364393252795cb9f13ceb0fa2a0685
parent 5a539887ea0a5d591d66f86aca830b19ac98a2b1
Author: tim <tim>
Date: Tue, 20 May 1997 14:40:41 +0000
Initial revision
Diffstat:
A | walk.c | | | 358 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 358 insertions(+), 0 deletions(-)
diff --git a/walk.c b/walk.c
@@ -0,0 +1,358 @@
+/* walk.c: walks the parse tree. */
+
+#include <signal.h>
+#include <setjmp.h>
+#include "rc.h"
+#include "jbwrap.h"
+
+/*
+ global which indicates whether rc is executing a test;
+ used by rc -e so that if (false) does not exit.
+*/
+bool cond = FALSE;
+
+static bool haspreredir(Node *);
+static bool isallpre(Node *);
+static bool dofork(bool);
+static void dopipe(Node *);
+
+/* Tail-recursive version of walk() */
+
+#define WALK(x, y) { n = x; parent = y; goto top; }
+
+/* walk the parse-tree. "obvious". */
+
+extern bool walk(Node *n, bool parent) {
+top: sigchk();
+ if (n == NULL) {
+ if (!parent)
+ exit(0);
+ set(TRUE);
+ return TRUE;
+ }
+ switch (n->type) {
+ case nArgs: case nBackq: case nConcat: case nCount:
+ case nFlat: case nLappend: case nRedir: case nVar:
+ case nVarsub: case nWord: case nQword:
+ exec(glob(glom(n)), parent); /* simple command */
+ break;
+ case nBody:
+ walk(n->u[0].p, TRUE);
+ WALK(n->u[1].p, parent);
+ /* WALK doesn't fall through */
+ case nNowait: {
+ int pid;
+ if ((pid = rc_fork()) == 0) {
+#if !defined(NOJOB) && defined(SIGTTOU) && defined(SIGTTIN) && defined(SIGTSTP)
+ setsigdefaults(FALSE);
+ rc_signal(SIGTTOU, SIG_IGN); /* Berkeleyized version: put it in a new pgroup. */
+ rc_signal(SIGTTIN, SIG_IGN);
+ rc_signal(SIGTSTP, SIG_IGN);
+ setpgrp(0, getpid());
+#else
+ setsigdefaults(TRUE); /* ignore SIGINT, SIGQUIT, SIGTERM */
+#endif
+ mvfd(rc_open("/dev/null", rFrom), 0);
+ walk(n->u[0].p, FALSE);
+ exit(getstatus());
+ }
+ if (interactive)
+ fprint(2, "%d\n", pid);
+ varassign("apid", word(nprint("%d", pid), NULL), FALSE);
+ redirq = NULL; /* kill pre-redir queue */
+ break;
+ }
+ case nAndalso: {
+ bool oldcond = cond;
+ cond = TRUE;
+ if (walk(n->u[0].p, TRUE)) {
+ cond = oldcond;
+ WALK(n->u[1].p, parent);
+ } else
+ cond = oldcond;
+ break;
+ }
+ case nOrelse: {
+ bool oldcond = cond;
+ cond = TRUE;
+ if (!walk(n->u[0].p, TRUE)) {
+ cond = oldcond;
+ WALK(n->u[1].p, parent);
+ } else
+ cond = oldcond;
+ break;
+ }
+ case nBang:
+ set(!walk(n->u[0].p, TRUE));
+ break;
+ case nIf: {
+ bool oldcond = cond;
+ Node *true_cmd = n->u[1].p, *false_cmd = NULL;
+ if (true_cmd != NULL && true_cmd->type == nElse) {
+ false_cmd = true_cmd->u[1].p;
+ true_cmd = true_cmd->u[0].p;
+ }
+ cond = TRUE;
+ if (!walk(n->u[0].p, TRUE))
+ true_cmd = false_cmd; /* run the else clause */
+ cond = oldcond;
+ WALK(true_cmd, parent);
+ }
+ case nWhile: {
+ Jbwrap j;
+ Edata jbreak;
+ Estack e1, e2;
+ bool testtrue, oldcond = cond;
+ cond = TRUE;
+ if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
+ cond = oldcond;
+ break;
+ }
+ if (setjmp(j.j))
+ break;
+ jbreak.jb = &j;
+ except(eBreak, jbreak, &e1);
+ do {
+ Edata block;
+ block.b = newblock();
+ cond = oldcond;
+ except(eArena, block, &e2);
+ walk(n->u[1].p, TRUE);
+ testtrue = walk(n->u[0].p, TRUE);
+ unexcept(); /* eArena */
+ cond = TRUE;
+ } while (testtrue);
+ cond = oldcond;
+ unexcept(); /* eBreak */
+ break;
+ }
+ case nForin: {
+ List *l, *var = glom(n->u[0].p);
+ Jbwrap j;
+ Estack e1, e2;
+ Edata jbreak;
+ if (setjmp(j.j))
+ break;
+ jbreak.jb = &j;
+ except(eBreak, jbreak, &e1);
+ for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) {
+ Edata block;
+ assign(var, word(l->w, NULL), FALSE);
+ block.b = newblock();
+ except(eArena, block, &e2);
+ walk(n->u[2].p, TRUE);
+ unexcept(); /* eArena */
+ }
+ unexcept(); /* eBreak */
+ break;
+ }
+ case nSubshell:
+ if (dofork(TRUE)) {
+ walk(n->u[0].p, FALSE);
+ rc_exit(getstatus());
+ }
+ break;
+ case nAssign:
+ if (n->u[0].p == NULL)
+ rc_error("null variable name");
+ assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE);
+ set(TRUE);
+ break;
+ case nPipe:
+ dopipe(n);
+ break;
+ case nNewfn: {
+ List *l = glom(n->u[0].p);
+ if (l == NULL)
+ rc_error("null function name");
+ while (l != NULL) {
+ if (dashex)
+ prettyprint_fn(2, l->w, n->u[1].p);
+ fnassign(l->w, n->u[1].p);
+ l = l->n;
+ }
+ set(TRUE);
+ break;
+ }
+ case nRmfn: {
+ List *l = glom(n->u[0].p);
+ while (l != NULL) {
+ if (dashex)
+ fprint(2, "fn %S\n", l->w);
+ fnrm(l->w);
+ l = l->n;
+ }
+ set(TRUE);
+ break;
+ }
+ case nDup:
+ redirq = NULL;
+ break; /* Null command */
+ case nMatch: {
+ List *a = glob(glom(n->u[0].p)), *b = glom(n->u[1].p);
+ if (dashex)
+ fprint(2, (a != NULL && a->n != NULL) ? "~ (%L) %L\n" : "~ %L %L\n", a, " ", b, " ");
+ set(lmatch(a, b));
+ break;
+ }
+ case nSwitch: {
+ List *v = glom(n->u[0].p);
+ while (1) {
+ do {
+ n = n->u[1].p;
+ if (n == NULL)
+ return istrue();
+ } while (n->u[0].p == NULL || n->u[0].p->type != nCase);
+ if (lmatch(v, glom(n->u[0].p->u[0].p))) {
+ for (n = n->u[1].p; n != NULL && (n->u[0].p == NULL || n->u[0].p->type != nCase); n = n->u[1].p)
+ walk(n->u[0].p, TRUE);
+ break;
+ }
+ }
+ break;
+ }
+ case nPre: {
+ List *v;
+ if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) {
+ if (redirq == NULL && !dofork(parent)) /* subshell on first preredir */
+ break;
+ qredir(n->u[0].p);
+ if (!haspreredir(n->u[1].p))
+ doredirs(); /* no more preredirs, empty queue */
+ walk(n->u[1].p, FALSE);
+ rc_exit(getstatus());
+ /* NOTREACHED */
+ } else if (n->u[0].p->type == nAssign) {
+ if (isallpre(n->u[1].p)) {
+ walk(n->u[0].p, TRUE);
+ WALK(n->u[1].p, parent);
+ } else {
+ Estack e;
+ Edata var;
+ v = glom(n->u[0].p->u[0].p);
+ assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE);
+ var.name = v->w;
+ except(eVarstack, var, &e);
+ walk(n->u[1].p, parent);
+ varrm(v->w, TRUE);
+ unexcept(); /* eVarstack */
+ }
+ } else
+ panic("unexpected node in preredir section of walk");
+ break;
+ }
+ case nBrace:
+ if (n->u[1].p == NULL) {
+ WALK(n->u[0].p, parent);
+ } else if (dofork(parent)) {
+ walk(n->u[1].p, TRUE); /* Do redirections */
+ redirq = NULL; /* Reset redirection queue */
+ walk(n->u[0].p, FALSE); /* Do commands */
+ rc_exit(getstatus());
+ /* NOTREACHED */
+ }
+ break;
+ case nEpilog:
+ qredir(n->u[0].p);
+ if (n->u[1].p != NULL) {
+ WALK(n->u[1].p, parent); /* Do more redirections. */
+ } else {
+ doredirs(); /* Okay, we hit the bottom. */
+ }
+ break;
+ case nNmpipe:
+ rc_error("named pipes cannot be executed as commands");
+ /* NOTREACHED */
+ default:
+ panic("unknown node in walk");
+ /* NOTREACHED */
+ }
+ return istrue();
+}
+
+/* checks to see whether there are any pre-redirections left in the tree */
+
+static bool haspreredir(Node *n) {
+ while (n != NULL && n->type == nPre) {
+ if (n->u[0].p->type == nDup || n->u[0].p->type == nRedir)
+ return TRUE;
+ n = n->u[1].p;
+ }
+ return FALSE;
+}
+
+/* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */
+
+static bool isallpre(Node *n) {
+ while (n != NULL && n->type == nPre)
+ n = n->u[1].p;
+ return n == NULL || n->type == nRedir || n->type == nAssign || n->type == nDup;
+}
+
+/*
+ A code-saver. Forks, child returns (for further processing in walk()), and the parent
+ waits for the child to finish, setting $status appropriately.
+*/
+
+static bool dofork(bool parent) {
+ int pid, sp;
+
+ if (!parent || (pid = rc_fork()) == 0)
+ return TRUE;
+ redirq = NULL; /* clear out the pre-redirection queue in the parent */
+ rc_wait4(pid, &sp, TRUE);
+ setstatus(-1, sp);
+ sigchk();
+ return FALSE;
+}
+
+static void dopipe(Node *n) {
+ int i, j, sp, pid, fd_prev, fd_out, pids[512], stats[512], p[2];
+ bool intr;
+ Node *r;
+
+ fd_prev = fd_out = 1;
+ for (r = n, i = 0; r != NULL && r->type == nPipe; r = r->u[2].p, i++) {
+ if (i > 500) /* the only hard-wired limit in rc? */
+ rc_error("pipe too long");
+ if (pipe(p) < 0) {
+ uerror("pipe");
+ rc_error(NULL);
+ }
+ if ((pid = rc_fork()) == 0) {
+ redirq = NULL; /* clear preredir queue */
+ mvfd(p[0], r->u[1].i);
+ if (fd_prev != 1)
+ mvfd(fd_prev, fd_out);
+ close(p[1]);
+ walk(r->u[3].p, FALSE);
+ exit(getstatus());
+ }
+ if (fd_prev != 1)
+ close(fd_prev); /* parent must close all pipe fd's */
+ pids[i] = pid;
+ fd_prev = p[1];
+ fd_out = r->u[0].i;
+ close(p[0]);
+ }
+ if ((pid = rc_fork()) == 0) {
+ mvfd(fd_prev, fd_out);
+ walk(r, FALSE);
+ exit(getstatus());
+ /* NOTREACHED */
+ }
+ redirq = NULL; /* clear preredir queue */
+ close(fd_prev);
+ pids[i++] = pid;
+
+ /* collect statuses */
+
+ intr = FALSE;
+ for (j = 0; j < i; j++) {
+ rc_wait4(pids[j], &sp, TRUE);
+ stats[j] = sp;
+ intr |= (sp == SIGINT);
+ }
+ setpipestatus(stats, i);
+ sigchk();
+}