Multiple executables in path (Was: NON-SOURCE POSTINGS CONSIDERED HARMFUL!)

Mark Leisher mleisher at nmsu.edu
Sat Jan 19 10:58:38 AEST 1991


Tom's Perl script reminded me of a program I have that hasn't been
posted for a while.

Here's a program called which5 by Maarten Litmaath that is
particularly fast at finding executables that occur more than once in
a path.
---- Cut Here and feed the following to sh ----
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 01/18/1991 23:55 UTC by mleisher at nmsu.edu
# Source directory /thrinakia1/mleisher/c_stuff
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    169 -rw-rw-r-- which5/Makefile
#   2384 -rw-rw-r-- which5/which.1
#   5142 -rw-rw-r-- which5/which5.c
#
# ============= which5/Makefile ==============
if test ! -d 'which5'; then
    echo 'x - creating directory which5'
    mkdir 'which5'
fi
if test -f 'which5/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping which5/Makefile (File already exists)'
else
echo 'x - extracting which5/Makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'which5/Makefile' &&
# Makefile for /usr/local/bin/which
X
which:		which5.c
X		cc -O which5.c -o which
X
install:	which
X		/bin/mv -f which /usr/local/bin
X
doc:
X		nroff -man which.1 > which.man
SHAR_EOF
chmod 0664 which5/Makefile ||
echo 'restore of which5/Makefile failed'
Wc_c="`wc -c < 'which5/Makefile'`"
test 169 -eq "$Wc_c" ||
	echo 'which5/Makefile: original size 169, current size' "$Wc_c"
fi
# ============= which5/which.1 ==============
if test -f 'which5/which.1' -a X"$1" != X"-c"; then
	echo 'x - skipping which5/which.1 (File already exists)'
else
echo 'x - extracting which5/which.1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'which5/which.1' &&
.TH WHICH 1 Apr\ 04\ 1990
.SH NAME
which \- give alias, function or path expansion of command
.SH SYNOPSIS
.B which
[
.B \-i
] [
.B \-a
] [
.B \-\-
] [
.I command
]
.SH DESCRIPTION
.I Which
provides the user with the full expansion of the
.I command
argument, be it either an \fIalias\fR, a \fIshell function\fR
or an executable file (default). To enable search for
.I aliases
and \fIshell functions\fR
the user should supply the `\fI\-i\fR'
(= interactive) flag. In that case
.I which
expects as standard input the expansion of the \fIalias\fR
or \fIshell function\fR.
If the standard input is empty or the `\fI\-i\fR' flag has not been
given, \fIwhich\fR will try to locate \fIcommand\fR
in the user's \fBPATH\fR.
The interactive mode is easily used by setting an
.I alias
like the following:
.ft B
.nf
X
X	alias	which	alias !\\$ \\| /usr/local/bin/which \-i !\\*
X
.fi
.ft R
in \fIcsh\fR, or
.ft B
.nf
X
X	alias	which	eval alias '\\"\\$$#\\" |' \\
X			/usr/local/bin/which \-i '${1+"$@"}'
X
.fi
.ft R
in shells which are supersets of
.I sh
and which know \fIaliases\fR. If your shell has \fIshell functions\fR, you
can use the following function:
.ft B
.nf
X
X	which()
X	{
X		eval last=\\"\\$$#\\"
X		set | sed \-n "/^$last(){$/,/^}$/p" |
X			/usr/local/bin/which \-i ${1+"$@"}
X	}
X
.fi
.ft R
If the `\fI\-a\fR' (= all) flag is given,
.I which
will not stop after the first `match', but search for all occurrences of
.I command
in the user's
.B PATH.
The `\fI\-\-\fR'
flag can be used to end the list of options: the next argument (if present)
will be taken as \fIcommand\fR, even if it starts with a `\-'.
\fIWhich [\-i] [\-a] [\-\-]\fR
without further arguments prints the user's
.B PATH
broken up into its components,
one per line.
.PP
This new version of the
.I which
command is not a
.I csh
script.
Being an executable it is much faster, and not sourcing 
.I .cshrc
it gives a true picture of one's
.I aliases.
Furthermore it will give the correct answers even if:
.IP \-
the \fIeffective uid\fR
(\fIgid\fR) differs from the \fIreal\fR uid (gid)
.IP \-
the effective uid is 0 (\fIroot\fR).
.SH EXAMPLE
.ft B
.nf
% alias
which	alias !$ | /usr/local/bin/which \-i !*
% which which
which	alias !$ | /usr/local/bin/which \-i !*
% which \-a which
which	alias !$ | /usr/local/bin/which \-i !*
/usr/local/bin/which
/usr/ucb/which
%
.fi
.ft R
.SH AUTHOR
Maarten Litmaath @ VU Informatika Amsterdam
SHAR_EOF
chmod 0664 which5/which.1 ||
echo 'restore of which5/which.1 failed'
Wc_c="`wc -c < 'which5/which.1'`"
test 2384 -eq "$Wc_c" ||
	echo 'which5/which.1: original size 2384, current size' "$Wc_c"
fi
# ============= which5/which5.c ==============
if test -f 'which5/which5.c' -a X"$1" != X"-c"; then
	echo 'x - skipping which5/which5.c (File already exists)'
else
echo 'x - extracting which5/which5.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'which5/which5.c' &&
/*
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 * 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 */
X
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<stdio.h>
X
#define		BUF_SIZE	512
#define		M_USR		0700
#define		M_GRP		0070
#define		M_OTH		0007
#define		X_ALL		0111
#define		R_ALL		0444
X
char	Version[] =
X	"@(#)which 5.0 90/03/24 Maarten Litmaath @ VU Informatika Amsterdam";
char	*Prog;
X
X
void	usage()
{
X	fprintf(stderr, "Usage: %s [-i] [-a] [--] [<command>]\n", Prog);
X	exit(1);
}
X
X
main(argc, argv) 
int	argc;
register char	**argv;
{
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 : 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		if (!(st.st_mode & rmask)) {
X			fprintf(stderr,
X				"%s: %s found in unreadable directory %s!\n",
X				Prog, *argv, buf);
X			found = 1;
X			continue;
X		}
X
X		/* directory is readable */
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
X
char	**convert(path)
char	*path;
{
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;
}
SHAR_EOF
chmod 0664 which5/which5.c ||
echo 'restore of which5/which5.c failed'
Wc_c="`wc -c < 'which5/which5.c'`"
test 5142 -eq "$Wc_c" ||
	echo 'which5/which5.c: original size 5142, current size' "$Wc_c"
fi
exit 0
-----------------------------------------------------------------------------
mleisher at nmsu.edu                      "I laughed.
Mark Leisher                                I cried.
Computing Research Lab                          I fell down.
New Mexico State University                        It changed my life."
Las Cruces, NM                     - Rich [Cowboy Feng's Space Bar and Grille]



More information about the Alt.sources.d mailing list