updated
[platform/upstream/glib.git] / glib / gpattern.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997, 1999  Peter Mattis, Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "gpattern.h"
20
21 #include "gmacros.h"
22 #include "gmessages.h"
23 #include "gmem.h"
24 #include "gutils.h" /* inline hassle */
25 #include <string.h>
26
27
28 /* --- functions --- */
29 static inline gboolean
30 g_pattern_ph_match (const gchar *match_pattern,
31                     const gchar *match_string)
32 {
33   register const gchar *pattern, *string;
34   register gchar ch;
35
36   pattern = match_pattern;
37   string = match_string;
38
39   ch = *pattern;
40   pattern++;
41   while (ch)
42     {
43       switch (ch)
44         {
45         case '?':
46           if (!*string)
47             return FALSE;
48           string++;
49           break;
50
51         case '*':
52           do
53             {
54               ch = *pattern;
55               pattern++;
56               if (ch == '?')
57                 {
58                   if (!*string)
59                     return FALSE;
60                   string++;
61                 }
62             }
63           while (ch == '*' || ch == '?');
64           if (!ch)
65             return TRUE;
66           do
67             {
68               while (ch != *string)
69                 {
70                   if (!*string)
71                     return FALSE;
72                   string++;
73                 }
74               string++;
75               if (g_pattern_ph_match (pattern, string))
76                 return TRUE;
77             }
78           while (*string);
79           break;
80
81         default:
82           if (ch == *string)
83             string++;
84           else
85             return FALSE;
86           break;
87         }
88
89       ch = *pattern;
90       pattern++;
91     }
92
93   return *string == 0;
94 }
95
96 gboolean
97 g_pattern_match (GPatternSpec *pspec,
98                  guint         string_length,
99                  const gchar  *string,
100                  const gchar  *string_reversed)
101 {
102   g_return_val_if_fail (pspec != NULL, FALSE);
103   g_return_val_if_fail (string != NULL, FALSE);
104   g_return_val_if_fail (string_reversed != NULL, FALSE);
105
106   switch (pspec->match_type)
107     {
108     case G_MATCH_ALL:
109       return g_pattern_ph_match (pspec->pattern, string);
110
111     case G_MATCH_ALL_TAIL:
112       return g_pattern_ph_match (pspec->pattern_reversed, string_reversed);
113
114     case G_MATCH_HEAD:
115       if (pspec->pattern_length > string_length)
116         return FALSE;
117       else if (pspec->pattern_length == string_length)
118         return strcmp (pspec->pattern, string) == 0;
119       else if (pspec->pattern_length)
120         return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
121       else
122         return TRUE;
123
124     case G_MATCH_TAIL:
125       if (pspec->pattern_length > string_length)
126         return FALSE;
127       else if (pspec->pattern_length == string_length)
128         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
129       else if (pspec->pattern_length)
130         return strncmp (pspec->pattern_reversed,
131                         string_reversed,
132                         pspec->pattern_length) == 0;
133       else
134         return TRUE;
135
136     case G_MATCH_EXACT:
137       if (pspec->pattern_length != string_length)
138         return FALSE;
139       else
140         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
141
142     default:
143       g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
144       return FALSE;
145     }
146 }
147
148 GPatternSpec*
149 g_pattern_spec_new (const gchar *pattern)
150 {
151   GPatternSpec *pspec;
152   gchar *p, *t;
153   const gchar *h;
154   guint hw = 0, tw = 0, hj = 0, tj = 0;
155
156   g_return_val_if_fail (pattern != NULL, NULL);
157
158   pspec = g_new (GPatternSpec, 1);
159   pspec->pattern_length = strlen (pattern);
160   pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), pattern);
161   pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
162   t = pspec->pattern_reversed + pspec->pattern_length;
163   *(t--) = 0;
164   h = pattern;
165   while (t >= pspec->pattern_reversed)
166     {
167       register gchar c = *(h++);
168
169       if (c == '*')
170         {
171           if (t < h)
172             hw++;
173           else
174             tw++;
175         }
176       else if (c == '?')
177         {
178           if (t < h)
179             hj++;
180           else
181             tj++;
182         }
183
184       *(t--) = c;
185     }
186   pspec->match_type = hw > tw || (hw == tw && hj > tj) ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
187
188   if (hj || tj)
189     return pspec;
190
191   if (hw == 0 && tw == 0)
192     {
193       pspec->match_type = G_MATCH_EXACT;
194       return pspec;
195     }
196
197   if (hw)
198     {
199       p = pspec->pattern;
200       while (*p == '*')
201         p++;
202       if (p > pspec->pattern && !strchr (p, '*'))
203         {
204           gchar *tmp;
205
206           pspec->match_type = G_MATCH_TAIL;
207           pspec->pattern_length = strlen (p);
208           tmp = pspec->pattern;
209           pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
210           g_free (tmp);
211           g_free (pspec->pattern_reversed);
212           pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
213           t = pspec->pattern_reversed + pspec->pattern_length;
214           *(t--) = 0;
215           h = pspec->pattern;
216           while (t >= pspec->pattern_reversed)
217             *(t--) = *(h++);
218           return pspec;
219         }
220     }
221
222   if (tw)
223     {
224       p = pspec->pattern_reversed;
225       while (*p == '*')
226         p++;
227       if (p > pspec->pattern_reversed && !strchr (p, '*'))
228         {
229           gchar *tmp;
230
231           pspec->match_type = G_MATCH_HEAD;
232           pspec->pattern_length = strlen (p);
233           tmp = pspec->pattern_reversed;
234           pspec->pattern_reversed = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
235           g_free (tmp);
236           g_free (pspec->pattern);
237           pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
238           t = pspec->pattern + pspec->pattern_length;
239           *(t--) = 0;
240           h = pspec->pattern_reversed;
241           while (t >= pspec->pattern)
242             *(t--) = *(h++);
243         }
244     }
245
246   return pspec;
247 }
248
249 gboolean
250 g_pattern_match_string (GPatternSpec *pspec,
251                         const gchar  *string)
252 {
253   gchar *string_reversed, *t;
254   const gchar *h;
255   guint length;
256   gboolean ergo;
257
258   g_return_val_if_fail (pspec != NULL, FALSE);
259   g_return_val_if_fail (string != NULL, FALSE);
260
261   length = strlen (string);
262   string_reversed = g_new (gchar, length + 1);
263   t = string_reversed + length;
264   *(t--) = 0;
265   h = string;
266   while (t >= string_reversed)
267     *(t--) = *(h++);
268
269   ergo = g_pattern_match (pspec, length, string, string_reversed);
270   g_free (string_reversed);
271
272   return ergo;
273 }
274
275 gboolean
276 g_pattern_match_simple (const gchar *pattern,
277                         const gchar *string)
278 {
279   GPatternSpec *pspec;
280   gboolean ergo;
281
282   g_return_val_if_fail (pattern != NULL, FALSE);
283   g_return_val_if_fail (string != NULL, FALSE);
284
285   pspec = g_pattern_spec_new (pattern);
286   ergo = g_pattern_match_string (pspec, string);
287   g_pattern_spec_free (pspec);
288
289   return ergo;
290 }
291
292 void
293 g_pattern_spec_free (GPatternSpec *pspec)
294 {
295   g_return_if_fail (pspec != NULL);
296
297   g_free (pspec->pattern);
298   g_free (pspec->pattern_reversed);
299   g_free (pspec);
300 }