which - once a match is found in $PATH, test that it is a regular file

and can be accessed as executable
This commit is contained in:
Nathan Fisher 2023-01-10 22:07:30 -05:00
parent 32b827061c
commit 2dcdba7704
3 changed files with 61 additions and 4 deletions

View File

@ -1,5 +1,7 @@
# Makefile - hhl - /src/world/bin/which
# Makefile - hhl - /src/world/usr.bin/which
# Copyright 2020 Nathan Fisher <nfisher.sr@gmail.com>
#
progname = which
include hhl.script.mk
hhl_source = 1
onestage = true
include hhl.cprog.mk

View File

@ -0,0 +1,57 @@
#include <dirent.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
char *__progname;
void usage() {
printf("Usage: %s [COMMAND]\n", __progname);
exit(1);
}
int main(int argc, char **argv) {
char *path, *seg, *__path;
const char sep[2] = ":";
__progname = basename(argv[0]);
if (argc < 2) {
fprintf(stderr, "%s: Error: missing argument\n", __progname);
usage();
} else if (argc > 2) {
fprintf(stderr, "%s: Error: missing argument\n", __progname);
usage();
}
path = getenv("PATH");
if (path == NULL) {
path = "/usr/bin:/bin";
}
// Save a copy for later use, since strtok will modify the original
__path = strndup(path, strlen(path) + 1);
seg = strtok(path, sep);
while (seg != NULL) {
struct dirent *dp;
DIR *dir = opendir(seg);
if (dir) {
while ((dp = readdir(dir)) != NULL) {
if (strcmp(argv[1], dp->d_name) == 0) {
char *p = malloc(strlen(argv[1] + strlen(seg) + 2));
(void)strncpy(p, seg, strlen(seg) + 1);
(void)strncat(p, "/", 2);
(void)strncat(p, argv[1], strlen(argv[1]) + 1);
struct stat path_stat;
stat(p, &path_stat);
if (access(p, X_OK) == 0 && S_ISREG(path_stat.st_mode)) {
printf("%s\n", p);
exit(0);
}
}
}
}
seg = strtok(NULL, sep);
}
printf("%s: no %s in (%s)\n", __progname, argv[1], __path);
}

View File

@ -1,2 +0,0 @@
#!/bin/zsh
which $@