master xplshn/aruu / cmd / pseudo / which.c
  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}