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