2 Unix SMB/CIFS implementation.
3 filename matching routine
4 Copyright (C) Andrew Tridgell 1992-2004
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 This module was originally based on fnmatch.c copyright by the Free
22 Software Foundation. It bears little (if any) resemblence to that
29 static int null_match(const smb_ucs2_t *p)
32 if (*p != UCS2_CHAR('*') &&
33 *p != UCS2_CHAR('<') &&
34 *p != UCS2_CHAR('"') &&
35 *p != UCS2_CHAR('>')) return -1;
41 the max_n structure is purely for efficiency, it doesn't contribute
42 to the matching algorithm except by ensuring that the algorithm does
43 not grow exponentially
46 const smb_ucs2_t *predot;
47 const smb_ucs2_t *postdot;
52 p and n are the pattern and string being matched. The max_n array is
53 an optimisation only. The ldot pointer is NULL if the string does
54 not contain a '.', otherwise it points at the last dot in 'n'.
56 static int ms_fnmatch_core(const smb_ucs2_t *p, const smb_ucs2_t *n,
57 struct max_n *max_n, const smb_ucs2_t *ldot,
58 BOOL is_case_sensitive)
65 /* a '*' matches zero or more characters of any type */
67 if (max_n->predot && max_n->predot <= n) {
70 for (i=0; n[i]; i++) {
71 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) {
75 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
78 /* a '<' matches zero or more characters of
79 any type, but stops matching at the last
82 if (max_n->predot && max_n->predot <= n) {
85 if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
88 for (i=0; n[i]; i++) {
89 if (ms_fnmatch_core(p, n+i, max_n+1, ldot, is_case_sensitive) == 0) return 0;
91 if (ms_fnmatch_core(p, n+i+1, max_n+1, ldot, is_case_sensitive) == 0) return 0;
92 if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
96 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
99 /* a '?' matches any single character */
107 /* a '?' matches any single character */
109 if (n[0] == UCS2_CHAR('.')) {
110 if (! n[1] && null_match(p) == 0) {
115 if (! *n) return null_match(p);
120 if (*n == 0 && null_match(p) == 0) {
123 if (*n != UCS2_CHAR('.')) return -1;
129 if (is_case_sensitive) {
132 if (toupper_w(c) != toupper_w(*n)) {
148 int ms_fnmatch(const char *pattern, const char *string, BOOL translate_pattern,
149 BOOL is_case_sensitive)
153 struct max_n *max_n = NULL;
155 if (strcmp(string, "..") == 0) {
159 if (strpbrk(pattern, "<>*?\"") == NULL) {
160 /* this is not just an optmisation - it is essential
161 for LANMAN1 correctness */
162 if (is_case_sensitive) {
163 return strcmp(pattern, string);
165 return StrCaseCmp(pattern, string);
169 if (push_ucs2(NULL, p, pattern, sizeof(p), STR_TERMINATE) == (size_t)-1) {
170 /* Not quite the right answer, but finding the right one
171 under this failure case is expensive, and it's pretty close */
175 if (push_ucs2(NULL, s, string, sizeof(s), STR_TERMINATE) == (size_t)-1) {
176 /* Not quite the right answer, but finding the right one
177 under this failure case is expensive, and it's pretty close */
181 if (translate_pattern) {
183 for older negotiated protocols it is possible to
184 translate the pattern to produce a "new style"
185 pattern that exactly matches w2k behaviour
188 if (p[i] == UCS2_CHAR('?')) {
189 p[i] = UCS2_CHAR('>');
190 } else if (p[i] == UCS2_CHAR('.') &&
191 (p[i+1] == UCS2_CHAR('?') ||
192 p[i+1] == UCS2_CHAR('*') ||
194 p[i] = UCS2_CHAR('"');
195 } else if (p[i] == UCS2_CHAR('*') && p[i+1] == UCS2_CHAR('.')) {
196 p[i] = UCS2_CHAR('<');
201 for (count=i=0;p[i];i++) {
202 if (p[i] == UCS2_CHAR('*') || p[i] == UCS2_CHAR('<')) count++;
206 max_n = SMB_CALLOC_ARRAY(struct max_n, count);
212 ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive);
222 /* a generic fnmatch function - uses for non-CIFS pattern matching */
223 int gen_fnmatch(const char *pattern, const char *string)
225 return ms_fnmatch(pattern, string, PROTOCOL_NT1, False);