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