1156608Sdeischen/*-
2156608Sdeischen * SPDX-License-Identifier: BSD-3-Clause
3156608Sdeischen *
4156608Sdeischen * Copyright (c) 2000 Dan Papasian.  All rights reserved.
5156608Sdeischen *
6156608Sdeischen * Redistribution and use in source and binary forms, with or without
7156608Sdeischen * modification, are permitted provided that the following conditions
8156608Sdeischen * are met:
9156608Sdeischen * 1. Redistributions of source code must retain the above copyright
10156608Sdeischen *    notice, this list of conditions and the following disclaimer.
11156608Sdeischen * 2. Redistributions in binary form must reproduce the above copyright
12156608Sdeischen *    notice, this list of conditions and the following disclaimer in the
13156608Sdeischen *    documentation and/or other materials provided with the distribution.
14156608Sdeischen * 3. The name of the author may not be used to endorse or promote products
15156608Sdeischen *    derived from this software without specific prior written permission.
16156608Sdeischen *
17156608Sdeischen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18156608Sdeischen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19156608Sdeischen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20156608Sdeischen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21156608Sdeischen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22156608Sdeischen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23156608Sdeischen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24156608Sdeischen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25156608Sdeischen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26156608Sdeischen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27156608Sdeischen */
28156608Sdeischen
29156608Sdeischen#include <sys/cdefs.h>
30156608Sdeischen
31156608Sdeischen__FBSDID("$FreeBSD: stable/11/usr.bin/which/which.c 330449 2018-03-05 07:26:05Z eadler $");
32156608Sdeischen
33156608Sdeischen#include <sys/param.h>
34156608Sdeischen#include <sys/stat.h>
35156608Sdeischen#include <err.h>
36156608Sdeischen#include <stdio.h>
37156608Sdeischen#include <stdlib.h>
38156608Sdeischen#include <string.h>
39156608Sdeischen#include <unistd.h>
40156608Sdeischen
41156608Sdeischenstatic void	 usage(void);
42156608Sdeischenstatic int	 print_matches(char *, char *);
43156608Sdeischen
44156608Sdeischenstatic int 	 silent;
45156608Sdeischenstatic int 	 allpaths;
46156608Sdeischen
47156608Sdeischenint
48156608Sdeischenmain(int argc, char **argv)
49156608Sdeischen{
50156608Sdeischen	char *p, *path;
51156608Sdeischen	ssize_t pathlen;
52156608Sdeischen	int opt, status;
53156608Sdeischen
54156608Sdeischen	status = EXIT_SUCCESS;
55156608Sdeischen
56156608Sdeischen	while ((opt = getopt(argc, argv, "as")) != -1) {
57156608Sdeischen		switch (opt) {
58156608Sdeischen		case 'a':
59156608Sdeischen			allpaths = 1;
60156608Sdeischen			break;
61156608Sdeischen		case 's':
62156608Sdeischen			silent = 1;
63156608Sdeischen			break;
64156608Sdeischen		default:
65156608Sdeischen			usage();
66156608Sdeischen			break;
67156608Sdeischen		}
68156608Sdeischen	}
69156608Sdeischen
70156608Sdeischen	argv += optind;
71156608Sdeischen	argc -= optind;
72156608Sdeischen
73156608Sdeischen	if (argc == 0)
74156608Sdeischen		usage();
75156608Sdeischen
76156608Sdeischen	if ((p = getenv("PATH")) == NULL)
77156608Sdeischen		exit(EXIT_FAILURE);
78156608Sdeischen	pathlen = strlen(p) + 1;
79156608Sdeischen	path = malloc(pathlen);
80156608Sdeischen	if (path == NULL)
81156608Sdeischen		err(EXIT_FAILURE, NULL);
82156608Sdeischen
83156608Sdeischen	while (argc > 0) {
84156608Sdeischen		memcpy(path, p, pathlen);
85156608Sdeischen
86156608Sdeischen		if (strlen(*argv) >= FILENAME_MAX ||
87156608Sdeischen		    print_matches(path, *argv) == -1)
88156608Sdeischen			status = EXIT_FAILURE;
89156608Sdeischen
90156608Sdeischen		argv++;
91156608Sdeischen		argc--;
92156608Sdeischen	}
93156608Sdeischen
94156608Sdeischen	exit(status);
95156608Sdeischen}
96156608Sdeischen
97156608Sdeischenstatic void
98156608Sdeischenusage(void)
99156608Sdeischen{
100156608Sdeischen
101156608Sdeischen	(void)fprintf(stderr, "usage: which [-as] program ...\n");
102156608Sdeischen	exit(EXIT_FAILURE);
103156608Sdeischen}
104156608Sdeischen
105156608Sdeischenstatic int
106156608Sdeischenis_there(char *candidate)
107156608Sdeischen{
108156608Sdeischen	struct stat fin;
109156608Sdeischen
110156608Sdeischen	/* XXX work around access(2) false positives for superuser */
111156608Sdeischen	if (access(candidate, X_OK) == 0 &&
112156608Sdeischen	    stat(candidate, &fin) == 0 &&
113156608Sdeischen	    S_ISREG(fin.st_mode) &&
114156608Sdeischen	    (getuid() != 0 ||
115156608Sdeischen	    (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
116156608Sdeischen		if (!silent)
117156608Sdeischen			printf("%s\n", candidate);
118156608Sdeischen		return (1);
119156608Sdeischen	}
120156608Sdeischen	return (0);
121156608Sdeischen}
122156608Sdeischen
123156608Sdeischenstatic int
124156608Sdeischenprint_matches(char *path, char *filename)
125156608Sdeischen{
126156608Sdeischen	char candidate[PATH_MAX];
127156608Sdeischen	const char *d;
128156608Sdeischen	int found;
129156608Sdeischen
130156608Sdeischen	if (strchr(filename, '/') != NULL)
131156608Sdeischen		return (is_there(filename) ? 0 : -1);
132156608Sdeischen	found = 0;
133156608Sdeischen	while ((d = strsep(&path, ":")) != NULL) {
134156608Sdeischen		if (*d == '\0')
135156608Sdeischen			d = ".";
136156608Sdeischen		if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
137156608Sdeischen		    filename) >= (int)sizeof(candidate))
138156608Sdeischen			continue;
139156608Sdeischen		if (is_there(candidate)) {
140156608Sdeischen			found = 1;
141156608Sdeischen			if (!allpaths)
142156608Sdeischen				break;
143156608Sdeischen		}
144156608Sdeischen	}
145156608Sdeischen	return (found ? 0 : -1);
146156608Sdeischen}
147156608Sdeischen
148156608Sdeischen