home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / ash / part02 < prev    next >
Text File  |  1989-05-29  |  50KB  |  2,191 lines

  1. Subject:  v19i002:  A reimplementation of the System V shell, Part02/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 2
  8. Archive-name: ash/part02
  9.  
  10. # This is part 2 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 adjind.c
  15. cat > adjind.c <<\EOF
  16. /*
  17.  * Adjust indentation.  The first arg is the old indentation amout,
  18.  * the second arg is the new indentation amount, and the remaining
  19.  * args are files to update.  Files are updated in place.  The original
  20.  * version of a file named file.c is moved fo file.c~.
  21.  *
  22.  * Copyright (C) 1989 by Kenneth Almquist.
  23.  * Permission to use, copy, modify, and distribute this software and
  24.  * its documentation for any purpose without fee is hereby granted,
  25.  * provided that this copyright and permission notice is retained.
  26.  */
  27.  
  28.  
  29. #include <stdio.h>
  30. #include <errno.h>
  31.  
  32. char tempfile[64];
  33.  
  34. FILE *ckfopen();
  35.  
  36. #define is_digit(c)    ((unsigned)((c) - '0') <= 9)
  37.  
  38.  
  39. main(argc, argv)
  40.       char **argv;
  41.       {
  42.       int oldind, newind;
  43.       double ratio;
  44.       char **ap;
  45.  
  46.       if (argc < 3)
  47.         error("Usage: adjind old_indent new_indent file...");
  48.       oldind = number(argv[1]);
  49.       newind = number(argv[2]);
  50.       if (oldind == 0)
  51.         error("Old indent cannot be zero");
  52.       ratio = (double)newind / oldind;
  53.       sprintf(tempfile, "/tmp/adjind%d", getpid());
  54.       for (ap = argv + 3 ; *ap ; ap++) {
  55.         dofile(*ap, ratio);
  56.       }
  57.       done(0);
  58. }
  59.  
  60.  
  61.  
  62. done(status) {
  63.       exit(status);
  64. }
  65.  
  66.  
  67.  
  68. dofile(fname, ratio)
  69.       char *fname;
  70.       double ratio;
  71.       {
  72.       register FILE *fp;
  73.       register FILE *temp;
  74.       register int c;
  75.       register indent;
  76.       double findent;
  77.       char buf[1024];
  78.  
  79.       sprintf(tempfile, "%s.new", fname);
  80.       fp = ckfopen(fname, "r");
  81.       temp = ckfopen(tempfile, "w");
  82.       for (;;) {    /* for each line of input, until EOF */
  83.         indent = 0;
  84.         for (;;) {
  85.           if ((c = getc(fp)) == ' ')
  86.             indent++;
  87.           else if (c == '\t')
  88.             indent = indent + 8 &~ 07;
  89.           else
  90.             break;
  91.         }
  92.         findent = (double)indent * ratio;
  93.         indent = findent;
  94.         if (findent - indent > 0.5)
  95.           indent++;
  96.         while (indent >= 8) {
  97.           putc('\t', temp);
  98.           indent -= 8;
  99.         }
  100.         while (indent > 0) {
  101.           putc(' ', temp);
  102.           indent--;
  103.         }
  104.         if (c == EOF)
  105.           break;
  106.         putc(c, temp);
  107.         if (c != '\n') {
  108.           if (fgets(buf, 1024, fp) == NULL)
  109.             break;
  110.           fputs(buf, temp);
  111.         }
  112.       }
  113.       fclose(fp);
  114.       if (ferror(temp) || fclose(temp) == EOF)
  115.         error("Write error");
  116.       sprintf(buf, "%s~", fname);
  117.       movefile(fname, buf);
  118.       movefile(tempfile, fname);
  119. }
  120.  
  121.  
  122.  
  123. /*
  124.  * Rename a file.  We do it with links since the rename system call is
  125.  * not universal.
  126.  */
  127.  
  128. movefile(old, new)
  129.       char *old, *new;
  130.       {
  131.       int status;
  132.  
  133.       if ((status = link(old, new)) < 0 && errno == EEXIST) {
  134.         unlink(new);
  135.         status = link(old, new);
  136.       }
  137.       if (status < 0)
  138.         error("link failed");
  139.       if (unlink(old) < 0)
  140.         perror("unlink failed");
  141. }
  142.  
  143.  
  144.  
  145. FILE *
  146. ckfopen(file, mode)
  147.       char *file;
  148.       char *mode;
  149.       {
  150.       FILE *fp;
  151.  
  152.       if ((fp = fopen(file, mode)) == NULL) {
  153.         fprintf(stderr, "Can't open %s\n", file);
  154.         done(2);
  155.       }
  156.       return fp;
  157. }
  158.  
  159.  
  160. int
  161. number(s)
  162.       char *s;
  163.       {
  164.       register char *p;
  165.  
  166.       for (p = s ; is_digit(*p) ; p++);
  167.       if (p == s || *p != '\0') {
  168.         fprintf(stderr, "Illegal number: %s\n", s);
  169.         done(2);
  170.       }
  171.       return atoi(s);
  172. }
  173.  
  174.  
  175.  
  176. error(msg)
  177.       char *msg;
  178.       {
  179.       fprintf(stderr, "%s\n", msg);
  180.       done(2);
  181. }
  182. EOF
  183. if test `wc -c < adjind.c` -ne 3265
  184. then    echo 'adjind.c is the wrong size'
  185. fi
  186. echo extracting builtins
  187. cat > builtins <<\EOF
  188. # This file lists all the builtin commands.  The first column is the name
  189. # of a C routine.  The -j flag, if present, specifies that this command
  190. # is to be excluded from systems without job control.  The rest of the line
  191. # specifies the command name or names used to run the command.  The entry
  192. # for nullcmd, which is run when the user does not specify a command, must
  193. # come first.
  194. #
  195. # Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  196. # This file is part of ash, which is distributed under the terms specified
  197. # by the Ash General Public License.  See the file named LICENSE.
  198.  
  199. bltincmd    bltin
  200. #alloccmd    alloc
  201. bgcmd -j    bg
  202. breakcmd    break continue
  203. catfcmd        catf
  204. cdcmd        cd
  205. dotcmd        .
  206. echocmd        echo
  207. evalcmd        eval
  208. execcmd        exec
  209. exitcmd        exit
  210. exportcmd    export readonly
  211. exprcmd        expr test [
  212. fgcmd -j    fg
  213. getoptscmd    getopts
  214. hashcmd        hash
  215. jobidcmd    jobid
  216. jobscmd        jobs
  217. lccmd        lc
  218. linecmd        line
  219. localcmd    local
  220. nlechocmd    nlecho
  221. pwdcmd        pwd
  222. readcmd        read
  223. returncmd    return
  224. setcmd        set
  225. setvarcmd    setvar
  226. shiftcmd    shift
  227. trapcmd        trap
  228. truecmd        : true
  229. umaskcmd    umask
  230. unsetcmd    unset
  231. waitcmd        wait
  232. EOF
  233. if test `wc -c < builtins` -ne 1088
  234. then    echo 'builtins is the wrong size'
  235. fi
  236. echo extracting cd.c
  237. cat > cd.c <<\EOF
  238. /*
  239.  * The cd and pwd commands.
  240.  *
  241.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  242.  * This file is part of ash, which is distributed under the terms specified
  243.  * by the Ash General Public License.  See the file named LICENSE.
  244.  */
  245.  
  246. #include "shell.h"
  247. #include "var.h"
  248. #include "nodes.h"    /* for jobs.h */
  249. #include "jobs.h"
  250. #include "options.h"
  251. #include "output.h"
  252. #include "memalloc.h"
  253. #include "error.h"
  254. #include "mystring.h"
  255. #include <sys/types.h>
  256. #include <sys/stat.h>
  257. #include "myerrno.h"
  258.  
  259.  
  260. #ifdef __STDC__
  261. STATIC int docd(char *, int);
  262. STATIC void updatepwd(char *);
  263. STATIC void getpwd(void);
  264. STATIC char *getcomponent(void);
  265. #else
  266. STATIC int docd();
  267. STATIC void updatepwd();
  268. STATIC void getpwd();
  269. STATIC char *getcomponent();
  270. #endif
  271.  
  272.  
  273. char *curdir;            /* current working directory */
  274. STATIC char *cdcomppath;
  275.  
  276. #if UDIR
  277. extern int didudir;        /* set if /u/logname expanded */
  278. #endif
  279.  
  280.  
  281. int
  282. cdcmd(argc, argv)  char **argv; {
  283.       char *dest;
  284.       char *path;
  285.       char *p;
  286.       struct stat statb;
  287.       char *padvance();
  288.  
  289.       nextopt(nullstr);
  290.       if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
  291.         error("HOME not set");
  292.       if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
  293.         path = nullstr;
  294.       while ((p = padvance(&path, dest)) != NULL) {
  295.         if (stat(p, &statb) >= 0
  296.          && (statb.st_mode & S_IFMT) == S_IFDIR
  297.          && docd(p, strcmp(p, dest)) >= 0)
  298.           return 0;
  299.       }
  300.       error("can't cd to %s", dest);
  301. }
  302.  
  303.  
  304. /*
  305.  * Actually do the chdir.  If the name refers to symbolic links, we
  306.  * compute the actual directory name before doing the cd.  In an
  307.  * interactive shell, print the directory name if "print" is nonzero
  308.  * or if the name refers to a symbolic link.  We also print the name
  309.  * if "/u/logname" was expanded in it, since this is similar to a
  310.  * symbolic link.  (The check for this breaks if the user gives the
  311.  * cd command some additional, unused arguments.)
  312.  */
  313.  
  314. #if SYMLINKS == 0
  315. STATIC int
  316. docd(dest, print)
  317.       char *dest;
  318.       {
  319. #if UDIR
  320.       if (didudir)
  321.         print = 1;
  322. #endif
  323.       INTOFF;
  324.       if (chdir(dest) < 0) {
  325.         INTON;
  326.         return -1;
  327.       }
  328.       updatepwd(dest);
  329.       INTON;
  330.       if (print && iflag)
  331.         out1fmt("%s\n", stackblock());
  332.       return 0;
  333. }
  334.  
  335. #else
  336.  
  337.  
  338.  
  339. STATIC int
  340. docd(dest, print)
  341.       char *dest;
  342.       {
  343.       register char *p;
  344.       register char *q;
  345.       char *symlink;
  346.       char *component;
  347.       struct stat statb;
  348.       int first;
  349.       int i;
  350.  
  351.       TRACE(("docd(\"%s\", %d) called\n", dest, print));
  352. #if UDIR
  353.       if (didudir)
  354.         print = 1;
  355. #endif
  356.  
  357. top:
  358.       cdcomppath = dest;
  359.       STARTSTACKSTR(p);
  360.       if (*dest == '/') {
  361.         STPUTC('/', p);
  362.         cdcomppath++;
  363.       }
  364.       first = 1;
  365.       while ((q = getcomponent()) != NULL) {
  366.         if (q[0] == '\0' || q[0] == '.' && q[1] == '\0')
  367.           continue;
  368.         if (! first)
  369.           STPUTC('/', p);
  370.         first = 0;
  371.         component = q;
  372.         while (*q)
  373.           STPUTC(*q++, p);
  374.         if (equal(component, ".."))
  375.           continue;
  376.         STACKSTRNUL(p);
  377.         if (lstat(stackblock(), &statb) < 0)
  378.           error("lstat %s failed", stackblock());
  379.         if ((statb.st_mode & S_IFMT) != S_IFLNK)
  380.           continue;
  381.  
  382.         /* Hit a symbolic link.  We have to start all over again. */
  383.         print = 1;
  384.         STPUTC('\0', p);
  385.         symlink = grabstackstr(p);
  386.         i = (int)statb.st_size + 2;        /* 2 for '/' and '\0' */
  387.         if (cdcomppath != NULL)
  388.           i += strlen(cdcomppath);
  389.         p = stalloc(i);
  390.         if (readlink(symlink, p, (int)statb.st_size) < 0) {
  391.           error("readlink %s failed", stackblock());
  392.         }
  393.         if (cdcomppath != NULL) {
  394.           p[(int)statb.st_size] = '/';
  395.           scopy(cdcomppath, p + (int)statb.st_size + 1);
  396.         } else {
  397.           p[(int)statb.st_size] = '\0';
  398.         }
  399.         if (p[0] != '/') {    /* relative path name */
  400.           char *r;
  401.           q = r = symlink;
  402.           while (*q) {
  403.             if (*q++ == '/')
  404.                   r = q;
  405.           }
  406.           *r = '\0';
  407.           dest = stalloc(strlen(symlink) + strlen(p) + 1);
  408.           scopy(symlink, dest);
  409.           strcat(dest, p);
  410.         } else {
  411.           dest = p;
  412.         }
  413.         goto top;
  414.       }
  415.       STPUTC('\0', p);
  416.       p = grabstackstr(p);
  417.       INTOFF;
  418.       if (chdir(p) < 0) {
  419.         INTON;
  420.         return -1;
  421.       }
  422.       updatepwd(p);
  423.       INTON;
  424.       if (print && iflag)
  425.         out1fmt("%s\n", p);
  426.       return 0;
  427. }
  428. #endif /* SYMLINKS */
  429.  
  430.  
  431.  
  432. /*
  433.  * Get the next component of the path name pointed to by cdcomppath.
  434.  * This routine overwrites the string pointed to by cdcomppath.
  435.  */
  436.  
  437. STATIC char *
  438. getcomponent() {
  439.       register char *p;
  440.       char *start;
  441.  
  442.       if ((p = cdcomppath) == NULL)
  443.         return NULL;
  444.       start = cdcomppath;
  445.       while (*p != '/' && *p != '\0')
  446.         p++;
  447.       if (*p == '\0') {
  448.         cdcomppath = NULL;
  449.       } else {
  450.         *p++ = '\0';
  451.         cdcomppath = p;
  452.       }
  453.       return start;
  454. }
  455.  
  456.  
  457.  
  458. /*
  459.  * Update curdir (the name of the current directory) in response to a
  460.  * cd command.  We also call hashcd to let the routines in exec.c know
  461.  * that the current directory has changed.
  462.  */
  463.  
  464. void hashcd();
  465.  
  466. STATIC void
  467. updatepwd(dir)
  468.       char *dir;
  469.       {
  470.       char *new;
  471.       char *p;
  472.  
  473.       hashcd();                /* update command hash table */
  474.       cdcomppath = stalloc(strlen(dir) + 1);
  475.       scopy(dir, cdcomppath);
  476.       STARTSTACKSTR(new);
  477.       if (*dir != '/') {
  478.         if (curdir == NULL)
  479.           return;
  480.         p = curdir;
  481.         while (*p)
  482.           STPUTC(*p++, new);
  483.         if (p[-1] == '/')
  484.           STUNPUTC(new);
  485.       }
  486.       while ((p = getcomponent()) != NULL) {
  487.         if (equal(p, "..")) {
  488.           while (new > stackblock() && (STUNPUTC(new), *new) != '/');
  489.         } else if (*p != '\0' && ! equal(p, ".")) {
  490.           STPUTC('/', new);
  491.           while (*p)
  492.             STPUTC(*p++, new);
  493.         }
  494.       }
  495.       if (new == stackblock())
  496.         STPUTC('/', new);
  497.       STACKSTRNUL(new);
  498.       if (curdir)
  499.         ckfree(curdir);
  500.       curdir = savestr(stackblock());
  501. }
  502.  
  503.  
  504.  
  505. int
  506. pwdcmd(argc, argv)  char **argv; {
  507.       getpwd();
  508.       out1str(curdir);
  509.       out1c('\n');
  510.       return 0;
  511. }
  512.  
  513.  
  514.  
  515. /*
  516.  * Run /bin/pwd to find out what the current directory is.  We suppress
  517.  * interrupts throughout most of this, but the user can still break out
  518.  * of it by killing the pwd program.  If we already know the current
  519.  * directory, this routine returns immediately.
  520.  */
  521.  
  522. #define MAXPWD 256
  523.  
  524. STATIC void
  525. getpwd() {
  526.       char buf[MAXPWD];
  527.       char *p;
  528.       int i;
  529.       int status;
  530.       struct job *jp;
  531.       int pip[2];
  532.  
  533.       if (curdir)
  534.         return;
  535.       INTOFF;
  536.       if (pipe(pip) < 0)
  537.         error("Pipe call failed");
  538.       jp = makejob((union node *)NULL, 1);
  539.       if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
  540.         close(pip[0]);
  541.         if (pip[1] != 1) {
  542.           close(1);
  543.           copyfd(pip[1], 1);
  544.           close(pip[1]);
  545.         }
  546.         execl("/bin/pwd", "pwd", (char *)0);
  547.         error("Cannot exec /bin/pwd");
  548.       }
  549.       close(pip[1]);
  550.       pip[1] = -1;
  551.       p = buf;
  552.       while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
  553.       || i == -1 && errno == EINTR) {
  554.         if (i > 0)
  555.           p += i;
  556.       }
  557.       close(pip[0]);
  558.       pip[0] = -1;
  559.       status = waitforjob(jp);
  560.       if (status != 0)
  561.         error((char *)0);
  562.       if (i < 0 || p == buf || p[-1] != '\n')
  563.         error("pwd command failed");
  564.       p[-1] = '\0';
  565.       curdir = savestr(buf);
  566.       INTON;
  567. }
  568. EOF
  569. if test `wc -c < cd.c` -ne 7200
  570. then    echo 'cd.c is the wrong size'
  571. fi
  572. echo extracting mydirent.h
  573. cat > mydirent.h <<\EOF
  574. /*
  575.  * System V directory routines.  The BSD ones are almost the same except
  576.  * that the structure tag "direct" is used instead of "dirent" and opendir
  577.  * is replaced with myopendir (which checks that the file being opened is
  578.  * a directory).  If we don't have the BSD ones, we use our own code which
  579.  * assumes an old style directory format.  This file requires sys/types.h.
  580.  *
  581.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  582.  * This file is part of ash, which is distributed under the terms specified
  583.  * by the Ash General Public License.  See the file named LICENSE.
  584.  */
  585.  
  586. #if DIRENT        /* System V directory routines available */
  587. #include <dirent.h>
  588. #else
  589. #ifdef BSD        /* 4.2 BSD directory routines available */
  590. #include <sys/dir.h>
  591. #ifdef __STDC__
  592. DIR *myopendir(char *);
  593. #else
  594. DIR *myopendir();
  595. #endif
  596. #define dirent direct
  597. #define opendir myopendir
  598. #else            /* Use our own directory access routines */
  599. #include <sys/dir.h>
  600.  
  601. struct dirent {                /* data from readdir */
  602.       long d_ino;            /* inode number of entry */
  603.       char d_name[DIRSIZ+1];        /* name of file */    /* non-POSIX */
  604. };
  605.  
  606. #define DIRBUFENT 64
  607.  
  608. typedef struct {
  609.       struct dirent dd_entry;        /* directory entry */
  610.       int        dd_fd;        /* file descriptor */
  611.       int        dd_nleft;        /* amount of valid data */
  612.       struct direct *dd_loc;        /* location in block */
  613.       struct direct dd_buf[DIRBUFENT];    /* -> directory block */
  614. } DIR;                    /* stream data from opendir() */
  615.  
  616. #ifdef __STDC__
  617. DIR *opendir(char *);
  618. struct dirent *readdir(DIR *);
  619. int closedir(DIR *);
  620. #else
  621. DIR *opendir();
  622. struct dirent *readdir();
  623. int closedir();
  624. #endif
  625.  
  626. #endif /* BSD */
  627. #endif /* DIRENT */
  628. EOF
  629. if test `wc -c < mydirent.h` -ne 1652
  630. then    echo 'mydirent.h is the wrong size'
  631. fi
  632. echo extracting dirent.c
  633. cat > dirent.c <<\EOF
  634. /*
  635.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  636.  * This file is part of ash, which is distributed under the terms specified
  637.  * by the Ash General Public License.  See the file named LICENSE.
  638.  */
  639.  
  640. #include "shell.h"    /* definitions for pointer, NULL, DIRENT, and BSD */
  641.  
  642. #if ! DIRENT
  643.  
  644. #include "myerrno.h"
  645. #include <sys/types.h>
  646. #include <sys/stat.h>
  647. #include <fcntl.h>
  648. #include "mydirent.h"
  649.  
  650. #ifndef S_ISDIR                /* macro to test for directory file */
  651. #define    S_ISDIR(mode)        (((mode) & S_IFMT) == S_IFDIR)
  652. #endif
  653.  
  654. #ifdef BSD
  655.  
  656. #ifdef __STDC__
  657. int stat(char *, struct stat *);
  658. #else
  659. int stat();
  660. #endif
  661.  
  662.  
  663. /*
  664.  * The BSD opendir routine doesn't check that what is being opened is a
  665.  * directory, so we have to include the check in a wrapper routine.
  666.  */
  667.  
  668. #undef opendir
  669.  
  670. DIR *
  671. myopendir(dirname)
  672.       char *dirname;            /* name of directory */
  673.       {
  674.       struct stat statb;
  675.  
  676.       if (stat(dirname, &statb) != 0 || ! S_ISDIR(statb.st_mode)) {
  677.         errno = ENOTDIR;
  678.         return NULL;        /* not a directory */
  679.       }
  680.       return opendir(dirname);
  681. }
  682.  
  683. #else /* not BSD */
  684.  
  685. /*
  686.  * Dirent routines for old style file systems.
  687.  */
  688.  
  689. #ifdef __STDC__
  690. pointer malloc(unsigned);
  691. void free(pointer);
  692. int open(char *, int, ...);
  693. int close(int);
  694. int fstat(int, struct stat *);
  695. #else
  696. pointer malloc();
  697. void free();
  698. int open();
  699. int close();
  700. int fstat();
  701. #endif
  702.  
  703.  
  704. DIR *
  705. opendir(dirname)
  706.       char        *dirname;    /* name of directory */
  707.       {
  708.       register DIR    *dirp;        /* -> malloc'ed storage */
  709.       register int    fd;        /* file descriptor for read */
  710.       struct stat    statb;        /* result of fstat() */
  711.  
  712. #ifdef O_NDELAY
  713.       fd = open(dirname, O_RDONLY|O_NDELAY);
  714. #else
  715.       fd = open(dirname, O_RDONLY);
  716. #endif
  717.       if (fd < 0)
  718.         return NULL;        /* errno set by open() */
  719.  
  720.       if (fstat(fd, &statb) != 0 || !S_ISDIR(statb.st_mode)) {
  721.         (void)close(fd);
  722.         errno = ENOTDIR;
  723.         return NULL;        /* not a directory */
  724.       }
  725.  
  726.       if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
  727.         (void)close(fd);
  728.         errno = ENOMEM;
  729.         return NULL;        /* not enough memory */
  730.       }
  731.  
  732.       dirp->dd_fd = fd;
  733.       dirp->dd_nleft = 0;        /* refill needed */
  734.  
  735.       return dirp;
  736. }
  737.  
  738.  
  739.  
  740. int
  741. closedir(dirp)
  742.       register DIR *dirp;        /* stream from opendir() */
  743.       {
  744.       register int fd;
  745.  
  746.       if (dirp == NULL) {
  747.         errno = EFAULT;
  748.         return -1;            /* invalid pointer */
  749.       }
  750.  
  751.       fd = dirp->dd_fd;
  752.       free((pointer)dirp);
  753.       return close(fd);
  754. }
  755.  
  756.  
  757.  
  758. struct dirent *
  759. readdir(dirp)
  760.       register DIR *dirp;        /* stream from opendir() */
  761.       {
  762.       register struct direct *dp;
  763.       register char *p, *q;
  764.       register int i;
  765.  
  766.       do {
  767.         if ((dirp->dd_nleft -= sizeof (struct direct)) < 0) {
  768.           if ((i = read(dirp->dd_fd,
  769.                 (char *)dirp->dd_buf,
  770.                 DIRBUFENT*sizeof(struct direct))) <= 0) {
  771.             if (i == 0)
  772.                   errno = 0;    /* unnecessary */
  773.             return NULL;        /* EOF or error */
  774.           }
  775.           dirp->dd_loc = dirp->dd_buf;
  776.           dirp->dd_nleft = i - sizeof (struct direct);
  777.         }
  778.         dp = dirp->dd_loc++;
  779.       } while (dp->d_ino == 0);
  780.       dirp->dd_entry.d_ino = dp->d_ino;
  781.  
  782.       /* now copy the name, nul terminating it */
  783.       p = dp->d_name;
  784.       q = dirp->dd_entry.d_name;
  785.       i = DIRSIZ;
  786.       while (--i >= 0 && *p != '\0')
  787.         *q++ = *p++;
  788.       *q = '\0';
  789.       return &dirp->dd_entry;
  790. }
  791.  
  792. #endif /* BSD */
  793. #endif /* DIRENT */
  794. EOF
  795. if test `wc -c < dirent.c` -ne 3356
  796. then    echo 'dirent.c is the wrong size'
  797. fi
  798. echo extracting errmsg.h
  799. cat > errmsg.h <<\EOF
  800. #define E_OPEN 01
  801. #define E_CREAT 02
  802. #define E_EXEC 04
  803.  
  804. #ifdef __STDC__
  805. char *errmsg(int, int);
  806. #else
  807. char *errmsg();
  808. #endif
  809. EOF
  810. if test `wc -c < errmsg.h` -ne 125
  811. then    echo 'errmsg.h is the wrong size'
  812. fi
  813. echo extracting errmsg.c
  814. cat > errmsg.c <<\EOF
  815. #include "shell.h"
  816. #include "output.h"
  817. #include "errmsg.h"
  818. #include "myerrno.h"
  819.  
  820.  
  821. #define ALL (E_OPEN|E_CREAT|E_EXEC)
  822.  
  823.  
  824. struct errname {
  825.       short errcode;        /* error number */
  826.       short action;        /* operation which encountered the error */
  827.       char *msg;        /* text describing the error */
  828. };
  829.  
  830.  
  831. STATIC const struct errname errormsg[] = {
  832.       EINTR, ALL,    "interrupted",
  833.       EACCES, ALL,    "permission denied",
  834.       EIO, ALL,        "I/O error",
  835.       ENOENT, E_OPEN,    "no such file",
  836.       ENOENT, E_CREAT,    "directory nonexistent",
  837.       ENOENT, E_EXEC,    "not found",
  838.       ENOTDIR, E_OPEN,    "no such file",
  839.       ENOTDIR, E_CREAT,    "directory nonexistent",
  840.       ENOTDIR, E_EXEC,    "not found",
  841.       EISDIR, ALL,    "is a directory",
  842. /*    EMFILE, ALL,    "too many open files", */
  843.       ENFILE, ALL,    "file table overflow",
  844.       ENOSPC, ALL,    "file system full",
  845. #ifdef EDQUOT
  846.       EDQUOT, ALL,    "disk quota exceeded",
  847. #endif
  848. #ifdef ENOSR
  849.       ENOSR, ALL,    "no streams resources",
  850. #endif
  851.       ENXIO, ALL,    "no such device or address",
  852.       EROFS, ALL,    "read-only file system",
  853.       ETXTBSY, ALL,    "text busy",
  854. #ifdef SYSV
  855.       EAGAIN, E_EXEC,    "not enough memory",
  856. #endif
  857.       ENOMEM, ALL,    "not enough memory",
  858. #ifdef ENOLINK
  859.       ENOLINK, ALL,    "remote access failed"
  860. #endif
  861. #ifdef EMULTIHOP
  862.       EMULTIHOP, ALL,    "remote access failed",
  863. #endif
  864. #ifdef ECOMM
  865.       ECOMM, ALL,    "remote access failed",
  866. #endif
  867. #ifdef ESTALE
  868.       ESTALE, ALL,    "remote access failed",
  869. #endif
  870. #ifdef ETIMEDOUT
  871.       ETIMEDOUT, ALL,    "remote access failed",
  872. #endif
  873. #ifdef ELOOP
  874.       ELOOP, ALL,    "symbolic link loop",
  875. #endif
  876.       E2BIG, E_EXEC,    "argument list too long",
  877. #ifdef ELIBACC
  878.       ELIBACC, E_EXEC,    "shared library missing",
  879. #endif
  880.       0, 0,        NULL
  881. };
  882.  
  883.  
  884. /*
  885.  * Return a string describing an error.  The returned string may be a
  886.  * pointer to a static buffer that will be overwritten on the next call.
  887.  * Action describes the operation that got the error.
  888.  */
  889.  
  890. char *
  891. errmsg(e, action) {
  892.       struct errname const *ep;
  893.       static char buf[12];
  894.  
  895.       for (ep = errormsg ; ep->errcode ; ep++) {
  896.         if (ep->errcode == e && (ep->action & action) != 0)
  897.           return ep->msg;
  898.       }
  899.       fmtstr(buf, sizeof buf, "error %d", e);
  900.       return buf;
  901. }
  902. EOF
  903. if test `wc -c < errmsg.c` -ne 2226
  904. then    echo 'errmsg.c is the wrong size'
  905. fi
  906. echo extracting eval.h
  907. cat > eval.h <<\EOF
  908. extern char *commandname;    /* currently executing command */
  909. extern int exitstatus;        /* exit status of last command */
  910. extern struct strlist *cmdenviron;  /* environment for builtin command */
  911.  
  912.  
  913. struct backcmd {        /* result of evalbackcmd */
  914.       int fd;            /* file descriptor to read from */
  915.       char *buf;        /* buffer */
  916.       int nleft;        /* number of chars in buffer */
  917.       struct job *jp;        /* job structure for command */
  918. };
  919.  
  920.  
  921. #ifdef __STDC__
  922. void evalstring(char *);
  923. void evaltree(union node *, int);
  924. void evalbackcmd(union node *, struct backcmd *);
  925. #else
  926. void evalstring();
  927. void evaltree();
  928. void evalbackcmd();
  929. #endif
  930.  
  931. /* in_function returns nonzero if we are currently evaluating a function */
  932. #define in_function()    funcnest
  933. extern int funcnest;
  934. EOF
  935. if test `wc -c < eval.h` -ne 755
  936. then    echo 'eval.h is the wrong size'
  937. fi
  938. echo extracting eval.c
  939. cat > eval.c <<\EOF
  940. /*
  941.  * Evaluate a command.
  942.  *
  943.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  944.  * This file is part of ash, which is distributed under the terms specified
  945.  * by the Ash General Public License.  See the file named LICENSE.
  946.  */
  947.  
  948. #include "shell.h"
  949. #include "nodes.h"
  950. #include "syntax.h"
  951. #include "expand.h"
  952. #include "parser.h"
  953. #include "jobs.h"
  954. #include "eval.h"
  955. #include "builtins.h"
  956. #include "options.h"
  957. #include "exec.h"
  958. #include "redir.h"
  959. #include "input.h"
  960. #include "output.h"
  961. #include "trap.h"
  962. #include "var.h"
  963. #include "memalloc.h"
  964. #include "error.h"
  965. #include "mystring.h"
  966. #include <signal.h>
  967.  
  968.  
  969. /* flags in argument to evaltree */
  970. #define EV_EXIT 01        /* exit after evaluating tree */
  971. #define EV_TESTED 02        /* exit status is checked; ignore -e flag */
  972. #define EV_BACKCMD 04        /* command executing within back quotes */
  973.  
  974.  
  975. /* reasons for skipping commands (see comment on breakcmd routine) */
  976. #define SKIPBREAK 1
  977. #define SKIPCONT 2
  978. #define SKIPFUNC 3
  979.  
  980. MKINIT int evalskip;        /* set if we are skipping commands */
  981. STATIC int skipcount;        /* number of levels to skip */
  982. MKINIT int loopnest;        /* current loop nesting level */
  983. int funcnest;            /* depth of function calls */
  984.  
  985.  
  986. char *commandname;
  987. struct strlist *cmdenviron;
  988. int exitstatus;            /* exit status of last command */
  989.  
  990.  
  991. #ifdef __STDC__
  992. STATIC void evalloop(union node *);
  993. STATIC void evalfor(union node *);
  994. STATIC void evalcase(union node *, int);
  995. STATIC void evalsubshell(union node *, int);
  996. STATIC void expredir(union node *);
  997. STATIC void evalpipe(union node *);
  998. STATIC void evalcommand(union node *, int, struct backcmd *);
  999. STATIC void prehash(union node *);
  1000. #else
  1001. STATIC void evalloop();
  1002. STATIC void evalfor();
  1003. STATIC void evalcase();
  1004. STATIC void evalsubshell();
  1005. STATIC void expredir();
  1006. STATIC void evalpipe();
  1007. STATIC void evalcommand();
  1008. STATIC void prehash();
  1009. #endif
  1010.  
  1011.  
  1012.  
  1013. /*
  1014.  * Called to reset things after an exception.
  1015.  */
  1016.  
  1017. #ifdef mkinit
  1018. INCLUDE "eval.h"
  1019.  
  1020. RESET {
  1021.       evalskip = 0;
  1022.       loopnest = 0;
  1023.       funcnest = 0;
  1024. }
  1025.  
  1026. SHELLPROC {
  1027.       exitstatus = 0;
  1028. }
  1029. #endif
  1030.  
  1031.  
  1032.  
  1033. /*
  1034.  * The eval builtin.  Do you want clean, straight-forward semantics for
  1035.  * your eval command?  If so, read on....
  1036.  */
  1037.  
  1038. #ifdef ELIGANT
  1039. evalcmd(argc, argv)  char **argv; {
  1040.       char **ap;
  1041.  
  1042.       for (ap = argv + 1 ; *ap ; ap++) {
  1043.         evalstring(*ap);
  1044.       }
  1045.       return exitstatus;
  1046. }
  1047. #else
  1048.  
  1049. /*
  1050.  * If, on the other hand, you prefer downright bogus semantics in the
  1051.  * name of compatibility, here it is...
  1052.  */
  1053.  
  1054. evalcmd(argc, argv)  char **argv; {
  1055.       char *p;
  1056.       char *concat;
  1057.       char **ap;
  1058.  
  1059.       if (argc > 1) {
  1060.         p = argv[1];
  1061.         if (argc > 2) {
  1062.           STARTSTACKSTR(concat);
  1063.           ap = argv + 2;
  1064.           for (;;) {
  1065.             while (*p)
  1066.                   STPUTC(*p++, concat);
  1067.             if ((p = *ap++) == NULL)
  1068.                   break;
  1069.             STPUTC(' ', concat);
  1070.           }
  1071.           STPUTC('\0', concat);
  1072.           p = grabstackstr(concat);
  1073.         }
  1074.         evalstring(p);
  1075.       }
  1076.       return exitstatus;
  1077. }
  1078. #endif
  1079.  
  1080.  
  1081.  
  1082. /*
  1083.  * Execute a command or commands contained in a string.
  1084.  */
  1085.  
  1086. void
  1087. evalstring(s)
  1088.       char *s;
  1089.       {
  1090.       union node *n;
  1091.       struct stackmark smark;
  1092.  
  1093.       setstackmark(&smark);
  1094.       setinputstring(s, 1);
  1095.       while ((n = parsecmd(0)) != NEOF) {
  1096.         evaltree(n, 0);
  1097.         popstackmark(&smark);
  1098.       }
  1099.       popfile();
  1100.       popstackmark(&smark);
  1101. }
  1102.  
  1103.  
  1104.  
  1105. /*
  1106.  * Evaluate a parse tree.  The value is left in the global variable
  1107.  * exitstatus.
  1108.  */
  1109.  
  1110. void
  1111. evaltree(n, flags)
  1112.       union node *n;
  1113.       {
  1114.       if (n == NULL) {
  1115.         TRACE(("evaltree(NULL) called\n"));
  1116.         return;
  1117.       }
  1118.       TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type));
  1119.       switch (n->type) {
  1120.       case NSEMI:
  1121.         evaltree(n->nbinary.ch1, 0);
  1122.         if (evalskip)
  1123.           goto out;
  1124.         evaltree(n->nbinary.ch2, flags);
  1125.         break;
  1126.       case NAND:
  1127.         evaltree(n->nbinary.ch1, EV_TESTED);
  1128.         if (evalskip || exitstatus != 0)
  1129.           goto out;
  1130.         evaltree(n->nbinary.ch2, flags);
  1131.         break;
  1132.       case NOR:
  1133.         evaltree(n->nbinary.ch1, EV_TESTED);
  1134.         if (evalskip || exitstatus == 0)
  1135.           goto out;
  1136.         evaltree(n->nbinary.ch2, flags);
  1137.         break;
  1138.       case NREDIR:
  1139.         expredir(n->nredir.redirect);
  1140.         redirect(n->nredir.redirect, REDIR_PUSH);
  1141.         evaltree(n->nredir.n, flags);
  1142.         popredir();
  1143.         break;
  1144.       case NSUBSHELL:
  1145.         evalsubshell(n, flags);
  1146.         break;
  1147.       case NBACKGND:
  1148.         evalsubshell(n, flags);
  1149.         break;
  1150.       case NIF:
  1151.         evaltree(n->nif.test, EV_TESTED);
  1152.         if (evalskip)
  1153.           goto out;
  1154.         if (exitstatus == 0) {
  1155.           evaltree(n->nif.ifpart, flags);
  1156.         } else if (n->nif.elsepart) {
  1157.           evaltree(n->nif.elsepart, flags);
  1158.         }
  1159.         break;
  1160.       case NWHILE:
  1161.       case NUNTIL:
  1162.         evalloop(n);
  1163.         break;
  1164.       case NFOR:
  1165.         evalfor(n);
  1166.         break;
  1167.       case NCASE:
  1168.         evalcase(n, flags);
  1169.         break;
  1170.       case NDEFUN:
  1171.         defun(n->narg.text, n->narg.next);
  1172.         exitstatus = 0;
  1173.         break;
  1174.       case NPIPE:
  1175.         evalpipe(n);
  1176.         break;
  1177.       case NCMD:
  1178.         evalcommand(n, flags, (struct backcmd *)NULL);
  1179.         break;
  1180.       default:
  1181.         out1fmt("Node type = %d\n", n->type);
  1182.         flushout(&output);
  1183.         break;
  1184.       }
  1185. out:
  1186.       if (sigpending)
  1187.         dotrap();
  1188.       if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
  1189.         exitshell(exitstatus);
  1190. }
  1191.  
  1192.  
  1193. STATIC void
  1194. evalloop(n)
  1195.       union node *n;
  1196.       {
  1197.       int status;
  1198.  
  1199.       loopnest++;
  1200.       status = 0;
  1201.       for (;;) {
  1202.         evaltree(n->nbinary.ch1, EV_TESTED);
  1203.         if (evalskip) {
  1204. skipping:      if (evalskip == SKIPCONT && --skipcount <= 0) {
  1205.             evalskip = 0;
  1206.             continue;
  1207.           }
  1208.           if (evalskip == SKIPBREAK && --skipcount <= 0)
  1209.             evalskip = 0;
  1210.           break;
  1211.         }
  1212.         if (n->type == NWHILE) {
  1213.           if (exitstatus != 0)
  1214.             break;
  1215.         } else {
  1216.           if (exitstatus == 0)
  1217.             break;
  1218.         }
  1219.         evaltree(n->nbinary.ch2, 0);
  1220.         status = exitstatus;
  1221.         if (evalskip)
  1222.           goto skipping;
  1223.       }
  1224.       loopnest--;
  1225.       exitstatus = status;
  1226. }
  1227.  
  1228.  
  1229.  
  1230. STATIC void
  1231. evalfor(n)
  1232.       union node *n;
  1233.       {
  1234.       struct arglist arglist;
  1235.       union node *argp;
  1236.       struct strlist *sp;
  1237.       struct stackmark smark;
  1238.  
  1239.       setstackmark(&smark);
  1240.       arglist.lastp = &arglist.list;
  1241.       for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
  1242.         expandarg(argp, &arglist, 1);
  1243.         if (evalskip)
  1244.           goto out;
  1245.       }
  1246.       *arglist.lastp = NULL;
  1247.  
  1248.       exitstatus = 0;
  1249.       loopnest++;
  1250.       for (sp = arglist.list ; sp ; sp = sp->next) {
  1251.         setvar(n->nfor.var, sp->text, 0);
  1252.         evaltree(n->nfor.body, 0);
  1253.         if (evalskip) {
  1254.           if (evalskip == SKIPCONT && --skipcount <= 0) {
  1255.             evalskip = 0;
  1256.             continue;
  1257.           }
  1258.           if (evalskip == SKIPBREAK && --skipcount <= 0)
  1259.             evalskip = 0;
  1260.           break;
  1261.         }
  1262.       }
  1263.       loopnest--;
  1264. out:
  1265.       popstackmark(&smark);
  1266. }
  1267.  
  1268.  
  1269.  
  1270. STATIC void
  1271. evalcase(n, flags)
  1272.       union node *n;
  1273.       {
  1274.       union node *cp;
  1275.       union node *patp;
  1276.       struct arglist arglist;
  1277.       struct stackmark smark;
  1278.  
  1279.       setstackmark(&smark);
  1280.       arglist.lastp = &arglist.list;
  1281.       expandarg(n->ncase.expr, &arglist, 0);
  1282.       for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
  1283.         for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
  1284.           if (casematch(patp, arglist.list->text)) {
  1285.             if (evalskip == 0) {
  1286.                   evaltree(cp->nclist.body, flags);
  1287.             }
  1288.             goto out;
  1289.           }
  1290.         }
  1291.       }
  1292. out:
  1293.       popstackmark(&smark);
  1294. }
  1295.  
  1296.  
  1297.  
  1298. /*
  1299.  * Kick off a subshell to evaluate a tree.
  1300.  */
  1301.  
  1302. STATIC void
  1303. evalsubshell(n, flags)
  1304.       union node *n;
  1305.       {
  1306.       struct job *jp;
  1307.       int backgnd = (n->type == NBACKGND);
  1308.  
  1309.       expredir(n->nredir.redirect);
  1310.       jp = makejob(n, 1);
  1311.       if (forkshell(jp, n, backgnd) == 0) {
  1312.         if (backgnd)
  1313.           flags &=~ EV_TESTED;
  1314.         redirect(n->nredir.redirect, 0);
  1315.         evaltree(n->nredir.n, flags | EV_EXIT);    /* never returns */
  1316.       }
  1317.       if (! backgnd) {
  1318.         INTOFF;
  1319.         exitstatus = waitforjob(jp);
  1320.         INTON;
  1321.       }
  1322. }
  1323.  
  1324.  
  1325.  
  1326. /*
  1327.  * Compute the names of the files in a redirection list.
  1328.  */
  1329.  
  1330. STATIC void
  1331. expredir(n)
  1332.       union node *n;
  1333.       {
  1334.       register union node *redir;
  1335.  
  1336.       for (redir = n ; redir ; redir = redir->nfile.next) {
  1337.         if (redir->type == NFROM
  1338.          || redir->type == NTO
  1339.          || redir->type == NAPPEND) {
  1340.           struct arglist fn;
  1341.           fn.lastp = &fn.list;
  1342.           expandarg(redir->nfile.fname, &fn, 0);
  1343.           redir->nfile.expfname = fn.list->text;
  1344.         }
  1345.       }
  1346. }
  1347.  
  1348.  
  1349.  
  1350. /*
  1351.  * Evaluate a pipeline.  All the processes in the pipeline are children
  1352.  * of the process creating the pipeline.  (This differs from some versions
  1353.  * of the shell, which make the last process in a pipeline the parent
  1354.  * of all the rest.)
  1355.  */
  1356.  
  1357. STATIC void
  1358. evalpipe(n)
  1359.       union node *n;
  1360.       {
  1361.       struct job *jp;
  1362.       struct nodelist *lp;
  1363.       int pipelen;
  1364.       int prevfd;
  1365.       int pip[2];
  1366.  
  1367.       TRACE(("evalpipe(0x%x) called\n", (int)n));
  1368.       pipelen = 0;
  1369.       for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
  1370.         pipelen++;
  1371.       INTOFF;
  1372.       jp = makejob(n, pipelen);
  1373.       prevfd = -1;
  1374.       for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
  1375.         prehash(lp->n);
  1376.         pip[1] = -1;
  1377.         if (lp->next) {
  1378.           if (pipe(pip) < 0) {
  1379.             close(prevfd);
  1380.             error("Pipe call failed");
  1381.           }
  1382.         }
  1383.         if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
  1384.           INTON;
  1385.           if (prevfd > 0) {
  1386.             close(0);
  1387.             copyfd(prevfd, 0);
  1388.             close(prevfd);
  1389.           }
  1390.           if (pip[1] >= 0) {
  1391.             close(pip[0]);
  1392.             if (pip[1] != 1) {
  1393.                   close(1);
  1394.                   copyfd(pip[1], 1);
  1395.                   close(pip[1]);
  1396.             }
  1397.           }
  1398.           evaltree(lp->n, EV_EXIT);
  1399.         }
  1400.         if (prevfd >= 0)
  1401.           close(prevfd);
  1402.         prevfd = pip[0];
  1403.         close(pip[1]);
  1404.       }
  1405.       INTON;
  1406.       if (n->npipe.backgnd == 0) {
  1407.         INTOFF;
  1408.         exitstatus = waitforjob(jp);
  1409.         TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
  1410.         INTON;
  1411.       }
  1412. }
  1413.  
  1414.  
  1415.  
  1416. /*
  1417.  * Execute a command inside back quotes.  If it's a builtin command, we
  1418.  * want to save its output in a block obtained from malloc.  Otherwise
  1419.  * we fork off a subprocess and get the output of the command via a pipe.
  1420.  * Should be called with interrupts off.
  1421.  */
  1422.  
  1423. void
  1424. evalbackcmd(n, result)
  1425.       union node *n;
  1426.       struct backcmd *result;
  1427.       {
  1428.       int pip[2];
  1429.       struct job *jp;
  1430.       struct stackmark smark;        /* unnecessary */
  1431.  
  1432.       setstackmark(&smark);
  1433.       result->fd = -1;
  1434.       result->buf = NULL;
  1435.       result->nleft = 0;
  1436.       result->jp = NULL;
  1437.       if (n->type == NCMD) {
  1438.         evalcommand(n, EV_BACKCMD, result);
  1439.       } else {
  1440.         if (pipe(pip) < 0)
  1441.           error("Pipe call failed");
  1442.         jp = makejob(n, 1);
  1443.         if (forkshell(jp, n, FORK_NOJOB) == 0) {
  1444.           FORCEINTON;
  1445.           close(pip[0]);
  1446.           if (pip[1] != 1) {
  1447.             close(1);
  1448.             copyfd(pip[1], 1);
  1449.             close(pip[1]);
  1450.           }
  1451.           evaltree(n, EV_EXIT);
  1452.         }
  1453.         close(pip[1]);
  1454.         result->fd = pip[0];
  1455.         result->jp = jp;
  1456.       }
  1457.       popstackmark(&smark);
  1458.       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
  1459.         result->fd, result->buf, result->nleft, result->jp));
  1460. }
  1461.  
  1462.  
  1463.  
  1464. /*
  1465.  * Execute a simple command.
  1466.  */
  1467.  
  1468. STATIC void
  1469. evalcommand(cmd, flags, backcmd)
  1470.       union node *cmd;
  1471.       struct backcmd *backcmd;
  1472.       {
  1473.       struct stackmark smark;
  1474.       union node *argp;
  1475.       struct arglist arglist;
  1476.       struct arglist varlist;
  1477.       char **argv;
  1478.       int argc;
  1479.       char **envp;
  1480.       int varflag;
  1481.       struct strlist *sp;
  1482.       register char *p;
  1483.       int mode;
  1484.       int pip[2];
  1485.       struct cmdentry cmdentry;
  1486.       struct job *jp;
  1487.       struct jmploc jmploc;
  1488.       struct jmploc *volatile savehandler;
  1489.       char *volatile savecmdname;
  1490.       volatile struct shparam saveparam;
  1491.       struct localvar *volatile savelocalvars;
  1492.       volatile int e;
  1493.       char *lastarg;
  1494.  
  1495.       /* First expand the arguments. */
  1496.       TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags));
  1497.       setstackmark(&smark);
  1498.       arglist.lastp = &arglist.list;
  1499.       varlist.lastp = &varlist.list;
  1500.       varflag = 1;
  1501.       for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
  1502.         p = argp->narg.text;
  1503.         if (varflag && is_name(*p)) {
  1504.           do {
  1505.             p++;
  1506.           } while (is_in_name(*p));
  1507.           if (*p == '=') {
  1508.             expandarg(argp, &varlist, 0);
  1509.             continue;
  1510.           }
  1511.         }
  1512.         expandarg(argp, &arglist, 1);
  1513.         varflag = 0;
  1514.       }
  1515.       *arglist.lastp = NULL;
  1516.       *varlist.lastp = NULL;
  1517.       expredir(cmd->ncmd.redirect);
  1518.       argc = 0;
  1519.       for (sp = arglist.list ; sp ; sp = sp->next)
  1520.         argc++;
  1521.       argv = stalloc(sizeof (char *) * (argc + 1));
  1522.       for (sp = arglist.list ; sp ; sp = sp->next)
  1523.         *argv++ = sp->text;
  1524.       *argv = NULL;
  1525.       lastarg = NULL;
  1526.       if (iflag && funcnest == 0 && argc > 0)
  1527.         lastarg = argv[-1];
  1528.       argv -= argc;
  1529.  
  1530.       /* Print the command if xflag is set. */
  1531.       if (xflag) {
  1532.         outc('+', &errout);
  1533.         for (sp = varlist.list ; sp ; sp = sp->next) {
  1534.           outc(' ', &errout);
  1535.           out2str(sp->text);
  1536.         }
  1537.         for (sp = arglist.list ; sp ; sp = sp->next) {
  1538.           outc(' ', &errout);
  1539.           out2str(sp->text);
  1540.         }
  1541.         outc('\n', &errout);
  1542.         flushout(&errout);
  1543.       }
  1544.  
  1545.       /* Now locate the command. */
  1546.       if (argc == 0) {
  1547.         cmdentry.cmdtype = CMDBUILTIN;
  1548.         cmdentry.u.index = BLTINCMD;
  1549.       } else {
  1550.         find_command(argv[0], &cmdentry, 1);
  1551.         if (cmdentry.cmdtype == CMDUNKNOWN) {    /* command not found */
  1552.           exitstatus = 2;
  1553.           flushout(&errout);
  1554.           return;
  1555.         }
  1556.         /* implement the bltin builtin here */
  1557.         if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
  1558.           for (;;) {
  1559.             argv++;
  1560.             if (--argc == 0)
  1561.                   break;
  1562.             if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
  1563.                   outfmt(&errout, "%s: not found\n", *argv);
  1564.                   exitstatus = 2;
  1565.                   flushout(&errout);
  1566.                   return;
  1567.             }
  1568.             if (cmdentry.u.index != BLTINCMD)
  1569.                   break;
  1570.           }
  1571.         }
  1572.       }
  1573.  
  1574.       /* Fork off a child process if necessary. */
  1575.       if (cmd->ncmd.backgnd
  1576.        || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0
  1577.        || (flags & EV_BACKCMD) != 0
  1578.      && (cmdentry.cmdtype != CMDBUILTIN
  1579.          || cmdentry.u.index == DOTCMD
  1580.          || cmdentry.u.index == EVALCMD)) {
  1581.         jp = makejob(cmd, 1);
  1582.         mode = cmd->ncmd.backgnd;
  1583.         if (flags & EV_BACKCMD) {
  1584.           mode = FORK_NOJOB;
  1585.           if (pipe(pip) < 0)
  1586.             error("Pipe call failed");
  1587.         }
  1588.         if (forkshell(jp, cmd, mode) != 0)
  1589.           goto parent;    /* at end of routine */
  1590.         if (flags & EV_BACKCMD) {
  1591.           FORCEINTON;
  1592.           close(pip[0]);
  1593.           if (pip[1] != 1) {
  1594.             close(1);
  1595.             copyfd(pip[1], 1);
  1596.             close(pip[1]);
  1597.           }
  1598.         }
  1599.         flags |= EV_EXIT;
  1600.       }
  1601.  
  1602.       /* This is the child process if a fork occurred. */
  1603.       /* Execute the command. */
  1604.       if (cmdentry.cmdtype == CMDFUNCTION) {
  1605.         trputs("Shell function:  ");  trargs(argv);
  1606.         redirect(cmd->ncmd.redirect, REDIR_PUSH);
  1607.         saveparam = shellparam;
  1608.         shellparam.malloc = 0;
  1609.         shellparam.nparam = argc - 1;
  1610.         shellparam.p = argv + 1;
  1611.         shellparam.optnext = NULL;
  1612.         INTOFF;
  1613.         savelocalvars = localvars;
  1614.         localvars = NULL;
  1615.         INTON;
  1616.         if (setjmp(jmploc.loc)) {
  1617.           if (exception == EXSHELLPROC)
  1618.             freeparam((struct shparam *)&saveparam);
  1619.           else {
  1620.             freeparam(&shellparam);
  1621.             shellparam = saveparam;
  1622.           }
  1623.           poplocalvars();
  1624.           localvars = savelocalvars;
  1625.           handler = savehandler;
  1626.           longjmp(handler->loc, 1);
  1627.         }
  1628.         savehandler = handler;
  1629.         handler = &jmploc;
  1630.         for (sp = varlist.list ; sp ; sp = sp->next)
  1631.           mklocal(sp->text);
  1632.         funcnest++;
  1633.         evaltree(cmdentry.u.func, 0);
  1634.         funcnest--;
  1635.         INTOFF;
  1636.         poplocalvars();
  1637.         localvars = savelocalvars;
  1638.         freeparam(&shellparam);
  1639.         shellparam = saveparam;
  1640.         handler = savehandler;
  1641.         popredir();
  1642.         INTON;
  1643.         if (evalskip == SKIPFUNC) {
  1644.           evalskip = 0;
  1645.           skipcount = 0;
  1646.         }
  1647.         if (flags & EV_EXIT)
  1648.           exitshell(exitstatus);
  1649.       } else if (cmdentry.cmdtype == CMDBUILTIN) {
  1650.         trputs("builtin command:  ");  trargs(argv);
  1651.         mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
  1652.         if (flags == EV_BACKCMD) {
  1653.           memout.nleft = 0;
  1654.           memout.nextc = memout.buf;
  1655.           memout.bufsize = 64;
  1656.           mode |= REDIR_BACKQ;
  1657.         }
  1658.         redirect(cmd->ncmd.redirect, mode);
  1659.         savecmdname = commandname;
  1660.         cmdenviron = varlist.list;
  1661.         e = -1;
  1662.         if (setjmp(jmploc.loc)) {
  1663.           e = exception;
  1664.           exitstatus = (e == EXINT)? SIGINT+128 : 2;
  1665.           goto cmddone;
  1666.         }
  1667.         savehandler = handler;
  1668.         handler = &jmploc;
  1669.         commandname = argv[0];
  1670.         argptr = argv + 1;
  1671.         optptr = NULL;            /* initialize nextopt */
  1672.         exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
  1673.         flushall();
  1674. cmddone:
  1675.         out1 = &output;
  1676.         out2 = &errout;
  1677.         freestdout();
  1678.         if (e != EXSHELLPROC) {
  1679.           commandname = savecmdname;
  1680.           if (flags & EV_EXIT) {
  1681.             exitshell(exitstatus);
  1682.           }
  1683.         }
  1684.         handler = savehandler;
  1685.         if (e != -1) {
  1686.           if (e != EXERROR || cmdentry.u.index == BLTINCMD
  1687.                    || cmdentry.u.index == DOTCMD
  1688.                    || cmdentry.u.index == EVALCMD
  1689.                    || cmdentry.u.index == EXECCMD)
  1690.             raise(e);
  1691.           FORCEINTON;
  1692.         }
  1693.         if (cmdentry.u.index != EXECCMD)
  1694.           popredir();
  1695.         if (flags == EV_BACKCMD) {
  1696.           backcmd->buf = memout.buf;
  1697.           backcmd->nleft = memout.nextc - memout.buf;
  1698.           memout.buf = NULL;
  1699.         }
  1700.       } else {
  1701.         trputs("normal command:  ");  trargs(argv);
  1702.         clearredir();
  1703.         redirect(cmd->ncmd.redirect, 0);
  1704.         if (varlist.list) {
  1705.           p = stalloc(strlen(pathval()) + 1);
  1706.           scopy(pathval(), p);
  1707.         } else {
  1708.           p = pathval();
  1709.         }
  1710.         for (sp = varlist.list ; sp ; sp = sp->next)
  1711.           setvareq(sp->text, VEXPORT|VSTACK);
  1712.         envp = environment();
  1713.         shellexec(argv, envp, p, cmdentry.u.index);
  1714.         /*NOTREACHED*/
  1715.       }
  1716.       goto out;
  1717.  
  1718. parent:    /* parent process gets here (if we forked) */
  1719.       if (mode == 0) {    /* argument to fork */
  1720.         INTOFF;
  1721.         exitstatus = waitforjob(jp);
  1722.         INTON;
  1723.       } else if (mode == 2) {
  1724.         backcmd->fd = pip[0];
  1725.         close(pip[1]);
  1726.         backcmd->jp = jp;
  1727.       }
  1728.  
  1729. out:
  1730.       if (lastarg)
  1731.         setvar("_", lastarg, 0);
  1732.       popstackmark(&smark);
  1733. }
  1734.  
  1735.  
  1736.  
  1737. /*
  1738.  * Search for a command.  This is called before we fork so that the
  1739.  * location of the command will be available in the parent as well as
  1740.  * the child.  The check for "goodname" is an overly conservative
  1741.  * check that the name will not be subject to expansion.
  1742.  */
  1743.  
  1744. STATIC void
  1745. prehash(n)
  1746.       union node *n;
  1747.       {
  1748.       struct cmdentry entry;
  1749.  
  1750.       if (n->type == NCMD && goodname(n->ncmd.args->narg.text))
  1751.         find_command(n->ncmd.args->narg.text, &entry, 0);
  1752. }
  1753.  
  1754.  
  1755.  
  1756. /*
  1757.  * Builtin commands.  Builtin commands whose functions are closely
  1758.  * tied to evaluation are implemented here.
  1759.  */
  1760.  
  1761. /*
  1762.  * No command given, or a bltin command with no arguments.  Set the
  1763.  * specified variables.
  1764.  */
  1765.  
  1766. bltincmd(argc, argv)  char **argv; {
  1767.       listsetvar(cmdenviron);
  1768.       return exitstatus;
  1769. }
  1770.  
  1771.  
  1772. /*
  1773.  * Handle break and continue commands.  Break, continue, and return are
  1774.  * all handled by setting the evalskip flag.  The evaluation routines
  1775.  * above all check this flag, and if it is set they start skipping
  1776.  * commands rather than executing them.  The variable skipcount is
  1777.  * the number of loops to break/continue, or the number of function
  1778.  * levels to return.  (The latter is always 1.)  It should probably
  1779.  * be an error to break out of more loops than exist, but it isn't
  1780.  * in the standard shell so we don't make it one here.
  1781.  */
  1782.  
  1783. breakcmd(argc, argv)  char **argv; {
  1784.       int n;
  1785.  
  1786.       n = 1;
  1787.       if (argc > 1)
  1788.         n = number(argv[1]);
  1789.       if (n > loopnest)
  1790.         n = loopnest;
  1791.       if (n > 0) {
  1792.         evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
  1793.         skipcount = n;
  1794.       }
  1795.       return 0;
  1796. }
  1797.  
  1798.  
  1799. /*
  1800.  * The return command.
  1801.  */
  1802.  
  1803. returncmd(argc, argv)  char **argv; {
  1804.       int ret;
  1805.  
  1806.       ret = exitstatus;
  1807.       if (argc > 1)
  1808.         ret = number(argv[1]);
  1809.       if (funcnest) {
  1810.         evalskip = SKIPFUNC;
  1811.         skipcount = 1;
  1812.       }
  1813.       return ret;
  1814. }
  1815.  
  1816.  
  1817. truecmd(argc, argv)  char **argv; {
  1818.       return 0;
  1819. }
  1820.  
  1821.  
  1822. execcmd(argc, argv)  char **argv; {
  1823.       if (argc > 1) {
  1824.         iflag = 0;        /* exit on error */
  1825.         setinteractive(0);
  1826. #if JOBS
  1827.         jflag = 0;
  1828.         setjobctl(0);
  1829. #endif
  1830.         shellexec(argv + 1, environment(), pathval(), 0);
  1831.  
  1832.       }
  1833.       return 0;
  1834. }
  1835. EOF
  1836. if test `wc -c < eval.c` -ne 20104
  1837. then    echo 'eval.c is the wrong size'
  1838. fi
  1839. echo extracting error.h
  1840. cat > error.h <<\EOF
  1841. /*
  1842.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1843.  * This file is part of ash, which is distributed under the terms specified
  1844.  * by the Ash General Public License.  See the file named LICENSE.
  1845.  */
  1846.  
  1847. /*
  1848.  * Types of operations (passed to the errmsg routine).
  1849.  */
  1850.  
  1851. #define E_OPEN 01    /* opening a file */
  1852. #define E_CREAT 02    /* creating a file */
  1853. #define E_EXEC 04    /* executing a program */
  1854.  
  1855.  
  1856. /*
  1857.  * We enclose jmp_buf in a structure so that we can declare pointers to
  1858.  * jump locations.  The global variable handler contains the location to
  1859.  * jump to when an exception occurs, and the global variable exception
  1860.  * contains a code identifying the exeception.  To implement nested
  1861.  * exception handlers, the user should save the value of handler on entry
  1862.  * to an inner scope, set handler to point to a jmploc structure for the
  1863.  * inner scope, and restore handler on exit from the scope.
  1864.  */
  1865.  
  1866. #include <setjmp.h>
  1867.  
  1868. struct jmploc {
  1869.       jmp_buf loc;
  1870. };
  1871.  
  1872. extern struct jmploc *handler;
  1873. extern int exception;
  1874.  
  1875. /* exceptions */
  1876. #define EXINT 0        /* SIGINT received */
  1877. #define EXERROR 1    /* a generic error */
  1878. #define EXSHELLPROC 2    /* execute a shell procedure */
  1879.  
  1880.  
  1881. /*
  1882.  * These macros allow the user to suspend the handling of interrupt signals
  1883.  * over a period of time.  This is similar to SIGHOLD to or sigblock, but
  1884.  * much more efficient and portable.  (But hacking the kernel is so much
  1885.  * more fun than worrying about efficiency and portability. :-))
  1886.  */
  1887.  
  1888. extern volatile int suppressint;
  1889. extern volatile int intpending;
  1890. extern char *commandname;    /* name of command--printed on error */
  1891.  
  1892. #define INTOFF suppressint++
  1893. #define INTON if (--suppressint == 0 && intpending) onint(); else
  1894. #define FORCEINTON {suppressint = 0; if (intpending) onint();}
  1895. #define CLEAR_PENDING_INT intpending = 0
  1896. #define int_pending() intpending
  1897.  
  1898. #ifdef __STDC__
  1899. void raise(int);
  1900. void onint(void);
  1901. void error2(char *, char *);
  1902. void error(char *, ...);
  1903. char *errmsg(int, int);
  1904. #else
  1905. void raise();
  1906. void onint();
  1907. void error2();
  1908. void error();
  1909. char *errmsg();
  1910. #endif
  1911.  
  1912.  
  1913. /*
  1914.  * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
  1915.  * so we use _setjmp instead.
  1916.  */
  1917.  
  1918. #ifdef BSD
  1919. #define setjmp(jmploc)    _setjmp(jmploc)
  1920. #define longjmp(jmploc, val)    _longjmp(jmploc, val)
  1921. #endif
  1922. EOF
  1923. if test `wc -c < error.h` -ne 2269
  1924. then    echo 'error.h is the wrong size'
  1925. fi
  1926. echo extracting error.c
  1927. cat > error.c <<\EOF
  1928. /*
  1929.  * Errors and exceptions.
  1930.  *
  1931.  * Copyright (C) 1989 by Kenneth Almquist.  All rights reserved.
  1932.  * This file is part of ash, which is distributed under the terms specified
  1933.  * by the Ash General Public License.  See the file named LICENSE.
  1934.  */
  1935.  
  1936. #include "shell.h"
  1937. #include "main.h"
  1938. #include "options.h"
  1939. #include "output.h"
  1940. #include "error.h"
  1941. #include <signal.h>
  1942. #ifdef __STDC__
  1943. #include "stdarg.h"
  1944. #else
  1945. #include <varargs.h>    
  1946. #endif
  1947. #include "myerrno.h"
  1948.  
  1949.  
  1950. /*
  1951.  * Code to handle exceptions in C.
  1952.  */
  1953.  
  1954. struct jmploc *handler;
  1955. int exception;
  1956. volatile int suppressint;
  1957. volatile int intpending;
  1958. char *commandname;
  1959.  
  1960.  
  1961. /*
  1962.  * Called to raise an exception.  Since C doesn't include exceptions, we
  1963.  * just do a longjmp to the exception handler.  The type of exception is
  1964.  * stored in the global variable "exception".
  1965.  */
  1966.  
  1967. void
  1968. raise(e) {
  1969.       if (handler == NULL)
  1970.         abort();
  1971.       exception = e;
  1972.       longjmp(handler->loc, 1);
  1973. }
  1974.  
  1975.  
  1976. /*
  1977.  * Called from trap.c when a SIGINT is received.  (If the user specifies
  1978.  * that SIGINT is to be trapped or ignored using the trap builtin, then
  1979.  * this routine is not called.)  Suppressint is nonzero when interrupts
  1980.  * are held using the INTOFF macro.  The call to _exit is necessary because
  1981.  * there is a short period after a fork before the signal handlers are
  1982.  * set to the appropriate value for the child.  (The test for iflag is
  1983.  * just defensive programming.)
  1984.  */
  1985.  
  1986. void
  1987. onint() {
  1988.       if (suppressint) {
  1989.         intpending++;
  1990.         return;
  1991.       }
  1992.       intpending = 0;
  1993. #ifdef BSD
  1994.       sigsetmask(0);
  1995. #endif
  1996.       if (rootshell && iflag)
  1997.         raise(EXINT);
  1998.       else
  1999.         _exit(128 + SIGINT);
  2000. }
  2001.  
  2002.  
  2003.  
  2004. void
  2005. error2(a, b)
  2006.       char *a, *b;
  2007.       {
  2008.       error("%s: %s", a, b);
  2009. }
  2010.  
  2011.  
  2012. /*
  2013.  * Error is called to raise the error exception.  If the first argument
  2014.  * is not NULL then error prints an error message using printf style
  2015.  * formatting.  It then raises the error exception.
  2016.  */
  2017.  
  2018. #ifdef __STDC__
  2019. void
  2020. error(char *msg, ...) {
  2021. #else
  2022. void
  2023. error(va_alist)
  2024.       va_dcl
  2025.       {
  2026.       char *msg;
  2027. #endif
  2028.       va_list ap;
  2029.  
  2030.       CLEAR_PENDING_INT;
  2031.       INTOFF;
  2032. #ifdef __STDC__
  2033.       va_start(ap, msg);
  2034. #else
  2035.       va_start(ap);
  2036.       msg = va_arg(ap, char *);
  2037. #endif
  2038. #ifdef DEBUG
  2039.       if (msg)
  2040.         TRACE(("error(\"%s\") pid=%d\n", msg, getpid()));
  2041.       else
  2042.         TRACE(("error(NULL) pid=%d\n", getpid()));
  2043. #endif
  2044.       if (msg) {
  2045.         if (commandname)
  2046.           outfmt(&errout, "%s: ", commandname);
  2047.         doformat(&errout, msg, ap);
  2048.         out2c('\n');
  2049.       }
  2050.       va_end(ap);
  2051.       flushall();
  2052.       raise(EXERROR);
  2053. }
  2054.  
  2055.  
  2056.  
  2057. /*
  2058.  * Table of error messages.
  2059.  */
  2060.  
  2061. struct errname {
  2062.       short errcode;        /* error number */
  2063.       short action;        /* operation which encountered the error */
  2064.       char *msg;        /* text describing the error */
  2065. };
  2066.  
  2067.  
  2068. #define ALL (E_OPEN|E_CREAT|E_EXEC)
  2069.  
  2070. STATIC const struct errname errormsg[] = {
  2071.       EINTR, ALL,    "interrupted",
  2072.       EACCES, ALL,    "permission denied",
  2073.       EIO, ALL,        "I/O error",
  2074.       ENOENT, E_OPEN,    "no such file",
  2075.       ENOENT, E_CREAT,    "directory nonexistent",
  2076.       ENOENT, E_EXEC,    "not found",
  2077.       ENOTDIR, E_OPEN,    "no such file",
  2078.       ENOTDIR, E_CREAT,    "directory nonexistent",
  2079.       ENOTDIR, E_EXEC,    "not found",
  2080.       EISDIR, ALL,    "is a directory",
  2081. /*    EMFILE, ALL,    "too many open files", */
  2082.       ENFILE, ALL,    "file table overflow",
  2083.       ENOSPC, ALL,    "file system full",
  2084. #ifdef EDQUOT
  2085.       EDQUOT, ALL,    "disk quota exceeded",
  2086. #endif
  2087. #ifdef ENOSR
  2088.       ENOSR, ALL,    "no streams resources",
  2089. #endif
  2090.       ENXIO, ALL,    "no such device or address",
  2091.       EROFS, ALL,    "read-only file system",
  2092.       ETXTBSY, ALL,    "text busy",
  2093. #ifdef SYSV
  2094.       EAGAIN, E_EXEC,    "not enough memory",
  2095. #endif
  2096.       ENOMEM, ALL,    "not enough memory",
  2097. #ifdef ENOLINK
  2098.       ENOLINK, ALL,    "remote access failed"
  2099. #endif
  2100. #ifdef EMULTIHOP
  2101.       EMULTIHOP, ALL,    "remote access failed",
  2102. #endif
  2103. #ifdef ECOMM
  2104.       ECOMM, ALL,    "remote access failed",
  2105. #endif
  2106. #ifdef ESTALE
  2107.       ESTALE, ALL,    "remote access failed",
  2108. #endif
  2109. #ifdef ETIMEDOUT
  2110.       ETIMEDOUT, ALL,    "remote access failed",
  2111. #endif
  2112. #ifdef ELOOP
  2113.       ELOOP, ALL,    "symbolic link loop",
  2114. #endif
  2115.       E2BIG, E_EXEC,    "argument list too long",
  2116. #ifdef ELIBACC
  2117.       ELIBACC, E_EXEC,    "shared library missing",
  2118. #endif
  2119.       0, 0,        NULL
  2120. };
  2121.  
  2122.  
  2123. /*
  2124.  * Return a string describing an error.  The returned string may be a
  2125.  * pointer to a static buffer that will be overwritten on the next call.
  2126.  * Action describes the operation that got the error.
  2127.  */
  2128.  
  2129. char *
  2130. errmsg(e, action) {
  2131.       struct errname const *ep;
  2132.       static char buf[12];
  2133.  
  2134.       for (ep = errormsg ; ep->errcode ; ep++) {
  2135.         if (ep->errcode == e && (ep->action & action) != 0)
  2136.           return ep->msg;
  2137.       }
  2138.       fmtstr(buf, sizeof buf, "error %d", e);
  2139.       return buf;
  2140. }
  2141. EOF
  2142. if test `wc -c < error.c` -ne 4722
  2143. then    echo 'error.c is the wrong size'
  2144. fi
  2145. echo extracting exec.h
  2146. cat > exec.h <<\EOF
  2147. /* values of cmdtype */
  2148. #define CMDUNKNOWN -1        /* no entry in table for command */
  2149. #define CMDNORMAL 0        /* command is an executable program */
  2150. #define CMDBUILTIN 1        /* command is a shell builtin */
  2151. #define CMDFUNCTION 2        /* command is a shell function */
  2152.  
  2153.  
  2154. struct cmdentry {
  2155.       int cmdtype;
  2156.       union param {
  2157.         int index;
  2158.         union node *func;
  2159.       } u;
  2160. };
  2161.  
  2162.  
  2163. extern char *pathopt;        /* set by padvance */
  2164.  
  2165. #ifdef __STDC__
  2166. void shellexec(char **, char **, char *, int);
  2167. char *padvance(char **, char *);
  2168. void find_command(char *, struct cmdentry *, int);
  2169. int find_builtin(char *);
  2170. void hashcd(void);
  2171. void changepath(char *);
  2172. void defun(char *, union node *);
  2173. void unsetfunc(char *);
  2174. #else
  2175. void shellexec();
  2176. char *padvance();
  2177. void find_command();
  2178. int find_builtin();
  2179. void hashcd();
  2180. void changepath();
  2181. void defun();
  2182. void unsetfunc();
  2183. #endif
  2184. EOF
  2185. if test `wc -c < exec.h` -ne 846
  2186. then    echo 'exec.h is the wrong size'
  2187. fi
  2188. echo Archive 2 unpacked
  2189. exit
  2190.  
  2191.