Fix filter matching of directory specifications

This commit is contained in:
Andrea Mazzoleni 2026-04-01 00:32:07 +02:00
parent f76e617f70
commit ea5f8d0f5f
3 changed files with 40 additions and 16 deletions

View file

@ -186,27 +186,35 @@ static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter**
path += strlen(filter->root);
}
/* match dirs with dirs and files with files */
if (filter->is_dir && !is_dir)
return 0;
/* if the filter is for files, it doesn't applies to directories */
if (!filter->is_dir && is_dir)
return 0;
/*
* If the filter is for directories, it should be applied to all files inside
* that directory.
*
* This is done allowing a partial matching as far it ends at a directory separator
*/
int match_sub = 0;
if (filter->is_dir && !is_dir)
match_sub = 1;
int ret = 0;
if (filter->is_abs) {
/* skip initial slash, as always missing in the path */
if (wnmatch(filter->pattern + 1, path) == 0)
if (wnmatch_sub(filter->pattern + 1, path, match_sub) == 0)
ret = filter->direction;
} else {
/* the path is relative, first try to match from the root */
if (wnmatch(filter->pattern, path) == 0) {
if (wnmatch_sub(filter->pattern, path, match_sub) == 0) {
ret = filter->direction;
} else {
/* then try to match after all the / presents */
char* slash = strchr(path, '/');
while (slash) {
if (wnmatch(filter->pattern, slash + 1) == 0) {
if (wnmatch_sub(filter->pattern, slash + 1, match_sub) == 0) {
ret = filter->direction;
break;
}

View file

@ -2541,7 +2541,7 @@ static const char* match_class(const char* p, char t)
return p;
}
int wnmatch(const char* p, const char* t)
int wnmatch_sub(const char* p, const char* t, int match_sub)
{
char p1 = 0; /* previous char */
while (*p) {
@ -2582,20 +2582,20 @@ int wnmatch(const char* p, const char* t)
*/
if (*p == '/' && (p1 == 0 || p1 == '/')) {
/* try reducing to nothing */
if (wnmatch(p + 1, t) == 0)
if (wnmatch_sub(p + 1, t, match_sub) == 0)
return 0;
/* otherwise / should match in the text */
}
/* try matching with 0 or more characters */
while (*t) {
if (wnmatch(p, t) == 0)
if (wnmatch_sub(p, t, match_sub) == 0)
return 0;
++t;
}
/* try matching at the end */
return wnmatch(p, t);
return wnmatch_sub(p, t, match_sub);
}
} else {
/* skip the * */
@ -2611,13 +2611,13 @@ int wnmatch(const char* p, const char* t)
/* try matching with 0 or more characters */
while (*t && *t != '/') {
if (wnmatch(p, t) == 0)
if (wnmatch_sub(p, t, match_sub) == 0)
return 0;
++t;
}
/* try matching at the end */
return wnmatch(p, t);
return wnmatch_sub(p, t, match_sub);
case '[' :
/* character class */
if (*t == 0 || *t == '/')
@ -2640,7 +2640,13 @@ int wnmatch(const char* p, const char* t)
p1 = p0;
}
/* match successful if we've consumed all text */
return *t != 0;
/* if we match sub directory */
if (match_sub) {
/* match successfully only if we are at a directory border */
return *t != '/';
} else {
/* match successfully if we've consumed all text */
return *t != 0;
}
}

View file

@ -523,14 +523,24 @@ void thread_yield(void);
/*
* Wild match function.
*
* If match_sub is !=0, it matches sub directory. Specifically it matches if the
* string "t" is not fully consumed and the next character to consume is a /.
*
* - ? matches any single character except /
* - * matches any sequence of characters except /
* - ** (nearby a /) matches everything including /
* - ** (not near a /) like *
* - ##/ reduces to nothing in addition to the normal matching of ** (using # instead of * to mess the C comment)
* - ##/ reduces to nothing in addition to the normal matching of ** (using # instead of * to not mess the C comment)
* - [...] matches character classes with support for ranges and negation lile [!...] or [^...], except /
*
* \return 0 if it matches
*/
int wnmatch(const char* p, const char* t);
int wnmatch_sub(const char* p, const char* t, int match_sub);
static inline int wnmatch(const char* p, const char* t)
{
return wnmatch_sub(p, t, 0);
}
#endif