summaryrefslogtreecommitdiffstats
path: root/src/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse.c')
-rw-r--r--src/parse.c2381
1 files changed, 1280 insertions, 1101 deletions
diff --git a/src/parse.c b/src/parse.c
index 3416d9e..9c39d6b 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -9,19 +9,20 @@
*/
#include "parse.h"
+
#include "alloc.h"
+#include "bfs.h"
#include "bfstd.h"
#include "bftw.h"
#include "color.h"
-#include "config.h"
#include "ctx.h"
-#include "darray.h"
#include "diag.h"
#include "dir.h"
#include "eval.h"
#include "exec.h"
#include "expr.h"
#include "fsade.h"
+#include "list.h"
#include "opt.h"
#include "printf.h"
#include "pwcache.h"
@@ -31,6 +32,7 @@
#include "xregex.h"
#include "xspawn.h"
#include "xtime.h"
+
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
@@ -41,109 +43,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/time.h>
#include <sys/stat.h>
-#include <sys/wait.h>
+#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// Strings printed by -D tree for "fake" expressions
-static char *fake_and_arg = "-a";
-static char *fake_false_arg = "-false";
+static char *fake_and_arg = "-and";
static char *fake_hidden_arg = "-hidden";
-static char *fake_or_arg = "-o";
+static char *fake_or_arg = "-or";
static char *fake_print_arg = "-print";
static char *fake_true_arg = "-true";
-struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) {
- struct bfs_expr *expr = ZALLOC(struct bfs_expr);
- if (!expr) {
- perror("zalloc()");
- return NULL;
- }
-
- expr->eval_fn = eval_fn;
- expr->argc = argc;
- expr->argv = argv;
- expr->probability = 0.5;
- return expr;
-}
-
-bool bfs_expr_is_parent(const struct bfs_expr *expr) {
- return expr->eval_fn == eval_and
- || expr->eval_fn == eval_or
- || expr->eval_fn == eval_not
- || expr->eval_fn == eval_comma;
-}
-
-bool bfs_expr_never_returns(const struct bfs_expr *expr) {
- // Expressions that never return are vacuously both always true and always false
- return expr->always_true && expr->always_false;
-}
-
-void bfs_expr_free(struct bfs_expr *expr) {
- if (!expr) {
- return;
- }
-
- if (bfs_expr_is_parent(expr)) {
- bfs_expr_free(expr->rhs);
- bfs_expr_free(expr->lhs);
- } else if (expr->eval_fn == eval_exec) {
- bfs_exec_free(expr->exec);
- } else if (expr->eval_fn == eval_fprintf) {
- bfs_printf_free(expr->printf);
- } else if (expr->eval_fn == eval_regex) {
- bfs_regfree(expr->regex);
- }
-
- free(expr);
-}
-
-/**
- * Create a new unary expression.
- */
-static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) {
- struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv);
- if (!expr) {
- bfs_expr_free(rhs);
- return NULL;
- }
-
- expr->lhs = NULL;
- expr->rhs = rhs;
- bfs_assert(bfs_expr_is_parent(expr));
-
- expr->persistent_fds = rhs->persistent_fds;
- expr->ephemeral_fds = rhs->ephemeral_fds;
- return expr;
-}
-
-/**
- * Create a new binary expression.
- */
-static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) {
- struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv);
- if (!expr) {
- bfs_expr_free(rhs);
- bfs_expr_free(lhs);
- return NULL;
- }
-
- expr->lhs = lhs;
- expr->rhs = rhs;
- bfs_assert(bfs_expr_is_parent(expr));
-
- expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds;
- if (lhs->ephemeral_fds > rhs->ephemeral_fds) {
- expr->ephemeral_fds = lhs->ephemeral_fds;
- } else {
- expr->ephemeral_fds = rhs->ephemeral_fds;
- }
-
- return expr;
-}
-
/**
* Color use flags.
*/
@@ -154,9 +65,9 @@ enum use_color {
};
/**
- * Ephemeral state for parsing the command line.
+ * Command line parser state.
*/
-struct parser_state {
+struct bfs_parser {
/** The command line being constructed. */
struct bfs_ctx *ctx;
/** The command line arguments being parsed. */
@@ -169,14 +80,10 @@ struct parser_state {
/** 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. */
bool implicit_print;
- /** Whether the default root "." should be used. */
- bool implicit_root;
/** Whether the expression has started. */
bool expr_started;
/** Whether an information option like -help or -version was passed. */
@@ -186,46 +93,30 @@ struct parser_state {
/** The last non-path argument. */
char **last_arg;
- /** A "-depth"-type argument, if any. */
- char **depth_arg;
- /** A "-prune" argument, if any. */
- char **prune_arg;
- /** A "-mount" argument, if any. */
- char **mount_arg;
- /** An "-xdev" argument, if any. */
- char **xdev_arg;
- /** A "-files0-from -" argument, if any. */
- char **files0_stdin_arg;
- /** An "-ok"-type expression, if any. */
- const struct bfs_expr *ok_expr;
+ /** A "-depth"-type expression, if any. */
+ const struct bfs_expr *depth_expr;
+ /** A "-limit" expression, if any. */
+ const struct bfs_expr *limit_expr;
+ /** A "-prune" expression, if any. */
+ const struct bfs_expr *prune_expr;
+ /** A "-mount" expression, if any. */
+ const struct bfs_expr *mount_expr;
+ /** An "-xdev" expression, if any. */
+ const struct bfs_expr *xdev_expr;
+ /** A "-files0-from" expression, if any. */
+ const struct bfs_expr *files0_expr;
+ /** An expression that consumes stdin, if any. */
+ const struct bfs_expr *stdin_expr;
/** The current time (maybe modified by -daystart). */
struct timespec now;
};
/**
- * 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 parser_state *state, const char *str) {
- bfs_perror(state->ctx, str);
+static void parse_perror(const struct bfs_parser *parser, const char *str) {
+ bfs_perror(parser->ctx, str);
}
/** Initialize an empty highlighted range. */
@@ -247,30 +138,27 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc,
/**
* Print an error message during parsing.
*/
-BFS_FORMATTER(2, 3)
-static void parse_error(const struct parser_state *state, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_printf(2, 3)
+static void parse_error(const struct bfs_parser *parser, const char *format, ...) {
+ const struct bfs_ctx *ctx = parser->ctx;
bool highlight[ctx->argc];
init_highlight(ctx, highlight);
- highlight_args(ctx, state->argv, 1, highlight);
+ highlight_args(ctx, parser->argv, 1, highlight);
bfs_argv_error(ctx, highlight);
va_list args;
va_start(args, format);
- errno = error;
- bfs_verror(state->ctx, format, args);
+ bfs_verror(parser->ctx, format, args);
va_end(args);
}
/**
* Print an error about some command line arguments.
*/
-BFS_FORMATTER(4, 5)
-static void parse_argv_error(const struct parser_state *state, char **argv, size_t argc, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_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;
bool highlight[ctx->argc];
init_highlight(ctx, highlight);
@@ -279,7 +167,6 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size
va_list args;
va_start(args, format);
- errno = error;
bfs_verror(ctx, format, args);
va_end(args);
}
@@ -287,20 +174,18 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size
/**
* Print an error about conflicting command line arguments.
*/
-BFS_FORMATTER(6, 7)
-static void parse_conflict_error(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_printf(4, 5)
+static void parse_conflict_error(const struct bfs_parser *parser, const struct bfs_expr *expr1, const struct bfs_expr *expr2, const char *format, ...) {
+ const struct bfs_ctx *ctx = parser->ctx;
bool highlight[ctx->argc];
init_highlight(ctx, highlight);
- highlight_args(ctx, argv1, argc1, highlight);
- highlight_args(ctx, argv2, argc2, highlight);
+ highlight_args(ctx, expr1->argv, expr1->argc, highlight);
+ highlight_args(ctx, expr2->argv, expr2->argc, highlight);
bfs_argv_error(ctx, highlight);
va_list args;
va_start(args, format);
- errno = error;
bfs_verror(ctx, format, args);
va_end(args);
}
@@ -308,16 +193,14 @@ static void parse_conflict_error(const struct parser_state *state, char **argv1,
/**
* Print an error about an expression.
*/
-BFS_FORMATTER(3, 4)
-static void parse_expr_error(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_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;
bfs_expr_error(ctx, expr);
va_list args;
va_start(args, format);
- errno = error;
bfs_verror(ctx, format, args);
va_end(args);
}
@@ -325,22 +208,20 @@ static void parse_expr_error(const struct parser_state *state, const struct bfs_
/**
* Print a warning message during parsing.
*/
-BFS_FORMATTER(2, 3)
-static bool parse_warning(const struct parser_state *state, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_printf(2, 3)
+static bool parse_warning(const struct bfs_parser *parser, const char *format, ...) {
+ const struct bfs_ctx *ctx = parser->ctx;
bool highlight[ctx->argc];
init_highlight(ctx, highlight);
- highlight_args(ctx, state->argv, 1, highlight);
+ highlight_args(ctx, parser->argv, 1, highlight);
if (!bfs_argv_warning(ctx, highlight)) {
return false;
}
va_list args;
va_start(args, format);
- errno = error;
- bool ret = bfs_vwarning(state->ctx, format, args);
+ bool ret = bfs_vwarning(parser->ctx, format, args);
va_end(args);
return ret;
}
@@ -348,22 +229,20 @@ static bool parse_warning(const struct parser_state *state, const char *format,
/**
* Print a warning about conflicting command line arguments.
*/
-BFS_FORMATTER(6, 7)
-static bool parse_conflict_warning(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_printf(4, 5)
+static bool parse_conflict_warning(const struct bfs_parser *parser, const struct bfs_expr *expr1, const struct bfs_expr *expr2, const char *format, ...) {
+ const struct bfs_ctx *ctx = parser->ctx;
bool highlight[ctx->argc];
init_highlight(ctx, highlight);
- highlight_args(ctx, argv1, argc1, highlight);
- highlight_args(ctx, argv2, argc2, highlight);
+ highlight_args(ctx, expr1->argv, expr1->argc, highlight);
+ highlight_args(ctx, expr2->argv, expr2->argc, highlight);
if (!bfs_argv_warning(ctx, highlight)) {
return false;
}
va_list args;
va_start(args, format);
- errno = error;
bool ret = bfs_vwarning(ctx, format, args);
va_end(args);
return ret;
@@ -372,10 +251,9 @@ static bool parse_conflict_warning(const struct parser_state *state, char **argv
/**
* Print a warning about an expression.
*/
-BFS_FORMATTER(3, 4)
-static bool parse_expr_warning(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) {
- int error = errno;
- const struct bfs_ctx *ctx = state->ctx;
+_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;
if (!bfs_expr_warning(ctx, expr)) {
return false;
@@ -383,25 +261,79 @@ static bool parse_expr_warning(const struct parser_state *state, const struct bf
va_list args;
va_start(args, format);
- errno = error;
bool ret = bfs_vwarning(ctx, format, args);
va_end(args);
return ret;
}
/**
+ * Report an error if stdin is already consumed, then consume it.
+ */
+static bool consume_stdin(struct bfs_parser *parser, const struct bfs_expr *expr) {
+ if (parser->stdin_expr) {
+ parse_conflict_error(parser, parser->stdin_expr, expr,
+ "%pX and %pX can't both use standard input.\n",
+ parser->stdin_expr, expr);
+ return false;
+ }
+
+ parser->stdin_expr = expr;
+ return true;
+}
+
+/**
+ * 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, 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()");
+ }
+ return expr;
+}
+
+/**
+ * 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, BFS_OPERATOR);
+ if (!expr) {
+ return NULL;
+ }
+
+ bfs_assert(bfs_expr_is_parent(expr));
+ bfs_expr_append(expr, rhs);
+ return expr;
+}
+
+/**
+ * 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, BFS_OPERATOR);
+ if (!expr) {
+ return NULL;
+ }
+
+ bfs_assert(bfs_expr_is_parent(expr));
+ bfs_expr_append(expr, lhs);
+ bfs_expr_append(expr, rhs);
+ return expr;
+}
+
+/**
* Fill in a "-print"-type expression.
*/
-static void init_print_expr(struct parser_state *state, struct bfs_expr *expr) {
- expr->cfile = state->ctx->cout;
+static void init_print_expr(struct bfs_parser *parser, struct bfs_expr *expr) {
+ expr->cfile = parser->ctx->cout;
expr->path = NULL;
}
/**
* Open a file for an expression.
*/
-static int expr_open(struct parser_state *state, struct bfs_expr *expr, const char *path) {
- struct bfs_ctx *ctx = state->ctx;
+static int expr_open(struct bfs_parser *parser, struct bfs_expr *expr, const char *path) {
+ struct bfs_ctx *ctx = parser->ctx;
FILE *file = NULL;
CFILE *cfile = NULL;
@@ -411,7 +343,7 @@ static int expr_open(struct parser_state *state, struct bfs_expr *expr, const ch
goto fail;
}
- cfile = cfwrap(file, state->use_color ? ctx->colors : NULL, true);
+ cfile = cfwrap(file, parser->use_color ? ctx->colors : NULL, true);
if (!cfile) {
goto fail;
}
@@ -430,7 +362,7 @@ static int expr_open(struct parser_state *state, struct bfs_expr *expr, const ch
return 0;
fail:
- parse_expr_error(state, expr, "%m.\n");
+ parse_expr_error(parser, expr, "%s.\n", errstr());
if (cfile) {
cfclose(cfile);
} else if (file) {
@@ -442,15 +374,15 @@ fail:
/**
* Invoke bfs_stat() on an argument.
*/
-static int stat_arg(const struct parser_state *state, char **arg, struct bfs_stat *sb) {
- const struct bfs_ctx *ctx = state->ctx;
+static int stat_arg(const struct bfs_parser *parser, char **arg, struct bfs_stat *sb) {
+ const struct bfs_ctx *ctx = parser->ctx;
bool follow = ctx->flags & (BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL);
enum bfs_stat_flags flags = follow ? BFS_STAT_TRYFOLLOW : BFS_STAT_NOFOLLOW;
int ret = bfs_stat(AT_FDCWD, *arg, flags, sb);
if (ret != 0) {
- parse_argv_error(state, arg, 1, "%m.\n");
+ parse_argv_error(parser, arg, 1, "%s.\n", errstr());
}
return ret;
}
@@ -458,52 +390,57 @@ static int stat_arg(const struct parser_state *state, char **arg, struct bfs_sta
/**
* Parse the expression specified on the command line.
*/
-static struct bfs_expr *parse_expr(struct parser_state *state);
+static struct bfs_expr *parse_expr(struct bfs_parser *parser);
/**
* Advance by a single token.
*/
-static char **parser_advance(struct parser_state *state, enum token_type type, size_t argc) {
- if (type != T_FLAG && type != T_PATH) {
- state->expr_started = true;
+static char **parser_advance(struct bfs_parser *parser, enum bfs_kind kind, size_t argc) {
+ struct bfs_ctx *ctx = parser->ctx;
+
+ if (kind != BFS_FLAG && kind != BFS_PATH) {
+ parser->expr_started = true;
}
- if (type != T_PATH) {
- state->last_arg = state->argv;
+ if (kind != BFS_PATH) {
+ parser->last_arg = parser->argv;
}
- char **argv = state->argv;
- state->argv += argc;
+ size_t i = parser->argv - ctx->argv;
+ ctx->kinds[i] = kind;
+
+ char **argv = parser->argv;
+ parser->argv += argc;
return argv;
}
/**
* Parse a root path.
*/
-static int parse_root(struct parser_state *state, const char *path) {
- char *copy = strdup(path);
- if (!copy) {
- parse_perror(state, "strdup()");
+static int parse_root(struct bfs_parser *parser, const char *path) {
+ struct bfs_ctx *ctx = parser->ctx;
+ const char **root = RESERVE(const char *, &ctx->paths, &ctx->npaths);
+ if (!root) {
+ parse_perror(parser, "RESERVE()");
return -1;
}
- struct bfs_ctx *ctx = state->ctx;
- if (DARRAY_PUSH(&ctx->paths, &copy) != 0) {
- parse_perror(state, "DARRAY_PUSH()");
- free(copy);
+ *root = strdup(path);
+ if (!*root) {
+ --ctx->npaths;
+ parse_perror(parser, "strdup()");
return -1;
}
- state->implicit_root = false;
return 0;
}
/**
* While parsing an expression, skip any paths and add them to ctx->paths.
*/
-static int skip_paths(struct parser_state *state) {
+static int skip_paths(struct bfs_parser *parser) {
while (true) {
- const char *arg = state->argv[0];
+ const char *arg = parser->argv[0];
if (!arg) {
return 0;
}
@@ -513,7 +450,7 @@ static int skip_paths(struct parser_state *state) {
// 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(state, T_FLAG, 1);
+ parser_advance(parser, BFS_FLAG, 1);
continue;
}
if (strcmp(arg, "-") != 0) {
@@ -528,7 +465,7 @@ static int skip_paths(struct parser_state *state) {
return 0;
}
- if (state->expr_started) {
+ if (parser->expr_started) {
// 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) {
@@ -536,16 +473,16 @@ static int skip_paths(struct parser_state *state) {
}
}
- if (state->excluding) {
- parse_warning(state, "This path will not be excluded. Use a test like ${blu}-name${rs} or ${blu}-path${rs}\n");
- bfs_warning(state->ctx, "within ${red}-exclude${rs} to exclude matching files.\n\n");
+ if (parser->excluding) {
+ parse_warning(parser, "This path will not be excluded. Use a test like ${blu}-name${rs} or ${blu}-path${rs}\n");
+ bfs_warning(parser->ctx, "within ${red}-exclude${rs} to exclude matching files.\n\n");
}
- if (parse_root(state, arg) != 0) {
+ if (parse_root(parser, arg) != 0) {
return -1;
}
- parser_advance(state, T_PATH, 1);
+ parser_advance(parser, BFS_PATH, 1);
}
}
@@ -564,17 +501,15 @@ enum int_flags {
/**
* Parse an integer.
*/
-static const char *parse_int(const struct parser_state *state, char **arg, const char *str, void *result, enum int_flags flags) {
- char *endptr;
-
+static const char *parse_int(const struct bfs_parser *parser, char **arg, const char *str, void *result, enum int_flags flags) {
int base = flags & IF_BASE_MASK;
if (base == 0) {
base = 10;
}
- errno = 0;
- long long value = strtoll(str, &endptr, base);
- if (errno != 0) {
+ char *endptr;
+ long long value;
+ if (xstrtoll(str, &endptr, base, &value) != 0) {
if (errno == ERANGE) {
goto range;
} else {
@@ -582,13 +517,6 @@ static const char *parse_int(const struct parser_state *state, 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;
}
@@ -625,19 +553,19 @@ static const char *parse_int(const struct parser_state *state, char **arg, const
bad:
if (!(flags & IF_QUIET)) {
- parse_argv_error(state, arg, 1, "${bld}%pq${rs} is not a valid integer.\n", str);
+ parse_argv_error(parser, arg, 1, "${bld}%pq${rs} is not a valid integer.\n", str);
}
return NULL;
negative:
if (!(flags & IF_QUIET)) {
- parse_argv_error(state, arg, 1, "Negative integer ${bld}%pq${rs} is not allowed here.\n", str);
+ parse_argv_error(parser, arg, 1, "Negative integer ${bld}%pq${rs} is not allowed here.\n", str);
}
return NULL;
range:
if (!(flags & IF_QUIET)) {
- parse_argv_error(state, arg, 1, "${bld}%pq${rs} is too large an integer.\n", str);
+ parse_argv_error(parser, arg, 1, "${bld}%pq${rs} is too large an integer.\n", str);
}
return NULL;
}
@@ -645,7 +573,7 @@ range:
/**
* Parse an integer and a comparison flag.
*/
-static const char *parse_icmp(const struct parser_state *state, struct bfs_expr *expr, enum int_flags flags) {
+static const char *parse_icmp(const struct bfs_parser *parser, struct bfs_expr *expr, enum int_flags flags) {
char **arg = &expr->argv[1];
const char *str = *arg;
switch (str[0]) {
@@ -662,7 +590,7 @@ static const char *parse_icmp(const struct parser_state *state, struct bfs_expr
break;
}
- return parse_int(state, arg, str, &expr->num, flags | IF_LONG_LONG | IF_UNSIGNED);
+ return parse_int(parser, arg, str, &expr->num, flags | IF_LONG_LONG | IF_UNSIGNED);
}
/**
@@ -684,153 +612,164 @@ static bool looks_like_icmp(const char *str) {
/**
* Parse a single flag.
*/
-static struct bfs_expr *parse_flag(struct parser_state *state, size_t argc) {
- char **argv = parser_advance(state, T_FLAG, argc);
- return bfs_expr_new(eval_true, argc, argv);
+static struct bfs_expr *parse_flag(struct bfs_parser *parser, size_t argc) {
+ char **argv = parser_advance(parser, BFS_FLAG, argc);
+ return parse_new_expr(parser, eval_true, argc, argv, BFS_FLAG);
}
/**
* Parse a flag that doesn't take a value.
*/
-static struct bfs_expr *parse_nullary_flag(struct parser_state *state) {
- return parse_flag(state, 1);
+static struct bfs_expr *parse_nullary_flag(struct bfs_parser *parser) {
+ return parse_flag(parser, 1);
}
/**
* Parse a flag that takes a value.
*/
-static struct bfs_expr *parse_unary_flag(struct parser_state *state) {
- const char *arg = state->argv[0];
- const char *value = state->argv[1];
+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(state, "${cyn}%s${rs} needs a value.\n", arg);
+ parse_error(parser, "${cyn}-%c${rs} needs a value.\n", flag);
+ return NULL;
+ }
+
+ return parse_flag(parser, 2);
+}
+
+/**
+ * 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_flag(state, 2);
+ return parse_unary_flag(parser);
}
/**
* Parse a single option.
*/
-static struct bfs_expr *parse_option(struct parser_state *state, size_t argc) {
- char **argv = parser_advance(state, T_OPTION, argc);
- return bfs_expr_new(eval_true, argc, argv);
+static struct bfs_expr *parse_option(struct bfs_parser *parser, size_t argc) {
+ char **argv = parser_advance(parser, BFS_OPTION, argc);
+ return parse_new_expr(parser, eval_true, argc, argv, BFS_OPTION);
}
/**
* Parse an option that doesn't take a value.
*/
-static struct bfs_expr *parse_nullary_option(struct parser_state *state) {
- return parse_option(state, 1);
+static struct bfs_expr *parse_nullary_option(struct bfs_parser *parser) {
+ return parse_option(parser, 1);
}
/**
* Parse an option that takes a value.
*/
-static struct bfs_expr *parse_unary_option(struct parser_state *state) {
- const char *arg = state->argv[0];
- const char *value = state->argv[1];
+static struct bfs_expr *parse_unary_option(struct bfs_parser *parser) {
+ const char *arg = parser->argv[0];
+ const char *value = parser->argv[1];
if (!value) {
- parse_error(state, "${blu}%s${rs} needs a value.\n", arg);
+ parse_error(parser, "${blu}%s${rs} needs a value.\n", arg);
return NULL;
}
- return parse_option(state, 2);
+ return parse_option(parser, 2);
}
/**
* Parse a single test.
*/
-static struct bfs_expr *parse_test(struct parser_state *state, bfs_eval_fn *eval_fn, size_t argc) {
- char **argv = parser_advance(state, T_TEST, argc);
- return bfs_expr_new(eval_fn, argc, argv);
+static struct bfs_expr *parse_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) {
+ char **argv = parser_advance(parser, BFS_TEST, argc);
+ return parse_new_expr(parser, eval_fn, argc, argv, BFS_TEST);
}
/**
* Parse a test that doesn't take a value.
*/
-static struct bfs_expr *parse_nullary_test(struct parser_state *state, bfs_eval_fn *eval_fn) {
- return parse_test(state, eval_fn, 1);
+static struct bfs_expr *parse_nullary_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn) {
+ return parse_test(parser, eval_fn, 1);
}
/**
* Parse a test that takes a value.
*/
-static struct bfs_expr *parse_unary_test(struct parser_state *state, bfs_eval_fn *eval_fn) {
- const char *arg = state->argv[0];
- const char *value = state->argv[1];
+static struct bfs_expr *parse_unary_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn) {
+ const char *arg = parser->argv[0];
+ const char *value = parser->argv[1];
if (!value) {
- parse_error(state, "${blu}%s${rs} needs a value.\n", arg);
+ parse_error(parser, "${blu}%s${rs} needs a value.\n", arg);
return NULL;
}
- return parse_test(state, eval_fn, 2);
+ return parse_test(parser, eval_fn, 2);
}
/**
* Parse a single action.
*/
-static struct bfs_expr *parse_action(struct parser_state *state, bfs_eval_fn *eval_fn, size_t argc) {
- char **argv = parser_advance(state, T_ACTION, argc);
+static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) {
+ char **argv = parser_advance(parser, BFS_ACTION, argc);
- if (state->excluding) {
- parse_argv_error(state, argv, argc, "This action is not supported within ${red}-exclude${rs}.\n");
+ if (parser->excluding) {
+ parse_argv_error(parser, argv, argc, "This action is not supported within ${red}-exclude${rs}.\n");
return NULL;
}
- if (eval_fn != eval_prune && eval_fn != eval_quit) {
- state->implicit_print = false;
+ if (eval_fn != eval_limit && eval_fn != eval_prune && eval_fn != eval_quit) {
+ parser->implicit_print = false;
}
- return bfs_expr_new(eval_fn, argc, argv);
+ return parse_new_expr(parser, eval_fn, argc, argv, BFS_ACTION);
}
/**
* Parse an action that takes no arguments.
*/
-static struct bfs_expr *parse_nullary_action(struct parser_state *state, bfs_eval_fn *eval_fn) {
- return parse_action(state, eval_fn, 1);
+static struct bfs_expr *parse_nullary_action(struct bfs_parser *parser, bfs_eval_fn *eval_fn) {
+ return parse_action(parser, eval_fn, 1);
}
/**
* Parse an action that takes one argument.
*/
-static struct bfs_expr *parse_unary_action(struct parser_state *state, bfs_eval_fn *eval_fn) {
- const char *arg = state->argv[0];
- const char *value = state->argv[1];
+static struct bfs_expr *parse_unary_action(struct bfs_parser *parser, bfs_eval_fn *eval_fn) {
+ const char *arg = parser->argv[0];
+ const char *value = parser->argv[1];
if (!value) {
- parse_error(state, "${blu}%s${rs} needs a value.\n", arg);
+ parse_error(parser, "${blu}%s${rs} needs a value.\n", arg);
return NULL;
}
- return parse_action(state, eval_fn, 2);
-}
-
-/**
- * Add an expression to the exclusions.
- */
-static int parse_exclude(struct parser_state *state, struct bfs_expr *expr) {
- struct bfs_ctx *ctx = state->ctx;
- ctx->exclude = new_binary_expr(eval_or, ctx->exclude, expr, &fake_or_arg);
- if (ctx->exclude) {
- return 0;
- } else {
- return -1;
- }
+ return parse_action(parser, eval_fn, 2);
}
/**
* Parse a test expression with integer data and a comparison flag.
*/
-static struct bfs_expr *parse_test_icmp(struct parser_state *state, bfs_eval_fn *eval_fn) {
- struct bfs_expr *expr = parse_unary_test(state, eval_fn);
+static struct bfs_expr *parse_test_icmp(struct bfs_parser *parser, bfs_eval_fn *eval_fn) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_fn);
if (!expr) {
return NULL;
}
- if (!parse_icmp(state, expr, 0)) {
- bfs_expr_free(expr);
+ if (!parse_icmp(parser, expr, 0)) {
return NULL;
}
@@ -866,10 +805,11 @@ static bool parse_debug_flag(const char *flag, size_t len, const char *expected)
/**
* Parse -D FLAG.
*/
-static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int arg2) {
- struct bfs_ctx *ctx = state->ctx;
+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(state);
+ const char *flags;
+ struct bfs_expr *expr = parse_prefix_flag(parser, 'D', true, &flags);
if (!expr) {
cfprintf(ctx->cerr, "\n");
debug_help(ctx->cerr);
@@ -878,7 +818,7 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar
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;
@@ -888,8 +828,7 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar
if (parse_debug_flag(flag, len, "help")) {
debug_help(ctx->cout);
- state->just_info = true;
- bfs_expr_free(expr);
+ parser->just_info = true;
return NULL;
} else if (parse_debug_flag(flag, len, "all")) {
ctx->debug = DEBUG_ALL;
@@ -907,7 +846,7 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar
if (DEBUG_ALL & i) {
ctx->debug |= i;
} else {
- if (parse_expr_warning(state, expr, "Unrecognized debug flag ${bld}")) {
+ if (parse_expr_warning(parser, expr, "Unrecognized debug flag ${bld}")) {
fwrite(flag, 1, len, stderr);
cfprintf(ctx->cerr, "${rs}.\n\n");
unrecognized = true;
@@ -926,23 +865,23 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar
/**
* Parse -On.
*/
-static struct bfs_expr *parse_optlevel(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_flag(state);
+static struct bfs_expr *parse_optlevel(struct bfs_parser *parser, int arg1, int arg2) {
+ const char *arg;
+ struct bfs_expr *expr = parse_prefix_flag(parser, 'O', false, &arg);
if (!expr) {
return NULL;
}
- int *optlevel = &state->ctx->optlevel;
+ int *optlevel = &parser->ctx->optlevel;
- if (strcmp(expr->argv[0], "-Ofast") == 0) {
+ if (strcmp(arg, "fast") == 0) {
*optlevel = 4;
- } else if (!parse_int(state, expr->argv, expr->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) {
- bfs_expr_free(expr);
+ } else if (!parse_int(parser, expr->argv, arg, optlevel, IF_INT | IF_UNSIGNED)) {
return NULL;
}
if (*optlevel > 4) {
- parse_expr_warning(state, 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;
@@ -951,30 +890,30 @@ static struct bfs_expr *parse_optlevel(struct parser_state *state, int arg1, int
/**
* Parse -[PHL], -follow.
*/
-static struct bfs_expr *parse_follow(struct parser_state *state, int flags, int option) {
- struct bfs_ctx *ctx = state->ctx;
+static struct bfs_expr *parse_follow(struct bfs_parser *parser, int flags, int option) {
+ struct bfs_ctx *ctx = parser->ctx;
ctx->flags &= ~(BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL);
ctx->flags |= flags;
if (option) {
- return parse_nullary_option(state);
+ return parse_nullary_option(parser);
} else {
- return parse_nullary_flag(state);
+ return parse_nullary_flag(parser);
}
}
/**
* Parse -X.
*/
-static struct bfs_expr *parse_xargs_safe(struct parser_state *state, int arg1, int arg2) {
- state->ctx->xargs_safe = true;
- return parse_nullary_flag(state);
+static struct bfs_expr *parse_xargs_safe(struct bfs_parser *parser, int arg1, int arg2) {
+ parser->ctx->xargs_safe = true;
+ return parse_nullary_flag(parser);
}
/**
* Parse -executable, -readable, -writable
*/
-static struct bfs_expr *parse_access(struct parser_state *state, int flag, int arg2) {
- struct bfs_expr *expr = parse_nullary_test(state, eval_access);
+static struct bfs_expr *parse_access(struct bfs_parser *parser, int flag, int arg2) {
+ struct bfs_expr *expr = parse_nullary_test(parser, eval_access);
if (expr) {
expr->num = flag;
}
@@ -984,11 +923,11 @@ static struct bfs_expr *parse_access(struct parser_state *state, int flag, int a
/**
* Parse -acl.
*/
-static struct bfs_expr *parse_acl(struct parser_state *state, int flag, int arg2) {
+static struct bfs_expr *parse_acl(struct bfs_parser *parser, int flag, int arg2) {
#if BFS_CAN_CHECK_ACL
- return parse_nullary_test(state, eval_acl);
+ return parse_nullary_test(parser, eval_acl);
#else
- parse_error(state, "Missing platform support.\n");
+ parse_error(parser, "Missing platform support.\n");
return NULL;
#endif
}
@@ -996,36 +935,32 @@ static struct bfs_expr *parse_acl(struct parser_state *state, int flag, int arg2
/**
* Parse -[aBcm]?newer.
*/
-static struct bfs_expr *parse_newer(struct parser_state *state, int field, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_newer);
+static struct bfs_expr *parse_newer(struct bfs_parser *parser, int field, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_newer);
if (!expr) {
return NULL;
}
struct bfs_stat sb;
- if (stat_arg(state, &expr->argv[1], &sb) != 0) {
- goto fail;
+ if (stat_arg(parser, &expr->argv[1], &sb) != 0) {
+ return NULL;
}
expr->reftime = sb.mtime;
expr->stat_field = field;
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -[aBcm]min.
*/
-static struct bfs_expr *parse_min(struct parser_state *state, int field, int arg2) {
- struct bfs_expr *expr = parse_test_icmp(state, eval_time);
+static struct bfs_expr *parse_min(struct bfs_parser *parser, int field, int arg2) {
+ struct bfs_expr *expr = parse_test_icmp(parser, eval_time);
if (!expr) {
return NULL;
}
- expr->reftime = state->now;
+ expr->reftime = parser->now;
expr->stat_field = field;
expr->time_unit = BFS_MINUTES;
return expr;
@@ -1034,18 +969,18 @@ static struct bfs_expr *parse_min(struct parser_state *state, int field, int arg
/**
* Parse -[aBcm]time.
*/
-static struct bfs_expr *parse_time(struct parser_state *state, int field, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_time);
+static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_time);
if (!expr) {
return NULL;
}
- expr->reftime = state->now;
+ expr->reftime = parser->now;
expr->stat_field = field;
- const char *tail = parse_icmp(state, expr, IF_PARTIAL_OK);
+ const char *tail = parse_icmp(parser, expr, IF_PARTIAL_OK);
if (!tail) {
- goto fail;
+ return NULL;
}
if (!*tail) {
@@ -1060,21 +995,21 @@ static struct bfs_expr *parse_time(struct parser_state *state, int field, int ar
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:
- parse_expr_error(state, expr, "Unknown time unit ${bld}%c${rs}.\n", *tail);
- goto fail;
+ parse_expr_error(parser, expr, "Unknown time unit ${bld}%c${rs}.\n", *tail);
+ return NULL;
}
expr->num += time;
@@ -1083,32 +1018,28 @@ static struct bfs_expr *parse_time(struct parser_state *state, int field, int ar
break;
}
- tail = parse_int(state, &expr->argv[1], tail, &time, IF_PARTIAL_OK | IF_LONG_LONG | IF_UNSIGNED);
+ tail = parse_int(parser, &expr->argv[1], tail, &time, IF_PARTIAL_OK | IF_LONG_LONG | IF_UNSIGNED);
if (!tail) {
- goto fail;
+ return NULL;
}
if (!*tail) {
- parse_expr_error(state, expr, "Missing time unit.\n");
- goto fail;
+ parse_expr_error(parser, expr, "Missing time unit.\n");
+ return NULL;
}
}
expr->time_unit = BFS_SECONDS;
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -capable.
*/
-static struct bfs_expr *parse_capable(struct parser_state *state, int flag, int arg2) {
+static struct bfs_expr *parse_capable(struct bfs_parser *parser, int flag, int arg2) {
#if BFS_CAN_CHECK_CAPABILITIES
- return parse_nullary_test(state, eval_capable);
+ return parse_nullary_test(parser, eval_capable);
#else
- parse_error(state, "Missing platform support.\n");
+ parse_error(parser, "Missing platform support.\n");
return NULL;
#endif
}
@@ -1116,27 +1047,26 @@ static struct bfs_expr *parse_capable(struct parser_state *state, int flag, int
/**
* Parse -(no)?color.
*/
-static struct bfs_expr *parse_color(struct parser_state *state, int color, int arg2) {
- struct bfs_expr *expr = parse_nullary_option(state);
+static struct bfs_expr *parse_color(struct bfs_parser *parser, int color, int arg2) {
+ struct bfs_expr *expr = parse_nullary_option(parser);
if (!expr) {
return NULL;
}
- struct bfs_ctx *ctx = state->ctx;
+ struct bfs_ctx *ctx = parser->ctx;
struct colors *colors = ctx->colors;
if (color) {
if (!colors) {
- parse_expr_error(state, expr, "Error parsing $$LS_COLORS: %s.\n", strerror(ctx->colors_error));
- bfs_expr_free(expr);
+ parse_expr_error(parser, expr, "Error parsing $$LS_COLORS: %s.\n", xstrerror(ctx->colors_error));
return NULL;
}
- state->use_color = COLOR_ALWAYS;
+ parser->use_color = COLOR_ALWAYS;
ctx->cout->colors = colors;
ctx->cerr->colors = colors;
} else {
- state->use_color = COLOR_NEVER;
+ parser->use_color = COLOR_NEVER;
ctx->cout->colors = NULL;
ctx->cerr->colors = NULL;
}
@@ -1145,23 +1075,84 @@ static struct bfs_expr *parse_color(struct parser_state *state, int color, int a
}
/**
+ * Common code for fnmatch() tests.
+ */
+static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bfs_expr *expr, bool casefold) {
+ if (!expr) {
+ return NULL;
+ }
+
+ expr->pattern = expr->argv[1];
+
+ if (casefold) {
+#ifdef FNM_CASEFOLD
+ expr->fnm_flags = FNM_CASEFOLD;
+#else
+ parse_expr_error(parser, expr, "Missing platform support.\n");
+ return NULL;
+#endif
+ } else {
+ expr->fnm_flags = 0;
+ }
+
+ // POSIX says, about fnmatch():
+ //
+ // If pattern ends with an unescaped <backslash>, fnmatch() shall
+ // return a non-zero value (indicating either no match or an error).
+ //
+ // But not all implementations obey this, so check for it ourselves.
+ size_t i, len = strlen(expr->pattern);
+ for (i = 0; i < len; ++i) {
+ if (expr->pattern[len - i - 1] != '\\') {
+ break;
+ }
+ }
+ if (i % 2 != 0) {
+ parse_expr_warning(parser, expr, "Unescaped trailing backslash.\n\n");
+ expr->eval_fn = eval_false;
+ return expr;
+ }
+
+ // 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/9799919799/utilities/V3_chap02.html#tag_19_14_01
+ expr->literal = strcspn(expr->pattern, "?*\\[") == len;
+
+ return expr;
+}
+
+/**
+ * Parse -context.
+ */
+static struct bfs_expr *parse_context(struct bfs_parser *parser, int flag, int arg2) {
+#if BFS_CAN_CHECK_CONTEXT
+ struct bfs_expr *expr = parse_unary_test(parser, eval_context);
+ return parse_fnmatch(parser, expr, false);
+#else
+ parse_error(parser, "Missing platform support.\n");
+ return NULL;
+#endif
+}
+
+/**
* Parse -{false,true}.
*/
-static struct bfs_expr *parse_const(struct parser_state *state, int value, int arg2) {
- return parse_nullary_test(state, value ? eval_true : eval_false);
+static struct bfs_expr *parse_const(struct bfs_parser *parser, int value, int arg2) {
+ return parse_nullary_test(parser, value ? eval_true : eval_false);
}
/**
* Parse -daystart.
*/
-static struct bfs_expr *parse_daystart(struct parser_state *state, int arg1, int arg2) {
+static struct bfs_expr *parse_daystart(struct bfs_parser *parser, int arg1, int arg2) {
struct tm tm;
- if (xlocaltime(&state->now.tv_sec, &tm) != 0) {
- parse_perror(state, "xlocaltime()");
+ if (!localtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "localtime_r()");
return NULL;
}
- if (tm.tm_hour || tm.tm_min || tm.tm_sec || state->now.tv_nsec) {
+ if (tm.tm_hour || tm.tm_min || tm.tm_sec || parser->now.tv_nsec) {
++tm.tm_mday;
}
tm.tm_hour = 0;
@@ -1170,60 +1161,74 @@ static struct bfs_expr *parse_daystart(struct parser_state *state, int arg1, int
time_t time;
if (xmktime(&tm, &time) != 0) {
- parse_perror(state, "xmktime()");
+ parse_perror(parser, "xmktime()");
return NULL;
}
- state->now.tv_sec = time;
- state->now.tv_nsec = 0;
+ parser->now.tv_sec = time;
+ parser->now.tv_nsec = 0;
- return parse_nullary_option(state);
+ return parse_nullary_option(parser);
}
/**
* Parse -delete.
*/
-static struct bfs_expr *parse_delete(struct parser_state *state, int arg1, int arg2) {
- state->ctx->flags |= BFTW_POST_ORDER;
- state->depth_arg = state->argv;
- return parse_nullary_action(state, eval_delete);
+static struct bfs_expr *parse_delete(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_delete);
+ if (!expr) {
+ return NULL;
+ }
+
+ struct bfs_ctx *ctx = parser->ctx;
+ ctx->flags |= BFTW_POST_ORDER;
+ ctx->dangerous = true;
+
+ parser->depth_expr = expr;
+ return expr;
}
/**
* Parse -d.
*/
-static struct bfs_expr *parse_depth(struct parser_state *state, int arg1, int arg2) {
- state->ctx->flags |= BFTW_POST_ORDER;
- state->depth_arg = state->argv;
- return parse_nullary_flag(state);
+static struct bfs_expr *parse_depth(struct bfs_parser *parser, int flag, int arg2) {
+ struct bfs_expr *expr = flag
+ ? parse_nullary_flag(parser)
+ : parse_nullary_option(parser);
+ if (!expr) {
+ return NULL;
+ }
+
+ parser->ctx->flags |= BFTW_POST_ORDER;
+ parser->depth_expr = expr;
+ return expr;
}
/**
* Parse -depth [N].
*/
-static struct bfs_expr *parse_depth_n(struct parser_state *state, int arg1, int arg2) {
- const char *arg = state->argv[1];
+static struct bfs_expr *parse_depth_n(struct bfs_parser *parser, int arg1, int arg2) {
+ const char *arg = parser->argv[1];
if (arg && looks_like_icmp(arg)) {
- return parse_test_icmp(state, eval_depth);
+ return parse_test_icmp(parser, eval_depth);
} else {
- return parse_depth(state, arg1, arg2);
+ return parse_depth(parser, arg1, arg2);
}
}
/**
* Parse -{min,max}depth N.
*/
-static struct bfs_expr *parse_depth_limit(struct parser_state *state, int is_min, int arg2) {
- struct bfs_expr *expr = parse_unary_option(state);
+static struct bfs_expr *parse_depth_limit(struct bfs_parser *parser, int is_min, int arg2) {
+ struct bfs_expr *expr = parse_unary_option(parser);
if (!expr) {
return NULL;
}
- struct bfs_ctx *ctx = state->ctx;
+ struct bfs_ctx *ctx = parser->ctx;
int *depth = is_min ? &ctx->mindepth : &ctx->maxdepth;
char **arg = &expr->argv[1];
- if (!parse_int(state, arg, *arg, depth, IF_INT | IF_UNSIGNED)) {
- bfs_expr_free(expr);
+ if (!parse_int(parser, arg, *arg, depth, IF_INT | IF_UNSIGNED)) {
return NULL;
}
@@ -1233,8 +1238,8 @@ static struct bfs_expr *parse_depth_limit(struct parser_state *state, int is_min
/**
* Parse -empty.
*/
-static struct bfs_expr *parse_empty(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_test(state, eval_empty);
+static struct bfs_expr *parse_empty(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_test(parser, eval_empty);
if (expr) {
// For opendir()
expr->ephemeral_fds = 1;
@@ -1245,13 +1250,15 @@ static struct bfs_expr *parse_empty(struct parser_state *state, int arg1, int ar
/**
* Parse -exec(dir)?/-ok(dir)?.
*/
-static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int arg2) {
- struct bfs_exec *execbuf = bfs_exec_parse(state->ctx, state->argv, flags);
+static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg2) {
+ struct bfs_ctx *ctx = parser->ctx;
+
+ struct bfs_exec *execbuf = bfs_exec_parse(ctx, parser->argv, flags);
if (!execbuf) {
return NULL;
}
- struct bfs_expr *expr = parse_action(state, eval_exec, execbuf->tmpl_argc + 2);
+ struct bfs_expr *expr = parse_action(parser, eval_exec, execbuf->tmpl_argc + 2);
if (!expr) {
bfs_exec_free(execbuf);
return NULL;
@@ -1263,6 +1270,28 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar
expr->ephemeral_fds = 2;
if (execbuf->flags & BFS_EXEC_CHDIR) {
+ // Check for relative paths in $PATH
+ const char *path = getenv("PATH");
+ while (path) {
+ if (*path != '/') {
+ size_t len = strcspn(path, ":");
+ char *comp = strndup(path, len);
+ if (comp) {
+ parse_expr_error(parser, expr,
+ "This action would be unsafe, since ${bld}$$PATH${rs} contains the relative path ${bld}%pq${rs}\n", comp);
+ free(comp);
+ } else {
+ parse_perror(parser, "strndup()");
+ }
+ return NULL;
+ }
+
+ path = strchr(path, ':');
+ if (path) {
+ ++path;
+ }
+ }
+
// To dup() the parent directory
if (execbuf->flags & BFS_EXEC_MULTI) {
++expr->persistent_fds;
@@ -1272,7 +1301,11 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar
}
if (execbuf->flags & BFS_EXEC_CONFIRM) {
- state->ok_expr = expr;
+ if (!consume_stdin(parser, expr)) {
+ return NULL;
+ }
+ } else {
+ ctx->dangerous = true;
}
return expr;
@@ -1281,16 +1314,16 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar
/**
* Parse -exit [STATUS].
*/
-static struct bfs_expr *parse_exit(struct parser_state *state, int arg1, int arg2) {
+static struct bfs_expr *parse_exit(struct bfs_parser *parser, int arg1, int arg2) {
size_t argc = 1;
- const char *value = state->argv[1];
+ const char *value = parser->argv[1];
int status = EXIT_SUCCESS;
- if (value && parse_int(state, NULL, value, &status, IF_INT | IF_UNSIGNED | IF_QUIET)) {
+ if (value && parse_int(parser, NULL, value, &status, IF_INT | IF_UNSIGNED | IF_QUIET)) {
argc = 2;
}
- struct bfs_expr *expr = parse_action(state, eval_exit, argc);
+ struct bfs_expr *expr = parse_action(parser, eval_exit, argc);
if (expr) {
expr->num = status;
}
@@ -1300,14 +1333,19 @@ static struct bfs_expr *parse_exit(struct parser_state *state, int arg1, int arg
/**
* Parse -f PATH.
*/
-static struct bfs_expr *parse_f(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_flag(state);
+static struct bfs_expr *parse_f(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_ctx *ctx = parser->ctx;
+
+ struct bfs_expr *expr = parse_unary_flag(parser);
if (!expr) {
return NULL;
}
- if (parse_root(state, expr->argv[1]) != 0) {
- bfs_expr_free(expr);
+ // Mark the path as a path, not a regular argument
+ size_t i = expr->argv - ctx->argv;
+ ctx->kinds[i + 1] = BFS_PATH;
+
+ if (parse_root(parser, expr->argv[1]) != 0) {
return NULL;
}
@@ -1317,64 +1355,27 @@ static struct bfs_expr *parse_f(struct parser_state *state, int arg1, int arg2)
/**
* Parse -files0-from PATH.
*/
-static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_option(state);
+static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_option(parser);
if (!expr) {
return NULL;
}
- const char *from = expr->argv[1];
-
- FILE *file;
- if (strcmp(from, "-") == 0) {
- file = stdin;
- } else {
- file = xfopen(from, O_RDONLY | O_CLOEXEC);
- }
- if (!file) {
- parse_expr_error(state, expr, "%m.\n");
- goto fail;
- }
-
- while (true) {
- char *path = xgetdelim(file, '\0');
- if (!path) {
- if (errno) {
- goto fail;
- } else {
- break;
- }
- }
-
- int ret = parse_root(state, path);
- free(path);
- if (ret != 0) {
- goto fail;
- }
- }
-
- if (file == stdin) {
- state->files0_stdin_arg = expr->argv;
- } else {
- fclose(file);
- }
-
- state->implicit_root = false;
+ // For compatibility with GNU find,
+ //
+ // bfs -files0-from a -files0-from b
+ //
+ // should *only* use b, not a. So stash the expression here and only
+ // process the last one at the end of parsing.
+ parser->files0_expr = expr;
return expr;
-
-fail:
- if (file && file != stdin) {
- fclose(file);
- }
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -flags FLAGS.
*/
-static struct bfs_expr *parse_flags(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_flags);
+static struct bfs_expr *parse_flags(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_flags);
if (!expr) {
return NULL;
}
@@ -1396,11 +1397,10 @@ static struct bfs_expr *parse_flags(struct parser_state *state, int arg1, int ar
if (xstrtofflags(&flags, &expr->set_flags, &expr->clear_flags) != 0) {
if (errno == ENOTSUP) {
- parse_expr_error(state, expr, "Missing platform support.\n");
+ parse_expr_error(parser, expr, "Missing platform support.\n");
} else {
- parse_expr_error(state, expr, "Invalid flags.\n");
+ parse_expr_error(parser, expr, "Invalid flags.\n");
}
- bfs_expr_free(expr);
return NULL;
}
@@ -1410,107 +1410,96 @@ static struct bfs_expr *parse_flags(struct parser_state *state, int arg1, int ar
/**
* Parse -fls FILE.
*/
-static struct bfs_expr *parse_fls(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_action(state, eval_fls);
+static struct bfs_expr *parse_fls(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_action(parser, eval_fls);
if (!expr) {
- goto fail;
+ return NULL;
}
- if (expr_open(state, expr, expr->argv[1]) != 0) {
- goto fail;
+ if (expr_open(parser, expr, expr->argv[1]) != 0) {
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -fprint FILE.
*/
-static struct bfs_expr *parse_fprint(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_action(state, eval_fprint);
- if (expr) {
- if (expr_open(state, expr, expr->argv[1]) != 0) {
- goto fail;
- }
+static struct bfs_expr *parse_fprint(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_action(parser, eval_fprint);
+ if (!expr) {
+ return NULL;
}
- return expr;
-fail:
- bfs_expr_free(expr);
- return NULL;
+ if (expr_open(parser, expr, expr->argv[1]) != 0) {
+ return NULL;
+ }
+
+ return expr;
}
/**
* Parse -fprint0 FILE.
*/
-static struct bfs_expr *parse_fprint0(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_action(state, eval_fprint0);
- if (expr) {
- if (expr_open(state, expr, expr->argv[1]) != 0) {
- goto fail;
- }
+static struct bfs_expr *parse_fprint0(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_action(parser, eval_fprint0);
+ if (!expr) {
+ return NULL;
}
- return expr;
-fail:
- bfs_expr_free(expr);
- return NULL;
+ if (expr_open(parser, expr, expr->argv[1]) != 0) {
+ return NULL;
+ }
+
+ return expr;
}
/**
* Parse -fprintf FILE FORMAT.
*/
-static struct bfs_expr *parse_fprintf(struct parser_state *state, int arg1, int arg2) {
- const char *arg = state->argv[0];
+static struct bfs_expr *parse_fprintf(struct bfs_parser *parser, int arg1, int arg2) {
+ const char *arg = parser->argv[0];
- const char *file = state->argv[1];
+ const char *file = parser->argv[1];
if (!file) {
- parse_error(state, "${blu}%s${rs} needs a file.\n", arg);
+ parse_error(parser, "${blu}%s${rs} needs a file.\n", arg);
return NULL;
}
- const char *format = state->argv[2];
+ const char *format = parser->argv[2];
if (!format) {
- parse_error(state, "${blu}%s${rs} needs a format string.\n", arg);
+ parse_error(parser, "${blu}%s${rs} needs a format string.\n", arg);
return NULL;
}
- struct bfs_expr *expr = parse_action(state, eval_fprintf, 3);
+ struct bfs_expr *expr = parse_action(parser, eval_fprintf, 3);
if (!expr) {
return NULL;
}
- if (expr_open(state, expr, file) != 0) {
- goto fail;
+ if (expr_open(parser, expr, file) != 0) {
+ return NULL;
}
- if (bfs_printf_parse(state->ctx, expr, format) != 0) {
- goto fail;
+ if (bfs_printf_parse(parser->ctx, expr, format) != 0) {
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -fstype TYPE.
*/
-static struct bfs_expr *parse_fstype(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_fstype);
+static struct bfs_expr *parse_fstype(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_fstype);
if (!expr) {
return NULL;
}
- if (!bfs_ctx_mtab(state->ctx)) {
- parse_expr_error(state, expr, "Couldn't parse the mount table: %m.\n");
- bfs_expr_free(expr);
+ if (!bfs_ctx_mtab(parser->ctx)) {
+ parse_expr_error(parser, expr, "Couldn't parse the mount table: %s.\n", errstr());
return NULL;
}
@@ -1520,237 +1509,199 @@ static struct bfs_expr *parse_fstype(struct parser_state *state, int arg1, int a
/**
* Parse -gid/-group.
*/
-static struct bfs_expr *parse_group(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_gid);
+static struct bfs_expr *parse_group(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_gid);
if (!expr) {
return NULL;
}
- const struct group *grp = bfs_getgrnam(state->ctx->groups, expr->argv[1]);
+ const struct group *grp = bfs_getgrnam(parser->ctx->groups, expr->argv[1]);
if (grp) {
expr->num = grp->gr_gid;
expr->int_cmp = BFS_INT_EQUAL;
} else if (looks_like_icmp(expr->argv[1])) {
- if (!parse_icmp(state, expr, 0)) {
- goto fail;
+ if (!parse_icmp(parser, expr, 0)) {
+ return NULL;
}
} else if (errno) {
- parse_expr_error(state, expr, "%m.\n");
- goto fail;
+ parse_expr_error(parser, expr, "%s.\n", errstr());
+ return NULL;
} else {
- parse_expr_error(state, expr, "No such group.\n");
- goto fail;
+ parse_expr_error(parser, expr, "No such group.\n");
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -unique.
*/
-static struct bfs_expr *parse_unique(struct parser_state *state, int arg1, int arg2) {
- state->ctx->unique = true;
- return parse_nullary_option(state);
+static struct bfs_expr *parse_unique(struct bfs_parser *parser, int arg1, int arg2) {
+ parser->ctx->unique = true;
+ return parse_nullary_option(parser);
}
/**
* Parse -used N.
*/
-static struct bfs_expr *parse_used(struct parser_state *state, int arg1, int arg2) {
- return parse_test_icmp(state, eval_used);
+static struct bfs_expr *parse_used(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_test_icmp(parser, eval_used);
}
/**
* Parse -uid/-user.
*/
-static struct bfs_expr *parse_user(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_uid);
+static struct bfs_expr *parse_user(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_uid);
if (!expr) {
return NULL;
}
- const struct passwd *pwd = bfs_getpwnam(state->ctx->users, expr->argv[1]);
+ const struct passwd *pwd = bfs_getpwnam(parser->ctx->users, expr->argv[1]);
if (pwd) {
expr->num = pwd->pw_uid;
expr->int_cmp = BFS_INT_EQUAL;
} else if (looks_like_icmp(expr->argv[1])) {
- if (!parse_icmp(state, expr, 0)) {
- goto fail;
+ if (!parse_icmp(parser, expr, 0)) {
+ return NULL;
}
} else if (errno) {
- parse_expr_error(state, expr, "%m.\n");
- goto fail;
+ parse_expr_error(parser, expr, "%s.\n", errstr());
+ return NULL;
} else {
- parse_expr_error(state, expr, "No such user.\n");
- goto fail;
+ parse_expr_error(parser, expr, "No such user.\n");
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -hidden.
*/
-static struct bfs_expr *parse_hidden(struct parser_state *state, int arg1, int arg2) {
- return parse_nullary_test(state, eval_hidden);
+static struct bfs_expr *parse_hidden(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_nullary_test(parser, eval_hidden);
}
/**
* Parse -(no)?ignore_readdir_race.
*/
-static struct bfs_expr *parse_ignore_races(struct parser_state *state, int ignore, int arg2) {
- state->ctx->ignore_races = ignore;
- return parse_nullary_option(state);
+static struct bfs_expr *parse_ignore_races(struct bfs_parser *parser, int ignore, int arg2) {
+ parser->ctx->ignore_races = ignore;
+ return parse_nullary_option(parser);
}
/**
* Parse -inum N.
*/
-static struct bfs_expr *parse_inum(struct parser_state *state, int arg1, int arg2) {
- return parse_test_icmp(state, eval_inum);
+static struct bfs_expr *parse_inum(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_test_icmp(parser, eval_inum);
}
/**
* Parse -j<n>.
*/
-static struct bfs_expr *parse_jobs(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_flag(state);
+static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2) {
+ const char *arg;
+ struct bfs_expr *expr = parse_prefix_flag(parser, 'j', false, &arg);
if (!expr) {
return NULL;
}
unsigned int n;
- if (!parse_int(state, expr->argv, expr->argv[0] + 2, &n, IF_INT | IF_UNSIGNED)) {
- bfs_expr_free(expr);
+ if (!parse_int(parser, expr->argv, arg, &n, IF_INT | IF_UNSIGNED)) {
return NULL;
}
if (n == 0) {
- parse_expr_error(state, expr, "${bld}0${rs} is not enough threads.\n");
- bfs_expr_free(expr);
+ parse_expr_error(parser, expr, "${bld}0${rs} is not enough threads.\n");
return NULL;
}
- state->ctx->threads = n;
+ parser->ctx->threads = n;
return expr;
}
/**
- * Parse -links N.
+ * Parse -limit N.
*/
-static struct bfs_expr *parse_links(struct parser_state *state, int arg1, int arg2) {
- return parse_test_icmp(state, eval_links);
-}
-
-/**
- * Parse -ls.
- */
-static struct bfs_expr *parse_ls(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_action(state, eval_fls);
+static struct bfs_expr *parse_limit(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_action(parser, eval_limit);
if (!expr) {
return NULL;
}
- init_print_expr(state, expr);
+ char **arg = &expr->argv[1];
+ if (!parse_int(parser, arg, *arg, &expr->num, IF_LONG_LONG)) {
+ return NULL;
+ }
+
+ if (expr->num <= 0) {
+ parse_expr_error(parser, expr, "The %pX must be at least ${bld}1${rs}.\n", expr);
+ return NULL;
+ }
+
+ parser->limit_expr = expr;
return expr;
}
/**
- * Parse -mount.
+ * Parse -links N.
+ */
+static struct bfs_expr *parse_links(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_test_icmp(parser, eval_links);
+}
+
+/**
+ * Parse -ls.
*/
-static struct bfs_expr *parse_mount(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_option(state);
+static struct bfs_expr *parse_ls(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_fls);
if (!expr) {
return NULL;
}
- parse_expr_warning(state, expr, "In the future, ${blu}%s${rs} will skip mount points entirely, unlike\n", expr->argv[0]);
- bfs_warning(state->ctx, "${blu}-xdev${rs}, due to http://austingroupbugs.net/view.php?id=1133.\n\n");
-
- state->ctx->flags |= BFTW_PRUNE_MOUNTS;
- state->mount_arg = expr->argv;
+ init_print_expr(parser, expr);
return expr;
}
/**
- * Common code for fnmatch() tests.
+ * Parse -mount.
*/
-static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct bfs_expr *expr, bool casefold) {
+static struct bfs_expr *parse_mount(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_option(parser);
if (!expr) {
return NULL;
}
- expr->pattern = expr->argv[1];
-
- if (casefold) {
-#ifdef FNM_CASEFOLD
- expr->fnm_flags = FNM_CASEFOLD;
-#else
- parse_expr_error(state, expr, "Missing platform support.\n");
- bfs_expr_free(expr);
- return NULL;
-#endif
- } else {
- expr->fnm_flags = 0;
- }
-
- // POSIX says, about fnmatch():
- //
- // If pattern ends with an unescaped <backslash>, fnmatch() shall
- // return a non-zero value (indicating either no match or an error).
- //
- // But not all implementations obey this, so check for it ourselves.
- size_t i, len = strlen(expr->pattern);
- for (i = 0; i < len; ++i) {
- if (expr->pattern[len - i - 1] != '\\') {
- break;
- }
- }
- if (i % 2 != 0) {
- parse_expr_warning(state, expr, "Unescaped trailing backslash.\n\n");
- expr->eval_fn = eval_false;
- return expr;
- }
-
- // 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
- expr->literal = strcspn(expr->pattern, "?*\\[") == len;
-
+ parser->ctx->flags |= BFTW_SKIP_MOUNTS;
+ parser->mount_expr = expr;
return expr;
}
/**
* Parse -i?name.
*/
-static struct bfs_expr *parse_name(struct parser_state *state, int casefold, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_name);
- return parse_fnmatch(state, expr, casefold);
+static struct bfs_expr *parse_name(struct bfs_parser *parser, int casefold, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_name);
+ return parse_fnmatch(parser, expr, casefold);
}
/**
* Parse -i?path, -i?wholename.
*/
-static struct bfs_expr *parse_path(struct parser_state *state, int casefold, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_path);
- return parse_fnmatch(state, expr, casefold);
+static struct bfs_expr *parse_path(struct bfs_parser *parser, int casefold, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_path);
+ return parse_fnmatch(parser, expr, casefold);
}
/**
* Parse -i?lname.
*/
-static struct bfs_expr *parse_lname(struct parser_state *state, int casefold, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_lname);
- return parse_fnmatch(state, expr, casefold);
+static struct bfs_expr *parse_lname(struct bfs_parser *parser, int casefold, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_lname);
+ return parse_fnmatch(parser, expr, casefold);
}
/** Get the bfs_stat_field for X/Y in -newerXY. */
@@ -1770,20 +1721,20 @@ static enum bfs_stat_field parse_newerxy_field(char c) {
}
/** Parse an explicit reference timestamp for -newerXt and -*since. */
-static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr) {
+static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr) {
if (xgetdate(expr->argv[1], &expr->reftime) == 0) {
return 0;
} else if (errno != EINVAL) {
- parse_expr_error(state, expr, "%m.\n");
+ parse_expr_error(parser, expr, "%s.\n", errstr());
return -1;
}
- parse_expr_error(state, expr, "Invalid timestamp.\n\n");
+ parse_expr_error(parser, expr, "Invalid timestamp.\n\n");
fprintf(stderr, "Supported timestamp formats are ISO 8601-like, e.g.\n\n");
struct tm tm;
- if (xlocaltime(&state->now.tv_sec, &tm) != 0) {
- parse_perror(state, "xlocaltime()");
+ if (!localtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "localtime_r()");
return -1;
}
@@ -1792,18 +1743,18 @@ static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr
fprintf(stderr, " - %04d-%02d-%02d\n", year, month, tm.tm_mday);
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d\n", year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-#if __FreeBSD__
+#if BFS_HAS_TM_GMTOFF
int gmtoff = tm.tm_gmtoff;
#else
int gmtoff = -timezone;
#endif
- int tz_hour = gmtoff/3600;
- int tz_min = (labs(gmtoff)/60)%60;
+ int tz_hour = gmtoff / 3600;
+ int tz_min = (labs(gmtoff) / 60) % 60;
fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n",
- year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min);
+ year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min);
- if (xgmtime(&state->now.tv_sec, &tm) != 0) {
- parse_perror(state, "xgmtime()");
+ if (!gmtime_r(&parser->now.tv_sec, &tm)) {
+ parse_perror(parser, "gmtime_r()");
return -1;
}
@@ -1817,66 +1768,69 @@ static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr
/**
* Parse -newerXY.
*/
-static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int arg2) {
- const char *arg = state->argv[0];
+static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int arg2) {
+ const char *arg = parser->argv[0];
if (strlen(arg) != 8) {
- parse_error(state, "Expected ${blu}-newer${bld}XY${rs}; found ${blu}-newer${bld}%pq${rs}.\n", arg + 6);
+ parse_error(parser, "Expected ${blu}-newer${bld}XY${rs}; found ${blu}-newer${bld}%pq${rs}.\n", arg + 6);
return NULL;
}
- struct bfs_expr *expr = parse_unary_test(state, eval_newer);
+ struct bfs_expr *expr = parse_unary_test(parser, eval_newer);
if (!expr) {
- goto fail;
+ return NULL;
}
expr->stat_field = parse_newerxy_field(arg[6]);
if (!expr->stat_field) {
- parse_expr_error(state, expr,
- "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n",
- arg[6]);
- goto fail;
+ parse_expr_error(parser, expr,
+ "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n",
+ arg[6]);
+ return NULL;
}
if (arg[7] == 't') {
- if (parse_reftime(state, expr) != 0) {
- goto fail;
+ if (parse_reftime(parser, expr) != 0) {
+ return NULL;
}
} else {
enum bfs_stat_field field = parse_newerxy_field(arg[7]);
if (!field) {
- parse_expr_error(state, expr,
- "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n",
- arg[7]);
- goto fail;
+ parse_expr_error(parser, expr,
+ "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n",
+ arg[7]);
+ return NULL;
}
struct bfs_stat sb;
- if (stat_arg(state, &expr->argv[1], &sb) != 0) {
- goto fail;
+ if (stat_arg(parser, &expr->argv[1], &sb) != 0) {
+ return NULL;
}
-
const struct timespec *reftime = bfs_stat_time(&sb, field);
if (!reftime) {
- parse_expr_error(state, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field));
- goto fail;
+ parse_expr_error(parser, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field));
+ return NULL;
}
expr->reftime = *reftime;
}
return expr;
+}
-fail:
- bfs_expr_free(expr);
- return NULL;
+/**
+ * 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 parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_test(state, eval_nogroup);
+static struct bfs_expr *parse_nogroup(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_test(parser, eval_nogroup);
if (expr) {
// Who knows how many FDs getgrgid_r() needs?
expr->ephemeral_fds = 3;
@@ -1887,33 +1841,36 @@ static struct bfs_expr *parse_nogroup(struct parser_state *state, int arg1, int
/**
* Parse -nohidden.
*/
-static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *hidden = bfs_expr_new(eval_hidden, 1, &fake_hidden_arg);
+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, BFS_TEST);
if (!hidden) {
return NULL;
}
- if (parse_exclude(state, hidden) != 0) {
- return NULL;
- }
-
- return parse_nullary_option(state);
+ bfs_expr_append(parser->ctx->exclude, hidden);
+ return parse_nullary_option(parser);
}
/**
* Parse -noleaf.
*/
-static struct bfs_expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) {
- parse_warning(state, "${ex}%s${rs} does not apply the optimization that ${blu}%s${rs} inhibits.\n\n",
- BFS_COMMAND, state->argv[0]);
- return parse_nullary_option(state);
+static struct bfs_expr *parse_noleaf(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_option(parser);
+ if (!expr) {
+ return NULL;
+ }
+
+ parse_expr_warning(parser, expr,
+ "${ex}%s${rs} does not apply the optimization that %px inhibits.\n\n",
+ BFS_COMMAND, expr);
+ return expr;
}
/**
* Parse -nouser.
*/
-static struct bfs_expr *parse_nouser(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_test(state, eval_nouser);
+static struct bfs_expr *parse_nouser(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_test(parser, eval_nouser);
if (expr) {
// Who knows how many FDs getpwuid_r() needs?
expr->ephemeral_fds = 3;
@@ -1924,10 +1881,10 @@ static struct bfs_expr *parse_nouser(struct parser_state *state, int arg1, int a
/**
* Parse a permission mode like chmod(1).
*/
-static int parse_mode(const struct parser_state *state, const char *mode, struct bfs_expr *expr) {
+static int parse_mode(const struct bfs_parser *parser, const char *mode, struct bfs_expr *expr) {
if (mode[0] >= '0' && mode[0] <= '9') {
unsigned int parsed;
- if (!parse_int(state, NULL, mode, &parsed, 8 | IF_INT | IF_UNSIGNED | IF_QUIET)) {
+ if (!parse_int(parser, NULL, mode, &parsed, 8 | IF_INT | IF_UNSIGNED | IF_QUIET)) {
goto fail;
}
if (parsed > 07777) {
@@ -1939,6 +1896,8 @@ static int parse_mode(const struct parser_state *state, const char *mode, struct
return 0;
}
+ mode_t umask = parser->ctx->umask;
+
expr->file_mode = 0;
expr->dir_mode = 0;
@@ -1967,25 +1926,27 @@ static int parse_mode(const struct parser_state *state, const char *mode, struct
MODE_ACTION_APPLY,
MODE_OP,
MODE_PERM,
- } mstate = MODE_CLAUSE;
+ } state = MODE_CLAUSE;
enum {
MODE_PLUS,
MODE_MINUS,
MODE_EQUALS,
- } op = uninit(op, MODE_EQUALS);
+ } op uninit(MODE_EQUALS);
- mode_t who = uninit(who, 0);
- mode_t file_change = uninit(file_change, 0);
- mode_t dir_change = uninit(dir_change, 0);
+ 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 (mstate) {
+ switch (state) {
case MODE_CLAUSE:
who = 0;
- mstate = MODE_WHO;
- fallthru;
+ mask = 0777;
+ state = MODE_WHO;
+ _fallthrough;
case MODE_WHO:
switch (*i) {
@@ -2002,7 +1963,7 @@ static int parse_mode(const struct parser_state *state, const char *mode, struct
who |= 0777;
break;
default:
- mstate = MODE_ACTION;
+ state = MODE_ACTION;
continue;
}
break;
@@ -2012,7 +1973,7 @@ static int parse_mode(const struct parser_state *state, 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;
@@ -2022,37 +1983,40 @@ static int parse_mode(const struct parser_state *state, 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;
- mstate = MODE_OP;
+ state = MODE_OP;
break;
case '-':
op = MODE_MINUS;
- mstate = MODE_OP;
+ state = MODE_OP;
break;
case '=':
op = MODE_EQUALS;
- mstate = MODE_OP;
+ state = MODE_OP;
break;
case ',':
- if (mstate == MODE_ACTION_APPLY) {
- mstate = MODE_CLAUSE;
+ if (state == MODE_ACTION_APPLY) {
+ state = MODE_CLAUSE;
} else {
goto fail;
}
break;
case '\0':
- if (mstate == MODE_ACTION_APPLY) {
+ if (state == MODE_ACTION_APPLY) {
goto done;
} else {
goto fail;
@@ -2081,32 +2045,32 @@ static int parse_mode(const struct parser_state *state, const char *mode, struct
default:
file_change = 0;
dir_change = 0;
- mstate = 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;
- mstate = 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) {
@@ -2125,7 +2089,7 @@ static int parse_mode(const struct parser_state *state, const char *mode, struct
}
break;
default:
- mstate = MODE_ACTION_APPLY;
+ state = MODE_ACTION_APPLY;
continue;
}
break;
@@ -2138,15 +2102,15 @@ done:
return 0;
fail:
- parse_expr_error(state, expr, "Invalid mode.\n");
+ parse_expr_error(parser, expr, "Invalid mode.\n");
return -1;
}
/**
* Parse -perm MODE.
*/
-static struct bfs_expr *parse_perm(struct parser_state *state, int field, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_perm);
+static struct bfs_expr *parse_perm(struct bfs_parser *parser, int field, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_perm);
if (!expr) {
return NULL;
}
@@ -2167,30 +2131,26 @@ static struct bfs_expr *parse_perm(struct parser_state *state, int field, int ar
++mode;
break;
}
- fallthru;
+ _fallthrough;
default:
expr->mode_cmp = BFS_MODE_EQUAL;
break;
}
- if (parse_mode(state, mode, expr) != 0) {
- goto fail;
+ if (parse_mode(parser, mode, expr) != 0) {
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -print.
*/
-static struct bfs_expr *parse_print(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_action(state, eval_fprint);
+static struct bfs_expr *parse_print(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_fprint);
if (expr) {
- init_print_expr(state, expr);
+ init_print_expr(parser, expr);
}
return expr;
}
@@ -2198,10 +2158,10 @@ static struct bfs_expr *parse_print(struct parser_state *state, int arg1, int ar
/**
* Parse -print0.
*/
-static struct bfs_expr *parse_print0(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_action(state, eval_fprint0);
+static struct bfs_expr *parse_print0(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_fprint0);
if (expr) {
- init_print_expr(state, expr);
+ init_print_expr(parser, expr);
}
return expr;
}
@@ -2209,16 +2169,15 @@ static struct bfs_expr *parse_print0(struct parser_state *state, int arg1, int a
/**
* Parse -printf FORMAT.
*/
-static struct bfs_expr *parse_printf(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_action(state, eval_fprintf);
+static struct bfs_expr *parse_printf(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_action(parser, eval_fprintf);
if (!expr) {
return NULL;
}
- init_print_expr(state, expr);
+ init_print_expr(parser, expr);
- if (bfs_printf_parse(state->ctx, expr, expr->argv[1]) != 0) {
- bfs_expr_free(expr);
+ if (bfs_printf_parse(parser->ctx, expr, expr->argv[1]) != 0) {
return NULL;
}
@@ -2228,10 +2187,10 @@ static struct bfs_expr *parse_printf(struct parser_state *state, int arg1, int a
/**
* Parse -printx.
*/
-static struct bfs_expr *parse_printx(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_nullary_action(state, eval_fprintx);
+static struct bfs_expr *parse_printx(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_fprintx);
if (expr) {
- init_print_expr(state, expr);
+ init_print_expr(parser, expr);
}
return expr;
}
@@ -2239,67 +2198,67 @@ static struct bfs_expr *parse_printx(struct parser_state *state, int arg1, int a
/**
* Parse -prune.
*/
-static struct bfs_expr *parse_prune(struct parser_state *state, int arg1, int arg2) {
- state->prune_arg = state->argv;
- return parse_nullary_action(state, eval_prune);
+static struct bfs_expr *parse_prune(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_action(parser, eval_prune);
+ if (!expr) {
+ return NULL;
+ }
+
+ parser->prune_expr = expr;
+ return expr;
}
/**
* Parse -quit.
*/
-static struct bfs_expr *parse_quit(struct parser_state *state, int arg1, int arg2) {
- return parse_nullary_action(state, eval_quit);
+static struct bfs_expr *parse_quit(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_nullary_action(parser, eval_quit);
}
/**
* Parse -i?regex.
*/
-static struct bfs_expr *parse_regex(struct parser_state *state, int flags, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_regex);
+static struct bfs_expr *parse_regex(struct bfs_parser *parser, int flags, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_regex);
if (!expr) {
- goto fail;
+ return NULL;
}
- if (bfs_regcomp(&expr->regex, expr->argv[1], state->regex_type, flags) != 0) {
- if (!expr->regex) {
- parse_perror(state, "bfs_regcomp()");
- goto fail;
- }
-
- char *str = bfs_regerror(expr->regex);
- if (!str) {
- parse_perror(state, "bfs_regerror()");
- goto fail;
+ if (bfs_regcomp(&expr->regex, expr->argv[1], parser->regex_type, flags) != 0) {
+ if (expr->regex) {
+ char *str = bfs_regerror(expr->regex);
+ if (str) {
+ parse_expr_error(parser, expr, "%s.\n", str);
+ free(str);
+ } else {
+ parse_perror(parser, "bfs_regerror()");
+ }
+ } else {
+ parse_perror(parser, "bfs_regcomp()");
}
- parse_expr_error(state, expr, "%s.\n", str);
- free(str);
- goto fail;
+ return NULL;
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -E.
*/
-static struct bfs_expr *parse_regex_extended(struct parser_state *state, int arg1, int arg2) {
- state->regex_type = BFS_REGEX_POSIX_EXTENDED;
- return parse_nullary_flag(state);
+static struct bfs_expr *parse_regex_extended(struct bfs_parser *parser, int arg1, int arg2) {
+ parser->regex_type = BFS_REGEX_POSIX_EXTENDED;
+ return parse_nullary_flag(parser);
}
/**
* Parse -regextype TYPE.
*/
-static struct bfs_expr *parse_regextype(struct parser_state *state, int arg1, int arg2) {
- struct bfs_ctx *ctx = state->ctx;
+static struct bfs_expr *parse_regextype(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_ctx *ctx = parser->ctx;
CFILE *cfile = ctx->cerr;
- struct bfs_expr *expr = parse_unary_option(state);
+ struct bfs_expr *expr = parse_unary_option(parser);
if (!expr) {
cfprintf(cfile, "\n");
goto list_types;
@@ -2308,23 +2267,34 @@ static struct bfs_expr *parse_regextype(struct parser_state *state, int arg1, in
// 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) {
- state->regex_type = BFS_REGEX_POSIX_BASIC;
+ parser->regex_type = BFS_REGEX_POSIX_BASIC;
} else if (strcmp(type, "posix-extended") == 0) {
- state->regex_type = BFS_REGEX_POSIX_EXTENDED;
-#if BFS_USE_ONIGURUMA
+ 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) {
- state->regex_type = BFS_REGEX_EMACS;
+ parser->regex_type = BFS_REGEX_EMACS;
} else if (strcmp(type, "grep") == 0) {
- state->regex_type = BFS_REGEX_GREP;
+ 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) {
- state->just_info = true;
+ parser->just_info = true;
cfile = ctx->cout;
goto list_types;
} else {
- parse_expr_error(state, expr, "Unsupported regex type.\n\n");
+ parse_expr_error(parser, expr, "Unsupported regex type.\n\n");
goto list_types;
}
@@ -2332,39 +2302,45 @@ static struct bfs_expr *parse_regextype(struct parser_state *state, int arg1, in
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");
-#if BFS_USE_ONIGURUMA
- cfprintf(cfile, " ${bld}emacs${rs}: Like ${grn}emacs${rs}\n");
- cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n");
-#endif
- cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${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");
- bfs_expr_free(expr);
+ cfprintf(cfile, " ${bld}posix-extended${rs}: POSIX extended regular expressions (ERE)\n\n");
+
+#if BFS_WITH_ONIGURUMA
+ 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
return NULL;
}
/**
* Parse -s.
*/
-static struct bfs_expr *parse_s(struct parser_state *state, int arg1, int arg2) {
- state->ctx->flags |= BFTW_SORT;
- return parse_nullary_flag(state);
+static struct bfs_expr *parse_s(struct bfs_parser *parser, int arg1, int arg2) {
+ parser->ctx->flags |= BFTW_SORT;
+ return parse_nullary_flag(parser);
}
/**
* Parse -samefile FILE.
*/
-static struct bfs_expr *parse_samefile(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_samefile);
+static struct bfs_expr *parse_samefile(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_samefile);
if (!expr) {
return NULL;
}
struct bfs_stat sb;
- if (stat_arg(state, &expr->argv[1], &sb) != 0) {
- bfs_expr_free(expr);
+ if (stat_arg(parser, &expr->argv[1], &sb) != 0) {
return NULL;
}
@@ -2376,17 +2352,17 @@ static struct bfs_expr *parse_samefile(struct parser_state *state, int arg1, int
/**
* Parse -S STRATEGY.
*/
-static struct bfs_expr *parse_search_strategy(struct parser_state *state, int arg1, int arg2) {
- struct bfs_ctx *ctx = state->ctx;
+static struct bfs_expr *parse_search_strategy(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_ctx *ctx = parser->ctx;
CFILE *cfile = ctx->cerr;
- struct bfs_expr *expr = parse_unary_flag(state);
+ 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) {
@@ -2396,11 +2372,11 @@ static struct bfs_expr *parse_search_strategy(struct parser_state *state, int ar
} else if (strcmp(arg, "eds") == 0) {
ctx->strategy = BFTW_EDS;
} else if (strcmp(arg, "help") == 0) {
- state->just_info = true;
+ parser->just_info = true;
cfile = ctx->cout;
goto list_strategies;
} else {
- parse_expr_error(state, expr, "Unrecognized search strategy.\n\n");
+ parse_expr_error(parser, expr, "Unrecognized search strategy.\n\n");
goto list_strategies;
}
@@ -2412,44 +2388,38 @@ list_strategies:
cfprintf(cfile, " ${bld}dfs${rs}: depth-first search\n");
cfprintf(cfile, " ${bld}ids${rs}: iterative deepening search\n");
cfprintf(cfile, " ${bld}eds${rs}: exponential deepening search\n");
-
- bfs_expr_free(expr);
return NULL;
}
/**
* Parse -[aBcm]?since.
*/
-static struct bfs_expr *parse_since(struct parser_state *state, int field, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_newer);
+static struct bfs_expr *parse_since(struct bfs_parser *parser, int field, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_newer);
if (!expr) {
return NULL;
}
- if (parse_reftime(state, expr) != 0) {
- goto fail;
+ if (parse_reftime(parser, expr) != 0) {
+ return NULL;
}
expr->stat_field = field;
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -size N[cwbkMGTP]?.
*/
-static struct bfs_expr *parse_size(struct parser_state *state, int arg1, int arg2) {
- struct bfs_expr *expr = parse_unary_test(state, eval_size);
+static struct bfs_expr *parse_size(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_unary_test(parser, eval_size);
if (!expr) {
return NULL;
}
- const char *unit = parse_icmp(state, expr, IF_PARTIAL_OK);
+ const char *unit = parse_icmp(parser, expr, IF_PARTIAL_OK);
if (!unit) {
- goto fail;
+ return NULL;
}
if (strlen(unit) > 1) {
@@ -2484,39 +2454,37 @@ static struct bfs_expr *parse_size(struct parser_state *state, int arg1, int arg
break;
default:
- goto bad_unit;
+ bad_unit:
+ parse_expr_error(parser, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit);
+ return NULL;
}
return expr;
-
-bad_unit:
- parse_expr_error(state, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit);
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -sparse.
*/
-static struct bfs_expr *parse_sparse(struct parser_state *state, int arg1, int arg2) {
- return parse_nullary_test(state, eval_sparse);
+static struct bfs_expr *parse_sparse(struct bfs_parser *parser, int arg1, int arg2) {
+ return parse_nullary_test(parser, eval_sparse);
}
/**
* Parse -status.
*/
-static struct bfs_expr *parse_status(struct parser_state *state, int arg1, int arg2) {
- state->ctx->status = true;
- return parse_nullary_option(state);
+static struct bfs_expr *parse_status(struct bfs_parser *parser, int arg1, int arg2) {
+ parser->ctx->status = true;
+ return parse_nullary_option(parser);
}
/**
* Parse -x?type [bcdpflsD].
*/
-static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) {
+static struct bfs_expr *parse_type(struct bfs_parser *parser, int x, int arg2) {
+ struct bfs_ctx *ctx = parser->ctx;
+
bfs_eval_fn *eval = x ? eval_xtype : eval_type;
- struct bfs_expr *expr = parse_unary_test(state, eval);
+ struct bfs_expr *expr = parse_unary_test(parser, eval);
if (!expr) {
return NULL;
}
@@ -2552,15 +2520,16 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2)
break;
case 'w':
expr->num |= 1 << BFS_WHT;
+ ctx->flags |= BFTW_WHITEOUTS;
break;
case '\0':
- parse_expr_error(state, expr, "Expected a type flag.\n");
- goto fail;
+ parse_expr_error(parser, expr, "Expected a type flag.\n");
+ return NULL;
default:
- parse_expr_error(state, expr, "Unknown type flag ${err}%c${rs}; expected one of [${bld}bcdpflsD${rs}].\n", *c);
- goto fail;
+ parse_expr_error(parser, expr, "Unknown type flag ${err}%c${rs}; expected one of [${bld}bcdpflsD${rs}].\n", *c);
+ return NULL;
}
++c;
@@ -2570,34 +2539,30 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2)
++c;
continue;
} else {
- parse_expr_error(state, expr, "Types must be comma-separated.\n");
- goto fail;
+ parse_expr_error(parser, expr, "Types must be comma-separated.\n");
+ return NULL;
}
}
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
/**
* Parse -(no)?warn.
*/
-static struct bfs_expr *parse_warn(struct parser_state *state, int warn, int arg2) {
- state->ctx->warn = warn;
- return parse_nullary_option(state);
+static struct bfs_expr *parse_warn(struct bfs_parser *parser, int warn, int arg2) {
+ parser->ctx->warn = warn;
+ return parse_nullary_option(parser);
}
/**
* Parse -xattr.
*/
-static struct bfs_expr *parse_xattr(struct parser_state *state, int arg1, int arg2) {
+static struct bfs_expr *parse_xattr(struct bfs_parser *parser, int arg1, int arg2) {
#if BFS_CAN_CHECK_XATTRS
- return parse_nullary_test(state, eval_xattr);
+ return parse_nullary_test(parser, eval_xattr);
#else
- parse_error(state, "Missing platform support.\n");
+ parse_error(parser, "Missing platform support.\n");
return NULL;
#endif
}
@@ -2605,11 +2570,11 @@ static struct bfs_expr *parse_xattr(struct parser_state *state, int arg1, int ar
/**
* Parse -xattrname.
*/
-static struct bfs_expr *parse_xattrname(struct parser_state *state, int arg1, int arg2) {
+static struct bfs_expr *parse_xattrname(struct bfs_parser *parser, int arg1, int arg2) {
#if BFS_CAN_CHECK_XATTRS
- return parse_unary_test(state, eval_xattrname);
+ return parse_unary_test(parser, eval_xattrname);
#else
- parse_error(state, "Missing platform support.\n");
+ parse_error(parser, "Missing platform support.\n");
return NULL;
#endif
}
@@ -2617,10 +2582,15 @@ static struct bfs_expr *parse_xattrname(struct parser_state *state, int arg1, in
/**
* Parse -xdev.
*/
-static struct bfs_expr *parse_xdev(struct parser_state *state, int arg1, int arg2) {
- state->ctx->flags |= BFTW_PRUNE_MOUNTS;
- state->xdev_arg = state->argv;
- return parse_nullary_option(state);
+static struct bfs_expr *parse_xdev(struct bfs_parser *parser, int arg1, int arg2) {
+ struct bfs_expr *expr = parse_nullary_option(parser);
+ if (!expr) {
+ return NULL;
+ }
+
+ parser->ctx->flags |= BFTW_PRUNE_MOUNTS;
+ parser->xdev_expr = expr;
+ return expr;
}
/**
@@ -2721,21 +2691,21 @@ fail:
/**
* "Parse" -help.
*/
-static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg2) {
- CFILE *cout = state->ctx->cout;
+static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2) {
+ CFILE *cout = parser->ctx->cout;
pid_t pager = -1;
- if (state->stdout_tty) {
+ if (parser->stdout_tty) {
cout = launch_pager(&pager, cout);
}
cfprintf(cout, "Usage: ${ex}%s${rs} [${cyn}flags${rs}...] [${mag}paths${rs}...] [${blu}expression${rs}...]\n\n",
- state->command);
+ parser->command);
cfprintf(cout, "${ex}%s${rs} is compatible with ${ex}find${rs}, with some extensions. "
- "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n"
- "and ${blu}expressions${rs} may be freely mixed in any order.\n\n",
- BFS_COMMAND);
+ "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n"
+ "and ${blu}expressions${rs} may be freely mixed in any order.\n\n",
+ BFS_COMMAND);
cfprintf(cout, "${bld}Flags:${rs}\n\n");
@@ -2807,14 +2777,15 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg
cfprintf(cout, " ${blu}-ignore_readdir_race${rs}\n");
cfprintf(cout, " ${blu}-noignore_readdir_race${rs}\n");
cfprintf(cout, " Whether to report an error if ${ex}%s${rs} detects that the file tree is modified\n",
- BFS_COMMAND);
+ BFS_COMMAND);
cfprintf(cout, " during the search (default: ${blu}-noignore_readdir_race${rs})\n");
cfprintf(cout, " ${blu}-maxdepth${rs} ${bld}N${rs}\n");
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");
@@ -2850,6 +2821,10 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg
cfprintf(cout, " ${blu}-capable${rs}\n");
cfprintf(cout, " Find files with POSIX.1e capabilities set\n");
#endif
+#if BFS_CAN_CHECK_CONTEXT
+ cfprintf(cout, " ${blu}-context${rs} ${bld}GLOB${rs}\n");
+ cfprintf(cout, " Find files with SELinux context matching a glob pattern\n");
+#endif
cfprintf(cout, " ${blu}-depth${rs} ${bld}[-+]N${rs}\n");
cfprintf(cout, " Find files with depth ${bld}N${rs}\n");
cfprintf(cout, " ${blu}-empty${rs}\n");
@@ -2952,6 +2927,8 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg
cfprintf(cout, " ${blu}-fprintf${rs} ${bld}FILE${rs} ${bld}FORMAT${rs}\n");
cfprintf(cout, " Like ${blu}-ls${rs}/${blu}-print${rs}/${blu}-print0${rs}/${blu}-printf${rs}, but write to ${bld}FILE${rs} instead of standard\n"
" output\n");
+ cfprintf(cout, " ${blu}-limit${rs} ${bld}N${rs}\n");
+ cfprintf(cout, " Quit after this action is evaluated ${bld}N${rs} times\n");
cfprintf(cout, " ${blu}-ls${rs}\n");
cfprintf(cout, " List files like ${ex}ls${rs} ${bld}-dils${rs}\n");
cfprintf(cout, " ${blu}-print${rs}\n");
@@ -2978,161 +2955,207 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg
if (pager > 0) {
cfclose(cout);
- waitpid(pager, NULL, 0);
+ xwaitpid(pager, NULL, 0);
}
- state->just_info = true;
+ parser->just_info = true;
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 parser_state *state, int arg1, int arg2) {
- cfprintf(state->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, BFS_VERSION);
+static struct bfs_expr *parse_version(struct bfs_parser *parser, int arg1, int arg2) {
+ print_logo(parser->ctx->cout);
- printf("%s\n", BFS_HOMEPAGE);
+ printf("Copyright © Tavian Barnes and the bfs contributors\n");
+ printf("No rights reserved (https://opensource.org/license/0BSD)\n\n");
- state->just_info = true;
+ 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;
}
-typedef struct bfs_expr *parse_fn(struct parser_state *state, int arg1, int arg2);
+/** Parser callback function type. */
+typedef struct bfs_expr *parse_fn(struct bfs_parser *parser, int arg1, int arg2);
/**
* An entry in the parse table for primary expressions.
*/
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},
- {"-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},
- {"-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, true},
+ {"-daystart", BFS_OPTION, parse_daystart},
+ {"-delete", BFS_ACTION, parse_delete},
+ {"-depth", BFS_OPTION, parse_depth_n, false},
+ {"-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},
};
@@ -3153,6 +3176,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;
@@ -3174,9 +3274,11 @@ static const struct table_entry *table_lookup_fuzzy(const char *arg) {
* | TEST
* | ACTION
*/
-static struct bfs_expr *parse_primary(struct parser_state *state) {
+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 = state->argv[0];
+ const char *arg = parser->argv[0];
if (arg[0] != '-') {
goto unexpected;
@@ -3191,15 +3293,19 @@ static struct bfs_expr *parse_primary(struct parser_state *state) {
}
}
+ if (is_flag_group(arg)) {
+ return parse_flag_group(parser);
+ }
+
match = table_lookup_fuzzy(arg);
- CFILE *cerr = state->ctx->cerr;
- parse_error(state, "Unknown argument; did you mean ");
- switch (match->type) {
- case T_FLAG:
+ CFILE *cerr = ctx->cerr;
+ parse_error(parser, "Unknown argument; did you mean ");
+ 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:
@@ -3207,7 +3313,7 @@ static struct bfs_expr *parse_primary(struct parser_state *state) {
break;
}
- if (!state->interactive || !match->parse) {
+ if (!ctx->interactive || !match->parse) {
fprintf(stderr, "\n");
goto unmatched;
}
@@ -3218,16 +3324,16 @@ static struct bfs_expr *parse_primary(struct parser_state *state) {
}
fprintf(stderr, "\n");
- state->argv[0] = match->arg;
+ parser->argv[0] = match->arg;
matched:
- return match->parse(state, match->arg1, match->arg2);
+ return match->parse(parser, match->arg1, match->arg2);
unmatched:
return NULL;
unexpected:
- parse_error(state, "Expected a predicate.\n");
+ parse_error(parser, "Expected a predicate.\n");
return NULL;
}
@@ -3237,71 +3343,66 @@ unexpected:
* | "-exclude" FACTOR
* | PRIMARY
*/
-static struct bfs_expr *parse_factor(struct parser_state *state) {
- if (skip_paths(state) != 0) {
+static struct bfs_expr *parse_factor(struct bfs_parser *parser) {
+ if (skip_paths(parser) != 0) {
return NULL;
}
- const char *arg = state->argv[0];
+ const char *arg = parser->argv[0];
if (!arg) {
- parse_argv_error(state, state->last_arg, 1, "Expression terminated prematurely here.\n");
+ parse_argv_error(parser, parser->last_arg, 1, "Expression terminated prematurely here.\n");
return NULL;
}
if (strcmp(arg, "(") == 0) {
- parser_advance(state, T_OPERATOR, 1);
+ parser_advance(parser, BFS_OPERATOR, 1);
- struct bfs_expr *expr = parse_expr(state);
+ struct bfs_expr *expr = parse_expr(parser);
if (!expr) {
return NULL;
}
- if (skip_paths(state) != 0) {
- bfs_expr_free(expr);
+ if (skip_paths(parser) != 0) {
return NULL;
}
- arg = state->argv[0];
+ arg = parser->argv[0];
if (!arg || strcmp(arg, ")") != 0) {
- parse_argv_error(state, state->last_arg, 1, "Expected a ${red})${rs}.\n");
- bfs_expr_free(expr);
+ parse_argv_error(parser, parser->last_arg, 1, "Expected a ${red})${rs}.\n");
return NULL;
}
- parser_advance(state, T_OPERATOR, 1);
+ parser_advance(parser, BFS_OPERATOR, 1);
return expr;
} else if (strcmp(arg, "-exclude") == 0) {
- if (state->excluding) {
- parse_error(state, "${err}%s${rs} is not supported within ${red}-exclude${rs}.\n", arg);
+ if (parser->excluding) {
+ parse_error(parser, "${err}%s${rs} is not supported within ${red}-exclude${rs}.\n", arg);
return NULL;
}
- char **argv = parser_advance(state, T_OPERATOR, 1);
- state->excluding = true;
+ char **argv = parser_advance(parser, BFS_OPERATOR, 1);
+ parser->excluding = true;
- struct bfs_expr *factor = parse_factor(state);
+ struct bfs_expr *factor = parse_factor(parser);
if (!factor) {
return NULL;
}
- state->excluding = false;
+ parser->excluding = false;
- if (parse_exclude(state, factor) != 0) {
- return NULL;
- }
-
- return bfs_expr_new(eval_true, state->argv - argv, argv);
+ bfs_expr_append(parser->ctx->exclude, factor);
+ 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(state, T_OPERATOR, 1);
+ char **argv = parser_advance(parser, BFS_OPERATOR, 1);
- struct bfs_expr *factor = parse_factor(state);
+ struct bfs_expr *factor = parse_factor(parser);
if (!factor) {
return NULL;
}
- return new_unary_expr(eval_not, factor, argv);
+ return new_unary_expr(parser, eval_not, factor, argv);
} else {
- return parse_primary(state);
+ return parse_primary(parser);
}
}
@@ -3311,16 +3412,15 @@ static struct bfs_expr *parse_factor(struct parser_state *state) {
* | TERM "-a" FACTOR
* | TERM "-and" FACTOR
*/
-static struct bfs_expr *parse_term(struct parser_state *state) {
- struct bfs_expr *term = parse_factor(state);
+static struct bfs_expr *parse_term(struct bfs_parser *parser) {
+ struct bfs_expr *term = parse_factor(parser);
while (term) {
- if (skip_paths(state) != 0) {
- bfs_expr_free(term);
+ if (skip_paths(parser) != 0) {
return NULL;
}
- const char *arg = state->argv[0];
+ const char *arg = parser->argv[0];
if (!arg) {
break;
}
@@ -3333,17 +3433,16 @@ static struct bfs_expr *parse_term(struct parser_state *state) {
char **argv = &fake_and_arg;
if (strcmp(arg, "-a") == 0 || strcmp(arg, "-and") == 0) {
- argv = parser_advance(state, T_OPERATOR, 1);
+ argv = parser_advance(parser, BFS_OPERATOR, 1);
}
struct bfs_expr *lhs = term;
- struct bfs_expr *rhs = parse_factor(state);
+ struct bfs_expr *rhs = parse_factor(parser);
if (!rhs) {
- bfs_expr_free(lhs);
return NULL;
}
- term = new_binary_expr(eval_and, lhs, rhs, argv);
+ term = new_binary_expr(parser, eval_and, lhs, rhs, argv);
}
return term;
@@ -3354,16 +3453,15 @@ static struct bfs_expr *parse_term(struct parser_state *state) {
* | CLAUSE "-o" TERM
* | CLAUSE "-or" TERM
*/
-static struct bfs_expr *parse_clause(struct parser_state *state) {
- struct bfs_expr *clause = parse_term(state);
+static struct bfs_expr *parse_clause(struct bfs_parser *parser) {
+ struct bfs_expr *clause = parse_term(parser);
while (clause) {
- if (skip_paths(state) != 0) {
- bfs_expr_free(clause);
+ if (skip_paths(parser) != 0) {
return NULL;
}
- const char *arg = state->argv[0];
+ const char *arg = parser->argv[0];
if (!arg) {
break;
}
@@ -3372,16 +3470,15 @@ static struct bfs_expr *parse_clause(struct parser_state *state) {
break;
}
- char **argv = parser_advance(state, T_OPERATOR, 1);
+ char **argv = parser_advance(parser, BFS_OPERATOR, 1);
struct bfs_expr *lhs = clause;
- struct bfs_expr *rhs = parse_term(state);
+ struct bfs_expr *rhs = parse_term(parser);
if (!rhs) {
- bfs_expr_free(lhs);
return NULL;
}
- clause = new_binary_expr(eval_or, lhs, rhs, argv);
+ clause = new_binary_expr(parser, eval_or, lhs, rhs, argv);
}
return clause;
@@ -3391,16 +3488,15 @@ static struct bfs_expr *parse_clause(struct parser_state *state) {
* EXPR : CLAUSE
* | EXPR "," CLAUSE
*/
-static struct bfs_expr *parse_expr(struct parser_state *state) {
- struct bfs_expr *expr = parse_clause(state);
+static struct bfs_expr *parse_expr(struct bfs_parser *parser) {
+ struct bfs_expr *expr = parse_clause(parser);
while (expr) {
- if (skip_paths(state) != 0) {
- bfs_expr_free(expr);
+ if (skip_paths(parser) != 0) {
return NULL;
}
- const char *arg = state->argv[0];
+ const char *arg = parser->argv[0];
if (!arg) {
break;
}
@@ -3409,90 +3505,165 @@ static struct bfs_expr *parse_expr(struct parser_state *state) {
break;
}
- char **argv = parser_advance(state, T_OPERATOR, 1);
+ char **argv = parser_advance(parser, BFS_OPERATOR, 1);
struct bfs_expr *lhs = expr;
- struct bfs_expr *rhs = parse_clause(state);
+ struct bfs_expr *rhs = parse_clause(parser);
if (!rhs) {
- bfs_expr_free(lhs);
return NULL;
}
- expr = new_binary_expr(eval_comma, lhs, rhs, argv);
+ expr = new_binary_expr(parser, eval_comma, lhs, rhs, argv);
}
return expr;
}
+/** Handle -files0-from after parsing. */
+static int parse_files0_roots(struct bfs_parser *parser) {
+ const struct bfs_ctx *ctx = parser->ctx;
+ const struct bfs_expr *expr = parser->files0_expr;
+
+ if (ctx->npaths > 0) {
+ bool highlight[ctx->argc];
+ init_highlight(ctx, highlight);
+ highlight_args(ctx, expr->argv, expr->argc, highlight);
+
+ for (size_t i = 0; i < ctx->argc; ++i) {
+ if (ctx->kinds[i] == BFS_PATH) {
+ highlight[i] = true;
+ }
+ }
+
+ bfs_argv_error(ctx, highlight);
+ bfs_error(ctx, "Cannot combine %pX with explicit root paths.\n", expr);
+ return -1;
+ }
+
+ const char *from = expr->argv[1];
+
+ FILE *file;
+ if (strcmp(from, "-") == 0) {
+ if (!consume_stdin(parser, expr)) {
+ return -1;
+ }
+ file = stdin;
+ } else {
+ file = xfopen(from, O_RDONLY | O_CLOEXEC);
+ }
+ if (!file) {
+ parse_expr_error(parser, expr, "%s.\n", errstr());
+ return -1;
+ }
+
+ while (true) {
+ char *path = xgetdelim(file, '\0');
+ if (!path) {
+ if (errno) {
+ goto fail;
+ } else {
+ break;
+ }
+ }
+
+ int ret = parse_root(parser, path);
+ free(path);
+ if (ret != 0) {
+ goto fail;
+ }
+ }
+
+ if (file != stdin) {
+ fclose(file);
+ }
+
+ return 0;
+
+fail:
+ if (file != stdin) {
+ fclose(file);
+ }
+ return -1;
+}
+
/**
* Parse the top-level expression.
*/
-static struct bfs_expr *parse_whole_expr(struct parser_state *state) {
- if (skip_paths(state) != 0) {
+static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) {
+ struct bfs_ctx *ctx = parser->ctx;
+
+ if (skip_paths(parser) != 0) {
return NULL;
}
struct bfs_expr *expr;
- if (state->argv[0]) {
- expr = parse_expr(state);
+ if (parser->argv[0]) {
+ expr = parse_expr(parser);
} else {
- expr = bfs_expr_new(eval_true, 1, &fake_true_arg);
+ expr = parse_new_expr(parser, eval_true, 1, &fake_true_arg, BFS_TEST);
}
if (!expr) {
return NULL;
}
- if (state->argv[0]) {
- parse_error(state, "Unexpected argument.\n");
- goto fail;
+ if (parser->argv[0]) {
+ parse_error(parser, "Unexpected argument.\n");
+ return NULL;
}
- if (state->implicit_print) {
- struct bfs_expr *print = bfs_expr_new(eval_fprint, 1, &fake_print_arg);
+ if (parser->files0_expr) {
+ if (parse_files0_roots(parser) != 0) {
+ return NULL;
+ }
+ } else if (ctx->npaths == 0) {
+ if (parse_root(parser, ".") != 0) {
+ return NULL;
+ }
+ }
+
+ if (parser->implicit_print) {
+ const struct bfs_expr *limit = parser->limit_expr;
+ if (limit) {
+ parse_expr_error(parser, limit,
+ "With %pX, you must specify an action explicitly; for example, ${blu}-print${rs} %px.\n",
+ limit, limit);
+ return NULL;
+ }
+
+ struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg, BFS_ACTION);
if (!print) {
- goto fail;
+ return NULL;
}
- init_print_expr(state, print);
+ init_print_expr(parser, print);
- expr = new_binary_expr(eval_and, expr, print, &fake_and_arg);
+ expr = new_binary_expr(parser, eval_and, expr, print, &fake_and_arg);
if (!expr) {
- goto fail;
+ return NULL;
}
}
- if (state->mount_arg && state->xdev_arg) {
- parse_conflict_warning(state, state->mount_arg, 1, state->xdev_arg, 1,
- "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n",
- state->xdev_arg[0], state->mount_arg[0]);
+ if (parser->mount_expr && parser->xdev_expr) {
+ parse_conflict_warning(parser, parser->mount_expr, parser->xdev_expr,
+ "%px is redundant in the presence of %px.\n\n",
+ parser->xdev_expr, parser->mount_expr);
}
- if (state->ctx->warn && state->depth_arg && state->prune_arg) {
- parse_conflict_warning(state, state->depth_arg, 1, state->prune_arg, 1,
- "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n",
- state->prune_arg[0], state->depth_arg[0]);
+ if (ctx->warn && parser->depth_expr && parser->prune_expr) {
+ parse_conflict_warning(parser, parser->depth_expr, parser->prune_expr,
+ "%px does not work in the presence of %px.\n",
+ parser->prune_expr, parser->depth_expr);
- if (state->interactive) {
- bfs_warning(state->ctx, "Do you want to continue? ");
- if (ynprompt() == 0) {
- goto fail;
+ if (ctx->interactive) {
+ bfs_warning(ctx, "Do you want to continue? ");
+ if (ynprompt() <= 0) {
+ return NULL;
}
}
fprintf(stderr, "\n");
}
- if (state->ok_expr && state->files0_stdin_arg) {
- parse_conflict_error(state, state->ok_expr->argv, state->ok_expr->argc, state->files0_stdin_arg, 2,
- "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n",
- state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]);
- goto fail;
- }
-
return expr;
-
-fail:
- bfs_expr_free(expr);
- return NULL;
}
static const char *bftw_strategy_name(enum bftw_strategy strategy) {
@@ -3518,19 +3689,29 @@ static void dump_expr_multiline(const struct bfs_ctx *ctx, enum debug_flags flag
cfprintf(ctx->cerr, " ");
}
+ bool close = true;
+
if (bfs_expr_is_parent(expr)) {
- cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]);
- if (expr->lhs) {
- dump_expr_multiline(ctx, flag, expr->lhs, indent + 1, 0);
+ if (SLIST_EMPTY(&expr->children)) {
+ cfprintf(ctx->cerr, "(${red}%s${rs}", expr->argv[0]);
+ ++rparens;
+ } else {
+ cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]);
+ for_expr (child, expr) {
+ int parens = child->next ? 0 : rparens + 1;
+ dump_expr_multiline(ctx, flag, child, indent + 1, parens);
+ }
+ close = false;
}
- dump_expr_multiline(ctx, flag, expr->rhs, indent + 1, rparens + 1);
} else {
if (flag == DEBUG_RATES) {
cfprintf(ctx->cerr, "%pE", expr);
} else {
cfprintf(ctx->cerr, "%pe", expr);
}
+ }
+ if (close) {
for (int i = 0; i < rparens; ++i) {
cfprintf(ctx->cerr, ")");
}
@@ -3563,14 +3744,12 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) {
cfprintf(cerr, " ${cyn}-s${rs}");
}
+ cfprintf(cerr, " ${cyn}-j${bld}%d${rs}", ctx->threads);
+
if (ctx->optlevel != 3) {
cfprintf(cerr, " ${cyn}-O${bld}%d${rs}", ctx->optlevel);
}
- if (ctx->threads > 0) {
- cfprintf(cerr, " ${cyn}-j${bld}%d${rs}", ctx->threads);
- }
-
cfprintf(cerr, " ${cyn}-S${rs} ${bld}%s${rs}", bftw_strategy_name(ctx->strategy));
enum debug_flags debug = ctx->debug;
@@ -3589,7 +3768,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) {
}
}
- for (size_t i = 0; i < darray_length(ctx->paths); ++i) {
+ for (size_t i = 0; i < ctx->npaths; ++i) {
const char *path = ctx->paths[i];
char c = path[0];
if (c == '-' || c == '(' || c == ')' || c == '!' || c == ',') {
@@ -3630,10 +3809,8 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) {
fputs("\n", stderr);
- if (ctx->exclude->eval_fn != eval_false) {
- bfs_debug(ctx, flag, "(${red}-exclude${rs}\n");
- dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1);
- }
+ bfs_debug(ctx, flag, "(${red}-exclude${rs}\n");
+ dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1);
dump_expr_multiline(ctx, flag, ctx->expr, 0, 0);
}
@@ -3644,17 +3821,17 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) {
static void dump_costs(const struct bfs_ctx *ctx) {
const struct bfs_expr *expr = ctx->expr;
bfs_debug(ctx, DEBUG_COST, " Cost: ~${ylw}%g${rs}\n", expr->cost);
- bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0*expr->probability);
+ bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0 * expr->probability);
}
struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) {
struct bfs_ctx *ctx = bfs_ctx_new();
if (!ctx) {
- perror("bfs_new_ctx()");
+ perror("bfs_ctx_new()");
goto fail;
}
- static char* default_argv[] = {BFS_COMMAND, NULL};
+ static char *default_argv[] = {BFS_COMMAND, NULL};
if (argc < 1) {
argc = 1;
argv = default_argv;
@@ -3667,8 +3844,15 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) {
goto fail;
}
+ ctx->kinds = ZALLOC_ARRAY(enum bfs_kind, argc);
+ if (!ctx->kinds) {
+ perror("zalloc()");
+ goto fail;
+ }
+
enum use_color use_color = COLOR_AUTO;
- if (getenv("NO_COLOR")) {
+ const char *no_color = getenv("NO_COLOR");
+ if (no_color && *no_color) {
// https://no-color.org/
use_color = COLOR_NEVER;
}
@@ -3704,55 +3888,50 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) {
} else {
ctx->warn = stdin_tty;
}
+ ctx->interactive = stdin_tty && stderr_tty;
- struct parser_state state = {
+ struct bfs_parser parser = {
.ctx = ctx,
.argv = ctx->argv + 1,
.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,
.just_info = false,
.excluding = false,
.last_arg = NULL,
- .depth_arg = NULL,
- .prune_arg = NULL,
- .mount_arg = NULL,
- .xdev_arg = NULL,
- .files0_stdin_arg = NULL,
- .ok_expr = NULL,
+ .depth_expr = NULL,
+ .prune_expr = NULL,
+ .mount_expr = NULL,
+ .xdev_expr = NULL,
+ .stdin_expr = NULL,
.now = ctx->now,
};
- ctx->exclude = bfs_expr_new(eval_false, 1, &fake_false_arg);
+ ctx->exclude = parse_new_expr(&parser, eval_or, 1, &fake_or_arg, BFS_OPERATOR);
if (!ctx->exclude) {
goto fail;
}
- ctx->expr = parse_whole_expr(&state);
+ ctx->expr = parse_whole_expr(&parser);
if (!ctx->expr) {
- if (state.just_info) {
+ if (parser.just_info) {
goto done;
} else {
goto fail;
}
}
- if (state.use_color == COLOR_AUTO && !ctx->colors) {
- bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", strerror(ctx->colors_error));
+ if (parser.use_color == COLOR_AUTO && !ctx->colors) {
+ bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", xstrerror(ctx->colors_error));
}
if (bfs_optimize(ctx) != 0) {
- goto fail;
- }
-
- if (darray_length(ctx->paths) == 0 && state.implicit_root) {
- if (parse_root(&state, ".") != 0) {
- goto fail;
+ if (errno != 0) {
+ bfs_perror(ctx, "bfs_optimize()");
}
+ goto fail;
}
if ((ctx->flags & BFTW_FOLLOW_ALL) && !ctx->unique) {