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