From a54c9309c3291a960fcbcbc9e6407330a9edd044 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 13 Feb 2016 13:13:51 -0500 Subject: Implement -xtype. --- bfs.h | 1 + eval.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ parse.c | 15 ++++++++++----- tests.sh | 18 +++++++++++++++++- 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 @@ -370,6 +370,54 @@ bool eval_type(const struct expr *expr, struct eval_state *state) { return state->ftwbuf->typeflag == expr->idata; } +/** + * -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. */ 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=$? -- cgit v1.2.3