diff options
Diffstat (limited to '')
-rw-r--r-- | lib/util/ms_fnmatch.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/lib/util/ms_fnmatch.c b/lib/util/ms_fnmatch.c new file mode 100644 index 0000000..48454a8 --- /dev/null +++ b/lib/util/ms_fnmatch.c @@ -0,0 +1,249 @@ +/* + Unix SMB/CIFS implementation. + filename matching routine + Copyright (C) Andrew Tridgell 1992-2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 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/>. +*/ + +/* + This module was originally based on fnmatch.c copyright by the Free + Software Foundation. It bears little (if any) resemblance to that + code now +*/ + +/** + * @file + * @brief MS-style Filename matching + */ + +#include "replace.h" +#include "lib/util/samba_util.h" +#include "libcli/smb/smb_constants.h" + +static int null_match(const char *p) +{ + for (;*p;p++) { + if (*p != '*' && + *p != '<' && + *p != '"' && + *p != '>') return -1; + } + return 0; +} + +/* + the max_n structure is purely for efficiency, it doesn't contribute + to the matching algorithm except by ensuring that the algorithm does + not grow exponentially +*/ +struct max_n { + const char *predot; + const char *postdot; +}; + + +/* + p and n are the pattern and string being matched. The max_n array is + an optimisation only. The ldot pointer is NULL if the string does + not contain a '.', otherwise it points at the last dot in 'n'. +*/ +static int ms_fnmatch_core(const char *p, const char *n, + struct max_n *max_n, const char *ldot, + bool is_case_sensitive) +{ + codepoint_t c, c2; + int i; + size_t size, size_n; + + while ((c = next_codepoint(p, &size))) { + p += size; + + switch (c) { + case '*': + /* a '*' matches zero or more characters of any type */ + if (max_n != NULL && max_n->predot && + max_n->predot <= n) { + return null_match(p); + } + for (i=0; n[i]; i += size_n) { + next_codepoint(n+i, &size_n); + if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) { + return 0; + } + } + if (max_n != NULL && (!max_n->predot || + max_n->predot > n)) { + max_n->predot = n; + } + return null_match(p); + + case '<': + /* a '<' matches zero or more characters of + any type, but stops matching at the last + '.' in the string. */ + if (max_n != NULL && max_n->predot && + max_n->predot <= n) { + return null_match(p); + } + if (max_n != NULL && max_n->postdot && + max_n->postdot <= n && n <= ldot) { + return -1; + } + for (i=0; n[i]; i += size_n) { + next_codepoint(n+i, &size_n); + if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0; + if (n+i == ldot) { + if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot, is_case_sensitive) == 0) return 0; + if (max_n != NULL) { + if (!max_n->postdot || + max_n->postdot > n) { + max_n->postdot = n; + } + } + return -1; + } + } + if (max_n != NULL && (!max_n->predot || + max_n->predot > n)) { + max_n->predot = n; + } + return null_match(p); + + case '?': + /* a '?' matches any single character */ + if (! *n) { + return -1; + } + next_codepoint(n, &size_n); + n += size_n; + break; + + case '>': + /* a '?' matches any single character, but + treats '.' specially */ + if (n[0] == '.') { + if (! n[1] && null_match(p) == 0) { + return 0; + } + break; + } + if (! *n) return null_match(p); + next_codepoint(n, &size_n); + n += size_n; + break; + + case '"': + /* a bit like a soft '.' */ + if (*n == 0 && null_match(p) == 0) { + return 0; + } + if (*n != '.') return -1; + next_codepoint(n, &size_n); + n += size_n; + break; + + default: + c2 = next_codepoint(n, &size_n); + if (c != c2) { + if (is_case_sensitive) { + return -1; + } + if (codepoint_cmpi(c, c2) != 0) { + return -1; + } + } + n += size_n; + break; + } + } + + if (! *n) { + return 0; + } + + return -1; +} + +int ms_fnmatch_protocol(const char *pattern, const char *string, int protocol, + bool is_case_sensitive) +{ + int ret = -1; + size_t count, i; + + if (strcmp(string, "..") == 0) { + string = "."; + } + + if (strpbrk(pattern, "<>*?\"") == NULL) { + /* this is not just an optimisation - it is essential + for LANMAN1 correctness */ + return strcasecmp_m(pattern, string); + } + + if (protocol <= PROTOCOL_LANMAN2) { + char *p = talloc_strdup(NULL, pattern); + if (p == NULL) { + return -1; + } + /* + for older negotiated protocols it is possible to + translate the pattern to produce a "new style" + pattern that exactly matches w2k behaviour + */ + for (i=0;p[i];i++) { + if (p[i] == '?') { + p[i] = '>'; + } else if (p[i] == '.' && + (p[i+1] == '?' || + p[i+1] == '*' || + p[i+1] == 0)) { + p[i] = '"'; + } else if (p[i] == '*' && + p[i+1] == '.') { + p[i] = '<'; + } + } + ret = ms_fnmatch_protocol(p, string, PROTOCOL_NT1, + is_case_sensitive); + talloc_free(p); + return ret; + } + + for (count=i=0;pattern[i];i++) { + if (pattern[i] == '*' || pattern[i] == '<') count++; + } + + /* If the pattern includes '*' or '<' */ + if (count >= 1) { + struct max_n max_n[count]; + + memset(max_n, 0, sizeof(struct max_n) * count); + + ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'), + is_case_sensitive); + } else { + ret = ms_fnmatch_core(pattern, string, NULL, strrchr(string, '.'), + is_case_sensitive); + } + + return ret; +} + + +/** a generic fnmatch function - uses for non-CIFS pattern matching */ +int gen_fnmatch(const char *pattern, const char *string) +{ + return ms_fnmatch_protocol(pattern, string, PROTOCOL_NT1, false); +} |