maart@cs.vu.nl (Maarten Litmaath) (02/14/90)
Posting-number: Volume 10, Issue 64 Submitted-by: maart@cs.vu.nl (Maarten Litmaath) Archive-name: which4 Dear moderator, below is the latest version of which2.c, the fast form of the which(1) command. Besides aliases and executables, shell functions are now recognized too. Furthermore the output will be correct even if the effective uid (gid) of the invoker doesn't equal the real uid (gid). An updated manual is included. I suggest the previous 2 versions (v04i010, v05i016) to be deleted from the archives, or to be replaced with pointers to which4. Thanks for your time, Maarten Litmaath @ VU Amsterdam: maart@cs.vu.nl, uunet!mcsun!botter!maart : 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 'which.1' sed 's/^X//' > 'which.1' << '+ END-OF-FILE ''which.1' X.TH WHICH 1 Sep\ 21\ 1989 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 \fIPATH\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.I 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.I 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. 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 2205) :;; *) echo 'Bad character count in ''which.1' >&2 echo 'Count should be 2205' >&2 esac echo Extracting 'Makefile' sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile' X# Makefile for /usr/local/bin/which X Xwhich: which4.c X cc -O which4.c -o which X Xinstall: which X /bin/mv -f which /usr/local/bin 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 169) :;; *) echo 'Bad character count in ''Makefile' >&2 echo 'Count should be 169' >&2 esac echo Extracting 'which4.c' sed 's/^X//' > 'which4.c' << '+ END-OF-FILE ''which4.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@cs.vu.nl) X * first change: X * Emile LeBlanc (leblanc%math.Berkeley.EDU@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 */ X X#include <sys/types.h> 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 4.0 90/01/24 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; X char *save, *strcpy(), *getenv(), *fgets(), buf[BUF_SIZE]; X int all = 0, inter = 0, stop = 0, found = 0, uid, gid, mask, X xmask, rmask; X struct stat st; X void usage(), convert(); 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 (!(save = path = getenv("PATH"))) { X fprintf(stderr, "%s: no PATH in environment!\n", Prog); X exit(1); X } X X if (!*path) X save = path = "."; X X if (!*argv) { X convert(path, buf); X puts(buf); 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 while (*path) { X s = buf; X while ((*s++ = *path) && *path++ != ':') X ; X if (*buf == ':') { X /* X * to deal with the dubious convention that a X * spurious colon is equivalent to a dot... X */ X *buf = '.'; X ++s; 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 convert(save, buf); X fprintf(stderr, "%s not found in\n%s\n", *argv, buf); X exit(1); X} X X Xvoid convert(path, buf) Xregister char *path, *buf; X{ X for (;;) { X while ((*buf++ = *path) && *path++ != ':') X ; X if (!*path) X break; X *buf++ = '\n'; X } X *buf = '\0'; /* to cope with a PATH ending in ':' */ X} + END-OF-FILE which4.c chmod 'u=rw,g=r,o=r' 'which4.c' set `wc -c 'which4.c'` count=$1 case $count in 3907) :;; *) echo 'Bad character count in ''which4.c' >&2 echo 'Count should be 3907' >&2 esac exit 0