which.c (1648B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/stat.h> 3 #include <sys/types.h> 4 5 #include <fcntl.h> 6 #include <limits.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <unistd.h> 11 12 #include "util.h" 13 14 static int aflag; 15 16 static int 17 canexec(int fd, const char *name) 18 { 19 struct stat st; 20 21 if (fstatat(fd, name, &st, 0) < 0 || !S_ISREG(st.st_mode)) 22 return 0; 23 return faccessat(fd, name, X_OK, AT_EACCESS) == 0; 24 } 25 26 static int 27 which(const char *path, const char *name) 28 { 29 char *ptr, *p; 30 size_t i, len; 31 int dirfd, found = 0; 32 33 if (strchr(name, '/')) { 34 found = canexec(AT_FDCWD, name); 35 if (found) 36 puts(name); 37 return found; 38 } 39 40 ptr = p = enstrdup(3, path); 41 len = strlen(p); 42 for (i = 0; i < len + 1; i++) { 43 if (ptr[i] != ':' && ptr[i] != '\0') 44 continue; 45 ptr[i] = '\0'; 46 if ((dirfd = open(p, O_RDONLY)) >= 0) { 47 if (canexec(dirfd, name)) { 48 found = 1; 49 fputs(p, stdout); 50 if (i && ptr[i - 1] != '/') 51 fputc('/', stdout); 52 puts(name); 53 } 54 close(dirfd); 55 if (!aflag && found) 56 break; 57 } 58 p = ptr + i + 1; 59 } 60 free(ptr); 61 62 return found; 63 } 64 65 static void 66 usage(void) 67 { 68 eprintf("usage: %s [-a] name ...\n", argv0); 69 } 70 71 int 72 main(int argc, char *argv[]) 73 { 74 char *path; 75 int found = 0, foundall = 1; 76 77 ARGBEGIN { 78 case 'a': 79 aflag = 1; 80 break; 81 default: 82 usage(); 83 } ARGEND 84 85 if (!argc) 86 usage(); 87 88 if (!(path = getenv("PATH"))) 89 enprintf(3, "$PATH is not set\n"); 90 91 for (; *argv; argc--, argv++) { 92 if (which(path, *argv)) { 93 found = 1; 94 } else { 95 weprintf("%s: not an external command\n", *argv); 96 foundall = 0; 97 } 98 } 99 100 return found ? foundall ? 0 : 1 : 2; 101 }