home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / AP / ASH / ASH-LINU.2 / ASH-LINU / ash-linux-0.2 / exec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-04  |  17.4 KB  |  833 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.  
  115. /*
  116.  * Exec a program.  Never returns.  If you change this routine, you may
  117.  * have to change the find_command routine as well.
  118.  */
  119.  
  120. void
  121. shellexec(argv, envp, path, index)
  122.     char **argv, **envp;
  123.     char *path;
  124.     {
  125.     char *cmdname;
  126.     int e;
  127.  
  128.     if (strchr(argv[0], '/') != NULL) {
  129.         tryexec(argv[0], argv, envp);
  130.         e = errno;
  131.     } else {
  132.         e = ENOENT;
  133.         while ((cmdname = padvance(&path, argv[0])) != NULL) {
  134.             if (--index < 0 && pathopt == NULL) {
  135.                 tryexec(cmdname, argv, envp);
  136.                 if (errno != ENOENT && errno != ENOTDIR)
  137.                     e = errno;
  138.             }
  139.             stunalloc(cmdname);
  140.         }
  141.     }
  142.     error2(argv[0], errmsg(e, E_EXEC));
  143. }
  144.  
  145.  
  146. STATIC void
  147. tryexec(cmd, argv, envp)
  148.     char *cmd;
  149.     char **argv;
  150.     char **envp;
  151.     {
  152.     int e;
  153.     char *p;
  154.  
  155. #ifdef SYSV
  156.     do {
  157.         execve(cmd, argv, envp);
  158.     } while (errno == EINTR);
  159. #else
  160.     execve(cmd, argv, envp);
  161. #endif
  162.     e = errno;
  163.     if (e == ENOEXEC) {
  164.         initshellproc();
  165.         setinputfile(cmd, 0);
  166.         commandname = arg0 = savestr(argv[0]);
  167. #ifndef BSD
  168.         pgetc(); pungetc();        /* fill up input buffer */
  169.         p = parsenextc;
  170.         if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
  171.             argv[0] = cmd;
  172.             execinterp(argv, envp);
  173.         }
  174. #endif
  175.         setparam(argv + 1);
  176.         exraise(EXSHELLPROC);
  177.         /*NOTREACHED*/
  178.     }
  179.     errno = e;
  180. }
  181.  
  182.  
  183. #ifndef BSD
  184. /*
  185.  * Execute an interpreter introduced by "#!", for systems where this
  186.  * feature has not been built into the kernel.  If the interpreter is
  187.  * the shell, return (effectively ignoring the "#!").  If the execution
  188.  * of the interpreter fails, exit.
  189.  *
  190.  * This code peeks inside the input buffer in order to avoid actually
  191.  * reading any input.  It would benefit from a rewrite.
  192.  */
  193.  
  194. #define NEWARGS 5
  195.  
  196. STATIC void
  197. execinterp(argv, envp)
  198.     char **argv, **envp;
  199.     {
  200.     int n;
  201.     char *inp;
  202.     char *outp;
  203.     char c;
  204.     char *p;
  205.     char **ap;
  206.     char *newargs[NEWARGS];
  207.     int i;
  208.     char **ap2;
  209.     char **new;
  210.  
  211.     n = parsenleft - 2;
  212.     inp = parsenextc + 2;
  213.     ap = newargs;
  214.     for (;;) {
  215.         while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
  216.             inp++;
  217.         if (n < 0)
  218.             goto bad;
  219.         if ((c = *inp++) == '\n')
  220.             break;
  221.         if (ap == &newargs[NEWARGS])
  222. bad:          error("Bad #! line");
  223.         STARTSTACKSTR(outp);
  224.         do {
  225.             STPUTC(c, outp);
  226.         } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
  227.         STPUTC('\0', outp);
  228.         n++, inp--;
  229.         *ap++ = grabstackstr(outp);
  230.     }
  231.     if (ap == newargs + 1) {    /* if no args, maybe no exec is needed */
  232.         p = newargs[0];
  233.         for (;;) {
  234.             if (equal(p, "sh") || equal(p, "ash")) {
  235.                 return;
  236.             }
  237.             while (*p != '/') {
  238.                 if (*p == '\0')
  239.                     goto break2;
  240.                 p++;
  241.             }
  242.             p++;
  243.         }
  244. break2:;
  245.     }
  246.     i = (char *)ap - (char *)newargs;        /* size in bytes */
  247.     if (i == 0)
  248.         error("Bad #! line");
  249.     for (ap2 = argv ; *ap2++ != NULL ; );
  250.     new = ckmalloc(i + ((char *)ap2 - (char *)argv));
  251.     ap = newargs, ap2 = new;
  252.     while ((i -= sizeof (char **)) >= 0)
  253.         *ap2++ = *ap++;
  254.     ap = argv;
  255.     while (*ap2++ = *ap++);
  256.     shellexec(new, envp, pathval(), 0);
  257. }
  258. #endif
  259.  
  260.  
  261.  
  262. /*
  263.  * Do a path search.  The variable path (passed by reference) should be
  264.  * set to the start of the path before the first call; padvance will update
  265.  * this value as it proceeds.  Successive calls to padvance will return
  266.  * the possible path expansions in sequence.  If an option (indicated by
  267.  * a percent sign) appears in the path entry then the global variable
  268.  * pathopt will be set to point to it; otherwise pathopt will be set to
  269.  * NULL.
  270.  */
  271.  
  272. char *pathopt;
  273.  
  274. char *
  275. padvance(path, name)
  276.     char **path;
  277.     char *name;
  278.     {
  279.     register char *p, *q;
  280.     char *start;
  281.     int len;
  282.  
  283.     if (*path == NULL)
  284.         return NULL;
  285.     start = *path;
  286.     for (p = start ; *p && *p != ':' && *p != '%' ; p++);
  287.     len = p - start + strlen(name) + 2;    /* "2" is for '/' and '\0' */
  288.     while (stackblocksize() < len)
  289.         growstackblock();
  290.     q = stackblock();
  291.     if (p != start) {
  292.         bcopy(start, q, p - start);
  293.         q += p - start;
  294.         *q++ = '/';
  295.     }
  296.     strcpy(q, name);
  297.     pathopt = NULL;
  298.     if (*p == '%') {
  299.         pathopt = ++p;
  300.         while (*p && *p != ':')  p++;
  301.     }
  302.     if (*p == ':')
  303.         *path = p + 1;
  304.     else
  305.         *path = NULL;
  306.     return stalloc(len);
  307. }
  308.  
  309.  
  310.  
  311. /*** Command hashing code ***/
  312.  
  313.  
  314. hashcmd(argc, argv)  char **argv; {
  315.     struct tblentry **pp;
  316.     struct tblentry *cmdp;
  317.     int c;
  318.     int verbose;
  319.     struct cmdentry entry;
  320.     char *name;
  321.  
  322.     if (argc <= 1) {
  323.         for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  324.             for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  325.                 printentry(cmdp);
  326.             }
  327.         }
  328.         return 0;
  329.     }
  330.     verbose = 0;
  331.     while ((c = nextopt("rv")) != '\0') {
  332.         if (c == 'r') {
  333.             clearcmdentry(0);
  334.         } else if (c == 'v') {
  335.             verbose++;
  336.         }
  337.     }
  338.     while ((name = *argptr) != NULL) {
  339.         if ((cmdp = cmdlookup(name, 0)) != NULL
  340.          && (cmdp->cmdtype == CMDNORMAL
  341.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
  342.             delete_cmd_entry();
  343.         find_command(name, &entry, 1);
  344.         if (verbose) {
  345.             if (entry.cmdtype != CMDUNKNOWN) {    /* if no error msg */
  346.                 cmdp = cmdlookup(name, 0);
  347.                 printentry(cmdp);
  348.             }
  349.             flushall();
  350.         }
  351.         argptr++;
  352.     }
  353.     return 0;
  354. }
  355.  
  356.  
  357. STATIC void
  358. printentry(cmdp)
  359.     struct tblentry *cmdp;
  360.     {
  361.     int index;
  362.     char *path;
  363.     char *name;
  364.  
  365.     if (cmdp->cmdtype == CMDNORMAL) {
  366.         index = cmdp->param.index;
  367.         path = pathval();
  368.         do {
  369.             name = padvance(&path, cmdp->cmdname);
  370.             stunalloc(name);
  371.         } while (--index >= 0);
  372.         out1str(name);
  373.     } else if (cmdp->cmdtype == CMDBUILTIN) {
  374.         out1fmt("builtin %s", cmdp->cmdname);
  375.     } else if (cmdp->cmdtype == CMDFUNCTION) {
  376.         out1fmt("function %s", cmdp->cmdname);
  377. #ifdef DEBUG
  378.     } else {
  379.         error("internal error: cmdtype %d", cmdp->cmdtype);
  380. #endif
  381.     }
  382.     if (cmdp->rehash)
  383.         out1c('*');
  384.     out1c('\n');
  385. }
  386.  
  387.  
  388.  
  389. /*
  390.  * Resolve a command name.  If you change this routine, you may have to
  391.  * change the shellexec routine as well.
  392.  */
  393.  
  394. void
  395. find_command(name, entry, printerr)
  396.     char *name;
  397.     struct cmdentry *entry;
  398.     {
  399.     struct tblentry *cmdp;
  400.     int index;
  401.     int prev;
  402.     char *path;
  403.     char *fullname;
  404.     struct stat statb;
  405.     int e;
  406.     int i;
  407.  
  408.     /* If name contains a slash, don't use the hash table */
  409.     if (strchr(name, '/') != NULL) {
  410.         entry->cmdtype = CMDNORMAL;
  411.         entry->u.index = 0;
  412.         return;
  413.     }
  414.  
  415.     /* If name is in the table, and not invalidated by cd, we're done */
  416.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
  417.         goto success;
  418.  
  419.     /* If %builtin not in path, check for builtin next */
  420.     if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
  421.         INTOFF;
  422.         cmdp = cmdlookup(name, 1);
  423.         cmdp->cmdtype = CMDBUILTIN;
  424.         cmdp->param.index = i;
  425.         INTON;
  426.         goto success;
  427.     }
  428.  
  429.     /* We have to search path. */
  430.     prev = -1;        /* where to start */
  431.     if (cmdp) {        /* doing a rehash */
  432.         if (cmdp->cmdtype == CMDBUILTIN)
  433.             prev = builtinloc;
  434.         else
  435.             prev = cmdp->param.index;
  436.     }
  437.  
  438.     path = pathval();
  439.     e = ENOENT;
  440.     index = -1;
  441. loop:
  442.     while ((fullname = padvance(&path, name)) != NULL) {
  443.         stunalloc(fullname);
  444.         index++;
  445.         if (pathopt) {
  446.             if (prefix("builtin", pathopt)) {
  447.                 if ((i = find_builtin(name)) < 0)
  448.                     goto loop;
  449.                 INTOFF;
  450.                 cmdp = cmdlookup(name, 1);
  451.                 cmdp->cmdtype = CMDBUILTIN;
  452.                 cmdp->param.index = i;
  453.                 INTON;
  454.                 goto success;
  455.             } else if (prefix("func", pathopt)) {
  456.                 /* handled below */
  457.             } else {
  458.                 goto loop;    /* ignore unimplemented options */
  459.             }
  460.         }
  461.         /* if rehash, don't redo absolute path names */
  462.         if (fullname[0] == '/' && index <= prev) {
  463.             if (index < prev)
  464.                 goto loop;
  465.             TRACE(("searchexec \"%s\": no change\n", name));
  466.             goto success;
  467.         }
  468.         while (stat(fullname, &statb) < 0) {
  469. #ifdef SYSV
  470.             if (errno == EINTR)
  471.                 continue;
  472. #endif
  473.             if (errno != ENOENT && errno != ENOTDIR)
  474.                 e = errno;
  475.             goto loop;
  476.         }
  477.         e = EACCES;    /* if we fail, this will be the error */
  478.         if ((statb.st_mode & S_IFMT) != S_IFREG)
  479.             goto loop;
  480.         if (pathopt) {        /* this is a %func directory */
  481.             stalloc(strlen(fullname) + 1);
  482.             readcmdfile(fullname);
  483.             if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
  484.                 error("%s not defined in %s", name, fullname);
  485.             stunalloc(fullname);
  486.             goto success;
  487.         }
  488.         if (geteuid() == 0) {
  489.             if ((statb.st_mode & 0111) == 0)
  490.                 goto loop;
  491.         } else if (statb.st_uid == geteuid()) {
  492.             if ((statb.st_mode & 0100) == 0)
  493.                 goto loop;
  494.         } else if (statb.st_gid == getegid()) {
  495.             if ((statb.st_mode & 010) == 0)
  496.                 goto loop;
  497.         } else {
  498.             if ((statb.st_mode & 01) == 0) {
  499. #ifdef  BSD
  500.                 if ((statb.st_mode & 010) == 0)
  501.                     goto loop;
  502.                 /* Are you in this group too? */
  503.                 {
  504.                     int group_list[NGROUPS];
  505.                     int ngroups, i;
  506.  
  507.                     ngroups = getgroups(NGROUPS, group_list);
  508.                     for (i = 0; i < ngroups; i++)
  509.                         if (statb.st_gid == group_list[i])
  510.                             goto Found;
  511.                 }
  512. #endif
  513.                 goto loop;
  514.             }
  515.         }
  516. #ifdef  BSD
  517.     Found:
  518. #endif
  519.         TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
  520.         INTOFF;
  521.         cmdp = cmdlookup(name, 1);
  522.         cmdp->cmdtype = CMDNORMAL;
  523.         cmdp->param.index = index;
  524.         INTON;
  525.         goto success;
  526.     }
  527.  
  528.     /* We failed.  If there was an entry for this command, delete it */
  529.     if (cmdp)
  530.         delete_cmd_entry();
  531.     if (printerr)
  532.         outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
  533.     entry->cmdtype = CMDUNKNOWN;
  534.     return;
  535.  
  536. success:
  537.     cmdp->rehash = 0;
  538.     entry->cmdtype = cmdp->cmdtype;
  539.     entry->u = cmdp->param;
  540. }
  541.  
  542.  
  543.  
  544. /*
  545.  * Search the table of builtin commands.
  546.  */
  547.  
  548. int
  549. find_builtin(name)
  550.     char *name;
  551.     {
  552.     const register struct builtincmd *bp;
  553.  
  554.     for (bp = builtincmd ; bp->name ; bp++) {
  555.         if (*bp->name == *name && equal(bp->name, name))
  556.             return bp->code;
  557.     }
  558.     return -1;
  559. }
  560.  
  561.  
  562.  
  563. /*
  564.  * Called when a cd is done.  Marks all commands so the next time they
  565.  * are executed they will be rehashed.
  566.  */
  567.  
  568. void
  569. hashcd() {
  570.     struct tblentry **pp;
  571.     struct tblentry *cmdp;
  572.  
  573.     for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
  574.         for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  575.             if (cmdp->cmdtype == CMDNORMAL
  576.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)
  577.                 cmdp->rehash = 1;
  578.         }
  579.     }
  580. }
  581.  
  582.  
  583.  
  584. /*
  585.  * Called before PATH is changed.  The argument is the new value of PATH;
  586.  * pathval() still returns the old value at this point.  Called with
  587.  * interrupts off.
  588.  */
  589.  
  590. void
  591. changepath(newval)
  592.     char *newval;
  593.     {
  594.     char *old, *new;
  595.     int index;
  596.     int firstchange;
  597.     int bltin;
  598.  
  599.     old = pathval();
  600.     new = newval;
  601.     firstchange = 9999;    /* assume no change */
  602.     index = 0;
  603.     bltin = -1;
  604.     for (;;) {
  605.         if (*old != *new) {
  606.             firstchange = index;
  607.             if (*old == '\0' && *new == ':'
  608.              || *old == ':' && *new == '\0')
  609.                 firstchange++;
  610.             old = new;    /* ignore subsequent differences */
  611.         }
  612.         if (*new == '\0')
  613.             break;
  614.         if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
  615.             bltin = index;
  616.         if (*new == ':') {
  617.             index++;
  618.         }
  619.         new++, old++;
  620.     }
  621.     if (builtinloc < 0 && bltin >= 0)
  622.         builtinloc = bltin;        /* zap builtins */
  623.     if (builtinloc >= 0 && bltin < 0)
  624.         firstchange = 0;
  625.     clearcmdentry(firstchange);
  626.     builtinloc = bltin;
  627. }
  628.  
  629.  
  630. /*
  631.  * Clear out command entries.  The argument specifies the first entry in
  632.  * PATH which has changed.
  633.  */
  634.  
  635. STATIC void
  636. clearcmdentry(firstchange) {
  637.     struct tblentry **tblp;
  638.     struct tblentry **pp;
  639.     struct tblentry *cmdp;
  640.  
  641.     INTOFF;
  642.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  643.         pp = tblp;
  644.         while ((cmdp = *pp) != NULL) {
  645.             if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange
  646.              || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) {
  647.                 *pp = cmdp->next;
  648.                 ckfree(cmdp);
  649.             } else {
  650.                 pp = &cmdp->next;
  651.             }
  652.         }
  653.     }
  654.     INTON;
  655. }
  656.  
  657.  
  658. /*
  659.  * Delete all functions.
  660.  */
  661.  
  662. #ifdef mkinit
  663. MKINIT void deletefuncs();
  664.  
  665. SHELLPROC {
  666.     deletefuncs();
  667. }
  668. #endif
  669.  
  670. void
  671. deletefuncs() {
  672.     struct tblentry **tblp;
  673.     struct tblentry **pp;
  674.     struct tblentry *cmdp;
  675.  
  676.     INTOFF;
  677.     for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
  678.         pp = tblp;
  679.         while ((cmdp = *pp) != NULL) {
  680.             if (cmdp->cmdtype == CMDFUNCTION) {
  681.                 *pp = cmdp->next;
  682.                 freefunc(cmdp->param.func);
  683.                 ckfree(cmdp);
  684.             } else {
  685.                 pp = &cmdp->next;
  686.             }
  687.         }
  688.     }
  689.     INTON;
  690. }
  691.  
  692.  
  693.  
  694. /*
  695.  * Locate a command in the command hash table.  If "add" is nonzero,
  696.  * add the command to the table if it is not already present.  The
  697.  * variable "lastcmdentry" is set to point to the address of the link
  698.  * pointing to the entry, so that delete_cmd_entry can delete the
  699.  * entry.
  700.  */
  701.  
  702. struct tblentry **lastcmdentry;
  703.  
  704.  
  705. STATIC struct tblentry *
  706. cmdlookup(name, add)
  707.     char *name;
  708.     {
  709.     int hashval;
  710.     register char *p;
  711.     struct tblentry *cmdp;
  712.     struct tblentry **pp;
  713.  
  714.     p = name;
  715.     hashval = *p << 4;
  716.     while (*p)
  717.         hashval += *p++;
  718.     hashval &= 0x7FFF;
  719.     pp = &cmdtable[hashval % CMDTABLESIZE];
  720.     for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
  721.         if (equal(cmdp->cmdname, name))
  722.             break;
  723.         pp = &cmdp->next;
  724.     }
  725.     if (add && cmdp == NULL) {
  726.         INTOFF;
  727.         cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
  728.                     + strlen(name) + 1);
  729.         cmdp->next = NULL;
  730.         cmdp->cmdtype = CMDUNKNOWN;
  731.         cmdp->rehash = 0;
  732.         strcpy(cmdp->cmdname, name);
  733.         INTON;
  734.     }
  735.     lastcmdentry = pp;
  736.     return cmdp;
  737. }
  738.  
  739.  
  740. /*
  741.  * Delete the command entry returned on the last lookup.
  742.  */
  743.  
  744. STATIC void
  745. delete_cmd_entry() {
  746.     struct tblentry *cmdp;
  747.  
  748.     INTOFF;
  749.     cmdp = *lastcmdentry;
  750.     *lastcmdentry = cmdp->next;
  751.     ckfree(cmdp);
  752.     INTON;
  753. }
  754.  
  755.  
  756.  
  757. #ifdef notdef
  758. void
  759. getcmdentry(name, entry)
  760.     char *name;
  761.     struct cmdentry *entry; 
  762.     {
  763.     struct tblentry *cmdp = cmdlookup(name, 0);
  764.  
  765.     if (cmdp) {
  766.         entry->u = cmdp->param;
  767.         entry->cmdtype = cmdp->cmdtype;
  768.     } else {
  769.         entry->cmdtype = CMDUNKNOWN;
  770.         entry->u.index = 0;
  771.     }
  772. }
  773. #endif
  774.  
  775.  
  776. /*
  777.  * Add a new command entry, replacing any existing command entry for
  778.  * the same name.
  779.  */
  780.  
  781. void
  782. addcmdentry(name, entry)
  783.     char *name;
  784.     struct cmdentry *entry;
  785.     {
  786.     struct tblentry *cmdp;
  787.  
  788.     INTOFF;
  789.     cmdp = cmdlookup(name, 1);
  790.     if (cmdp->cmdtype == CMDFUNCTION) {
  791.         freefunc(cmdp->param.func);
  792.     }
  793.     cmdp->cmdtype = entry->cmdtype;
  794.     cmdp->param = entry->u;
  795.     INTON;
  796. }
  797.  
  798.  
  799. /*
  800.  * Define a shell function.
  801.  */
  802.  
  803. void
  804. defun(name, func)
  805.     char *name;
  806.     union node *func;
  807.     {
  808.     struct cmdentry entry;
  809.  
  810.     INTOFF;
  811.     entry.cmdtype = CMDFUNCTION;
  812.     entry.u.func = copyfunc(func);
  813.     addcmdentry(name, &entry);
  814.     INTON;
  815. }
  816.  
  817.  
  818. /*
  819.  * Delete a function if it exists.
  820.  */
  821.  
  822. void
  823. unsetfunc(name)
  824.     char *name;
  825.     {
  826.     struct tblentry *cmdp;
  827.  
  828.     if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
  829.         freefunc(cmdp->param.func);
  830.         delete_cmd_entry();
  831.     }
  832. }
  833.