diff options
Diffstat (limited to 'src/parse.c')
-rw-r--r-- | src/parse.c | 709 |
1 files changed, 432 insertions, 277 deletions
diff --git a/src/parse.c b/src/parse.c index 539aa05..42f71cc 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,9 +8,10 @@ * flags like always-true options, and skipping over paths wherever they appear. */ -#include "prelude.h" #include "parse.h" + #include "alloc.h" +#include "bfs.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -31,6 +32,7 @@ #include "xregex.h" #include "xspawn.h" #include "xtime.h" + #include <errno.h> #include <fcntl.h> #include <fnmatch.h> @@ -78,8 +80,6 @@ struct bfs_parser { /** Whether stdout is a terminal. */ bool stdout_tty; - /** Whether this session is interactive (stdin and stderr are each a terminal). */ - bool interactive; /** Whether -color or -nocolor has been passed. */ enum use_color use_color; /** Whether a -print action is implied. */ @@ -115,24 +115,6 @@ struct bfs_parser { }; /** - * Possible token types. - */ -enum token_type { - /** A flag. */ - T_FLAG, - /** A root path. */ - T_PATH, - /** An option. */ - T_OPTION, - /** A test. */ - T_TEST, - /** An action. */ - T_ACTION, - /** An operator. */ - T_OPERATOR, -}; - -/** * Print a low-level error message during parsing. */ static void parse_perror(const struct bfs_parser *parser, const char *str) { @@ -158,9 +140,8 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, /** * Print an error message during parsing. */ -attr(printf(2, 3)) +_printf(2, 3) static void parse_error(const struct bfs_parser *parser, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bool highlight[ctx->argc]; @@ -170,7 +151,6 @@ static void parse_error(const struct bfs_parser *parser, const char *format, ... va_list args; va_start(args, format); - errno = error; bfs_verror(parser->ctx, format, args); va_end(args); } @@ -178,9 +158,8 @@ static void parse_error(const struct bfs_parser *parser, const char *format, ... /** * Print an error about some command line arguments. */ -attr(printf(4, 5)) +_printf(4, 5) static void parse_argv_error(const struct bfs_parser *parser, char **argv, size_t argc, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bool highlight[ctx->argc]; @@ -190,7 +169,6 @@ static void parse_argv_error(const struct bfs_parser *parser, char **argv, size_ va_list args; va_start(args, format); - errno = error; bfs_verror(ctx, format, args); va_end(args); } @@ -198,9 +176,8 @@ static void parse_argv_error(const struct bfs_parser *parser, char **argv, size_ /** * Print an error about conflicting command line arguments. */ -attr(printf(6, 7)) +_printf(6, 7) static void parse_conflict_error(const struct bfs_parser *parser, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bool highlight[ctx->argc]; @@ -211,7 +188,6 @@ static void parse_conflict_error(const struct bfs_parser *parser, char **argv1, va_list args; va_start(args, format); - errno = error; bfs_verror(ctx, format, args); va_end(args); } @@ -219,16 +195,14 @@ static void parse_conflict_error(const struct bfs_parser *parser, char **argv1, /** * Print an error about an expression. */ -attr(printf(3, 4)) +_printf(3, 4) static void parse_expr_error(const struct bfs_parser *parser, const struct bfs_expr *expr, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bfs_expr_error(ctx, expr); va_list args; va_start(args, format); - errno = error; bfs_verror(ctx, format, args); va_end(args); } @@ -236,9 +210,8 @@ static void parse_expr_error(const struct bfs_parser *parser, const struct bfs_e /** * Print a warning message during parsing. */ -attr(printf(2, 3)) +_printf(2, 3) static bool parse_warning(const struct bfs_parser *parser, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bool highlight[ctx->argc]; @@ -250,7 +223,6 @@ static bool parse_warning(const struct bfs_parser *parser, const char *format, . va_list args; va_start(args, format); - errno = error; bool ret = bfs_vwarning(parser->ctx, format, args); va_end(args); return ret; @@ -259,9 +231,8 @@ static bool parse_warning(const struct bfs_parser *parser, const char *format, . /** * Print a warning about conflicting command line arguments. */ -attr(printf(6, 7)) +_printf(6, 7) static bool parse_conflict_warning(const struct bfs_parser *parser, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; bool highlight[ctx->argc]; @@ -274,7 +245,6 @@ static bool parse_conflict_warning(const struct bfs_parser *parser, char **argv1 va_list args; va_start(args, format); - errno = error; bool ret = bfs_vwarning(ctx, format, args); va_end(args); return ret; @@ -283,9 +253,8 @@ static bool parse_conflict_warning(const struct bfs_parser *parser, char **argv1 /** * Print a warning about an expression. */ -attr(printf(3, 4)) +_printf(3, 4) static bool parse_expr_warning(const struct bfs_parser *parser, const struct bfs_expr *expr, const char *format, ...) { - int error = errno; const struct bfs_ctx *ctx = parser->ctx; if (!bfs_expr_warning(ctx, expr)) { @@ -294,7 +263,6 @@ static bool parse_expr_warning(const struct bfs_parser *parser, const struct bfs va_list args; va_start(args, format); - errno = error; bool ret = bfs_vwarning(ctx, format, args); va_end(args); return ret; @@ -303,8 +271,8 @@ static bool parse_expr_warning(const struct bfs_parser *parser, const struct bfs /** * Allocate a new expression. */ -static struct bfs_expr *parse_new_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc, char **argv) { - struct bfs_expr *expr = bfs_expr_new(parser->ctx, eval_fn, argc, argv); +static struct bfs_expr *parse_new_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc, char **argv, enum bfs_kind kind) { + struct bfs_expr *expr = bfs_expr_new(parser->ctx, eval_fn, argc, argv, kind); if (!expr) { parse_perror(parser, "bfs_expr_new()"); } @@ -315,7 +283,7 @@ static struct bfs_expr *parse_new_expr(const struct bfs_parser *parser, bfs_eval * Create a new unary expression. */ static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv, BFS_OPERATOR); if (!expr) { return NULL; } @@ -329,7 +297,7 @@ static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval * Create a new binary expression. */ static struct bfs_expr *new_binary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv, BFS_OPERATOR); if (!expr) { return NULL; } @@ -414,12 +382,12 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser); /** * Advance by a single token. */ -static char **parser_advance(struct bfs_parser *parser, enum token_type type, size_t argc) { - if (type != T_FLAG && type != T_PATH) { +static char **parser_advance(struct bfs_parser *parser, enum bfs_kind kind, size_t argc) { + if (kind != BFS_FLAG && kind != BFS_PATH) { parser->expr_started = true; } - if (type != T_PATH) { + if (kind != BFS_PATH) { parser->last_arg = parser->argv; } @@ -465,7 +433,7 @@ static int skip_paths(struct bfs_parser *parser) { // find uses -- to separate flags from the rest // of the command line. We allow mixing flags // and paths/predicates, so we just ignore --. - parser_advance(parser, T_FLAG, 1); + parser_advance(parser, BFS_FLAG, 1); continue; } if (strcmp(arg, "-") != 0) { @@ -497,7 +465,7 @@ static int skip_paths(struct bfs_parser *parser) { return -1; } - parser_advance(parser, T_PATH, 1); + parser_advance(parser, BFS_PATH, 1); } } @@ -517,20 +485,14 @@ enum int_flags { * Parse an integer. */ static const char *parse_int(const struct bfs_parser *parser, char **arg, const char *str, void *result, enum int_flags flags) { - // strtoll() skips leading spaces, but we want to reject them - if (xisspace(str[0])) { - goto bad; - } - int base = flags & IF_BASE_MASK; if (base == 0) { base = 10; } char *endptr; - errno = 0; - long long value = strtoll(str, &endptr, base); - if (errno != 0) { + long long value; + if (xstrtoll(str, &endptr, base, &value) != 0) { if (errno == ERANGE) { goto range; } else { @@ -538,13 +500,6 @@ static const char *parse_int(const struct bfs_parser *parser, char **arg, const } } - // https://github.com/llvm/llvm-project/issues/64946 - sanitize_init(&endptr); - - if (endptr == str) { - goto bad; - } - if (!(flags & IF_PARTIAL_OK) && *endptr != '\0') { goto bad; } @@ -641,8 +596,8 @@ static bool looks_like_icmp(const char *str) { * Parse a single flag. */ static struct bfs_expr *parse_flag(struct bfs_parser *parser, size_t argc) { - char **argv = parser_advance(parser, T_FLAG, argc); - return parse_new_expr(parser, eval_true, argc, argv); + char **argv = parser_advance(parser, BFS_FLAG, argc); + return parse_new_expr(parser, eval_true, argc, argv, BFS_FLAG); } /** @@ -657,9 +612,11 @@ static struct bfs_expr *parse_nullary_flag(struct bfs_parser *parser) { */ static struct bfs_expr *parse_unary_flag(struct bfs_parser *parser) { const char *arg = parser->argv[0]; + char flag = arg[strlen(arg) - 1]; + const char *value = parser->argv[1]; if (!value) { - parse_error(parser, "${cyn}%s${rs} needs a value.\n", arg); + parse_error(parser, "${cyn}-%c${rs} needs a value.\n", flag); return NULL; } @@ -667,11 +624,34 @@ static struct bfs_expr *parse_unary_flag(struct bfs_parser *parser) { } /** + * Parse a prefix flag like -O3, -j8, etc. + */ +static struct bfs_expr *parse_prefix_flag(struct bfs_parser *parser, char flag, bool allow_separate, const char **value) { + const char *arg = parser->argv[0]; + + const char *suffix = strchr(arg, flag) + 1; + if (*suffix) { + *value = suffix; + return parse_nullary_flag(parser); + } + + suffix = parser->argv[1]; + if (allow_separate && suffix) { + *value = suffix; + } else { + parse_error(parser, "${cyn}-%c${rs} needs a value.\n", flag); + return NULL; + } + + return parse_unary_flag(parser); +} + +/** * Parse a single option. */ static struct bfs_expr *parse_option(struct bfs_parser *parser, size_t argc) { - char **argv = parser_advance(parser, T_OPTION, argc); - return parse_new_expr(parser, eval_true, argc, argv); + char **argv = parser_advance(parser, BFS_OPTION, argc); + return parse_new_expr(parser, eval_true, argc, argv, BFS_OPTION); } /** @@ -699,8 +679,8 @@ static struct bfs_expr *parse_unary_option(struct bfs_parser *parser) { * Parse a single test. */ static struct bfs_expr *parse_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) { - char **argv = parser_advance(parser, T_TEST, argc); - return parse_new_expr(parser, eval_fn, argc, argv); + char **argv = parser_advance(parser, BFS_TEST, argc); + return parse_new_expr(parser, eval_fn, argc, argv, BFS_TEST); } /** @@ -728,7 +708,7 @@ static struct bfs_expr *parse_unary_test(struct bfs_parser *parser, bfs_eval_fn * Parse a single action. */ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) { - char **argv = parser_advance(parser, T_ACTION, argc); + char **argv = parser_advance(parser, BFS_ACTION, argc); if (parser->excluding) { parse_argv_error(parser, argv, argc, "This action is not supported within ${red}-exclude${rs}.\n"); @@ -739,7 +719,7 @@ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eva parser->implicit_print = false; } - return parse_new_expr(parser, eval_fn, argc, argv); + return parse_new_expr(parser, eval_fn, argc, argv, BFS_ACTION); } /** @@ -811,7 +791,8 @@ static bool parse_debug_flag(const char *flag, size_t len, const char *expected) static struct bfs_expr *parse_debug(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_ctx *ctx = parser->ctx; - struct bfs_expr *expr = parse_unary_flag(parser); + const char *flags; + struct bfs_expr *expr = parse_prefix_flag(parser, 'D', true, &flags); if (!expr) { cfprintf(ctx->cerr, "\n"); debug_help(ctx->cerr); @@ -820,7 +801,7 @@ static struct bfs_expr *parse_debug(struct bfs_parser *parser, int arg1, int arg bool unrecognized = false; - for (const char *flag = expr->argv[1], *next; flag; flag = next) { + for (const char *flag = flags, *next; flag; flag = next) { size_t len = strcspn(flag, ","); if (flag[len]) { next = flag + len + 1; @@ -868,21 +849,22 @@ static struct bfs_expr *parse_debug(struct bfs_parser *parser, int arg1, int arg * Parse -On. */ static struct bfs_expr *parse_optlevel(struct bfs_parser *parser, int arg1, int arg2) { - struct bfs_expr *expr = parse_nullary_flag(parser); + const char *arg; + struct bfs_expr *expr = parse_prefix_flag(parser, 'O', false, &arg); if (!expr) { return NULL; } int *optlevel = &parser->ctx->optlevel; - if (strcmp(expr->argv[0], "-Ofast") == 0) { + if (strcmp(arg, "fast") == 0) { *optlevel = 4; - } else if (!parse_int(parser, expr->argv, expr->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) { + } else if (!parse_int(parser, expr->argv, arg, optlevel, IF_INT | IF_UNSIGNED)) { return NULL; } if (*optlevel > 4) { - parse_expr_warning(parser, expr, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", expr->argv[0] + 2); + parse_expr_warning(parser, expr, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", arg); } return expr; @@ -996,16 +978,16 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg switch (*tail) { case 'w': time *= 7; - fallthru; + _fallthrough; case 'd': time *= 24; - fallthru; + _fallthrough; case 'h': time *= 60; - fallthru; + _fallthrough; case 'm': time *= 60; - fallthru; + _fallthrough; case 's': break; default: @@ -1117,7 +1099,7 @@ static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bf // strcmp() can be much faster than fnmatch() since it doesn't have to // parse the pattern, so special-case patterns with no wildcards. // - // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 + // https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#tag_19_14_01 expr->literal = strcspn(expr->pattern, "?*\\[") == len; return expr; @@ -1176,8 +1158,12 @@ static struct bfs_expr *parse_daystart(struct bfs_parser *parser, int arg1, int * Parse -delete. */ static struct bfs_expr *parse_delete(struct bfs_parser *parser, int arg1, int arg2) { - parser->ctx->flags |= BFTW_POST_ORDER; + struct bfs_ctx *ctx = parser->ctx; + ctx->flags |= BFTW_POST_ORDER; + ctx->dangerous = true; + parser->depth_arg = parser->argv; + return parse_nullary_action(parser, eval_delete); } @@ -1237,7 +1223,9 @@ static struct bfs_expr *parse_empty(struct bfs_parser *parser, int arg1, int arg * Parse -exec(dir)?/-ok(dir)?. */ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg2) { - struct bfs_exec *execbuf = bfs_exec_parse(parser->ctx, parser->argv, flags); + struct bfs_ctx *ctx = parser->ctx; + + struct bfs_exec *execbuf = bfs_exec_parse(ctx, parser->argv, flags); if (!execbuf) { return NULL; } @@ -1286,6 +1274,8 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg if (execbuf->flags & BFS_EXEC_CONFIRM) { parser->ok_expr = expr; + } else { + ctx->dangerous = true; } return expr; @@ -1613,13 +1603,14 @@ static struct bfs_expr *parse_inum(struct bfs_parser *parser, int arg1, int arg2 * Parse -j<n>. */ static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2) { - struct bfs_expr *expr = parse_nullary_flag(parser); + const char *arg; + struct bfs_expr *expr = parse_prefix_flag(parser, 'j', false, &arg); if (!expr) { return NULL; } unsigned int n; - if (!parse_int(parser, expr->argv, expr->argv[0] + 2, &n, IF_INT | IF_UNSIGNED)) { + if (!parse_int(parser, expr->argv, arg, &n, IF_INT | IF_UNSIGNED)) { return NULL; } @@ -1684,10 +1675,7 @@ static struct bfs_expr *parse_mount(struct bfs_parser *parser, int arg1, int arg return NULL; } - parse_expr_warning(parser, expr, "In the future, ${blu}%s${rs} will skip mount points entirely, unlike\n", expr->argv[0]); - bfs_warning(parser->ctx, "${blu}-xdev${rs}, due to http://austingroupbugs.net/view.php?id=1133.\n\n"); - - parser->ctx->flags |= BFTW_PRUNE_MOUNTS; + parser->ctx->flags |= BFTW_SKIP_MOUNTS; parser->mount_arg = expr->argv; return expr; } @@ -1831,6 +1819,14 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a } /** + * Parse -noerror. + */ +static struct bfs_expr *parse_noerror(struct bfs_parser *parser, int arg1, int arg2) { + parser->ctx->ignore_errors = true; + return parse_nullary_option(parser); +} + +/** * Parse -nogroup. */ static struct bfs_expr *parse_nogroup(struct bfs_parser *parser, int arg1, int arg2) { @@ -1846,7 +1842,7 @@ static struct bfs_expr *parse_nogroup(struct bfs_parser *parser, int arg1, int a * Parse -nohidden. */ static struct bfs_expr *parse_nohidden(struct bfs_parser *parser, int arg1, int arg2) { - struct bfs_expr *hidden = parse_new_expr(parser, eval_hidden, 1, &fake_hidden_arg); + struct bfs_expr *hidden = parse_new_expr(parser, eval_hidden, 1, &fake_hidden_arg, BFS_TEST); if (!hidden) { return NULL; } @@ -1894,6 +1890,8 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct return 0; } + mode_t umask = parser->ctx->umask; + expr->file_mode = 0; expr->dir_mode = 0; @@ -1914,7 +1912,7 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct // // PERMCOPY : "u" | "g" | "o" - // Parser machine parser + // State machine state enum { MODE_CLAUSE, MODE_WHO, @@ -1922,7 +1920,7 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct MODE_ACTION_APPLY, MODE_OP, MODE_PERM, - } mparser = MODE_CLAUSE; + } state = MODE_CLAUSE; enum { MODE_PLUS, @@ -1931,16 +1929,18 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct } op uninit(MODE_EQUALS); mode_t who uninit(0); + mode_t mask uninit(0); mode_t file_change uninit(0); mode_t dir_change uninit(0); const char *i = mode; while (true) { - switch (mparser) { + switch (state) { case MODE_CLAUSE: who = 0; - mparser = MODE_WHO; - fallthru; + mask = 0777; + state = MODE_WHO; + _fallthrough; case MODE_WHO: switch (*i) { @@ -1957,7 +1957,7 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct who |= 0777; break; default: - mparser = MODE_ACTION; + state = MODE_ACTION; continue; } break; @@ -1967,7 +1967,7 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct case MODE_EQUALS: expr->file_mode &= ~who; expr->dir_mode &= ~who; - fallthru; + _fallthrough; case MODE_PLUS: expr->file_mode |= file_change; expr->dir_mode |= dir_change; @@ -1977,37 +1977,40 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct expr->dir_mode &= ~dir_change; break; } - fallthru; + _fallthrough; case MODE_ACTION: if (who == 0) { who = 0777; + mask = who & ~umask; + } else { + mask = who; } switch (*i) { case '+': op = MODE_PLUS; - mparser = MODE_OP; + state = MODE_OP; break; case '-': op = MODE_MINUS; - mparser = MODE_OP; + state = MODE_OP; break; case '=': op = MODE_EQUALS; - mparser = MODE_OP; + state = MODE_OP; break; case ',': - if (mparser == MODE_ACTION_APPLY) { - mparser = MODE_CLAUSE; + if (state == MODE_ACTION_APPLY) { + state = MODE_CLAUSE; } else { goto fail; } break; case '\0': - if (mparser == MODE_ACTION_APPLY) { + if (state == MODE_ACTION_APPLY) { goto done; } else { goto fail; @@ -2036,32 +2039,32 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct default: file_change = 0; dir_change = 0; - mparser = MODE_PERM; + state = MODE_PERM; continue; } file_change |= (file_change << 6) | (file_change << 3); - file_change &= who; + file_change &= mask; dir_change |= (dir_change << 6) | (dir_change << 3); - dir_change &= who; - mparser = MODE_ACTION_APPLY; + dir_change &= mask; + state = MODE_ACTION_APPLY; break; case MODE_PERM: switch (*i) { case 'r': - file_change |= who & 0444; - dir_change |= who & 0444; + file_change |= mask & 0444; + dir_change |= mask & 0444; break; case 'w': - file_change |= who & 0222; - dir_change |= who & 0222; + file_change |= mask & 0222; + dir_change |= mask & 0222; break; case 'x': - file_change |= who & 0111; - fallthru; + file_change |= mask & 0111; + _fallthrough; case 'X': - dir_change |= who & 0111; + dir_change |= mask & 0111; break; case 's': if (who & 0700) { @@ -2080,7 +2083,7 @@ static int parse_mode(const struct bfs_parser *parser, const char *mode, struct } break; default: - mparser = MODE_ACTION_APPLY; + state = MODE_ACTION_APPLY; continue; } break; @@ -2122,7 +2125,7 @@ static struct bfs_expr *parse_perm(struct bfs_parser *parser, int field, int arg ++mode; break; } - fallthru; + _fallthrough; default: expr->mode_cmp = BFS_MODE_EQUAL; break; @@ -2253,16 +2256,27 @@ static struct bfs_expr *parse_regextype(struct bfs_parser *parser, int arg1, int // See https://www.gnu.org/software/gnulib/manual/html_node/Predefined-Syntaxes.html const char *type = expr->argv[1]; if (strcmp(type, "posix-basic") == 0 + || strcmp(type, "posix-minimal-basic") == 0 || strcmp(type, "ed") == 0 || strcmp(type, "sed") == 0) { parser->regex_type = BFS_REGEX_POSIX_BASIC; } else if (strcmp(type, "posix-extended") == 0) { parser->regex_type = BFS_REGEX_POSIX_EXTENDED; #if BFS_WITH_ONIGURUMA + } else if (strcmp(type, "awk") == 0 + || strcmp(type, "posix-awk") == 0) { + parser->regex_type = BFS_REGEX_AWK; + } else if (strcmp(type, "gnu-awk") == 0) { + parser->regex_type = BFS_REGEX_GNU_AWK; } else if (strcmp(type, "emacs") == 0) { parser->regex_type = BFS_REGEX_EMACS; } else if (strcmp(type, "grep") == 0) { parser->regex_type = BFS_REGEX_GREP; + } else if (strcmp(type, "egrep") == 0 + || strcmp(type, "posix-egrep") == 0) { + parser->regex_type = BFS_REGEX_EGREP; + } else if (strcmp(type, "findutils-default") == 0) { + parser->regex_type = BFS_REGEX_GNU_FIND; #endif } else if (strcmp(type, "help") == 0) { parser->just_info = true; @@ -2277,14 +2291,23 @@ static struct bfs_expr *parse_regextype(struct bfs_parser *parser, int arg1, int list_types: cfprintf(cfile, "Supported types are:\n\n"); - cfprintf(cfile, " ${bld}posix-basic${rs}: POSIX basic regular expressions (BRE)\n"); - cfprintf(cfile, " ${bld}posix-extended${rs}: POSIX extended regular expressions (ERE)\n"); - cfprintf(cfile, " ${bld}ed${rs}: Like ${grn}ed${rs} (same as ${bld}posix-basic${rs})\n"); + cfprintf(cfile, " ${bld}posix-basic${rs}: POSIX basic regular expressions (BRE)\n"); + cfprintf(cfile, " ${bld}ed${rs}: Like ${grn}ed${rs} (same as ${bld}posix-basic${rs})\n"); + cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${rs} (same as ${bld}posix-basic${rs})\n\n"); + + cfprintf(cfile, " ${bld}posix-extended${rs}: POSIX extended regular expressions (ERE)\n\n"); + #if BFS_WITH_ONIGURUMA - cfprintf(cfile, " ${bld}emacs${rs}: Like ${grn}emacs${rs}\n"); - cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n"); + cfprintf(cfile, " [${bld}posix-${rs}]${bld}awk${rs}: Like ${grn}awk${rs}\n"); + cfprintf(cfile, " ${bld}gnu-awk${rs}: Like GNU ${grn}awk${rs}\n\n"); + + cfprintf(cfile, " ${bld}emacs${rs}: Like ${grn}emacs${rs}\n\n"); + + cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n"); + cfprintf(cfile, " [${bld}posix-${rs}]${bld}egrep${rs}: Like ${grn}grep${rs} ${cyn}-E${rs}\n\n"); + + cfprintf(cfile, " ${bld}findutils-default${rs}: Like GNU ${grn}find${rs}\n"); #endif - cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${rs} (same as ${bld}posix-basic${rs})\n"); return NULL; } @@ -2322,13 +2345,13 @@ static struct bfs_expr *parse_search_strategy(struct bfs_parser *parser, int arg struct bfs_ctx *ctx = parser->ctx; CFILE *cfile = ctx->cerr; - struct bfs_expr *expr = parse_unary_flag(parser); + const char *arg; + struct bfs_expr *expr = parse_prefix_flag(parser, 'S', true, &arg); if (!expr) { cfprintf(cfile, "\n"); goto list_strategies; } - const char *arg = expr->argv[1]; if (strcmp(arg, "bfs") == 0) { ctx->strategy = BFTW_BFS; } else if (strcmp(arg, "dfs") == 0) { @@ -2744,8 +2767,9 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 cfprintf(cout, " ${blu}-mindepth${rs} ${bld}N${rs}\n"); cfprintf(cout, " Ignore files deeper/shallower than ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-mount${rs}\n"); - cfprintf(cout, " Don't descend into other mount points (same as ${blu}-xdev${rs} for now, but will\n"); - cfprintf(cout, " skip mount points entirely in the future)\n"); + cfprintf(cout, " Exclude mount points entirely from the results\n"); + cfprintf(cout, " ${blu}-noerror${rs}\n"); + cfprintf(cout, " Ignore any errors that occur during traversal\n"); cfprintf(cout, " ${blu}-nohidden${rs}\n"); cfprintf(cout, " Exclude hidden files\n"); cfprintf(cout, " ${blu}-noleaf${rs}\n"); @@ -2922,18 +2946,60 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 return NULL; } +/** Print the bfs "logo". */ +static void print_logo(CFILE *cout) { + if (!cout->colors) { + goto boring; + } + + size_t vwidth = xstrwidth(bfs_version); + dchar *spaces = dstrepeat(" ", vwidth); + dchar *lines = dstrepeat("─", vwidth); + if (!spaces || !lines) { + dstrfree(lines); + dstrfree(spaces); + goto boring; + } + + // We do ----\r<emoji> rather than <emoji>--- so we don't have to assume + // anything about the width of the emoji + cfprintf(cout, "╭─────%s╮\r📂\n", lines); + cfprintf(cout, "├${ex}b${rs} %s │\n", spaces); + cfprintf(cout, "╰├${ex}f${rs} ${bld}%s${rs} │\n", bfs_version); + cfprintf(cout, " ╰├${ex}s${rs} %s │\n", spaces); + cfprintf(cout, " ╰──%s─╯\n\n", lines); + + dstrfree(lines); + dstrfree(spaces); + return; + +boring: + printf("%s %s\n\n", BFS_COMMAND, bfs_version); +} + /** * "Parse" -version. */ static struct bfs_expr *parse_version(struct bfs_parser *parser, int arg1, int arg2) { - cfprintf(parser->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, bfs_version); + print_logo(parser->ctx->cout); + + printf("Copyright © Tavian Barnes and the bfs contributors\n"); + printf("No rights reserved (https://opensource.org/license/0BSD)\n\n"); - printf("%s\n", BFS_HOMEPAGE); + printf("CONFFLAGS := %s\n", bfs_confflags); + printf("CC := %s\n", bfs_cc); + printf("CPPFLAGS := %s\n", bfs_cppflags); + printf("CFLAGS := %s\n", bfs_cflags); + printf("LDFLAGS := %s\n", bfs_ldflags); + printf("LDLIBS := %s\n", bfs_ldlibs); + + printf("\n%s\n", BFS_HOMEPAGE); parser->just_info = true; return NULL; } +/** Parser callback function type. */ typedef struct bfs_expr *parse_fn(struct bfs_parser *parser, int arg1, int arg2); /** @@ -2941,137 +3007,139 @@ typedef struct bfs_expr *parse_fn(struct bfs_parser *parser, int arg1, int arg2) */ struct table_entry { char *arg; - enum token_type type; + enum bfs_kind kind; parse_fn *parse; int arg1; int arg2; bool prefix; + bool needs_arg; }; /** * The parse table for primary expressions. */ static const struct table_entry parse_table[] = { - {"--", T_FLAG}, - {"--help", T_ACTION, parse_help}, - {"--version", T_ACTION, parse_version}, - {"-Bmin", T_TEST, parse_min, BFS_STAT_BTIME}, - {"-Bnewer", T_TEST, parse_newer, BFS_STAT_BTIME}, - {"-Bsince", T_TEST, parse_since, BFS_STAT_BTIME}, - {"-Btime", T_TEST, parse_time, BFS_STAT_BTIME}, - {"-D", T_FLAG, parse_debug}, - {"-E", T_FLAG, parse_regex_extended}, - {"-H", T_FLAG, parse_follow, BFTW_FOLLOW_ROOTS, false}, - {"-L", T_FLAG, parse_follow, BFTW_FOLLOW_ALL, false}, - {"-O", T_FLAG, parse_optlevel, 0, 0, true}, - {"-P", T_FLAG, parse_follow, 0, false}, - {"-S", T_FLAG, parse_search_strategy}, - {"-X", T_FLAG, parse_xargs_safe}, - {"-a", T_OPERATOR}, - {"-acl", T_TEST, parse_acl}, - {"-amin", T_TEST, parse_min, BFS_STAT_ATIME}, - {"-and", T_OPERATOR}, - {"-anewer", T_TEST, parse_newer, BFS_STAT_ATIME}, - {"-asince", T_TEST, parse_since, BFS_STAT_ATIME}, - {"-atime", T_TEST, parse_time, BFS_STAT_ATIME}, - {"-capable", T_TEST, parse_capable}, - {"-cmin", T_TEST, parse_min, BFS_STAT_CTIME}, - {"-cnewer", T_TEST, parse_newer, BFS_STAT_CTIME}, - {"-color", T_OPTION, parse_color, true}, - {"-context", T_TEST, parse_context, true}, - {"-csince", T_TEST, parse_since, BFS_STAT_CTIME}, - {"-ctime", T_TEST, parse_time, BFS_STAT_CTIME}, - {"-d", T_FLAG, parse_depth}, - {"-daystart", T_OPTION, parse_daystart}, - {"-delete", T_ACTION, parse_delete}, - {"-depth", T_OPTION, parse_depth_n}, - {"-empty", T_TEST, parse_empty}, - {"-exclude", T_OPERATOR}, - {"-exec", T_ACTION, parse_exec, 0}, - {"-execdir", T_ACTION, parse_exec, BFS_EXEC_CHDIR}, - {"-executable", T_TEST, parse_access, X_OK}, - {"-exit", T_ACTION, parse_exit}, - {"-f", T_FLAG, parse_f}, - {"-false", T_TEST, parse_const, false}, - {"-files0-from", T_OPTION, parse_files0_from}, - {"-flags", T_TEST, parse_flags}, - {"-fls", T_ACTION, parse_fls}, - {"-follow", T_OPTION, parse_follow, BFTW_FOLLOW_ALL, true}, - {"-fprint", T_ACTION, parse_fprint}, - {"-fprint0", T_ACTION, parse_fprint0}, - {"-fprintf", T_ACTION, parse_fprintf}, - {"-fstype", T_TEST, parse_fstype}, - {"-gid", T_TEST, parse_group}, - {"-group", T_TEST, parse_group}, - {"-help", T_ACTION, parse_help}, - {"-hidden", T_TEST, parse_hidden}, - {"-ignore_readdir_race", T_OPTION, parse_ignore_races, true}, - {"-ilname", T_TEST, parse_lname, true}, - {"-iname", T_TEST, parse_name, true}, - {"-inum", T_TEST, parse_inum}, - {"-ipath", T_TEST, parse_path, true}, - {"-iregex", T_TEST, parse_regex, BFS_REGEX_ICASE}, - {"-iwholename", T_TEST, parse_path, true}, - {"-j", T_FLAG, parse_jobs, 0, 0, true}, - {"-limit", T_ACTION, parse_limit}, - {"-links", T_TEST, parse_links}, - {"-lname", T_TEST, parse_lname, false}, - {"-ls", T_ACTION, parse_ls}, - {"-maxdepth", T_OPTION, parse_depth_limit, false}, - {"-mindepth", T_OPTION, parse_depth_limit, true}, - {"-mmin", T_TEST, parse_min, BFS_STAT_MTIME}, - {"-mnewer", T_TEST, parse_newer, BFS_STAT_MTIME}, - {"-mount", T_OPTION, parse_mount}, - {"-msince", T_TEST, parse_since, BFS_STAT_MTIME}, - {"-mtime", T_TEST, parse_time, BFS_STAT_MTIME}, - {"-name", T_TEST, parse_name, false}, - {"-newer", T_TEST, parse_newer, BFS_STAT_MTIME}, - {"-newer", T_TEST, parse_newerxy, 0, 0, true}, - {"-nocolor", T_OPTION, parse_color, false}, - {"-nogroup", T_TEST, parse_nogroup}, - {"-nohidden", T_TEST, parse_nohidden}, - {"-noignore_readdir_race", T_OPTION, parse_ignore_races, false}, - {"-noleaf", T_OPTION, parse_noleaf}, - {"-not", T_OPERATOR}, - {"-nouser", T_TEST, parse_nouser}, - {"-nowarn", T_OPTION, parse_warn, false}, - {"-o", T_OPERATOR}, - {"-ok", T_ACTION, parse_exec, BFS_EXEC_CONFIRM}, - {"-okdir", T_ACTION, parse_exec, BFS_EXEC_CONFIRM | BFS_EXEC_CHDIR}, - {"-or", T_OPERATOR}, - {"-path", T_TEST, parse_path, false}, - {"-perm", T_TEST, parse_perm}, - {"-print", T_ACTION, parse_print}, - {"-print0", T_ACTION, parse_print0}, - {"-printf", T_ACTION, parse_printf}, - {"-printx", T_ACTION, parse_printx}, - {"-prune", T_ACTION, parse_prune}, - {"-quit", T_ACTION, parse_quit}, - {"-readable", T_TEST, parse_access, R_OK}, - {"-regex", T_TEST, parse_regex, 0}, - {"-regextype", T_OPTION, parse_regextype}, - {"-rm", T_ACTION, parse_delete}, - {"-s", T_FLAG, parse_s}, - {"-samefile", T_TEST, parse_samefile}, - {"-since", T_TEST, parse_since, BFS_STAT_MTIME}, - {"-size", T_TEST, parse_size}, - {"-sparse", T_TEST, parse_sparse}, - {"-status", T_OPTION, parse_status}, - {"-true", T_TEST, parse_const, true}, - {"-type", T_TEST, parse_type, false}, - {"-uid", T_TEST, parse_user}, - {"-unique", T_OPTION, parse_unique}, - {"-used", T_TEST, parse_used}, - {"-user", T_TEST, parse_user}, - {"-version", T_ACTION, parse_version}, - {"-warn", T_OPTION, parse_warn, true}, - {"-wholename", T_TEST, parse_path, false}, - {"-writable", T_TEST, parse_access, W_OK}, - {"-x", T_FLAG, parse_xdev}, - {"-xattr", T_TEST, parse_xattr}, - {"-xattrname", T_TEST, parse_xattrname}, - {"-xdev", T_OPTION, parse_xdev}, - {"-xtype", T_TEST, parse_type, true}, + {"--", BFS_FLAG}, + {"--help", BFS_ACTION, parse_help}, + {"--version", BFS_ACTION, parse_version}, + {"-Bmin", BFS_TEST, parse_min, BFS_STAT_BTIME}, + {"-Bnewer", BFS_TEST, parse_newer, BFS_STAT_BTIME}, + {"-Bsince", BFS_TEST, parse_since, BFS_STAT_BTIME}, + {"-Btime", BFS_TEST, parse_time, BFS_STAT_BTIME}, + {"-D", BFS_FLAG, parse_debug, .prefix = true}, + {"-E", BFS_FLAG, parse_regex_extended}, + {"-H", BFS_FLAG, parse_follow, BFTW_FOLLOW_ROOTS, false}, + {"-L", BFS_FLAG, parse_follow, BFTW_FOLLOW_ALL, false}, + {"-O", BFS_FLAG, parse_optlevel, .prefix = true}, + {"-P", BFS_FLAG, parse_follow, 0, false}, + {"-S", BFS_FLAG, parse_search_strategy, .prefix = true}, + {"-X", BFS_FLAG, parse_xargs_safe}, + {"-a", BFS_OPERATOR}, + {"-acl", BFS_TEST, parse_acl}, + {"-amin", BFS_TEST, parse_min, BFS_STAT_ATIME}, + {"-and", BFS_OPERATOR}, + {"-anewer", BFS_TEST, parse_newer, BFS_STAT_ATIME}, + {"-asince", BFS_TEST, parse_since, BFS_STAT_ATIME}, + {"-atime", BFS_TEST, parse_time, BFS_STAT_ATIME}, + {"-capable", BFS_TEST, parse_capable}, + {"-cmin", BFS_TEST, parse_min, BFS_STAT_CTIME}, + {"-cnewer", BFS_TEST, parse_newer, BFS_STAT_CTIME}, + {"-color", BFS_OPTION, parse_color, true}, + {"-context", BFS_TEST, parse_context, true}, + {"-csince", BFS_TEST, parse_since, BFS_STAT_CTIME}, + {"-ctime", BFS_TEST, parse_time, BFS_STAT_CTIME}, + {"-d", BFS_FLAG, parse_depth}, + {"-daystart", BFS_OPTION, parse_daystart}, + {"-delete", BFS_ACTION, parse_delete}, + {"-depth", BFS_OPTION, parse_depth_n}, + {"-empty", BFS_TEST, parse_empty}, + {"-exclude", BFS_OPERATOR}, + {"-exec", BFS_ACTION, parse_exec, 0}, + {"-execdir", BFS_ACTION, parse_exec, BFS_EXEC_CHDIR}, + {"-executable", BFS_TEST, parse_access, X_OK}, + {"-exit", BFS_ACTION, parse_exit}, + {"-f", BFS_FLAG, parse_f, .needs_arg = true}, + {"-false", BFS_TEST, parse_const, false}, + {"-files0-from", BFS_OPTION, parse_files0_from}, + {"-flags", BFS_TEST, parse_flags}, + {"-fls", BFS_ACTION, parse_fls}, + {"-follow", BFS_OPTION, parse_follow, BFTW_FOLLOW_ALL, true}, + {"-fprint", BFS_ACTION, parse_fprint}, + {"-fprint0", BFS_ACTION, parse_fprint0}, + {"-fprintf", BFS_ACTION, parse_fprintf}, + {"-fstype", BFS_TEST, parse_fstype}, + {"-gid", BFS_TEST, parse_group}, + {"-group", BFS_TEST, parse_group}, + {"-help", BFS_ACTION, parse_help}, + {"-hidden", BFS_TEST, parse_hidden}, + {"-ignore_readdir_race", BFS_OPTION, parse_ignore_races, true}, + {"-ilname", BFS_TEST, parse_lname, true}, + {"-iname", BFS_TEST, parse_name, true}, + {"-inum", BFS_TEST, parse_inum}, + {"-ipath", BFS_TEST, parse_path, true}, + {"-iregex", BFS_TEST, parse_regex, BFS_REGEX_ICASE}, + {"-iwholename", BFS_TEST, parse_path, true}, + {"-j", BFS_FLAG, parse_jobs, .prefix = true}, + {"-limit", BFS_ACTION, parse_limit}, + {"-links", BFS_TEST, parse_links}, + {"-lname", BFS_TEST, parse_lname, false}, + {"-ls", BFS_ACTION, parse_ls}, + {"-maxdepth", BFS_OPTION, parse_depth_limit, false}, + {"-mindepth", BFS_OPTION, parse_depth_limit, true}, + {"-mmin", BFS_TEST, parse_min, BFS_STAT_MTIME}, + {"-mnewer", BFS_TEST, parse_newer, BFS_STAT_MTIME}, + {"-mount", BFS_OPTION, parse_mount}, + {"-msince", BFS_TEST, parse_since, BFS_STAT_MTIME}, + {"-mtime", BFS_TEST, parse_time, BFS_STAT_MTIME}, + {"-name", BFS_TEST, parse_name, false}, + {"-newer", BFS_TEST, parse_newer, BFS_STAT_MTIME}, + {"-newer", BFS_TEST, parse_newerxy, .prefix = true}, + {"-nocolor", BFS_OPTION, parse_color, false}, + {"-noerror", BFS_OPTION, parse_noerror}, + {"-nogroup", BFS_TEST, parse_nogroup}, + {"-nohidden", BFS_TEST, parse_nohidden}, + {"-noignore_readdir_race", BFS_OPTION, parse_ignore_races, false}, + {"-noleaf", BFS_OPTION, parse_noleaf}, + {"-not", BFS_OPERATOR}, + {"-nouser", BFS_TEST, parse_nouser}, + {"-nowarn", BFS_OPTION, parse_warn, false}, + {"-o", BFS_OPERATOR}, + {"-ok", BFS_ACTION, parse_exec, BFS_EXEC_CONFIRM}, + {"-okdir", BFS_ACTION, parse_exec, BFS_EXEC_CONFIRM | BFS_EXEC_CHDIR}, + {"-or", BFS_OPERATOR}, + {"-path", BFS_TEST, parse_path, false}, + {"-perm", BFS_TEST, parse_perm}, + {"-print", BFS_ACTION, parse_print}, + {"-print0", BFS_ACTION, parse_print0}, + {"-printf", BFS_ACTION, parse_printf}, + {"-printx", BFS_ACTION, parse_printx}, + {"-prune", BFS_ACTION, parse_prune}, + {"-quit", BFS_ACTION, parse_quit}, + {"-readable", BFS_TEST, parse_access, R_OK}, + {"-regex", BFS_TEST, parse_regex, 0}, + {"-regextype", BFS_OPTION, parse_regextype}, + {"-rm", BFS_ACTION, parse_delete}, + {"-s", BFS_FLAG, parse_s}, + {"-samefile", BFS_TEST, parse_samefile}, + {"-since", BFS_TEST, parse_since, BFS_STAT_MTIME}, + {"-size", BFS_TEST, parse_size}, + {"-sparse", BFS_TEST, parse_sparse}, + {"-status", BFS_OPTION, parse_status}, + {"-true", BFS_TEST, parse_const, true}, + {"-type", BFS_TEST, parse_type, false}, + {"-uid", BFS_TEST, parse_user}, + {"-unique", BFS_OPTION, parse_unique}, + {"-used", BFS_TEST, parse_used}, + {"-user", BFS_TEST, parse_user}, + {"-version", BFS_ACTION, parse_version}, + {"-warn", BFS_OPTION, parse_warn, true}, + {"-wholename", BFS_TEST, parse_path, false}, + {"-writable", BFS_TEST, parse_access, W_OK}, + {"-x", BFS_FLAG, parse_xdev}, + {"-xattr", BFS_TEST, parse_xattr}, + {"-xattrname", BFS_TEST, parse_xattrname}, + {"-xdev", BFS_OPTION, parse_xdev}, + {"-xtype", BFS_TEST, parse_type, true}, {0}, }; @@ -3092,6 +3160,83 @@ static const struct table_entry *table_lookup(const char *arg) { return NULL; } +/** Look up a single-character flag in the parse table. */ +static const struct table_entry *flag_lookup(char flag) { + for (const struct table_entry *entry = parse_table; entry->arg; ++entry) { + enum bfs_kind kind = entry->kind; + if (kind == BFS_FLAG && entry->arg[1] == flag && !entry->arg[2]) { + return entry; + } + } + + return NULL; +} + +/** Check for a multi-flag argument like -LEXO2. */ +static bool is_flag_group(const char *arg) { + // We enforce that at least one flag in a flag group must be a capital + // letter, to avoid ambiguity with primary expressions + bool has_upper = false; + + // Flags that take an argument must appear last + bool needs_arg = false; + + for (size_t i = 1; arg[i]; ++i) { + char c = arg[i]; + if (c >= 'A' && c <= 'Z') { + has_upper = true; + } + + if (needs_arg) { + return false; + } + + const struct table_entry *entry = flag_lookup(c); + if (!entry || !entry->parse) { + return false; + } + + if (entry->prefix) { + // The rest is the flag's argument + break; + } + + needs_arg |= entry->needs_arg; + } + + return has_upper; +} + +/** Parse a multi-flag argument. */ +static struct bfs_expr *parse_flag_group(struct bfs_parser *parser) { + struct bfs_expr *expr = NULL; + + char **start = parser->argv; + char **end = start; + const char *arg = start[0]; + + for (size_t i = 1; arg[i]; ++i) { + parser->argv = start; + + const struct table_entry *entry = flag_lookup(arg[i]); + expr = entry->parse(parser, entry->arg1, entry->arg2); + + if (parser->argv > end) { + end = parser->argv; + } + + if (!expr || entry->prefix) { + break; + } + } + + if (expr) { + bfs_assert(parser->argv == end, "Didn't eat enough tokens"); + } + + return expr; +} + /** Search for a fuzzy match in the parse table. */ static const struct table_entry *table_lookup_fuzzy(const char *arg) { const struct table_entry *best = NULL; @@ -3114,6 +3259,8 @@ static const struct table_entry *table_lookup_fuzzy(const char *arg) { * | ACTION */ static struct bfs_expr *parse_primary(struct bfs_parser *parser) { + struct bfs_ctx *ctx = parser->ctx; + // Paths are already skipped at this point const char *arg = parser->argv[0]; @@ -3130,15 +3277,19 @@ static struct bfs_expr *parse_primary(struct bfs_parser *parser) { } } + if (is_flag_group(arg)) { + return parse_flag_group(parser); + } + match = table_lookup_fuzzy(arg); - CFILE *cerr = parser->ctx->cerr; + CFILE *cerr = ctx->cerr; parse_error(parser, "Unknown argument; did you mean "); - switch (match->type) { - case T_FLAG: + switch (match->kind) { + case BFS_FLAG: cfprintf(cerr, "${cyn}%s${rs}?", match->arg); break; - case T_OPERATOR: + case BFS_OPERATOR: cfprintf(cerr, "${red}%s${rs}?", match->arg); break; default: @@ -3146,7 +3297,7 @@ static struct bfs_expr *parse_primary(struct bfs_parser *parser) { break; } - if (!parser->interactive || !match->parse) { + if (!ctx->interactive || !match->parse) { fprintf(stderr, "\n"); goto unmatched; } @@ -3188,7 +3339,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { } if (strcmp(arg, "(") == 0) { - parser_advance(parser, T_OPERATOR, 1); + parser_advance(parser, BFS_OPERATOR, 1); struct bfs_expr *expr = parse_expr(parser); if (!expr) { @@ -3205,7 +3356,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - parser_advance(parser, T_OPERATOR, 1); + parser_advance(parser, BFS_OPERATOR, 1); return expr; } else if (strcmp(arg, "-exclude") == 0) { if (parser->excluding) { @@ -3213,7 +3364,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - char **argv = parser_advance(parser, T_OPERATOR, 1); + char **argv = parser_advance(parser, BFS_OPERATOR, 1); parser->excluding = true; struct bfs_expr *factor = parse_factor(parser); @@ -3224,9 +3375,9 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { parser->excluding = false; bfs_expr_append(parser->ctx->exclude, factor); - return parse_new_expr(parser, eval_true, parser->argv - argv, argv); + return parse_new_expr(parser, eval_true, parser->argv - argv, argv, BFS_OPERATOR); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { - char **argv = parser_advance(parser, T_OPERATOR, 1); + char **argv = parser_advance(parser, BFS_OPERATOR, 1); struct bfs_expr *factor = parse_factor(parser); if (!factor) { @@ -3266,7 +3417,7 @@ static struct bfs_expr *parse_term(struct bfs_parser *parser) { char **argv = &fake_and_arg; if (strcmp(arg, "-a") == 0 || strcmp(arg, "-and") == 0) { - argv = parser_advance(parser, T_OPERATOR, 1); + argv = parser_advance(parser, BFS_OPERATOR, 1); } struct bfs_expr *lhs = term; @@ -3303,7 +3454,7 @@ static struct bfs_expr *parse_clause(struct bfs_parser *parser) { break; } - char **argv = parser_advance(parser, T_OPERATOR, 1); + char **argv = parser_advance(parser, BFS_OPERATOR, 1); struct bfs_expr *lhs = clause; struct bfs_expr *rhs = parse_term(parser); @@ -3338,7 +3489,7 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { break; } - char **argv = parser_advance(parser, T_OPERATOR, 1); + char **argv = parser_advance(parser, BFS_OPERATOR, 1); struct bfs_expr *lhs = expr; struct bfs_expr *rhs = parse_clause(parser); @@ -3356,6 +3507,8 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { * Parse the top-level expression. */ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { + struct bfs_ctx *ctx = parser->ctx; + if (skip_paths(parser) != 0) { return NULL; } @@ -3364,7 +3517,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->argv[0]) { expr = parse_expr(parser); } else { - expr = parse_new_expr(parser, eval_true, 1, &fake_true_arg); + expr = parse_new_expr(parser, eval_true, 1, &fake_true_arg, BFS_TEST); } if (!expr) { return NULL; @@ -3384,7 +3537,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { return NULL; } - struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg); + struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg, BFS_ACTION); if (!print) { return NULL; } @@ -3402,14 +3555,14 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { parser->xdev_arg[0], parser->mount_arg[0]); } - if (parser->ctx->warn && parser->depth_arg && parser->prune_arg) { + if (ctx->warn && parser->depth_arg && parser->prune_arg) { parse_conflict_warning(parser, parser->depth_arg, 1, parser->prune_arg, 1, "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", parser->prune_arg[0], parser->depth_arg[0]); - if (parser->interactive) { - bfs_warning(parser->ctx, "Do you want to continue? "); - if (ynprompt() == 0) { + if (ctx->interactive) { + bfs_warning(ctx, "Do you want to continue? "); + if (ynprompt() <= 0) { return NULL; } } @@ -3643,6 +3796,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } else { ctx->warn = stdin_tty; } + ctx->interactive = stdin_tty && stderr_tty; struct bfs_parser parser = { .ctx = ctx, @@ -3650,7 +3804,6 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .command = ctx->argv[0], .regex_type = BFS_REGEX_POSIX_BASIC, .stdout_tty = stdout_tty, - .interactive = stdin_tty && stderr_tty, .use_color = use_color, .implicit_print = true, .implicit_root = true, @@ -3666,7 +3819,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - ctx->exclude = parse_new_expr(&parser, eval_or, 1, &fake_or_arg); + ctx->exclude = parse_new_expr(&parser, eval_or, 1, &fake_or_arg, BFS_OPERATOR); if (!ctx->exclude) { goto fail; } @@ -3685,7 +3838,9 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } if (bfs_optimize(ctx) != 0) { - bfs_perror(ctx, "bfs_optimize()"); + if (errno != 0) { + bfs_perror(ctx, "bfs_optimize()"); + } goto fail; } |