home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / mail / listserv / utils / which.sh.Z / which.sh
Encoding:
Text File  |  1993-04-28  |  11.2 KB  |  471 lines

  1. >From: maart@nat.vu.nl (Maarten Litmaath)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i012:  which6 - fast which(1) version 6, Part01/01
  4. Message-ID: <1991Feb22.021009.15138@sparky.IMD.Sterling.COM>
  5. Date: 22 Feb 91 02:10:09 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: 94c3a9b1 c13eb623 1350a826 8e7dad30
  8.  
  9. Submitted-by: Maarten Litmaath <maart@nat.vu.nl>
  10. Posting-number: Volume 17, Issue 12
  11. Archive-name: which6/part01
  12.  
  13. I've included the latest version of the `fast which' program.  Though the
  14. changes are small, I decided to resubmit the source in its entirety, because
  15. a patch, which is always a hassle, would not have been substantially smaller.
  16.  
  17. This sixth version supersedes the following issues, which can be deleted
  18. from the archives (or replaced with pointers to `which6'):
  19.  
  20.     v04i010, v05i016, v10i064, and v11i096
  21.  
  22. Maarten Litmaath <maart@nat.vu.nl>
  23.  
  24. : This is a shar archive.  Extract with sh, not csh.
  25. : This archive ends with exit, so do not worry about trailing junk.
  26. : --------------------------- cut here --------------------------
  27. PATH=/bin:/usr/bin:/usr/ucb
  28. echo Extracting 'which6.c'
  29. sed 's/^X//' > 'which6.c' << '+ END-OF-FILE ''which6.c'
  30. X/*
  31. X * which [-i] [-a] [--] [<command>]
  32. X * alias which alias !\$ \| /usr/local/bin/which -i !\*
  33. X * alias which 'eval alias \$$# | /usr/local/bin/which -i ${1+"$@"}'
  34. X * which()
  35. X * {
  36. X *    eval last=\"\$$#\"
  37. X *    set | sed -n "/^$last(){$/,/^}$/p" |
  38. X *        /usr/local/bin/which -i ${1+"$@"}
  39. X * }
  40. X *
  41. X * Author: Maarten Litmaath @ VU University Amsterdam (maart@cs.vu.nl)
  42. X *
  43. X * First change:
  44. X *    Emile LeBlanc (leblanc%math.Berkeley.EDU@ucbvax.berkeley.edu) notes
  45. X *    the access() system call considering everything executable for
  46. X *    root (!), so we give root a special treatment.  :-(
  47. X *    `which', `which -i' and `which -a' with no further arguments now
  48. X *    return the PATH environment variable, split up into its components.
  49. X *    The aliases defined above are slightly different from the previous
  50. X *    version - now it's the shell who's doing the alias checking.
  51. X * Second change:
  52. X *    Added support for shell functions and multiline aliases, added the
  53. X *    `--' option, changed the source style.
  54. X * Third change:
  55. X *    To hell with access()!
  56. X *    Now stat() is used to give the right answer even if the effective
  57. X *    uid (gid) differs from the real uid (gid).
  58. X *    We can't use setuid(geteuid()), because that's nonportable.  :-(
  59. X * Fourth change:
  60. X *    Jim Meyering <meyering@cs.utexas.edu> notes convert() will clobber
  61. X *    the stack if the PATH is longer than BUF_SIZE - 1 characters.
  62. X *    I've changed convert() altogether to return a path vector (cf. argv),
  63. X *    whose components are the respective directories in the PATH.
  64. X *    Furthermore in printing the PATH there are no trailing colons anymore.
  65. X * Fifth change:
  66. X *    I've added support for multiple groups (see getgroups(2) on BSD-
  67. X *    derivatives).
  68. X *    Thanks to John M. Sellens <jmsellens@watdragon.uwaterloo.ca> (and
  69. X *    Andy at the same site).
  70. X *    Furthermore, if a matching executable is found in an unreadable
  71. X *    directory, beside the warning on stderr the name is now printed on
  72. X *    stdout as well.
  73. X */
  74. X
  75. X#ifdef    MULTIPLE_GROUPS
  76. X#include    <sys/param.h>
  77. X#else
  78. X#include    <sys/types.h>
  79. X#endif    /* MULTIPLE_GROUPS */
  80. X#include    <sys/stat.h>
  81. X#include    <stdio.h>
  82. X
  83. X#define        BUF_SIZE    512
  84. X#define        M_USR        0700
  85. X#define        M_GRP        0070
  86. X#define        M_OTH        0007
  87. X#define        X_ALL        0111
  88. X#define        R_ALL        0444
  89. X
  90. Xchar    Version[] =
  91. X    "@(#)which 6.0 91/02/02 Maarten Litmaath @ VU Informatika Amsterdam";
  92. Xchar    *Prog;
  93. X
  94. X
  95. Xvoid    usage()
  96. X{
  97. X    fprintf(stderr, "Usage: %s [-i] [-a] [--] [<command>]\n", Prog);
  98. X    exit(1);
  99. X}
  100. X
  101. X
  102. Xmain(argc, argv) 
  103. Xint    argc;
  104. Xregister char    **argv;
  105. X{
  106. X    register char    *path, *s, **pathv, **p;
  107. X    char    *strcpy(), *getenv(), *fgets(), buf[BUF_SIZE], **convert();
  108. X    int    all = 0, inter = 0, stop = 0, found = 0, uid, gid, mask,
  109. X        xmask, rmask;
  110. X    struct    stat    st;
  111. X    void    usage();
  112. X
  113. X
  114. X    Prog = *argv++;
  115. X    --argc;
  116. X
  117. X    while (!stop && (s = *argv) && (*s == '-')) {
  118. X        ++argv;
  119. X        --argc;
  120. X        ++s;
  121. X        while (*s)
  122. X            switch (*s++) {
  123. X            case 'a':
  124. X                all = 1;
  125. X                break;
  126. X            case 'i':
  127. X                inter = 1;
  128. X                break;
  129. X            case '-':
  130. X                stop = 1;
  131. X                break;
  132. X            default:
  133. X                usage();
  134. X            }
  135. X    }
  136. X
  137. X    if (argc > 1)
  138. X        usage();
  139. X
  140. X    if (inter && *argv) {
  141. X        while (fgets(buf, sizeof buf, stdin)) {
  142. X            if (!found) {
  143. X                printf("%s", *argv);
  144. X                found = 1;
  145. X            }
  146. X            printf("\t%s", buf);
  147. X        }
  148. X        if (found && !all)
  149. X            exit(0);
  150. X    }
  151. X
  152. X    if (!(path = getenv("PATH"))) {
  153. X        fprintf(stderr, "%s: no PATH in environment!\n", Prog);
  154. X        exit(1);
  155. X    }
  156. X
  157. X    if (!*path)
  158. X        path = ".";        /* another dubious convention */
  159. X
  160. X    pathv = convert(path);        /* convert path string to vector */
  161. X
  162. X    if (!*argv) {            /* print path if no more arguments */
  163. X        while (*pathv)
  164. X            puts(*pathv++);
  165. X        exit(0);
  166. X    }
  167. X
  168. X    uid = geteuid();
  169. X    gid = getegid();
  170. X    if (uid == 0) {
  171. X        xmask = X_ALL;
  172. X        rmask = R_ALL;
  173. X    }
  174. X
  175. X    for (p = pathv; path = *p++; ) {    /* try every component */
  176. X        s = buf;
  177. X        while (*s++ = *path++)
  178. X            ;
  179. X        (void) strcpy(s, *argv);
  180. X        *--s = '/';
  181. X
  182. X        if (stat(buf, &st) != 0 || (st.st_mode & S_IFMT) != S_IFREG)
  183. X            continue;
  184. X
  185. X        /* file exists and is regular */
  186. X
  187. X        if (uid != 0) {
  188. X            mask = st.st_uid == uid ? M_USR :
  189. X                st.st_gid == gid ? M_GRP :
  190. X#ifdef    MULTIPLE_GROUPS
  191. X                groups_member(st.st_gid) ? M_GRP :
  192. X#endif    /* MULTIPLE_GROUPS */
  193. X                M_OTH;
  194. X            xmask = X_ALL & mask;
  195. X            rmask = R_ALL & mask;
  196. X        }
  197. X
  198. X        if (!(st.st_mode & xmask))
  199. X            continue;
  200. X
  201. X        /* file is executable */
  202. X
  203. X        *s = 0;
  204. X        if (stat(buf, &st) != 0) {
  205. X            perror(buf);
  206. X            continue;
  207. X        }
  208. X
  209. X        /* warn user if the directory is unreadable */
  210. X
  211. X        if (!(st.st_mode & rmask))
  212. X            fprintf(stderr,
  213. X                "%s: %s found in unreadable directory `%s'!\n",
  214. X                Prog, *argv, buf);
  215. X
  216. X        *s = '/';
  217. X        puts(buf);
  218. X        if (!all)
  219. X            exit(0);
  220. X        found = 1;
  221. X    }
  222. X
  223. X    if (found)
  224. X        exit(0);
  225. X
  226. X    fprintf(stderr, "%s not found in:\n", *argv);
  227. X    while (*pathv)
  228. X        fprintf(stderr, "%s\n", *pathv++);
  229. X    exit(1);
  230. X    /* NOTREACHED */
  231. X}
  232. X
  233. X
  234. Xchar    **convert(path)
  235. Xchar    *path;
  236. X{
  237. X    register char    *s, c;
  238. X    register int    pathc;        /* the length of the path vector */
  239. X    char    **v, **pathv, *malloc();
  240. X
  241. X    for (s = path, pathc = 2; c = *s++; )
  242. X        if (c == ':')
  243. X            ++pathc;
  244. X
  245. X    if (!(pathv = (char **) malloc(pathc * sizeof(char *)))) {
  246. X        perror("malloc");
  247. X        exit(1);
  248. X    }
  249. X
  250. X    for (s = path, v = pathv; (c = *s) != '\0'; ) {
  251. X        if (c == ':') {
  252. X            /*
  253. X             * This colon is spurious.  According to some
  254. X             * dubious convention it is made equivalent to a dot.
  255. X             */
  256. X            *v++ = ".";
  257. X            if (*++s == '\0')
  258. X                *v++ = ".";
  259. X                /*
  260. X                 * The PATH ended in a spurious colon.
  261. X                 * To be consistent we add another dot
  262. X                 * to the path vector.  One day you'll
  263. X                 * be glad we did.
  264. X                 */
  265. X        } else {
  266. X            *v++ = s;
  267. X            while ((c = *++s) != '\0')
  268. X                if (c == ':') {
  269. X                    *s++ = '\0';
  270. X                    if (*s == '\0')
  271. X                        *v++ = ".";
  272. X                        /*
  273. X                         * The PATH ended in a
  274. X                         * (spurious) colon, so
  275. X                         * add dot to the vector.
  276. X                         */
  277. X                    break;
  278. X                }
  279. X        }
  280. X    }
  281. X
  282. X    *v = 0;        /* Signal the end of the path vector. */
  283. X
  284. X    return pathv;
  285. X}
  286. X
  287. X
  288. X#ifdef    MULTIPLE_GROUPS
  289. Xint    groups_member(gid)
  290. Xint    gid;
  291. X{
  292. X    register
  293. X    int    *p, ngroups;
  294. X    int    groups[NGROUPS];
  295. X
  296. X    if ((ngroups = getgroups(NGROUPS, groups)) < 0)
  297. X        return 0;
  298. X    p = groups;
  299. X        while (--ngroups >= 0)
  300. X        if (*p++ == gid)
  301. X            return 1;
  302. X    return 0;
  303. X}
  304. X#endif    /* MULTIPLE_GROUPS */
  305. + END-OF-FILE which6.c
  306. chmod 'u=rw,g=r,o=r' 'which6.c'
  307. set `wc -c 'which6.c'`
  308. count=$1
  309. case $count in
  310. 5970)    :;;
  311. *)    echo 'Bad character count in ''which6.c' >&2
  312.         echo 'Count should be 5970' >&2
  313. esac
  314. echo Extracting 'which.1'
  315. sed 's/^X//' > 'which.1' << '+ END-OF-FILE ''which.1'
  316. X.TH WHICH 1 Apr\ 04\ 1990
  317. X.SH NAME
  318. Xwhich \- give alias, function or path expansion of command
  319. X.SH SYNOPSIS
  320. X.B which
  321. X[
  322. X.B \-i
  323. X] [
  324. X.B \-a
  325. X] [
  326. X.B \-\-
  327. X] [
  328. X.I command
  329. X]
  330. X.SH DESCRIPTION
  331. X.I Which
  332. Xprovides the user with the full expansion of the
  333. X.I command
  334. Xargument, be it either an \fIalias\fR, a \fIshell function\fR
  335. Xor an executable file (default). To enable search for
  336. X.I aliases
  337. Xand \fIshell functions\fR
  338. Xthe user should supply the `\fI\-i\fR'
  339. X(= interactive) flag. In that case
  340. X.I which
  341. Xexpects as standard input the expansion of the \fIalias\fR
  342. Xor \fIshell function\fR.
  343. XIf the standard input is empty or the `\fI\-i\fR' flag has not been
  344. Xgiven, \fIwhich\fR will try to locate \fIcommand\fR
  345. Xin the user's \fBPATH\fR.
  346. XThe interactive mode is easily used by setting an
  347. X.I alias
  348. Xlike the following:
  349. X.ft B
  350. X.nf
  351. X
  352. X    alias    which    alias !\\$ \\| /usr/local/bin/which \-i !\\*
  353. X
  354. X.fi
  355. X.ft R
  356. Xin \fIcsh\fR, or
  357. X.ft B
  358. X.nf
  359. X
  360. X    alias    which    eval alias '\\"\\$$#\\" |' \\
  361. X            /usr/local/bin/which \-i '${1+"$@"}'
  362. X
  363. X.fi
  364. X.ft R
  365. Xin shells which are supersets of
  366. X.I sh
  367. Xand which know \fIaliases\fR. If your shell has \fIshell functions\fR, you
  368. Xcan use the following function:
  369. X.ft B
  370. X.nf
  371. X
  372. X    which()
  373. X    {
  374. X        eval last=\\"\\$$#\\"
  375. X        set | sed \-n "/^$last(){$/,/^}$/p" |
  376. X            /usr/local/bin/which \-i ${1+"$@"}
  377. X    }
  378. X
  379. X.fi
  380. X.ft R
  381. XIf the `\fI\-a\fR' (= all) flag is given,
  382. X.I which
  383. Xwill not stop after the first `match', but search for all occurrences of
  384. X.I command
  385. Xin the user's
  386. X.B PATH.
  387. XThe `\fI\-\-\fR'
  388. Xflag can be used to end the list of options: the next argument (if present)
  389. Xwill be taken as \fIcommand\fR, even if it starts with a `\-'.
  390. X\fIWhich [\-i] [\-a] [\-\-]\fR
  391. Xwithout further arguments prints the user's
  392. X.B PATH
  393. Xbroken up into its components,
  394. Xone per line.
  395. X.PP
  396. XThis new version of the
  397. X.I which
  398. Xcommand is not a
  399. X.I csh
  400. Xscript.
  401. XBeing an executable it is much faster, and not sourcing 
  402. X.I .cshrc
  403. Xit gives a true picture of one's
  404. X.I aliases.
  405. XFurthermore it will give the correct answers even if:
  406. X.IP \-
  407. Xthe \fIeffective uid\fR
  408. X(\fIgid\fR) differs from the \fIreal\fR uid (gid)
  409. X.IP \-
  410. Xthe effective uid is 0 (\fIroot\fR).
  411. X.SH EXAMPLE
  412. X.ft B
  413. X.nf
  414. X% alias
  415. Xwhich    alias !$ | /usr/local/bin/which \-i !*
  416. X% which which
  417. Xwhich    alias !$ | /usr/local/bin/which \-i !*
  418. X% which \-a which
  419. Xwhich    alias !$ | /usr/local/bin/which \-i !*
  420. X/usr/local/bin/which
  421. X/usr/ucb/which
  422. X%
  423. X.fi
  424. X.ft R
  425. X.SH AUTHOR
  426. XMaarten Litmaath @ VU Informatika Amsterdam
  427. + END-OF-FILE which.1
  428. chmod 'u=rw,g=r,o=r' 'which.1'
  429. set `wc -c 'which.1'`
  430. count=$1
  431. case $count in
  432. 2384)    :;;
  433. *)    echo 'Bad character count in ''which.1' >&2
  434.         echo 'Count should be 2384' >&2
  435. esac
  436. echo Extracting 'Makefile'
  437. sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
  438. X# Makefile for /usr/local/bin/which
  439. X# If your operating system does not support multiple groups (see getgroups(2)
  440. X# on BSD-derivatives), comment out the next line.
  441. XMULTIPLE_GROUPS=-DMULTIPLE_GROUPS
  442. X
  443. Xwhich:        which6.c
  444. X        cc -O $(MULTIPLE_GROUPS) -o which which6.c
  445. X
  446. Xinstall:    which doc
  447. X        /bin/mv -f which /usr/local/bin
  448. X        /bin/mv -f which.man /usr/local/man/cat1/which.1
  449. X        /bin/cp which.1 /usr/local/man/man1
  450. X
  451. Xdoc:
  452. X        nroff -man which.1 > which.man
  453. + END-OF-FILE Makefile
  454. chmod 'u=rw,g=r,o=r' 'Makefile'
  455. set `wc -c 'Makefile'`
  456. count=$1
  457. case $count in
  458. 443)    :;;
  459. *)    echo 'Bad character count in ''Makefile' >&2
  460.         echo 'Count should be 443' >&2
  461. esac
  462. exit 0
  463.  
  464. exit 0 # Just in case...
  465. -- 
  466. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  467. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  468. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  469. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  470.  
  471.