find: fix handling of trailing slashes in -name PATTERN comparisons
[oweals/busybox.git] / findutils / find.c
index d71c697827079c7d12e3c627beae89c33e528377..27698e537a1d68fc4f1ed7e92e9b0add574cd261 100644 (file)
@@ -502,26 +502,54 @@ static char *strcpy_upcase(char *dst, const char *src)
 
 ACTF(name)
 {
+       int r;
        const char *tmp = bb_basename(fileName);
-       if (tmp != fileName && *tmp == '\0') {
-               /* "foo/bar/". Oh no... go back to 'b' */
-               tmp--;
-               while (tmp != fileName && *--tmp != '/')
-                       continue;
-               if (*tmp == '/')
-                       tmp++;
+       /* GNU findutils: find DIR/ -name DIR
+        * prints "DIR/" (DIR// prints "DIR//" etc).
+        * Need to strip trailing "/".
+        * Such names can come only from top-level names, but
+        * we can't do this before recursive_action() call,
+        * since then "find FILE/ -name FILE"
+        * would also work (on non-directories), which is wrong.
+        */
+       char *trunc_slash = NULL;
+
+       if (*tmp == '\0') {
+               /* "foo/bar/[//...]" */
+               while (tmp != fileName && tmp[-1] == '/')
+                       tmp--;
+               if (tmp == fileName) { /* entire fileName is "//.."? */
+                       /* yes, convert "//..." to "/"
+                        * Testcases:
+                        * find / -maxdepth 1 -name /: prints /
+                        * find // -maxdepth 1 -name /: prints //
+                        * find / -maxdepth 1 -name //: prints nothing
+                        * find // -maxdepth 1 -name //: prints nothing
+                        */
+                       if (tmp[1])
+                               trunc_slash = (char*)tmp + 1;
+               } else {
+                       /* no, it's "foo/bar/[//...]", go back to 'b' */
+                       trunc_slash = (char*)tmp;
+                       while (tmp != fileName && tmp[-1] != '/')
+                               tmp--;
+               }
        }
+
        /* Was using FNM_PERIOD flag too,
         * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
         * find -name '*foo' should match .foo too:
         */
+       if (trunc_slash) *trunc_slash = '\0';
 #if FNM_CASEFOLD
-       return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
+       r = fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0));
 #else
        if (ap->iname)
                tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
-       return fnmatch(ap->pattern, tmp, 0) == 0;
+       r = fnmatch(ap->pattern, tmp, 0);
 #endif
+       if (trunc_slash) *trunc_slash = '/';
+       return r == 0;
 }
 
 #if ENABLE_FEATURE_FIND_PATH