commit 6ea8b2ac06382a2f30383ef19a4fcbc3197d8463
parent 828cb5129d376c4d5035821c9fc538c8321e32fc
Author: aabacchus <ben@bvnf.space>
Date: Sat, 22 Apr 2023 03:28:31 +0100
add globbing for search
Diffstat:
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);