165226Sgreen/**
265226Sgreen * Copyright (c) 2000 Dan Papasian.  All rights reserved.
365226Sgreen *
465226Sgreen * Redistribution and use in source and binary forms, with or without
565226Sgreen * modification, are permitted provided that the following conditions
665226Sgreen * are met:
765226Sgreen * 1. Redistributions of source code must retain the above copyright
865226Sgreen *    notice, this list of conditions and the following disclaimer.
965226Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1065226Sgreen *    notice, this list of conditions and the following disclaimer in the
1165226Sgreen *    documentation and/or other materials provided with the distribution.
1265226Sgreen * 3. The name of the author may not be used to endorse or promote products
1365226Sgreen *    derived from this software without specific prior written permission.
1465226Sgreen *
1565226Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1665226Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1765226Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1865226Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1965226Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2065226Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2165226Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2265226Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2365226Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2465226Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2565226Sgreen */
2665226Sgreen
2787678Smarkm#include <sys/cdefs.h>
2887678Smarkm
2987678Smarkm__FBSDID("$FreeBSD: stable/10/usr.bin/which/which.c 320722 2017-07-06 05:59:27Z ngie $");
3087678Smarkm
31320722Sngie#include <sys/param.h>
3265226Sgreen#include <sys/stat.h>
3365226Sgreen#include <err.h>
3465226Sgreen#include <stdio.h>
3565226Sgreen#include <stdlib.h>
3665226Sgreen#include <string.h>
3765226Sgreen#include <unistd.h>
3865226Sgreen
3965226Sgreenstatic void	 usage(void);
4065226Sgreenstatic int	 print_matches(char *, char *);
41318609Sngie
42227245Sedstatic int 	 silent;
43227245Sedstatic int 	 allpaths;
4465226Sgreen
4565226Sgreenint
4665226Sgreenmain(int argc, char **argv)
4765226Sgreen{
4865226Sgreen	char *p, *path;
4965226Sgreen	ssize_t pathlen;
5065226Sgreen	int opt, status;
5165226Sgreen
5265226Sgreen	status = EXIT_SUCCESS;
5365226Sgreen
5465226Sgreen	while ((opt = getopt(argc, argv, "as")) != -1) {
5565226Sgreen		switch (opt) {
5665226Sgreen		case 'a':
5765226Sgreen			allpaths = 1;
5865226Sgreen			break;
5965226Sgreen		case 's':
6065226Sgreen			silent = 1;
6165226Sgreen			break;
6265226Sgreen		default:
6365226Sgreen			usage();
6465226Sgreen			break;
6565226Sgreen		}
6665226Sgreen	}
6765226Sgreen
6865226Sgreen	argv += optind;
6965226Sgreen	argc -= optind;
7065226Sgreen
71141656Sru	if (argc == 0)
72141656Sru		usage();
73141656Sru
7465226Sgreen	if ((p = getenv("PATH")) == NULL)
7565226Sgreen		exit(EXIT_FAILURE);
7665226Sgreen	pathlen = strlen(p) + 1;
7765226Sgreen	path = malloc(pathlen);
7865226Sgreen	if (path == NULL)
7965226Sgreen		err(EXIT_FAILURE, NULL);
8065226Sgreen
8165226Sgreen	while (argc > 0) {
8265226Sgreen		memcpy(path, p, pathlen);
83318609Sngie
8491945Swosch		if (strlen(*argv) >= FILENAME_MAX ||
8565226Sgreen		    print_matches(path, *argv) == -1)
8665226Sgreen			status = EXIT_FAILURE;
8765226Sgreen
8865226Sgreen		argv++;
8965226Sgreen		argc--;
9065226Sgreen	}
9165226Sgreen
9265226Sgreen	exit(status);
9365226Sgreen}
9465226Sgreen
9565226Sgreenstatic void
9665226Sgreenusage(void)
9765226Sgreen{
9865226Sgreen
99141656Sru	(void)fprintf(stderr, "usage: which [-as] program ...\n");
100141656Sru	exit(EXIT_FAILURE);
10165226Sgreen}
10265226Sgreen
10365226Sgreenstatic int
10465226Sgreenis_there(char *candidate)
10565226Sgreen{
10665226Sgreen	struct stat fin;
10765226Sgreen
10865226Sgreen	/* XXX work around access(2) false positives for superuser */
10965226Sgreen	if (access(candidate, X_OK) == 0 &&
11065226Sgreen	    stat(candidate, &fin) == 0 &&
11165226Sgreen	    S_ISREG(fin.st_mode) &&
11265226Sgreen	    (getuid() != 0 ||
11365226Sgreen	    (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) {
11465226Sgreen		if (!silent)
11565226Sgreen			printf("%s\n", candidate);
11665226Sgreen		return (1);
11765226Sgreen	}
11865226Sgreen	return (0);
11965226Sgreen}
12065226Sgreen
12165226Sgreenstatic int
12265226Sgreenprint_matches(char *path, char *filename)
12365226Sgreen{
12465226Sgreen	char candidate[PATH_MAX];
12599118Stjr	const char *d;
12665226Sgreen	int found;
12765226Sgreen
12899120Stjr	if (strchr(filename, '/') != NULL)
12965226Sgreen		return (is_there(filename) ? 0 : -1);
13065226Sgreen	found = 0;
13165226Sgreen	while ((d = strsep(&path, ":")) != NULL) {
13299118Stjr		if (*d == '\0')
13399118Stjr			d = ".";
13465226Sgreen		if (snprintf(candidate, sizeof(candidate), "%s/%s", d,
13587678Smarkm		    filename) >= (int)sizeof(candidate))
13665226Sgreen			continue;
13765226Sgreen		if (is_there(candidate)) {
13865226Sgreen			found = 1;
13965226Sgreen			if (!allpaths)
14065226Sgreen				break;
14165226Sgreen		}
14265226Sgreen	}
14365226Sgreen	return (found ? 0 : -1);
14465226Sgreen}
14565226Sgreen
146