Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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