home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume30 / rc / part04 / walk.c < prev   
C/C++ Source or Header  |  1992-05-30  |  8KB  |  345 lines

  1. /* walk.c: walks the parse tree. */
  2.  
  3. #include <signal.h>
  4. #include <setjmp.h>
  5. #include "rc.h"
  6. #include "jbwrap.h"
  7.  
  8. /*
  9.    global which indicates whether rc is executing a test;
  10.    used by rc -e so that if (false) does not exit.
  11. */
  12. bool cond = FALSE;
  13.  
  14. static bool isallpre(Node *);
  15. static bool dofork(bool);
  16. static void dopipe(Node *);
  17.  
  18. /* Tail-recursive version of walk() */
  19.  
  20. #define WALK(x, y) { n = x; parent = y; goto top; }
  21.  
  22. /* walk the parse-tree. "obvious". */
  23.  
  24. extern bool walk(Node *n, bool parent) {
  25. top:    SIGCHK;
  26.     if (n == NULL) {
  27.         if (!parent)
  28.             exit(0);
  29.         set(TRUE);
  30.         return TRUE;
  31.     }
  32.     switch (n->type) {
  33.     case nArgs: case nBackq: case nConcat: case nCount:
  34.     case nFlat: case nLappend: case nRedir: case nVar:
  35.     case nVarsub: case nWord: case nQword:
  36.         exec(glob(glom(n)), parent);    /* simple command */
  37.         break;
  38.     case nBody:
  39.         walk(n->u[0].p, TRUE);
  40.         WALK(n->u[1].p, parent);
  41.         /* WALK doesn't fall through */
  42.     case nNowait: {
  43.         int pid;
  44.         if ((pid = rc_fork()) == 0) {
  45. #if !defined(NOJOB) && defined(SIGTTOU) && defined(SIGTTIN) && defined(SIGTSTP)
  46.             setsigdefaults(FALSE);
  47.             rc_signal(SIGTTOU, SIG_IGN);    /* Berkeleyized version: put it in a new pgroup. */
  48.             rc_signal(SIGTTIN, SIG_IGN);
  49.             rc_signal(SIGTSTP, SIG_IGN);
  50.             setpgrp(0, getpid());
  51. #else
  52.             setsigdefaults(TRUE);        /* ignore SIGINT, SIGQUIT, SIGTERM */
  53. #endif
  54.             mvfd(rc_open("/dev/null", rFrom), 0);
  55.             walk(n->u[0].p, FALSE);
  56.             exit(getstatus());
  57.         }
  58.         if (interactive)
  59.             fprint(2, "%d\n", pid);
  60.         varassign("apid", word(nprint("%d", pid), NULL), FALSE);
  61.         redirq = NULL; /* kill pre-redir queue */
  62.         break;
  63.     }
  64.     case nAndalso: {
  65.         bool oldcond = cond;
  66.         cond = TRUE;
  67.         if (walk(n->u[0].p, TRUE)) {
  68.             cond = oldcond;
  69.             WALK(n->u[1].p, parent);
  70.         } else
  71.             cond = oldcond;
  72.         break;
  73.     }
  74.     case nOrelse: {
  75.         bool oldcond = cond;
  76.         cond = TRUE;
  77.         if (!walk(n->u[0].p, TRUE)) {
  78.             cond = oldcond;
  79.             WALK(n->u[1].p, parent);
  80.         } else
  81.             cond = oldcond;
  82.         break;
  83.     }
  84.     case nBang:
  85.         set(!walk(n->u[0].p, TRUE));
  86.         break;
  87.     case nIf: {
  88.         bool oldcond = cond;
  89.         Node *true_cmd = n->u[1].p, *false_cmd = NULL;
  90.         if (true_cmd != NULL && true_cmd->type == nElse) {
  91.             false_cmd = true_cmd->u[1].p;
  92.             true_cmd = true_cmd->u[0].p;
  93.         }
  94.         cond = TRUE;
  95.         if (!walk(n->u[0].p, TRUE))
  96.             true_cmd = false_cmd; /* run the else clause */
  97.         cond = oldcond;
  98.         WALK(true_cmd, parent);
  99.     }
  100.     case nWhile: {
  101.         Jbwrap j;
  102.         Edata jbreak;
  103.         Estack e1, e2;
  104.         bool testtrue, oldcond = cond;
  105.         cond = TRUE;
  106.         if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
  107.             cond = oldcond;
  108.             break;
  109.         }
  110.         if (setjmp(j.j))
  111.             break;
  112.         jbreak.jb = &j;
  113.         except(eBreak, jbreak, &e1);
  114.         do {
  115.             Edata block;
  116.             block.b = newblock();
  117.             cond = oldcond;
  118.             except(eArena, block, &e2);
  119.             walk(n->u[1].p, TRUE);
  120.             testtrue = walk(n->u[0].p, TRUE);
  121.             unexcept(); /* eArena */
  122.             cond = TRUE;
  123.         } while (testtrue);
  124.         cond = oldcond;
  125.         unexcept(); /* eBreak */
  126.         break;
  127.     }
  128.     case nForin: {
  129.         List *l, *var = glom(n->u[0].p);
  130.         Jbwrap j;
  131.         Estack e1, e2;
  132.         Edata jbreak;
  133.         if (setjmp(j.j))
  134.             break;
  135.         jbreak.jb = &j;
  136.         except(eBreak, jbreak, &e1);
  137.         for (l = listcpy(glob(glom(n->u[1].p)), nalloc); l != NULL; l = l->n) {
  138.             Edata block;
  139.             assign(var, word(l->w, NULL), FALSE);
  140.             block.b = newblock();
  141.             except(eArena, block, &e2);
  142.             walk(n->u[2].p, TRUE);
  143.             unexcept(); /* eArena */
  144.         }
  145.         unexcept(); /* eBreak */
  146.         break;
  147.     }
  148.     case nSubshell:
  149.         if (dofork(TRUE)) {
  150.             setsigdefaults(FALSE);
  151.             walk(n->u[0].p, FALSE);
  152.             rc_exit(getstatus());
  153.         }
  154.         break;
  155.     case nAssign:
  156.         if (n->u[0].p == NULL)
  157.             rc_error("null variable name");
  158.         assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE);
  159.         set(TRUE);
  160.         break;
  161.     case nPipe:
  162.         dopipe(n);
  163.         break;
  164.     case nNewfn: {
  165.         List *l = glom(n->u[0].p);
  166.         if (l == NULL)
  167.             rc_error("null function name");
  168.         while (l != NULL) {
  169.             if (dashex)
  170.                 prettyprint_fn(2, l->w, n->u[1].p);
  171.             fnassign(l->w, n->u[1].p);
  172.             l = l->n;
  173.         }
  174.         set(TRUE);
  175.         break;
  176.     }
  177.     case nRmfn: {
  178.         List *l = glom(n->u[0].p);
  179.         while (l != NULL) {
  180.             if (dashex)
  181.                 fprint(2, "fn %S\n", l->w);
  182.             fnrm(l->w);
  183.             l = l->n;
  184.         }
  185.         set(TRUE);
  186.         break;
  187.     }
  188.     case nDup:
  189.         redirq = NULL;
  190.         break; /* Null command */
  191.     case nMatch: {
  192.         List *a = glob(glom(n->u[0].p)), *b = glom(n->u[1].p);
  193.         if (dashex)
  194.             fprint(2, (a != NULL && a->n != NULL) ? "~ (%L) %L\n" : "~ %L %L\n", a, " ", b, " ");
  195.         set(lmatch(a, b));
  196.         break;
  197.     }
  198.     case nSwitch: {
  199.         List *v = glom(n->u[0].p);
  200.         while (1) {
  201.             do {
  202.                 n = n->u[1].p;
  203.                 if (n == NULL)
  204.                     return istrue();
  205.             } while (n->u[0].p == NULL || n->u[0].p->type != nCase);
  206.             if (lmatch(v, glom(n->u[0].p->u[0].p))) {
  207.                 for (n = n->u[1].p; n != NULL && (n->u[0].p == NULL || n->u[0].p->type != nCase); n = n->u[1].p)
  208.                     walk(n->u[0].p, TRUE);
  209.                 break;
  210.             }
  211.         }
  212.         break;
  213.     }
  214.     case nPre: {
  215.         List *v;
  216.         if (n->u[0].p->type == nRedir || n->u[0].p->type == nDup) {
  217.             qredir(n->u[0].p);
  218.             walk(n->u[1].p, parent);
  219.         } else if (n->u[0].p->type == nAssign) {
  220.             if (isallpre(n->u[1].p)) {
  221.                 walk(n->u[0].p, TRUE);
  222.                 WALK(n->u[1].p, parent);
  223.             } else {
  224.                 Estack e;
  225.                 Edata var;
  226.                 v = glom(n->u[0].p->u[0].p);
  227.                 assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE);
  228.                 var.name = v->w;
  229.                 except(eVarstack, var, &e);
  230.                 walk(n->u[1].p, parent);
  231.                 varrm(v->w, TRUE);
  232.                 unexcept(); /* eVarstack */
  233.             }
  234.         } else
  235.             panic("unexpected node in preredir section of walk");
  236.         break;
  237.     }
  238.     case nBrace:
  239.         if (n->u[1].p == NULL) {
  240.             WALK(n->u[0].p, parent);
  241.         } else if (dofork(parent)) {
  242.             setsigdefaults(FALSE);
  243.             walk(n->u[1].p, TRUE); /* Do redirections */
  244.             redirq = NULL;   /* Reset redirection queue */
  245.             walk(n->u[0].p, FALSE); /* Do commands */
  246.             rc_exit(getstatus());
  247.             /* NOTREACHED */
  248.         }
  249.         break;
  250.     case nEpilog:
  251.         qredir(n->u[0].p);
  252.         if (n->u[1].p != NULL) {
  253.             WALK(n->u[1].p, parent); /* Do more redirections. */
  254.         } else {
  255.             doredirs();    /* Okay, we hit the bottom. */
  256.         }
  257.         break;
  258.     case nNmpipe:
  259.         rc_error("named pipes cannot be executed as commands");
  260.         /* NOTREACHED */
  261.     default:
  262.         panic("unknown node in walk");
  263.         /* NOTREACHED */
  264.     }
  265.     return istrue();
  266. }
  267.  
  268. /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */
  269.  
  270. static bool isallpre(Node *n) {
  271.     while (n != NULL && n->type == nPre)
  272.         n = n->u[1].p;
  273.     return n == NULL || n->type == nRedir || n->type == nAssign || n->type == nDup;
  274. }
  275.  
  276. /*
  277.    A code-saver. Forks, child returns (for further processing in walk()), and the parent
  278.    waits for the child to finish, setting $status appropriately.
  279. */
  280.  
  281. static bool dofork(bool parent) {
  282.     int pid, sp;
  283.  
  284.     if (!parent || (pid = rc_fork()) == 0)
  285.         return TRUE;
  286.     redirq = NULL; /* clear out the pre-redirection queue in the parent */
  287.     rc_wait4(pid, &sp, TRUE);
  288.     setstatus(-1, sp);
  289.     SIGCHK;
  290.     return FALSE;
  291. }
  292.  
  293. static void dopipe(Node *n) {
  294.     int i, j, sp, pid, fd_prev, fd_out, pids[512], stats[512], p[2];
  295.     bool intr;
  296.     Node *r;
  297.  
  298.     fd_prev = fd_out = 1;
  299.     for (r = n, i = 0; r != NULL && r->type == nPipe; r = r->u[2].p, i++) {
  300.         if (i > 500) /* the only hard-wired limit in rc? */
  301.             rc_error("pipe too long");
  302.         if (pipe(p) < 0) {
  303.             uerror("pipe");
  304.             rc_error(NULL);
  305.         }
  306.         if ((pid = rc_fork()) == 0) {
  307.             setsigdefaults(FALSE);
  308.             redirq = NULL; /* clear preredir queue */
  309.             mvfd(p[0], r->u[1].i);
  310.             if (fd_prev != 1)
  311.                 mvfd(fd_prev, fd_out);
  312.             close(p[1]);
  313.             walk(r->u[3].p, FALSE);
  314.             exit(getstatus());
  315.         }
  316.         if (fd_prev != 1)
  317.             close(fd_prev); /* parent must close all pipe fd's */
  318.         pids[i] = pid;
  319.         fd_prev = p[1];
  320.         fd_out = r->u[0].i;
  321.         close(p[0]);
  322.     }
  323.     if ((pid = rc_fork()) == 0) {
  324.         setsigdefaults(FALSE);
  325.         mvfd(fd_prev, fd_out);
  326.         walk(r, FALSE);
  327.         exit(getstatus());
  328.         /* NOTREACHED */
  329.     }
  330.     redirq = NULL; /* clear preredir queue */
  331.     close(fd_prev);
  332.     pids[i++] = pid;
  333.  
  334.     /* collect statuses */
  335.  
  336.     intr = FALSE;
  337.     for (j = 0; j < i; j++) {
  338.         rc_wait4(pids[j], &sp, TRUE);
  339.         stats[j] = sp;
  340.         intr |= (sp == SIGINT);
  341.     }
  342.     setpipestatus(stats, i);
  343.     SIGCHK;
  344. }
  345.