home *** CD-ROM | disk | FTP | other *** search
- /* walk.c: walks the parse tree. */
-
- #include <setjmp.h>
- #include <signal.h>
- #include "rc.h"
- #include "utils.h"
- #include "status.h"
- #include "exec.h"
- #include "walk.h"
- #include "glom.h"
- #include "hash.h"
- #include "glob.h"
- #include "lex.h"
- #include "open.h"
- #include "except.h"
-
- boolean cond = FALSE;
-
- static boolean iscase(Node *);
- static boolean isallpre(Node *);
- static boolean dofork(void);
-
- /* walk the parse-tree. "obvious". */
-
- boolean walk(Node *n, boolean parent) {
- if (n == NULL) {
- if (!parent)
- exit(0);
- set(TRUE);
- return TRUE;
- }
-
- switch (n->type) {
- case ARGS: case BACKQ: case CONCAT: case rCOUNT: case rFLAT:
- case LAPPEND: case rREDIR: case VAR: case VARSUB: case rWORD:
- exec(glob(glom(n)), parent); /* simple command */
- break;
- case BODY:
- walk(n->u[0].p, TRUE);
- walk(n->u[1].p, TRUE);
- break;
- case NOWAIT: {
- int pid;
- char apid[8];
-
- switch (pid = fork()) {
- case -1:
- uerror("fork");
- rc_error(NULL);
- /* NOTREACHED */
- case 0:
- setsigdefaults();
- #ifdef NOJOB
- signal(SIGINT, SIG_IGN); /* traditional backgrounding procedure: ignore SIGINT */
- #else
- signal(SIGTTOU, SIG_IGN); /* Berkeleyized version: put it in a new pgroup. */
- signal(SIGTTIN, SIG_IGN);
- signal(SIGTSTP, SIG_IGN);
- setpgrp(0, getpid());
- #endif
- dup2(rc_open("/dev/null", FROM), 0);
- walk(n->u[0].p, FALSE);
- exit(getstatus());
- /* NOTREACHED */
- default:
- if (interactive)
- fprint(2,"%d\n",pid);
- varassign("apid", word(sprint(apid,"%d",pid), NULL), FALSE);
- redirq = NULL; /* kill pre-redir queue */
- fifoq = NULL;
- }
- break;
- }
- case rANDAND: {
- boolean oldcond = cond;
-
- cond = TRUE;
- if (walk(n->u[0].p, TRUE)) {
- cond = oldcond;
- walk(n->u[1].p, TRUE);
- } else
- cond = oldcond;
- break;
- }
- case rOROR: {
- boolean oldcond = cond;
-
- cond = TRUE;
- if (!walk(n->u[0].p, TRUE)) {
- cond = oldcond;
- walk(n->u[1].p, TRUE);
- } else
- cond = oldcond;
- break;
- }
- case rBANG:
- set(!walk(n->u[0].p, TRUE));
- break;
- case rIF: {
- boolean oldcond = cond;
- Node *true_cmd = n->u[1].p, *false_cmd = NULL;
-
- if (true_cmd != NULL && true_cmd->type == rELSE) {
- 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, TRUE);
- break;
- }
- case rWHILE: {
- jmp_buf j;
- boolean oldcond = cond;
- Estack e1,e2;
-
- cond = TRUE;
-
- if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
- cond = oldcond;
- break;
- }
-
- if (setjmp(j))
- break;
-
- except(BREAK, j, &e1);
- do {
- cond = oldcond;
- except(ARENA, NULL, &e2);
- walk(n->u[1].p, TRUE);
- unexcept(); /* ARENA */
- cond = TRUE;
- } while (walk(n->u[0].p, TRUE));
- cond = oldcond;
- unexcept(); /* BREAK */
- break;
- }
- case FORIN: {
- List *l;
- jmp_buf j;
- Estack e1,e2;
-
- if (setjmp(j))
- break;
-
- except(BREAK, j, &e1);
- for (l = glob(glom(n->u[1].p)); l != NULL; l = l->n) {
- assign(glom(n->u[0].p), word(l->w, NULL), FALSE);
- except(ARENA, NULL, &e2);
- walk(n->u[2].p, TRUE);
- unexcept(); /* ARENA */
- }
- unexcept(); /* BREAK */
- break;
- }
- case rSUBSHELL:
- if (dofork()) {
- setsigdefaults();
- walk(n->u[0].p, TRUE);
- rc_exit(getstatus());
- }
- break;
- case ASSIGN:
- 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 rPIPE: {
- int i, j, k, sp, pid, wpid, fd_prev, fd_out, pids[512], stats[512], p[2];
- void (*handler)(int);
- Node *r;
-
- fd_prev = fd_out = 1;
-
- for (r = n, i = 0; r != NULL && r->type == rPIPE; 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);
- }
-
- switch (pid = fork()) {
- case -1:
- uerror("fork");
- rc_error(NULL);
- /* NOTREACHED */
- case 0:
- setsigdefaults();
- redirq = NULL; /* clear preredir queue */
- fifoq = NULL;
- dup2(p[0],r->u[1].i);
- if (fd_prev != 1) {
- dup2(fd_prev, fd_out);
- close(fd_prev);
- }
- close(p[0]);
- close(p[1]);
- walk(r->u[3].p, FALSE);
- exit(getstatus());
- /* NOTREACHED */
- default:
- 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]);
- }
- }
-
- switch (pid = fork()) {
- case -1:
- uerror("fork");
- rc_error(NULL);
- /* NOTREACHED */
- case 0:
- setsigdefaults();
- dup2(fd_prev, fd_out);
- close(fd_prev);
- walk(r, FALSE);
- exit(getstatus());
- /* NOTREACHED */
- default:
- redirq = NULL; /* clear preredir queue */
- fifoq = NULL;
- close(fd_prev);
- pids[i++] = pid;
-
- /* collect statuses */
-
- if ((handler = signal(SIGINT, SIG_IGN)) == SIG_DFL)
- signal(SIGINT, SIG_DFL); /* don't ignore interrupts in noninteractive mode */
-
- for (k = i; k != 0;) {
- if ((wpid = wait(&sp)) < 0)
- uerror("wait");
- for (j = 0; j < i; j++)
- if (wpid == pids[j]) {
- stats[j] = sp;
- pids[j] = 0;
- --k;
- }
- }
-
- setpipestatus(stats, i);
- signal(SIGINT, handler);
- }
- break;
- }
- case NEWFN: {
- List *l = glom(n->u[0].p);
-
- if (l == NULL)
- rc_error("null function name");
- while (l != NULL) {
- fnassign(l->w, n->u[1].p);
- l = l->n;
- }
- set(TRUE);
- break;
- }
- case RMFN: {
- List *l = glom(n->u[0].p);
-
- while (l != NULL) {
- fnrm(l->w);
- l = l->n;
- }
- set(TRUE);
- break;
- }
- case rDUP:
- break; /* Null command */
- case MATCH:
- set(lmatch(glob(glom(n->u[0].p)), glom(n->u[1].p)));
- break;
- case rSWITCH: {
- List *v = glom(n->u[0].p);
-
- while (1) {
- do {
- n = n->u[1].p;
- if (n == NULL || n->type != BODY)
- return istrue();
- } while (!iscase(n->u[0].p));
- if (lmatch(v, glom(n->u[0].p)->n)) {
- for (n = n->u[1].p; n != NULL && !iscase(n->u[0].p); n = n->u[1].p) {
- if (n->type != BODY) { /* special case at the end of BODY subtree */
- walk(n, TRUE);
- break;
- }
- walk(n->u[0].p, TRUE);
- }
- break;
- }
- }
- break;
- }
- case PRE: {
- List *v;
-
- if (n->u[0].p->type == rREDIR) {
- qredir(n->u[0].p);
- walk(n->u[1].p, TRUE);
- break;
- } else if (n->u[0].p->type == ASSIGN) {
- if (isallpre(n->u[1].p)) {
- walk(n->u[0].p, TRUE);
- walk(n->u[1].p, TRUE);
- break;
- } else {
- v = glom(n->u[0].p->u[0].p);
- assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE);
- walk(n->u[1].p, TRUE);
- varrm(v->w, TRUE);
- }
- } else
- rc_error("walk: node other than assign or redir in PRE. help!");
- break;
- }
- case BRACE:
- if (dofork()) {
- setsigdefaults();
- walk(n->u[1].p, TRUE); /* Do redirections */
- redirq = NULL; /* Reset redirection queue */
- walk(n->u[0].p, TRUE); /* Do commands */
- rc_exit(getstatus());
- /* NOTREACHED */
- }
- break;
- case EPILOG:
- qredir(n->u[0].p);
- if (n->u[1].p != NULL)
- walk(n->u[1].p, TRUE); /* Do more redirections. */
- else
- doredirs(); /* Okay, we hit the bottom. */
- break;
- case NMPIPE:
- rc_error("named pipes cannot be executed as commands");
- /* NOTREACHED */
- default:
- rc_error("walk: unknown node; this can't happen");
- /* NOTREACHED */
- }
- return istrue();
- }
-
- /* checks a "command-line" (in parsetree form) to see if it contains the fake keyword "case". */
-
- static boolean iscase(Node *n) {
- if (n == NULL)
- return FALSE;
-
- switch (n->type) {
- case rWORD:
- return streq(n->u[0].s, "case");
- case ARGS: case LAPPEND:
- return iscase(n->u[0].p);
- default:
- return FALSE;
- }
- }
-
- /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */
-
- static boolean isallpre(Node *n) {
- if (n == NULL)
- return TRUE;
-
- switch (n->type) {
- case PRE:
- return isallpre(n->u[1].p);
- case rREDIR: case ASSIGN: case rDUP:
- return TRUE;
- default:
- return FALSE;
- }
- }
-
- /*
- A code-saver. Forks, child returns (for further processing in walk()), and the parent
- waits for the child to finish, setting $status appropriately.
- */
-
- static boolean dofork() {
- int pid, sp;
-
- switch (pid = fork()) {
- case -1:
- uerror("fork");
- rc_error(NULL);
- /* NOTREACHED */
- case 0:
- return TRUE;
- default:
- redirq = NULL; /* clear out the pre-redirection queue in the parent */
- fifoq = NULL;
- while (pid != wait(&sp))
- if (pid < 0)
- uerror("wait");
- setstatus(sp);
- return FALSE;
- }
- }
-
-