From fcc1a4bb999856574404c629e59fd949ffe6cf3d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 8 Jun 2016 22:22:09 -0400 Subject: Treat '-', ')', and ',' as paths when possible. --- parse.c | 96 +++++++++++++++++++++++++++++++++++++++++----------------------- tests.sh | 43 +++++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 37 deletions(-) diff --git a/parse.c b/parse.c index c65d61f..0caa66e 100644 --- a/parse.c +++ b/parse.c @@ -186,6 +186,8 @@ struct parser_state { bool implicit_print; /** Whether warnings are enabled (see -warn, -nowarn). */ bool warn; + /** Whether any non-path arguments have been encountered. */ + bool non_path_seen; /** Whether any non-option arguments have been encountered. */ bool non_option_seen; /** Whether an information option like -help or -version was passed. */ @@ -195,6 +197,22 @@ struct parser_state { struct timespec now; }; +/** + * Possible token types. + */ +enum token_type { + /** A root path. */ + T_PATH, + /** An option. */ + T_OPTION, + /** A test. */ + T_TEST, + /** An action. */ + T_ACTION, + /** An operator. */ + T_OPERATOR, +}; + /** * Log an optimization. */ @@ -252,26 +270,51 @@ static int stat_arg(const struct parser_state *state, struct expr *expr, struct */ static struct expr *parse_expr(struct parser_state *state); +/** + * Advance by a single token. + */ +static char **parser_advance(struct parser_state *state, enum token_type type, size_t argc) { + if (type != T_PATH) { + state->non_path_seen = true; + + if (type != T_OPTION) { + state->non_option_seen = true; + } + } + + char **argv = state->argv; + state->argv += argc; + return argv; +} + /** * While parsing an expression, skip any paths and add them to the cmdline. */ static const char *skip_paths(struct parser_state *state) { while (true) { const char *arg = state->argv[0]; + + // By POSIX, these are always options if (!arg - || arg[0] == '-' + || (arg[0] == '-' && arg[1]) || strcmp(arg, "(") == 0 - || strcmp(arg, ")") == 0 - || strcmp(arg, "!") == 0 - || strcmp(arg, ",") == 0) { + || strcmp(arg, "!") == 0) { return arg; } + if (state->non_path_seen) { + // By POSIX, these can be paths. We only treat them as + // such at the beginning of the command line + if (strcmp(arg, ")") == 0 || strcmp(arg, ",") == 0) { + return arg; + } + } + if (!cmdline_add_root(state->cmdline, arg)) { return NULL; } - ++state->argv; + parser_advance(state, T_PATH, 1); } } @@ -362,8 +405,7 @@ static const char *parse_icmp(const struct parser_state *state, const char *str, * Parse a single option. */ static struct expr *parse_option(struct parser_state *state, size_t argc) { - const char *arg = state->argv[0]; - state->argv += argc; + const char *arg = *parser_advance(state, T_OPTION, argc); if (state->warn && state->non_option_seen) { pretty_warning(state->cmdline->stderr_colors, @@ -394,7 +436,7 @@ static struct expr *parse_unary_option(struct parser_state *state) { * Parse a single positional option. */ static struct expr *parse_positional_option(struct parser_state *state, size_t argc) { - state->argv += argc; + parser_advance(state, T_OPTION, argc); return &expr_true; } @@ -412,22 +454,12 @@ static struct expr *parse_unary_positional_option(struct parser_state *state) { return parse_positional_option(state, 2); } -/** - * Parse a non-option argument. - */ -static struct expr *parse_non_option(struct parser_state *state, eval_fn *eval, bool pure, size_t argc) { - state->non_option_seen = true; - - char **argv = state->argv; - state->argv += argc; - return new_expr(eval, pure, argc, argv); -} - /** * Parse a single test. */ static struct expr *parse_test(struct parser_state *state, eval_fn *eval, size_t argc) { - return parse_non_option(state, eval, true, argc); + char **argv = parser_advance(state, T_TEST, argc); + return new_expr(eval, true, argc, argv); } /** @@ -464,7 +496,8 @@ static struct expr *parse_action(struct parser_state *state, eval_fn *eval, size state->implicit_print = false; } - return parse_non_option(state, eval, false, argc); + char **argv = parser_advance(state, T_ACTION, argc); + return new_expr(eval, false, argc, argv); } /** @@ -1138,7 +1171,7 @@ static struct expr *parse_literal(struct parser_state *state) { case 'f': if (strcmp(arg, "-false") == 0) { - ++state->argv; + parser_advance(state, T_TEST, 1); return &expr_false; } else if (strcmp(arg, "-follow") == 0) { cmdline->flags |= BFTW_FOLLOW | BFTW_DETECT_CYCLES; @@ -1260,7 +1293,7 @@ static struct expr *parse_literal(struct parser_state *state) { case 't': if (strcmp(arg, "-true") == 0) { - ++state->argv; + parser_advance(state, T_TEST, 1); return &expr_true; } else if (strcmp(arg, "-type") == 0) { return parse_type(state, eval_type); @@ -1381,8 +1414,7 @@ static struct expr *parse_factor(struct parser_state *state) { } if (strcmp(arg, "(") == 0) { - ++state->argv; - state->non_option_seen = true; + parser_advance(state, T_OPERATOR, 1); struct expr *expr = parse_expr(state); if (!expr) { @@ -1395,12 +1427,11 @@ static struct expr *parse_factor(struct parser_state *state) { free_expr(expr); return NULL; } - ++state->argv; + parser_advance(state, T_OPERATOR, 1); return expr; } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { - char **argv = state->argv++; - state->non_option_seen = true; + char **argv = parser_advance(state, T_OPERATOR, 1); struct expr *factor = parse_factor(state); if (!factor) { @@ -1478,8 +1509,7 @@ static struct expr *parse_term(struct parser_state *state) { char **argv = &fake_and_arg; if (strcmp(arg, "-a") == 0 || strcmp(arg, "-and") == 0) { - argv = state->argv++; - state->non_option_seen = true; + argv = parser_advance(state, T_OPERATOR, 1); } struct expr *lhs = term; @@ -1555,8 +1585,7 @@ static struct expr *parse_clause(struct parser_state *state) { break; } - char **argv = state->argv++; - state->non_option_seen = true; + char **argv = parser_advance(state, T_OPERATOR, 1); struct expr *lhs = clause; struct expr *rhs = parse_term(state); @@ -1603,8 +1632,7 @@ static struct expr *parse_expr(struct parser_state *state) { break; } - char **argv = state->argv++; - state->non_option_seen = true; + char **argv = parser_advance(state, T_OPERATOR, 1); struct expr *lhs = expr; struct expr *rhs = parse_clause(state); diff --git a/tests.sh b/tests.sh index 8cf493c..d4ac7fc 100755 --- a/tests.sh +++ b/tests.sh @@ -48,17 +48,41 @@ function make_links() { links="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.links.XXXXXXXXXX)" make_links "$links" +# Creates a file+directory structure with various weird file/directory names +function make_weirdnames() { + touchp "$1/-/a" + touchp "$1/(/b" + touchp "$1/(-/c" + touchp "$1/!/d" + touchp "$1/!-/e" + touchp "$1/,/f" + touchp "$1/)/g" +} + +weirdnames="$(mktemp -d "${TMPDIR:-/tmp}"/bfs.weirdnames.XXXXXXXXXX)" +make_weirdnames "$weirdnames" + # Clean up temporary directories on exit function cleanup() { + rm -rf "$weirdnames" rm -rf "$links" rm -rf "$perms" rm -rf "$basic" } trap cleanup EXIT +function _realpath() { + ( + cd "${1%/*}" + echo "$PWD/${1##*/}" + ) +} + +BFS="$(_realpath ./bfs)" + # Checks for any (order-independent) differences between bfs and find function find_diff() { - diff -u <(./bfs "$@" | sort) <(find "$@" | sort) + diff -u <("$BFS" "$@" | sort) <(find "$@" | sort) } # Test cases @@ -303,9 +327,22 @@ function test_0058() { find_diff "$basic" -execdir echo '-{}-' ';' } -for i in {1..58}; do +function test_0059() { + find_diff "$basic" \( -name '*f*' \) +} + +function test_0060() { + find_diff "$basic" -name '*f*' -print , -print +} + +function test_0061() { + cd "$weirdnames" + find_diff '-' '(-' '!-' ',' ')' './(' './!' \( \! -print , -print \) +} + +for i in {1..61}; do test="test_$(printf '%04d' $i)" - "$test" "$dir" + ("$test" "$dir") status=$? if [ $status -ne 0 ]; then echo "$test failed!" >&2 -- cgit v1.2.3