which6 - successor of ... which5!

Maarten Litmaath maart at cs.vu.nl
Sat Feb 2 21:40:58 AEST 1991


Now multiple groups (see getgroups(2) on BSD-derivatives) are supported.
Enjoy.

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'which6.c'
sed 's/^X//' > 'which6.c' << '+ END-OF-FILE ''which6.c'
X/*
X * which [-i] [-a] [--] [<command>]
X * alias which alias !\$ \| /usr/local/bin/which -i !\*
X * alias which 'eval alias \$$# | /usr/local/bin/which -i ${1+"$@"}'
X * which()
X * {
X *	eval last=\"\$$#\"
X *	set | sed -n "/^$last(){$/,/^}$/p" |
X *		/usr/local/bin/which -i ${1+"$@"}
X * }
X *
X * Author: Maarten Litmaath @ VU University Amsterdam (maart at cs.vu.nl)
X *
X * First change:
X *	Emile LeBlanc (leblanc%math.Berkeley.EDU at ucbvax.berkeley.edu) notes
X *	the access() system call considering everything executable for
X *	root (!), so we give root a special treatment.  :-(
X *	`which', `which -i' and `which -a' with no further arguments now
X *	return the PATH environment variable, split up into its components.
X *	The aliases defined above are slightly different from the previous
X *	version - now it's the shell who's doing the alias checking.
X * Second change:
X *	Added support for shell functions and multiline aliases, added the
X *	`--' option, changed the source style.
X * Third change:
X *	To hell with access()!
X *	Now stat() is used to give the right answer even if the effective
X *	uid (gid) differs from the real uid (gid).
X *	We can't use setuid(geteuid()), because that's nonportable.  :-(
X * Fourth change:
X *	Jim Meyering <meyering at cs.utexas.edu> notes convert() will clobber
X *	the stack if the PATH is longer than BUF_SIZE - 1 characters.
X *	I've changed convert() altogether to return a path vector (cf. argv),
X *	whose components are the respective directories in the PATH.
X *	Furthermore in printing the PATH there are no trailing colons anymore.
X * Fifth change:
X *	I've added support for multiple groups (see getgroups(2) on BSD-
X *	derivatives).
X *	Thanks to John M. Sellens <jmsellens at watdragon.uwaterloo.ca> (and
X *	Andy at the same site).
X *	Furthermore, if a matching executable is found in an unreadable
X *	directory, beside the warning on stderr the name is now printed on
X *	stdout as well.
X */
X
X#ifdef	MULTIPLE_GROUPS
X#include	<sys/param.h>
X#else
X#include	<sys/types.h>
X#endif	/* MULTIPLE_GROUPS */
X#include	<sys/stat.h>
X#include	<stdio.h>
X
X#define		BUF_SIZE	512
X#define		M_USR		0700
X#define		M_GRP		0070
X#define		M_OTH		0007
X#define		X_ALL		0111
X#define		R_ALL		0444
X
Xchar	Version[] =
X	"@(#)which 6.0 91/02/02 Maarten Litmaath @ VU Informatika Amsterdam";
Xchar	*Prog;
X
X
Xvoid	usage()
X{
X	fprintf(stderr, "Usage: %s [-i] [-a] [--] [<command>]\n", Prog);
X	exit(1);
X}
X
X
Xmain(argc, argv) 
Xint	argc;
Xregister char	**argv;
X{
X	register char	*path, *s, **pathv, **p;
X	char	*strcpy(), *getenv(), *fgets(), buf[BUF_SIZE], **convert();
X	int	all = 0, inter = 0, stop = 0, found = 0, uid, gid, mask,
X		xmask, rmask;
X	struct	stat	st;
X	void	usage();
X
X
X	Prog = *argv++;
X	--argc;
X
X	while (!stop && (s = *argv) && (*s == '-')) {
X		++argv;
X		--argc;
X		++s;
X		while (*s)
X			switch (*s++) {
X			case 'a':
X				all = 1;
X				break;
X			case 'i':
X				inter = 1;
X				break;
X			case '-':
X				stop = 1;
X				break;
X			default:
X				usage();
X			}
X	}
X
X	if (argc > 1)
X		usage();
X
X	if (inter && *argv) {
X		while (fgets(buf, sizeof buf, stdin)) {
X			if (!found) {
X				printf("%s", *argv);
X				found = 1;
X			}
X			printf("\t%s", buf);
X		}
X		if (found && !all)
X			exit(0);
X	}
X
X	if (!(path = getenv("PATH"))) {
X		fprintf(stderr, "%s: no PATH in environment!\n", Prog);
X		exit(1);
X	}
X
X	if (!*path)
X		path = ".";		/* another dubious convention */
X
X	pathv = convert(path);		/* convert path string to vector */
X
X	if (!*argv) {			/* print path if no more arguments */
X		while (*pathv)
X			puts(*pathv++);
X		exit(0);
X	}
X
X	uid = geteuid();
X	gid = getegid();
X	if (uid == 0) {
X		xmask = X_ALL;
X		rmask = R_ALL;
X	}
X
X	for (p = pathv; path = *p++; ) {	/* try every component */
X		s = buf;
X		while (*s++ = *path++)
X			;
X		(void) strcpy(s, *argv);
X		*--s = '/';
X
X		if (stat(buf, &st) != 0 || (st.st_mode & S_IFMT) != S_IFREG)
X			continue;
X
X		/* file exists and is regular */
X
X		if (uid != 0) {
X			mask = st.st_uid == uid ? M_USR :
X				st.st_gid == gid ? M_GRP :
X#ifdef	MULTIPLE_GROUPS
X				groups_member(st.st_gid) ? M_GRP :
X#endif	/* MULTIPLE_GROUPS */
X				M_OTH;
X			xmask = X_ALL & mask;
X			rmask = R_ALL & mask;
X		}
X
X		if (!(st.st_mode & xmask))
X			continue;
X
X		/* file is executable */
X
X		*s = 0;
X		if (stat(buf, &st) != 0) {
X			perror(buf);
X			continue;
X		}
X
X		/* warn user if the directory is unreadable */
X
X		if (!(st.st_mode & rmask))
X			fprintf(stderr,
X				"%s: %s found in unreadable directory `%s'!\n",
X				Prog, *argv, buf);
X
X		*s = '/';
X		puts(buf);
X		if (!all)
X			exit(0);
X		found = 1;
X	}
X
X	if (found)
X		exit(0);
X
X	fprintf(stderr, "%s not found in:\n", *argv);
X	while (*pathv)
X		fprintf(stderr, "%s\n", *pathv++);
X	exit(1);
X	/* NOTREACHED */
X}
X
X
Xchar	**convert(path)
Xchar	*path;
X{
X	register char	*s, c;
X	register int	pathc;		/* the length of the path vector */
X	char	**v, **pathv, *malloc();
X
X	for (s = path, pathc = 2; c = *s++; )
X		if (c == ':')
X			++pathc;
X
X	if (!(pathv = (char **) malloc(pathc * sizeof(char *)))) {
X		perror("malloc");
X		exit(1);
X	}
X
X	for (s = path, v = pathv; (c = *s) != '\0'; ) {
X		if (c == ':') {
X			/*
X			 * This colon is spurious.  According to some
X			 * dubious convention it is made equivalent to a dot.
X			 */
X			*v++ = ".";
X			if (*++s == '\0')
X				*v++ = ".";
X				/*
X				 * The PATH ended in a spurious colon.
X				 * To be consistent we add another dot
X				 * to the path vector.  One day you'll
X				 * be glad we did.
X				 */
X		} else {
X			*v++ = s;
X			while ((c = *++s) != '\0')
X				if (c == ':') {
X					*s++ = '\0';
X					if (*s == '\0')
X						*v++ = ".";
X						/*
X						 * The PATH ended in a
X						 * (spurious) colon, so
X						 * add dot to the vector.
X						 */
X					break;
X				}
X		}
X	}
X
X	*v = 0;		/* Signal the end of the path vector. */
X
X	return pathv;
X}
X
X
X#ifdef	MULTIPLE_GROUPS
Xint	groups_member(gid)
Xint	gid;
X{
X	register
X	int	*p, ngroups;
X	int	groups[NGROUPS];
X
X	if ((ngroups = getgroups(NGROUPS, groups)) < 0)
X		return 0;
X	p = groups;
X        while (--ngroups >= 0)
X		if (*p++ == gid)
X			return 1;
X	return 0;
X}
X#endif	/* MULTIPLE_GROUPS */
+ END-OF-FILE which6.c
chmod 'u=rw,g=r,o=r' 'which6.c'
set `wc -c 'which6.c'`
count=$1
case $count in
5970)	:;;
*)	echo 'Bad character count in ''which6.c' >&2
		echo 'Count should be 5970' >&2
