+static char *sccsid = "@(#)exec.c 4.1 %G%";
+
+#include "sh.h"
+
+/*
+ * C shell
+ */
+
+/*
+ * System level search and execute of a command.
+ * We look in each directory for the specified command name.
+ * If the name contains a '/' then we execute only the full path name.
+ * If there is no search path then we execute only full path names.
+ */
+
+/*
+ * As we search for the command we note the first non-trivial error
+ * message for presentation to the user. This allows us often
+ * to show that a file has the wrong mode/no access when the file
+ * is not in the last component of the search path, so we must
+ * go on after first detecting the error.
+ */
+char *exerr; /* Execution error message */
+char *expath; /* Path for exerr */
+
+/*
+ * Xhash is an array of HSHSIZ chars, which are used to hash execs.
+ * If it is allocated, then to tell whether ``name'' is (possibly)
+ * present in the i'th component of the variable path, you look at
+ * the i'th bit of xhash[hash("name")]. This is setup automatically
+ * after .login is executed, and recomputed whenever ``path'' is
+ * changed.
+ */
+int havhash;
+#define HSHSIZ 511
+char xhash[HSHSIZ];
+#ifdef VFORK
+int hits, misses;
+#endif
+
+/* Dummy search path for just absolute search when no path */
+char *justabs[] = { "", 0 };
+
+doexec(t)
+ register struct command *t;
+{
+ char *sav;
+ register char *dp, **pv, **av;
+ register struct varent *v;
+ bool slash = any('/', t->t_dcom[0]);
+ int hashval, i;
+ char *blk[2];
+
+ /*
+ * Glob the command name. If this does anything, then we
+ * will execute the command only relative to ".". One special
+ * case: if there is no PATH, then we execute only commands
+ * which start with '/'.
+ */
+ dp = globone(t->t_dcom[0]);
+ sav = t->t_dcom[0];
+ exerr = 0; expath = t->t_dcom[0] = dp;
+ xfree(sav);
+ v = adrof("path");
+ if (v == 0 && expath[0] != '/')
+ pexerr();
+ slash |= gflag;
+
+ /*
+ * Glob the argument list, if necessary.
+ * Otherwise trim off the quote bits.
+ */
+ gflag = 0; av = &t->t_dcom[1];
+ rscan(av, tglob);
+ if (gflag) {
+ av = glob(av);
+ if (av == 0)
+ error("No match");
+ }
+ blk[0] = t->t_dcom[0];
+ blk[1] = 0;
+ av = blkspl(blk, av);
+#ifdef VFORK
+ Vav = av;
+#endif
+ scan(av, trim);
+
+ xechoit(av); /* Echo command if -x */
+ closech(); /* Close random fd's */
+
+ /*
+ * We must do this after any possible forking (like `foo`
+ * in glob) so that this shell can still do subprocesses.
+ */
+ sigsys(SIGCHLD, SIG_IGN); /* sigsys for vforks sake */
+
+ /*
+ * If no path, no words in path, or a / in the filename
+ * then restrict the command search.
+ */
+ if (v == 0 || v->vec[0] == 0 || slash)
+ pv = justabs;
+ else
+ pv = v->vec;
+ sav = strspl("/", *av); /* / command name for postpending */
+#ifdef VFORK
+ Vsav = sav;
+#endif
+ if (havhash)
+ hashval = xhash[hash(*av)];
+ i = 0;
+#ifdef VFORK
+ hits++;
+#endif
+ do {
+ if (!slash && pv[0][0] == '/' && havhash && (hashval & (1 << (i % 8))) == 0)
+ goto cont;
+ if (pv[0][0] == 0 || eq(pv[0], ".")) /* don't make ./xxx */
+ texec(*av, av);
+ else {
+ dp = strspl(*pv, sav);
+#ifdef VFORK
+ Vdp = dp;
+#endif
+ texec(dp, av);
+#ifdef VFORK
+ Vdp = 0;
+#endif
+ xfree(dp);
+ }
+#ifdef VFORK
+ misses++;
+#endif
+cont:
+ pv++;
+ i++;
+ } while (*pv);
+#ifdef VFORK
+ hits--;
+#endif
+#ifdef VFORK
+ Vsav = 0;
+ Vav = 0;
+#endif
+ xfree(sav);
+ xfree(av);
+ pexerr();
+}
+
+pexerr()
+{
+
+ /* Couldn't find the damn thing */
+ setname(expath);
+ /* xfree(expath); */
+ if (exerr)
+ bferr(exerr);
+ bferr("Command not found");
+}
+
+/* Last resort shell */
+char *lastsh[] = { SHELLPATH, 0 };
+
+/*
+ * Execute command f, arg list t.
+ * Record error message if not found.
+ * Also do shell scripts here.
+ */
+texec(f, t)
+ char *f;
+ register char **t;
+{
+ register struct varent *v;
+ register char **vp;
+ extern char *sys_errlist[];
+
+ execv(f, t);
+ switch (errno) {
+
+ case ENOEXEC:
+ /*
+ * If there is an alias for shell, then
+ * put the words of the alias in front of the
+ * argument list replacing the command name.
+ * Note no interpretation of the words at this point.
+ */
+ v = adrof1("shell", &aliases);
+ if (v == 0) {
+#ifdef OTHERSH
+ register int ff = open(f, 0);
+ char ch;
+#endif
+
+ vp = lastsh;
+ vp[0] = adrof("shell") ? value("shell") : SHELLPATH;
+#ifdef OTHERSH
+ if (ff != -1 && read(ff, &ch, 1) == 1 && ch != '#')
+ vp[0] = OTHERSH;
+ close(ff);
+#endif
+ } else
+ vp = v->vec;
+ t[0] = f;
+ t = blkspl(vp, t); /* Splice up the new arglst */
+ f = *t;
+ execv(f, t);
+ xfree((char *)t);
+ /* The sky is falling, the sky is falling! */
+
+ case ENOMEM:
+ Perror(f);
+
+ case ENOENT:
+ break;
+
+ default:
+ if (exerr == 0) {
+ exerr = sys_errlist[errno];
+ expath = savestr(f);
+ }
+ }
+}
+
+execash(t, kp)
+ register struct command *kp;
+{
+
+ didcch++;
+ signal(SIGINT, parintr);
+ signal(SIGQUIT, parintr);
+ signal(SIGTERM, parterm); /* if doexec loses, screw */
+ lshift(kp->t_dcom, 1);
+ exiterr++;
+ doexec(kp);
+ /*NOTREACHED*/
+}
+
+xechoit(t)
+ char **t;
+{
+
+ if (adrof("echo")) {
+ flush();
+ haderr = 1;
+ blkpr(t), printf("\n");
+ haderr = 0;
+ }
+}
+
+dohash()
+{
+ struct stat stb;
+ struct direct dirbuf[BUFSIZ / sizeof (struct direct)];
+ char d_name[DIRSIZ + 1];
+ register int dirf, cnt;
+ int i = 0;
+ struct varent *v = adrof("path");
+ char **pv;
+
+ havhash = 1;
+ for (cnt = 0; cnt < HSHSIZ; cnt++)
+ xhash[cnt] = 0;
+ if (v == 0)
+ return;
+ for (pv = v->vec; *pv; pv++, i = (i + 1) % 8) {
+ if (pv[0][0] != '/')
+ continue;
+ dirf = open(*pv, 0);
+ if (dirf < 0)
+ continue;
+ if (fstat(dirf, &stb) < 0 || !isdir(stb)) {
+ close(dirf);
+ continue;
+ }
+ while ((cnt = read(dirf, (char *) dirbuf, sizeof dirbuf)) >= sizeof dirbuf[0]) {
+ register struct direct *ep = dirbuf;
+
+ for (cnt /= sizeof(struct direct); cnt > 0; cnt--, ep++) {
+ if (ep->d_ino == 0)
+ continue;
+ copdent(d_name, ep->d_name);
+ xhash[hash(d_name)] |= (1 << i);
+ }
+ }
+ close(dirf);
+ }
+}
+
+dounhash()
+{
+
+ havhash = 0;
+}
+
+#ifdef VFORK
+hashstat()
+{
+
+ if (hits+misses)
+ printf("%d hits, %d misses, %2d%%\n", hits, misses, 100 * hits / (hits + misses));
+}
+#endif
+
+hash(cp)
+ register char *cp;
+{
+ register long hash = 0;
+ int retval;
+
+ while (*cp)
+ hash += hash + *cp++;
+ if (hash < 0)
+ hash = -hash;
+ retval = hash % HSHSIZ;
+ return (retval);
+}