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