summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2025-04-01 06:50:26 -0400
committerTavian Barnes <tavianator@tavianator.com>2025-04-01 07:28:02 -0400
commita662fda2642e17478bc8e78adb4c6642a8505cdb (patch)
tree95107aed722b822a72856f75bd2746b0318885b8
parentb1fe97289315cfb6278eec4554b92776df98f28d (diff)
downloadbfs-a662fda2642e17478bc8e78adb4c6642a8505cdb.tar.xz
parse: Only process the last -files0-from
GNU find intentionally makes later -files0-from options override earlier ones, for symmetry with similar features like du --files0-from. Change bfs to match. Link: https://savannah.gnu.org/bugs/?66965
-rw-r--r--src/parse.c122
-rw-r--r--tests/bfs/files0_from_stdin_twice.sh1
-rw-r--r--tests/gnu/files0_from_empty.sh2
-rw-r--r--tests/gnu/files0_from_file_file.out (renamed from tests/bfs/files0_from_twice.out)2
-rw-r--r--tests/gnu/files0_from_file_file.sh (renamed from tests/bfs/files0_from_twice.sh)0
-rw-r--r--tests/gnu/files0_from_ok.sh1
-rw-r--r--tests/gnu/files0_from_stdin_ok.sh1
-rw-r--r--tests/gnu/files0_from_stdin_ok_file.out45
-rw-r--r--tests/gnu/files0_from_stdin_ok_file.sh4
-rw-r--r--tests/gnu/files0_from_stdin_stdin.out45
-rw-r--r--tests/gnu/files0_from_stdin_stdin.sh2
-rw-r--r--tests/gnu/ok_files0_from.sh1
-rw-r--r--tests/gnu/ok_files0_from_stdin.sh1
-rw-r--r--tests/gnu/ok_flush.sh2
14 files changed, 168 insertions, 61 deletions
diff --git a/src/parse.c b/src/parse.c
index 9631b91..a19e689 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -84,8 +84,6 @@ struct bfs_parser {
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. */
@@ -105,6 +103,8 @@ struct bfs_parser {
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;
@@ -427,7 +427,6 @@ static int parse_root(struct bfs_parser *parser, const char *path) {
return -1;
}
- parser->implicit_root = false;
return 0;
}
@@ -1351,51 +1350,14 @@ static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, i
return NULL;
}
- const char *from = expr->argv[1];
-
- FILE *file;
- if (strcmp(from, "-") == 0) {
- if (!consume_stdin(parser, expr)) {
- return NULL;
- }
- file = stdin;
- } else {
- file = xfopen(from, O_RDONLY | O_CLOEXEC);
- }
- if (!file) {
- parse_expr_error(parser, expr, "%s.\n", errstr());
- return NULL;
- }
-
- 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);
- }
-
- parser->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 != stdin) {
- fclose(file);
- }
- return NULL;
}
/**
@@ -3546,6 +3508,55 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) {
return expr;
}
+/** Handle -files0-from after parsing. */
+static int parse_files0_roots(struct bfs_parser *parser) {
+ const struct bfs_expr *expr = parser->files0_expr;
+ 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.
*/
@@ -3571,6 +3582,16 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) {
return NULL;
}
+ 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) {
@@ -3842,7 +3863,6 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) {
.stdout_tty = stdout_tty,
.use_color = use_color,
.implicit_print = true,
- .implicit_root = true,
.just_info = false,
.excluding = false,
.last_arg = NULL,
@@ -3879,12 +3899,6 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) {
goto fail;
}
- if (ctx->npaths == 0 && parser.implicit_root) {
- if (parse_root(&parser, ".") != 0) {
- goto fail;
- }
- }
-
if ((ctx->flags & BFTW_FOLLOW_ALL) && !ctx->unique) {
// We need bftw() to detect cycles unless -unique does it for us
ctx->flags |= BFTW_DETECT_CYCLES;
diff --git a/tests/bfs/files0_from_stdin_twice.sh b/tests/bfs/files0_from_stdin_twice.sh
deleted file mode 100644
index 752e9de..0000000
--- a/tests/bfs/files0_from_stdin_twice.sh
+++ /dev/null
@@ -1 +0,0 @@
-! invoke_bfs -files0-from - -files0-from - </dev/null
diff --git a/tests/gnu/files0_from_empty.sh b/tests/gnu/files0_from_empty.sh
index 85eee8f..7b42772 100644
--- a/tests/gnu/files0_from_empty.sh
+++ b/tests/gnu/files0_from_empty.sh
@@ -1 +1 @@
-! printf "\0" | invoke_bfs -files0-from -
+! printf '\0' | invoke_bfs -files0-from -
diff --git a/tests/bfs/files0_from_twice.out b/tests/gnu/files0_from_file_file.out
index 5087ae9..fb683c7 100644
--- a/tests/bfs/files0_from_twice.out
+++ b/tests/gnu/files0_from_file_file.out
@@ -1,4 +1,2 @@
-basic/c
-basic/c/d
basic/g
basic/g/h
diff --git a/tests/bfs/files0_from_twice.sh b/tests/gnu/files0_from_file_file.sh
index 1119952..1119952 100644
--- a/tests/bfs/files0_from_twice.sh
+++ b/tests/gnu/files0_from_file_file.sh
diff --git a/tests/gnu/files0_from_ok.sh b/tests/gnu/files0_from_ok.sh
deleted file mode 100644
index 8e145ce..0000000
--- a/tests/gnu/files0_from_ok.sh
+++ /dev/null
@@ -1 +0,0 @@
-! printf "basic\0" | invoke_bfs -files0-from - -ok echo {} \;
diff --git a/tests/gnu/files0_from_stdin_ok.sh b/tests/gnu/files0_from_stdin_ok.sh
new file mode 100644
index 0000000..0283c8d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok.sh
@@ -0,0 +1 @@
+! printf 'basic\0' | invoke_bfs -files0-from - -ok echo {} \;
diff --git a/tests/gnu/files0_from_stdin_ok_file.out b/tests/gnu/files0_from_stdin_ok_file.out
new file mode 100644
index 0000000..0f6b00d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok_file.out
@@ -0,0 +1,45 @@
+
+
+
+
+
+ /j
+ /j
+!
+!-
+!-/e
+!-/e
+!/d
+!/d
+(
+(-
+(-/c
+(-/c
+(/b
+(/b
+)
+)/g
+)/g
+*
+*/m
+*/m
+,
+,/f
+,/f
+-
+-/a
+-/a
+...
+.../h
+.../h
+/n
+/n
+[
+[/k
+[/k
+\
+\/i
+\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/files0_from_stdin_ok_file.sh b/tests/gnu/files0_from_stdin_ok_file.sh
new file mode 100644
index 0000000..028df0c
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_ok_file.sh
@@ -0,0 +1,4 @@
+FILE="$TMP/$TEST.in"
+cd weirdnames
+invoke_bfs -mindepth 1 -fprintf "$FILE" "%P\0"
+yes | bfs_diff -files0-from - -ok printf '%s\n' {} \; -files0-from "$FILE"
diff --git a/tests/gnu/files0_from_stdin_stdin.out b/tests/gnu/files0_from_stdin_stdin.out
new file mode 100644
index 0000000..0f6b00d
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_stdin.out
@@ -0,0 +1,45 @@
+
+
+
+
+
+ /j
+ /j
+!
+!-
+!-/e
+!-/e
+!/d
+!/d
+(
+(-
+(-/c
+(-/c
+(/b
+(/b
+)
+)/g
+)/g
+*
+*/m
+*/m
+,
+,/f
+,/f
+-
+-/a
+-/a
+...
+.../h
+.../h
+/n
+/n
+[
+[/k
+[/k
+\
+\/i
+\/i
+{
+{/l
+{/l
diff --git a/tests/gnu/files0_from_stdin_stdin.sh b/tests/gnu/files0_from_stdin_stdin.sh
new file mode 100644
index 0000000..8f6368f
--- /dev/null
+++ b/tests/gnu/files0_from_stdin_stdin.sh
@@ -0,0 +1,2 @@
+cd weirdnames
+invoke_bfs -mindepth 1 -printf "%P\0" | bfs_diff -files0-from - -files0-from -
diff --git a/tests/gnu/ok_files0_from.sh b/tests/gnu/ok_files0_from.sh
deleted file mode 100644
index 0e2818b..0000000
--- a/tests/gnu/ok_files0_from.sh
+++ /dev/null
@@ -1 +0,0 @@
-! printf "basic\0" | invoke_bfs -ok echo {} \; -files0-from -
diff --git a/tests/gnu/ok_files0_from_stdin.sh b/tests/gnu/ok_files0_from_stdin.sh
new file mode 100644
index 0000000..2c4de7b
--- /dev/null
+++ b/tests/gnu/ok_files0_from_stdin.sh
@@ -0,0 +1 @@
+! printf 'basic\0' | invoke_bfs -ok echo {} \; -files0-from -
diff --git a/tests/gnu/ok_flush.sh b/tests/gnu/ok_flush.sh
index 87c7298..a5dc0d0 100644
--- a/tests/gnu/ok_flush.sh
+++ b/tests/gnu/ok_flush.sh
@@ -1,4 +1,4 @@
# I/O streams should be flushed before -ok prompts
-yes | invoke_bfs basic -printf '%p ? ' -ok echo found \; 2>&1 | tr '\0' ' ' | sed 's/?.*?/?/' >"$OUT"
+yes | invoke_bfs basic -printf '%p ? ' -ok echo found \; 2>&1 | sed 's/?.*?/?/' >"$OUT"
sort_output
diff_output