diff --git a/Makefile.am b/Makefile.am index 841bf10..9f58ddd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ pkginclude_HEADERS = lite.h queue.h conio.h tree.h strlite.h strdupa.h strndupa libite_la_SOURCES = chomp.c copyfile.c dir.c fexist.c fisdir.c fmode.c fparseln.c \ fsendfile.c ifconfig.c lfile.c makepath.c pidfile.c pidfilefn.c \ progress.c reallocarray.c rsync.c strlcpy.c strlcat.c strtonum.c \ - tempfile.c tree.c + tempfile.c tree.c which.c libite_la_CPPFLAGS = -D_GNU_SOURCE libite_la_CFLAGS = -W -Wall -Wextra diff --git a/README.md b/README.md index d86d822..d836594 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,11 @@ Generic Functions optional listing of file and directory permissions if `show_perms` is set. The `path` argument should be a directory. +- `which(cmd)` + + C implementation of UNIX which(1). Returns a malloc'ed string with the + full path to `cmd` on success, otherwise `NULL`. + GNU Functions ------------- diff --git a/lite.h b/lite.h index 2f6fa06..1fd8bf8 100644 --- a/lite.h +++ b/lite.h @@ -85,6 +85,8 @@ int tree(char *path, int show_perms); void progress (int percent, int max_width); void progress_simple(int percent); +char *which (const char *cmd); + #ifndef touch #include /* utimensat() */ #include /* utimensat() on *BSD */ diff --git a/tests/.gitignore b/tests/.gitignore index 505c643..050dd23 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,3 +14,4 @@ rsync tempfile touch tree +which diff --git a/tests/Makefile.am b/tests/Makefile.am index 4e6d6f3..44ed5f4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,6 +16,7 @@ TESTS += progress TESTS += rsync TESTS += tempfile TESTS += tree +TESTS += which check_PROGRAMS = $(TESTS) diff --git a/tests/which.c b/tests/which.c new file mode 100644 index 0000000..d055ad6 --- /dev/null +++ b/tests/which.c @@ -0,0 +1,47 @@ +#include "check.h" + +struct tc { + char *cmd; + int exp; + int res; +}; + +int main(void) +{ + int result = 0; + size_t i; + struct tc test[] = { + { "ls", 1 }, + { "free", 1 }, + { "modinfo", 1 }, + { "useradd", 1 }, + { "/etc/passwd", 0 }, + { NULL, 0 } + }; + + for (i = 0; test[i].cmd; i++) { + char *path; + + path = which(test[i].cmd); + if (!path) { + test[i].res = 0; + fprintf(stderr, "%20s : Not hot dog\n", test[i].cmd); + } else { + test[i].res = 1; + fprintf(stderr, "%20s : hot dog --> %s\n", test[i].cmd, path); + free(path); + } + } + + for (i = 0; test[i].cmd; i++) + result += test[i].exp != test[i].res; + + return result; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/which.c b/which.c new file mode 100644 index 0000000..6b69f96 --- /dev/null +++ b/which.c @@ -0,0 +1,89 @@ +/* C implementation of UNIX which(1) + * + * Copyright (c) 2017 Joachim Nilsson + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include /* snprintf() */ +#include /* realloc(), free() */ +#include /* strtok_r() */ +#include /* access() */ + +/* + * Like which(1), returns a malloc'ed path to cmd on success, or NULL + */ +char *which(const char *cmd) +{ + size_t pathlen = 0; + char *ptr, *tok, *env, *path = NULL; + + if (!cmd) { + errno = EINVAL; + return NULL; + } + + if (cmd[0] == '/') { + path = strdup(cmd); + if (!access(path, X_OK)) + return path; + + if (path) + free(path); + + return NULL; + } + + ptr = getenv("PATH"); + if (!ptr) + return NULL; + + env = strdup(ptr); + if (!env) + return NULL; + + tok = strtok_r(env, ":", &ptr); + while (tok) { + size_t len = strlen(tok) + strlen(cmd) + 2; + + if (pathlen < len) { + path = realloc(path, len); + if (!path) { + free(env); + return NULL; + } + pathlen = len; + } + + snprintf(path, pathlen, "%s/%s", tok, cmd); + if (!access(path, X_OK)) { + free(env); + return path; + } + + tok = strtok_r(NULL, ":", &ptr); + } + + if (path) + free(path); + free(env); + + return NULL; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */