diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2016-02-10 22:15:41 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2016-02-10 22:15:41 -0500 |
commit | aeff24ee344e5b9c4f2f9040a02320952e70ccd7 (patch) | |
tree | 4336db598aab697f683fd62b0696d37a3e9be6e7 | |
parent | fc3b5fab4cb4f9a20671e17e31126f360b0e941a (diff) | |
download | bfs-aeff24ee344e5b9c4f2f9040a02320952e70ccd7.tar.xz |
Implement -samefile.
-rw-r--r-- | bfs.h | 7 | ||||
-rw-r--r-- | eval.c | 12 | ||||
-rw-r--r-- | parse.c | 48 | ||||
-rwxr-xr-x | tests.sh | 7 |
4 files changed, 66 insertions, 8 deletions
@@ -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); @@ -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) { @@ -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; @@ -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" |