[net.sources] Son of 'which'

larry@tikal.UUCP (Larry J. Barello) (08/26/85)

A week or so I posted a quick C version of the UCB shell script
"which".  It takes as arguments command names and searches your path
for instances of them.  The version I posted had some cute bugs: it
didn't work if you didn't have a path or if there were a null
component in the path (Thanks to Tom Truscott).  To the folks with v8
shell: wish I had it, sounds like it make a lot of utilities, like
this one, useless.

Here is a fancified version of the original one.

	..!uw-beaver!teltone!larry

-------- cut here (duh) -------

#include <stdio.h>

char *getenv();
char *index();

int
main(ac,av)
char **av;
{
    char *origpath, *path, *cp;
    char buf[200];
    char patbuf[512];
    int quit, found;

    if (ac < 2) {
	fprintf(stderr, "Usage: %s cmd [cmd, ..]\n", *av);
	exit(1);
    }
    if ((origpath = getenv("PATH")) == 0)
	origpath = ".";

    av[ac] = 0;
    for(av++ ; *av; av++) {

	strcpy(patbuf, origpath);
	cp = path = patbuf;
	quit = found = 0;

	while(!quit) {
	    cp = index(path, ':');
	    if (cp == NULL) 
		quit++;
	    else
		*cp = '\0';

	    sprintf(buf, "%s/%s", (*path ? path:"."), *av);
	    path = ++cp;

	    if (access(buf, 1) == 0) {
		printf("%s\n", buf);
		found++;
	    }
	}
	if (!found) 
	    printf("No %s in %s\n", *av, origpath);
    }
    exit(0);
}

------------------

jeff@ssc-vax.UUCP (Jeffrey Jongeward) (08/28/85)

> A week or so I posted a quick C version of the UCB shell script
> "which".  It takes as arguments command names and searches your path
> for instances of them.  The version I posted had some cute bugs: it
> didn't work if you didn't have a path or if there were a null
> component in the path (Thanks to Tom Truscott).  To the folks with v8
> shell: wish I had it, sounds like it make a lot of utilities, like
> this one, useless.
> 
> Here is a fancified version of the original one.
> 
> 	..!uw-beaver!teltone!larry
> 

Larry's "which" clone is a big win because it results in about a 3X
speed improvement over the sh-coded version.  However, in the 4.2BSD environment
(at least) there is 1 bug, and 3 incompatibilities with the standard
version.  In addition, the standard version embodies (at least one) bug of
it's own.

The Bugs:
Access(2) will return return 0 if a file is a directory and searchable,
but not executable (since X_OK only checks only the execute bit); so a
stat(2) is required in addition to check that the file is not a
directory.  In addition, access(... X_OK) is truly worthless
if the user is 'su' - since every existing file will return 0 
regardless of it's mode.

The 4.2BSD original embodies the 'su' problem as will; it will incorrectly
identify a non-executable in the search path.  The diffs posted below "fix"
that problem too - the price for this is to perform a somewhat pointless
access(2) call for the 'su'ed user, and to perform a worthless check
that some execute bit is set (since it duplicates the check in access)
for the non-su user.

The Incompatibilities:
1.  The program should bail out after the first found occurrence.
2.  The path should be printed delimited with ' ', not ':' when not found.
3.  the leading 'No' should be 'no' when not found.

Here are the diffs:

1a2,4
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <sys/file.h>
4a8
> struct stat sb;
18a23
> 
39,41c44,49
< 	    if (access(buf, 1) == 0) {
< 		printf("%s\n", buf);
< 		found++;
---
> 	    if ( access(buf, 1) == 0 && !stat(buf,&sb) ) {
> 		if((sb.st_mode&S_IFDIR) == 0 && sb.st_mode&0111) {
> 			printf("%s\n", buf);
> 			found++;
> 			break;
> 		}
44,45c52,62
< 	if (!found) 
< 	    printf("No %s in %s\n", *av, origpath);
---
> 	if (!found)  {
> 	    char *optr = origpath;
> 	    char *oend = optr+strlen(origpath);
> 
> 	    while(optr < oend) {
> 		if(*optr == ':')
> 			*optr = ' ';
> 		optr++;
> 	    }
> 	    printf("no %s in %s\n", *av, origpath);
> 	}

robert@smcvax (09/02/85)

>
>Here is a fancified version of the original one.
>
>	..!uw-beaver!teltone!larry
>
>-------- cut here (duh) -------
>
>#include <stdio.h>
>
>char *getenv();
>char *index();
>...
>...
>	while(!quit) {
>	    cp = index(path, ':');
>	    if (cp == NULL) 
>		quit++;

For those of you without "index()", the intent of which is to return
a pointer to the first occurrence of str2 in str1 (or NULL if no match),
replace then name "index" with "strchr".

Robert Wallace
infoswx!smcvax!robert