summaryrefslogtreecommitdiffstats
path: root/src/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.c')
-rw-r--r--src/parse.c526
1 files changed, 279 insertions, 247 deletions
diff --git a/src/parse.c b/src/parse.c
index 86ce72c..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,31 +115,6 @@ struct bfs_parser {
};
/**
- * Token types and flags.
- */
-enum token_info {
- /** A flag. */
- T_FLAG = 1,
- /** A root path. */
- T_PATH = 2,
- /** An option. */
- T_OPTION = 3,
- /** A test. */
- T_TEST = 4,
- /** An action. */
- T_ACTION = 5,
- /** An operator. */
- T_OPERATOR = 6,
- /** Mask for token types. */
- T_TYPE = (1 << 3) - 1,
-
- /** A token can match a prefix of an argument, like -On, -newerXY, etc. */
- T_PREFIX = 1 << 3,
- /** A flag that takes an argument. */
- T_NEEDS_ARG = 1 << 4,
-};
-
-/**
* Print a low-level error message during parsing.
*/
static void parse_perror(const struct bfs_parser *parser, const char *str) {
@@ -165,7 +140,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -183,7 +158,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -201,7 +176,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -220,7 +195,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -235,7 +210,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -256,7 +231,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -278,7 +253,7 @@ 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, ...) {
const struct bfs_ctx *ctx = parser->ctx;
@@ -296,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()");
}
@@ -308,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;
}
@@ -322,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;
}
@@ -407,14 +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_info type, size_t argc) {
- bfs_assert(type == (type & T_TYPE));
-
- 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;
}
@@ -460,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) {
@@ -492,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);
}
}
@@ -623,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);
}
/**
@@ -677,8 +650,8 @@ static struct bfs_expr *parse_prefix_flag(struct bfs_parser *parser, char flag,
* 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);
}
/**
@@ -706,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);
}
/**
@@ -735,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");
@@ -746,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);
}
/**
@@ -1005,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:
@@ -1126,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;
@@ -1185,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);
}
@@ -1246,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;
}
@@ -1295,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;
@@ -1694,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;
}
@@ -1841,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) {
@@ -1856,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;
}
@@ -1904,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;
@@ -1924,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,
@@ -1932,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,
@@ -1941,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) {
@@ -1967,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;
@@ -1977,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;
@@ -1987,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;
@@ -2046,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) {
@@ -2090,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;
@@ -2132,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;
@@ -2774,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");
@@ -2952,11 +2946,42 @@ 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");
@@ -2982,136 +3007,139 @@ typedef struct bfs_expr *parse_fn(struct bfs_parser *parser, int arg1, int arg2)
*/
struct table_entry {
char *arg;
- enum token_info info;
+ 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 | T_PREFIX, 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 | T_PREFIX, parse_optlevel},
- {"-P", T_FLAG, parse_follow, 0, false},
- {"-S", T_FLAG | T_PREFIX, 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 | T_NEEDS_ARG, 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 | T_PREFIX, parse_jobs},
- {"-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 | T_PREFIX, parse_newerxy},
- {"-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},
};
@@ -3119,7 +3147,7 @@ static const struct table_entry parse_table[] = {
static const struct table_entry *table_lookup(const char *arg) {
for (const struct table_entry *entry = parse_table; entry->arg; ++entry) {
bool match;
- if (entry->info & T_PREFIX) {
+ if (entry->prefix) {
match = strncmp(arg, entry->arg, strlen(entry->arg)) == 0;
} else {
match = strcmp(arg, entry->arg) == 0;
@@ -3135,8 +3163,8 @@ static const struct table_entry *table_lookup(const char *arg) {
/** 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 token_info type = entry->info & T_TYPE;
- if (type == T_FLAG && entry->arg[1] == flag && !entry->arg[2]) {
+ enum bfs_kind kind = entry->kind;
+ if (kind == BFS_FLAG && entry->arg[1] == flag && !entry->arg[2]) {
return entry;
}
}
@@ -3168,14 +3196,12 @@ static bool is_flag_group(const char *arg) {
return false;
}
- if (entry->info & T_PREFIX) {
+ if (entry->prefix) {
// The rest is the flag's argument
break;
}
- if (entry->info & T_NEEDS_ARG) {
- needs_arg = true;
- }
+ needs_arg |= entry->needs_arg;
}
return has_upper;
@@ -3199,7 +3225,7 @@ static struct bfs_expr *parse_flag_group(struct bfs_parser *parser) {
end = parser->argv;
}
- if (!expr || entry->info & T_PREFIX) {
+ if (!expr || entry->prefix) {
break;
}
}
@@ -3233,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];
@@ -3255,13 +3283,13 @@ static struct bfs_expr *parse_primary(struct bfs_parser *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->info & T_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:
@@ -3269,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;
}
@@ -3311,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) {
@@ -3328,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) {
@@ -3336,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);
@@ -3347,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) {
@@ -3389,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;
@@ -3426,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);
@@ -3461,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);
@@ -3479,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;
}
@@ -3487,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;
@@ -3507,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;
}
@@ -3525,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;
}
}
@@ -3766,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,
@@ -3773,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,
@@ -3789,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;
}
@@ -3808,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;
}