r1085: Now it's had some proper user testing, merge in the deferred open fix. I'm
[tprouty/samba.git] / source / lib / ms_fnmatch.c
1 /* 
2    Unix SMB/CIFS implementation.
3    filename matching routine
4    Copyright (C) Andrew Tridgell 1992-1998 
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 /*
21    This module was originally based on fnmatch.c copyright by the Free
22    Software Foundation. It bears little resemblence to that code now 
23 */  
24
25
26 #if FNMATCH_TEST
27 #include <stdio.h>
28 #include <stdlib.h>
29 #else
30 #include "includes.h"
31 #endif
32
33 /* 
34    bugger. we need a separate wildcard routine for older versions
35    of the protocol. This is not yet perfect, but its a lot
36    better than what we had */
37 static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern, 
38                                   const smb_ucs2_t *string,
39                                   BOOL case_sensitive)
40 {
41         const smb_ucs2_t *p = pattern, *n = string;
42         smb_ucs2_t c;
43
44         if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match;
45
46         while ((c = *p++)) {
47                 switch (c) {
48                 case UCS2_CHAR('.'):
49                         if (! *n) goto next;
50                         if (*n != UCS2_CHAR('.')) goto nomatch;
51                         n++;
52                         break;
53
54                 case UCS2_CHAR('?'):
55                         if (! *n) goto next;
56                         if ((*n == UCS2_CHAR('.') && 
57                              n[1] != UCS2_CHAR('.')) || ! *n) 
58                                 goto next;
59                         n++;
60                         break;
61
62                 case UCS2_CHAR('>'):
63                         if (! *n) goto next;
64                         if (n[0] == UCS2_CHAR('.')) {
65                                 if (! n[1] && ms_fnmatch_lanman_core(p, n+1, case_sensitive) == 0) goto match;
66                                 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
67                                 goto nomatch;
68                         }
69                         n++;
70                         break;
71
72                 case UCS2_CHAR('*'):
73                         if (! *n) goto next;
74                         if (! *p) goto match;
75                         for (; *n; n++) {
76                                 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
77                         }
78                         break;
79
80                 case UCS2_CHAR('<'):
81                         for (; *n; n++) {
82                                 if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
83                                 if (*n == UCS2_CHAR('.') && 
84                                     !strchr_w(n+1,UCS2_CHAR('.'))) {
85                                         n++;
86                                         break;
87                                 }
88                         }
89                         break;
90
91                 case UCS2_CHAR('"'):
92                         if (*n == 0 && ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
93                         if (*n != UCS2_CHAR('.')) goto nomatch;
94                         n++;
95                         break;
96
97                 default:
98                         if (case_sensitive) {
99                                 if (c != *n) goto nomatch;
100                         } else {
101                                 if (tolower_w(c) != tolower_w(*n)) goto nomatch;
102                         }
103                         n++;
104                 }
105         }
106         
107         if (! *n) goto match;
108         
109  nomatch:
110         /*
111         if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
112         */
113         return -1;
114
115 next:
116         if (ms_fnmatch_lanman_core(p, n, case_sensitive) == 0) goto match;
117         goto nomatch;
118
119  match:
120         /*
121         if (verbose) printf("MATCH   pattern=[%s] string=[%s]\n", pattern, string);
122         */
123         return 0;
124 }
125
126 static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, 
127                               const smb_ucs2_t *string, BOOL case_sensitive)
128 {
129         if (!strpbrk_wa(pattern, "?*<>\"")) {
130                 smb_ucs2_t s[] = {UCS2_CHAR('.'), 0};
131                 if (strcmp_wa(string,"..") == 0) string = s;
132                 return strcasecmp_w(pattern, string);
133         }
134
135         if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) {
136                 smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0};
137                 smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0};
138                 return ms_fnmatch_lanman_core(pattern, dotdot, case_sensitive) &&
139                         ms_fnmatch_lanman_core(pattern, dot, case_sensitive);
140         }
141
142         return ms_fnmatch_lanman_core(pattern, string, case_sensitive);
143 }
144
145
146 /* the following function was derived using the masktest utility -
147    after years of effort we finally have a perfect MS wildcard
148    matching routine! 
149
150    NOTE: this matches only filenames with no directory component
151
152    Returns 0 on match, -1 on fail.
153 */
154 static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string, 
155                         int protocol, BOOL case_sensitive)
156 {
157         const smb_ucs2_t *p = pattern, *n = string;
158         smb_ucs2_t c;
159
160         if (protocol <= PROTOCOL_LANMAN2) {
161                 return ms_fnmatch_lanman1(pattern, string, case_sensitive);
162         }
163
164         while ((c = *p++)) {
165                 switch (c) {
166                 case UCS2_CHAR('?'):
167                         if (! *n) return -1;
168                         n++;
169                         break;
170
171                 case UCS2_CHAR('>'):
172                         if (n[0] == UCS2_CHAR('.')) {
173                                 if (! n[1] && ms_fnmatch_w(p, n+1, protocol, case_sensitive) == 0) return 0;
174                                 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
175                                 return -1;
176                         }
177                         if (! *n) return ms_fnmatch_w(p, n, protocol, case_sensitive);
178                         n++;
179                         break;
180
181                 case UCS2_CHAR('*'):
182                         for (; *n; n++) {
183                                 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
184                         }
185                         break;
186
187                 case UCS2_CHAR('<'):
188                         for (; *n; n++) {
189                                 if (ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
190                                 if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) {
191                                         n++;
192                                         break;
193                                 }
194                         }
195                         break;
196
197                 case UCS2_CHAR('"'):
198                         if (*n == 0 && ms_fnmatch_w(p, n, protocol, case_sensitive) == 0) return 0;
199                         if (*n != UCS2_CHAR('.')) return -1;
200                         n++;
201                         break;
202
203                 default:
204                         if (case_sensitive) {
205                                 if (c != *n) return -1;
206                         } else {
207                                 if (tolower_w(c) != tolower_w(*n)) return -1;
208                         }
209                         n++;
210                 }
211         }
212         
213         if (! *n) return 0;
214         
215         return -1;
216 }
217
218 int ms_fnmatch(const char *pattern, const char *string, int protocol, 
219                BOOL case_senstive)
220 {
221         wpstring buffer_pattern, buffer_string;
222         int ret;
223         size_t size;
224
225         size = push_ucs2(NULL, buffer_pattern, pattern, sizeof(buffer_pattern), STR_TERMINATE);
226         if (size == (size_t)-1) {
227                 return -1;
228                 /* Not quite the right answer, but finding the right one
229                    under this failure case is expensive, and it's pretty close */
230         }
231         
232         size = push_ucs2(NULL, buffer_string, string, sizeof(buffer_string), STR_TERMINATE);
233         if (size == (size_t)-1) {
234                 return -1;
235                 /* Not quite the right answer, but finding the right one
236                    under this failure case is expensive, and it's pretty close */
237         }
238
239         ret = ms_fnmatch_w(buffer_pattern, buffer_string, protocol, case_senstive);
240         DEBUG(10,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret));
241
242         return ret;
243 }
244
245 /* a generic fnmatch function - uses for non-CIFS pattern matching */
246 int gen_fnmatch(const char *pattern, const char *string)
247 {
248         return ms_fnmatch(pattern, string, PROTOCOL_NT1, True);
249 }