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