/* * which [-i] [-a] [--] [] * alias which alias !\$ \| /usr/local/bin/which -i !\* * alias which 'eval alias \$$# | /usr/local/bin/which -i ${1+"$@"}' * which() * { * eval last=\"\$$#\" * set | sed -n "/^$last(){$/,/^}$/p" | * /usr/local/bin/which -i ${1+"$@"} * } * * Author: Maarten Litmaath @ VU University Amsterdam (maart@cs.vu.nl) * First change: * Emile LeBlanc (leblanc%math.Berkeley.EDU@ucbvax.berkeley.edu) notes * the access() system call considering everything executable for * root (!), so we give root a special treatment. :-( * `which', `which -i' and `which -a' with no further arguments now * return the PATH environment variable, split up into its components. * The aliases defined above are slightly different from the previous * version - now it's the shell who's doing the alias checking. * Second change: * Added support for shell functions and multiline aliases, added the * `--' option, changed the source style. * Third change: * To hell with access()! * Now stat() is used to give the right answer even if the effective * uid (gid) differs from the real uid (gid). * We can't use setuid(geteuid()), because that's nonportable. :-( * Fourth change: * Jim Meyering notes convert() will clobber * the stack if the PATH is longer than BUF_SIZE - 1 characters. * I've changed convert() altogether to return a path vector (cf. argv), * whose components are the respective directories in the PATH. * Furthermore in printing the PATH there are no trailing colons anymore. */ #include #include #include #ifdef __GNUC__ /* for all the extern progs... */ #include #include #include #endif #if defined(__GNUC__) && defined(atarist) /* note: tcsh keeps PATH like .:/dev/c/bin:... but when it sets up an env for a prog, it converts it to .,c:\bin,... */ #define DIRSEP ',' #define SLASH '\\' #else #define DIRSEP ':' #define SLASH '/' #endif #define BUF_SIZE 512 #define M_USR 0700 #define M_GRP 0070 #define M_OTH 0007 #define X_ALL 0111 #define R_ALL 0444 char Version[] = "@(#)which 5.0 90/03/24 Maarten Litmaath @ VU Informatika Amsterdam"; char *Prog; #ifdef atarist /* we need to look for files with extensions, too... */ char *extn[] = {"", ".ttp", ".prg", ".tos", ".acc", ".csh", ".sh", ".g", (char *)0}; #endif void usage() { fprintf(stderr, "%s\n\n", Version); fprintf(stderr, "Usage: %s [-i] [-a] [--] []\n", Prog); exit(1); } #ifdef __GNUC__ char **convert(); #endif void main(argc, argv) int argc; register char **argv; { register char *path, *s, **pathv, **p; #ifdef __GNUC__ char buf[BUF_SIZE]; #else char *strcpy(), *getenv(), *fgets(), buf[BUF_SIZE], **convert(); #endif int all = 0, inter = 0, stop = 0, found = 0, uid, gid, mask, xmask, rmask; struct stat st; void usage(); #ifdef atarist /* to deal with extensions... */ int stret; char *pend; char **pextn; #endif Prog = *argv++; --argc; while (!stop && (s = *argv) && (*s == '-')) { ++argv; --argc; ++s; while (*s) switch (*s++) { case 'a': all = 1; break; case 'i': inter = 1; break; case '-': stop = 1; break; default: usage(); } } if (argc > 1) usage(); if (inter && *argv) { while (fgets(buf, sizeof buf, stdin)) { if (!found) { /*!!! printf("%s", *argv);*/ printf("%s:", *argv); found = 1; } /*!!! printf("\t%s", buf);*/ printf("\taliased to %s", buf); } if (found && !all) exit(0); } if (!(path = getenv("PATH"))) { fprintf(stderr, "%s: no PATH in environment!\n", Prog); exit(1); } if (!*path) path = "."; /* another dubious convention */ pathv = convert(path); /* convert path string to vector */ if (!*argv) { /* print path if no more arguments */ while (*pathv) puts(*pathv++); exit(0); } uid = geteuid(); gid = getegid(); if (uid == 0) { xmask = X_ALL; rmask = R_ALL; } for (p = pathv; path = *p++; ) { /* try every component */ s = buf; while (*s++ = *path++) ; (void) strcpy(s, *argv); *--s = SLASH; #ifdef atarist /* * here we deal with extensions. keep a ptr to null at * end of basic string. this assumes user did NOT give * a suffix! then we move along the list until we find * a regular file. if we get thru the loop, and if list * was exhausted, next. otherwise test again if reg file. */ pend = &buf[strlen(buf)]; /* -> null at end */ for (pextn = extn; *pextn; pextn++) { strcpy (pend, *pextn); /*fprintf(stderr,"test %s\n",buf);*/ if (stret = stat(buf, &st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) break; } if (*pextn == NULL) continue; if (stret == 0 && (st.st_mode & S_IFMT) != S_IFREG) continue; #else if (stat(buf, &st) != 0 || (st.st_mode & S_IFMT) != S_IFREG) continue; #endif /* file exists and is regular */ if (uid != 0) { mask = st.st_uid == uid ? M_USR : st.st_gid == gid ? M_GRP : M_OTH; xmask = X_ALL & mask; rmask = R_ALL & mask; } if (!(st.st_mode & xmask)) continue; /* file is executable */ *s = 0; if (stat(buf, &st) != 0) { perror(buf); continue; } if (!(st.st_mode & rmask)) { fprintf(stderr, "%s: %s found in unreadable directory %s!\n", Prog, *argv, buf); found = 1; continue; } /* directory is readable */ *s = SLASH; puts(buf); if (!all) exit(0); found = 1; } if (found) exit(0); fprintf(stderr, "%s not found in:\n", *argv); while (*pathv) fprintf(stderr, "%s\n", *pathv++); exit(1); } char **convert(path) char *path; { register char *s, c; register int pathc; /* the length of the path vector */ #ifdef __GNUC__ char **v, **pathv; #else char **v, **pathv, *malloc(); #endif for (s = path, pathc = 2; c = *s++; ) if (c == DIRSEP) ++pathc; if (!(pathv = (char **) malloc(pathc * sizeof(char *)))) { perror("malloc"); exit(1); } for (s = path, v = pathv; (c = *s) != '\0'; ) { if (c == DIRSEP) { /* * This colon is spurious. According to some * dubious convention it is made equivalent to a dot. */ *v++ = "."; if (*++s == '\0') *v++ = "."; /* * The PATH ended in a spurious colon. * To be consistent we add another dot * to the path vector. One day you'll * be glad we did. */ } else { *v++ = s; while ((c = *++s) != '\0') if (c == DIRSEP) { *s++ = '\0'; if (*s == '\0') *v++ = "."; /* * The PATH ended in a * (spurious) colon, so * add dot to the vector. */ break; } } } *v = 0; /* Signal the end of the path vector. */ return pathv; }