2009-12-06 11:22:20 -08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
|
2008-07-24 11:18:55 -07:00
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "fnmatch.h"
|
2008-10-09 14:56:31 -07:00
|
|
|
#include "evil_fnmatch_private.h"
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
enum fnmatch_status
|
2008-10-09 14:56:31 -07:00
|
|
|
{
|
|
|
|
fnmatch_not_found = 0,
|
|
|
|
fnmatch_found = 1,
|
|
|
|
fnmatch_syntax_error = 2
|
|
|
|
};
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch_match_class_token(enum fnmatch_status *status,
|
|
|
|
const char *class_token,
|
|
|
|
char c)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
|
|
|
if (! *class_token)
|
|
|
|
{
|
|
|
|
*status = fnmatch_syntax_error;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (class_token[1] == '-' && class_token[2] != ']')
|
|
|
|
{
|
|
|
|
if (class_token[0] <= c && c <= class_token[2])
|
|
|
|
*status = fnmatch_found;
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (c == *class_token)
|
|
|
|
*status = fnmatch_found;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
|
|
|
fnmatch_complement_class(const char *class_token)
|
|
|
|
{
|
|
|
|
switch (*class_token)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return FNM_SYNTAXERR;
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch_match_class(const char *class,
|
|
|
|
char c)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
2008-10-09 14:56:31 -07:00
|
|
|
const size_t complement = fnmatch_complement_class(class + 1);
|
|
|
|
enum fnmatch_status status;
|
|
|
|
size_t pos;
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
if (complement == FNM_SYNTAXERR)
|
|
|
|
return FNM_SYNTAXERR;
|
|
|
|
|
2008-09-14 23:56:14 -07:00
|
|
|
status = fnmatch_not_found;
|
|
|
|
pos = 1 + complement;
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
do
|
|
|
|
pos += fnmatch_match_class_token(&status, class + pos, c);
|
|
|
|
while (class[pos] && class[pos] != ']');
|
|
|
|
|
|
|
|
if (status == fnmatch_syntax_error || ! class[pos])
|
|
|
|
return FNM_SYNTAXERR;
|
|
|
|
|
|
|
|
if (status == fnmatch_found)
|
|
|
|
return complement ? 0 : pos + 1;
|
|
|
|
else
|
|
|
|
return complement ? pos + 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch_chrcasecmp(char a,
|
|
|
|
char b)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
|
|
|
if ('A' <= a && a <= 'Z')
|
|
|
|
a += 'a' - 'A';
|
|
|
|
if ('A' <= b && b <= 'Z')
|
|
|
|
b += 'a' - 'A';
|
|
|
|
return a == b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch_match_token(const char *token,
|
|
|
|
char c,
|
|
|
|
e_bool leading,
|
|
|
|
int flags)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
|
|
|
if (*token == '\\' && !(flags & FNM_NOESCAPE))
|
|
|
|
return token[1] ? (token[1] == c ? 2 : 0) : FNM_SYNTAXERR;
|
|
|
|
|
|
|
|
if (c == '/' && (flags & FNM_PATHNAME))
|
|
|
|
return *token == '/';
|
|
|
|
|
|
|
|
if (c == '.' && leading && (flags & FNM_PERIOD))
|
|
|
|
return *token == '.';
|
|
|
|
|
|
|
|
switch (*token)
|
|
|
|
{
|
|
|
|
case '?':
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
return fnmatch_match_class(token, c);
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (flags & FNM_CASEFOLD)
|
|
|
|
return fnmatch_chrcasecmp(*token, c);
|
|
|
|
return *token == c ? 1 : 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void
|
|
|
|
fnmatch_init_states(struct list_of_states *states)
|
|
|
|
{
|
|
|
|
states->size = 1;
|
|
|
|
states->states[0] = 0;
|
|
|
|
memset(states->has, 0, states->reserved * sizeof (*states->has));
|
|
|
|
states->has[0] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
size_t
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch_check_finals(const char *pattern,
|
|
|
|
const struct list_of_states *states)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
2008-09-14 23:56:14 -07:00
|
|
|
size_t i, j;
|
2008-10-09 14:56:31 -07:00
|
|
|
for (i = 0; i < states->size; ++i)
|
|
|
|
{
|
|
|
|
e_bool match = 1;
|
|
|
|
|
|
|
|
for (j = states->states[i]; pattern[j]; ++j)
|
|
|
|
if (pattern[j] != '*')
|
|
|
|
{
|
|
|
|
match = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return FNM_NOMATCH;
|
2008-07-24 11:18:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-10-09 14:56:31 -07:00
|
|
|
fnmatch(const char *pattern,
|
|
|
|
const char *string,
|
|
|
|
int flags)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
2008-10-09 14:56:31 -07:00
|
|
|
struct list_of_states *states;
|
|
|
|
struct list_of_states *new_states;
|
|
|
|
e_bool leading = 1;
|
|
|
|
char *c;
|
|
|
|
size_t r;
|
2008-09-14 23:56:14 -07:00
|
|
|
|
2008-07-24 11:18:55 -07:00
|
|
|
assert(pattern);
|
|
|
|
assert(string);
|
|
|
|
|
2008-09-14 23:56:14 -07:00
|
|
|
states = fnmatch_list_of_states_alloc(2, strlen(pattern));
|
|
|
|
new_states = states + 1;
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
if (! states)
|
|
|
|
return FNM_NOMEM;
|
|
|
|
fnmatch_init_states(states);
|
|
|
|
|
|
|
|
|
2008-10-13 23:52:09 -07:00
|
|
|
for (c = (char *)string; *c && states->size; ++c)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
2008-09-14 23:56:14 -07:00
|
|
|
size_t i;
|
2008-07-24 11:18:55 -07:00
|
|
|
fnmatch_list_of_states_clear(new_states);
|
|
|
|
|
2008-09-14 23:56:14 -07:00
|
|
|
for (i = 0; i < states->size; ++i)
|
2008-07-24 11:18:55 -07:00
|
|
|
{
|
2008-10-09 14:56:31 -07:00
|
|
|
const size_t pos = states->states[i];
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
if (! pattern[pos])
|
|
|
|
{
|
|
|
|
if (*c == '/' && (flags & FNM_LEADING_DIR))
|
|
|
|
return 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (pattern[pos] == '*')
|
|
|
|
{
|
|
|
|
fnmatch_list_of_states_insert(states, pos + 1);
|
|
|
|
if ((*c != '/' || !(flags & FNM_PATHNAME)) &&
|
|
|
|
(*c != '.' || !leading || !(flags & FNM_PERIOD)))
|
|
|
|
fnmatch_list_of_states_insert(new_states, pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-01-11 22:03:51 -08:00
|
|
|
const size_t m = fnmatch_match_token(pattern + pos, *c,
|
2008-07-24 11:18:55 -07:00
|
|
|
leading, flags);
|
|
|
|
|
|
|
|
if (m == FNM_SYNTAXERR)
|
|
|
|
return FNM_SYNTAXERR;
|
|
|
|
else if (m)
|
|
|
|
fnmatch_list_of_states_insert(new_states, pos + m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
2008-10-09 14:56:31 -07:00
|
|
|
struct list_of_states *tmp = states;
|
2008-07-24 11:18:55 -07:00
|
|
|
|
|
|
|
states = new_states;
|
|
|
|
new_states = tmp;
|
|
|
|
}
|
|
|
|
leading = *c == '/' && (flags & FNM_PATHNAME);
|
|
|
|
}
|
|
|
|
|
2008-09-14 23:56:14 -07:00
|
|
|
r = fnmatch_check_finals(pattern, states);
|
2008-07-24 11:18:55 -07:00
|
|
|
fnmatch_list_of_states_free(states < new_states ? states : new_states, 2);
|
2009-01-11 22:03:51 -08:00
|
|
|
return (int)r;
|
2008-07-24 11:18:55 -07:00
|
|
|
}
|