diff options
-rw-r--r-- | bfs.h | 1 | ||||
-rw-r--r-- | eval.c | 48 | ||||
-rw-r--r-- | parse.c | 15 | ||||
-rwxr-xr-x | tests.sh | 18 |
4 files changed, 76 insertions, 6 deletions
@@ -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); @@ -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) { @@ -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); @@ -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=$? |