summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2016-02-10 22:15:41 -0500
committerTavian Barnes <tavianator@tavianator.com>2016-02-10 22:15:41 -0500
commitaeff24ee344e5b9c4f2f9040a02320952e70ccd7 (patch)
tree4336db598aab697f683fd62b0696d37a3e9be6e7
parentfc3b5fab4cb4f9a20671e17e31126f360b0e941a (diff)
downloadbfs-aeff24ee344e5b9c4f2f9040a02320952e70ccd7.tar.xz
Implement -samefile.
-rw-r--r--bfs.h7
-rw-r--r--eval.c12
-rw-r--r--parse.c48
-rwxr-xr-xtests.sh7
4 files changed, 66 insertions, 8 deletions
diff --git a/bfs.h b/bfs.h
index 24aeed8..c20fa17 100644
--- a/bfs.h
+++ b/bfs.h
@@ -15,6 +15,7 @@
#include "color.h"
#include <stdbool.h>
#include <stddef.h>
+#include <sys/types.h>
#include <time.h>
/**
@@ -117,6 +118,11 @@ struct expr {
/** The optional time unit. */
enum timeunit timeunit;
+ /** Optional device number for a target file. */
+ dev_t dev;
+ /** Optional inode number for a target file. */
+ ino_t ino;
+
/** Optional integer data for this expression. */
int idata;
@@ -155,6 +161,7 @@ bool eval_empty(const struct expr *expr, struct eval_state *state);
bool eval_hidden(const struct expr *expr, struct eval_state *state);
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_name(const struct expr *expr, struct eval_state *state);
diff --git a/eval.c b/eval.c
index 543a98a..01da7a4 100644
--- a/eval.c
+++ b/eval.c
@@ -342,6 +342,18 @@ bool eval_quit(const struct expr *expr, struct eval_state *state) {
}
/**
+ * -samefile test.
+ */
+bool eval_samefile(const struct expr *expr, struct eval_state *state) {
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ return false;
+ }
+
+ return statbuf->st_dev == expr->dev && statbuf->st_ino == expr->ino;
+}
+
+/**
* -type test.
*/
bool eval_type(const struct expr *expr, struct eval_state *state) {
diff --git a/parse.c b/parse.c
index 4613aed..3498901 100644
--- a/parse.c
+++ b/parse.c
@@ -149,6 +149,21 @@ struct parser_state {
};
/**
+ * Invoke stat() on an argument.
+ */
+static int stat_arg(const struct parser_state *state, struct expr *expr, struct stat *sb) {
+ bool follow = state->cl->flags & BFTW_FOLLOW;
+ int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
+
+ int ret = fstatat(AT_FDCWD, expr->sdata, sb, flags);
+ if (ret != 0) {
+ print_error(NULL, expr->sdata, errno);
+ free_expr(expr);
+ }
+ return ret;
+}
+
+/**
* Parse the expression specified on the command line.
*/
static struct expr *parse_expr(struct parser_state *state);
@@ -344,13 +359,7 @@ static struct expr *parse_acnewer(struct parser_state *state, const char *option
}
struct stat sb;
-
- bool follow = state->cl->flags & BFTW_FOLLOW;
- int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;
-
- if (fstatat(AT_FDCWD, expr->sdata, &sb, flags) != 0) {
- print_error(NULL, expr->sdata, errno);
- free_expr(expr);
+ if (stat_arg(state, expr, &sb) != 0) {
return NULL;
}
@@ -409,6 +418,26 @@ static struct expr *parse_depth(struct parser_state *state, const char *option,
}
/**
+ * Parse -samefile FILE.
+ */
+static struct expr *parse_samefile(struct parser_state *state, const char *option) {
+ struct expr *expr = parse_test_sdata(state, option, eval_samefile);
+ if (!expr) {
+ return NULL;
+ }
+
+ struct stat sb;
+ if (stat_arg(state, expr, &sb) != 0) {
+ return NULL;
+ }
+
+ expr->dev = sb.st_dev;
+ expr->ino = sb.st_ino;
+
+ return expr;
+}
+
+/**
* Parse -type [bcdpfls].
*/
static struct expr *parse_type(struct parser_state *state) {
@@ -619,6 +648,11 @@ static struct expr *parse_literal(struct parser_state *state) {
}
break;
+ case 's':
+ if (strcmp(arg, "-samefile") == 0) {
+ return parse_samefile(state, arg);
+ }
+
case 't':
if (strcmp(arg, "-true") == 0) {
return &expr_true;
diff --git a/tests.sh b/tests.sh
index 8c8c347..f869acc 100755
--- a/tests.sh
+++ b/tests.sh
@@ -189,7 +189,12 @@ function test_0027() {
find_diff -L "$1" -depth 2>/dev/null
}
-for i in {1..27}; do
+function test_0028() {
+ links_structure "$1"
+ find_diff "$1" -samefile "$1/a"
+}
+
+for i in {1..28}; do
dir="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX)"
test="test_$(printf '%04d' $i)"
"$test" "$dir"