Path: utzoo!utgpu!cs.utexas.edu!wuarchive!zaphod.mps.ohio-state.edu!maverick.ksu.ksu.edu!unmvax!ariel.unm.edu!nmsu!opus!mleisher From: mleisher@nmsu.edu (Mark Leisher) Newsgroups: alt.sources.d Subject: Multiple executables in path (Was: NON-SOURCE POSTINGS CONSIDERED HARMFUL!) Message-ID: Date: 18 Jan 91 23:58:38 GMT References: <1991Jan17.224403.16050@convex.com> Sender: news@NMSU.Edu Followup-To: alt.sources.d Organization: Computing Research Lab Lines: 436 In-reply-to: tchrist@convex.COM's message of 17 Jan 91 22:44:03 GMT 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@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] [--] [] 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 * Fourth change: X * Jim Meyering 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 #include #include 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] [--] []\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@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]