v17i012: which6 - fast which(1) version 6, Part01/01
Maarten Litmaath
maart at nat.vu.nl
Fri Feb 22 13:10:09 AEST 1991
Submitted-by: Maarten Litmaath <maart at nat.vu.nl>
Posting-number: Volume 17, Issue 12
Archive-name: which6/part01
I've included the latest version of the `fast which' program. Though the
changes are small, I decided to resubmit the source in its entirety, because
a patch, which is always a hassle, would not have been substantially smaller.
This sixth version supersedes the following issues, which can be deleted
from the archives (or replaced with pointers to `which6'):
v04i010, v05i016, v10i064, and v11i096
Maarten Litmaath <maart at nat.vu.nl>
: 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
exit 0 # Just in case...
--
Kent Landfield INTERNET: kent at sparky.IMD.Sterling.COM
Sterling Software, IMD UUCP: uunet!sparky!kent
Phone: (402) 291-8300 FAX: (402) 291-4362
Please send comp.sources.misc-related mail to kent at uunet.uu.net.
More information about the Comp.sources.misc
mailing list