rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit a005c00d82364393252795cb9f13ceb0fa2a0685
parent 5a539887ea0a5d591d66f86aca830b19ac98a2b1
Author: tim <tim>
Date:   Tue, 20 May 1997 14:40:41 +0000

Initial revision

Diffstat:
Awalk.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(); +}