home *** CD-ROM | disk | FTP | other *** search
/ minnie.tuhs.org / unixen.tar / unixen / PDP-11 / Distributions / ucb / spencer_2bsd.tar.gz / 2bsd.tar / src / csh / sh.c < prev    next >
C/C++ Source or Header  |  1980-02-17  |  16KB  |  751 lines

  1. /* Copyright (c) 1979 Regents of the University of California */
  2. #include "sh.h"
  3.  
  4. /*
  5.  * C Shell
  6.  *
  7.  * Bill Joy, UC Berkeley
  8.  * October, 1978
  9.  */
  10.  
  11. char    *pathlist[] =    { SRCHPATH, 0 };
  12.  
  13. main(c, av)
  14.     int c;
  15.     char **av;
  16. {
  17.     register char **v, *cp;
  18.     int nofile = 0;
  19.     int reenter = 0;
  20.     bool nverbose = 0, nexececho = 0, quitit = 0, fast = 0, prompt = 1;
  21.     char *hp;
  22.  
  23.     settimes();            /* Immed. estab. timing base */
  24.     hp = getenv("HOME");
  25.     v = av;
  26.     if (eq(v[0], "a.out"))        /* A.out's are quittable */
  27.         quitit = 1;
  28.     uid = getuid();
  29. #ifdef V6
  30.     loginsh = eq(*v, "-");        /* To do .login/.logout */
  31. #else
  32.     loginsh = **v == '-';
  33. #endif
  34.     if (loginsh)
  35.         time(&chktim);
  36.  
  37.     /*
  38.      * Move the descriptors to safe places.
  39.      * The variable didfds is 0 while we have only FSH* to work with.
  40.      * When didfds is true, we have 0,1,2 and prefer to use these.
  41.      */
  42.     initdesc();
  43.  
  44.     /*
  45.      * Initialize the shell variables.
  46.      * ARGV and PROMPT are initialized later.
  47.      * STATUS is also munged in several places.
  48.      * CHILD is munged when forking/waiting
  49.      */
  50.  
  51.     set("status", "0");
  52.     if (hp == 0)
  53.         fast++;            /* No home -> can't read scripts */
  54.     else
  55.         set("home", hp);
  56.     if (uid == 0)
  57.         pathlist[0] = "/etc";
  58.     set1("path", saveblk(pathlist), &shvhed);
  59.     /*
  60.      * Re-initialize path if set in environment
  61.      *
  62.     cp = getenv("PATH");
  63.     if (cp != 0) {
  64.         register int i = 0;
  65.         register char *dp;
  66.         register char **pv;
  67.  
  68.         for (dp = cp; *dp; dp++)
  69.             if (*dp == ':')
  70.                 i++;
  71.         pv = calloc(i+1, sizeof (char **));
  72.         dp = cp;
  73.         i = 0;
  74.         while (*dp) {
  75.             if (*dp == ':') {
  76.                 *dp = 0;
  77.                 pv[i++] = savestr(cp);
  78.                 *dp = ':';
  79.             } else if (*dp == 0) {
  80.                 pv[i++] = savestr(cp);
  81.                 break;
  82.             }
  83.             dp++;
  84.         }
  85.         pv[i] = 0;
  86.         set1("path", pv, &shvhed);
  87.     }
  88. */
  89.     set("shell", SHELLPATH);
  90.  
  91.     doldol = putn(getpid());        /* For $$ */
  92.     shtemp = strspl("/tmp/sh", doldol);    /* For << */
  93.  
  94.     /*
  95.      * Record the interrupt states from the parent process.
  96.      * If the parent is non-interruptible our hand must be forced
  97.      * or we (and our children) won't be either.
  98.      * Our children inherit termination from our parent.
  99.      * We catch it only if we are the login shell.
  100.      */
  101.     parintr = signal(SIGINT, SIG_IGN);    /* parents interruptibility */
  102.     signal(SIGINT, parintr);            /* ... restore */
  103.     parterm = signal(SIGTERM, SIG_IGN);    /* parents terminability */
  104.     signal(SIGTERM, parterm);            /* ... restore */
  105.  
  106.     /*
  107.      * Process the arguments.
  108.      *
  109.      * Note that processing of -v/-x is actually delayed till after
  110.      * script processing.
  111.      *
  112.      * We set the first character of our name to be '-' if we are
  113.      * a shell running interruptible commands.  Many programs which
  114.      * examine ps'es use this to filter such shells out.
  115.      */
  116.     c--, v++;
  117.     while (c > 0 && (cp = v[0])[0] == '-') {
  118.         do switch (*cp++) {
  119.  
  120.         case 0:            /* -    Interruptible, no prompt */
  121.             prompt = 0;
  122.             **av = '-';
  123.             nofile++;
  124.             break;
  125.  
  126.         case 'c':        /* -c    Command input from arg */
  127.             if (c == 1)
  128.                 exit(0);
  129.             c--, v++;
  130.             arginp = v[0];
  131.             prompt = 0;
  132.             nofile++;
  133.             break;
  134.  
  135.         case 'e':        /* -e    Exit on any error */
  136.             exiterr++;
  137.             break;
  138.  
  139.         case 'f':        /* -f    Fast start */
  140.             fast++;
  141.             break;
  142.  
  143.         case 'i':        /* -i    Interactive, even if !intty */
  144.             intact++;
  145.             **av = '-';
  146.             nofile++;
  147.             break;
  148.  
  149.         case 'n':        /* -n    Don't execute */
  150.             noexec++;
  151.             break;
  152.  
  153.         case 'q':        /* -q    (Undoc'd) ... die on quit */
  154.             quitit = 1;
  155.             break;
  156.  
  157.         case 's':        /* -s    Read from std input */
  158.             nofile++;
  159.             if (isatty(SHIN))
  160.                 **v = '-';
  161.             break;
  162.  
  163.         case 't':        /* -t    Read one line from input */
  164.             onelflg = 2;
  165.             if (isatty(SHIN))
  166.                 **v = '-';
  167.             prompt = 0;
  168.             nofile++;
  169.             break;
  170.  
  171.         case 'v':        /* -v    Echo hist expanded input */
  172.             nverbose = 1;            /* ... later */
  173.             break;
  174.  
  175.         case 'x':        /* -x    Echo just before execution */
  176.             nexececho = 1;            /* ... later */
  177.             break;
  178.  
  179.         case 'V':        /* -V    Echo hist expanded input */
  180.             setNS("verbose");        /* NOW! */
  181.             break;
  182.  
  183.         case 'X':        /* -X    Echo just before execution */
  184.             setNS("echo");            /* NOW! */
  185.             break;
  186.  
  187.         } while (*cp);
  188.         v++, c--;
  189.     }
  190.  
  191.     if (quitit)            /* With all due haste, for debugging */
  192.         signal(SIGQUIT, SIG_DFL);
  193.  
  194.     /*
  195.      * Unless prevented by -, -c, -i, -s, or -t, if there
  196.      * are remaining arguments the first of them is the name
  197.      * of a shell file from which to read commands.
  198.      */
  199.     if (nofile == 0 && c > 0) {
  200.         nofile = open(v[0], 0);
  201.         if (nofile < 0) {
  202.             child++;        /* So this ... */
  203.             Perror(v[0]);        /* ... doesn't return */
  204.         }
  205.         file = v[0];
  206.         SHIN = dmove(nofile, FSHIN);    /* Replace FSHIN */
  207.         prompt = 0;
  208.         c--, v++;
  209.     }
  210.  
  211.     /*
  212.      * Consider input a tty if it really is or we are interactive.
  213.      */
  214.     intty = intact || isatty(SHIN);
  215. #ifdef TELL
  216.     settell();
  217. #endif
  218.     /*
  219.      * Commands are interruptible if we are interactive
  220.      * or the process which created us was.
  221.      */
  222.     if (intact || parintr == SIG_DFL)
  223.         **av = '-';
  224.  
  225.     /*
  226.      * Save the remaining arguments in ARGV.
  227.      * Normally the system-supplied argument list is ok as
  228.      * a zero terminated value block.
  229.      * On some version 6 systems, it is -1 terminated and setting it
  230.      * to zero messes up "ps" so we change it to zero, copy
  231.      * the block of pointers, and put it back the way it was.
  232.      */
  233. /*
  234.     if (c == 0)
  235.         set("argv", 0);
  236.     else
  237.  */
  238.     if ((int) v[c] == -1) {
  239.         /* ick */
  240.         v[c] = 0, setq("argv", copyblk(v), &shvhed), v[c] = (char *) -1;
  241.     } else
  242.         setq("argv", v, &shvhed);
  243.  
  244.     /*
  245.      * Set up the prompt.
  246.      */
  247.     if (prompt)
  248.         set("prompt", uid == 0 ? "# " : "% ");
  249.  
  250.     /*
  251.      * If we are an interactive shell, then start fiddling
  252.      * with the signals; this is a tricky game.
  253.      */
  254.     if (**av == '-') {
  255.         setintr++;
  256.         if (!quitit)        /* Wary! */
  257.             signal(SIGQUIT, SIG_IGN);
  258.         signal(SIGINT, SIG_IGN);
  259.         signal(SIGTERM, SIG_IGN);
  260.     }
  261.  
  262.     /*
  263.      * Set an exit here in case of an interrupt or error reading
  264.      * the shell start-up scripts.
  265.      */
  266.     setexit();
  267.     haderr = 0;        /* In case second time through */
  268.     if (!fast && reenter == 0) {
  269.         reenter++;
  270.         /* Will have value("home") here because set fast if don't */
  271.         srccat(value("home"), "/.cshrc");
  272.         if (loginsh)
  273. #ifdef NOHELP
  274.             srccat("", ".login");
  275. #else
  276.             srccat(value("home"), "/.login");
  277. #endif
  278.     }
  279.  
  280.     /*
  281.      * Now are ready for the -v and -x flags
  282.      */
  283.     if (nverbose)
  284.         setNS("verbose");
  285.     if (nexececho)
  286.         setNS("echo");
  287.  
  288.     /*
  289.      * All the rest of the world is inside this call.
  290.      * The argument to process indicates whether it should
  291.      * catch "error unwinds".  Thus if we are a interactive shell
  292.      * our call here will never return by being blown past on an error.
  293.      */
  294.     process(setintr);
  295.  
  296.     /*
  297.      * Mop-up.
  298.      */
  299.     if (loginsh) {
  300.         printf("logout\n");
  301.         close(SHIN);
  302.         child++;
  303.         goodbye();
  304.     }
  305.     exitstat();
  306. }
  307.  
  308. /*
  309.  * Source to the file which is the catenation of the argument names.
  310.  */
  311. srccat(cp, dp)
  312.     char *cp, *dp;
  313. {
  314.     register char *ep = strspl(cp, dp);
  315.     register int unit = dmove(open(ep, 0), -1);
  316.  
  317.     /* ioctl(unit, FIOCLEX, NULL); */
  318.     xfree(ep);
  319. #ifdef INGRES
  320.     srcunit(unit, 0);
  321. #else
  322.     srcunit(unit, 1);
  323. #endif
  324. }
  325.  
  326. /*
  327.  * Source to a unit.  If onlyown it must be our file or
  328.  * we don't chance it.    This occurs on ".cshrc"s and the like.
  329.  */
  330. srcunit(unit, onlyown)
  331.     register int unit;
  332.     bool onlyown;
  333. {
  334.     /* We have to push down a lot of state here */
  335.     /* All this could go into a structure */
  336.     int oSHIN = -1, oldintty = intty;
  337.     struct whyle *oldwhyl = whyles;
  338.     char *ogointr = gointr, *oarginp = arginp;
  339.     int oonelflg = onelflg;
  340. #ifdef TELL
  341.     bool otell = cantell;
  342. #endif
  343.     struct Bin saveB;
  344.  
  345.     /* The (few) real local variables */
  346.     jmp_buf oldexit;
  347.     int reenter;
  348.     register int (*oldint)();
  349.  
  350.     if (unit < 0)
  351.         return;
  352.     if (onlyown) {
  353.         struct stat stb;
  354.  
  355. #ifdef CC
  356.         if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) {
  357. #endif
  358. #ifdef CORY
  359.         if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) {
  360. #endif
  361. #ifndef CC
  362. #ifndef CORY
  363.         if (fstat(unit, &stb) < 0 || stb.st_uid != uid) {
  364. #endif
  365. #endif
  366.             close(unit);
  367.             return;
  368.         }
  369.     }
  370.  
  371.     /*
  372.      * There is a critical section here while we are pushing down the
  373.      * input stream since we have stuff in different structures.
  374.      * If we weren't careful an interrupt could corrupt SHIN's Bin
  375.      * structure and kill the shell.
  376.      *
  377.      * We could avoid the critical region by grouping all the stuff
  378.      * in a single structure and pointing at it to move it all at
  379.      * once.  This is less efficient globally on many variable references
  380.      * however.
  381.      */
  382.     getexit(oldexit);
  383.     reenter = 0;
  384.     oldint = signal(SIGINT, SIG_IGN);
  385.     setexit();
  386.     reenter++;
  387.     if (reenter == 1) {
  388.         /* Setup the new values of the state stuff saved above */
  389.         copy(&saveB, &B, sizeof saveB);
  390.         fbuf = (char **) 0;
  391.         fseekp = feobp = fblocks = 0;
  392.         oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
  393.         intty = isatty(SHIN), whyles = 0, gointr = 0;
  394.         /*
  395.          * Now if we are allowing commands to be interrupted,
  396.          * we let ourselves be interrupted.
  397.          */
  398.         signal(SIGINT, setintr ? pintr : oldint);
  399. #ifdef TELL
  400.         settell();
  401. #endif
  402.         process(0);        /* 0 -> blow away on errors */
  403.     }
  404.     signal(SIGINT, oldint);
  405.     if (oSHIN >= 0) {
  406.         register int i;
  407.  
  408.         /* We made it to the new state... free up its storage */
  409.         /* This code could get run twice but xfree doesn't care */
  410.         for (i = 0; i < fblocks; i++)
  411.             xfree(fbuf[i]);
  412.         xfree(fbuf);
  413.  
  414.         /* Reset input arena */
  415.         copy(&B, &saveB, sizeof B);
  416.  
  417.         close(SHIN), SHIN = oSHIN;
  418.         arginp = oarginp, onelflg = oonelflg;
  419.         intty = oldintty, whyles = oldwhyl, gointr = ogointr;
  420. #ifdef TELL
  421.         cantell = otell;
  422. #endif
  423.     }
  424.  
  425.     resexit(oldexit);
  426.     /*
  427.      * If process reset() (effectively an unwind) then
  428.      * we must also unwind.
  429.      */
  430.     if (reenter >= 2)
  431.         error(0);
  432. }
  433.  
  434. goodbye()
  435. {
  436.  
  437.     if (loginsh) {
  438.         signal(SIGQUIT, SIG_IGN);
  439.         signal(SIGINT, SIG_IGN);
  440.         signal(SIGTERM, SIG_IGN);
  441.         setintr = 0;        /* No interrupts after "logout" */
  442.         if (adrof("home"))
  443.             srccat(value("home"), "/.logout");
  444.     }
  445.     exitstat();
  446. }
  447.  
  448. exitstat()
  449. {
  450.  
  451.     /*
  452.      * Note that if STATUS is corrupted (i.e. getn bombs)
  453.      * then error will exit directly because we poke child here.
  454.      * Otherwise we might continue unwarrantedly (sic).
  455.      */
  456.     child++;
  457.     exit(getn(value("status")));
  458. }
  459.  
  460. /*
  461.  * Catch an interrupt, e.g. during lexical input.
  462.  * If we are an interactive shell, we reset the interrupt catch
  463.  * immediately.  In any case we drain the shell output,
  464.  * and finally go through the normal error mechanism, which
  465.  * gets a chance to make the shell go away.
  466.  */
  467. pintr()
  468. {
  469.  
  470.     if (setintr)
  471.         signal(SIGINT, SIG_IGN);
  472.     draino();
  473.  
  474.     /*
  475.      * If we have an active "onintr" then we search for the label.
  476.      * Note that if one does "onintr -" then we shan't be interruptible
  477.      * so we needn't worry about that here.
  478.      */
  479.     if (gointr) {
  480.         search(ZGOTO, 0, gointr);
  481.         timflg = 0;
  482.         reset();
  483.     } else if (intty)
  484.         printf("\n");        /* Some like this, others don't */
  485.     error(0);
  486. }
  487.  
  488. /*
  489.  * Process is the main driving routine for the shell.
  490.  * It runs all command processing, except for those within { ... }
  491.  * in expressions (which is run by a routine evalav in sh.exp.c which
  492.  * is a stripped down process), and `...` evaluation which is run
  493.  * also by a subset of this code in sh.glob.c in the routine backeval.
  494.  *
  495.  * The code here is a little strange because part of it is interruptible
  496.  * and hence freeing of structures appears to occur when none is necessary
  497.  * if this is ignored.
  498.  *
  499.  * Note that if catch is not set then we will unwind on any error.
  500.  * In an end-of-file occurs, we return.
  501.  */
  502. process(catch)
  503.     bool catch;
  504. {
  505.     register char *cp;
  506.     jmp_buf osetexit;
  507.     struct wordent paraml;
  508.     struct command *t;
  509.  
  510.     getexit(osetexit);
  511.     for (;;) {
  512.         paraml.next = paraml.prev = ¶ml;
  513.         paraml.word = "";
  514.         t = 0;
  515.         setexit();
  516.         justpr = 0;            /* A chance to execute */
  517.  
  518.         /*
  519.          * Interruptible during interactive reads
  520.          */
  521.         if (setintr)
  522.             signal(SIGINT, pintr);
  523.  
  524.         /*
  525.          * For the sake of reset()
  526.          */
  527.         freelex(¶ml), freesyn(t), t = 0;
  528.  
  529.         if (haderr) {
  530.             if (!catch) {
  531.                 /* unwind */
  532.                 doneinp = 0;
  533.                 resexit(osetexit);
  534.                 reset();
  535.             }
  536.             haderr = 0;
  537.             /*
  538.              * Every error is eventually caught here or
  539.              * the shell dies.  It is at this
  540.              * point that we clean up any left-over open
  541.              * files, by closing all but a fixed number
  542.              * of pre-defined files.  Thus routines don't
  543.              * have to worry about leaving files open due
  544.              * to deeper errors... they will get closed here.
  545.              */
  546.             closem();
  547.             continue;
  548.         }
  549.         if (doneinp) {
  550.             doneinp = 0;
  551.             break;
  552.         }
  553.         if (intty) {
  554.             mailchk();
  555.             /*
  556.              * If we are at the end of the input buffer
  557.              * then we are going to read fresh stuff.
  558.              * Otherwise, we are rereading input and don't
  559.              * need or want to prompt.
  560.              */
  561.             if (fseekp == feobp)
  562.                 if (!whyles)
  563.                     for (cp = value("prompt"); *cp; cp++)
  564.                         if (*cp == '!')
  565.                             printf("%d", eventno + 1);
  566.                         else {
  567.                             if (*cp == '\\' && cp[1] == '!')
  568.                                 cp++;
  569.                             putchar(*cp | QUOTE);
  570.                         }
  571.                 else
  572.                     /*
  573.                      * Prompt for forward reading loop
  574.                      * body content.
  575.                      */
  576.                     printf("? ");
  577.             flush();
  578.         }
  579.         err = 0;
  580.  
  581.         /*
  582.          * Echo not only on VERBOSE, but also with history expansion.
  583.          * If there is a lexical error then we forego history echo.
  584.          */
  585.         if (lex(¶ml) && !err && intty || adrof("verbose")) {
  586.             haderr = 1;
  587.             prlex(¶ml);
  588.             haderr = 0;
  589.         }
  590.  
  591.         /*
  592.          * The parser may lose space if interrupted.
  593.          */
  594.         if (setintr)
  595.             signal(SIGINT, SIG_IGN);
  596.  
  597.         /*
  598.          * Save input text on the history list if it
  599.          * is from the terminal at the top level and not
  600.          * in a loop.
  601.          */
  602.         if (catch && intty && !whyles)
  603.             savehist(¶ml);
  604.  
  605.         /*
  606.          * Print lexical error messages.
  607.          */
  608.         if (err)
  609.             error(err);
  610.  
  611.         /*
  612.          * If had a history command :p modifier then
  613.          * this is as far as we should go
  614.          */
  615.         if (justpr)
  616.             reset();
  617.  
  618.         alias(¶ml);
  619.  
  620.         /*
  621.          * Parse the words of the input into a parse tree.
  622.          */
  623.         t = syntax(paraml.next, ¶ml);
  624.         if (err)
  625.             error(err);
  626.  
  627.         /*
  628.          * Execute the parse tree
  629.          */
  630.         execute(t);
  631.  
  632.         /*
  633.          * Made it!
  634.          */
  635.         freelex(¶ml), freesyn(t);
  636.     }
  637.     resexit(osetexit);
  638. }
  639.  
  640. dosource(t)
  641.     register char **t;
  642. {
  643.     register char *f;
  644.     register int u;
  645.  
  646.     t++;
  647.     f = globone(*t);
  648.     u = dmove(open(f, 0), -1);
  649.     xfree(f);
  650.     if (u < 0)
  651.         Perror(f);
  652.     srcunit(u, 0);
  653. }
  654.  
  655. /*
  656.  * Check for mail.
  657.  * If we are a login shell, then we don't want to tell
  658.  * about any mail file unless its been modified
  659.  * after the time we started.
  660.  * This prevents us from telling the user things he already
  661.  * knows, since the login program insist on saying
  662.  * "You have mail."
  663.  */
  664. mailchk()
  665. {
  666.     register struct varent *v;
  667.     register char **vp;
  668.     time_t t;
  669.     int intvl, cnt;
  670.  
  671.     v = adrof("mail");
  672.     if (v == 0)
  673.         return;
  674.     time(&t);
  675.     vp = v->vec;
  676.     cnt = blklen(vp);
  677.     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
  678.     if (intvl < 1)
  679.         intvl = 1;
  680.     if (chktim + intvl > t)
  681.         return;
  682.     for (; *vp; vp++) {
  683.         bool new;
  684.         struct stat stb;
  685.  
  686.         if (stat(*vp, &stb) < 0)
  687.             continue;
  688.         /*
  689.          * We assume that a file has been read if the access time is
  690.          * greater than the mod time.
  691.          */
  692. #ifndef CORY
  693.         if (stb.st_size == 0)
  694.             continue;
  695. #endif
  696.         if (stb.st_atime > stb.st_mtime || stb.st_atime < chktim)
  697.             continue;
  698.         new = stb.st_mtime > time0;
  699.         if (loginsh && !new)
  700.             continue;
  701.         if (cnt == 1)
  702.             printf("You have %smail.\n", new ? "new " : "");
  703.         else
  704.             printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
  705.     }
  706.     chktim = t;
  707. }
  708.  
  709. #include <pwd.h>
  710. /*
  711.  * Extract a home directory from the password file
  712.  * The argument points to a buffer where the name of the
  713.  * user whose home directory is sought is currently.
  714.  * We write the home directory of the user back there.
  715.  */
  716. gethdir(home)
  717.     char *home;
  718. {
  719.     register struct passwd *pp = getpwnam(home);
  720.  
  721.     if (pp == 0)
  722.         return (1);
  723.     strcpy(home, pp->pw_dir);
  724.     return (0);
  725. }
  726.  
  727. /*
  728.  * Move the initial descriptors to their eventual
  729.  * resting places, closin all other units.
  730.  */
  731. initdesc()
  732. {
  733.  
  734.     didcch = 0;            /* Havent closed for child */
  735.     didfds = 0;            /* 0, 1, 2 aren't set up */
  736.     SHIN = dcopy(0, FSHIN);
  737.     SHOUT = dcopy(1, FSHOUT);
  738.     SHDIAG = dcopy(2, FSHDIAG);
  739.     OLDSTD = dcopy(SHIN, FOLDSTD);
  740.     closem();
  741. }
  742.  
  743. #ifndef V6
  744. exit(i)
  745.     int i;
  746. {
  747.  
  748.     _exit(i);
  749. }
  750. #endif
  751.