source: branches/samba-3.5.x/lib/util/ms_fnmatch.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 5.1 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 filename matching routine
4 Copyright (C) Andrew Tridgell 1992-2004
5
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.
10
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.
15
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/>.
18*/
19
20/*
21 This module was originally based on fnmatch.c copyright by the Free
22 Software Foundation. It bears little (if any) resemblence to that
23 code now
24*/
25
26/**
27 * @file
28 * @brief MS-style Filename matching
29 */
30
31#include "includes.h"
32
33static int null_match(const char *p)
34{
35 for (;*p;p++) {
36 if (*p != '*' &&
37 *p != '<' &&
38 *p != '"' &&
39 *p != '>') return -1;
40 }
41 return 0;
42}
43
44/*
45 the max_n structure is purely for efficiency, it doesn't contribute
46 to the matching algorithm except by ensuring that the algorithm does
47 not grow exponentially
48*/
49struct max_n {
50 const char *predot;
51 const char *postdot;
52};
53
54
55/*
56 p and n are the pattern and string being matched. The max_n array is
57 an optimisation only. The ldot pointer is NULL if the string does
58 not contain a '.', otherwise it points at the last dot in 'n'.
59*/
60static int ms_fnmatch_core(const char *p, const char *n,
61 struct max_n *max_n, const char *ldot)
62{
63 codepoint_t c, c2;
64 int i;
65 size_t size, size_n;
66
67 while ((c = next_codepoint(p, &size))) {
68 p += size;
69
70 switch (c) {
71 case '*':
72 /* a '*' matches zero or more characters of any type */
73 if (max_n->predot && max_n->predot <= n) {
74 return null_match(p);
75 }
76 for (i=0; n[i]; i += size_n) {
77 next_codepoint(n+i, &size_n);
78 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) {
79 return 0;
80 }
81 }
82 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
83 return null_match(p);
84
85 case '<':
86 /* a '<' matches zero or more characters of
87 any type, but stops matching at the last
88 '.' in the string. */
89 if (max_n->predot && max_n->predot <= n) {
90 return null_match(p);
91 }
92 if (max_n->postdot && max_n->postdot <= n && n <= ldot) {
93 return -1;
94 }
95 for (i=0; n[i]; i += size_n) {
96 next_codepoint(n+i, &size_n);
97 if (ms_fnmatch_core(p, n+i, max_n+1, ldot) == 0) return 0;
98 if (n+i == ldot) {
99 if (ms_fnmatch_core(p, n+i+size_n, max_n+1, ldot) == 0) return 0;
100 if (!max_n->postdot || max_n->postdot > n) max_n->postdot = n;
101 return -1;
102 }
103 }
104 if (!max_n->predot || max_n->predot > n) max_n->predot = n;
105 return null_match(p);
106
107 case '?':
108 /* a '?' matches any single character */
109 if (! *n) {
110 return -1;
111 }
112 next_codepoint(n, &size_n);
113 n += size_n;
114 break;
115
116 case '>':
117 /* a '?' matches any single character, but
118 treats '.' specially */
119 if (n[0] == '.') {
120 if (! n[1] && null_match(p) == 0) {
121 return 0;
122 }
123 break;
124 }
125 if (! *n) return null_match(p);
126 next_codepoint(n, &size_n);
127 n += size_n;
128 break;
129
130 case '"':
131 /* a bit like a soft '.' */
132 if (*n == 0 && null_match(p) == 0) {
133 return 0;
134 }
135 if (*n != '.') return -1;
136 next_codepoint(n, &size_n);
137 n += size_n;
138 break;
139
140 default:
141 c2 = next_codepoint(n, &size_n);
142 if (c != c2 && codepoint_cmpi(c, c2) != 0) {
143 return -1;
144 }
145 n += size_n;
146 break;
147 }
148 }
149
150 if (! *n) {
151 return 0;
152 }
153
154 return -1;
155}
156
157int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol)
158{
159 int ret, count, i;
160 struct max_n *max_n = NULL;
161
162 if (strcmp(string, "..") == 0) {
163 string = ".";
164 }
165
166 if (strpbrk(pattern, "<>*?\"") == NULL) {
167 /* this is not just an optimisation - it is essential
168 for LANMAN1 correctness */
169 return strcasecmp_m(pattern, string);
170 }
171
172 if (protocol <= PROTOCOL_LANMAN2) {
173 char *p = talloc_strdup(NULL, pattern);
174 if (p == NULL) {
175 return -1;
176 }
177 /*
178 for older negotiated protocols it is possible to
179 translate the pattern to produce a "new style"
180 pattern that exactly matches w2k behaviour
181 */
182 for (i=0;p[i];i++) {
183 if (p[i] == '?') {
184 p[i] = '>';
185 } else if (p[i] == '.' &&
186 (p[i+1] == '?' ||
187 p[i+1] == '*' ||
188 p[i+1] == 0)) {
189 p[i] = '"';
190 } else if (p[i] == '*' &&
191 p[i+1] == '.') {
192 p[i] = '<';
193 }
194 }
195 ret = ms_fnmatch(p, string, PROTOCOL_NT1);
196 talloc_free(p);
197 return ret;
198 }
199
200 for (count=i=0;pattern[i];i++) {
201 if (pattern[i] == '*' || pattern[i] == '<') count++;
202 }
203
204 max_n = talloc_zero_array(NULL, struct max_n, count);
205 if (max_n == NULL) {
206 return -1;
207 }
208
209 ret = ms_fnmatch_core(pattern, string, max_n, strrchr(string, '.'));
210
211 talloc_free(max_n);
212
213 return ret;
214}
215
216
217/** a generic fnmatch function - uses for non-CIFS pattern matching */
218int gen_fnmatch(const char *pattern, const char *string)
219{
220 return ms_fnmatch(pattern, string, PROTOCOL_NT1);
221}
Note: See TracBrowser for help on using the repository browser.