From ccc9985c455753298a8799a4d12d5f531c67ae81 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 25 Nov 2016 20:14:33 +0100 Subject: find: fix handling of trailing slashes in -name PATTERN comparisons Signed-off-by: Denys Vlasenko --- findutils/find.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) (limited to 'findutils') diff --git a/findutils/find.c b/findutils/find.c index d71c69782..27698e537 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -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 -- cgit v1.2.3