[comp.bugs.4bsd] Builtin 'which' command for csh

tonyb@tektools.UUCP (Tony Birnseth) (12/12/86)

Index: /bin/csh 4.3BSD

Description:
	/usr/ucb/which does not handle builtin csh commands and is slower than
	a slug!  The following is an internal which command for csh.  
	We had a long argument about the functionality of this command.  Should
	it be useful and fast?  Or should it be compatible with /usr/ucb/which?
	Unfortunately, the compatibility argument won out.  However it should
	be easy enough for a site to remove the -u option stuff and get a
	reasonably fast useful, which command.

Fix:
	Apply the following diffs to sh.func.c and sh.init.c.

*** /tmp/,RCSt1027852	Thu Dec 11 17:03:03 1986
--- sh.func.c	Thu Dec 11 16:58:59 1986
***************
*** 1149,1151
  	if (reenter >= 2)
  		error(NOSTR);
  }

--- 1170,1325 -----
  	if (reenter >= 2)
  		error(NOSTR);
  }
+ 
+ 
+ #ifdef TEK_MODS
+ 
+ /*
+  * After much disucussion, it was decided to have this fast clean internal
+  * version of 'which' produce the same ugly output as it slower counterpart
+  * /usr/ucb/which.  A -u (useful) flag was added to allow a user to alias
+  * which to which -u for a cleaner more useful form of the command.
+  * tonyb@tek
+  */
+ dowhich(vec)
+ 	char **vec;
+ {
+ 	register struct biltins *bp;
+ 	register char *word, **pp, *func;
+ 	struct varent *vp, *pth;
+ 	char dir[MAXPATHLEN+1];
+ 	int cmp, hit, outty, uflag;
+ 	
+ 
+ 	/*
+ 	 * Glob it up
+ 	 */
+ 	uflag = gflag = 0;
+ 	tglob(++vec);
+ 	if(gflag) { 			/* has globbing chars */
+ 		vec = glob(vec);	/* glob it */
+ 		if(vec == 0)
+ 			bferr("No match");
+ 	} else
+ 		trim( vec );
+ 	
+ 	pth = adrof("path");
+ 
+ 	/*
+ 	 * Find out if we're writting to a tty.  If not, use "command type"
+ 	 * output, else use "user" type output.
+ 	 * command type output is used to do things like 'echo `which foobar`'
+ 	 */
+ 	outty = isatty(1);
+ 
+ 	/*
+ 	 * If no -u, go into useless mode, else provide useable,
+ 	 * coherent output.
+ 	 */
+ 	if(eq(*vec,"-u")) {
+ 		uflag++;
+ 		vec++;
+ 	}
+ 
+ 	while(word = *vec++) {
+ 
+ 		/*
+ 		 * check aliases first.  
+ 		 */
+ 		vp = adrof1(word, &aliases);
+ 		if(vp) {
+ 			if(!uflag) {
+ 				printf("%s: \t aliased to '", word);
+ 				blkpr(vp->vec);printf("'\n");
+ 			} else if(outty) {
+ 				printf("alias/%s '", word);
+ 				blkpr(vp->vec); printf("'\n");
+ 			} else
+ 				printf("%s\n",word);
+ 			continue;
+ 		}
+ 
+ 		/*
+ 		 * If a hard path, just return it if it is executable, else
+ 		 * return nothing.
+ 		 */
+ 		if(index(word, '/') ) {
+ 			if(executable(word)) {
+ 				printf("%s\n",word);
+ 				continue;
+ 			}
+ 			else if( !uflag ) {
+ 				printf("%s not found\n", word);
+ 				continue;
+ 			}
+ 		}
+ 	
+ 		/*
+ 		 * Check builtins
+ 		 * "standard" /usr/ucb/which does not support the concept
+ 		 * of builtin commands. YUK!!!
+ 		 */
+ 		if(uflag) {
+ 			hit = 0;
+ 			for(bp=bfunc; func = bp->bname; bp++) {
+ 				cmp = strcmp(func, word);
+ 				if(cmp == 0) {
+ 					if(outty)
+ 						printf("builtin/%s\n",word);
+ 					else
+ 						printf("%s\n",word);
+ 					hit++;
+ 				}
+ 				else if(cmp > 0)
+ 					break;
+ 			}
+ 			if(hit) 
+ 				continue;
+ 		}
+ 
+ 
+ 		/*
+ 		 * Check the paths
+ 		 * No need to read 'em, just check if it is executable.
+ 		 */
+ 		if(pth) {
+ 			register char *ep;
+ 	
+ 			hit = 0;
+ 			for(pp = pth->vec; pp && *pp && **pp; pp++) {
+ 				ep = strcpy(dir,*pp);
+ 				while(*ep)
+ 					ep++;
+ 				*ep++ = '/';
+ 				strcpy(ep,word);
+ 				if(executable(dir)) {
+ 					printf("%s\n",dir);
+ 					hit++;
+ 					break;
+ 				}
+ 			}
+ 		}
+ 
+ 		/*
+ 		 * If not found and in useless mode
+ 		 */
+ 		if(!hit && !uflag) {
+ 			printf("no %s in ", word); 
+ 			blkpr(pth->vec);
+ 			printf("\n");
+ 		}
+ 	} /* end while */
+ }
+ 
+ 
+ executable(path)
+ 	register char *path;
+ {
+ 	struct stat st;
+ 
+ 	if(access(path,1) || stat(path, &st) )
+ 		st.st_mode = 0;
+ 	return( (st.st_mode & S_IFMT) == S_IFREG);
+ }
+ #endif /* TEK_MODS */
+ 
*** /tmp/,RCSt1027862	Thu Dec 11 17:03:14 1986
--- sh.init.c	Thu Dec 11 12:47:00 1986
***************
*** 77,82
  extern	int dounhash();
  extern	int unset();
  extern	int dounsetenv();
  
  #define	INF	1000
  

