diff --git a/README b/README index 46b2bf7..1259808 100644 --- a/README +++ b/README @@ -91,7 +91,7 @@ The following tools are implemented: =*|o uudecode . =*|o uuencode . #*|o wc . -=* x which . +=*|x which . =*|o xargs (-p) =*|x yes . diff --git a/which.1 b/which.1 index 05989ae..ed98d26 100644 --- a/which.1 +++ b/which.1 @@ -3,33 +3,42 @@ .Os sbase .Sh NAME .Nm which -.Nd locate a program file (or files) in the path +.Nd locate programs in the path .Sh SYNOPSIS .Nm .Op Fl a -.Op Ar name ... +.Ar name ... .Sh DESCRIPTION .Nm -looks for programs in +looks for each +.Ar name +in the .Ev PATH -. -.Pp -If -.Fl a -is specified it will display all matches and not stop at the first match. +directories, stopping at the first match and printing +the full path to stdout. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl a +Don't stop at the first match and search all +.Ev PATH +directories. +.El .Sh EXIT STATUS -The -.Nm -utility exits with one of the following values: .Bl -tag -width Ds .It 0 -All names were successfully resolved. +Each +.Ar name +was found. .It 1 -Some names were resolved but not all. +At least one +.Ar name +was not found. .It 2 -No names were resolved. +No +.Ar name +was found. +.It 3 +An error occurred. .El -.Sh DIAGNOSTICS -If a program is not found it will print "Command not found" to stderr. .Sh SEE ALSO .Xr environ 7 diff --git a/which.c b/which.c index ed09e07..f3f296b 100644 --- a/which.c +++ b/which.c @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -14,30 +16,34 @@ static int aflag; static int which(const char *path, const char *name) { - char file[PATH_MAX], *p, *s, *ptr; - size_t len; + char *ptr, *p; + size_t i, len; struct stat st; - int found = 0; + int dirfd, found = 0; - p = ptr = estrdup(path); - for (s = p; (s = strsep(&p, ":")); ) { - if (!s[0]) - s = "."; - len = strlen(s); - - if (snprintf(file, sizeof(file), "%s%s%s", - s, - len > 0 && s[len - 1] != '/' ? "/" : "", - name) >= sizeof(file)) - eprintf("path too long\n"); - - if (stat(file, &st) == 0 && S_ISREG(st.st_mode) && - access(file, X_OK) == 0) { - found = 1; - puts(file); - if (!aflag) - break; + ptr = p = enstrdup(3, path); + len = strlen(p); + for (i = 0; i < len + 1; i++) { + if (ptr[i] != ':' && ptr[i] != '\0') + continue; + ptr[i] = '\0'; + if ((dirfd = open(p, O_RDONLY, 0)) >= 0) { + if (!fstatat(dirfd, name, &st, 0) && + S_ISREG(st.st_mode) && + !faccessat(dirfd, name, X_OK, 0)) { + found = 1; + fputs(p, stdout); + if (i && ptr[i - 1] != '/') + fputc('/', stdout); + puts(name); + if (!aflag) { + close(dirfd); + break; + } + } + close(dirfd); } + p = ptr + i + 1; } free(ptr); @@ -47,14 +53,14 @@ which(const char *path, const char *name) static void usage(void) { - eprintf("usage: %s [-a] name...\n", argv0); + eprintf("usage: %s [-a] name ...\n", argv0); } int main(int argc, char *argv[]) { char *path; - int i, found; + int found = 0, foundall = 1; ARGBEGIN { case 'a': @@ -68,13 +74,16 @@ main(int argc, char *argv[]) usage(); if (!(path = getenv("PATH"))) - eprintf("$PATH not set\n"); + enprintf(3, "$PATH is not set\n"); - for (i = 0, found = 0; i < argc; i++) { - if (which(path, argv[i])) - found++; - else - weprintf("%s: Command not found.\n", argv[i]); + for (; *argv; argc--, argv++) { + if (which(path, *argv)) { + found = 1; + } else { + weprintf("%s: command not found.\n", *argv); + foundall = 0; + } } - return !found ? 2 : found == argc ? 0 : 1; + + return found ? foundall ? 0 : 1 : 2; }