ckiss

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 6ea8b2ac06382a2f30383ef19a4fcbc3197d8463
parent 828cb5129d376c4d5035821c9fc538c8321e32fc
Author: aabacchus <ben@bvnf.space>
Date:   Sat, 22 Apr 2023 03:28:31 +0100

add globbing for search

Diffstat:
Msrc/kiss.h | 7+++++--
Msrc/search.c | 3++-
Msrc/utils.c | 62+++++++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/src/kiss.h b/src/kiss.h @@ -2,6 +2,7 @@ #include <stdbool.h> #include <unistd.h> #include <stdnoreturn.h> +#include <sys/stat.h> #ifndef noreturn #define noreturn @@ -56,8 +57,10 @@ char **split(char *s, char *sep); /* Goes through the path array looking for a file in each directory with the * given name. Returns the first one. The returned string must be freed by the * caller. If limit is true, only return the first result found in path, - * otherwise return an array of all results found in path. */ -char **find_in_path(char *name, char **path, bool limit); + * otherwise return an array of all results found in path. If isglob is true, + * treat name as a glob. test_flags is OR'd with the file's mode (from stat(3)) + * to check if it matches a criterion (eg. executable, directory). */ +char **find_in_path(char *name, char **path, mode_t test_flags, bool limit, bool isglob); /* Checks for the first cmd which may be found in path. Returns the index of the * cmd (0, 1, 2, ...). Arg list must be terminated with a NULL */ diff --git a/src/search.c b/src/search.c @@ -2,10 +2,11 @@ #include <stdlib.h> #include "kiss.h" +/* TODO: append sys_db to kiss_path */ int search(int argc, char **argv, struct env *e) { for (int i = 1; i < argc; i++) { - char **s = find_in_path(argv[i], e->kiss_path, false); + char **s = find_in_path(argv[i], e->kiss_path, S_IFDIR, false, true); if (s == NULL) die2(argv[i], "not found"); for (int j = 0; s[j] != NULL; j++) { diff --git a/src/utils.c b/src/utils.c @@ -1,8 +1,10 @@ #include <errno.h> +#include <glob.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <time.h> #include "kiss.h" @@ -123,25 +125,59 @@ split(char *s, char *sep) { } char ** -find_in_path(char *name, char **path, bool limit) { +find_in_path(char *name, char **path, mode_t test_flags, bool limit, bool isglob) { char **s = NULL, **tmp; int n = 0; for (int i = 0; path[i] != NULL; i++) { char *file = concat(path[i], "/", name, NULL); - if (access(file, R_OK) == 0) { - tmp = realloc(s, sizeof(char *) * ++n); - if (tmp == NULL) { - free(file); - free(s); - die_perror("realloc"); + char **list = NULL; + if (isglob) { + glob_t pglob; + int r = glob(file, GLOB_ERR, NULL, &pglob); + free(file); + if (r != 0 || pglob.gl_pathc == 0) { + if (r != GLOB_NOMATCH) + die_perror("glob"); + continue; } - s = tmp; - s[n - 1] = file; - if (limit) - return s; + list = malloc(sizeof(char *) * (pglob.gl_pathc + 1)); + if (list == NULL) die_perror("malloc"); + for (size_t j = 0; j < pglob.gl_pathc; j++) { + list[j] = strdup(pglob.gl_pathv[j]); + } + list[pglob.gl_pathc] = NULL; + globfree(&pglob); } else { - free(file); + /* just put the one file into an array like pglob.gl_pathv so that + * the code below can be used for both cases of isglob. */ + list = malloc(sizeof(char *) * 2); + if (list == NULL) die_perror("malloc"); + list[0] = file; + list[1] = NULL; + } + + for (int j = 0; list[j] != NULL; j++) { + struct stat sb; + if (stat(list[j], &sb) == -1 || !(sb.st_mode & test_flags)) { + free(list[j]); + } else { + char *found = list[j]; + tmp = realloc(s, sizeof(char *) * ++n); + if (tmp == NULL) { + free(found); + free(s); + free(list); + die_perror("realloc"); + } + s = tmp; + s[n - 1] = found; + if (limit) { + free(list); + return s; + } + } } + free(list); } if (s != NULL) { /* add terminating NULL */ @@ -162,7 +198,7 @@ available_cmd(char **path, char *cmd, ...) { int n = 0; va_start(ap, cmd); while (cmd != NULL) { - char **s = find_in_path(cmd, path, true); + char **s = find_in_path(cmd, path, S_IXUSR | S_IXGRP | S_IXOTH, true, false); if (s != NULL) { free(*s); free(s);