mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 19:01:57 -04:00
Add support for ** in the patterns
This commit is contained in:
parent
36cef9ee89
commit
9f14dd94e5
55 changed files with 2274 additions and 1325 deletions
109
cmdline/elem.c
109
cmdline/elem.c
|
|
@ -96,27 +96,23 @@ struct snapraid_filter* filter_alloc_file(int direction, const char* root, const
|
|||
|
||||
if (first == 0) {
|
||||
/* no slash */
|
||||
filter->is_path = 0;
|
||||
filter->is_abs = 0;
|
||||
filter->is_dir = 0;
|
||||
} else if (first == last && last[1] == 0) {
|
||||
/* one slash at the end */
|
||||
filter->is_path = 0;
|
||||
filter->is_dir = 1;
|
||||
last[0] = 0;
|
||||
} else {
|
||||
/* at least a slash not at the end */
|
||||
filter->is_path = 1;
|
||||
if (last[1] == 0) {
|
||||
filter->is_dir = 1;
|
||||
last[0] = 0;
|
||||
if (first == filter->pattern) {
|
||||
/* slash at the start */
|
||||
filter->is_abs = 1;
|
||||
} else {
|
||||
filter->is_dir = 0;
|
||||
/* no slash at the start */
|
||||
filter->is_abs = 0;
|
||||
}
|
||||
|
||||
/* a slash must be the first char, as we don't support PATH/FILE and PATH/DIR/ */
|
||||
if (filter->pattern[0] != '/') {
|
||||
free(filter);
|
||||
return 0;
|
||||
if (last[1] == 0) {
|
||||
/* slash at the end */
|
||||
filter->is_dir = 1;
|
||||
last[0] = 0; /* clear the slash */
|
||||
} else {
|
||||
/* no slash at the end */
|
||||
filter->is_dir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +129,7 @@ struct snapraid_filter* filter_alloc_disk(int direction, const char* pattern)
|
|||
|
||||
/* it's a disk filter */
|
||||
filter->is_disk = 1;
|
||||
filter->is_path = 0;
|
||||
filter->is_abs = 0;
|
||||
filter->is_dir = 0;
|
||||
|
||||
/* no slash allowed in disk names */
|
||||
|
|
@ -171,9 +167,14 @@ const char* filter_type(struct snapraid_filter* filter, char* out, size_t out_si
|
|||
return out;
|
||||
}
|
||||
|
||||
static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* path, const char* name, int is_dir)
|
||||
static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* path, int is_dir)
|
||||
{
|
||||
int ret = 0;
|
||||
if (filter->root[0] != 0) { /* if it's a local filter */
|
||||
if (!path_is_root_of(filter->root, path)) /* applies it only if it matches the root */
|
||||
return 0;
|
||||
/* advance the path to compare to make it local */
|
||||
path += strlen(filter->root);
|
||||
}
|
||||
|
||||
/* match dirs with dirs and files with files */
|
||||
if (filter->is_dir && !is_dir)
|
||||
|
|
@ -181,13 +182,28 @@ static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter**
|
|||
if (!filter->is_dir && is_dir)
|
||||
return 0;
|
||||
|
||||
if (filter->is_path) {
|
||||
/* skip initial slash, as always missing from the path */
|
||||
if (fnmatch(filter->pattern + 1, path, FNM_PATHNAME | FNM_CASEINSENSITIVE_FOR_WIN) == 0)
|
||||
int ret = 0;
|
||||
|
||||
if (filter->is_abs) {
|
||||
/* skip initial slash, as always missing in the path */
|
||||
if (wnmatch(filter->pattern + 1, path) == 0)
|
||||
ret = filter->direction;
|
||||
} else {
|
||||
if (fnmatch(filter->pattern, name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
|
||||
/* the patch is relative, first try to match from the root */
|
||||
if (wnmatch(filter->pattern, path) == 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) {
|
||||
ret = filter->direction;
|
||||
break;
|
||||
}
|
||||
|
||||
slash = strchr(slash + 1, '/');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reason != 0 && ret < 0)
|
||||
|
|
@ -196,47 +212,6 @@ static int filter_apply(struct snapraid_filter* filter, struct snapraid_filter**
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int filter_recurse(struct snapraid_filter* filter, struct snapraid_filter** reason, const char* const_path, int is_dir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char* name;
|
||||
unsigned i;
|
||||
|
||||
if (filter->root[0] != 0) { /* if it's a local filter */
|
||||
if (!path_is_root_of(filter->root, const_path)) /* applies it only if it matches the root */
|
||||
return 0;
|
||||
/* advance the path to compare to make it local */
|
||||
const_path += strlen(filter->root);
|
||||
}
|
||||
|
||||
pathcpy(path, sizeof(path), const_path);
|
||||
|
||||
/* filter all the dir/subdir until we reach the final file/dir */
|
||||
name = path;
|
||||
for (i = 0; path[i] != 0; ++i) {
|
||||
if (path[i] == '/') {
|
||||
/* set a terminator */
|
||||
path[i] = 0;
|
||||
|
||||
/* filter the directory */
|
||||
if (filter_apply(filter, reason, path, name, 1) != 0)
|
||||
return filter->direction;
|
||||
|
||||
/* restore the slash */
|
||||
path[i] = '/';
|
||||
|
||||
/* next name */
|
||||
name = path + i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* filter the final file/dir */
|
||||
if (filter_apply(filter, reason, path, name, is_dir) != 0)
|
||||
return filter->direction;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int filter_element(tommy_list* filterlist, struct snapraid_filter** reason, const char* disk, const char* sub, int is_dir, int is_def_include)
|
||||
{
|
||||
tommy_node* i;
|
||||
|
|
@ -249,14 +224,14 @@ static int filter_element(tommy_list* filterlist, struct snapraid_filter** reaso
|
|||
struct snapraid_filter* filter = i->data;
|
||||
|
||||
if (filter->is_disk) {
|
||||
if (fnmatch(filter->pattern, disk, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
|
||||
if (wnmatch(filter->pattern, disk) == 0)
|
||||
ret = filter->direction;
|
||||
else
|
||||
ret = 0;
|
||||
if (reason != 0 && ret < 0)
|
||||
*reason = filter;
|
||||
} else {
|
||||
ret = filter_recurse(filter, reason, sub, is_dir);
|
||||
ret = filter_apply(filter, reason, sub, is_dir);
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ struct snapraid_filter {
|
|||
char pattern[PATH_MAX]; /**< Filter pattern. */
|
||||
char root[PATH_MAX]; /**< If empty, it's a global pattern. If not empty, it's a local pattern that applies only to that dir. */
|
||||
int is_disk; /**< If the pattern is a disk one, otherwise it's a file pattern */
|
||||
int is_path; /**< If the pattern is a complete path, otherwise it's just a file name */
|
||||
int is_abs; /**< If the pattern is an absolute path (start with /), otherwise it's a relative one */
|
||||
int is_dir; /**< If the pattern is only for dir, otherwise it's only for file. */
|
||||
int direction; /**< If it's an inclusion (=1) or an exclusion (=-1). */
|
||||
tommy_node node; /**< Next node in the list. */
|
||||
|
|
|
|||
|
|
@ -1,491 +0,0 @@
|
|||
/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library 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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#if !HAVE_FNMATCH
|
||||
|
||||
/* Enable GNU extensions in fnmatch.h. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#if HAVE_FNMATCH_H
|
||||
#include <fnmatch.h>
|
||||
#else
|
||||
#include "fnmatch.h"
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
#if HAVE_STRING_H || defined _LIBC
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#if defined STDC_HEADERS || defined _LIBC
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/* For platforms which support the ISO C amendment 1 functionality we
|
||||
support user defined character classes. */
|
||||
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
|
||||
# include <wchar.h>
|
||||
# include <wctype.h>
|
||||
#endif
|
||||
|
||||
/* Comment out all this code if we are using the GNU C Library, and are not
|
||||
actually compiling the library itself. This code is part of the GNU C
|
||||
Library, but also included in many other GNU distributions. Compiling
|
||||
and linking in this code is a waste when using the GNU C library
|
||||
(especially if it is a shared library). Rather than having every GNU
|
||||
program understand `configure --with-gnu-libc' and omit the object files,
|
||||
it is simpler to just do this in the source for each such file. */
|
||||
|
||||
#if defined _LIBC || !defined __GNU_LIBRARY__
|
||||
|
||||
|
||||
# if defined STDC_HEADERS || !defined isascii
|
||||
# define ISASCII(c) 1
|
||||
# else
|
||||
# define ISASCII(c) isascii(c)
|
||||
# endif
|
||||
|
||||
# ifdef isblank
|
||||
# define ISBLANK(c) (ISASCII (c) && isblank (c))
|
||||
# else
|
||||
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
|
||||
# endif
|
||||
# ifdef isgraph
|
||||
# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
|
||||
# else
|
||||
# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
|
||||
# endif
|
||||
|
||||
# define ISPRINT(c) (ISASCII (c) && isprint (c))
|
||||
# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
|
||||
# define ISALNUM(c) (ISASCII (c) && isalnum (c))
|
||||
# define ISALPHA(c) (ISASCII (c) && isalpha (c))
|
||||
# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
|
||||
# define ISLOWER(c) (ISASCII (c) && islower (c))
|
||||
# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
|
||||
# define ISSPACE(c) (ISASCII (c) && isspace (c))
|
||||
# define ISUPPER(c) (ISASCII (c) && isupper (c))
|
||||
# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
|
||||
|
||||
# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
|
||||
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
/* The GNU C library provides support for user-defined character classes
|
||||
and the functions from ISO C amendment 1. */
|
||||
# ifdef CHARCLASS_NAME_MAX
|
||||
# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
|
||||
# else
|
||||
/* This shouldn't happen but some implementation might still have this
|
||||
problem. Use a reasonable default value. */
|
||||
# define CHAR_CLASS_MAX_LENGTH 256
|
||||
# endif
|
||||
|
||||
# ifdef _LIBC
|
||||
# define IS_CHAR_CLASS(string) __wctype (string)
|
||||
# else
|
||||
# define IS_CHAR_CLASS(string) wctype (string)
|
||||
# endif
|
||||
# else
|
||||
# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
|
||||
|
||||
# define IS_CHAR_CLASS(string) \
|
||||
(STREQ (string, "alpha") || STREQ (string, "upper") \
|
||||
|| STREQ (string, "lower") || STREQ (string, "digit") \
|
||||
|| STREQ (string, "alnum") || STREQ (string, "xdigit") \
|
||||
|| STREQ (string, "space") || STREQ (string, "print") \
|
||||
|| STREQ (string, "punct") || STREQ (string, "graph") \
|
||||
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
|
||||
# endif
|
||||
|
||||
/* Avoid depending on library functions or files
|
||||
whose names are inconsistent. */
|
||||
|
||||
# if !defined _LIBC && !defined getenv
|
||||
extern char *getenv (const char*);
|
||||
# endif
|
||||
|
||||
# ifndef errno
|
||||
extern int errno;
|
||||
# endif
|
||||
|
||||
/* This function doesn't exist on most systems. */
|
||||
|
||||
# if !defined HAVE___STRCHRNUL && !defined _LIBC
|
||||
static char *
|
||||
__strchrnul (const char *s, int c)
|
||||
{
|
||||
char *result = strchr (s, c);
|
||||
if (result == NULL)
|
||||
result = strchr (s, '\0');
|
||||
return result;
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifndef internal_function
|
||||
/* Inside GNU libc we mark some function in a special way. In other
|
||||
environments simply ignore the marking. */
|
||||
# define internal_function
|
||||
# endif
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN, returning zero if
|
||||
it matches, nonzero if not. */
|
||||
static int internal_fnmatch __P ((const char *pattern, const char *string,
|
||||
int no_leading_period, int flags))
|
||||
internal_function;
|
||||
static int
|
||||
internal_function
|
||||
internal_fnmatch (const char *pattern, const char *string, int no_leading_period, int flags)
|
||||
{
|
||||
register const char *p = pattern, *n = string;
|
||||
register unsigned char c;
|
||||
|
||||
/* Note that this evaluates C many times. */
|
||||
# ifdef _LIBC
|
||||
# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
|
||||
# else
|
||||
# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
|
||||
# endif
|
||||
|
||||
while ((c = *p++) != '\0')
|
||||
{
|
||||
c = FOLD (c);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
else if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
return FNM_NOMATCH;
|
||||
else if (*n == '.' && no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (!(flags & FNM_NOESCAPE))
|
||||
{
|
||||
c = *p++;
|
||||
if (c == '\0')
|
||||
/* Trailing \ loses. */
|
||||
return FNM_NOMATCH;
|
||||
c = FOLD (c);
|
||||
}
|
||||
if (FOLD ((unsigned char) *n) != c)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (*n == '.' && no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
for (c = *p++; c == '?' || c == '*'; c = *p++)
|
||||
{
|
||||
if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
/* A slash does not match a wildcard under FNM_FILE_NAME. */
|
||||
return FNM_NOMATCH;
|
||||
else if (c == '?')
|
||||
{
|
||||
/* A ? needs to match one character. */
|
||||
if (*n == '\0')
|
||||
/* There isn't another character; no match. */
|
||||
return FNM_NOMATCH;
|
||||
else
|
||||
/* One character of the string is consumed in matching
|
||||
this ? wildcard, so *??? won't match if there are
|
||||
less than three characters. */
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
/* The wildcard(s) is/are the last element of the pattern.
|
||||
If the name is a file name and contains another slash
|
||||
this does mean it cannot match. */
|
||||
return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
|
||||
? FNM_NOMATCH : 0);
|
||||
else
|
||||
{
|
||||
const char *endp;
|
||||
|
||||
endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
|
||||
|
||||
if (c == '[')
|
||||
{
|
||||
int flags2 = ((flags & FNM_FILE_NAME)
|
||||
? flags : (flags & ~FNM_PERIOD));
|
||||
|
||||
for (--p; n < endp; ++n)
|
||||
if (internal_fnmatch (p, n,
|
||||
(no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME)))),
|
||||
flags2)
|
||||
== 0)
|
||||
return 0;
|
||||
}
|
||||
else if (c == '/' && (flags & FNM_FILE_NAME))
|
||||
{
|
||||
while (*n != '\0' && *n != '/')
|
||||
++n;
|
||||
if (*n == '/'
|
||||
&& (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
|
||||
flags) == 0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int flags2 = ((flags & FNM_FILE_NAME)
|
||||
? flags : (flags & ~FNM_PERIOD));
|
||||
|
||||
if (c == '\\' && !(flags & FNM_NOESCAPE))
|
||||
c = *p;
|
||||
c = FOLD (c);
|
||||
for (--p; n < endp; ++n)
|
||||
if (FOLD ((unsigned char) *n) == c
|
||||
&& (internal_fnmatch (p, n,
|
||||
(no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME)))),
|
||||
flags2) == 0))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we come here no match is possible with the wildcard. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
case '[':
|
||||
{
|
||||
/* Nonzero if the sense of the character class is inverted. */
|
||||
static int posixly_correct;
|
||||
register int not;
|
||||
char cold;
|
||||
|
||||
if (posixly_correct == 0)
|
||||
posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
|
||||
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (*n == '.' && no_leading_period && (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
/* `/' cannot be matched. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
|
||||
if (not)
|
||||
++p;
|
||||
|
||||
c = *p++;
|
||||
for (;;)
|
||||
{
|
||||
unsigned char fn = FOLD ((unsigned char) *n);
|
||||
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
{
|
||||
if (*p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
c = FOLD ((unsigned char) *p);
|
||||
++p;
|
||||
|
||||
if (c == fn)
|
||||
goto matched;
|
||||
}
|
||||
else if (c == '[' && *p == ':')
|
||||
{
|
||||
/* Leave room for the null. */
|
||||
char str[CHAR_CLASS_MAX_LENGTH + 1];
|
||||
size_t c1 = 0;
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
wctype_t wt;
|
||||
# endif
|
||||
const char *startp = p;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (c1 == CHAR_CLASS_MAX_LENGTH)
|
||||
/* The name is too long and therefore the pattern
|
||||
is ill-formed. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *++p;
|
||||
if (c == ':' && p[1] == ']')
|
||||
{
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (c < 'a' || c >= 'z')
|
||||
{
|
||||
/* This cannot possibly be a character class name.
|
||||
Match it as a normal range. */
|
||||
p = startp;
|
||||
c = '[';
|
||||
goto normal_bracket;
|
||||
}
|
||||
str[c1++] = c;
|
||||
}
|
||||
str[c1] = '\0';
|
||||
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
wt = IS_CHAR_CLASS (str);
|
||||
if (wt == 0)
|
||||
/* Invalid character class name. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (__iswctype (__btowc ((unsigned char) *n), wt))
|
||||
goto matched;
|
||||
# else
|
||||
if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
|
||||
|| (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
|
||||
|| (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
|
||||
|| (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
|
||||
|| (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
|
||||
|| (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
|
||||
|| (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
|
||||
|| (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
|
||||
|| (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
|
||||
|| (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
|
||||
|| (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
|
||||
|| (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
|
||||
goto matched;
|
||||
# endif
|
||||
}
|
||||
else if (c == '\0')
|
||||
/* [ (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
else
|
||||
{
|
||||
normal_bracket:
|
||||
if (FOLD (c) == fn)
|
||||
goto matched;
|
||||
|
||||
cold = c;
|
||||
c = *p++;
|
||||
|
||||
if (c == '-' && *p != ']')
|
||||
{
|
||||
/* It is a range. */
|
||||
unsigned char cend = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && cend == '\\')
|
||||
cend = *p++;
|
||||
if (cend == '\0')
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (cold <= fn && fn <= FOLD (cend))
|
||||
goto matched;
|
||||
|
||||
c = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == ']')
|
||||
break;
|
||||
}
|
||||
|
||||
if (!not)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
matched:
|
||||
/* Skip the rest of the [...] that already matched. */
|
||||
while (c != ']')
|
||||
{
|
||||
if (c == '\0')
|
||||
/* [... (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
{
|
||||
if (*p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
/* XXX 1003.2d11 is unclear if this is right. */
|
||||
++p;
|
||||
}
|
||||
else if (c == '[' && *p == ':')
|
||||
{
|
||||
do
|
||||
if (*++p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
while (*p != ':' || p[1] == ']');
|
||||
p += 2;
|
||||
c = *p;
|
||||
}
|
||||
}
|
||||
if (not)
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c != FOLD ((unsigned char) *n))
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
if (*n == '\0')
|
||||
return 0;
|
||||
|
||||
if ((flags & FNM_LEADING_DIR) && *n == '/')
|
||||
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
|
||||
return 0;
|
||||
|
||||
return FNM_NOMATCH;
|
||||
|
||||
# undef FOLD
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fnmatch (const char *pattern, const char *string, int flags)
|
||||
{
|
||||
return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
|
||||
}
|
||||
|
||||
#endif /* _LIBC or not __GNU_LIBRARY__. */
|
||||
|
||||
#else
|
||||
|
||||
/* avoid the warning: ISO C forbids an empty translation unit */
|
||||
extern int make_iso_compilers_happy;
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Library 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 <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _FNMATCH_H
|
||||
#define _FNMATCH_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
|
||||
# if !defined __GLIBC__ || !defined __P
|
||||
# undef __P
|
||||
# define __P(protos) protos
|
||||
# endif
|
||||
#else /* Not C++ or ANSI C. */
|
||||
# undef __P
|
||||
# define __P(protos) ()
|
||||
/* We can get away without defining `const' here only because in this file
|
||||
it is used only inside the prototype for `fnmatch', which is elided in
|
||||
non-ANSI C where `const' is problematical. */
|
||||
#endif /* C++ or ANSI C. */
|
||||
|
||||
#ifndef const
|
||||
# if (defined __STDC__ && __STDC__) || defined __cplusplus
|
||||
# define __const const
|
||||
# else
|
||||
# define __const
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* We #undef these before defining them because some losing systems
|
||||
(HP-UX A.08.07 for example) define these in <unistd.h>. */
|
||||
#undef FNM_PATHNAME
|
||||
#undef FNM_NOESCAPE
|
||||
#undef FNM_PERIOD
|
||||
#undef FNM_LEADING_DIR
|
||||
#undef FNM_CASEFOLD
|
||||
|
||||
/* Bits set in the FLAGS argument to `fnmatch'. */
|
||||
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
|
||||
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
|
||||
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
|
||||
|
||||
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
|
||||
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
|
||||
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
|
||||
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
|
||||
#endif
|
||||
|
||||
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
|
||||
#define FNM_NOMATCH 1
|
||||
|
||||
/* This value is returned if the implementation does not support
|
||||
`fnmatch'. Since this is not the case here it will never be
|
||||
returned but the conformance test suites still require the symbol
|
||||
to be defined. */
|
||||
#ifdef _XOPEN_SOURCE
|
||||
# define FNM_NOSYS (-1)
|
||||
#endif
|
||||
|
||||
/* Match NAME against the filename pattern PATTERN,
|
||||
returning zero if it matches, FNM_NOMATCH if not. */
|
||||
extern int fnmatch __P ((__const char *__pattern, __const char *__name,
|
||||
int __flags));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* fnmatch.h */
|
||||
|
|
@ -219,12 +219,6 @@
|
|||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_FNMATCH_H
|
||||
#include <fnmatch.h>
|
||||
#else
|
||||
#include "fnmatch.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
|
@ -274,15 +268,6 @@ typedef pthread_cond_t thread_cond_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Disable case check in Windows.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#define FNM_CASEINSENSITIVE_FOR_WIN FNM_CASEFOLD
|
||||
#else
|
||||
#define FNM_CASEINSENSITIVE_FOR_WIN 0
|
||||
#endif
|
||||
|
||||
#if HAVE_IO_H
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -590,6 +590,271 @@ bail:
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
struct {
|
||||
const char* pattern;
|
||||
const char* text;
|
||||
int result;
|
||||
} WNMATCH_TEST[] = {
|
||||
/* basic literal matching */
|
||||
{ "hello", "hello", 0 },
|
||||
{ "hello", "world", 1 },
|
||||
{ "", "", 0 },
|
||||
{ "hello", "", 1 },
|
||||
{ "", "hello", 1 },
|
||||
|
||||
/* single asterisk (*) */
|
||||
{ "*", "anything", 0 },
|
||||
{ "*", "", 0 },
|
||||
{ "*.txt", "file.txt", 0 },
|
||||
{ "*.txt", "file.doc", 1 },
|
||||
{ "*file*", "myfile.txt", 0 },
|
||||
{ "f*le", "file", 0 },
|
||||
{ "f*le", "fiiiile", 0 },
|
||||
{ "f*le", "folder", 1 },
|
||||
{ "a*b*c", "abc", 0 },
|
||||
{ "a*b*c", "aXbYc", 0 },
|
||||
{ "a*b*c", "aXXbYYc", 0 },
|
||||
{ "a*b*c", "ac", 1 },
|
||||
{ "***", "anything", 0 },
|
||||
{ "*.txt", "notes.txt", 0 },
|
||||
{ "*.txt", "folder/notes.txt", 1 },
|
||||
{ "*.js", "main.jsx", 1 },
|
||||
|
||||
/* single asterisk with / */
|
||||
{ "*", "a/b", 1 },
|
||||
{ "*.txt", "dir/file.txt", 1 },
|
||||
{ "dir/*.txt", "dir/file.txt", 0 },
|
||||
{ "*/*.txt", "dir/file.txt", 0 },
|
||||
{ "*/*.txt", "a/b/file.txt", 1 },
|
||||
|
||||
/* question mark (?) */
|
||||
{ "?", "a", 0 },
|
||||
{ "?", "ab", 1 },
|
||||
{ "?", "", 1 },
|
||||
{ "file?.txt", "file1.txt", 0 },
|
||||
{ "file?.txt", "fileA.txt", 0 },
|
||||
{ "file?.txt", "file10.txt", 1 },
|
||||
{ "???", "abc", 0 },
|
||||
{ "???", "ab", 1 },
|
||||
{ "a?c", "abc", 0 },
|
||||
{ "a?c", "ac", 1 },
|
||||
{ "file?.txt", "file1.txt", 0 },
|
||||
{ "file?.txt", "file12.txt", 1 },
|
||||
|
||||
/* question mark with / */
|
||||
{ "?", "/", 1 },
|
||||
{ "dir?file", "dir/file", 1 },
|
||||
{ "dir?file", "dirAfile", 0 },
|
||||
|
||||
/* character classes [...] */
|
||||
{ "[abc]", "a", 0 },
|
||||
{ "[abc]", "b", 0 },
|
||||
{ "[abc]", "c", 0 },
|
||||
{ "[abc]", "d", 1 },
|
||||
{ "[abc]", "", 1 },
|
||||
{ "file[0-9].txt", "file5.txt", 0 },
|
||||
{ "file[0-9].txt", "fileA.txt", 1 },
|
||||
{ "[a-z]", "m", 0 },
|
||||
{ "[A-Z]", "M", 0 },
|
||||
{ "[0-9a-f]", "a", 0 },
|
||||
{ "[0-9a-f]", "5", 0 },
|
||||
{ "[0-9a-f]", "g", 1 },
|
||||
{ "[a-z].js", "p.js", 0 },
|
||||
{ "[a-z].js", "1.js", 1 },
|
||||
{ "[0-9].txt", "a.txt", 1 },
|
||||
{ "[!a-z].js", "b.js", 1 },
|
||||
|
||||
/* negated character classes [!...] */
|
||||
{ "[!abc]", "d", 0 },
|
||||
{ "[!abc]", "a", 1 },
|
||||
{ "[!0-9]", "a", 0 },
|
||||
{ "[!0-9]", "5", 1 },
|
||||
{ "file[!0-9].txt", "filea.txt", 0 },
|
||||
{ "file[!0-9].txt", "file5.txt", 1 },
|
||||
{ "[^abc]", "d", 0 },
|
||||
{ "[^abc]", "a", 1 },
|
||||
|
||||
/* character classes with / */
|
||||
{ "[a-z]", "/", 1 },
|
||||
{ "dir[/]file", "dir/file", 1 },
|
||||
|
||||
#ifdef WIN32
|
||||
/* case */
|
||||
{ "hello", "HELLO", 0 },
|
||||
{ "Hello", "hello", 0 },
|
||||
{ "*.TXT", "file.txt", 0 },
|
||||
{ "FILE.txt", "file.TXT", 0 },
|
||||
{ "[a-z]", "A", 0 },
|
||||
{ "[ABC]", "b", 0 },
|
||||
{ "[a-z].js", "A.js", 0 },
|
||||
{ "[a-z]", "M", 0 },
|
||||
#else
|
||||
{ "hello", "HELLO", 1 },
|
||||
{ "Hello", "hello", 1 },
|
||||
{ "*.TXT", "file.txt", 1 },
|
||||
{ "FILE.txt", "file.TXT", 1 },
|
||||
{ "[a-z]", "A", 1 },
|
||||
{ "[ABC]", "b", 1 },
|
||||
{ "[a-z].js", "A.js", 1 },
|
||||
{ "[a-z]", "M", 1 },
|
||||
#endif
|
||||
|
||||
/* the /xx/ collapse case */
|
||||
{ "a/**/b", "a/b", 0 },
|
||||
{ "a/**/b", "a/x/b", 0 },
|
||||
|
||||
/* double asterisk (xx/) at start */
|
||||
{ "**/*.txt", "file.txt", 0 },
|
||||
{ "**/*.txt", "dir/file.txt", 0 },
|
||||
{ "**/*.txt", "a/b/c/file.txt", 0 },
|
||||
{ "**/*.txt", "file.doc", 1 },
|
||||
{ "**/test.txt", "test.txt", 0 },
|
||||
{ "**/test.txt", "a/b/test.txt", 0 },
|
||||
{ "**/test.txt", "a/b/other.txt", 1 },
|
||||
{ "**/*file*", "myfile.txt", 0 },
|
||||
{ "**/*file*", "dir/myfile.txt", 0 },
|
||||
|
||||
/* double asterisk (/xx) at end */
|
||||
{ "src/**", "src/", 0 },
|
||||
{ "src/**", "src/file.c", 0 },
|
||||
{ "src/**", "src/a/b/c/file.c", 0 },
|
||||
{ "src/**", "other/file.c", 1 },
|
||||
{ "src/**", "src", 1 },
|
||||
{ "dir/**", "dir/subdir/", 0 },
|
||||
|
||||
/* double asterisk (/xx/) in middle */
|
||||
{ "src/**/*.c", "src/file.c", 0 },
|
||||
{ "src/**/*.c", "src/lib/file.c", 0 },
|
||||
{ "src/**/*.c", "src/lib/util/file.c", 0 },
|
||||
{ "src/**/*.c", "src/file.h", 1 },
|
||||
{ "src/**/*.c", "other/file.c", 1 },
|
||||
{ "a/**/b/**/c", "a/b/c", 0 },
|
||||
{ "a/**/b/**/c", "a/x/b/y/c", 0 },
|
||||
{ "a/**/b/**/c", "a/x/y/b/z/w/c", 0 },
|
||||
{ "a/**/b", "a/x/y/z/b", 0 },
|
||||
{ "/docs/**/api.md", "/docs/api.md", 0 },
|
||||
{ "a/**/b", "a/xb", 1 },
|
||||
{ "a/**/b", "ax/b", 1 },
|
||||
{ "a/**/b", "a/c/xb", 1 },
|
||||
{ "a/**/b", "ax/c/b", 1 },
|
||||
{ "a/**/b", "a/x/y/c", 1 },
|
||||
{ "a/**/b", "axb", 1 },
|
||||
|
||||
/* multiple recursion segments */
|
||||
{ "**/**/file", "file", 0 },
|
||||
{ "**/**/file", "a/b/c/file", 0 },
|
||||
{ "a/**/b/**/c", "a/b/c", 0 },
|
||||
{ "a/**/b/**/c", "a/1/b/2/c", 0 },
|
||||
|
||||
/* combined patterns */
|
||||
{ "*.{txt,doc}", "file.txt", 1 },
|
||||
{ "file[0-9]*.txt", "file5abc.txt", 0 },
|
||||
{ "dir/*/file?.txt", "dir/sub/file1.txt", 0 },
|
||||
{ "**/src/**/*.c", "project/src/lib/file.c", 0 },
|
||||
|
||||
/* edge cases */
|
||||
{ "**", "anything", 0 },
|
||||
{ "a/**", "a/b", 0 },
|
||||
{ "a**", "aaa", 0 },
|
||||
{ "**a", "xxa", 0 },
|
||||
{ "a/**/", "a/b/", 0 },
|
||||
{ "a/**/b", "a/b", 0 },
|
||||
{ "a/**/b", "a//b", 0 },
|
||||
{ "*/**/file.txt", "a/file.txt", 0 },
|
||||
{ "*/**/file.txt", "a/b/c/file.txt", 0 },
|
||||
{ "a**/file.txt", "afile.txt", 1 },
|
||||
{ "/*/**/file.txt", "/file.txt", 1 },
|
||||
{ "*/**/file.txt", "/file.txt", 0 },
|
||||
{ "src/**/**/file.txt", "src/file.txt", 0 },
|
||||
{ "src/**/**/file.txt", "src/sub/file.txt", 0 },
|
||||
{ "src/***/file.txt", "src/file.txt", 0 },
|
||||
{ "src/***/file.txt", "src/sub/file.txt", 0 },
|
||||
{ "src/***/file.txt", "src/sub/sub/file.txt", 0 },
|
||||
{ "src/****/file.txt", "src/file.txt", 0 },
|
||||
{ "src/****/file.txt", "src/sub/file.txt", 0 },
|
||||
{ "src/****/file.txt", "src/sub/sub/file.txt", 0 },
|
||||
{ "**/build", "a/b/build", 0 },
|
||||
{ "src/**/test.js", "src/ui/test.js", 0 },
|
||||
{ "dist/**", "dist/bin/app.exe", 0 },
|
||||
{ "src-**", "src-folder/file.js", 1 },
|
||||
{ "**pkg/init.py", "libs/core/pkg/init.py", 1 },
|
||||
{ "**.log", "error.log", 0 },
|
||||
{ "**.log", "var/log/sys.log", 1 },
|
||||
{ "src/**.js", "src/app.js", 0 },
|
||||
{ "src/**.js", "src/components/ui/button.js", 0 }, /* <<<<< .GITIGNORE DIFFERS */
|
||||
{ "a**b.txt", "ab.txt", 0 },
|
||||
{ "a**b.txt", "axxb.txt", 0 },
|
||||
{ "a**b.txt", "a/b.txt", 1 },
|
||||
{ "a**b.txt", "a/subdir/b.txt", 1 },
|
||||
{ "a**b.txt", "a_folder/sub/b.txt", 1 },
|
||||
{ "a**b.txt", "a_folder/sub/folter_b.txt", 1 },
|
||||
{ "**/build", "build", 0 },
|
||||
{ "**/build", "project/out/build", 0 },
|
||||
|
||||
/* negative tests */
|
||||
{ "/docs/**/api.md", "docs/api.txt", 1 },
|
||||
{ "a/**/b", "a/b/c", 1 },
|
||||
{ "static/**", "static", 1 },
|
||||
{ "src/*.js", "src/ui/app.js", 1 },
|
||||
{ "/config.*", "etc/config.json", 1 },
|
||||
{ "*/*/*.c", "main.c", 1 },
|
||||
{ "a/*/b", "a/b", 1 },
|
||||
{ "a/**/b", "axb", 1 },
|
||||
{ "a/**/b", "ab", 1 },
|
||||
{ "/**/logs", "logs", 1 },
|
||||
{ "/src/**/logs", "src/web/log", 1 },
|
||||
{ "foo//bar", "foo/bar", 1 },
|
||||
{ "a/**/b", "a/b/", 1 },
|
||||
{ "a**b**c", "acb", 1 },
|
||||
{ "src-**-pkg", "src-pkg", 1 },
|
||||
{ "**/test/*.js", "test/ui/app.js", 1 },
|
||||
|
||||
/* complex real-world patterns */
|
||||
{ "**/.git/**", ".git/config", 0 },
|
||||
{ "**/.git/**", "project/.git/hooks/pre-commit", 0 },
|
||||
{ "**/node_modules/**", "node_modules/pkg/index.js", 0 },
|
||||
{ "**/node_modules/**", "app/node_modules/pkg/file.js", 0 },
|
||||
{ "src/**/*.{c,h}", "src/main.c", 1 },
|
||||
{ "**/test_*.py", "test_example.py", 0 },
|
||||
{ "**/test_*.py", "tests/test_feature.py", 0 },
|
||||
|
||||
/* performance/stress patterns */
|
||||
{ "a*b*c*d*e*f*g*h*i*j*k", "abcdefghijk", 0 },
|
||||
{ "a*b*c*d*e*f*g*h*i*j*k", "aXbXcXdXeXfXgXhXiXjXk", 0 },
|
||||
{ "**/**/**/*.txt", "a/b/c/d/e/f.txt", 0 },
|
||||
|
||||
/* trailing/leading slashes */
|
||||
{ "dir/", "dir/", 0 },
|
||||
{ "/root/*", "/root/file", 0 },
|
||||
|
||||
/* complex embedded patterns */
|
||||
{ "src-**-pkg/*.js", "src-web-pkg/main.js", 0 },
|
||||
{ "src-**-pkg/*.js", "src-lazy-load-ui-pkg/main.js", 0 },
|
||||
{ "src-**-pkg/*.js", "src-web-pkg/subdir/main.js", 1 },
|
||||
|
||||
/* anchoring and slashes */
|
||||
{ "/root.txt", "root.txt", 1 },
|
||||
{ "/root.txt", "subdir/root.txt", 1 },
|
||||
{ "docs/", "docs", 1 },
|
||||
{ "docs/", "docs/", 0 },
|
||||
{ "**/temp/", "src/temp/", 0 },
|
||||
{ "**/temp/", "src/temp", 1 },
|
||||
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static void test_wnmatch(void)
|
||||
{
|
||||
for (int i = 0; WNMATCH_TEST[i].pattern; ++i) {
|
||||
if (wnmatch(WNMATCH_TEST[i].pattern, WNMATCH_TEST[i].text) != WNMATCH_TEST[i].result) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Failed wnmatch test %s %s, expected %d\n", WNMATCH_TEST[i].pattern, WNMATCH_TEST[i].text, WNMATCH_TEST[i].result);
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void selftest(void)
|
||||
{
|
||||
log_tag("selftest:\n");
|
||||
|
|
@ -614,6 +879,7 @@ void selftest(void)
|
|||
test_hash();
|
||||
test_crc32c();
|
||||
test_tommy();
|
||||
test_wnmatch();
|
||||
if (raid_selftest() != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Failed SELF test\n");
|
||||
|
|
|
|||
|
|
@ -1000,7 +1000,6 @@ int snapraid_main(int argc, char* argv[])
|
|||
if (!filter) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Invalid filter specification '%s'\n", optarg);
|
||||
log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -490,13 +490,13 @@ static void state_config_check(struct snapraid_state* state, const char* path, t
|
|||
struct snapraid_filter* filter = i->data;
|
||||
for (j = state->disklist; j != 0; j = j->next) {
|
||||
struct snapraid_disk* disk = j->data;
|
||||
if (fnmatch(filter->pattern, disk->name, FNM_CASEINSENSITIVE_FOR_WIN) == 0)
|
||||
if (wnmatch(filter->pattern, disk->name) == 0)
|
||||
break;
|
||||
}
|
||||
if (j == 0) {
|
||||
/* check matching with parity disks */
|
||||
for (l = 0; l < state->level; ++l)
|
||||
if (fnmatch(filter->pattern, lev_config_name(l), FNM_CASEINSENSITIVE_FOR_WIN) == 0)
|
||||
if (wnmatch(filter->pattern, lev_config_name(l)) == 0)
|
||||
break;
|
||||
if (l == state->level) {
|
||||
/* LCOV_EXCL_START */
|
||||
|
|
@ -1234,7 +1234,6 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
|
|||
if (!filter) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Invalid 'exclude' specification '%s' in '%s' at line %u\n", buffer, path, line);
|
||||
log_fatal("Filters with relative paths are not supported. Ensure to add an initial slash\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
@ -1261,7 +1260,6 @@ void state_config(struct snapraid_state* state, const char* path, const char* co
|
|||
if (!filter) {
|
||||
/* LCOV_EXCL_START */
|
||||
log_fatal("Invalid 'include' specification '%s' in '%s' at line %u\n", buffer, path, line);
|
||||
log_fatal("Filters with relative paths are not supported. Ensure to add an initial slash\n");
|
||||
exit(EXIT_FAILURE);
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2010,3 +2010,166 @@ void thread_yield(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************/
|
||||
/* wnmatch */
|
||||
|
||||
/**
|
||||
* Helper function for case-insensitive character comparison
|
||||
*/
|
||||
static inline int char_match(char p, char t)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return tolower((unsigned char)p) == tolower((unsigned char)t);
|
||||
#else
|
||||
return p == t;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Match character class [...]
|
||||
* Return 0 if NOT matched
|
||||
*/
|
||||
static const char* match_class(const char* p, char t)
|
||||
{
|
||||
int negate = 0;
|
||||
int matched = 0;
|
||||
|
||||
if (*p == '!' || *p == '^') {
|
||||
negate = 1;
|
||||
++p;
|
||||
}
|
||||
|
||||
while (*p && *p != ']') {
|
||||
if (p[1] == '-' && p[2] != ']' && p[2] != 0) {
|
||||
/* range [a-z] */
|
||||
char start = *p;
|
||||
char end = p[2];
|
||||
#ifdef WIN32
|
||||
start = tolower((unsigned char)start);
|
||||
end = tolower((unsigned char)end);
|
||||
t = tolower((unsigned char)t);
|
||||
#endif
|
||||
if (t >= start && t <= end)
|
||||
matched = 1;
|
||||
p += 3;
|
||||
} else {
|
||||
/* single character */
|
||||
if (char_match(*p, t))
|
||||
matched = 1;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == ']')
|
||||
++p;
|
||||
|
||||
if (negate)
|
||||
matched = !matched;
|
||||
if (!matched)
|
||||
return 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int wnmatch(const char* p, const char* t)
|
||||
{
|
||||
char p1 = 0; /* previous char */
|
||||
while (*p) {
|
||||
char p0 = *p;
|
||||
switch (p0) {
|
||||
case '?' :
|
||||
/* ? matches any single character except / */
|
||||
if (*t == 0 || *t == '/')
|
||||
return 1;
|
||||
++p;
|
||||
++t;
|
||||
break;
|
||||
case '*' :
|
||||
/* check for ** */
|
||||
if (p[1] == '*') {
|
||||
/* skip the ** */
|
||||
p += 2;
|
||||
|
||||
/* munge all * */
|
||||
while (*p == '*')
|
||||
++p;
|
||||
|
||||
/* if its not near a slash, it's like a single * */
|
||||
if (p1 == '/' || *p == '/') {
|
||||
/* a ** at end matches everything */
|
||||
if (*p == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* In between slashes matches to nothing
|
||||
*
|
||||
* Check for /##/ or ^##/ (^ start of string)
|
||||
*
|
||||
* This is required for:
|
||||
* "/##/file.txt" matching "/file.txt"
|
||||
* "##/file.txt" matching "file.txt"
|
||||
* "x##/file.txt" NOT matching "xfile.txt"
|
||||
*/
|
||||
if (*p == '/' && (p1 == 0 || p1 == '/')) {
|
||||
/* try reducing to nothing */
|
||||
if (wnmatch(p + 1, t) == 0)
|
||||
return 0;
|
||||
/* otherwise / should match in the text */
|
||||
}
|
||||
|
||||
/* try matching with 0 or more characters */
|
||||
while (*t) {
|
||||
if (wnmatch(p, t) == 0)
|
||||
return 0;
|
||||
++t;
|
||||
}
|
||||
|
||||
/* try matching at the end */
|
||||
return wnmatch(p, t);
|
||||
}
|
||||
} else {
|
||||
/* skip the * */
|
||||
++p;
|
||||
}
|
||||
|
||||
/* a * at end matches rest of segment */
|
||||
if (*p == 0) {
|
||||
while (*t && *t != '/')
|
||||
++t;
|
||||
return *t != 0;
|
||||
}
|
||||
|
||||
/* try matching with 0 or more characters */
|
||||
while (*t && *t != '/') {
|
||||
if (wnmatch(p, t) == 0)
|
||||
return 0;
|
||||
++t;
|
||||
}
|
||||
|
||||
/* try matching at the end */
|
||||
return wnmatch(p, t);
|
||||
case '[' :
|
||||
/* character class */
|
||||
if (*t == 0 || *t == '/')
|
||||
return 1;
|
||||
|
||||
p = match_class(p + 1, *t);
|
||||
if (!p)
|
||||
return 1;
|
||||
|
||||
++t;
|
||||
break;
|
||||
default:
|
||||
/* literal character */
|
||||
if (*t == 0 || !char_match(*p, *t))
|
||||
return 1;
|
||||
++p;
|
||||
++t;
|
||||
break;
|
||||
}
|
||||
p1 = p0;
|
||||
}
|
||||
|
||||
/* match successful if we've consumed all text */
|
||||
return *t != 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,5 +487,19 @@ void thread_join(thread_id_t thread, void** retval);
|
|||
void thread_yield(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/****************************************************************************/
|
||||
/* match */
|
||||
|
||||
/*
|
||||
* Wild match function.
|
||||
*
|
||||
* - ? 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)
|
||||
* - [...] matches character classes with support for ranges and negation lile [!...] or [^...], except /
|
||||
*/
|
||||
int wnmatch(const char* p, const char* t);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue