]> Pileus Git - ~andy/gtk/blob - gtk/fnmatch.c
Add hidden aliases for exported symbols which are used internally in order
[~andy/gtk] / gtk / fnmatch.c
1 /* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 02111-1307, USA.
17  */
18
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
24  */
25
26 /*
27  * Stripped down, converted to UTF-8 and test cases added
28  *
29  *                    Owen Taylor, 13 December 2002;
30  */
31
32 #include <config.h>
33 #include <string.h>
34
35 #include <glib.h>
36
37 /* We need to make sure that all constants are defined
38  * to properly compile this file
39  */
40 #ifndef _GNU_SOURCE
41 #define _GNU_SOURCE
42 #endif
43
44 #include "gtkalias.h"
45
46 static gunichar
47 get_char (const char **str)
48 {
49   gunichar c = g_utf8_get_char (*str);
50   *str = g_utf8_next_char (*str);
51
52 #ifdef G_PLATFORM_WIN32
53   c = g_unichar_tolower (c);
54 #endif
55
56   return c;
57 }
58
59 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
60 #define DO_ESCAPE 0
61 #else  
62 #define DO_ESCAPE 1
63 #endif  
64
65 static gunichar
66 get_unescaped_char (const char **str,
67                     gboolean    *was_escaped)
68 {
69   gunichar c = get_char (str);
70
71   *was_escaped = DO_ESCAPE && c == '\\';
72   if (*was_escaped)
73     c = get_char (str);
74   
75   return c;
76 }
77
78 /* Match STRING against the filename pattern PATTERN, returning zero if
79    it matches, nonzero if not.  */
80
81 static gboolean
82 gtk_fnmatch_intern (const char *pattern,
83                     const char *string,
84                     gboolean    component_start,
85                     gboolean    no_leading_period)
86 {
87   const char *p = pattern, *n = string;
88   
89   while (*p)
90     {
91       const char *last_n = n;
92       
93       gunichar c = get_char (&p);
94       gunichar nc = get_char (&n);
95       
96       switch (c)
97         {
98         case '?':
99           if (nc == '\0')
100             return FALSE;
101           else if (nc == G_DIR_SEPARATOR)
102             return FALSE;
103           else if (nc == '.' && component_start && no_leading_period)
104             return FALSE;
105           break;
106         case '\\':
107           if (DO_ESCAPE)
108             c = get_char (&p);
109           if (nc != c)
110             return FALSE;
111           break;
112         case '*':
113           if (nc == '.' && component_start && no_leading_period)
114             return FALSE;
115
116           {
117             const char *last_p = p;
118
119             for (last_p = p, c = get_char (&p);
120                  c == '?' || c == '*';
121                  last_p = p, c = get_char (&p))
122               {
123                 if (c == '?')
124                   {
125                     if (nc == '\0')
126                       return FALSE;
127                     else if (nc == G_DIR_SEPARATOR)
128                       return FALSE;
129                     else
130                       {
131                         last_n = n; nc = get_char (&n);
132                       }
133                   }
134               }
135
136             /* If the pattern ends with wildcards, we have a
137              * guaranteed match unless there is a dir separator
138              * in the remainder of the string.
139              */
140             if (c == '\0')
141               {
142                 if (strchr (last_n, G_DIR_SEPARATOR) != NULL)
143                   return FALSE;
144                 else
145                   return TRUE;
146               }
147
148             if (DO_ESCAPE && c == '\\')
149               c = get_char (&p);
150
151             for (p = last_p; nc != '\0';)
152               {
153                 if ((c == '[' || nc == c) &&
154                     gtk_fnmatch_intern (p, last_n, component_start, no_leading_period))
155                   return TRUE;
156                 
157                 component_start = (nc == G_DIR_SEPARATOR);
158                 last_n = n;
159                 nc = get_char (&n);
160               }
161                   
162             return FALSE;
163           }
164
165         case '[':
166           {
167             /* Nonzero if the sense of the character class is inverted.  */
168             gboolean not;
169             gboolean was_escaped;
170
171             if (nc == '\0' || nc == G_DIR_SEPARATOR)
172               return FALSE;
173
174             if (nc == '.' && component_start && no_leading_period)
175               return FALSE;
176
177             not = (*p == '!' || *p == '^');
178             if (not)
179               ++p;
180
181             c = get_unescaped_char (&p, &was_escaped);
182             for (;;)
183               {
184                 register gunichar cstart = c, cend = c;
185                 if (c == '\0')
186                   /* [ (unterminated) loses.  */
187                   return FALSE;
188
189                 c = get_unescaped_char (&p, &was_escaped);
190                 
191                 if (!was_escaped && c == '-' && *p != ']')
192                   {
193                     cend = get_unescaped_char (&p, &was_escaped);
194                     if (cend == '\0')
195                       return FALSE;
196
197                     c = get_char (&p);
198                   }
199
200                 if (nc >= cstart && nc <= cend)
201                   goto matched;
202
203                 if (!was_escaped && c == ']')
204                   break;
205               }
206             if (!not)
207               return FALSE;
208             break;
209
210           matched:;
211             /* Skip the rest of the [...] that already matched.  */
212             /* XXX 1003.2d11 is unclear if was_escaped is right.  */
213             while (was_escaped || c != ']')
214               {
215                 if (c == '\0')
216                   /* [... (unterminated) loses.  */
217                   return FALSE;
218
219                 c = get_unescaped_char (&p, &was_escaped);
220               }
221             if (not)
222               return FALSE;
223           }
224           break;
225
226         default:
227           if (c != nc)
228             return FALSE;
229         }
230
231       component_start = (nc == G_DIR_SEPARATOR);
232     }
233
234   if (*n == '\0')
235     return TRUE;
236
237   return FALSE;
238 }
239
240 /* Match STRING against the filename pattern PATTERN, returning zero if
241  *  it matches, nonzero if not.
242  *
243  * GTK+ used to use a old version of GNU fnmatch() that was buggy
244  * in various ways and didn't handle UTF-8. The following is
245  * converted to UTF-8. To simplify the process of making it
246  * correct, this is special-cased to the combinations of flags
247  * that gtkfilesel.c uses.
248  *
249  *   FNM_FILE_NAME   - always set
250  *   FNM_LEADING_DIR - never set
251  *   FNM_NOESCAPE    - set only on windows
252  *   FNM_CASEFOLD    - set only on windows
253  */
254 gboolean
255 _gtk_fnmatch (const char *pattern,
256               const char *string,
257               gboolean no_leading_period)
258 {
259   return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period);
260 }
261
262 #undef FNMATCH_TEST_CASES
263 #ifdef FNMATCH_TEST_CASES
264
265 #define TEST(pat, str, no_leading_period, result) \
266   g_assert (_gtk_fnmatch ((pat), (str), (no_leading_period)) == result)
267
268 int main (int argc, char **argv)
269 {
270   TEST ("[a-]", "-", TRUE, TRUE);
271   
272   TEST ("a", "a", TRUE, TRUE);
273   TEST ("a", "b", TRUE, FALSE);
274
275   /* Test what ? matches */
276   TEST ("?", "a", TRUE, TRUE);
277   TEST ("?", ".", TRUE, FALSE);
278   TEST ("a?", "a.", TRUE, TRUE);
279   TEST ("a/?", "a/b", TRUE, TRUE);
280   TEST ("a/?", "a/.", TRUE, FALSE);
281   TEST ("?", "/", TRUE, FALSE);
282
283   /* Test what * matches */
284   TEST ("*", "a", TRUE, TRUE);
285   TEST ("*", ".", TRUE, FALSE);
286   TEST ("a*", "a.", TRUE, TRUE);
287   TEST ("a/*", "a/b", TRUE, TRUE);
288   TEST ("a/*", "a/.", TRUE, FALSE);
289   TEST ("*", "/", TRUE, FALSE);
290
291   /* Range tests */
292   TEST ("[ab]", "a", TRUE, TRUE);
293   TEST ("[ab]", "c", TRUE, FALSE);
294   TEST ("[^ab]", "a", TRUE, FALSE);
295   TEST ("[!ab]", "a", TRUE, FALSE);
296   TEST ("[^ab]", "c", TRUE, TRUE);
297   TEST ("[!ab]", "c", TRUE, TRUE);
298   TEST ("[a-c]", "b", TRUE, TRUE);
299   TEST ("[a-c]", "d", TRUE, FALSE);
300   TEST ("[a-]", "-", TRUE, TRUE);
301   TEST ("[]]", "]", TRUE, TRUE);
302   TEST ("[^]]", "a", TRUE, TRUE);
303   TEST ("[!]]", "a", TRUE, TRUE);
304
305   /* Various unclosed ranges */
306   TEST ("[ab", "a", TRUE, FALSE);
307   TEST ("[a-", "a", TRUE, FALSE);
308   TEST ("[ab", "c", TRUE, FALSE);
309   TEST ("[a-", "c", TRUE, FALSE);
310   TEST ("[^]", "a", TRUE, FALSE);
311
312   /* Ranges and special no-wildcard matches */
313   TEST ("[.]", ".", TRUE, FALSE);
314   TEST ("a[.]", "a.", TRUE, TRUE);
315   TEST ("a/[.]", "a/.", TRUE, FALSE);
316   TEST ("[/]", "/", TRUE, FALSE);
317   TEST ("[^/]", "a", TRUE, TRUE);
318   
319   /* Basic tests of * (and combinations of * and ?) */
320   TEST ("a*b", "ab", TRUE, TRUE);
321   TEST ("a*b", "axb", TRUE, TRUE);
322   TEST ("a*b", "axxb", TRUE, TRUE);
323   TEST ("a**b", "ab", TRUE, TRUE);
324   TEST ("a**b", "axb", TRUE, TRUE);
325   TEST ("a**b", "axxb", TRUE, TRUE);
326   TEST ("a*?*b", "ab", TRUE, FALSE);
327   TEST ("a*?*b", "axb", TRUE, TRUE);
328   TEST ("a*?*b", "axxb", TRUE, TRUE);
329
330   /* Test of  *[range] */
331   TEST ("a*[cd]", "ac", TRUE, TRUE);
332   TEST ("a*[cd]", "axc", TRUE, TRUE);
333   TEST ("a*[cd]", "axx", TRUE, FALSE);
334
335   TEST ("a/[.]", "a/.", TRUE, FALSE);
336   TEST ("a*[.]", "a/.", TRUE, FALSE);
337
338   /* Test of UTF-8 */
339
340   TEST ("ä", "ä", TRUE, TRUE);      /* TEST ("ä", "ä", TRUE); */
341   TEST ("?", "ä", TRUE, TRUE);       /* TEST ("?", "ä", TRUE); */
342   TEST ("*ö", "äö", TRUE, TRUE);   /* TEST ("*ö", "äö", TRUE); */
343   TEST ("*ö", "ääö", TRUE, TRUE); /* TEST ("*ö", "ääö", TRUE); */
344   TEST ("[ä]", "ä", TRUE, TRUE);    /* TEST ("[ä]", "ä", TRUE); */
345   TEST ("[ä-ö]", "é", TRUE, TRUE); /* TEST ("[ä-ö]", "é", TRUE); */
346   TEST ("[ä-ö]", "a", TRUE, FALSE); /* TEST ("[ä-ö]", "a", FALSE); */
347
348 #ifdef DO_ESCAPE
349   /* Tests of escaping */
350   TEST ("\\\\", "\\", TRUE, TRUE);
351   TEST ("\\?", "?", TRUE, TRUE);
352   TEST ("\\?", "a", TRUE, FALSE);
353   TEST ("\\*", "*", TRUE, TRUE);
354   TEST ("\\*", "a", TRUE, FALSE);
355   TEST ("\\[a-b]", "[a-b]", TRUE, TRUE);
356   TEST ("[\\\\]", "\\", TRUE, TRUE);
357   TEST ("[\\^a]", "a", TRUE, TRUE);
358   TEST ("[a\\-c]", "b", TRUE, FALSE);
359   TEST ("[a\\-c]", "-", TRUE, TRUE);
360   TEST ("[a\\]", "a", TRUE, FALSE);
361 #endif /* DO_ESCAPE */
362   
363   return 0;
364 }
365
366 #endif /* FNMATCH_TEST_CASES */