home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume20 / rc / part02 / walk.c < prev   
Encoding:
C/C++ Source or Header  |  1991-05-22  |  8.2 KB  |  412 lines

  1. /* walk.c: walks the parse tree. */
  2.  
  3. #include <setjmp.h>
  4. #include <signal.h>
  5. #include "rc.h"
  6. #include "utils.h"
  7. #include "status.h"
  8. #include "exec.h"
  9. #include "walk.h"
  10. #include "glom.h"
  11. #include "hash.h"
  12. #include "glob.h"
  13. #include "lex.h"
  14. #include "open.h"
  15. #include "except.h"
  16.  
  17. boolean cond = FALSE;
  18.  
  19. static boolean iscase(Node *);
  20. static boolean isallpre(Node *);
  21. static boolean dofork(void);
  22.  
  23. /* walk the parse-tree. "obvious". */
  24.  
  25. boolean walk(Node *n, boolean parent) {
  26.     if (n == NULL) {
  27.         if (!parent)
  28.             exit(0);
  29.         set(TRUE);
  30.         return TRUE;
  31.     }
  32.  
  33.     switch (n->type) {
  34.     case ARGS: case BACKQ: case CONCAT: case rCOUNT: case rFLAT:
  35.     case LAPPEND: case rREDIR: case VAR: case VARSUB: case rWORD:
  36.         exec(glob(glom(n)), parent);    /* simple command */
  37.         break;
  38.     case BODY:
  39.         walk(n->u[0].p, TRUE);
  40.         walk(n->u[1].p, TRUE);
  41.         break;
  42.     case NOWAIT: {
  43.         int pid;
  44.         char apid[8];
  45.  
  46.         switch (pid = fork()) {
  47.         case -1:
  48.             uerror("fork");
  49.             rc_error(NULL);
  50.             /* NOTREACHED */
  51.         case 0:
  52.             setsigdefaults();
  53. #ifdef NOJOB
  54.             signal(SIGINT, SIG_IGN);    /* traditional backgrounding procedure: ignore SIGINT */
  55. #else
  56.             signal(SIGTTOU, SIG_IGN);    /* Berkeleyized version: put it in a new pgroup. */
  57.             signal(SIGTTIN, SIG_IGN);
  58.             signal(SIGTSTP, SIG_IGN);
  59.             setpgrp(0, getpid());
  60. #endif
  61.             dup2(rc_open("/dev/null", FROM), 0);
  62.             walk(n->u[0].p, FALSE);
  63.             exit(getstatus());
  64.             /* NOTREACHED */
  65.         default:
  66.             if (interactive)
  67.                 fprint(2,"%d\n",pid);
  68.             varassign("apid", word(sprint(apid,"%d",pid), NULL), FALSE);
  69.             redirq = NULL; /* kill pre-redir queue */
  70.             fifoq = NULL;
  71.         }
  72.         break;
  73.     }
  74.     case rANDAND: {
  75.         boolean oldcond = cond;
  76.  
  77.         cond = TRUE;
  78.         if (walk(n->u[0].p, TRUE)) {
  79.             cond = oldcond;
  80.             walk(n->u[1].p, TRUE);
  81.         } else
  82.             cond = oldcond;
  83.         break;
  84.     }
  85.     case rOROR: {
  86.         boolean oldcond = cond;
  87.  
  88.         cond = TRUE;
  89.         if (!walk(n->u[0].p, TRUE)) {
  90.             cond = oldcond;
  91.             walk(n->u[1].p, TRUE);
  92.         } else
  93.             cond = oldcond;
  94.         break;
  95.     }
  96.     case rBANG:
  97.         set(!walk(n->u[0].p, TRUE));
  98.         break;
  99.     case rIF: {
  100.         boolean oldcond = cond;
  101.         Node *true_cmd = n->u[1].p, *false_cmd = NULL;
  102.  
  103.         if (true_cmd != NULL && true_cmd->type == rELSE) {
  104.             false_cmd = true_cmd->u[1].p;
  105.             true_cmd = true_cmd->u[0].p;
  106.         }
  107.         cond = TRUE;
  108.         if (!walk(n->u[0].p, TRUE))
  109.             true_cmd = false_cmd; /* run the else clause */
  110.         cond = oldcond;
  111.         walk(true_cmd, TRUE);
  112.         break;
  113.     }
  114.     case rWHILE: {
  115.         jmp_buf j;
  116.         boolean oldcond = cond;
  117.         Estack e1,e2;
  118.  
  119.         cond = TRUE;
  120.  
  121.         if (!walk(n->u[0].p, TRUE)) { /* prevent spurious breaks inside test */
  122.             cond = oldcond;
  123.             break;
  124.         }
  125.  
  126.         if (setjmp(j))
  127.             break;
  128.  
  129.         except(BREAK, j, &e1);
  130.         do {
  131.             cond = oldcond;
  132.             except(ARENA, NULL, &e2);
  133.             walk(n->u[1].p, TRUE);
  134.             unexcept(); /* ARENA */
  135.             cond = TRUE;
  136.         } while (walk(n->u[0].p, TRUE));
  137.         cond = oldcond;
  138.         unexcept(); /* BREAK */
  139.         break;
  140.     }
  141.     case FORIN: {
  142.         List *l;
  143.         jmp_buf j;
  144.         Estack e1,e2;
  145.  
  146.         if (setjmp(j))
  147.             break;
  148.  
  149.         except(BREAK, j, &e1);
  150.         for (l = glob(glom(n->u[1].p)); l != NULL; l = l->n) {
  151.             assign(glom(n->u[0].p), word(l->w, NULL), FALSE);
  152.             except(ARENA, NULL, &e2);
  153.             walk(n->u[2].p, TRUE);
  154.             unexcept(); /* ARENA */
  155.         }
  156.         unexcept(); /* BREAK */
  157.         break;
  158.     }
  159.     case rSUBSHELL:
  160.         if (dofork()) {
  161.             setsigdefaults();
  162.             walk(n->u[0].p, TRUE);
  163.             rc_exit(getstatus());
  164.         }
  165.         break;
  166.     case ASSIGN:
  167.         if (n->u[0].p == NULL)
  168.             rc_error("null variable name");
  169.         assign(glom(n->u[0].p), glob(glom(n->u[1].p)), FALSE);
  170.         set(TRUE);
  171.         break;
  172.     case rPIPE: {
  173.         int i, j, k, sp, pid, wpid, fd_prev, fd_out, pids[512], stats[512], p[2];
  174.         void (*handler)(int);
  175.         Node *r;
  176.  
  177.         fd_prev = fd_out = 1;
  178.  
  179.         for (r = n, i = 0; r != NULL && r->type == rPIPE; r = r->u[2].p, i++) {
  180.             if (i > 500)    /* the only hard-wired limit in rc? */
  181.                 rc_error("pipe too long");
  182.  
  183.             if (pipe(p) < 0) {
  184.                 uerror("pipe");
  185.                 rc_error(NULL);
  186.             }
  187.  
  188.             switch (pid = fork()) {
  189.             case -1:
  190.                 uerror("fork");
  191.                 rc_error(NULL);
  192.                 /* NOTREACHED */
  193.             case 0:
  194.                 setsigdefaults();
  195.                 redirq = NULL; /* clear preredir queue */
  196.                 fifoq = NULL;
  197.                 dup2(p[0],r->u[1].i);
  198.                 if (fd_prev != 1) {
  199.                     dup2(fd_prev, fd_out);
  200.                     close(fd_prev);
  201.                 }
  202.                 close(p[0]);
  203.                 close(p[1]);
  204.                 walk(r->u[3].p, FALSE);
  205.                 exit(getstatus());
  206.                 /* NOTREACHED */
  207.             default:
  208.                 if (fd_prev != 1)
  209.                     close(fd_prev); /* parent must close all pipe fd's */
  210.                 pids[i] = pid;
  211.                 fd_prev = p[1];
  212.                 fd_out = r->u[0].i;
  213.                 close(p[0]);
  214.             }
  215.         }
  216.  
  217.         switch (pid = fork()) {
  218.         case -1:
  219.             uerror("fork");
  220.             rc_error(NULL);
  221.             /* NOTREACHED */
  222.         case 0:
  223.             setsigdefaults();
  224.             dup2(fd_prev, fd_out);
  225.             close(fd_prev);
  226.             walk(r, FALSE);
  227.             exit(getstatus());
  228.             /* NOTREACHED */
  229.         default:
  230.             redirq = NULL; /* clear preredir queue */
  231.             fifoq = NULL;
  232.             close(fd_prev);
  233.             pids[i++] = pid;
  234.  
  235.             /* collect statuses */
  236.  
  237.             if ((handler = signal(SIGINT, SIG_IGN)) == SIG_DFL)
  238.                 signal(SIGINT, SIG_DFL); /* don't ignore interrupts in noninteractive mode */
  239.  
  240.             for (k = i; k != 0;) {
  241.                 if ((wpid = wait(&sp)) < 0)
  242.                     uerror("wait");
  243.                 for (j = 0; j < i; j++)
  244.                     if (wpid == pids[j]) {
  245.                         stats[j] = sp;
  246.                         pids[j] = 0;
  247.                         --k;
  248.                     }
  249.             }
  250.  
  251.             setpipestatus(stats, i);
  252.             signal(SIGINT, handler);
  253.         }
  254.         break;
  255.     }
  256.     case NEWFN: {
  257.         List *l = glom(n->u[0].p);
  258.  
  259.         if (l == NULL)
  260.             rc_error("null function name");
  261.         while (l != NULL) {
  262.             fnassign(l->w, n->u[1].p);
  263.             l = l->n;
  264.         }
  265.         set(TRUE);
  266.         break;
  267.     }
  268.     case RMFN: {
  269.         List *l = glom(n->u[0].p);
  270.  
  271.         while (l != NULL) {
  272.             fnrm(l->w);
  273.             l = l->n;
  274.         }
  275.         set(TRUE);
  276.         break;
  277.     }
  278.     case rDUP:
  279.         break; /* Null command */
  280.     case MATCH:
  281.         set(lmatch(glob(glom(n->u[0].p)), glom(n->u[1].p)));
  282.         break;
  283.     case rSWITCH: {
  284.         List *v = glom(n->u[0].p);
  285.  
  286.         while (1) {
  287.             do {
  288.                 n = n->u[1].p;
  289.                 if (n == NULL || n->type != BODY)
  290.                     return istrue();
  291.             } while (!iscase(n->u[0].p));
  292.             if (lmatch(v, glom(n->u[0].p)->n)) {
  293.                 for (n = n->u[1].p; n != NULL && !iscase(n->u[0].p); n = n->u[1].p) {
  294.                     if (n->type != BODY) { /* special case at the end of BODY subtree */
  295.                         walk(n, TRUE);
  296.                         break;
  297.                     }
  298.                     walk(n->u[0].p, TRUE);
  299.                 }
  300.                 break;
  301.             }
  302.         }
  303.         break;
  304.     }
  305.     case PRE: {
  306.         List *v;
  307.  
  308.         if (n->u[0].p->type == rREDIR) {
  309.             qredir(n->u[0].p);
  310.             walk(n->u[1].p, TRUE);
  311.             break;
  312.         } else if (n->u[0].p->type == ASSIGN) {
  313.             if (isallpre(n->u[1].p)) {
  314.                 walk(n->u[0].p, TRUE);
  315.                 walk(n->u[1].p, TRUE);
  316.                 break;
  317.             } else {
  318.                 v = glom(n->u[0].p->u[0].p);
  319.                 assign(v, glob(glom(n->u[0].p->u[1].p)), TRUE);
  320.                 walk(n->u[1].p, TRUE);
  321.                 varrm(v->w, TRUE);
  322.             }
  323.         } else
  324.             rc_error("walk: node other than assign or redir in PRE. help!");
  325.         break;
  326.     }
  327.     case BRACE:
  328.         if (dofork()) {
  329.             setsigdefaults();
  330.             walk(n->u[1].p, TRUE); /* Do redirections */
  331.             redirq = NULL;   /* Reset redirection queue */
  332.             walk(n->u[0].p, TRUE); /* Do commands */
  333.             rc_exit(getstatus());
  334.             /* NOTREACHED */
  335.         }
  336.         break;
  337.     case EPILOG:
  338.         qredir(n->u[0].p);
  339.         if (n->u[1].p != NULL)
  340.             walk(n->u[1].p, TRUE); /* Do more redirections. */
  341.         else
  342.             doredirs();    /* Okay, we hit the bottom. */
  343.         break;
  344.     case NMPIPE:
  345.         rc_error("named pipes cannot be executed as commands");
  346.         /* NOTREACHED */
  347.     default:
  348.         rc_error("walk: unknown node; this can't happen");
  349.         /* NOTREACHED */
  350.     }
  351.     return istrue();
  352. }
  353.  
  354. /* checks a "command-line" (in parsetree form) to see if it contains the fake keyword "case". */
  355.  
  356. static boolean iscase(Node *n) {
  357.     if (n == NULL)
  358.         return FALSE;
  359.  
  360.     switch (n->type) {
  361.     case rWORD:
  362.         return streq(n->u[0].s, "case");
  363.     case ARGS: case LAPPEND:
  364.         return iscase(n->u[0].p);
  365.     default:
  366.         return FALSE;
  367.     }
  368. }
  369.  
  370. /* checks to see whether a subtree is all pre-command directives, i.e., assignments and redirs only */
  371.  
  372. static boolean isallpre(Node *n) {
  373.     if (n == NULL)
  374.         return TRUE;
  375.  
  376.     switch (n->type) {
  377.     case PRE:
  378.         return isallpre(n->u[1].p);
  379.     case rREDIR: case ASSIGN: case rDUP:
  380.         return TRUE;
  381.     default:
  382.         return FALSE;
  383.     }
  384. }
  385.  
  386. /*
  387.    A code-saver. Forks, child returns (for further processing in walk()), and the parent
  388.    waits for the child to finish, setting $status appropriately.
  389. */
  390.  
  391. static boolean dofork() {
  392.     int pid, sp;
  393.  
  394.     switch (pid = fork()) {
  395.     case -1:
  396.         uerror("fork");
  397.         rc_error(NULL);
  398.         /* NOTREACHED */
  399.     case 0:
  400.         return TRUE;
  401.     default:
  402.         redirq = NULL; /* clear out the pre-redirection queue in the parent */
  403.         fifoq = NULL;
  404.         while (pid != wait(&sp))
  405.             if (pid < 0)
  406.                 uerror("wait");
  407.         setstatus(sp);
  408.         return FALSE;
  409.     }
  410. }
  411.  
  412.