which.c (2218B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include "../lib/getopt_nolong.h"
- #include "../lib/strchrnul.h"
- #include <errno.h>
- #include <limits.h> // PATH_MAX
- #include <stdbool.h>
- #include <stdio.h> // fprintf
- #include <stdlib.h> // getenv
- #include <string.h> // strcpy, memcpy
- #include <unistd.h> // access, getopt
- const char *argv0 = "which";
- int
- main(int argc, char *argv[])
- {
- bool opt_a = false, opt_s = false;
- int missing = 0;
- char *path = getenv("PATH");
- if(path == NULL)
- {
- fputs("which: error: $PATH environment unset", stderr);
- return 1;
- }
- for(int c = -1; (c = getopt_nolong(argc, argv, "as")) != -1;)
- {
- switch(c)
- {
- case 'a':
- opt_a = true;
- break;
- case 's':
- opt_s = true;
- break;
- case '?':
- GETOPT_UNKNOWN_OPT
- return 1;
- default:
- abort();
- }
- }
- argc -= optind;
- argv += optind;
- if(argc <= 0) return 1;
- for(int i = 0; i < argc; i++)
- {
- char *cmd = argv[i];
- size_t cmdlen = strlen(cmd);
- bool found = false;
- char *start = path;
- const char *prev = start;
- while(true)
- {
- static char buf[PATH_MAX] = "";
- const char *stop = utils_strchrnul(prev, ':');
- size_t buflen = stop - prev;
- memcpy(buf, prev, buflen);
- if((PATH_MAX - buflen - 1) < cmdlen)
- {
- buf[buflen] = '\0';
- fprintf(stderr,
- "which: warning: Concatenation of PATH element '%s' and command '%s' would be "
- "greater than PATH_MAX\n",
- buf,
- cmd);
- goto which_cont;
- }
- buf[buflen++] = '/';
- memcpy(buf + buflen, cmd, cmdlen);
- buflen += cmdlen;
- buf[buflen] = '\0';
- errno = 0;
- if(access(buf, X_OK) == 0)
- {
- if(!opt_s) puts(buf);
- found = true;
- if(!opt_a) break;
- }
- switch(errno)
- {
- case ENOENT:
- case 0:
- break;
- default:
- fprintf(stderr,
- "which: warning: Failed checking access on file '%s': %s\n",
- buf,
- strerror(errno));
- break;
- }
- which_cont:
- if(*stop == '\0') break;
- prev = stop + 1;
- }
- if(!found) missing++;
- }
- return missing;
- }