esac
echo Extracting 'which.1'
sed 's/^X//' > 'which.1' << '+ END-OF-FILE ''which.1'
X.TH WHICH 1 Apr\ 04\ 1990
X.SH NAME
Xwhich \- give alias, function or path expansion of command
X.SH SYNOPSIS
X.B which
X[
X.B \-i
X] [
X.B \-a
X] [
X.B \-\-
X] [
X.I command
X]
X.SH DESCRIPTION
X.I Which
Xprovides the user with the full expansion of the
X.I command
Xargument, be it either an \fIalias\fR, a \fIshell function\fR
Xor an executable file (default). To enable search for
X.I aliases
Xand \fIshell functions\fR
Xthe user should supply the `\fI\-i\fR'
X(= interactive) flag. In that case
X.I which
Xexpects as standard input the expansion of the \fIalias\fR
Xor \fIshell function\fR.
XIf the standard input is empty or the `\fI\-i\fR' flag has not been
Xgiven, \fIwhich\fR will try to locate \fIcommand\fR
Xin the user's \fBPATH\fR.
XThe interactive mode is easily used by setting an
X.I alias
Xlike the following:
X.ft B
X.nf
X
X	alias	which	alias !\\$ \\| /usr/local/bin/which \-i !\\*
X
X.fi
X.ft R
Xin \fIcsh\fR, or
X.ft B
X.nf
X
X	alias	which	eval alias '\\"\\$$#\\" |' \\
X			/usr/local/bin/which \-i '${1+"$@"}'
X
X.fi
X.ft R
Xin shells which are supersets of
X.I sh
Xand which know \fIaliases\fR. If your shell has \fIshell functions\fR, you
Xcan use the following function:
X.ft B
X.nf
X
X	which()
X	{
X		eval last=\\"\\$$#\\"
X		set | sed \-n "/^$last(){$/,/^}$/p" |
X			/usr/local/bin/which \-i ${1+"$@"}
X	}
X
X.fi
X.ft R
XIf the `\fI\-a\fR' (= all) flag is given,
X.I which
Xwill not stop after the first `match', but search for all occurrences of
X.I command
Xin the user's
X.B PATH.
XThe `\fI\-\-\fR'
Xflag can be used to end the list of options: the next argument (if present)
Xwill be taken as \fIcommand\fR, even if it starts with a `\-'.
X\fIWhich [\-i] [\-a] [\-\-]\fR
Xwithout further arguments prints the user's
X.B PATH
Xbroken up into its components,
Xone per line.
X.PP
XThis new version of the
X.I which
Xcommand is not a
X.I csh
Xscript.
XBeing an executable it is much faster, and not sourcing 
X.I .cshrc
Xit gives a true picture of one's
X.I aliases.
XFurthermore it will give the correct answers even if:
X.IP \-
Xthe \fIeffective uid\fR
X(\fIgid\fR) differs from the \fIreal\fR uid (gid)
X.IP \-
Xthe effective uid is 0 (\fIroot\fR).
X.SH EXAMPLE
X.ft B
X.nf
X% alias
Xwhich	alias !$ | /usr/local/bin/which \-i !*
X% which which
Xwhich	alias !$ | /usr/local/bin/which \-i !*
X% which \-a which
Xwhich	alias !$ | /usr/local/bin/which \-i !*
X/usr/local/bin/which
X/usr/ucb/which
X%
X.fi
X.ft R
X.SH AUTHOR
XMaarten Litmaath @ VU Informatika Amsterdam
+ END-OF-FILE which.1
chmod 'u=rw,g=r,o=r' 'which.1'
set `wc -c 'which.1'`
count=$1
case $count in
2384)	:;;
*)	echo 'Bad character count in ''which.1' >&2
		echo 'Count should be 2384' >&2
