A "+" only terminates -exec when it immediately follows an argument which is exactly "{}" (and not, for example, "{}x"). This fixes Savannah bug 66365.
* NEWS: explain this change. * doc/find.texi: update one place which omitted the '{}' before '+'. * find/parser.c (insert_exec_ok): consider + to be special ony if it follows an argument which is exactly '{}'. * tests/find/sv-bug-66365-exec.sh: test for this bug. * tests/local.mk: add the new test file. --- NEWS | 6 ++++++ doc/find.texi | 2 +- find/parser.c | 23 +++++++++++++++-------- tests/find/sv-bug-66365-exec.sh | 27 +++++++++++++++++++++++++++ tests/local.mk | 1 + 5 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 tests/find/sv-bug-66365-exec.sh diff --git a/NEWS b/NEWS index ae8c4a26..3276887a 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,12 @@ GNU findutils NEWS - User visible changes. -*- outline -*- (allout) 'find -ignore_readdir_race' now has a race between FTS read and the visiting of the entry when the file was removed. [#45930] + To fix a POSIX compatibility bug, -exec foo Z{} + is no longer a + complete predicate, because '+' is only a terminator when it follows + an argument which is exactly '{}'. The findutils documentation + already states this, and now find's behaviour matches the + documentation. + ** Documentation Changes The forthcoming Issue 8 of the POSIX standard will standardise "find diff --git a/doc/find.texi b/doc/find.texi index 4cae7419..9f63c8fb 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -1536,7 +1536,7 @@ This is different to @samp{-prune} because @samp{-prune} only applies to the contents of pruned directories, while @samp{-quit} simply makes @code{find} stop immediately. No child processes will be left running. Any command lines which have been built by @samp{-exec -... \+} or @samp{-execdir ... \+} are invoked before the program is +... @{@} +} or @samp{-execdir ... \+} are invoked before the program is exited. After @samp{-quit} is executed, no more files specified on the command line will be processed. For example, @samp{find /tmp/foo /tmp/bar -print -quit} will print only @samp{/tmp/foo}. One common diff --git a/find/parser.c b/find/parser.c index ad3b9904..1b389b3c 100644 --- a/find/parser.c +++ b/find/parser.c @@ -2770,7 +2770,7 @@ insert_exec_ok (const char *action, { int start, end; /* Indexes in ARGV of start & end of cmd. */ int i; /* Index into cmd args */ - int saw_braces; /* True if previous arg was '{}'. */ + bool prev_was_braces_only; /* Previous arg was '{}' (not e.g. 'Q' or '{}x'). */ bool allow_plus; /* True if + is a valid terminator */ int brace_count; /* Number of instances of {}. */ const char *brace_arg; /* Which arg did {} appear in? */ @@ -2827,28 +2827,35 @@ insert_exec_ok (const char *action, * Also figure out if the command is terminated by ";" or by "+". */ start = *arg_ptr; - for (end = start, saw_braces=0, brace_count=0, brace_arg=NULL; + for (end = start, prev_was_braces_only=false, brace_count=0, brace_arg=NULL; (argv[end] != NULL) && ((argv[end][0] != ';') || (argv[end][1] != '\0')); end++) { /* For -exec and -execdir, "{} +" can terminate the command. */ - if ( allow_plus - && argv[end][0] == '+' && argv[end][1] == 0 - && saw_braces) + if (allow_plus && prev_was_braces_only + && argv[end][0] == '+' && argv[end][1] == 0) { our_pred->args.exec_vec.multiple = 1; break; } - saw_braces = 0; + prev_was_braces_only = false; if (mbsstr (argv[end], "{}")) { - saw_braces = 1; + if (0 == strcmp(argv[end], "{}")) + { + /* Savannah bug 66365: + only terminates the predicate + * immediately after an argument which is exactly, "{}". + * However, the "{}" in "x{}" should get expanded for + * the ";" case. + */ + prev_was_braces_only = true; + } brace_arg = argv[end]; ++brace_count; - if (0 == end && (func == pred_execdir || func == pred_okdir)) + if (start == end && (func == pred_execdir || func == pred_okdir)) { /* The POSIX standard says that {} replacement should * occur even in the utility name. This is insecure diff --git a/tests/find/sv-bug-66365-exec.sh b/tests/find/sv-bug-66365-exec.sh new file mode 100644 index 00000000..79ac3f3d --- /dev/null +++ b/tests/find/sv-bug-66365-exec.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Test that find -exec ... + treats the + as a terminator only when it +# immediately follows a {}. See Savannah bug #66365. + +# Copyright (C) 2024 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +. "${srcdir=.}/tests/init.sh"; fu_path_prepend_ +print_ver_ find + +find . -prune -exec echo x{} + \; >| out +echo 'x. +' >| exp || framework_failure_ +compare exp out || fail=1 + +Exit $fail diff --git a/tests/local.mk b/tests/local.mk index 7a602c5e..34798451 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -130,6 +130,7 @@ sh_tests = \ tests/find/used.sh \ tests/find/newer.sh \ tests/find/opt-numeric-arg.sh \ + tests/find/sv-bug-66365-exec.sh \ tests/find/user-group-max.sh \ tests/xargs/conflicting_opts.sh \ tests/xargs/verbose-quote.sh \ -- 2.39.5