xref: /illumos-gate/usr/src/cmd/which/which.c (revision ddb365bfc9e868ad24ccdcb0dc91af18b10df082)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2000 Dan Papasian.  All rights reserved.
5  * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/fcntl.h>
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <err.h>
34 #include <limits.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #define	EXIT_USAGE	2
42 
43 static void	usage(void) __NORETURN;
44 static bool	print_matches(char *, const char *const);
45 
46 static bool	silent = false;
47 static bool	allpaths = false;
48 
49 int
50 main(int argc, char **argv)
51 {
52 	char *p, *path;
53 	size_t pathlen;
54 	int opt, status;
55 
56 	status = EXIT_SUCCESS;
57 
58 	while ((opt = getopt(argc, argv, "as")) != -1) {
59 		switch (opt) {
60 		case 'a':
61 			allpaths = true;
62 			break;
63 		case 's':
64 			silent = true;
65 			break;
66 		default:
67 			usage();
68 			break;
69 		}
70 	}
71 
72 	argv += optind;
73 	argc -= optind;
74 
75 	if (argc == 0)
76 		exit(EXIT_SUCCESS);
77 
78 	if ((p = getenv("PATH")) == NULL)
79 		errx(EXIT_FAILURE, "Could not find PATH in environment");
80 
81 	pathlen = strlen(p);
82 	path = strdup(p);
83 
84 	if (path == NULL)
85 		err(EXIT_FAILURE, "Failed to duplicate PATH");
86 
87 	while (argc > 0) {
88 		memcpy(path, p, pathlen + 1);
89 
90 		if (strlen(*argv) >= FILENAME_MAX) {
91 			status = EXIT_FAILURE;
92 
93 			warnx("operand too long '%s'", *argv);
94 		} else if (!print_matches(path, *argv)) {
95 			status = EXIT_FAILURE;
96 
97 			if (!silent) {
98 				(void) printf("no %s in", *argv);
99 
100 				if (pathlen > 0) {
101 					char *q = path;
102 					const char *d;
103 
104 					memcpy(q, p, pathlen + 1);
105 
106 					while ((d = strsep(&q, ":")) != NULL) {
107 						(void) printf(" %s",
108 						    *d == '\0' ? "." : d);
109 					}
110 				}
111 
112 				(void) printf("\n");
113 			}
114 		}
115 
116 		argv++;
117 		argc--;
118 	}
119 
120 	free(path);
121 	exit(status);
122 }
123 
124 static void
125 usage(void)
126 {
127 	(void) fprintf(stderr, "usage: which [-as] program ...\n");
128 	exit(EXIT_USAGE);
129 }
130 
131 static bool
132 is_there(const char *const candidate)
133 {
134 	struct stat fin;
135 
136 	if (faccessat(AT_FDCWD, candidate, X_OK, AT_EACCESS) == 0 &&
137 	    stat(candidate, &fin) == 0 &&
138 	    S_ISREG(fin.st_mode)) {
139 		if (!silent)
140 			printf("%s\n", candidate);
141 
142 		return (true);
143 	}
144 
145 	return (false);
146 }
147 
148 static bool
149 print_matches(char *path, const char *const filename)
150 {
151 	char candidate[PATH_MAX];
152 	const char *d;
153 	bool found = false;
154 
155 	if (strchr(filename, '/') != NULL)
156 		return (is_there(filename));
157 
158 	while ((d = strsep(&path, ":")) != NULL) {
159 		if (*d == '\0')
160 			d = ".";
161 		if (snprintf(candidate, sizeof (candidate), "%s/%s", d,
162 		    filename) >= (int)sizeof (candidate))
163 			continue;
164 		if (is_there(candidate)) {
165 			found = true;
166 			if (!allpaths)
167 				break;
168 		}
169 	}
170 
171 	return (found);
172 }
173