bug-findutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] find: -exec is terminated by + only if the prior arg is exactly


From: James Youngman
Subject: [PATCH] find: -exec is terminated by + only if the prior arg is exactly '{}'
Date: Sat, 2 Nov 2024 18:44:08 +0000

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]