summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bfs.h1
-rw-r--r--eval.c48
-rw-r--r--parse.c15
-rwxr-xr-xtests.sh18
4 files changed, 76 insertions, 6 deletions
diff --git a/bfs.h b/bfs.h
index f4e2d24..36b12e4 100644
--- a/bfs.h
+++ b/bfs.h
@@ -163,6 +163,7 @@ bool eval_inum(const struct expr *expr, struct eval_state *state);
bool eval_links(const struct expr *expr, struct eval_state *state);
bool eval_samefile(const struct expr *expr, struct eval_state *state);
bool eval_type(const struct expr *expr, struct eval_state *state);
+bool eval_xtype(const struct expr *expr, struct eval_state *state);
bool eval_name(const struct expr *expr, struct eval_state *state);
bool eval_path(const struct expr *expr, struct eval_state *state);
diff --git a/eval.c b/eval.c
index 7c2f104..5c78d0b 100644
--- a/eval.c
+++ b/eval.c
@@ -371,6 +371,54 @@ bool eval_type(const struct expr *expr, struct eval_state *state) {
}
/**
+ * -xtype test.
+ */
+bool eval_xtype(const struct expr *expr, struct eval_state *state) {
+ struct BFTW *ftwbuf = state->ftwbuf;
+
+ bool is_root = ftwbuf->depth == 0;
+ bool follow = state->cmdline->flags & (is_root ? BFTW_FOLLOW_ROOT : BFTW_FOLLOW_NONROOT);
+
+ bool is_link = ftwbuf->typeflag == BFTW_LNK;
+ if (follow == is_link) {
+ return eval_type(expr, state);
+ }
+
+ // -xtype does the opposite of everything else
+ int at_flags = follow ? AT_SYMLINK_NOFOLLOW : 0;
+
+ struct stat sb;
+ if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &sb, at_flags) != 0) {
+ if (!follow && errno == ENOENT) {
+ // Broken symlink
+ return eval_type(expr, state);
+ } else {
+ eval_error(state);
+ return false;
+ }
+ }
+
+ switch (expr->idata) {
+ case BFTW_BLK:
+ return S_ISBLK(sb.st_mode);
+ case BFTW_CHR:
+ return S_ISCHR(sb.st_mode);
+ case BFTW_DIR:
+ return S_ISDIR(sb.st_mode);
+ case BFTW_FIFO:
+ return S_ISFIFO(sb.st_mode);
+ case BFTW_LNK:
+ return S_ISLNK(sb.st_mode);
+ case BFTW_REG:
+ return S_ISREG(sb.st_mode);
+ case BFTW_SOCK:
+ return S_ISSOCK(sb.st_mode);
+ }
+
+ return false;
+}
+
+/**
* Evaluate a negation.
*/
bool eval_not(const struct expr *expr, struct eval_state *state) {
diff --git a/parse.c b/parse.c
index 6af1094..6272ba0 100644
--- a/parse.c
+++ b/parse.c
@@ -438,12 +438,12 @@ static struct expr *parse_samefile(struct parser_state *state, const char *optio
}
/**
- * Parse -type [bcdpfls].
+ * Parse -x?type [bcdpfls].
*/
-static struct expr *parse_type(struct parser_state *state) {
+static struct expr *parse_type(struct parser_state *state, const char *option, eval_fn *eval) {
const char *arg = state->argv[state->i];
if (!arg) {
- fputs("-type needs a value.\n", stderr);
+ fprintf(stderr, "%s needs a value.\n", option);
return NULL;
}
@@ -480,7 +480,7 @@ static struct expr *parse_type(struct parser_state *state) {
++state->i;
- return new_test_idata(state, eval_type, typeflag);
+ return new_test_idata(state, eval, typeflag);
}
/**
@@ -657,7 +657,7 @@ static struct expr *parse_literal(struct parser_state *state) {
if (strcmp(arg, "-true") == 0) {
return &expr_true;
} else if (strcmp(arg, "-type") == 0) {
- return parse_type(state);
+ return parse_type(state, arg, eval_type);
}
break;
@@ -677,6 +677,11 @@ static struct expr *parse_literal(struct parser_state *state) {
return new_test_idata(state, eval_access, W_OK);
}
break;
+
+ case 'x':
+ if (strcmp(arg, "-xtype") == 0) {
+ return parse_type(state, arg, eval_xtype);
+ }
}
fprintf(stderr, "Unknown argument '%s'.\n", arg);
diff --git a/tests.sh b/tests.sh
index 128c63c..046fddd 100755
--- a/tests.sh
+++ b/tests.sh
@@ -183,7 +183,23 @@ function test_0028() {
find_diff "$links" -samefile "$links/a"
}
-for i in {1..28}; do
+function test_0029() {
+ find_diff "$links" -xtype l
+}
+
+function test_0030() {
+ find_diff "$links" -xtype f
+}
+
+function test_0031() {
+ find_diff -L "$links" -xtype l 2>/dev/null
+}
+
+function test_0032() {
+ find_diff -L "$links" -xtype f 2>/dev/null
+}
+
+for i in {1..32}; do
test="test_$(printf '%04d' $i)"
"$test" "$dir"
status=$?