--- 77,85 -----
  extern	int dounhash();
  extern	int unset();
  extern	int dounsetenv();
+ #ifdef TEK_MODS
+ extern	int dowhich();
+ #endif /* TEK_MODS */
  
  #define	INF	1000
  
***************
*** 153,158
  	"unlimit",	dounlimit,	0,	INF,
  	"unset",	unset,		1,	INF,
  	"unsetenv",	dounsetenv,	1,	INF,
  	"wait",		dowait,		0,	0,
  	"while",	dowhile,	1,	INF,
  };

--- 160,168 -----
  	"unlimit",	dounlimit,	0,	INF,
  	"unset",	unset,		1,	INF,
  	"unsetenv",	dounsetenv,	1,	INF,
+ #ifdef TEK_MODS
+ 	"which",	dowhich,	1,	INF,
+ #endif /* TEK_MODS */
  	"wait",		dowait,		0,	0,
  	"while",	dowhile,	1,	INF,
  };

dgc@ulysses.cs.ucla.edu (David G Cantor) (12/13/86)

Summary:

Expires:

Sender:

Followup-To:

Distribution:


In article <1991@tektools.UUCP> tonyb@tektools.UUCP (Tony Birnseth)
gives a mod to the csh which supports builtin commands and aliases.

Here is a simplified which which does support aliases for the csh
(correctly) and is compatible with the Bourne shell.  It requires a one
line alias in the .cshrc file to handle aliases.  If not present it
functions as the Berkeley which.  It can be put in a users bin or in a
local bin (i.e. no mods to the "system" are required).

dgc

David G. Cantor
Internet:  dgc@cs.ucla.edu
UUCP:      ...!{ihnp4, randvax, sdcrdcf, ucbvax}!ucla-cs!dgc

/*
 *	Does a quick "which".
 *
 *  To get aliases when using the cshell, put the following
 *	in your ".cshrc":
 *
 *        alias which "this-function" whichalias \!^ \`alias \!^\` 
 *
 *	where "this-function" is replaced by the (completely-qualified)
 *  name of this function.
 *
 */

#include <stdio.h>
main(argc, argv) int argc; char **argv; {
  register char *path, *tp;
  char *getenv(), tmp[4096], file[4096];
  int	end = 0;
  if (argc < 2) exit(0);
  path = getenv("PATH");
  if (argc > 3) {
		if (strcmp(*++argv, "whichalias") || *argv[2] <= 32) exit(0);
		sprintf(file, "%s is an alias: ", *++argv);
		while (*++argv) {strcat(file, " "); strcat(file, *argv);}
		strcat(file, "\n");
		printf(file);
		exit(0);}
  if (argc == 3 && strcmp(*++argv, "whichalias")) exit(0);
  argv++;
  while(1) {
		tp = tmp;
		while(1) {
		  *tp++ = *path++;
			if (*(path-1) == ':') {*--tp = '\0'; break;}
			else if (*(path-1) == '\0') {end++; break;} }
		if (!access(tmp, 4)) {
	    sprintf(file, "%s/%s", tmp, *argv);
	    if (!access(file, 1)) printf("%s\n", file), exit(0);}
	  if (end) break;}
  printf("%s not found\n", *argv);
  exit(0);}