summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bftw.c20
-rw-r--r--eval.c26
-rwxr-xr-xtests.sh18
3 files changed, 60 insertions, 4 deletions
diff --git a/bftw.c b/bftw.c
index 8a55f13..e64a93c 100644
--- a/bftw.c
+++ b/bftw.c
@@ -600,14 +600,27 @@ static void bftw_set_error(struct bftw_state *state, int error) {
}
/**
+ * Figure out the name offset in a path.
+ */
+static size_t basename_offset(const char *path) {
+ size_t i;
+
+ // Strip trailing slashes
+ for (i = strlen(path); i > 0 && path[i - 1] == '/'; --i);
+
+ // Find the beginning of the name
+ for (; i > 0 && path[i - 1] != '/'; --i);
+
+ return i;
+}
+
+/**
* Initialize the buffers with data about the current path.
*/
static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de) {
struct BFTW *ftwbuf = &state->ftwbuf;
ftwbuf->path = state->path.str;
- ftwbuf->nameoff = 0;
ftwbuf->error = 0;
- ftwbuf->depth = 0;
ftwbuf->visit = (state->status == BFTW_GC ? BFTW_POST : BFTW_PRE);
ftwbuf->statbuf = NULL;
ftwbuf->at_fd = AT_FDCWD;
@@ -624,6 +637,9 @@ static void bftw_init_buffers(struct bftw_state *state, const struct dirent *de)
}
dircache_entry_base(&state->cache, current, &ftwbuf->at_fd, &ftwbuf->at_path);
+ } else {
+ ftwbuf->nameoff = basename_offset(ftwbuf->path);
+ ftwbuf->depth = 0;
}
if (de) {
diff --git a/eval.c b/eval.c
index 5c78d0b..cd9b2e5 100644
--- a/eval.c
+++ b/eval.c
@@ -16,6 +16,7 @@
#include <fcntl.h>
#include <fnmatch.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
@@ -311,7 +312,30 @@ bool eval_links(const struct expr *expr, struct eval_state *state) {
*/
bool eval_name(const struct expr *expr, struct eval_state *state) {
struct BFTW *ftwbuf = state->ftwbuf;
- return fnmatch(expr->sdata, ftwbuf->path + ftwbuf->nameoff, 0) == 0;
+
+ const char *name = ftwbuf->path + ftwbuf->nameoff;
+ char *copy = NULL;
+ if (ftwbuf->depth == 0) {
+ // Any trailing slashes are not part of the name. This can only
+ // happen for the root path.
+ const char *slash = strchr(name, '/');
+ if (slash == name) {
+ // The name of "/" (or "//", etc.) is "/"
+ name = "/";
+ } else if (slash) {
+ copy = strdup(name);
+ if (!copy) {
+ eval_error(state);
+ return false;
+ }
+ copy[slash - name] = '\0';
+ name = copy;
+ }
+ }
+
+ bool ret = fnmatch(expr->sdata, name, 0) == 0;
+ free(copy);
+ return ret;
}
/**
diff --git a/tests.sh b/tests.sh
index 046fddd..97d274b 100755
--- a/tests.sh
+++ b/tests.sh
@@ -199,7 +199,23 @@ function test_0032() {
find_diff -L "$links" -xtype f 2>/dev/null
}
-for i in {1..32}; do
+function test_0033() {
+ find_diff "$basic/a" -name 'a'
+}
+
+function test_0034() {
+ find_diff "$basic/g/" -name 'g'
+}
+
+function test_0035() {
+ find_diff "/" -maxdepth 0 -name '/' 2>/dev/null
+}
+
+function test_0036() {
+ find_diff "//" -maxdepth 0 -name '/' 2>/dev/null
+}
+
+for i in {1..36}; do
test="test_$(printf '%04d' $i)"
"$test" "$dir"
status=$?