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