added g_list_nth_prev() which walks ->prev instead of ->next.
[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 <string.h>
25
26
27 /* --- functions --- */
28 static inline gboolean
29 g_pattern_ph_match (const gchar *match_pattern,
30                     const gchar *match_string)
31 {
32   register const gchar *pattern, *string;
33   register gchar ch;
34
35   pattern = match_pattern;
36   string = match_string;
37
38   ch = *pattern;
39   pattern++;
40   while (ch)
41     {
42       switch (ch)
43         {
44         case '?':
45           if (!*string)
46             return FALSE;
47           string++;
48           break;
49
50         case '*':
51           do
52             {
53               ch = *pattern;
54               pattern++;
55               if (ch == '?')
56                 {
57                   if (!*string)
58                     return FALSE;
59                   string++;
60                 }
61             }
62           while (ch == '*' || ch == '?');
63           if (!ch)
64             return TRUE;
65           do
66             {
67               while (ch != *string)
68                 {
69                   if (!*string)
70                     return FALSE;
71                   string++;
72                 }
73               string++;
74               if (g_pattern_ph_match (pattern, string))
75                 return TRUE;
76             }
77           while (*string);
78           break;
79
80         default:
81           if (ch == *string)
82             string++;
83           else
84             return FALSE;
85           break;
86         }
87
88       ch = *pattern;
89       pattern++;
90     }
91
92   return *string == 0;
93 }
94
95 gboolean
96 g_pattern_match (GPatternSpec *pspec,
97                  guint         string_length,
98                  const gchar  *string,
99                  const gchar  *string_reversed)
100 {
101   g_return_val_if_fail (pspec != NULL, FALSE);
102   g_return_val_if_fail (string != NULL, FALSE);
103   g_return_val_if_fail (string_reversed != NULL, FALSE);
104
105   switch (pspec->match_type)
106     {
107     case G_MATCH_ALL:
108       return g_pattern_ph_match (pspec->pattern, string);
109
110     case G_MATCH_ALL_TAIL:
111       return g_pattern_ph_match (pspec->pattern_reversed, string_reversed);
112
113     case G_MATCH_HEAD:
114       if (pspec->pattern_length > string_length)
115         return FALSE;
116       else if (pspec->pattern_length == string_length)
117         return strcmp (pspec->pattern, string) == 0;
118       else if (pspec->pattern_length)
119         return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
120       else
121         return TRUE;
122
123     case G_MATCH_TAIL:
124       if (pspec->pattern_length > string_length)
125         return FALSE;
126       else if (pspec->pattern_length == string_length)
127         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
128       else if (pspec->pattern_length)
129         return strncmp (pspec->pattern_reversed,
130                         string_reversed,
131                         pspec->pattern_length) == 0;
132       else
133         return TRUE;
134
135     case G_MATCH_EXACT:
136       if (pspec->pattern_length != string_length)
137         return FALSE;
138       else
139         return strcmp (pspec->pattern_reversed, string_reversed) == 0;
140
141     default:
142       g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
143       return FALSE;
144     }
145 }
146
147 GPatternSpec*
148 g_pattern_spec_new (const gchar *pattern)
149 {
150   GPatternSpec *pspec;
151   gchar *p, *t;
152   const gchar *h;
153   guint hw = 0, tw = 0, hj = 0, tj = 0;
154
155   g_return_val_if_fail (pattern != NULL, NULL);
156
157   pspec = g_new (GPatternSpec, 1);
158   pspec->pattern_length = strlen (pattern);
159   pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), pattern);
160   pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
161   t = pspec->pattern_reversed + pspec->pattern_length;
162   *(t--) = 0;
163   h = pattern;
164   while (t >= pspec->pattern_reversed)
165     {
166       register gchar c = *(h++);
167
168       if (c == '*')
169         {
170           if (t < h)
171             hw++;
172           else
173             tw++;
174         }
175       else if (c == '?')
176         {
177           if (t < h)
178             hj++;
179           else
180             tj++;
181         }
182
183       *(t--) = c;
184     }
185   pspec->match_type = hw > tw || (hw == tw && hj > tj) ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
186
187   if (hj || tj)
188     return pspec;
189
190   if (hw == 0 && tw == 0)
191     {
192       pspec->match_type = G_MATCH_EXACT;
193       return pspec;
194     }
195
196   if (hw)
197     {
198       p = pspec->pattern;
199       while (*p == '*')
200         p++;
201       if (p > pspec->pattern && !strchr (p, '*'))
202         {
203           gchar *tmp;
204
205           pspec->match_type = G_MATCH_TAIL;
206           pspec->pattern_length = strlen (p);
207           tmp = pspec->pattern;
208           pspec->pattern = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
209           g_free (tmp);
210           g_free (pspec->pattern_reversed);
211           pspec->pattern_reversed = g_new (gchar, pspec->pattern_length + 1);
212           t = pspec->pattern_reversed + pspec->pattern_length;
213           *(t--) = 0;
214           h = pspec->pattern;
215           while (t >= pspec->pattern_reversed)
216             *(t--) = *(h++);
217           return pspec;
218         }
219     }
220
221   if (tw)
222     {
223       p = pspec->pattern_reversed;
224       while (*p == '*')
225         p++;
226       if (p > pspec->pattern_reversed && !strchr (p, '*'))
227         {
228           gchar *tmp;
229
230           pspec->match_type = G_MATCH_HEAD;
231           pspec->pattern_length = strlen (p);
232           tmp = pspec->pattern_reversed;
233           pspec->pattern_reversed = strcpy (g_new (gchar, pspec->pattern_length + 1), p);
234           g_free (tmp);
235           g_free (pspec->pattern);
236           pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
237           t = pspec->pattern + pspec->pattern_length;
238           *(t--) = 0;
239           h = pspec->pattern_reversed;
240           while (t >= pspec->pattern)
241             *(t--) = *(h++);
242         }
243     }
244
245   return pspec;
246 }
247
248 gboolean
249 g_pattern_match_string (GPatternSpec *pspec,
250                         const gchar  *string)
251 {
252   gchar *string_reversed, *t;
253   const gchar *h;
254   guint length;
255   gboolean ergo;
256
257   g_return_val_if_fail (pspec != NULL, FALSE);
258   g_return_val_if_fail (string != NULL, FALSE);
259
260   length = strlen (string);
261   string_reversed = g_new (gchar, length + 1);
262   t = string_reversed + length;
263   *(t--) = 0;
264   h = string;
265   while (t >= string_reversed)
266     *(t--) = *(h++);
267
268   ergo = g_pattern_match (pspec, length, string, string_reversed);
269   g_free (string_reversed);
270
271   return ergo;
272 }
273
274 gboolean
275 g_pattern_match_simple (const gchar *pattern,
276                         const gchar *string)
277 {
278   GPatternSpec *pspec;
279   gboolean ergo;
280
281   g_return_val_if_fail (pattern != NULL, FALSE);
282   g_return_val_if_fail (string != NULL, FALSE);
283
284   pspec = g_pattern_spec_new (pattern);
285   ergo = g_pattern_match_string (pspec, string);
286   g_pattern_spec_free (pspec);
287
288   return ergo;
289 }
290
291 void
292 g_pattern_spec_free (GPatternSpec *pspec)
293 {
294   g_return_if_fail (pspec != NULL);
295
296   g_free (pspec->pattern);
297   g_free (pspec->pattern_reversed);
298   g_free (pspec);
299 }