Subject: csh lacks 'which' builtin (#330) Index: bin/csh/sh.exec.c,sh.exec2.c,sh.h 2.11BSD Description: csh does not have the 'which' command as a builtin. This causes the very slow script /usr/ucb/which to be used. tcsh has 'which' as a builtin as does 4.4BSD's csh. Repeat-By: time which which /usr/ucb/which 1.4u 1.5s 0:03 76% 44+5io 3ov 0sw Fix: The 'which' code was borrowed from 4.4-Lite2's csh source. To ease porting the /sys/h/stat.h file was augmented with the macros for testing the file type (S_IFDIR, etc). Adding 'which' caused the sh.exec.o module to become too large for the overlay it resided in. To get around this a new source module (sh.exec2.c) was created. This new module was then placed into the second overlay. sh.exec2.c referenced the hash macros and definitions so these were moved from sh.exec.c into an include file (sh.exec.h). After rebuilding and installing csh the 'which' command is almost instantaneous: Script started on Sat Sep 21 21:05:21 1996 # time which which which: shell built-in command. 0.0u 0.0s 0:00 83% 0+0io 2ov 0sw # exit # script done on Sat Sep 21 21:05:31 1996 The update consists of two files: a shar archive of new files to be added to the system and a patch file which updates existing files. To install this update cut where indicated, saving to a file (/tmp/330). Then: cd /tmp sh 330 sh 330.shar patch -p0 < 330.patch cd /usr/src/bin/csh make csh tags make install make clean It will be necessary to log out and back in for the new shell to take effect. This and previous updates are available via anonymous FTP to either FTP.IIPO.GTEGSC.COM or MOE.2BSD.COM in the directory /pub/2.11BSD. ---------------------------cut here--------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # 330.patch # 330.shar # This archive created: Sat Sep 21 21:15:39 1996 export PATH; PATH=/bin:/usr/bin:$PATH if test -f '330.patch' then echo shar: "will not over-write existing file '330.patch'" else sed 's/^X//' << \SHAR_EOF > '330.patch' X*** /usr/src/bin/csh/Makefile.old Sat Apr 9 00:19:41 1994 X--- /usr/src/bin/csh/Makefile Sat Sep 21 20:36:13 1996 X*************** X*** 3,9 **** X # All rights reserved. The Berkeley Software License Agreement X # specifies the terms and conditions for redistribution. X # X! # @(#)Makefile 5.3.1 (2.11BSD GTE) 4/9/94 X # X # C Shell with process control; VM/UNIX VAX Makefile X # Bill Joy UC Berkeley; Jim Kulp IIASA, Austria X--- 3,9 ---- X # All rights reserved. The Berkeley Software License Agreement X # specifies the terms and conditions for redistribution. X # X! # @(#)Makefile 5.3.2 (2.11BSD GTE) 1996/9/20 X # X # C Shell with process control; VM/UNIX VAX Makefile X # Bill Joy UC Berkeley; Jim Kulp IIASA, Austria X*************** X*** 29,36 **** X sh.init.o sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.proc.o sh.sem.o \ X sh.set.o X OV1= sh.o sh.exec.o sh.time.o X X! OBJS= ${BASE} ${OV1} X X LIBOBJS= ndbm.o getpwent.o X X--- 29,37 ---- X sh.init.o sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.proc.o sh.sem.o \ X sh.set.o X OV1= sh.o sh.exec.o sh.time.o X+ OV2= sh.exec2.o X X! OBJS= ${BASE} ${OV1} ${OV2} X X LIBOBJS= ndbm.o getpwent.o X X*************** X*** 47,53 **** X rm -f csh X /bin/ld ${SEPFLAG} /lib/crt0.o ${BASE} \ X -Z ${OV1} \ X! -Z ${LIBOBJS} \ X -Y strings.o -o csh ${LIBES}; X X .DEFAULT: X--- 48,54 ---- X rm -f csh X /bin/ld ${SEPFLAG} /lib/crt0.o ${BASE} \ X -Z ${OV1} \ X! -Z ${OV2} ${LIBOBJS} \ X -Y strings.o -o csh ${LIBES}; X X .DEFAULT: X*************** X*** 91,97 **** X ${VGRIND} -t -x -h Index index >grind/index.t X X install: csh X! install -s csh ${DESTDIR}/bin/csh X X clean: X ${RM} -f a.out strings x.c xs.c csh errs X--- 92,98 ---- X ${VGRIND} -t -x -h Index index >grind/index.t X X install: csh X! install -m 755 -o bin -g bin -s csh ${DESTDIR}/bin/csh X X clean: X ${RM} -f a.out strings x.c xs.c csh errs X*************** X*** 98,104 **** X ${RM} -f *.o X ${RM} -rf vgrind X X! tags: X ${CTAGS} -t *.h sh*.c X X sh.o: sh.h sh.local.h sh.char.h X--- 99,105 ---- X ${RM} -f *.o X ${RM} -rf vgrind X X! tags: *.h sh*.c X ${CTAGS} -t *.h sh*.c X X sh.o: sh.h sh.local.h sh.char.h X*************** X*** 107,112 **** X--- 108,114 ---- X sh.dol.o: sh.h sh.local.h sh.char.h X sh.err.o: sh.h sh.local.h sh.char.h X sh.exec.o: sh.h sh.local.h sh.char.h X+ sh.exec2.o: sh.h sh.local.h sh.char.h X sh.exp.o: sh.h sh.local.h sh.char.h X sh.file.o: sh.h sh.local.h sh.char.h X sh.func.o: sh.h sh.local.h sh.char.h X*** /usr/src/bin/csh/sh.exec.c.old Sat Mar 30 23:09:13 1996 X--- /usr/src/bin/csh/sh.exec.c Sat Sep 21 20:17:59 1996 X*************** X*** 5,11 **** X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.exec.c 5.2.1 (2.11BSD) 1996/3/20"; X #endif X X #include "sh.h" X--- 5,11 ---- X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.exec.c 5.2.2 (2.11BSD) 1996/9/20"; X #endif X X #include "sh.h" X*************** X*** 33,61 **** X char *exerr; /* Execution error message */ X char *expath; /* Path for exerr */ X X! /* X! * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used X! * to hash execs. If it is allocated (havhash true), then to tell X! * whether ``name'' is (possibly) present in the i'th component X! * of the variable path, you look at the bit in xhash indexed by X! * hash(hashname("name"), i). This is setup automatically X! * after .login is executed, and recomputed whenever ``path'' is X! * changed. X! * The two part hash function is designed to let texec() call the X! * more expensive hashname() only once and the simple hash() several X! * times (once for each path component checked). X! * Byte size is assumed to be 8. X! */ X! #define HSHSIZ 8192 /* 1k bytes */ X! #define HSHMASK (HSHSIZ - 1) X! #define HSHMUL 243 X! char xhash[HSHSIZ / 8]; X! #define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) X! #define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ X! #define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ X! #ifdef VFORK X! int hits, misses; X! #endif X X /* Dummy search path for just absolute search when no path */ X char *justabs[] = { "", 0 }; X--- 33,39 ---- X char *exerr; /* Execution error message */ X char *expath; /* Path for exerr */ X X! #include "sh.exec.h" X X /* Dummy search path for just absolute search when no path */ X char *justabs[] = { "", 0 }; X*************** X*** 297,303 **** X dirp = opendir(*pv); X if (dirp == NULL) X continue; X! if (fstat(dirp->dd_fd, &stb) < 0 || !isdir(stb)) { X closedir(dirp); X continue; X } X--- 275,281 ---- X dirp = opendir(*pv); X if (dirp == NULL) X continue; X! if (fstat(dirp->dd_fd, &stb) < 0 || !S_ISDIR(stb.st_mode)) { X closedir(dirp); X continue; X } X*** /usr/src/bin/csh/sh.glob.c.old Sat Aug 31 00:08:21 1991 X--- /usr/src/bin/csh/sh.glob.c Fri Sep 20 01:34:49 1996 X*************** X*** 5,11 **** X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.glob.c 5.4 (Berkeley) 5/13/86"; X #endif X X #include "sh.h" X--- 5,11 ---- X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.glob.c 5.4.1 (2.11BSD) 1996/9/20"; X #endif X X #include "sh.h" X*************** X*** 189,195 **** X } X if (fstat(dirp->dd_fd, &stb) < 0) X goto patherr1; X! if (!isdir(stb)) { X errno = ENOTDIR; X goto patherr1; X } X--- 189,195 ---- X } X if (fstat(dirp->dd_fd, &stb) < 0) X goto patherr1; X! if (!S_ISDIR(stb.st_mode)) { X errno = ENOTDIR; X goto patherr1; X } X*************** X*** 376,382 **** X while (*s) X addpath(*s++); X addpath('/'); X! if (stat(gpath, &stb) == 0 && isdir(stb)) X if (*p == 0) { X Gcat(gpath, ""); X globcnt++; X--- 376,382 ---- X while (*s) X addpath(*s++); X addpath('/'); X! if (stat(gpath, &stb) == 0 && S_ISDIR(stb.st_mode)) X if (*p == 0) { X Gcat(gpath, ""); X globcnt++; X*** /usr/src/bin/csh/sh.h.old Mon Jan 10 21:38:15 1994 X--- /usr/src/bin/csh/sh.h Fri Sep 20 01:27:43 1996 X*************** X*** 3,9 **** X * All rights reserved. The Berkeley Software License Agreement X * specifies the terms and conditions for redistribution. X * X! * @(#)sh.h 5.3.1 (2.11BSD GTE) 1/1/94 X */ X X #include X--- 3,9 ---- X * All rights reserved. The Berkeley Software License Agreement X * specifies the terms and conditions for redistribution. X * X! * @(#)sh.h 5.3.2 (2.11BSD GTE) 1996/9/20 X */ X X #include X*************** X*** 25,32 **** X * Jim Kulp, IIASA, Laxenburg Austria X * April, 1980 X */ X- X- #define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR) X X typedef char bool; X X--- 25,30 ---- X*** /usr/src/bin/csh/sh.init.c.old Sat Aug 31 00:09:14 1991 X--- /usr/src/bin/csh/sh.init.c Fri Sep 20 00:19:18 1996 X*************** X*** 5,11 **** X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.init.c 5.2 (Berkeley) 6/6/85"; X #endif X X #include "sh.local.h" X--- 5,11 ---- X */ X X #if !defined(lint) && defined(DOSCCS) X! static char *sccsid = "@(#)sh.init.c 5.2.1 (2.11BSD) 1996/9/20"; X #endif X X #include "sh.local.h" X*************** X*** 66,71 **** X--- 66,72 ---- X extern int doumask(); X extern int dowait(); X extern int dowhile(); X+ extern int dowhich(); X extern int dozip(); X extern int execash(); X extern int goodbye(); X*************** X*** 159,164 **** X--- 160,166 ---- X "unset", unset, 1, INF, X "unsetenv", dounsetenv, 1, INF, X "wait", dowait, 0, 0, X+ "which", dowhich, 1, INF, X "while", dowhile, 1, INF, X }; X int nbfunc = sizeof bfunc / sizeof *bfunc; X*** /usr/src/sys/h/stat.h.old Mon Mar 13 21:06:42 1995 X--- /usr/src/sys/h/stat.h Fri Sep 20 00:50:01 1996 X*************** X*** 3,9 **** X * All rights reserved. The Berkeley software License Agreement X * specifies the terms and conditions for redistribution. X * X! * @(#)stat.h 7.1.4 (2.11BSD) 1995/03/13 X */ X X #ifndef _STAT_H_ X--- 3,9 ---- X * All rights reserved. The Berkeley software License Agreement X * specifies the terms and conditions for redistribution. X * X! * @(#)stat.h 7.1.5 (2.11BSD) 1996/09/20 X */ X X #ifndef _STAT_H_ X*************** X*** 65,70 **** X--- 65,75 ---- X #define S_IROTH 0000004 /* R for other */ X #define S_IWOTH 0000002 /* W for other */ X #define S_IXOTH 0000001 /* X for other */ X+ X+ #define S_ISDIR(m) ((m & 0170000) == 0040000) /* directory */ X+ #define S_ISCHR(m) ((m & 0170000) == 0020000) /* char special */ X+ #define S_ISBLK(m) ((m & 0170000) == 0060000) /* block special */ X+ #define S_ISREG(m) ((m & 0170000) == 0100000) /* regular file */ X X /* X * Definitions of flags stored in file flags word. Different from 4.4 because X*** /VERSION.old Sun Sep 15 22:15:36 1996 X--- /VERSION Mon Sep 16 21:00:09 1996 X*************** X*** 1,4 **** X! Current Patch Level: 329 X X 2.11 BSD X ============ X--- 1,4 ---- X! Current Patch Level: 330 X X 2.11 BSD X ============ SHAR_EOF fi if test -f '330.shar' then echo shar: "will not over-write existing file '330.shar'" else sed 's/^X//' << \SHAR_EOF > '330.shar' X#! /bin/sh X# This is a shell archive, meaning: X# 1. Remove everything above the #! /bin/sh line. X# 2. Save the resulting text in a file. X# 3. Execute the file with /bin/sh (not csh) to create: X# /usr/src/bin/csh/sh.exec2.c X# /usr/src/bin/csh/sh.exec.h X# This archive created: Sat Sep 21 20:46:01 1996 Xexport PATH; PATH=/bin:/usr/bin:$PATH Xif test -f '/usr/src/bin/csh/sh.exec2.c' Xthen X echo shar: "will not over-write existing file '/usr/src/bin/csh/sh.exec2.c'" Xelse Xsed 's/^Z//' << \SHAR_EOF > '/usr/src/bin/csh/sh.exec2.c' XZ/* XZ * From the 4.4-Lite2 CD's csh sources and modified appropriately. XZ*/ XZ XZ#if !defined(lint) && defined(DOSCCS) XZstatic char *sccsid = "@(#)sh.exec2.c 1.0 (2.11BSD) 1996/9/20"; XZ#endif XZ XZ#include "sh.h" XZ#include XZ#include XZ#include XZ#include XZ#include "sh.exec.h" XZ XZextern char *justabs[]; /* in sh.exec.c */ XZ XZstatic int XZiscommand(name) XZ char *name; XZ{ XZ register char **pv; XZ register char *sav; XZ register struct varent *v; XZ bool slash = any(name, '/'); XZ int hashval = 0, hashval1, i; XZ XZ v = adrof("path"); XZ if (v == 0 || v->vec[0] == 0 || slash) XZ pv = justabs; XZ else XZ pv = v->vec; XZ sav = strspl("/", name); /* / command name for postpending */ XZ if (havhash) XZ hashval = hashname(name); XZ i = 0; XZ do { XZ if (!slash && pv[0][0] == '/' && havhash) { XZ hashval1 = hash(hashval, i); XZ if (!bit(xhash, hashval1)) XZ goto cont; XZ } XZ if (pv[0][0] == 0 || eq(pv[0], ".")) { /* don't make ./xxx */ XZ if (executable(NULL, name, 0)) { XZ xfree(sav); XZ return i + 1; XZ } XZ } XZ else { XZ if (executable(*pv, sav, 0)) { XZ xfree(sav); XZ return i + 1; XZ } XZ } XZcont: XZ pv++; XZ i++; XZ } while (*pv); XZ xfree(sav); XZ return 0; XZ} XZ XZ/* Also by: XZ * Andreas Luik XZ * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung XZ * Azenberstr. 35 XZ * D-7000 Stuttgart 1 XZ * West-Germany XZ * is the executable() routine below and changes to iscommand(). XZ * Thanks again!! XZ */ XZ XZ/* XZ * executable() examines the pathname obtained by concatenating dir and name XZ * (dir may be NULL), and returns 1 either if it is executable by us, or XZ * if dir_ok is set and the pathname refers to a directory. XZ * This is a bit kludgy, but in the name of optimization... XZ */ XZstatic int XZexecutable(dir, name, dir_ok) XZ char *dir, *name; XZ bool dir_ok; XZ{ XZ struct stat stbuf; XZ char path[MAXPATHLEN + 1]; XZ register char *dp, *sp; XZ char *strname; XZ XZ if (dir && *dir) { XZ for (dp = path, sp = dir; *sp; *dp++ = *sp++) XZ if (dp == &path[MAXPATHLEN + 1]) { XZ *--dp = '\0'; XZ break; XZ } XZ for (sp = name; *sp; *dp++ = *sp++) XZ if (dp == &path[MAXPATHLEN + 1]) { XZ *--dp = '\0'; XZ break; XZ } XZ *dp = '\0'; XZ strname = path; XZ } XZ else XZ strname = name; XZ return (stat(strname, &stbuf) != -1 && XZ ((S_ISREG(stbuf.st_mode) && XZ /* save time by not calling access() in the hopeless case */ XZ (stbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) && XZ access(strname, X_OK) == 0) || XZ (dir_ok && S_ISDIR(stbuf.st_mode)))); XZ} XZ/* The dowhich() is by: XZ * Andreas Luik XZ * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung XZ * Azenberstr. 35 XZ * D-7000 Stuttgart 1 XZ * West-Germany XZ * Thanks!! XZ */ XZ/*ARGSUSED*/ XZvoid XZdowhich(v, c) XZ register char **v; XZ struct command *c; XZ{ XZ struct wordent lex[3]; XZ struct varent *vp; XZ XZ lex[0].next = &lex[1]; XZ lex[1].next = &lex[2]; XZ lex[2].next = &lex[0]; XZ XZ lex[0].prev = &lex[2]; XZ lex[1].prev = &lex[0]; XZ lex[2].prev = &lex[1]; XZ XZ lex[0].word = ""; XZ lex[2].word = "\n"; XZ XZ while (*++v) { XZ if ((vp = adrof1(*v, &aliases)) != NULL) { XZ (void) printf("%s: \t aliased to ", *v); XZ blkpr(vp->vec); XZ (void) putchar('\n'); XZ } XZ else { XZ lex[1].word = *v; XZ tellmewhat(lex); XZ } XZ } XZ} XZ XZstatic void XZtellmewhat(lex) XZ struct wordent *lex; XZ{ XZ int i; XZ struct biltins *bptr; XZ register struct wordent *sp = lex->next; XZ bool aliased = 0; XZ register char *s2; XZ char *s0, *s1, *cmd; XZ char qc; XZ XZ if (adrof1(sp->word, &aliases)) { XZ alias(lex); XZ sp = lex->next; XZ aliased = 1; XZ } XZ XZ s0 = sp->word; /* to get the memory freeing right... */ XZ XZ /* handle quoted alias hack */ XZ if ((*(sp->word) & (QUOTE | TRIM)) == QUOTE) XZ (sp->word)++; XZ XZ /* do quoting, if it hasn't been done */ XZ s1 = s2 = sp->word; XZ while (*s2) XZ switch (*s2) { XZ case '\'': XZ case '"': XZ qc = *s2++; XZ while (*s2 && *s2 != qc) XZ *s1++ = *s2++ | QUOTE; XZ if (*s2) XZ s2++; XZ break; XZ case '\\': XZ if (*++s2) XZ *s1++ = *s2++ | QUOTE; XZ break; XZ default: XZ *s1++ = *s2++; XZ } XZ *s1 = '\0'; XZ XZ for (bptr = bfunc; bptr < &bfunc[nbfunc]; bptr++) { XZ if (eq(sp->word, bptr->bname)) { XZ if (aliased) XZ prlex(lex); XZ (void) printf("%s: shell built-in command.\n", sp->word); XZ sp->word = s0; /* we save and then restore this */ XZ return; XZ } XZ } XZ XZ sp->word = cmd = globone(sp->word); XZ XZ if ((i = iscommand(strip(sp->word))) != 0) { XZ register char **pv; XZ register struct varent *v; XZ bool slash = any(sp->word, '/'); XZ XZ v = adrof("path"); XZ if (v == 0 || v->vec[0] == 0 || slash) XZ pv = justabs; XZ else XZ pv = v->vec; XZ XZ while (--i) XZ pv++; XZ if (pv[0][0] == 0 || eq(pv[0], ".")) { XZ if (!slash) { XZ sp->word = strspl("./", sp->word); XZ prlex(lex); XZ xfree(sp->word); XZ } XZ else XZ prlex(lex); XZ sp->word = s0; /* we save and then restore this */ XZ xfree(cmd); XZ return; XZ } XZ s1 = strspl(*pv, "/"); XZ sp->word = strspl(s1, sp->word); XZ xfree(s1); XZ prlex(lex); XZ xfree(sp->word); XZ } XZ else { XZ if (aliased) XZ prlex(lex); XZ (void) printf("%s: Command not found.\n", sp->word); XZ } XZ sp->word = s0; /* we save and then restore this */ XZ xfree(cmd); XZ} XSHAR_EOF Xchmod 644 '/usr/src/bin/csh/sh.exec2.c' Xfi Xif test -f '/usr/src/bin/csh/sh.exec.h' Xthen X echo shar: "will not over-write existing file '/usr/src/bin/csh/sh.exec.h'" Xelse Xsed 's/^Z//' << \SHAR_EOF > '/usr/src/bin/csh/sh.exec.h' XZ/* XZ * Xhash is an array of HSHSIZ bits (HSHSIZ / 8 chars), which are used XZ * to hash execs. If it is allocated (havhash true), then to tell XZ * whether ``name'' is (possibly) present in the i'th component XZ * of the variable path, you look at the bit in xhash indexed by XZ * hash(hashname("name"), i). This is setup automatically XZ * after .login is executed, and recomputed whenever ``path'' is XZ * changed. XZ * The two part hash function is designed to let texec() call the XZ * more expensive hashname() only once and the simple hash() several XZ * times (once for each path component checked). XZ * Byte size is assumed to be 8. XZ */ XZ#define HSHSIZ 8192 /* 1k bytes */ XZ#define HSHMASK (HSHSIZ - 1) XZ#define HSHMUL 243 XZchar xhash[HSHSIZ / 8]; XZ#define hash(a, b) ((a) * HSHMUL + (b) & HSHMASK) XZ#define bit(h, b) ((h)[(b) >> 3] & 1 << ((b) & 7)) /* bit test */ XZ#define bis(h, b) ((h)[(b) >> 3] |= 1 << ((b) & 7)) /* bit set */ XZ#ifdef VFORK XZint hits, misses; XZ#endif XSHAR_EOF Xchmod 644 '/usr/src/bin/csh/sh.exec.h' Xfi Xexit 0 X# End of shell archive SHAR_EOF fi exit 0 # End of shell archive