The big character set handling changeover!
[kai/samba.git] / source3 / lib / ms_fnmatch.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    filename matching routine
5    Copyright (C) Andrew Tridgell 1992-1998 
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
20
21 /*
22    This module was originally based on fnmatch.c copyright by the Free
23    Software Foundation. It bears little resemblence to that code now 
24 */  
25
26
27 #if FNMATCH_TEST
28 #include <stdio.h>
29 #include <stdlib.h>
30 #else
31 #include "includes.h"
32 #endif
33
34 /* 
35    bugger. we need a separate wildcard routine for older versions
36    of the protocol. This is not yet perfect, but its a lot
37    better than what we had */
38 static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern, 
39                                   const smb_ucs2_t *string)
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) == 0) goto match;
66                                 if (ms_fnmatch_lanman_core(p, n) == 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) == 0) goto match;
77                         }
78                         break;
79
80                 case UCS2_CHAR('<'):
81                         for (; *n; n++) {
82                                 if (ms_fnmatch_lanman_core(p, n) == 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) == 0) goto match;
93                         if (*n != UCS2_CHAR('.')) goto nomatch;
94                         n++;
95                         break;
96
97                 default:
98                         if (c != *n) goto nomatch;
99                         n++;
100                 }
101         }
102         
103         if (! *n) goto match;
104         
105  nomatch:
106         /*
107         if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string);
108         */
109         return -1;
110
111 next:
112         if (ms_fnmatch_lanman_core(p, n) == 0) goto match;
113         goto nomatch;
114
115  match:
116         /*
117         if (verbose) printf("MATCH   pattern=[%s] string=[%s]\n", pattern, string);
118         */
119         return 0;
120 }
121
122 static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, const smb_ucs2_t *string)
123 {
124         if (!strpbrk_wa(pattern, "?*<>\"")) {
125                 smb_ucs2_t s[] = {UCS2_CHAR('.'), 0};
126                 if (strcmp_wa(string,"..") == 0) string = s;
127                 return strcasecmp_w(pattern, string);
128         }
129
130         if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) {
131                 smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0};
132                 smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0};
133                 return ms_fnmatch_lanman_core(pattern, dotdot) &&
134                         ms_fnmatch_lanman_core(pattern, dot);
135         }
136
137         return ms_fnmatch_lanman_core(pattern, string);
138 }
139
140
141 /* the following function was derived using the masktest utility -
142    after years of effort we finally have a perfect MS wildcard
143    matching routine! 
144
145    NOTE: this matches only filenames with no directory component
146
147    Returns 0 on match, -1 on fail.
148 */
149 static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string)
150 {
151         const smb_ucs2_t *p = pattern, *n = string;
152         smb_ucs2_t c;
153         extern int Protocol;
154
155         if (Protocol <= PROTOCOL_LANMAN2) {
156                 return ms_fnmatch_lanman1(pattern, string);
157         }
158
159         while ((c = *p++)) {
160                 switch (c) {
161                 case UCS2_CHAR('?'):
162                         if (! *n) return -1;
163                         n++;
164                         break;
165
166                 case UCS2_CHAR('>'):
167                         if (n[0] == UCS2_CHAR('.')) {
168                                 if (! n[1] && ms_fnmatch_w(p, n+1) == 0) return 0;
169                                 if (ms_fnmatch_w(p, n) == 0) return 0;
170                                 return -1;
171                         }
172                         if (! *n) return ms_fnmatch_w(p, n);
173                         n++;
174                         break;
175
176                 case UCS2_CHAR('*'):
177                         for (; *n; n++) {
178                                 if (ms_fnmatch_w(p, n) == 0) return 0;
179                         }
180                         break;
181
182                 case UCS2_CHAR('<'):
183                         for (; *n; n++) {
184                                 if (ms_fnmatch_w(p, n) == 0) return 0;
185                                 if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) {
186                                         n++;
187                                         break;
188                                 }
189                         }
190                         break;
191
192                 case UCS2_CHAR('"'):
193                         if (*n == 0 && ms_fnmatch_w(p, n) == 0) return 0;
194                         if (*n != UCS2_CHAR('.')) return -1;
195                         n++;
196                         break;
197
198                 default:
199                         if (c != *n) return -1;
200                         n++;
201                 }
202         }
203         
204         if (! *n) return 0;
205         
206         return -1;
207 }
208
209
210 int ms_fnmatch(const char *pattern, const char *string)
211 {
212         wpstring p, s;
213
214         pstrcpy_wa(p, pattern);
215         pstrcpy_wa(s, string);
216
217         return ms_fnmatch_w(p, s);
218 }