which.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
which.c (1648B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/stat.h>
3 #include <sys/types.h>
4
5 #include <fcntl.h>
6 #include <limits.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11
12 #include "util.h"
13
14 static int aflag;
15
16 static int
17 canexec(int fd, const char *name)
18 {
19 struct stat st;
20
21 if (fstatat(fd, name, &st, 0) < 0 || !S_ISREG(st.st_mode))
22 return 0;
23 return faccessat(fd, name, X_OK, AT_EACCESS) == 0;
24 }
25
26 static int
27 which(const char *path, const char *name)
28 {
29 char *ptr, *p;
30 size_t i, len;
31 int dirfd, found = 0;
32
33 if (strchr(name, '/')) {
34 found = canexec(AT_FDCWD, name);
35 if (found)
36 puts(name);
37 return found;
38 }
39
40 ptr = p = enstrdup(3, path);
41 len = strlen(p);
42 for (i = 0; i < len + 1; i++) {
43 if (ptr[i] != ':' && ptr[i] != '\0')
44 continue;
45 ptr[i] = '\0';
46 if ((dirfd = open(p, O_RDONLY)) >= 0) {
47 if (canexec(dirfd, name)) {
48 found = 1;
49 fputs(p, stdout);
50 if (i && ptr[i - 1] != '/')
51 fputc('/', stdout);
52 puts(name);
53 }
54 close(dirfd);
55 if (!aflag && found)
56 break;
57 }
58 p = ptr + i + 1;
59 }
60 free(ptr);
61
62 return found;
63 }
64
65 static void
66 usage(void)
67 {
68 eprintf("usage: %s [-a] name ...\n", argv0);
69 }
70
71 int
72 main(int argc, char *argv[])
73 {
74 char *path;
75 int found = 0, foundall = 1;
76
77 ARGBEGIN {
78 case 'a':
79 aflag = 1;
80 break;
81 default:
82 usage();
83 } ARGEND
84
85 if (!argc)
86 usage();
87
88 if (!(path = getenv("PATH")))
89 enprintf(3, "$PATH is not set\n");
90
91 for (; *argv; argc--, argv++) {
92 if (which(path, *argv)) {
93 found = 1;
94 } else {
95 weprintf("%s: not an external command\n", *argv);
96 foundall = 0;
97 }
98 }
99
100 return found ? foundall ? 0 : 1 : 2;
101 }