home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / ash / part03 < prev    next >
Encoding:
Internet Message Format  |  1989-05-29  |  48.9 KB

  1. Subject:  v19i003:  A reimplementation of the System V shell, Part03/08
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ka@june.cs.washington.edu (Kenneth Almquist)
  7. Posting-number: Volume 19, Issue 3
  8. Archive-name: ash/part03
  9.  
  10. # This is part 3 of ash.  To unpack, feed it into the shell (not csh).
  11. # The ash distribution consists of eight pieces.  Be sure you get them all.
  12. # After you unpack everything, read the file README.
  13.  
  14. echo extracting exec.c
  15. cat > exec.c <<\EOF
  16. /*
  17.  * When commands are first encountered, they are entered in a hash table.
  18.  * This ensures that a full path search will not have to be done for them
  19.  * on each invocation.
  20.  *
  21.  * We should investigate converting to a linear search, even though that
  22.  * would make the command name "hash" a misnomer.
  23.  */
  24.  
  25. #include "shell.h"
  26. #include "main.h"
  27. #include "nodes.h"
  28. #include "parser.h"
  29. #include "redir.h"
  30. #include "eval.h"
  31. #include "exec.h"
  32. #include "builtins.h"
  33. #include "var.h"
  34. #include "options.h"
  35. #include "input.h"
  36. #include "output.h"
  37. #include "syntax.h"
  38. #include "memalloc.h"
  39. #include "error.h"
  40. #include "init.h"
  41. #include "mystring.h"
  42. #include <sys/types.h>
  43. #include <sys/stat.h>
  44. #include <fcntl.h>
  45. #include "myerrno.h"
  46.  
  47.  
  48. #define CMDTABLESIZE 31        /* should be prime */
  49. #define ARB 1            /* actual size determined at run time */
  50.  
  51.  
  52.  
  53. struct tblentry {
  54.       struct tblentry *next;    /* next entry in hash chain */
  55.       union param param;    /* definition of builtin function */
  56.       short cmdtype;        /* index identifying command */
  57.       char rehash;        /* if set, cd done since entry created */
  58.       char cmdname[ARB];    /* name of command */
  59. };
  60.  
  61.  
  62. STATIC struct tblentry *cmdtable[CMDTABLESIZE];
  63. STATIC int builtinloc;        /* index in path of %builtin, or -1 */
  64.  
  65.  
  66. #ifdef __STDC__
  67. STATIC void tryexec(char *, char **, char **);
  68. STATIC void execinterp(char **, char **);
  69. STATIC void printentry(struct tblentry *);
  70. STATIC void clearcmdentry(int);
  71. STATIC struct tblentry *cmdlookup(char *, int);
  72. STATIC void delete_cmd_entry(void);
  73. #else
  74. STATIC void tryexec();
  75. STATIC void execinterp();
  76. STATIC void printentry();
  77. STATIC void clearcmdentry();
  78. STATIC struct tblentry *cmdlookup();
  79. STATIC void delete_cmd_entry();
  80. #endif
  81.  
  82.  
  83.  
  84. /*
  85.  * Exec a program.  Never returns.  If you change this routine, you may
  86.  * have to change the find_command routine as well.
  87.  */
  88.  
  89. void
  90. shellexec(argv, envp, path, index)
  91.       char **argv, **envp;
  92.       char *path;
  93.       {
  94.       char *cmdname;
  95.       int e;
  96.  
  97.       if (strchr(argv[0], '/') != NULL) {
  98.         tryexec(argv[0], argv, envp);
  99.         e = errno;
  100.       } else {
  101.         e = ENOENT;
  102.         while ((cmdname = padvance(&path, argv[0])) != NULL) {
  103.           if (--index < 0 && pathopt == NULL) {
  104.             tryexec(cmdname, argv, envp);
  105.             if (errno != ENOENT && errno != ENOTDIR)
  106.                   e = errno;
  107.           }
  108.           stunalloc(cmdname);
  109.         }
  110.       }
  111.       error2(argv[0], errmsg(e, E_EXEC));
  112. }
  113.  
  114.  
  115. STATIC void
  116. tryexec(cmd, argv, envp)
  117.       char *cmd;
  118.       char **argv;
  119.       char **envp;
  120.       {
  121.       int e;
  122.       char *p;
  123.  
  124. #ifdef SYSV
  125.       do {
  126.         execve(cmd, argv, envp);
  127.       } while (errno == EINTR);
  128. #else
  129.       execve(cmd, argv, envp);
  130. #endif
  131.       e = errno;
  132.       if (e == ENOEXEC) {
  133.         initshellproc();
  134.         setinputfile(cmd, 0);
  135.         commandname = arg0 = savestr(argv[0]);
  136. #ifndef BSD
  137.         pgetc(); pungetc();        /* fill up input buffer */
  138.         p = parsenextc;
  139.         if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
  140.           argv[0] = cmd;
  141.           execinterp(argv, envp);
  142.         }
  143. #endif
  144.         setparam(argv + 1);
  145.         raise(EXSHELLPROC);
  146.         /*NOTREACHED*/
  147.       }
  148.       errno = e;
  149. }
  150.  
  151.  
  152. #ifndef BSD
  153. /*
  154.  * Execute an interpreter introduced by "#!", for systems where this
  155.  * feature has not been built into the kernel.  If the interpreter is
  156.  * the shell, return (effectively ignoring the "#!").  If the execution
  157.  * of the interpreter fails, exit.
  158.  *
  159.  * This code peeks inside the input buffer in order to avoid actually
  160.  * reading any input.  It would benefit from a rewrite.
  161.  */
  162.  
  163. #define NEWARGS 5
  164.  
  165. STATIC void
  166. execinterp(argv, envp)
  167.       char **argv, **envp;
  168.       {
  169.       int n;
  170.       char *inp;
  171.       char *outp;
  172.       char c;
  173.       char *p;
  174.       char **ap;
  175.       char *newargs[NEWARGS];
  176.       int i;
  177.       char **ap2;
  178.       char **new;
  179.  
  180.       n = parsenleft - 2;
  181.       inp = parsenextc + 2;
  182.       ap = newargs;
  183.       for (;;) {
  184.         while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
  185.           inp++;
  186.         if (n < 0)
  187.           goto bad;
  188.         if ((c = *inp++) == '\n')
  189.           break;
  190.         if (ap == &newargs[NEWARGS])
  191. bad:          error("Bad #! line");
  192.         STARTSTACKSTR(outp);
  193.         do {
  194.           STPUTC(c, outp);
  195.         } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
  196.         STPUTC('\0', outp);
  197.         n++, inp--;
  198.         *ap++ = grabstackstr(outp);
  199.       }
  200.       if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
  201.         p = newargs[0];
  202.         for (;;) {
  203.           if (equal(p, "sh") || equal(p, "ash")) {
  204.             return;
  205.           }
  206.           while (*p != '/') {
  207.             if (*p == '\0')
  208.                   goto break2;
  209.             p++;
  210.           }
  211.           p++;
  212.         }
  213. break2:;
  214.       }
  215.       i = (char *)ap - (char *)newargs;        /* size in bytes */
  216.       if (i == 0)
  217.         error("Bad #! line");
  218.       for (ap2 = argv ; *ap2++ != NULL ; );
  219.       new = ckmalloc(i + ((char *)ap2 - (char *)argv));
  220.       ap = newargs, ap2 = new;
  221.       while ((i -= sizeof (char **)) >= 0)
  222.         *ap2++ = *ap++;
  223.       ap = argv;
  224.       while (*ap2++ = *ap++);
  225.       shellexec(new, envp, pathval(), 0);
  226. }
  227. #endif
  228.  
  229.  
  230.  
  231. /*
  232.  * Do a path search.  The variable path (passed by reference) should be
  233.  * set to the start of the path before the first call; padvance will update
  234.  * this value as it proceeds.  Successive calls to padvance will return
  235.  * the possible path expansions in sequence.  If an option (indicated by
  236.  * a percent sign) appears in the path entry then the global variable
  237.  * pathopt will be set to point to it; otherwise pathopt will be set to
  238.  * NULL.
  239.  */
  240.  
  241. char *pathopt;
  242.  
  243. char *
  244. padvance(path, name)
  245.       char **path;
  246.       char *name;
  247.       {
  248.       register char *p, *q;
  249.       char *start;
  250.       int len;
  251.  
  252.       if (*path == NULL)
  253.         return NULL;
  254.       start = *path;
  255.       for (p = start ; *p && *p != ':' && *p != '%' ; p++);
  256.       len = p - start + strlen(name) + 2;    /* "2" is for '/' and '\0' */
  257.       while (stackblocksize() < len)
  258.         growstackblock();
  259.       q = stackblock();
  260.       if (p != start) {
  261.         bcopy(start, q, p - start);
  262.         q += p - start;
  263.         *q++ = '/';
  264.       }
  265.       strcpy(q, name);
  266.       pathopt = NULL;
  267.       if (*p == '%') {
  268.         pathopt = ++p;
  269.         while (*p && *p != ':')  p++;
  270.       }
  271.       if (*p == ':')
  272.         *path = p + 1;
  273.       else
  274.         *path = NULL;
  275.       return stalloc(len);
  276. }
  277.  
  278.  
  279.  
  280. /*** Command hashing code ***/
  281.  
  282.  
  283. hashcmd(argc, argv)  char **argv; {
  284.       struct tblentry **pp;
  285.       struct tblentry *cmdp;
  286.       int c;
  287.       int verbose;
  288.       struct cmdentry entry;
  289.       char *name;
  290.  
  291.       if (argc <= 1) {
  292.         for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  293.           for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  294.             printentry(cmdp);
  295.           }
  296.         }
  297.         return 0;
  298.       }
  299.       verbose = 0;
  300.       while ((c = nextopt("rv")) != '\0') {
  301.         if (c == 'r') {
  302.           clearcmdentry(0);
  303.         } else if (c == 'v') {
  304.           verbose++;
  305.         }
  306.       }
  307.       while ((name = *argptr) != NULL) {
  308.         if ((cmdp = cmdlookup(name, 0)) != NULL
  309.          && (cmdp->cmdtype == CMDNORMAL
  310.         || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
  311.           delete_cmd_entry();
  312.         find_command(name, &entry, 1);
  313.         if (verbose) {
  314.           if (entry.cmdtype != CMDUNKNOWN) {    /* if no error msg */
  315.             cmdp = cmdlookup(name, 0);
  316.             printentry(cmdp);
  317.           }
  318.           flushall();
  319.         }
  320.         argptr++;
  321.       }
  322.       return 0;
  323. }
  324.  
  325.  
  326. STATIC void
  327. printentry(cmdp)
  328.       struct tblentry *cmdp;
  329.       {
  330.       int index;
  331.       char *path;
  332.       char *name;
  333.  
  334.       if (cmdp->cmdtype == CMDNORMAL) {
  335.         index = cmdp->param.index;
  336.         path = pathval();
  337.         do {
  338.           name = padvance(&path, cmdp->cmdname);
  339.           stunalloc(name);
  340.         } while (--index >= 0);
  341.         out1str(name);
  342.       } else if (cmdp->cmdtype == CMDBUILTIN) {
  343.         out1fmt("builtin %s", cmdp->cmdname);
  344.       } else if (cmdp->cmdtype == CMDFUNCTION) {
  345.         out1fmt("function %s", cmdp->cmdname);
  346. #ifdef DEBUG
  347.       } else {
  348.         error("internal error: cmdtype %d", cmdp->cmdtype);
  349. #endif
  350.       }
  351.       if (cmdp->rehash)
  352.         out1c('*');
  353.       out1c('\n');
  354. }
  355.  
  356.  
  357.  
  358. /*
  359.  * Resolve a command name.  If you change this routine, you may have to
  360.  * change the shellexec routine as well.
  361.  */
  362.  
  363. void
  364. find_command(name, entry, printerr)
  365.       char *name;
  366.       struct cmdentry *entry;
  367.       {
  368.       struct tblentry *cmdp;
  369.       int index;
  370.       int prev;
  371.       char *path;
  372.       char *fullname;
  373.       struct stat statb;
  374.       int e;
  375.       int i;
  376.  
  377.       /* If name contains a slash, don't use the hash table */
  378.       if (strchr(name, '/') != NULL) {
  379.         entry->cmdtype = CMDNORMAL;
  380.         entry->u.index = 0;
  381.         return;
  382.       }
  383.  
  384.       /* If name is in the table, and not invalidated by cd, we're done */
  385.       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
  386.         goto success;
  387.  
  388.       /* If %builtin not in path, check for builtin next */
  389.       if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
  390.         INTOFF;
  391.         cmdp = cmdlookup(name, 1);
  392.         cmdp->cmdtype = CMDBUILTIN;
  393.         cmdp->param.index = i;
  394.         INTON;
  395.         goto success;
  396.       }
  397.  
  398.       /* We have to search path. */
  399.       prev = -1;        /* where to start */
  400.       if (cmdp) {        /* doing a rehash */
  401.         if (cmdp->cmdtype == CMDBUILTIN)
  402.           prev = builtinloc;
  403.         else
  404.           prev = cmdp->param.index;
  405.       }
  406.  
  407.       path = pathval();
  408.       e = ENOENT;
  409.       index = -1;
  410. loop:
  411.       while ((fullname = padvance(&path, name)) != NULL) {
  412.         stunalloc(fullname);
  413.         index++;
  414.         if (pathopt) {
  415.           if (prefix("builtin", pathopt)) {
  416.             if ((i = find_builtin(name)) < 0)
  417.                   goto loop;
  418.             INTOFF;
  419.             cmdp = cmdlookup(name, 1);
  420.             cmdp->cmdtype = CMDBUILTIN;
  421.             cmdp->param.index = i;
  422.             INTON;
  423.             goto success;
  424.           } else if (prefix("func", pathopt)) {
  425.             /* handled below */
  426.           } else {
  427.             goto loop;    /* ignore unimplemented options */
  428.           }
  429.         }
  430.         /* if rehash, don't redo absolute path names */
  431.         if (fullname[0] == '/' && index <= prev) {
  432.           if (index < prev)
  433.             goto loop;
  434.           TRACE(("searchexec \"%s\": no change\n", name));
  435.           goto success;
  436.         }
  437.         while (stat(fullname, &statb) < 0) {
  438. #ifdef SYSV
  439.           if (errno == EINTR)
  440.             continue;
  441. #endif
  442.           if (errno != ENOENT && errno != ENOTDIR)
  443.             e = errno;
  444.           goto loop;
  445.         }
  446.         e = EACCES;    /* if we fail, this will be the error */
  447.         if ((statb.st_mode & S_IFMT) != S_IFREG)
  448.           goto loop;
  449.         if (pathopt) {        /* this is a %func directory */
  450.           stalloc(strlen(fullname) + 1);
  451.           readcmdfile(fullname);
  452.           if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
  453.             error("%s not defined in %s", name, fullname);
  454.           stunalloc(fullname);
  455.           goto success;
  456.         }
  457.         if (statb.st_uid == geteuid()) {
  458.           if ((statb.st_mode & 0100) == 0)
  459.             goto loop;
  460.         } else if (statb.st_gid == getegid()) {
  461.           if ((statb.st_mode & 010) == 0)
  462.             goto loop;
  463.         } else {
  464.           if ((statb.st_mode & 01) == 0)
  465.             goto loop;
  466.         }
  467.         TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
  468.         INTOFF;
  469.         cmdp = cmdlookup(name, 1);
  470.         cmdp->cmdtype = CMDNORMAL;
  471.         cmdp->param.index = index;
  472.         INTON;
  473.         goto success;
  474.       }
  475.  
  476.       /* We failed.  If there was an entry for this command, delete it */
  477.       if (cmdp)
  478.         delete_cmd_entry();
  479.       if (printerr)
  480.         outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
  481.       entry->cmdtype = CMDUNKNOWN;
  482.       return;
  483.  
  484. success:
  485.       cmdp->rehash = 0;
  486.       entry->cmdtype = cmdp->cmdtype;
  487.       entry->u = cmdp->param;
  488. }
  489.  
  490.  
  491.  
  492. /*
  493.  * Search the table of builtin commands.
  494.  */
  495.  
  496. int
  497. find_builtin(name)
  498.       char *name;
  499.       {
  500.       const register struct builtincmd *bp;
  501.  
  502.       for (bp = builtincmd ; bp->name ; bp++) {
  503.         if (*bp->name == *name && equal(bp->name, name))
  504.           return bp->code;
  505.       }
  506.       return -1;
  507. }
  508.  
  509.  
  510.  
  511. /*
  512.  * Called when a cd is done.  Marks all commands so the next time they
  513.  * are executed they will be rehashed.
  514.  */
  515.  
  516. void
  517. hashcd() {
  518.       struct tblentry **pp;
  519.       struct tblentry *cmdp;
  520.  
  521.       for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  522.         for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  523.           if (cmdp->cmdtype == CMDNORMAL
  524.            || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
  525.             cmdp->rehash = 1;
  526.         }
  527.       }
  528. }
  529.  
  530.  
  531.  
  532. /*
  533.  * Called before PATH is changed.  The argument is the new value of PATH;
  534.  * pathval() still returns the old value at this point.  Called with
  535.  * interrupts off.
  536.  */
  537.  
  538. void
  539. changepath(newval)
  540.       char *newval;
  541.       {
  542.       char *old, *new;
  543.       int index;
  544.       int firstchange;
  545.       int bltin;
  546.  
  547.       old = pathval();
  548.       new = newval;
  549.       firstchange = 9999;    /* assume no change */
  550.       index = 0;
  551.       bltin = -1;
  552.       for (;;) {
  553.         if (*old != *new) {
  554.           firstchange = index;
  555.           if (*old == '\0' && *new == ':'
  556.            || *old == ':' && *new == '\0')
  557.             firstchange++;
  558.           old = new;    /* ignore subsequent differences */
  559.         }
  560.         if (*new == '\0')
  561.           break;
  562.         if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
  563.           bltin = index;
  564.         if (*new == ':') {
  565.           index++;
  566.         }
  567.         new++, old++;
  568.       }
  569.       if (builtinloc < 0 && bltin >= 0)
  570.         builtinloc = bltin;        /* zap builtins */
  571.       if (builtinloc >= 0 && bltin < 0)
  572.         firstchange = 0;
  573.       clearcmdentry(firstchange);
  574.       builtinloc = bltin;
  575. }
  576.  
  577.  
  578. /*
  579.  * Clear out command entries.  The argument specifies the first entry in
  580.  * PATH which has changed.
  581.  */
  582.  
  583. STATIC void
  584. clearcmdentry(firstchange) {
  585.       struct tblentry **tblp;
  586.       struct tblentry **pp;
  587.       struct tblentry *cmdp;
  588.  
  589.       INTOFF;
  590.       for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  591.         pp = tblp;
  592.         while ((cmdp = *pp) != NULL) {
  593.           if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
  594.            || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
  595.             *pp = cmdp->next;
  596.             ckfree(cmdp);
  597.           } else {
  598.             pp = &cmdp->next;
  599.           }
  600.         }
  601.       }
  602.       INTON;
  603. }
  604.  
  605.  
  606. /*
  607.  * Delete all functions.
  608.  */
  609.  
  610. #ifdef mkinit
  611. MKINIT void deletefuncs();
  612.  
  613. SHELLPROC {
  614.       deletefuncs();
  615. }
  616. #endif
  617.  
  618. void
  619. deletefuncs() {
  620.       struct tblentry **tblp;
  621.       struct tblentry **pp;
  622.       struct tblentry *cmdp;
  623.  
  624.       INTOFF;
  625.       for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  626.         pp = tblp;
  627.         while ((cmdp = *pp) != NULL) {
  628.           if (cmdp->cmdtype == CMDFUNCTION) {
  629.             *pp = cmdp->next;
  630.             freefunc(cmdp->param.func);
  631.             ckfree(cmdp);
  632.           } else {
  633.             pp = &cmdp->next;
  634.           }
  635.         }
  636.       }
  637.       INTON;
  638. }
  639.  
  640.  
  641.  
  642. /*
  643.  * Locate a command in the command hash table.  If "add" is nonzero,
  644.  * add the command to the table if it is not already present.  The
  645.  * variable "lastcmdentry" is set to point to the address of the link
  646.  * pointing to the entry, so that delete_cmd_entry can delete the
  647.  * entry.
  648.  */
  649.  
  650. struct tblentry **lastcmdentry;
  651.  
  652.  
  653. STATIC struct tblentry *
  654. cmdlookup(name, add)
  655.       char *name;
  656.       {
  657.       int hashval;
  658.       register char *p;
  659.       struct tblentry *cmdp;
  660.       struct tblentry **pp;
  661.  
  662.       p = name;
  663.       hashval = *p << 4;
  664.       while (*p)
  665.         hashval += *p++;
  666.       hashval &= 0x7FFF;
  667.       pp = &cmdtable[hashval % CMDTABLESIZE];
  668.       for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  669.         if (equal(cmdp->cmdname, name))
  670.           break;
  671.         pp = &cmdp->next;
  672.       }
  673.       if (add && cmdp == NULL) {
  674.         INTOFF;
  675.         cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
  676.                   + strlen(name) + 1);
  677.         cmdp->next = NULL;
  678.         cmdp->cmdtype = CMDUNKNOWN;
  679.         cmdp->rehash = 0;
  680.         strcpy(cmdp->cmdname, name);
  681.         INTON;
  682.       }
  683.       lastcmdentry = pp;
  684.       return cmdp;
  685. }
  686.  
  687.  
  688. /*
  689.  * Delete the command entry returned on the last lookup.
  690.  */
  691.  
  692. STATIC void
  693. delete_cmd_entry() {
  694.       struct tblentry *cmdp;
  695.  
  696.       INTOFF;
  697.       cmdp = *lastcmdentry;
  698.       *lastcmdentry = cmdp->next;
  699.       ckfree(cmdp);
  700.       INTON;
  701. }
  702.  
  703.  
  704.  
  705. #ifdef notdef
  706. void
  707. getcmdentry(name, entry)
  708.       char *name;
  709.       struct cmdentry *entry; 
  710.       {
  711.       struct tblentry *cmdp = cmdlookup(name, 0);
  712.  
  713.       if (cmdp) {
  714.         entry->u = cmdp->param;
  715.         entry->cmdtype = cmdp->cmdtype;
  716.       } else {
  717.         entry->cmdtype = CMDUNKNOWN;
  718.         entry->u.index = 0;
  719.       }
  720. }
  721. #endif
  722.  
  723.  
  724. /*
  725.  * Add a new command entry, replacing any existing command entry for
  726.  * the same name.
  727.  */
  728.  
  729. void
  730. addcmdentry(name, entry)
  731.       char *name;
  732.       struct cmdentry *entry;
  733.       {
  734.       struct tblentry *cmdp;
  735.  
  736.       INTOFF;
  737.       cmdp = cmdlookup(name, 1);
  738.       if (cmdp->cmdtype == CMDFUNCTION) {
  739.         freefunc(cmdp->param.func);
  740.       }
  741.       cmdp->cmdtype = entry->cmdtype;
  742.       cmdp->param = entry->u;
  743.       INTON;
  744. }
  745.  
  746.  
  747. /*
  748.  * Define a shell function.
  749.  */
  750.  
  751. void
  752. defun(name, func)
  753.       char *name;
  754.       union node *func;
  755.       {
  756.       struct cmdentry entry;
  757.  
  758.       INTOFF;
  759.       entry.cmdtype = CMDFUNCTION;
  760.       entry.u.func = copyfunc(func);
  761.       addcmdentry(name, &entry);
  762.       INTON;
  763. }
  764.  
  765.  
  766. /*
  767.  * Delete a function if it exists.
  768.  */
  769.  
  770. void
  771. unsetfunc(name)
  772.       char *name;
  773.       {
  774.       struct tblentry *cmdp;
  775.  
  776.       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
  777.         freefunc(cmdp->param.func);
  778.         delete_cmd_entry();
  779.       }
  780. }
  781. EOF
  782. if test `wc -c < exec.c` -ne 16808
  783. then    echo 'exec.c is the wrong size'
  784. fi
  785. echo extracting expand.h
  786. cat > expand.h <<\EOF
  787. /*
  788.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  789.  * This file is part of ash, which is distributed under the terms specified
  790.  * by the Ash General Public License.  See the file named LICENSE.
  791.  */
  792.  
  793. struct strlist {
  794.       struct strlist *next;
  795.       char *text;
  796. };
  797.  
  798.  
  799. struct arglist {
  800.       struct strlist *list;
  801.       struct strlist **lastp;
  802. };
  803.  
  804.  
  805.  
  806. #ifdef __STDC__
  807. void expandarg(union node *, struct arglist *, int);
  808. void expandhere(union node *, int);
  809. int patmatch(char *, char *);
  810. void rmescapes(char *);
  811. int casematch(union node *, char *);
  812. #else
  813. void expandarg();
  814. void expandhere();
  815. int patmatch();
  816. void rmescapes();
  817. int casematch();
  818. #endif
  819. EOF
  820. if test `wc -c < expand.h` -ne 662
  821. then    echo 'expand.h is the wrong size'
  822. fi
  823. echo extracting expand.c
  824. cat > expand.c <<\EOF
  825. /*
  826.  * Routines to expand arguments to commands.  We have to deal with
  827.  * backquotes, shell variables, and file metacharacters.
  828.  *
  829.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  830.  * This file is part of ash, which is distributed under the terms specified
  831.  * by the Ash General Public License.  See the file named LICENSE.
  832.  */
  833.  
  834.  
  835. #include "shell.h"
  836. #include "main.h"
  837. #include "nodes.h"
  838. #include "eval.h"
  839. #include "expand.h"
  840. #include "syntax.h"
  841. #include "parser.h"
  842. #include "jobs.h"
  843. #include "options.h"
  844. #include "var.h"
  845. #include "input.h"
  846. #include "output.h"
  847. #include "memalloc.h"
  848. #include "error.h"
  849. #include "mystring.h"
  850. #include <sys/types.h>
  851. #include <sys/stat.h>
  852. #include "mydirent.h"
  853. #include "myerrno.h"
  854.  
  855.  
  856.  
  857. /*
  858.  * Structure specifying which parts of the string should be searched
  859.  * for IFS characters.
  860.  */
  861.  
  862. struct ifsregion {
  863.       struct ifsregion *next;    /* next region in list */
  864.       int begoff;        /* offset of start of region */
  865.       int endoff;        /* offset of end of region */
  866.       int nulonly;        /* search for nul bytes only */
  867. };
  868.  
  869.  
  870. char *expdest;            /* output of current string */
  871. struct nodelist *argbackq;    /* list of back quote expressions */
  872. struct ifsregion ifsfirst;    /* first struct in list of ifs regions */
  873. struct ifsregion *ifslastp;    /* last struct in list */
  874. struct arglist exparg;        /* holds expanded arg list */
  875. #if UDIR
  876. /*
  877.  * Set if the last argument processed had /u/logname expanded.  This
  878.  * variable is read by the cd command.
  879.  */
  880. int didudir;
  881. #endif
  882.  
  883. #ifdef __STDC__
  884. STATIC void argstr(char *, int);
  885. STATIC void expbackq(union node *, int, int);
  886. STATIC char *evalvar(char *, int);
  887. STATIC int varisset(int);
  888. STATIC void varvalue(int, int, int);
  889. STATIC void recordregion(int, int, int);
  890. STATIC void ifsbreakup(char *, struct arglist *);
  891. STATIC void expandmeta(struct strlist *);
  892. STATIC void expmeta(char *, char *);
  893. STATIC void addfname(char *);
  894. STATIC struct strlist *expsort(struct strlist *);
  895. STATIC struct strlist *msort(struct strlist *, int);
  896. STATIC int pmatch(char *, char *);
  897. #else
  898. STATIC void argstr();
  899. STATIC void expbackq();
  900. STATIC char *evalvar();
  901. STATIC int varisset();
  902. STATIC void varvalue();
  903. STATIC void recordregion();
  904. STATIC void ifsbreakup();
  905. STATIC void expandmeta();
  906. STATIC void expmeta();
  907. STATIC void addfname();
  908. STATIC struct strlist *expsort();
  909. STATIC struct strlist *msort();
  910. STATIC int pmatch();
  911. #endif
  912. #if UDIR
  913. #ifdef __STDC__
  914. STATIC char *expudir(char *);
  915. #else
  916. STATIC char *expudir();
  917. #endif
  918. #endif /* UDIR */
  919.  
  920.  
  921.  
  922. /*
  923.  * Expand shell variables and backquotes inside a here document.
  924.  */
  925.  
  926. void
  927. expandhere(arg, fd)
  928.       union node *arg;    /* the document */
  929.       int fd;            /* where to write the expanded version */
  930.       {
  931.       herefd = fd;
  932.       expandarg(arg, (struct arglist *)NULL, 0);
  933.       xwrite(fd, stackblock(), expdest - stackblock());
  934. }
  935.  
  936.  
  937. /*
  938.  * Perform variable substitution and command substitution on an argument,
  939.  * placing the resulting list of arguments in arglist.  If full is true,
  940.  * perform splitting and file name expansion.  When arglist is NULL, perform
  941.  * here document expansion.
  942.  */
  943.  
  944. void
  945. expandarg(arg, arglist, full)
  946.       union node *arg;
  947.       struct arglist *arglist;
  948.       {
  949.       struct strlist *sp;
  950.       char *p;
  951.  
  952. #if UDIR
  953.       didudir = 0;
  954. #endif
  955.       argbackq = arg->narg.backquote;
  956.       STARTSTACKSTR(expdest);
  957.       ifsfirst.next = NULL;
  958.       ifslastp = NULL;
  959.       argstr(arg->narg.text, full);
  960.       if (arglist == NULL)
  961.         return;            /* here document expanded */
  962.       STPUTC('\0', expdest);
  963.       p = grabstackstr(expdest);
  964.       exparg.lastp = &exparg.list;
  965.       if (full) {
  966.         ifsbreakup(p, &exparg);
  967.         *exparg.lastp = NULL;
  968.         exparg.lastp = &exparg.list;
  969.         expandmeta(exparg.list);
  970.       } else {
  971.         sp = (struct strlist *)stalloc(sizeof (struct strlist));
  972.         sp->text = p;
  973.         *exparg.lastp = sp;
  974.         exparg.lastp = &sp->next;
  975.       }
  976.       while (ifsfirst.next != NULL) {
  977.         struct ifsregion *ifsp;
  978.         INTOFF;
  979.         ifsp = ifsfirst.next->next;
  980.         ckfree(ifsfirst.next);
  981.         ifsfirst.next = ifsp;
  982.         INTON;
  983.       }
  984.       *exparg.lastp = NULL;
  985.       if (exparg.list) {
  986.         *arglist->lastp = exparg.list;
  987.         arglist->lastp = exparg.lastp;
  988.       }
  989. }
  990.  
  991.  
  992.  
  993. /*
  994.  * Perform variable and command substitution.  If full is set, output CTLESC
  995.  * characters to allow for further processing.  If full is not set, treat
  996.  * $@ like $* since no splitting will be performed.
  997.  */
  998.  
  999. STATIC void
  1000. argstr(p, full)
  1001.       register char *p;
  1002.       {
  1003.       char c;
  1004.  
  1005.       for (;;) {
  1006.         switch (c = *p++) {
  1007.         case '\0':
  1008.         case CTLENDVAR:
  1009.           goto breakloop;
  1010.         case CTLESC:
  1011.           if (full)
  1012.             STPUTC(c, expdest);
  1013.           c = *p++;
  1014.           STPUTC(c, expdest);
  1015.           break;
  1016.         case CTLVAR:
  1017.           p = evalvar(p, full);
  1018.           break;
  1019.         case CTLBACKQ:
  1020.         case CTLBACKQ|CTLQUOTE:
  1021.           expbackq(argbackq->n, c & CTLQUOTE, full);
  1022.           argbackq = argbackq->next;
  1023.           break;
  1024.         default:
  1025.           STPUTC(c, expdest);
  1026.         }
  1027.       }
  1028. breakloop:;
  1029. }
  1030.  
  1031.  
  1032. /*
  1033.  * Expand stuff in backwards quotes.
  1034.  */
  1035.  
  1036. STATIC void
  1037. expbackq(cmd, quoted, full)
  1038.       union node *cmd;
  1039.       {
  1040.       struct backcmd in;
  1041.       int i;
  1042.       char buf[128];
  1043.       char *p;
  1044.       char *dest = expdest;
  1045.       struct ifsregion saveifs, *savelastp;
  1046.       struct nodelist *saveargbackq;
  1047.       char lastc;
  1048.       int startloc = dest - stackblock();
  1049.       char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
  1050.       int saveherefd;
  1051.  
  1052.       INTOFF;
  1053.       saveifs = ifsfirst;
  1054.       savelastp = ifslastp;
  1055.       saveargbackq = argbackq;
  1056.       saveherefd = herefd;      
  1057.       herefd = -1;
  1058.       p = grabstackstr(dest);
  1059.       evalbackcmd(cmd, &in);
  1060.       ungrabstackstr(p, dest);
  1061.       ifsfirst = saveifs;
  1062.       ifslastp = savelastp;
  1063.       argbackq = saveargbackq;
  1064.       herefd = saveherefd;
  1065.  
  1066.       p = in.buf;
  1067.       lastc = '\0';
  1068.       for (;;) {
  1069.         if (--in.nleft < 0) {
  1070.           if (in.fd < 0)
  1071.             break;
  1072.           while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
  1073.           TRACE(("expbackq: read returns %d\n", i));
  1074.           if (i <= 0)
  1075.             break;
  1076.           p = buf;
  1077.           in.nleft = i - 1;
  1078.         }
  1079.         lastc = *p++;
  1080.         if (lastc != '\0') {
  1081.           if (full && syntax[lastc] == CCTL)
  1082.             STPUTC(CTLESC, dest);
  1083.           STPUTC(lastc, dest);
  1084.         }
  1085.       }
  1086.       if (lastc == '\n') {
  1087.         STUNPUTC(dest);
  1088.       }
  1089.       if (in.fd >= 0)
  1090.         close(in.fd);
  1091.       if (in.buf)
  1092.         ckfree(in.buf);
  1093.       if (in.jp)
  1094.         waitforjob(in.jp);
  1095.       if (quoted == 0)
  1096.         recordregion(startloc, dest - stackblock(), 0);
  1097.       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
  1098.         (dest - stackblock()) - startloc,
  1099.         (dest - stackblock()) - startloc,
  1100.         stackblock() + startloc));
  1101.       expdest = dest;
  1102.       INTON;
  1103. }
  1104.  
  1105.  
  1106.  
  1107. /*
  1108.  * Expand a variable, and return a pointer to the next character in the
  1109.  * input string.
  1110.  */
  1111.  
  1112. STATIC char *
  1113. evalvar(p, full)
  1114.       char *p;
  1115.       {
  1116.       int subtype;
  1117.       int flags;
  1118.       char *var;
  1119.       char *val;
  1120.       int c;
  1121.       int set;
  1122.       int special;
  1123.       int startloc;
  1124.  
  1125.       flags = *p++;
  1126.       subtype = flags & VSTYPE;
  1127.       var = p;
  1128.       special = 0;
  1129.       if (! is_name(*p))
  1130.         special = 1;
  1131.       p = strchr(p, '=') + 1;
  1132. again: /* jump here after setting a variable with ${var=text} */
  1133.       if (special) {
  1134.         set = varisset(*var);
  1135.         val = NULL;
  1136.       } else {
  1137.         val = lookupvar(var);
  1138.         if (val == NULL || (flags & VSNUL) && val[0] == '\0') {
  1139.           val = NULL;
  1140.           set = 0;
  1141.         } else
  1142.           set = 1;
  1143.       }
  1144.       startloc = expdest - stackblock();
  1145.       if (set && subtype != VSPLUS) {
  1146.         /* insert the value of the variable */
  1147.         if (special) {
  1148.           varvalue(*var, flags & VSQUOTE, full);
  1149.         } else {
  1150.           char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
  1151.  
  1152.           while (*val) {
  1153.             if (full && syntax[*val] == CCTL)
  1154.                   STPUTC(CTLESC, expdest);
  1155.             STPUTC(*val++, expdest);
  1156.           }
  1157.         }
  1158.       }
  1159.       if (subtype == VSPLUS)
  1160.         set = ! set;
  1161.       if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
  1162.        && (set || subtype == VSNORMAL))
  1163.         recordregion(startloc, expdest - stackblock(), flags & VSQUOTE);
  1164.       if (! set && subtype != VSNORMAL) {
  1165.         if (subtype == VSPLUS || subtype == VSMINUS) {
  1166.           argstr(p, full);
  1167.         } else {
  1168.           char *startp;
  1169.           int saveherefd = herefd;
  1170.           herefd = -1;
  1171.           argstr(p, 0);
  1172.           STACKSTRNUL(expdest);
  1173.           herefd = saveherefd;
  1174.           startp = stackblock() + startloc;
  1175.           if (subtype == VSASSIGN) {
  1176.             setvar(var, startp, 0);
  1177.             STADJUST(startp - expdest, expdest);
  1178.             flags &=~ VSNUL;
  1179.             goto again;
  1180.           }
  1181.           /* subtype == VSQUESTION */
  1182.           if (*p != CTLENDVAR) {
  1183.             outfmt(&errout, "%s\n", startp);
  1184.             error((char *)NULL);
  1185.           }
  1186.           error("%.*s: parameter %snot set", p - var - 1,
  1187.             var, (flags & VSNUL)? "null or " : nullstr);
  1188.         }
  1189.       }
  1190.       if (subtype != VSNORMAL) {    /* skip to end of alternative */
  1191.         int nesting = 1;
  1192.         for (;;) {
  1193.           if ((c = *p++) == CTLESC)
  1194.             p++;
  1195.           else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
  1196.             if (set)
  1197.                   argbackq = argbackq->next;
  1198.           } else if (c == CTLVAR) {
  1199.             if ((*p++ & VSTYPE) != VSNORMAL)
  1200.                   nesting++;
  1201.           } else if (c == CTLENDVAR) {
  1202.             if (--nesting == 0)
  1203.                   break;
  1204.           }
  1205.         }
  1206.       }
  1207.       return p;
  1208. }
  1209.  
  1210.  
  1211.  
  1212. /*
  1213.  * Test whether a specialized variable is set.
  1214.  */
  1215.  
  1216. STATIC int
  1217. varisset(name)
  1218.       char name;
  1219.       {
  1220.       char **ap;
  1221.  
  1222.       if (name == '!') {
  1223.         if (backgndpid == -1)
  1224.           return 0;
  1225.       } else if (name == '@' || name == '*') {
  1226.         if (*shellparam.p == NULL)
  1227.           return 0;
  1228.       } else if ((unsigned)(name -= '1') <= '9' - '1') {
  1229.         ap = shellparam.p;
  1230.         do {
  1231.           if (*ap++ == NULL)
  1232.             return 0;
  1233.         } while (--name >= 0);
  1234.       }
  1235.       return 1;
  1236. }
  1237.  
  1238.  
  1239.  
  1240. /*
  1241.  * Add the value of a specialized variable to the stack string.
  1242.  */
  1243.  
  1244. STATIC void
  1245. varvalue(name, quoted, allow_split)
  1246.       char name;
  1247.       {
  1248.       int num;
  1249.       char temp[32];
  1250.       char *p;
  1251.       int i;
  1252.       extern int exitstatus;
  1253.       char sep;
  1254.       char **ap;
  1255.       char const *syntax;
  1256.  
  1257.       switch (name) {
  1258.       case '$':
  1259.         num = rootpid;
  1260.         goto numvar;
  1261.       case '?':
  1262.         num = exitstatus;
  1263.         goto numvar;
  1264.       case '#':
  1265.         num = shellparam.nparam;
  1266.         goto numvar;
  1267.       case '!':
  1268.         num = backgndpid;
  1269. numvar:
  1270.         p = temp + 31;
  1271.         temp[31] = '\0';
  1272.         do {
  1273.           *--p = num % 10 + '0';
  1274.         } while ((num /= 10) != 0);
  1275.         while (*p)
  1276.           STPUTC(*p++, expdest);
  1277.         break;
  1278.       case '-':
  1279.         for (i = 0 ; optchar[i] ; i++) {
  1280.           if (optval[i])
  1281.             STPUTC(optchar[i], expdest);
  1282.         }
  1283.         break;
  1284.       case '@':
  1285.         if (allow_split) {
  1286.           sep = '\0';
  1287.           goto allargs;
  1288.         }
  1289.         /* fall through */            
  1290.       case '*':
  1291.         sep = ' ';
  1292. allargs:
  1293.         syntax = quoted? DQSYNTAX : BASESYNTAX;
  1294.         for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
  1295.           /* should insert CTLESC characters */
  1296.           while (*p) {
  1297.             if (syntax[*p] == CCTL)
  1298.                   STPUTC(CTLESC, expdest);
  1299.             STPUTC(*p++, expdest);
  1300.           }
  1301.           if (*ap)
  1302.             STPUTC(sep, expdest);
  1303.         }
  1304.         break;
  1305.       case '0':
  1306.         p = arg0;
  1307. string:
  1308.         syntax = quoted? DQSYNTAX : BASESYNTAX;
  1309.         while (*p) {
  1310.           if (syntax[*p] == CCTL)
  1311.             STPUTC(CTLESC, expdest);
  1312.           STPUTC(*p++, expdest);
  1313.         }
  1314.         break;
  1315.       default:
  1316.         if ((unsigned)(name -= '1') <= '9' - '1') {
  1317.           p = shellparam.p[name];
  1318.           goto string;
  1319.         }
  1320.         break;
  1321.       }
  1322. }
  1323.  
  1324.  
  1325.  
  1326. /*
  1327.  * Record the the fact that we have to scan this region of the
  1328.  * string for IFS characters.
  1329.  */
  1330.  
  1331. STATIC void
  1332. recordregion(start, end, nulonly) {
  1333.       register struct ifsregion *ifsp;
  1334.  
  1335.       if (ifslastp == NULL) {
  1336.         ifsp = &ifsfirst;
  1337.       } else {
  1338.         ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
  1339.         ifslastp->next = ifsp;
  1340.       }
  1341.       ifslastp = ifsp;
  1342.       ifslastp->next = NULL;
  1343.       ifslastp->begoff = start;
  1344.       ifslastp->endoff = end;
  1345.       ifslastp->nulonly = nulonly;
  1346. }
  1347.  
  1348.  
  1349.  
  1350. /*
  1351.  * Break the argument string into pieces based upon IFS and add the
  1352.  * strings to the argument list.  The regions of the string to be
  1353.  * searched for IFS characters have been stored by recordregion.
  1354.  */
  1355.  
  1356. STATIC void
  1357. ifsbreakup(string, arglist)
  1358.       char *string;
  1359.       struct arglist *arglist;
  1360.       {
  1361.       struct ifsregion *ifsp;
  1362.       struct strlist *sp;
  1363.       char *start;
  1364.       register char *p;
  1365.       char *q;
  1366.       char *ifs;
  1367.  
  1368.       start = string;
  1369.       if (ifslastp != NULL) {
  1370.         ifsp = &ifsfirst;
  1371.         do {
  1372.           p = string + ifsp->begoff;
  1373.           ifs = ifsp->nulonly? nullstr : ifsval();
  1374.           while (p < string + ifsp->endoff) {
  1375.             q = p;
  1376.             if (*p == CTLESC)
  1377.                   p++;
  1378.             if (strchr(ifs, *p++)) {
  1379.                   if (q > start || *ifs != ' ') {
  1380.                     *q = '\0';
  1381.                     sp = (struct strlist *)stalloc(sizeof *sp);
  1382.                     sp->text = start;
  1383.                     *arglist->lastp = sp;
  1384.                     arglist->lastp = &sp->next;
  1385.                   }
  1386.                   if (*ifs == ' ') {
  1387.                     for (;;) {
  1388.                       if (p >= string + ifsp->endoff)
  1389.                         break;
  1390.                       q = p;
  1391.                       if (*p == CTLESC)
  1392.                         p++;
  1393.                       if (strchr(ifs, *p++) == NULL) {
  1394.                         p = q;
  1395.                         break;
  1396.                       }
  1397.                     }
  1398.                   }
  1399.                   start = p;
  1400.             }
  1401.           }
  1402.         } while ((ifsp = ifsp->next) != NULL);
  1403.         if (*start || (*ifs != ' ' && start > string)) {
  1404.           sp = (struct strlist *)stalloc(sizeof *sp);
  1405.           sp->text = start;
  1406.           *arglist->lastp = sp;
  1407.           arglist->lastp = &sp->next;
  1408.         }
  1409.       } else {
  1410.         sp = (struct strlist *)stalloc(sizeof *sp);
  1411.         sp->text = start;
  1412.         *arglist->lastp = sp;
  1413.         arglist->lastp = &sp->next;
  1414.       }
  1415. }
  1416.  
  1417.  
  1418.  
  1419. /*
  1420.  * Expand shell metacharacters.  At this point, the only control characters
  1421.  * should be escapes.  The results are stored in the list exparg.
  1422.  */
  1423.  
  1424. char *expdir;
  1425.  
  1426.  
  1427. STATIC void
  1428. expandmeta(str)
  1429.       struct strlist *str;
  1430.       {
  1431.       char *p;
  1432.       struct strlist **savelastp;
  1433.       struct strlist *sp;
  1434.       char c;
  1435.  
  1436.       while (str) {
  1437.         if (fflag)
  1438.           goto nometa;
  1439.         p = str->text;
  1440. #if UDIR
  1441.         if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
  1442.           str->text = p = expudir(p);
  1443. #endif
  1444.         for (;;) {            /* fast check for meta chars */
  1445.           if ((c = *p++) == '\0')
  1446.             goto nometa;
  1447.           if (c == '*' || c == '?' || c == '[' || c == '!')
  1448.             break;
  1449.         }
  1450.         savelastp = exparg.lastp;
  1451.         INTOFF;
  1452.         if (expdir == NULL)
  1453.           expdir = ckmalloc(1024); /* I hope this is big enough */
  1454.         expmeta(expdir, str->text);
  1455.         ckfree(expdir);
  1456.         expdir = NULL;
  1457.         INTON;
  1458.         if (exparg.lastp == savelastp) {
  1459.           if (! zflag) {
  1460. nometa:
  1461.             *exparg.lastp = str;
  1462.             rmescapes(str->text);
  1463.             exparg.lastp = &str->next;
  1464.           }
  1465.         } else {
  1466.           *exparg.lastp = NULL;
  1467.           *savelastp = sp = expsort(*savelastp);
  1468.           while (sp->next != NULL)
  1469.             sp = sp->next;
  1470.           exparg.lastp = &sp->next;
  1471.         }
  1472.         str = str->next;
  1473.       }
  1474. }
  1475.  
  1476.  
  1477. #if UDIR
  1478. /*
  1479.  * Expand /u/username into the home directory for the specified user.
  1480.  * We could use the getpw stuff here, but then we would have to load
  1481.  * in stdio and who knows what else.
  1482.  */
  1483.  
  1484. #define MAXLOGNAME 32
  1485. #define MAXPWLINE 128
  1486.  
  1487. char *pfgets();
  1488.  
  1489.  
  1490. STATIC char *
  1491. expudir(path)
  1492.       char *path;
  1493.       {
  1494.       register char *p, *q, *r;
  1495.       char name[MAXLOGNAME];
  1496.       char line[MAXPWLINE];
  1497.       int i;
  1498.  
  1499.       r = path;                /* result on failure */
  1500.       p = r + 3;            /* the 3 skips "/u/" */
  1501.       q = name;
  1502.       while (*p && *p != '/') {
  1503.         if (q >= name + MAXLOGNAME - 1)
  1504.           return r;        /* fail, name too long */
  1505.         *q++ = *p++;
  1506.       }
  1507.       *q = '\0';
  1508.       setinputfile("/etc/passwd", 1);
  1509.       q = line + strlen(name);
  1510.       while (pfgets(line, MAXPWLINE) != NULL) {
  1511.         if (line[0] == name[0] && prefix(name, line) && *q == ':') {
  1512.           /* skip to start of home directory */
  1513.           i = 4;
  1514.           do {
  1515.             while (*++q && *q != ':');
  1516.           } while (--i > 0);
  1517.           if (*q == '\0')
  1518.             break;        /* fail, corrupted /etc/passwd */
  1519.           q++;
  1520.           for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
  1521.           *r = '\0';        /* nul terminate home directory */
  1522.           i = r - q;        /* i = strlen(q) */
  1523.           r = stalloc(i + strlen(p) + 1);
  1524.           scopy(q, r);
  1525.           scopy(p, r + i);
  1526.           TRACE(("expudir converts %s to %s\n", path, r));
  1527.           didudir = 1;
  1528.           path = r;        /* succeed */
  1529.           break;
  1530.         }
  1531.       }
  1532.       popfile();
  1533.       return r;
  1534. }
  1535. #endif
  1536.  
  1537.  
  1538. /*
  1539.  * Do metacharacter (i.e. *, ?, [...]) expansion.
  1540.  */
  1541.  
  1542. STATIC void
  1543. expmeta(enddir, name)
  1544.       char *enddir;
  1545.       char *name;
  1546.       {
  1547.       register char *p;
  1548.       char *q;
  1549.       char *start;
  1550.       char *endname;
  1551.       int metaflag;
  1552.       struct stat statb;
  1553.       DIR *dirp;
  1554.       struct dirent *dp;
  1555.       int atend;
  1556.       int matchdot;
  1557.  
  1558.       metaflag = 0;
  1559.       start = name;
  1560.       for (p = name ; ; p++) {
  1561.         if (*p == '*' || *p == '?')
  1562.           metaflag = 1;
  1563.         else if (*p == '[') {
  1564.           q = p + 1;
  1565.           if (*q == '!')
  1566.             q++;
  1567.           for (;;) {
  1568.             if (*q == CTLESC)
  1569.                   q++;
  1570.             if (*q == '/' || *q == '\0')
  1571.                   break;
  1572.             if (*++q == ']') {
  1573.                   metaflag = 1;
  1574.                   break;
  1575.             }
  1576.           }
  1577.         } else if (*p == '!' && p[1] == '!'    && (p == name || p[-1] == '/')) {
  1578.           metaflag = 1;
  1579.         } else if (*p == '\0')
  1580.           break;
  1581.         else if (*p == CTLESC)
  1582.           p++;
  1583.         if (*p == '/') {
  1584.           if (metaflag)
  1585.             break;
  1586.           start = p + 1;
  1587.         }
  1588.       }
  1589.       if (metaflag == 0) {    /* we've reached the end of the file name */
  1590.         if (enddir != expdir)
  1591.           metaflag++;
  1592.         for (p = name ; ; p++) {
  1593.           if (*p == CTLESC)
  1594.             p++;
  1595.           *enddir++ = *p;
  1596.           if (*p == '\0')
  1597.             break;
  1598.         }
  1599.         if (metaflag == 0 || stat(expdir, &statb) >= 0)
  1600.           addfname(expdir);
  1601.         return;
  1602.       }
  1603.       endname = p;
  1604.       if (start != name) {
  1605.         p = name;
  1606.         while (p < start) {
  1607.           if (*p == CTLESC)
  1608.             p++;
  1609.           *enddir++ = *p++;
  1610.         }
  1611.       }
  1612.       if (enddir == expdir) {
  1613.         p = ".";
  1614.       } else if (enddir == expdir + 1 && *expdir == '/') {
  1615.         p = "/";
  1616.       } else {
  1617.         p = expdir;
  1618.         enddir[-1] = '\0';
  1619.       }
  1620.       if ((dirp = opendir(p)) == NULL)
  1621.         return;
  1622.       if (enddir != expdir)
  1623.         enddir[-1] = '/';
  1624.       if (*endname == 0) {
  1625.         atend = 1;
  1626.       } else {
  1627.         atend = 0;
  1628.         *endname++ = '\0';
  1629.       }
  1630.       matchdot = 0;
  1631.       if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
  1632.         matchdot++;
  1633.       while (! int_pending() && (dp = readdir(dirp)) != NULL) {
  1634.         if (dp->d_name[0] == '.' && ! matchdot)
  1635.           continue;
  1636.         if (patmatch(start, dp->d_name)) {
  1637.           if (atend) {
  1638.             scopy(dp->d_name, enddir);
  1639.             addfname(expdir);
  1640.           } else {
  1641.             char *q;
  1642.             for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
  1643.             p[-1] = '/';
  1644.             expmeta(p, endname);
  1645.           }
  1646.         }
  1647.       }
  1648.       closedir(dirp);
  1649.       if (! atend)
  1650.         endname[-1] = '/';
  1651. }
  1652.  
  1653.  
  1654. /*
  1655.  * Add a file name to the list.
  1656.  */
  1657.  
  1658. STATIC void
  1659. addfname(name)
  1660.       char *name;
  1661.       {
  1662.       char *p;
  1663.       struct strlist *sp;
  1664.  
  1665.       p = stalloc(strlen(name) + 1);
  1666.       scopy(name, p);
  1667.       sp = (struct strlist *)stalloc(sizeof *sp);
  1668.       sp->text = p;
  1669.       *exparg.lastp = sp;
  1670.       exparg.lastp = &sp->next;
  1671. }
  1672.  
  1673.  
  1674. /*
  1675.  * Sort the results of file name expansion.  It calculates the number of
  1676.  * strings to sort and then calls msort (short for merge sort) to do the
  1677.  * work.
  1678.  */
  1679.  
  1680. STATIC struct strlist *
  1681. expsort(str)
  1682.       struct strlist *str;
  1683.       {
  1684.       int len;
  1685.       struct strlist *sp;
  1686.  
  1687.       len = 0;
  1688.       for (sp = str ; sp ; sp = sp->next)
  1689.         len++;
  1690.       return msort(str, len);
  1691. }
  1692.  
  1693.  
  1694. STATIC struct strlist *
  1695. msort(list, len)
  1696.       struct strlist *list;
  1697.       {
  1698.       struct strlist *p, *q;
  1699.       struct strlist **lpp;
  1700.       int half;
  1701.       int n;
  1702.  
  1703.       if (len <= 1)
  1704.         return list;
  1705.       half = len >> 1;      
  1706.       p = list;
  1707.       for (n = half ; --n >= 0 ; ) {
  1708.         q = p;
  1709.         p = p->next;
  1710.       }
  1711.       q->next = NULL;            /* terminate first half of list */
  1712.       q = msort(list, half);        /* sort first half of list */
  1713.       p = msort(p, len - half);        /* sort second half */
  1714.       lpp = &list;
  1715.       for (;;) {
  1716.         if (strcmp(p->text, q->text) < 0) {
  1717.           *lpp = p;
  1718.           lpp = &p->next;
  1719.           if ((p = *lpp) == NULL) {
  1720.             *lpp = q;
  1721.             break;
  1722.           }
  1723.         } else {
  1724.           *lpp = q;
  1725.           lpp = &q->next;
  1726.           if ((q = *lpp) == NULL) {
  1727.             *lpp = p;
  1728.             break;
  1729.           }
  1730.         }
  1731.       }
  1732.       return list;
  1733. }
  1734.  
  1735.  
  1736.  
  1737. /*
  1738.  * Returns true if the pattern matches the string.
  1739.  */
  1740.  
  1741. int
  1742. patmatch(pattern, string)
  1743.       char *pattern;
  1744.       char *string;
  1745.       {
  1746.       if (pattern[0] == '!' && pattern[1] == '!')
  1747.         return 1 - pmatch(pattern + 2, string);
  1748.       else
  1749.         return pmatch(pattern, string);
  1750. }
  1751.  
  1752.  
  1753. STATIC int
  1754. pmatch(pattern, string)
  1755.       char *pattern;
  1756.       char *string;
  1757.       {
  1758.       register char *p, *q;
  1759.       register char c;
  1760.  
  1761.       p = pattern;
  1762.       q = string;
  1763.       for (;;) {
  1764.         switch (c = *p++) {
  1765.         case '\0':
  1766.           goto breakloop;
  1767.         case CTLESC:
  1768.           if (*q++ != *p++)
  1769.             return 0;
  1770.           break;
  1771.         case '?':
  1772.           if (*q++ == '\0')
  1773.             return 0;
  1774.           break;
  1775.         case '*':
  1776.           c = *p;
  1777.           if (c != CTLESC && c != '?' && c != '*' && c != '[') {
  1778.             while (*q != c) {
  1779.                   if (*q == '\0')
  1780.                     return 0;
  1781.                   q++;
  1782.             }
  1783.           }
  1784.           do {
  1785.             if (pmatch(p, q))
  1786.                   return 1;
  1787.           } while (*q++ != '\0');
  1788.           return 0;
  1789.         case '[': {
  1790.           char *endp;
  1791.           int invert, found;
  1792.           char chr;
  1793.  
  1794.           endp = p;
  1795.           if (*endp == '!')
  1796.             endp++;
  1797.           for (;;) {
  1798.             if (*endp == '\0')
  1799.                   goto dft;        /* no matching ] */
  1800.             if (*endp == CTLESC)
  1801.                   endp++;
  1802.             if (*++endp == ']')
  1803.                   break;
  1804.           }
  1805.           invert = 0;
  1806.           if (*p == '!') {
  1807.             invert++;
  1808.             p++;
  1809.           }
  1810.           found = 0;
  1811.           chr = *q++;
  1812.           c = *p++;
  1813.           do {
  1814.             if (c == CTLESC)
  1815.                   c = *p++;
  1816.             if (*p == '-' && p[1] != ']') {
  1817.                   p++;
  1818.                   if (*p == CTLESC)
  1819.                     p++;
  1820.                   if (chr >= c && chr <= *p)
  1821.                     found = 1;
  1822.                   p++;
  1823.             } else {
  1824.                   if (chr == c)
  1825.                     found = 1;
  1826.             }
  1827.           } while ((c = *p++) != ']');
  1828.           if (found == invert)
  1829.             return 0;
  1830.           break;
  1831.         }
  1832. dft:        default:
  1833.           if (*q++ != c)
  1834.             return 0;
  1835.           break;
  1836.         }
  1837.       }
  1838. breakloop:
  1839.       if (*q != '\0')
  1840.         return 0;
  1841.       return 1;
  1842. }
  1843.  
  1844.  
  1845.  
  1846. /*
  1847.  * Remove any CTLESC characters from a string.
  1848.  */
  1849.  
  1850. void
  1851. rmescapes(str)
  1852.       char *str;
  1853.       {
  1854.       register char *p, *q;
  1855.  
  1856.       p = str;
  1857.       while (*p != CTLESC) {
  1858.         if (*p++ == '\0')
  1859.           return;
  1860.       }
  1861.       q = p;
  1862.       while (*p) {
  1863.         if (*p == CTLESC)
  1864.           p++;
  1865.         *q++ = *p++;
  1866.       }
  1867.       *q = '\0';
  1868. }
  1869.  
  1870.  
  1871.  
  1872. /*
  1873.  * See if a pattern matches in a case statement.
  1874.  */
  1875.  
  1876. int
  1877. casematch(pattern, val)
  1878.       union node *pattern;
  1879.       char *val;
  1880.       {
  1881.       struct stackmark smark;
  1882.       int result;
  1883.       char *p;
  1884.  
  1885.       setstackmark(&smark);
  1886.       argbackq = pattern->narg.backquote;
  1887.       STARTSTACKSTR(expdest);
  1888.       ifslastp = NULL;
  1889.       argstr(pattern->narg.text, 0);
  1890.       STPUTC('\0', expdest);
  1891.       p = grabstackstr(expdest);
  1892.       result = patmatch(p, val);
  1893.       popstackmark(&smark);
  1894.       return result;
  1895. }
  1896. EOF
  1897. if test `wc -c < expand.c` -ne 22554
  1898. then    echo 'expand.c is the wrong size'
  1899. fi
  1900. echo extracting init.h
  1901. cat > init.h <<\EOF
  1902. /*
  1903.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1904.  * This file is part of ash, which is distributed under the terms specified
  1905.  * by the Ash General Public License.  See the file named LICENSE.
  1906.  */
  1907.  
  1908. #ifdef __STDC__
  1909. void init(void);
  1910. void reset(void);
  1911. void initshellproc(void);
  1912. #else
  1913. void init();
  1914. void reset();
  1915. void initshellproc();
  1916. #endif
  1917. EOF
  1918. if test `wc -c < init.h` -ne 355
  1919. then    echo 'init.h is the wrong size'
  1920. fi
  1921. echo extracting input.h
  1922. cat > input.h <<\EOF
  1923. /*
  1924.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1925.  * This file is part of ash, which is distributed under the terms specified
  1926.  * by the Ash General Public License.  See the file named LICENSE.
  1927.  */
  1928.  
  1929. /* PEOF (the end of file marker) is defined in syntax.h */
  1930.  
  1931. /*
  1932.  * The input line number.  Input.c just defines this variable, and saves
  1933.  * and restores it when files are pushed and popped.  The user of this
  1934.  * package must set its value.
  1935.  */
  1936. extern int plinno;
  1937. extern int parsenleft;        /* number of characters left in input buffer */
  1938. extern char *parsenextc;    /* next character in input buffer */
  1939.  
  1940.  
  1941. #ifdef __STDC__
  1942. char *pfgets(char *, int);
  1943. int pgetc(void);
  1944. int preadbuffer(void);
  1945. void pungetc(void);
  1946. void ppushback(char *, int);
  1947. void setinputfile(char *, int);
  1948. void setinputfd(int, int);
  1949. void setinputstring(char *, int);
  1950. void popfile(void);
  1951. void popallfiles(void);
  1952. void closescript(void);
  1953. #else
  1954. char *pfgets();
  1955. int pgetc();
  1956. int preadbuffer();
  1957. void pungetc();
  1958. void ppushback();
  1959. void setinputfile();
  1960. void setinputfd();
  1961. void setinputstring();
  1962. void popfile();
  1963. void popallfiles();
  1964. void closescript();
  1965. #endif
  1966.  
  1967. #define pgetc_macro()    (--parsenleft >= 0? *parsenextc++ : preadbuffer())
  1968. EOF
  1969. if test `wc -c < input.h` -ne 1194
  1970. then    echo 'input.h is the wrong size'
  1971. fi
  1972. echo extracting input.c
  1973. cat > input.c <<\EOF
  1974. /*
  1975.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1976.  * This file is part of ash, which is distributed under the terms specified
  1977.  * by the Ash General Public License.  See the file named LICENSE.
  1978.  *
  1979.  * This file implements the input routines used by the parser.
  1980.  */
  1981.  
  1982. #include <stdio.h>    /* defines BUFSIZ */
  1983. #include "shell.h"
  1984. #include "syntax.h"
  1985. #include "input.h"
  1986. #include "output.h"
  1987. #include "memalloc.h"
  1988. #include "error.h"
  1989. #include <fcntl.h>
  1990. #include "myerrno.h"
  1991.  
  1992. #define EOF_NLEFT -99        /* value of parsenleft when EOF pushed back */
  1993.  
  1994.  
  1995. /*
  1996.  * The parsefile structure pointed to by the global variable parsefile
  1997.  * contains information about the current file being read.
  1998.  */
  1999.  
  2000. MKINIT
  2001. struct parsefile {
  2002.       int linno;        /* current line */
  2003.       int fd;            /* file descriptor (or -1 if string) */
  2004.       int nleft;        /* number of chars left in buffer */
  2005.       char *nextc;        /* next char in buffer */
  2006.       struct parsefile *prev;    /* preceding file on stack */
  2007.       char *buf;        /* input buffer */
  2008. };
  2009.  
  2010.  
  2011. int plinno = 1;            /* input line number */
  2012. MKINIT int parsenleft;        /* copy of parsefile->nleft */
  2013. char *parsenextc;        /* copy of parsefile->nextc */
  2014. MKINIT struct parsefile basepf;    /* top level input file */
  2015. char basebuf[BUFSIZ];        /* buffer for top level input file */
  2016. struct parsefile *parsefile = &basepf;    /* current input file */
  2017. char *pushedstring;        /* copy of parsenextc when text pushed back */
  2018. int pushednleft;        /* copy of parsenleft when text pushed back */
  2019.  
  2020. #ifdef __STDC__
  2021. STATIC void pushfile(void);
  2022. #else
  2023. STATIC void pushfile();
  2024. #endif
  2025.  
  2026.  
  2027.  
  2028. #ifdef mkinit
  2029. INCLUDE "input.h"
  2030. INCLUDE "error.h"
  2031.  
  2032. INIT {
  2033.       extern char basebuf[];
  2034.  
  2035.       basepf.nextc = basepf.buf = basebuf;
  2036. }
  2037.  
  2038. RESET {
  2039.       if (exception != EXSHELLPROC)
  2040.         parsenleft = 0;            /* clear input buffer */
  2041.       popallfiles();
  2042. }
  2043.  
  2044. SHELLPROC {
  2045.       popallfiles();
  2046. }
  2047. #endif
  2048.  
  2049.  
  2050. /*
  2051.  * Read a line from the script.
  2052.  */
  2053.  
  2054. char *
  2055. pfgets(line, len)
  2056.       char *line;
  2057.       {
  2058.       register char *p = line;
  2059.       int nleft = len;
  2060.       int c;
  2061.  
  2062.       while (--nleft > 0) {
  2063.         c = pgetc_macro();
  2064.         if (c == PEOF) {
  2065.           if (p == line)
  2066.             return NULL;
  2067.           break;
  2068.         }
  2069.         *p++ = c;
  2070.         if (c == '\n')
  2071.           break;
  2072.       }
  2073.       *p = '\0';
  2074.       return line;
  2075. }
  2076.  
  2077.  
  2078.  
  2079. /*
  2080.  * Read a character from the script, returning PEOF on end of file.
  2081.  * Nul characters in the input are silently discarded.
  2082.  */
  2083.  
  2084. int
  2085. pgetc() {
  2086.       return pgetc_macro();
  2087. }
  2088.  
  2089.  
  2090. /*
  2091.  * Refill the input buffer and return the next input character:
  2092.  *
  2093.  * 1) If a string was pushed back on the input, switch back to the regular
  2094.  *    buffer.
  2095.  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
  2096.  *    from a string so we can't refill the buffer, return EOF.
  2097.  * 3) Call read to read in the characters.
  2098.  * 4) Delete all nul characters from the buffer.
  2099.  */
  2100.  
  2101. int
  2102. preadbuffer() {
  2103.       register char *p, *q;
  2104.       register int i;
  2105.  
  2106.       if (pushedstring) {
  2107.         parsenextc = pushedstring;
  2108.         pushedstring = NULL;
  2109.         parsenleft = pushednleft;
  2110.         if (--parsenleft >= 0)
  2111.           return *parsenextc++;
  2112.       }
  2113.       if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
  2114.         return PEOF;
  2115.       flushout(&output);
  2116.       flushout(&errout);
  2117. retry:
  2118.       p = parsenextc = parsefile->buf;
  2119.       i = read(parsefile->fd, p, BUFSIZ);
  2120.       if (i <= 0) {
  2121.         if (i < 0) {
  2122.           if (errno == EINTR)
  2123.             goto retry;
  2124. #ifdef BSD
  2125.           if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
  2126.             int flags = fcntl(0, F_GETFL, 0);
  2127.             if (flags >= 0 && flags & FNDELAY) {
  2128.                   flags &=~ FNDELAY;
  2129.                   if (fcntl(0, F_SETFL, flags) >= 0) {
  2130.                     out2str("ash: turning off NDELAY mode\n");
  2131.                     goto retry;
  2132.                   }
  2133.             }
  2134.           }
  2135. #endif
  2136.         }
  2137. #ifdef SYSV
  2138.         if (i == 0 && parsefile->fd == 0) {
  2139.           int flags = fcntl(0, F_GETFL, 0);
  2140.           if (flags >= 0 && flags & O_NDELAY) {
  2141.             flags &=~ O_NDELAY;
  2142.             if (fcntl(0, F_SETFL, flags) >= 0) {
  2143.                   out2str("ash: turning off NDELAY mode\n");
  2144.                   goto retry;
  2145.             }
  2146.           }
  2147.         }
  2148. #endif
  2149.         parsenleft = EOF_NLEFT;
  2150.         return PEOF;
  2151.       }
  2152.       parsenleft = i - 1;
  2153.  
  2154.       /* delete nul characters */
  2155.       for (;;) {
  2156.         if (*p++ == '\0')
  2157.           break;
  2158.         if (--i <= 0)
  2159.           return *parsenextc++;        /* no nul characters */
  2160.       }
  2161.       q = p - 1;
  2162.       while (--i > 0) {
  2163.         if (*p != '\0')
  2164.           *q++ = *p;
  2165.         p++;
  2166.       }
  2167.       if (q == parsefile->buf)
  2168.         goto retry;            /* buffer contained nothing but nuls */
  2169.       parsenleft = q - parsefile->buf - 1;
  2170.       return *parsenextc++;
  2171. }
  2172.  
  2173.  
  2174. /*
  2175.  * Undo the last call to pgetc.  Only one character may be pushed back.
  2176.  * PEOF may be pushed back.
  2177.  */
  2178.  
  2179. void
  2180. pungetc() {
  2181.       parsenleft++;
  2182.       parsenextc--;
  2183. }
  2184.  
  2185.  
  2186. /*
  2187.  * Push a string back onto the input.  This code doesn't work if the user
  2188.  * tries to push back more than one string at once.
  2189.  */
  2190.  
  2191. void
  2192. ppushback(string, length)
  2193.       char *string;
  2194.       {
  2195.       pushedstring = parsenextc;
  2196.       pushednleft = parsenleft;
  2197.       parsenextc = string;
  2198.       parsenleft = length;
  2199. }
  2200.  
  2201.  
  2202.  
  2203. /*
  2204.  * Set the input to take input from a file.  If push is set, push the
  2205.  * old input onto the stack first.
  2206.  */
  2207.  
  2208. void
  2209. setinputfile(fname, push)
  2210.       char *fname;
  2211.       {
  2212.       int fd;
  2213.       int fd2;
  2214.  
  2215.       INTOFF;
  2216.       if ((fd = open(fname, O_RDONLY)) < 0)
  2217.         error("Can't open %s", fname);
  2218.       if (fd < 10) {
  2219.         fd2 = copyfd(fd, 10);
  2220.         close(fd);
  2221.         if (fd2 < 0)
  2222.           error("Out of file descriptors");
  2223.         fd = fd2;
  2224.       }
  2225.       setinputfd(fd, push);
  2226.       INTON;
  2227. }
  2228.  
  2229.  
  2230. /*
  2231.  * Like setinputfile, but takes an open file descriptor.  Call this with
  2232.  * interrupts off.
  2233.  */
  2234.  
  2235. void
  2236. setinputfd(fd, push) {
  2237.       if (push) {
  2238.         pushfile();
  2239.         parsefile->buf = ckmalloc(BUFSIZ);
  2240.       }
  2241.       if (parsefile->fd > 0)
  2242.         close(parsefile->fd);
  2243.       parsefile->fd = fd;
  2244.       if (parsefile->buf == NULL)
  2245.         parsefile->buf = ckmalloc(BUFSIZ);
  2246.       parsenleft = 0;
  2247.       plinno = 1;
  2248. }
  2249.  
  2250.  
  2251. /*
  2252.  * Like setinputfile, but takes input from a string.
  2253.  */
  2254.  
  2255. void
  2256. setinputstring(string, push)
  2257.       char *string;
  2258.       {
  2259.       INTOFF;
  2260.       if (push)
  2261.         pushfile();
  2262.       parsenextc = string;
  2263.       parsenleft = strlen(string);
  2264.       parsefile->buf = NULL;
  2265.       plinno = 1;
  2266.       INTON;
  2267. }
  2268.  
  2269.  
  2270.  
  2271. /*
  2272.  * To handle the "." command, a stack of input files is used.  Pushfile
  2273.  * adds a new entry to the stack and popfile restores the previous level.
  2274.  */
  2275.  
  2276. STATIC void
  2277. pushfile() {
  2278.       struct parsefile *pf;
  2279.  
  2280.       parsefile->nleft = parsenleft;
  2281.       parsefile->nextc = parsenextc;
  2282.       parsefile->linno = plinno;
  2283.       pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
  2284.       pf->prev = parsefile;
  2285.       pf->fd = -1;
  2286.       parsefile = pf;
  2287. }
  2288.  
  2289.  
  2290. void
  2291. popfile() {
  2292.       struct parsefile *pf = parsefile;
  2293.  
  2294.       INTOFF;
  2295.       if (pf->fd >= 0)
  2296.         close(pf->fd);
  2297.       if (pf->buf)
  2298.         ckfree(pf->buf);
  2299.       parsefile = pf->prev;
  2300.       ckfree(pf);
  2301.       parsenleft = parsefile->nleft;
  2302.       parsenextc = parsefile->nextc;
  2303.       plinno = parsefile->linno;
  2304.       INTON;
  2305. }
  2306.  
  2307.  
  2308. /*
  2309.  * Return to top level.
  2310.  */
  2311.  
  2312. void
  2313. popallfiles() {
  2314.       while (parsefile != &basepf)
  2315.         popfile();
  2316. }
  2317.  
  2318.  
  2319.  
  2320. /*
  2321.  * Close the file(s) that the shell is reading commands from.  Called
  2322.  * after a fork is done.
  2323.  */
  2324.  
  2325. void
  2326. closescript() {
  2327.       popallfiles();
  2328.       if (parsefile->fd > 0) {
  2329.         close(parsefile->fd);
  2330.         parsefile->fd = 0;
  2331.       }
  2332. }
  2333. EOF
  2334. if test `wc -c < input.c` -ne 7288
  2335. then    echo 'input.c is the wrong size'
  2336. fi
  2337. echo Archive 3 unpacked
  2338. exit
  2339.  
  2340.