Merge branch 'master' of ssh://git.samba.org/data/git/samba into displaysec
[jra/samba/.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
33 static 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 */
49 struct 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 */
60 static 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
157 int 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 */
218 int gen_fnmatch(const char *pattern, const char *string)
219 {
220         return ms_fnmatch(pattern, string, PROTOCOL_NT1);
221 }