From 8a7f55d82b4d7ad40be25546c25f34282f1e3a86 Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <mail@bernhard-voelker.de>
Date: Tue, 29 Mar 2022 00:24:47 +0200
Subject: [PATCH 2/2] find: omit warning diagnostic for -name '/'

Although usually a pattern containing a directory separator does not match
anything, a pattern solely consisting of one '/' still does (and has to)
for the root directory "/".

* find/parser.c (check_name_arg): Omit the warning in the case the
given pattern equals "/".
find/find.1 (-name): Clarify better that the pattern "/" is valid to
match the "/" directory.

* tests/find/name-slash.sh: Add test.
* tests/local.mk (all_tests): Reference it.
* MEWS (Bug Fixes): Mention the fix.

Fixes https://savannah.gnu.org/bugs/?62227
---
 NEWS                     |  6 +++++
 find/find.1              | 25 +++++++++--------
 find/parser.c            | 14 +++++-----
 tests/find/name-slash.sh | 58 ++++++++++++++++++++++++++++++++++++++++
 tests/local.mk           |  1 +
 5 files changed, 86 insertions(+), 18 deletions(-)
 create mode 100755 tests/find/name-slash.sh

diff --git a/NEWS b/NEWS
index b9e28fb9..6b8c3493 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,12 @@ GNU findutils NEWS - User visible changes.      -*- outline -*- (allout)
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+** Bug Fixes
+
+  'find -name /' no longer outputs a warning, because that is a valid pattern
+  to match the root directory "/".  Previously, a diagnostic falsely claimed
+  that this pattern would not match anything. [#62227]
+
 
 * Noteworthy changes in release 4.9.0 (2022-02-22) [stable]
 
diff --git a/find/find.1 b/find/find.1
index 6bad0096..b3ec9753 100644
--- a/find/find.1
+++ b/find/find.1
@@ -848,20 +848,23 @@ modification times.
 Base of file name (the path with the leading directories removed)
 matches shell pattern
 .IR pattern .
-Because the leading directories are removed,
-the file names considered for a match with
-.B \-name
-will never include a slash, so `\-name a/b' will never match anything
-(you probably need to use
+Because the leading directories of the file names are removed, the
+.I pattern
+should not include a slash, because `\-name a/b' will never match anything
+(and you probably want to use
 .B \-path
 instead).
-A warning is issued if you try to do this,
-unless the environment variable
+An exception to this is when using only a slash as \fIpattern\fR (`-name /'),
+because that is a valid string for matching the root directory "/" (because the
+base name of "/" is "/").
+A warning is issued if you try to pass a pattern containing a - but not
+consisting solely of one - slash, unless the environment variable
 .B POSIXLY_CORRECT
-is set.
-The metacharacters (`*', `?',
-and `[]') match a `.\&' at the start of the base name (this is a change
-in findutils-4.2.2; see section STANDARDS CONFORMANCE below).  To ignore a
+is set or the option
+.B \-nowarn
+is used.
+
+To ignore a
 directory and the files under it, use
 .B \-prune
 rather than checking every file in the tree;
diff --git a/find/parser.c b/find/parser.c
index 75c730c3..5774bd94 100644
--- a/find/parser.c
+++ b/find/parser.c
@@ -1270,16 +1270,16 @@ fnmatch_sanitycheck (void)
 static void
 check_name_arg (const char *pred, const char *alt, const char *arg)
 {
-  if (should_issue_warnings () && strchr (arg, '/'))
+  if (should_issue_warnings () && strchr (arg, '/') && (0 != strcmp ("/", arg)))
     {
       error (0, 0,
 	     _("warning: %s matches against basenames only, "
-	       "but the given pattern contains a directory separator (%s), "
-	       "thus the expression will evaluate to false all the time.  "
-	       "Did you mean %s?"),
-	    safely_quote_err_filename (0, pred),
-	    safely_quote_err_filename (1, "/"),
-	    safely_quote_err_filename (2, alt));
+		"but the given pattern contains a directory separator (%s), "
+		"thus the expression will evaluate to false all the time.  "
+		"Did you mean %s?"),
+	     safely_quote_err_filename (0, pred),
+	     safely_quote_err_filename (1, "/"),
+	     safely_quote_err_filename (2, alt));
     }
 }
 
diff --git a/tests/find/name-slash.sh b/tests/find/name-slash.sh
new file mode 100755
index 00000000..dfe9b825
--- /dev/null
+++ b/tests/find/name-slash.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+# Exercise 'find -name PATTERN' behavior with a '/' in PATTERN.
+
+# Copyright (C) 2022 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
+
+unset POSIXLY_CORRECT
+
+# Exercise '-name PATTERN' with a '/' somewhere in PATTERN.
+find -name 'dir/file' > out 2> err || fail=1
+compare /dev/null out || fail=1
+grep 'warning: .*matches against basenames only.* will evaluate to false' err \
+  || { cat err; fail=1; }
+
+# Likewise in POSIX environment.
+POSIXLY_CORRECT=1 find -name 'dir/file' > out 2> err || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+
+# Likewise with -nowarn.
+find -nowarn -name 'dir/file' > out 2> err || fail=1
+compare /dev/null out || fail=1
+compare /dev/null err || fail=1
+
+# Exercise '-name /', i.e., PATTERN just being "/": no warning because this
+# is a valid basename in the (trivial) case comparing to root directory "/".
+echo '/' > exp || framework_failure_
+find / -maxdepth 0 -name '/' > out 2> err || fail=1
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
+# Exercise '-name /' in POSIX environment.
+POSIXLY_CORRECT=1 find / -maxdepth 0 -name '/' > out 2> err || fail=1
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
+# Exercise '-name /' with the -warn option.
+find / -warn -maxdepth 0 -name '/' > out 2> err || fail=1
+compare exp out || fail=1
+compare /dev/null err || fail=1
+
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index e75a9130..4e8efd41 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -110,6 +110,7 @@ all_tests = \
   tests/find/inode-zero.sh \
   tests/find/many-dir-entries-vs-OOM.sh \
   tests/find/name-lbracket-literal.sh \
+  tests/find/name-slash.sh \
   tests/find/printf_escapechars.sh \
   tests/find/printf_escape_c.sh \
   tests/find/printf_inode.sh \
-- 
2.35.1

