home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / ash02emx.zip / exec.c < prev    next >
C/C++ Source or Header  |  1997-12-26  |  21KB  |  948 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. /*static char sccsid[] = "from: @(#)exec.c    5.2 (Berkeley) 3/13/91";*/
  39. static char rcsid[] = "exec.c,v 1.5 1993/08/01 18:58:17 mycroft Exp";
  40. #endif /* not lint */
  41.  
  42. /*
  43.  * When commands are first encountered, they are entered in a hash table.
  44.  * This ensures that a full path search will not have to be done for them
  45.  * on each invocation.
  46.  *
  47.  * We should investigate converting to a linear search, even though that
  48.  * would make the command name "hash" a misnomer.
  49.  */
  50.  
  51. #include "shell.h"
  52. #include "main.h"
  53. #include "nodes.h"
  54. #include "parser.h"
  55. #include "redir.h"
  56. #include "eval.h"
  57. #include "exec.h"
  58. #include "builtins.h"
  59. #include "var.h"
  60. #include "options.h"
  61. #include "input.h"
  62. #include "output.h"
  63. #include "syntax.h"
  64. #include "memalloc.h"
  65. #include "error.h"
  66. #include "init.h"
  67. #include "mystring.h"
  68. #include <sys/types.h>
  69. #include <sys/stat.h>
  70. #include <fcntl.h>
  71. #include <errno.h>
  72. #ifdef  BSD
  73. #undef BSD      /* temporary, already defined in <sys/param.h> */
  74. #include <sys/param.h>
  75. #include <unistd.h>
  76. #endif
  77.  
  78.  
  79. #define CMDTABLESIZE 31        /* should be prime */
  80. #define ARB 1            /* actual size determined at run time */
  81.  
  82.  
  83.  
  84. struct tblentry {
  85.     struct tblentry *next;    /* next entry in hash chain */
  86.     union param param;    /* definition of builtin function */
  87.     short cmdtype;        /* index identifying command */
  88.     char rehash;        /* if set, cd done since entry created */
  89.     char cmdname[ARB];    /* name of command */
  90. };
  91.  
  92.  
  93. STATIC struct tblentry *cmdtable[CMDTABLESIZE];
  94. STATIC int builtinloc = -1;        /* index in path of %builtin, or -1 */
  95.  
  96.  
  97. #ifdef __STDC__
  98. STATIC void tryexec(char *, char **, char **);
  99. STATIC void execinterp(char **, char **);
  100. STATIC void printentry(struct tblentry *);
  101. STATIC void clearcmdentry(int);
  102. STATIC struct tblentry *cmdlookup(char *, int);
  103. STATIC void delete_cmd_entry(void);
  104. #else
  105. STATIC void tryexec();
  106. STATIC void execinterp();
  107. STATIC void printentry();
  108. STATIC void clearcmdentry();
  109. STATIC struct tblentry *cmdlookup();
  110. STATIC void delete_cmd_entry();
  111. #endif
  112.  
  113. /*
  114.  * Check whether the string is just a filename or it contains a path
  115.  */
  116. int
  117. has_dir_sep(path)
  118.         char *path;
  119.         {
  120. #if DOS_PATHS
  121.     if (*path == ':')
  122.                 if (!path[1])
  123.                         return 0;
  124.         while (*path) {
  125.                 if ((*path == '/') || (*path == ':') || (*path == '\\'))
  126.                         return 1;
  127.                 path++;
  128.         }
  129.     return 0;
  130. #else
  131.         return (strchr(path, '/') != NULL);
  132. #endif
  133. }
  134.  
  135. /*
  136.  * Exec a program.  Never returns.  If you change this routine, you may
  137.  * have to change the find_command routine as well.
  138.  */
  139. #ifdef __EMX__
  140. int
  141. _shellexec(argv, envp, path, index)
  142. #else
  143. void
  144. shellexec(argv, envp, path, index)
  145. #endif
  146.     char **argv, **envp;
  147.     char *path;
  148.     {
  149.     char *cmdname;
  150.     int e;
  151.  
  152.     if (has_dir_sep(argv[0])) {
  153.         tryexec(argv[0], argv, envp);
  154.         e = errno;
  155.     } else {
  156.         e = ENOENT;
  157.         while ((cmdname = padvance(&path, argv[0])) != NULL) {
  158.             if (--index < 0 && pathopt == NULL) {
  159.                 tryexec(cmdname, argv, envp);
  160.                 if (errno != ENOENT && errno != ENOTDIR)
  161.                     e = errno;
  162.             }
  163.             stunalloc(cmdname);
  164.         }
  165.     }
  166. #ifdef __EMX__
  167.     return (e);
  168. #else
  169.     error2(argv[0], errmsg(e, E_EXEC));
  170. #endif
  171. }
  172.  
  173. #ifdef __EMX__
  174. void
  175. shellexec(argv, envp, path, index)
  176.     char **argv, **envp;
  177.     char *path;
  178.     {
  179.         int length = strlen(argv[0]);
  180.  
  181.     if (length > 4) {
  182.         if (!stricmp(&argv[0][length-4], ".exe")) {
  183.         length = 0;
  184.         }
  185.     }
  186.     if (length) {
  187.         char *oldarg = argv[0];
  188.  
  189.         argv[0] = ckmalloc(length+4);
  190.         strcpy(argv[0], oldarg);
  191.         strcat(argv[0], ".exe");
  192.         _shellexec(argv, envp, path, index);
  193.         free(argv[0]);
  194.         argv[0] = oldarg;
  195.     }
  196.     error2(argv[0], errmsg(_shellexec(argv, envp, path, index), E_EXEC));
  197. }
  198. #endif /* __EMX__ */
  199.  
  200.  
  201. STATIC void
  202. tryexec(cmd, argv, envp)
  203.     char *cmd;
  204.     char **argv;
  205.     char **envp;
  206.     {
  207.     int e;
  208.     char *p;
  209.  
  210. #ifdef __EMX__
  211.     if (access(cmd, R_OK))
  212.         return;
  213.     execve(cmd, argv, envp);
  214.     initshellproc();
  215.     setinputfile(cmd, 0);
  216.     commandname = arg0 = savestr(argv[0]);
  217.     pgetc(); pungetc();        /* fill up input buffer */
  218.     p = parsenextc;
  219.     if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
  220.         argv[0] = cmd;
  221.         execinterp(argv, envp);
  222.     }
  223.     setparam(argv + 1);
  224.     exraise(EXSHELLPROC);
  225. #else
  226.  
  227. #ifdef SYSV
  228.     do {
  229.         execve(cmd, argv, envp);
  230.     } while (errno == EINTR);
  231. #else
  232.     execve(cmd, argv, envp);
  233. #endif
  234.     e = errno;
  235.     if (e == ENOEXEC) {
  236.         initshellproc();
  237.         setinputfile(cmd, 0);
  238.         commandname = arg0 = savestr(argv[0]);
  239. #ifndef BSD
  240.         pgetc(); pungetc();        /* fill up input buffer */
  241.         p = parsenextc;
  242.         if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
  243.             argv[0] = cmd;
  244.             execinterp(argv, envp);
  245.         }
  246. #endif
  247.         setparam(argv + 1);
  248.         exraise(EXSHELLPROC);
  249.         /*NOTREACHED*/
  250.     }
  251.     errno = e;
  252. #endif /* __EMX__ */
  253. }
  254.  
  255.  
  256. #ifndef BSD
  257. /*
  258.  * Execute an interpreter introduced by "#!", for systems where this
  259.  * feature has not been built into the kernel.  If the interpreter is
  260.  * the shell, return (effectively ignoring the "#!").  If the execution
  261.  * of the interpreter fails, exit.
  262.  *
  263.  * This code peeks inside the input buffer in order to avoid actually
  264.  * reading any input.  It would benefit from a rewrite.
  265.  */
  266.  
  267. #define NEWARGS 5
  268.  
  269. STATIC void
  270. execinterp(argv, envp)
  271.     char **argv, **envp;
  272.     {
  273.     int n;
  274.     char *inp;
  275.     char *outp;
  276.     char c;
  277.     char *p;
  278.     char **ap;
  279.     char *newargs[NEWARGS];
  280.     int i;
  281.     char **ap2;
  282.     char **new;
  283.  
  284.     n = parsenleft - 2;
  285.     inp = parsenextc + 2;
  286.     ap = newargs;
  287.     for (;;) {
  288.         while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
  289.             inp++;
  290.         if (n < 0)
  291.             goto bad;
  292.         if ((c = *inp++) == '\n')
  293.             break;
  294.         if (ap == &newargs[NEWARGS])
  295. bad:          error("Bad #! line");
  296.         STARTSTACKSTR(outp);
  297.         do {
  298.             STPUTC(c, outp);
  299.         } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
  300.         STPUTC('\0', outp);
  301.         n++, inp--;
  302.         *ap++ = grabstackstr(outp);
  303.     }
  304.     if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
  305.         p = newargs[0];
  306.         for (;;) {
  307.             if (equal(p, "sh") || equal(p, "ash")) {
  308.                 return;
  309.             }
  310.             while (!IS_SLASH(*p)) {
  311.                 if (*p == '\0')
  312.                     goto break2;
  313.                 p++;
  314.             }
  315.             p++;
  316.         }
  317. break2:;
  318.     }
  319.     i = (char *)ap - (char *)newargs;        /* size in bytes */
  320.     if (i == 0)
  321.         error("Bad #! line");
  322.     for (ap2 = argv ; *ap2++ != NULL ; );
  323.     new = ckmalloc(i + ((char *)ap2 - (char *)argv));
  324.     ap = newargs, ap2 = new;
  325.     while ((i -= sizeof (char **)) >= 0)
  326.         *ap2++ = *ap++;
  327.     ap = argv;
  328.     while (*ap2++ = *ap++);
  329.     shellexec(new, envp, pathval(), 0);
  330. }
  331. #endif
  332.  
  333.  
  334.  
  335. /*
  336.  * Do a path search.  The variable path (passed by reference) should be
  337.  * set to the start of the path before the first call; padvance will update
  338.  * this value as it proceeds.  Successive calls to padvance will return
  339.  * the possible path expansions in sequence.  If an option (indicated by
  340.  * a percent sign) appears in the path entry then the global variable
  341.  * pathopt will be set to point to it; otherwise pathopt will be set to
  342.  * NULL.
  343.  */
  344.  
  345. char *pathopt;
  346.  
  347. char *
  348. padvance(path, name)
  349.     char **path;
  350.     char *name;
  351.     {
  352.     register char *p, *q;
  353.     char *start;
  354.     int len;
  355.  
  356.     if (*path == NULL)
  357.         return NULL;
  358.     start = *path;
  359.     for (p = start ; *p && *p != PATH_SEP && *p != '%' ; p++);
  360.     len = p - start + strlen(name) + 2;    /* "2" is for '/' and '\0' */
  361.     while (stackblocksize() < len)
  362.         growstackblock();
  363.     q = stackblock();
  364.     if (p != start) {
  365.         bcopy(start, q, p - start);
  366.         q += p - start;
  367.         *q++ = SLASH;
  368.     }
  369.     strcpy(q, name);
  370.     pathopt = NULL;
  371.     if (*p == '%') {
  372.         pathopt = ++p;
  373.         while (*p && *p != PATH_SEP)  p++;
  374.     }
  375.     if (*p == PATH_SEP)
  376.         *path = p + 1;
  377.     else
  378.         *path = NULL;
  379.     return stalloc(len);
  380. }
  381.  
  382.  
  383.  
  384. /*** Command hashing code ***/
  385.  
  386.  
  387. hashcmd(argc, argv)  char **argv; {
  388.     struct tblentry **pp;
  389.     struct tblentry *cmdp;
  390.     int c;
  391.     int verbose;
  392.     struct cmdentry entry;
  393.     char *name;
  394.  
  395.     if (argc <= 1) {
  396.         for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  397.             for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  398.                 printentry(cmdp);
  399.             }
  400.         }
  401.         return 0;
  402.     }
  403.     verbose = 0;
  404.     while ((c = nextopt("rv")) != '\0') {
  405.         if (c == 'r') {
  406.             clearcmdentry(0);
  407.         } else if (c == 'v') {
  408.             verbose++;
  409.         }
  410.     }
  411.     while ((name = *argptr) != NULL) {
  412.         if ((cmdp = cmdlookup(name, 0)) != NULL
  413.          && (cmdp->cmdtype == CMDNORMAL
  414.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
  415.             delete_cmd_entry();
  416.         find_command(name, &entry, 1);
  417.         if (verbose) {
  418.             if (entry.cmdtype != CMDUNKNOWN) {    /* if no error msg */
  419.                 cmdp = cmdlookup(name, 0);
  420.                 printentry(cmdp);
  421.             }
  422.             flushall();
  423.         }
  424.         argptr++;
  425.     }
  426.     return 0;
  427. }
  428.  
  429.  
  430. STATIC void
  431. printentry(cmdp)
  432.     struct tblentry *cmdp;
  433.     {
  434.     int index;
  435.     char *path;
  436.     char *name;
  437.  
  438.     if (cmdp->cmdtype == CMDNORMAL) {
  439.         index = cmdp->param.index;
  440.         path = pathval();
  441.         do {
  442.             name = padvance(&path, cmdp->cmdname);
  443.             stunalloc(name);
  444.         } while (--index >= 0);
  445.         out1str(name);
  446.     } else if (cmdp->cmdtype == CMDBUILTIN) {
  447.         out1fmt("builtin %s", cmdp->cmdname);
  448.     } else if (cmdp->cmdtype == CMDFUNCTION) {
  449.         out1fmt("function %s", cmdp->cmdname);
  450. #ifdef DEBUG
  451.     } else {
  452.         error("internal error: cmdtype %d", cmdp->cmdtype);
  453. #endif
  454.     }
  455.     if (cmdp->rehash)
  456.         out1c('*');
  457.     out1c('\n');
  458. }
  459.  
  460.  
  461.  
  462. /*
  463.  * Resolve a command name.  If you change this routine, you may have to
  464.  * change the shellexec routine as well.
  465.  */
  466.  
  467. void
  468. #ifdef __EMX__
  469. _find_command(name, entry, printerr)
  470. #else
  471. find_command(name, entry, printerr)
  472. #endif
  473.     char *name;
  474.     struct cmdentry *entry;
  475.     {
  476.     struct tblentry *cmdp;
  477.     int index;
  478.     int prev;
  479.     char *path;
  480.     char *fullname;
  481.     struct stat statb;
  482.     int e;
  483.     int i;
  484.  
  485.     /* If name contains a slash, don't use the hash table */
  486.     if (has_dir_sep(name)) {
  487.         entry->cmdtype = CMDNORMAL;
  488.         entry->u.index = 0;
  489.         return;
  490.     }
  491.  
  492.     /* If name is in the table, and not invalidated by cd, we're done */
  493.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
  494.         goto success;
  495.  
  496.     /* If %builtin not in path, check for builtin next */
  497.     if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
  498.         INTOFF;
  499.         cmdp = cmdlookup(name, 1);
  500.         cmdp->cmdtype = CMDBUILTIN;
  501.         cmdp->param.index = i;
  502.         INTON;
  503.         goto success;
  504.     }
  505.  
  506.     /* We have to search path. */
  507.     prev = -1;        /* where to start */
  508.     if (cmdp) {        /* doing a rehash */
  509.         if (cmdp->cmdtype == CMDBUILTIN)
  510.             prev = builtinloc;
  511.         else
  512.             prev = cmdp->param.index;
  513.     }
  514.  
  515.     path = pathval();
  516.     e = ENOENT;
  517.     index = -1;
  518. loop:
  519.     while ((fullname = padvance(&path, name)) != NULL) {
  520.         stunalloc(fullname);
  521.         index++;
  522.         if (pathopt) {
  523.             if (prefix("builtin", pathopt)) {
  524.                 if ((i = find_builtin(name)) < 0)
  525.                     goto loop;
  526.                 INTOFF;
  527.                 cmdp = cmdlookup(name, 1);
  528.                 cmdp->cmdtype = CMDBUILTIN;
  529.                 cmdp->param.index = i;
  530.                 INTON;
  531.                 goto success;
  532.             } else if (prefix("func", pathopt)) {
  533.                 /* handled below */
  534.             } else {
  535.                 goto loop;    /* ignore unimplemented options */
  536.             }
  537.         }
  538.         /* if rehash, don't redo absolute path names */
  539.         if (IS_ABSOLUTE(fullname) && index <= prev) {
  540.             if (index < prev)
  541.                 goto loop;
  542.             TRACE(("searchexec \"%s\": no change\n", name));
  543.             goto success;
  544.         }
  545. #ifndef __EMX__
  546.         while (stat(fullname, &statb) < 0) {
  547. #ifdef SYSV
  548.             if (errno == EINTR)
  549.                 continue;
  550. #endif
  551.             if (errno != ENOENT && errno != ENOTDIR)
  552.                 e = errno;
  553.             goto loop;
  554.         }
  555.         e = EACCES;    /* if we fail, this will be the error */
  556.         if ((statb.st_mode & S_IFMT) != S_IFREG)
  557.             goto loop;
  558. #endif /* !def __EMX__ */
  559.         if (pathopt) {        /* this is a %func directory */
  560.             stalloc(strlen(fullname) + 1);
  561.             readcmdfile(fullname);
  562.             if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
  563.                 error("%s not defined in %s", name, fullname);
  564.             stunalloc(fullname);
  565.             goto success;
  566.         }
  567. #ifdef __EMX__
  568.         if (access(fullname, R_OK) == -1) {
  569.             e = errno;
  570.             goto loop;
  571.         }
  572. #else
  573.         if (geteuid() == 0) {
  574.             if ((statb.st_mode & 0111) == 0)
  575.                 goto loop;
  576.         } else if (statb.st_uid == geteuid()) {
  577.             if ((statb.st_mode & 0100) == 0)
  578.                 goto loop;
  579.         } else if (statb.st_gid == getegid()) {
  580.             if ((statb.st_mode & 010) == 0)
  581.                 goto loop;
  582.         } else {
  583.             if ((statb.st_mode & 01) == 0) {
  584. #ifdef  BSD
  585.                 if ((statb.st_mode & 010) == 0)
  586.                     goto loop;
  587.                 /* Are you in this group too? */
  588.                 {
  589.                     int group_list[NGROUPS];
  590.                     int ngroups, i;
  591.  
  592.                     ngroups = getgroups(NGROUPS, group_list);
  593.                     for (i = 0; i < ngroups; i++)
  594.                         if (statb.st_gid == group_list[i])
  595.                             goto Found;
  596.                 }
  597. #endif
  598.                 goto loop;
  599.             }
  600.         }
  601. #endif /* __EMX__ */
  602. #ifdef  BSD
  603.     Found:
  604. #endif
  605.         TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
  606.         INTOFF;
  607.         cmdp = cmdlookup(name, 1);
  608.         cmdp->cmdtype = CMDNORMAL;
  609.         cmdp->param.index = index;
  610.         INTON;
  611.         goto success;
  612.     }
  613.  
  614.     /* We failed.  If there was an entry for this command, delete it */
  615.     if (cmdp)
  616.         delete_cmd_entry();
  617.     if (printerr)
  618.         outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
  619.     entry->cmdtype = CMDUNKNOWN;
  620.     return;
  621.  
  622. success:
  623.     cmdp->rehash = 0;
  624.     entry->cmdtype = cmdp->cmdtype;
  625.     entry->u = cmdp->param;
  626. }
  627.  
  628.  
  629. #ifdef __EMX__
  630. void
  631. find_command(name, entry, printerr)
  632.     char *name;
  633.     struct cmdentry *entry;
  634.     {
  635.     int length;
  636.  
  637.     _find_command(name, entry, 0);
  638.     if (entry->cmdtype != CMDUNKNOWN)
  639.         return;
  640.     if ((length = strlen(name)) > 4) {
  641.         if (!stricmp(&name[length-4], ".exe")) {
  642.         if (printerr)
  643.             outfmt(out2, "%s: %s\n", name, errmsg(errno, E_EXEC));
  644.         return;
  645.         }
  646.     }
  647.     if (length) {
  648.         char *buffer = ckmalloc(length+4);
  649.  
  650.         strcpy(buffer, name);
  651.         strcat(buffer, ".exe");
  652.         _find_command(buffer, entry, printerr);
  653.         free(buffer);
  654.     }
  655. }
  656. #endif /* __EMX__ */
  657.  
  658.  
  659. /*
  660.  * Search the table of builtin commands.
  661.  */
  662.  
  663. int
  664. find_builtin(name)
  665.     char *name;
  666.     {
  667.     const register struct builtincmd *bp;
  668.  
  669.     for (bp = builtincmd ; bp->name ; bp++) {
  670.         if (*bp->name == *name && equal(bp->name, name))
  671.             return bp->code;
  672.     }
  673.     return -1;
  674. }
  675.  
  676.  
  677.  
  678. /*
  679.  * Called when a cd is done.  Marks all commands so the next time they
  680.  * are executed they will be rehashed.
  681.  */
  682.  
  683. void
  684. hashcd() {
  685.     struct tblentry **pp;
  686.     struct tblentry *cmdp;
  687.  
  688.     for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  689.         for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  690.             if (cmdp->cmdtype == CMDNORMAL
  691.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
  692.                 cmdp->rehash = 1;
  693.         }
  694.     }
  695. }
  696.  
  697.  
  698.  
  699. /*
  700.  * Called before PATH is changed.  The argument is the new value of PATH;
  701.  * pathval() still returns the old value at this point.  Called with
  702.  * interrupts off.
  703.  */
  704.  
  705. void
  706. changepath(newval)
  707.     char *newval;
  708.     {
  709.     char *old, *new;
  710.     int index;
  711.     int firstchange;
  712.     int bltin;
  713.  
  714.     old = pathval();
  715.     new = newval;
  716.     firstchange = 9999;    /* assume no change */
  717.     index = 0;
  718.     bltin = -1;
  719.     for (;;) {
  720.         if (*old != *new) {
  721.             firstchange = index;
  722.             if (*old == '\0' && *new == PATH_SEP
  723.              || *old == PATH_SEP && *new == '\0')
  724.                 firstchange++;
  725.             old = new;    /* ignore subsequent differences */
  726.         }
  727.         if (*new == '\0')
  728.             break;
  729.         if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
  730.             bltin = index;
  731.         if (*new == PATH_SEP) {
  732.             index++;
  733.         }
  734.         new++, old++;
  735.     }
  736.     if (builtinloc < 0 && bltin >= 0)
  737.         builtinloc = bltin;        /* zap builtins */
  738.     if (builtinloc >= 0 && bltin < 0)
  739.         firstchange = 0;
  740.     clearcmdentry(firstchange);
  741.     builtinloc = bltin;
  742. }
  743.  
  744.  
  745. /*
  746.  * Clear out command entries.  The argument specifies the first entry in
  747.  * PATH which has changed.
  748.  */
  749.  
  750. STATIC void
  751. clearcmdentry(firstchange) {
  752.     struct tblentry **tblp;
  753.     struct tblentry **pp;
  754.     struct tblentry *cmdp;
  755.  
  756.     INTOFF;
  757.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  758.         pp = tblp;
  759.         while ((cmdp = *pp) != NULL) {
  760.             if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
  761.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
  762.                 *pp = cmdp->next;
  763.                 ckfree(cmdp);
  764.             } else {
  765.                 pp = &cmdp->next;
  766.             }
  767.         }
  768.     }
  769.     INTON;
  770. }
  771.  
  772.  
  773. /*
  774.  * Delete all functions.
  775.  */
  776.  
  777. #ifdef mkinit
  778. MKINIT void deletefuncs();
  779.  
  780. SHELLPROC {
  781.     deletefuncs();
  782. }
  783. #endif
  784.  
  785. void
  786. deletefuncs() {
  787.     struct tblentry **tblp;
  788.     struct tblentry **pp;
  789.     struct tblentry *cmdp;
  790.  
  791.     INTOFF;
  792.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  793.         pp = tblp;
  794.         while ((cmdp = *pp) != NULL) {
  795.             if (cmdp->cmdtype == CMDFUNCTION) {
  796.                 *pp = cmdp->next;
  797.                 freefunc(cmdp->param.func);
  798.                 ckfree(cmdp);
  799.             } else {
  800.                 pp = &cmdp->next;
  801.             }
  802.         }
  803.     }
  804.     INTON;
  805. }
  806.  
  807.  
  808.  
  809. /*
  810.  * Locate a command in the command hash table.  If "add" is nonzero,
  811.  * add the command to the table if it is not already present.  The
  812.  * variable "lastcmdentry" is set to point to the address of the link
  813.  * pointing to the entry, so that delete_cmd_entry can delete the
  814.  * entry.
  815.  */
  816.  
  817. struct tblentry **lastcmdentry;
  818.  
  819.  
  820. STATIC struct tblentry *
  821. cmdlookup(name, add)
  822.     char *name;
  823.     {
  824.     int hashval;
  825.     register char *p;
  826.     struct tblentry *cmdp;
  827.     struct tblentry **pp;
  828.  
  829.     p = name;
  830.     hashval = *p << 4;
  831.     while (*p)
  832.         hashval += *p++;
  833.     hashval &= 0x7FFF;
  834.     pp = &cmdtable[hashval % CMDTABLESIZE];
  835.     for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  836.         if (equal(cmdp->cmdname, name))
  837.             break;
  838.         pp = &cmdp->next;
  839.     }
  840.     if (add && cmdp == NULL) {
  841.         INTOFF;
  842.         cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
  843.                     + strlen(name) + 1);
  844.         cmdp->next = NULL;
  845.         cmdp->cmdtype = CMDUNKNOWN;
  846.         cmdp->rehash = 0;
  847.         strcpy(cmdp->cmdname, name);
  848.         INTON;
  849.     }
  850.     lastcmdentry = pp;
  851.     return cmdp;
  852. }
  853.  
  854.  
  855. /*
  856.  * Delete the command entry returned on the last lookup.
  857.  */
  858.  
  859. STATIC void
  860. delete_cmd_entry() {
  861.     struct tblentry *cmdp;
  862.  
  863.     INTOFF;
  864.     cmdp = *lastcmdentry;
  865.     *lastcmdentry = cmdp->next;
  866.     ckfree(cmdp);
  867.     INTON;
  868. }
  869.  
  870.  
  871.  
  872. #ifdef notdef
  873. void
  874. getcmdentry(name, entry)
  875.     char *name;
  876.     struct cmdentry *entry; 
  877.     {
  878.     struct tblentry *cmdp = cmdlookup(name, 0);
  879.  
  880.     if (cmdp) {
  881.         entry->u = cmdp->param;
  882.         entry->cmdtype = cmdp->cmdtype;
  883.     } else {
  884.         entry->cmdtype = CMDUNKNOWN;
  885.         entry->u.index = 0;
  886.     }
  887. }
  888. #endif
  889.  
  890.  
  891. /*
  892.  * Add a new command entry, replacing any existing command entry for
  893.  * the same name.
  894.  */
  895.  
  896. void
  897. addcmdentry(name, entry)
  898.     char *name;
  899.     struct cmdentry *entry;
  900.     {
  901.     struct tblentry *cmdp;
  902.  
  903.     INTOFF;
  904.     cmdp = cmdlookup(name, 1);
  905.     if (cmdp->cmdtype == CMDFUNCTION) {
  906.         freefunc(cmdp->param.func);
  907.     }
  908.     cmdp->cmdtype = entry->cmdtype;
  909.     cmdp->param = entry->u;
  910.     INTON;
  911. }
  912.  
  913.  
  914. /*
  915.  * Define a shell function.
  916.  */
  917.  
  918. void
  919. defun(name, func)
  920.     char *name;
  921.     union node *func;
  922.     {
  923.     struct cmdentry entry;
  924.  
  925.     INTOFF;
  926.     entry.cmdtype = CMDFUNCTION;
  927.     entry.u.func = copyfunc(func);
  928.     addcmdentry(name, &entry);
  929.     INTON;
  930. }
  931.  
  932.  
  933. /*
  934.  * Delete a function if it exists.
  935.  */
  936.  
  937. void
  938. unsetfunc(name)
  939.     char *name;
  940.     {
  941.     struct tblentry *cmdp;
  942.  
  943.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
  944.         freefunc(cmdp->param.func);
  945.         delete_cmd_entry();
  946.     }
  947. }
  948.