esac
echo Extracting 'Makefile'
sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
X# Makefile for /usr/local/bin/which
X# If your operating system does not support multiple groups (see getgroups(2)
X# on BSD-derivatives), comment out the next line.
XMULTIPLE_GROUPS=-DMULTIPLE_GROUPS
X
Xwhich:		which6.c
X		cc -O $(MULTIPLE_GROUPS) -o which which6.c
X
Xinstall:	which doc
X		/bin/mv -f which /usr/local/bin
X		/bin/mv -f which.man /usr/local/man/cat1/which.1
X		/bin/cp which.1 /usr/local/man/man1
X
Xdoc:
X		nroff -man which.1 > which.man
+ END-OF-FILE Makefile
chmod 'u=rw,g=r,o=r' 'Makefile'
set `wc -c 'Makefile'`
count=$1
case $count in
443)	:;;
*)	echo 'Bad character count in ''Makefile' >&2
		echo 'Count should be 443' >&2
esac
exit 0
--
"Salman Rushdie received a copy just as his latest novel was being published.
He ignored it and received myriads of death threats. He quickly decided to
send out twenty copies (some to the Ayatollah) and is still alive."
			(John Banagan <jvbanagan at ucdavis.edu> in sci.skeptic)



More information about the Alt.sources mailing list