home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume26 / ssh-1.7 / part02 / exe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-15  |  22.6 KB  |  903 lines

  1. /* $Copyright:    $
  2.  * Copyright (c) 1991,1992,1993 by Steve Baker
  3.  * All rights reserved
  4.  *  
  5.  * This software is provided as is without any express or implied
  6.  * warranties, including, without limitation, the implied warranties
  7.  * of merchantability and fitness for a particular purpose.
  8.  */
  9.  
  10. #include <errno.h>
  11. #include <pwd.h>
  12. #include "shell.h"
  13.  
  14. enum {
  15.   T_WORD = 1, T_INPUT, T_OUTPUT, T_APP_OUTPUT, T_ERROR, T_APP_ERROR, T_BOTH,
  16.   T_APP_BOTH, T_BAR, T_BAR_ERROR, T_BAR_BOTH, T_AMP, T_NOTTY, T_EOL,
  17.   T_PINPUT, T_POUTPUT, T_PERROR, T_PBOTH, T_APINPUT, T_APP_POUTPUT,
  18.   T_APP_PERROR, T_APP_PBOTH, T_AND, T_OR, T_SEMICOLON
  19. };
  20.  
  21. #define FDFLAG        -2
  22.  
  23. /* ought to make a hash for this: */
  24. struct Token {
  25.   char tok[5];
  26.   char val;
  27. } tokens[] = {
  28.   "&", T_AMP,
  29.   "&!", T_NOTTY,
  30.   "&&", T_AND,
  31.   ";", T_SEMICOLON,
  32.   "<", T_INPUT,
  33.   "<%", T_PINPUT,
  34.   "<<%", T_APINPUT,
  35.   ">", T_OUTPUT,
  36.   ">!", T_ERROR,
  37.   ">!%", T_PERROR,
  38.   ">%", T_POUTPUT,
  39.   ">&", T_BOTH,
  40.   ">&%", T_PBOTH,
  41.   ">>", T_APP_OUTPUT,
  42.   ">>!", T_APP_ERROR,
  43.   ">>!%", T_APP_PERROR,
  44.   ">>%", T_APP_POUTPUT,
  45.   ">>&", T_APP_BOTH,
  46.   ">>&%", T_APP_PBOTH,
  47.   "|", T_BAR,
  48.   "|!", T_BAR_ERROR,
  49.   "|&", T_BAR_BOTH,
  50.   "||", T_OR
  51. };
  52.  
  53. #define N_TOKENS    22
  54.  
  55. int ptr, lvl;
  56. char **wrds;
  57.  
  58. extern char buf[1025],path[1025];
  59. extern char **PATH, _loginshell, _inpipe, _echo;
  60. extern char _nofork, _noclobber, _nohup, _nobgnull;
  61. extern int max_ent, errno, _pgrp, _status, _failat;
  62. extern unsigned long SIGMASK;
  63. extern struct proc_tab *proc;
  64. extern struct _FILES FILES[MAX_FILES];
  65.  
  66. char *getnext();
  67. void check_children(), *malloc();
  68.  
  69. /*
  70.  * Setup all the pointers and global variables for the getnext() routine
  71.  * and call check which will parse and send the resulting commands to
  72.  * invoke().
  73.  */
  74. EXEC(cmd)
  75. char **cmd;
  76. {
  77.   int pid;
  78.  
  79.   if (!cmd[0]) return 0;
  80.   wrds = cmd;
  81.   lvl = ptr = 0;
  82.   if (check(&pid,FALSE,NULL) < 0) _status = 1;
  83.   pwait();
  84.   return pid;
  85. }
  86.  
  87. /*
  88.  * This here is a recursive routine to evaluate the above defined tokens
  89.  * and to finally invoke the executor to execute the command (execute is
  90.  * the right word for it alright).  This routine traverses a pipe to the
  91.  * last command in the pipe and starts invoking the commands in revearse
  92.  * order, right to left.  Pipe descriptors are magically kept track of and
  93.  * cleaned up.  This routine also sets up file redirections for redirect
  94.  * and sh_redirect (for builtins) and removes all the tokens (and redirected
  95.  * file names) from the command line. Any remaining ;'s are parsed here as
  96.  * well as `&&' and `||'.
  97.  */
  98. check(wpid,imapipe,piped)
  99. char imapipe;
  100. int *wpid, *piped;
  101. {
  102.   int token, tok, pid;
  103.   int i= 0, in = 0, out = 1, err = 2, pd[2];
  104.   char **tmp = NULL, *word = NULL, *inf = NULL, *outf = NULL, *errf = NULL;
  105.   char f, bg = FALSE, outa = FALSE, erra = FALSE;
  106.  
  107.   tmp = (char **)calloc(5,sizeof(char *));
  108.   while(1) {
  109.     word = getnext(&token);
  110.     switch(token) {
  111.       case T_WORD:
  112.     tmp[i++] = word;
  113.     if (!(i % 5)) tmp = (char **)realloc(tmp,sizeof(char *) * (i+5));
  114.     tmp[i] = NULL;
  115.     continue;
  116.       case T_INPUT:
  117.       case T_PINPUT:
  118.       case T_APINPUT:
  119.     if (imapipe) {
  120.       free_stuff(tmp,in,inf,out,outf,err,errf);
  121.       fprintf(stderr,"Ambigous redirection.\n");
  122.       return -1;
  123.     }
  124.     if (in != 0) {
  125.       free_stuff(tmp,in,inf,out,outf,err,errf);
  126.       fprintf(stderr,"Extra < or <%.\n");
  127.       return -1;
  128.     }
  129.     inf = getnext(&tok);
  130.     if (tok != T_WORD) {
  131.       free_stuff(tmp,in,inf,out,outf,err,errf);
  132.       fprintf(stderr,"Invalid input redirection.\n");
  133.       return -1;
  134.     }
  135.     if (token != T_INPUT) {
  136.       if ((in = makepipe(inf,0,(token==T_APINPUT))) < 0) {
  137.         free_stuff(tmp,in,inf,out,outf,err,errf);
  138.         return -1;
  139.       }
  140.     } else in = FDFLAG;
  141.     continue;
  142.       case T_OUTPUT:
  143.       case T_POUTPUT:
  144.       case T_APP_OUTPUT:
  145.       case T_APP_POUTPUT:
  146.     if (out != 1) {
  147.       free_stuff(tmp,in,inf,out,outf,err,errf);
  148.       fprintf(stderr,"Extra >, >> or >%.\n");
  149.       return -1;
  150.     }
  151.     outf = getnext(&tok);
  152.     if (tok != T_WORD) {
  153.       free_stuff(tmp,in,inf,out,outf,err,errf);
  154.       fprintf(stderr,"Invalid output redirection\n");
  155.       return -1;
  156.     }
  157.     if (token == T_POUTPUT || token == T_APP_POUTPUT) {
  158.       if ((out = makepipe(outf,1,token==T_APP_POUTPUT)) < 0) {
  159.         free_stuff(tmp,in,inf,out,outf,err,errf);
  160.         return -1;
  161.       }
  162.     } else out = FDFLAG;
  163.     outa = (token == T_APP_OUTPUT);
  164.     if (_noclobber && out == FDFLAG) {
  165.       f = access(outf,F_OK);
  166.       if (outa) {
  167.         if (f) {
  168.           free_stuff(tmp,in,inf,out,outf,err,errf);
  169.           fprintf(stderr,"Output redirection would create a new file.\n");
  170.           return -1;
  171.         }
  172.       } else {
  173.         if (!f) {
  174.           free_stuff(tmp,in,inf,out,outf,err,errf);
  175.           fprintf(stderr,"Output redirection would overwrite file.\n");
  176.           return -1;
  177.         }
  178.       }
  179.     }
  180.     continue;
  181.       case T_ERROR:
  182.       case T_PERROR:
  183.       case T_APP_ERROR:
  184.       case T_APP_PERROR:
  185.     if (err != 2) {
  186.       free_stuff(tmp,in,inf,out,outf,err,errf);
  187.       fprintf(stderr,"extra >!, >!% or >>!\n");
  188.       return -1;
  189.     }
  190.     errf = getnext(&tok);
  191.     if (tok != T_WORD) {
  192.       free_stuff(tmp,in,inf,out,outf,err,errf);
  193.       fprintf(stderr,"invalid output redirection\n");
  194.       return -1;
  195.     }
  196.     if (token == T_PERROR || token == T_APP_PERROR) {
  197.       if ((err = makepipe(errf,2,token==T_APP_PERROR)) < 0) {
  198.         free_stuff(tmp,in,inf,out,outf,err,errf);
  199.         return -1;
  200.       }
  201.     } else err = FDFLAG;
  202.     erra = (token == T_APP_ERROR);
  203.     if (_noclobber && err == FDFLAG) {
  204.       f = access(errf,F_OK);
  205.       if (erra) {
  206.         if (f) {
  207.           free_stuff(tmp,in,inf,out,outf,err,errf);
  208.           fprintf(stderr,"output redirection would create a new file.\n");
  209.           return -1;
  210.         }
  211.       } else {
  212.         if (!f) {
  213.           free_stuff(tmp,in,inf,out,outf,err,errf);
  214.           fprintf(stderr,"output redirection would overwrite file.\n");
  215.           return -1;
  216.         }
  217.       }
  218.     }
  219.     continue;
  220.       case T_BOTH:
  221.       case T_PBOTH:
  222.       case T_APP_BOTH:
  223.       case T_APP_PBOTH:
  224.     if (out != 1) {
  225.       free_stuff(tmp,in,inf,out,outf,err,errf);
  226.       fprintf(stderr,"extra >, >% or >>\n");
  227.       return -1;
  228.     }
  229.     if (err != 2) {
  230.       free_stuff(tmp,in,inf,out,outf,err,errf);
  231.       fprintf(stderr,"extra >!, >!% or >>!\n");
  232.       return -1;
  233.     }
  234.     outf = getnext(&tok);
  235.     if (tok != T_WORD) {
  236.       free_stuff(tmp,in,inf,out,outf,err,errf);
  237.       fprintf(stderr,"invalid output redirection\n");
  238.       return -1;
  239.     }
  240.     if (token == T_PBOTH || token == T_APP_PBOTH) {
  241.       if ((err = out = makepipe(outf,3,token==T_APP_PBOTH)) < 0) {
  242.         free_stuff(tmp,in,inf,out,outf,err,errf);
  243.         return -1;
  244.       }
  245.     } else err = out = FDFLAG;
  246.     errf = outf;
  247.     erra = outa = (token == T_APP_BOTH);
  248.     if (_noclobber && out == FDFLAG) {
  249.       f = access(outf,F_OK);
  250.       if (outa) {
  251.         if (f) {
  252.           free_stuff(tmp,in,inf,out,outf,err,errf);
  253.           fprintf(stderr,"output redirection would create a new file.\n");
  254.           return -1;
  255.         }
  256.       } else {
  257.         if (!f) {
  258.           free_stuff(tmp,in,inf,out,outf,err,errf);
  259.           fprintf(stderr,"output redirection would overwrite file.\n");
  260.           return -1;
  261.         }
  262.       }
  263.     }
  264.     continue;
  265.       case T_BAR:
  266.       case T_BAR_ERROR:
  267.       case T_BAR_BOTH:
  268.       case T_AMP:
  269.       case T_NOTTY:
  270.       case T_EOL:
  271.     tmp[i] = NULL;
  272.     if (token == T_BAR) {
  273.       if (out != 1) {
  274.         free_stuff(tmp,in,inf,out,outf,err,errf);
  275.         fprintf(stderr,">, >% or >> conflicts with |\n");
  276.         return -1;
  277.       }
  278.       if ((bg = check(wpid,TRUE,&out)) < 0) return bg;
  279.     }
  280.     if (token == T_BAR_ERROR) {
  281.       if (err != 2) {
  282.         free_stuff(tmp,in,inf,out,outf,err,errf);
  283.         fprintf(stderr,">!, >!% or >>! conflicts with |!\n");
  284.         return -1;
  285.       }
  286.       if ((bg = check(wpid,TRUE,&err)) < 0) return bg;
  287.     }
  288.     if (token == T_BAR_BOTH) {
  289.       if (out != 1) {
  290.         free_stuff(tmp,in,inf,out,outf,err,errf);
  291.         fprintf(stderr,">, >% or >> conflicts with |&\n");
  292.         return -1;
  293.       }
  294.       if (err != 2) {
  295.         free_stuff(tmp,in,inf,out,outf,err,errf);
  296.         fprintf(stderr,">!, >!% or >>! conflicts with |&\n");
  297.         return -1;
  298.       }
  299.       if ((bg = check(wpid,TRUE,&out)) < 0) return bg;
  300.       err = out;
  301.     }
  302.     if (imapipe) {
  303.       if (pipe(pd) < 0) {
  304.         free_stuff(tmp,in,inf,out,outf,err,errf);
  305.         fprintf(stderr,"error creating pipe.\n");
  306.         return -1;
  307.       }
  308.       *piped = pd[1];
  309.       in = pd[0];
  310.     }
  311.     if (token == T_AMP) bg = 1;
  312.     if (token == T_NOTTY) bg = 2;
  313.     if ((pid = invoke(tmp,in,inf,out,outf,outa,err,errf,erra,bg)) < 0) _status = 1;
  314.     *wpid = pid;
  315.     free_stuff(tmp,in,inf,out,outf,err,errf);
  316.     return bg;
  317.       case T_AND:
  318.       case T_OR:
  319.       case T_SEMICOLON:
  320.     tmp[i] = NULL;
  321.     if ((pid = invoke(tmp,in,inf,out,outf,outa,err,errf,erra,bg)) < 0) _status = 1;
  322.     *wpid = pid;
  323.     free_stuff(tmp,in,inf,out,outf,err,errf);
  324.     pwait();
  325.     if (token == T_OR && !_status) return 0;
  326.     if (token == T_AND && _status) return 0;
  327.     if (token == T_SEMICOLON && badstat(_status)) return 0;
  328.     tmp = (char **)calloc(5,sizeof(char *));
  329.     i = 0;
  330.     break;
  331.     }
  332.   }
  333. }
  334.  
  335. /* Self explanitory. Used in the above routine */
  336. free_stuff(tmp,in,inf,out,outf,err,errf)
  337. char **tmp, *inf, *outf, *errf;
  338. BYTE in, out, err;
  339. {
  340.   int i;
  341.  
  342.   if (tmp) {
  343.     for(i=0;tmp[i];i++) free(tmp[i]);
  344.     free(tmp);
  345.   }
  346.   if (inf) free(inf);
  347.   if (outf) free(outf);
  348.   if (errf) free(errf);
  349.   if (in > 0) close(in);
  350.   if (out > 1) close(out);
  351.   if (err > 2) close(err);
  352. }
  353.  
  354. /*
  355.  * Returns a valid file descritor or will make a pipe file descriptor
  356.  * if necessary.  Normally if it is determined that we are dealing with
  357.  * a pipe, we delete the reference that we are going to use from the
  358.  * FILES information (whether it be the input or output side), unless
  359.  * app is true (therefore a appended pipe reference).
  360.  */
  361. makepipe(name,d,app)
  362. char *name, d, app;
  363. {
  364.   BYTE i, pd;
  365.   int p[2];
  366.  
  367.   for(i=0;i<MAX_FILES;i++)
  368.     if (FILES[i].name && !strcmp(FILES[i].name,name)) break;
  369.  
  370.   if (i == MAX_FILES) {
  371.     for(i=0;i<MAX_FILES;i++)
  372.       if (!FILES[i].name) break;
  373.     if (i == MAX_FILES) {
  374.       fprintf(stderr,"File descriptor table full.\n");
  375.       return -1;
  376.     }
  377.  
  378.     FILES[i].name = SCOPY(name);
  379.     FILES[i].file = SCOPY("<PIPE>");
  380.     FILES[i].pread = FILES[i].pwrite = FALSE;
  381.     FILES[i].ispipe = FILES[i].read = FILES[i].write = TRUE;
  382.     pipe(p);
  383.     FILES[i].pipe = p[0];
  384.     FILES[i].desc = p[1];
  385.     fcntl(p[0],F_SETFD,1);
  386.     fcntl(p[1],F_SETFD,1);
  387.   }
  388.   if (FILES[i].ispipe) {
  389.     if (!d) {
  390.       if (FILES[i].read) {
  391.     if (FILES[i].write) {
  392.       pd = FILES[i].pipe;
  393.       if (!app) FILES[i].pipe = FILES[i].read = 0;
  394.     } else {
  395.       pd = FILES[i].desc;
  396.       if (!app) {
  397.         free(FILES[i].name);
  398.         free(FILES[i].file);
  399.         FILES[i].name = NULL;
  400.       }
  401.     }
  402.       } else {
  403.         fprintf(stderr,"Could not open descriptor %s for reading.\n",name);
  404.     return -1;
  405.       }
  406.     } else {
  407.       if (FILES[i].write) {
  408.     pd = FILES[i].desc;
  409.     if (!app) {
  410.       if (FILES[i].read) {
  411.         FILES[i].desc = FILES[i].pipe;
  412.         FILES[i].pipe = FILES[i].write = 0;
  413.       } else {
  414.         free(FILES[i].name);
  415.         free(FILES[i].file);
  416.         FILES[i].name = NULL;
  417.       }
  418.     }
  419.       } else {
  420.         fprintf(stderr,"Could not open descriptor %s for writing.\n",name);
  421.     return -1;
  422.       }
  423.     }
  424.     return pd;
  425.   }
  426.   if (d) FILES[i].pwrite = 1;
  427.   else FILES[i].pread = 1;
  428.   return FILES[i].desc;
  429. }
  430.  
  431. /*
  432.  * This gets the next token or word.  Try not to malloc anything until
  433.  * we're sure we've got to.  Slightly recursive with respect to ()'s.
  434.  * We remove the outer parens only, made recursive so something like:
  435.  * secho ()()()() is handled correctly (all parens removed) and
  436.  * secho (()()()()) removes only the outer parens.
  437.  */
  438. char *getnext(tok)
  439. int *tok;
  440. {
  441.   char *wrd = NULL, st, end, n, m;
  442.  
  443.   if (wrds[ptr] == NULL) {
  444.     *tok = T_EOL;
  445.     return NULL;
  446.   }
  447.  
  448.   if (wrds[ptr][0] == '(' && !wrds[ptr][1]) {
  449.     if (!lvl++) {
  450.       ptr++;
  451.       return getnext(tok);
  452.     }
  453.   }
  454.  
  455.   if (lvl && wrds[ptr][0] == ')' && !wrds[ptr][1]) {
  456.     if (!--lvl) {
  457.       ptr++;
  458.       return getnext(tok);
  459.     }
  460.   }
  461.  
  462.   if (!lvl) {
  463.     st = 0;
  464.     end = N_TOKENS;
  465.     while(st <= end) {
  466.       m = (st+end)/2;
  467.       if (!(n = strcmp(tokens[m].tok,wrds[ptr]))) {
  468.     ptr++;
  469.     *tok = tokens[m].val;
  470.     return NULL;
  471.       } else if (n < 0) st = m+1;
  472.       else end = m-1;
  473.     }
  474.   }
  475.   
  476. /*
  477.  * if it wasn't for this part here, we wouldn't need to malloc for check()
  478.  * at all...
  479.  */
  480.   if (!lvl && (wrds[ptr][0] == '"' || wrds[ptr][0] == '\'')) {
  481.     wrd = SCOPY(wrds[ptr]+1);
  482.     wrd[strlen(wrd)-1] = 0;
  483.   } else wrd = SCOPY(wrds[ptr]);
  484.   ptr++;
  485.   *tok = T_WORD;
  486.   return wrd;
  487. }
  488.  
  489. /*
  490.  * This is where we actually open up the files for the redirection.
  491.  * Background jobs with no files to read or write try to get redirected to
  492.  * /dev/null.
  493.  *
  494.  * This routine is called from the child process before the exec.
  495.  */
  496. redirect(in,inf,out,outf,outa,err,errf,erra,bg)
  497. int in,out,err;
  498. char *inf,*outf,*errf;
  499. char outa,erra,bg;
  500. {
  501.   int i,flags;
  502.  
  503.   if (in == 0 && bg && !_nobgnull) {
  504.     if (inf) free(inf);
  505.     inf = SCOPY(NULL_DEV);
  506.     in = FDFLAG;
  507.   }
  508.   if (in != 0) {
  509.     close(0);
  510.     if (in > 0) fcntl(in,F_DUPFD,0);
  511.     else if (open(inf,O_RDONLY) == -1) {
  512.       fprintf(stderr,"can't open %s for input\n",inf);
  513.       return -1;
  514.     }
  515.   }
  516.   if (out == 1 && bg && !_nobgnull) {
  517.     if (outf) free(outf);
  518.     outf = SCOPY(NULL_DEV);
  519.     out = FDFLAG;
  520.   }
  521.   if (err == 2 && bg && !_nobgnull) {
  522.     if (errf) free(errf);
  523.     if (!strcmp(outf,NULL_DEV)) errf = outf;
  524.     else errf = SCOPY(NULL_DEV);
  525.     err = FDFLAG;
  526.   }
  527.   if (out != 1) {
  528.     close(1);
  529.     if (out > 1) fcntl(out,F_DUPFD,1);
  530.     else {
  531.       flags = O_WRONLY | O_CREAT | (outa? O_APPEND : O_TRUNC);
  532.       if (open(outf,flags,0666) == -1) {
  533.     fprintf(stderr,"can't open %s for output\n",outf);
  534.     return -1;
  535.       }
  536.     }
  537.   }
  538.   if (err != 2) {
  539.     close(2);
  540.     if (err > 2) fcntl(err,F_DUPFD,2);
  541.     else {
  542.       if (errf == outf) dup(1);
  543.       else {
  544.     flags = O_WRONLY | O_CREAT | (erra? O_APPEND : O_TRUNC);
  545.     if (open(errf,flags,0666) == -1) {
  546.       fprintf(stderr,"can't open %s for output\n",errf);
  547.       return -1;
  548.     }
  549.       }
  550.     }
  551.   }
  552.   for(i=3;i<20;i++) close(i);
  553.   if (inf) free(inf);
  554.   if (outf) free(outf);
  555.   if (errf && errf != outf) free(errf);
  556.   return 0;
  557. }
  558.  
  559. /*
  560.  * Finally, this is where we get to do our fork and exec the command.
  561.  * Not very pretty, but it works.
  562.  */ 
  563. invoke(arg,in,inf,out,outf,outa,err,errf,erra,bg)
  564. char **arg,*inf,*outf,*errf;
  565. int in,out,err;
  566. char outa,erra,bg;
  567. {
  568.   unsigned long mask;
  569.   int pid,i,j = 0,ent;
  570.   struct stat lbuf;
  571.   static WORD lpid = 0;
  572.  
  573.   if (_echo) {
  574.     for(i=0;arg[i];i++) {
  575.       if (i > 0) putchar(' ');
  576.       fputs(arg[i],stdout);
  577.     }
  578.     putchar('\n');
  579.   }
  580.  
  581. /* check for built-ins */
  582.   if (parse_arg(arg,in,inf,out,outf,outa,err,errf,erra,bg) == SHELL_COMMAND) return 0;
  583.  
  584. /*
  585.  * Evaluate paths here.  This is where we run through our PATH var and prepend
  586.  * paths to the command until we find where the command is and run it, if we
  587.  * don't find our command, we'll fall through and see if it's a directory in
  588.  * our current working directory.  If we have no PATH var then we'll at least
  589.  * try our current working dir.  Also, if the command already has path info
  590.  * in it, we'll leave it alone, assuming the user knows what he's doing.
  591.  *
  592.  * A hash table like csh has might be nice, but since most commands are
  593.  * stored in only a few directories, for most people it might be a waste
  594.  * of code and memory to do.
  595.  */
  596.   if (index(arg[0],'/')) {
  597.     if (arg[0][0] != '/') sprintf(path,"./%s",arg[0]);
  598.     else strcpy(path,arg[0]);
  599.     if ((access(path,F_OK)) == 0)    /* I imagine this is not needed */
  600.       if (stat(path,&lbuf) > -1)    /* just use this */
  601.     if ((lbuf.st_mode & S_IFMT) == S_IFREG) j = 1;
  602.   } else {
  603.     for(i=0;PATH[i];i++) {
  604.       sprintf(path,"%s/%s",PATH[i],arg[0]);
  605.       if ((access(path,F_OK)) == 0)
  606.     if (stat(path,&lbuf) > -1)
  607.       if ((lbuf.st_mode & S_IFMT) == S_IFREG) { j = 1; break; }
  608.     }
  609.   }
  610. /*
  611.  * Check if the command is a directory or not.  Only checked when we couldn't
  612.  * find anything worth running in our path list. 
  613.  */
  614.   if (j == 0) {
  615.     if (arg[1] == NULL && stat(arg[0],&lbuf) > -1) {
  616.       if (((lbuf.st_mode & S_IFMT) == S_IFDIR) || ((lbuf.st_mode & S_IFMT) == S_IFLNK)) {
  617.     auto_cd(arg[0]);
  618.     return 0;
  619.       }
  620.     }
  621.     fprintf(stderr,"%s: command not found.\n",arg[0]);
  622.     return -1;
  623.   }
  624. /* Check to see if we can actually run it! */
  625.   if (access(path,X_OK)) {
  626.     fprintf(stderr,"%s: permission denied.\n",arg[0]);
  627.     return -1;
  628.   }
  629.  
  630. /*
  631.  * Here we go.  Probably ought to try and use vfork, but I'm not too worried
  632.  * about it.  Probably be better to keep it fork for portibility anyway.
  633.  */
  634.   mask = sigblock(sigmask(SIGCHLD));
  635.  
  636.   if (_nofork) pid = 0;
  637.   else pid = fork();
  638.  
  639.   if (pid) {
  640.     if (pid < 0) {
  641.       if (errno == EAGAIN) {
  642.     fprintf(stderr,"%s: can't fork - process limit exceeded!\n",arg[0]);
  643.       } else fprintf(stderr,"%s: out of memory!\n",arg[0]);
  644.       _status = 2;
  645.       sigsetmask(mask);
  646.       return pid;
  647.     }
  648. /*
  649.  * What a mess! Have to do this here so we keep track of lpid.
  650.  * Ought to be some way to syncronize processes so that the parent gives
  651.  * out the control terminal, before the child can exec.  Perhaps some kind
  652.  * of semaphore locking mechanism?
  653.  */
  654.     if (in > 0 && out < 2 && err < 3) lpid = pid;
  655.     else if (in < 1 && out < 2 && err < 3) lpid = 0;
  656.  
  657. /* make our process table entry. */
  658.     ent = get_proc_ent();
  659.     proc[ent].pid = pid;
  660.     proc[ent].bg = bg;
  661.     proc[ent].cmd = (char *)strcpy(malloc(strlen(arg[0])+1),arg[0]);
  662.     proc[ent].pipe = lpid;
  663.  
  664.     if (!bg && lpid) proc[ent].pgrp = lpid;
  665.     else if (!bg) proc[ent].pgrp = pid;
  666.     else proc[ent].pgrp = _pgrp;
  667.     proc[ent].status = STAT_RUNNING;
  668.  
  669. /*
  670.  * Dangerous, since a bg'ed process could gain control of the terminal
  671.  * before we write this.  But if that happens we're screwed anyway
  672.  * when it comes time to read from the terminal.
  673.  */
  674.     if (bg == 1 && (!lpid || lpid == pid))
  675.       if (in > 0 || out > 1 || err > 2) printf(" [%d] (%d) %s\n",ent,pid,arg[0]);
  676.       else printf(" [%d] %d\n",ent,pid);
  677.  
  678.     sigsetmask(mask);
  679.     return pid;
  680.   } else {
  681.     if (bg == 2) {    /* Void tty association if spawned w/ &! */
  682.       close(0); close(1); close(2);
  683.       if (fork()) exit(0);
  684.       setpgrp(0,0);
  685.       if ((i=open("/dev/tty",O_RDWR)) < 0) exit(1);
  686.       ioctl(i,TIOCNOTTY,0);
  687.       close(i);
  688.       if (fork()) exit(0);
  689.       if (fork()) exit(0);
  690.     }
  691.  
  692.     pid = getpid();
  693.     if (in > 0 && out < 2 && err < 3) lpid = pid;
  694.     else if (in < 1 && out < 2 && err < 3) lpid = 0;
  695.  
  696. /*
  697.  * Gain control of the terminal if need be. Since there is no way to
  698.  * adaquately perform process syncronization, it must be done here.
  699.  * There has got to be a better way to do this... For crying out loud!
  700.  */
  701.     signal(SIGTSTP,SIG_IGN);    /* Hokey signal ignore hack to keep  */
  702.     signal(SIGTTIN,SIG_IGN);    /* child from stopping when it tries */
  703.     signal(SIGTTOU,SIG_IGN);    /* to get control of the terminal.   */
  704.  
  705.     if (!bg && lpid) setpgrp(pid,lpid);
  706.     else if (!bg) setpgrp(pid,pid);
  707.     if (!bg && (lpid == 0 || pid == lpid) && !_inpipe) tcsetpgrp(TTY,pid);
  708.  
  709. /*
  710.  * Redirect stuff if we can (otherwise exit), then turn signals back to
  711.  * their default and ignore SIGHUP for background processes.
  712.  * Then try to exec the command.
  713.  */
  714.     if (redirect(in,inf,out,outf,outa,err,errf,erra,bg) < 0) exit(1);
  715.  
  716.     for(i=1;i<32;i++) signal(i,SIG_DFL);
  717.     sigsetmask(0);
  718.     if (bg || _nohup) sigblock(sigmask(SIGHUP));
  719.  
  720.     execv(path,arg);
  721.     switch(errno) {
  722.       case ENOENT:
  723.     fprintf(stderr,"%s: nonexsistent path!\n",path);
  724.     exit(1);
  725.       case ENOTDIR:
  726.     fprintf(stderr,"%s: bad path!\n",path);
  727.     exit(1);
  728.       case EACCES:
  729.     fprintf(stderr,"exec: I can't run that!\n");
  730.     exit(1);
  731.       case ENOEXEC:
  732.     auto_source((out > 1 || err > 2),path,arg);
  733.     exit(0);
  734.       case ENOMEM:
  735.     fprintf(stderr,"%s: memory limit exceeded.\n",arg[0]);
  736.     exit(1);
  737.       case E2BIG:
  738.     fprintf(stderr,"%s: argument list exceeded 10K.\n",arg[0]);
  739.     exit(1);
  740.       case EFAULT:
  741.       case EIO:
  742.       case ETXTBSY:
  743.     fprintf(stderr,"%s: unrecoverable error while execing.\n",arg[0]);
  744.     exit(1);
  745.       default:
  746.     fprintf(stderr,"%s: cannot execute.\n",arg[0]);
  747.     }
  748.     exit(1);
  749.   }
  750.   /*NOTREACHED*/
  751. }
  752.  
  753. /*
  754.  * Pwait here waits until all jobs that have been spawned but are not in the
  755.  * background or stopped, to complete.
  756.  */
  757. pwait()
  758. {
  759.   unsigned long mask;
  760.   int i,flag;
  761.  
  762.   mask = sigblock(sigmask(SIGCHLD));
  763.   do {
  764.     flag = FALSE;
  765.     for(i=0;i<max_ent;i++)
  766.       if (proc[i].pid && !proc[i].bg && !proc[i].status) {
  767.     sigpause(mask);
  768.     flag = TRUE;
  769.     break;
  770.       }
  771.   } while(flag);
  772.   sigsetmask(mask);
  773. }
  774.  
  775. /*
  776.  * Redirection for builtins.  Looks a lot like the real thing, except no
  777.  * duping required.
  778.  */
  779. sh_redirect(in,inf,out,outf,outa,err,errf,erra,bg)
  780. int *in,*out,*err;
  781. char *inf,*outf,*errf;
  782. char outa,erra,bg;
  783. {
  784.   int flags;
  785.  
  786.   if (*in == 0 && bg && !_nobgnull) {
  787.     inf = SCOPY(NULL_DEV);
  788.     *in = FDFLAG;
  789.   }
  790.   if (*in < 0) {
  791.     if ((*in = open(inf,O_RDONLY)) == -1) {
  792.       fprintf(stderr,"Can't open %s for input.\n",inf);
  793.       return -1;
  794.     }
  795.   }
  796.   if (*out == 1 && bg && !_nobgnull) {
  797.     outf = SCOPY(NULL_DEV);
  798.     *out = FDFLAG;
  799.   }
  800.   if (*err == 2 && bg && !_nobgnull) {
  801.     if (!strcmp(inf,NULL_DEV)) errf = outf;
  802.     else errf = SCOPY(NULL_DEV);
  803.     *err = FDFLAG;
  804.   }
  805.   if (*out < 1) {
  806.     flags = O_WRONLY | O_CREAT | (outa? O_APPEND : O_TRUNC);
  807.     if ((*out = open(outf,flags,0666)) == -1) {
  808.       fprintf(stderr,"Can't open %s for output.\n",outf);
  809.       return -1;
  810.     }
  811.   }
  812.   if (*err < 2) {
  813.     if (outf == errf) *err = *out;
  814.     else {
  815.       flags = O_WRONLY | O_CREAT | (erra? O_APPEND : O_TRUNC);
  816.       if ((*err = open(errf,flags,0666)) == -1) {
  817.     fprintf(stderr,"Can't open %s for output.\n",errf);
  818.     return -1;
  819.       }
  820.     }
  821.   }
  822.   return 0;
  823. }
  824.  
  825. /*
  826.  * Routine that's called when we couldn't find a file to run, but we did
  827.  * find a directory we might be able to cd into.
  828.  */
  829. auto_cd(dir)
  830. char *dir;
  831. {
  832.   if (chdir(dir) == -1) {
  833.     if (errno == ELOOP)
  834.       printf("too many symbolic links.\n");
  835.     else
  836.       printf("%s: Permission denied.\n",dir);
  837.   } else {
  838.     getwd(path);
  839.     makeset("cwd",path);
  840.   }
  841. }
  842.  
  843. /*
  844.  * Source here is kinda dumb, takes anything and chews it up.  Need someway
  845.  * to keep it from sourcing things it shouldn't... Like say, object files
  846.  * and binary data files.
  847.  *
  848.  * If we're in a pipe, we'll need to remember that we're in a pipe so
  849.  * the shell won't try to take control of the terminal after a command in
  850.  * the source script completes.
  851.  */
  852. auto_source(inpipe,file,arg)
  853. char *file, **arg;
  854. {
  855.   int i;
  856.   struct _setvar *v;
  857.  
  858.   _inpipe = inpipe;
  859.   _pgrp = getpgrp(0);
  860.   _loginshell = FALSE;
  861.   for(i=0;i<32;i++) signal(i,SIG_IGN);
  862.   signal(SIGSTOP,SIG_DFL);
  863.   signal(SIGTSTP,SIG_DFL);
  864.   signal(SIGINT,SIG_DFL);
  865.  
  866.   if (!_nohup) signal(SIGHUP,SIG_DFL);
  867.  
  868. /*
  869.  * cleanup old process table because we don't want to wait for processes the
  870.  * other shell is tending to, otherwise we'll be waiting on them forever.
  871.  */
  872.   for(i=0;i<max_ent;i++)
  873.     if (proc[i].pid) {
  874.       if (proc[i].cmd) free(proc[i].cmd);
  875.       proc[i].pid = 0;
  876.     }
  877.   signal(SIGCHLD,check_children);
  878.  
  879.   v = (struct _setvar *)makeset("argv","");
  880.   v->sv.wrd = arg;
  881.   for(i=0;arg[i];i++);
  882.   v->nwrds = i;
  883.   makenvar("argc",i);
  884.   if (source(file) < 0) exit(1);
  885.   exit(0);
  886. }
  887.  
  888. #ifndef BSD4
  889. tcsetpgrp(fd,ppid)
  890. int fd, ppid;
  891. {
  892.   return ioctl(fd,TIOCSPGRP,&ppid);
  893. }
  894.  
  895. tcgetpgrp(fd)
  896. int fd;
  897. {
  898.   int pgrp;
  899.   ioctl(fd,TIOCGPGRP,&pgrp);
  900.   return pgrp;
  901. }
  902. #endif
  903.