]> Pileus Git - ~andy/gtk/blob - gtk/xdgmime/xdgmimeglob.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~andy/gtk] / gtk / xdgmime / xdgmimeglob.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmimeglob.c: Private file.  Datastructure for storing the globs.
3  *
4  * More info can be found at http://www.freedesktop.org/standards/
5  *
6  * Copyright (C) 2003  Red Hat, Inc.
7  * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
8  *
9  * Licensed under the Academic Free License version 2.0
10  * Or under the following terms:
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 #include <config.h>
29 #include "xdgmimeglob.h"
30 #include "xdgmimeint.h"
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #include <string.h>
35 #include <fnmatch.h>
36
37 #ifndef FALSE
38 #define FALSE   (0)
39 #endif
40
41 #ifndef TRUE
42 #define TRUE    (!FALSE)
43 #endif
44
45 typedef struct XdgGlobHashNode XdgGlobHashNode;
46 typedef struct XdgGlobList XdgGlobList;
47
48 struct XdgGlobHashNode
49 {
50   xdg_unichar_t character;
51   const char *mime_type;
52   XdgGlobHashNode *next;
53   XdgGlobHashNode *child;
54 };
55 struct XdgGlobList
56 {
57   const char *data;
58   const char *mime_type;
59   XdgGlobList *next;
60 };
61
62 struct XdgGlobHash
63 {
64   XdgGlobList *literal_list;
65   XdgGlobHashNode *simple_node;
66   XdgGlobList *full_list;
67 };
68
69
70 /* XdgGlobList
71  */
72 static XdgGlobList *
73 _xdg_glob_list_new (void)
74 {
75   XdgGlobList *new_element;
76
77   new_element = calloc (1, sizeof (XdgGlobList));
78
79   return new_element;
80 }
81
82 /* Frees glob_list and all of it's children */
83 static void
84 _xdg_glob_list_free (XdgGlobList *glob_list)
85 {
86   XdgGlobList *ptr, *next;
87
88   ptr = glob_list;
89
90   while (ptr != NULL)
91     {
92       next = ptr->next;
93
94       if (ptr->data)
95         free ((void *) ptr->data);
96       if (ptr->mime_type)
97         free ((void *) ptr->mime_type);
98       free (ptr);
99
100       ptr = next;
101     }
102 }
103
104 static XdgGlobList *
105 _xdg_glob_list_append (XdgGlobList *glob_list,
106                        void        *data,
107                        const char  *mime_type)
108 {
109   XdgGlobList *new_element;
110   XdgGlobList *tmp_element;
111
112   new_element = _xdg_glob_list_new ();
113   new_element->data = data;
114   new_element->mime_type = mime_type;
115   if (glob_list == NULL)
116     return new_element;
117
118   tmp_element = glob_list;
119   while (tmp_element->next != NULL)
120     tmp_element = tmp_element->next;
121
122   tmp_element->next = new_element;
123
124   return glob_list;
125 }
126
127 #if 0
128 static XdgGlobList *
129 _xdg_glob_list_prepend (XdgGlobList *glob_list,
130                         void        *data,
131                         const char  *mime_type)
132 {
133   XdgGlobList *new_element;
134
135   new_element = _xdg_glob_list_new ();
136   new_element->data = data;
137   new_element->next = glob_list;
138   new_element->mime_type = mime_type;
139
140   return new_element;
141 }
142 #endif
143
144 /* XdgGlobHashNode
145  */
146
147 static XdgGlobHashNode *
148 _xdg_glob_hash_node_new (void)
149 {
150   XdgGlobHashNode *glob_hash_node;
151
152   glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
153
154   return glob_hash_node;
155 }
156
157 static void
158 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
159                           int depth)
160 {
161   int i;
162   for (i = 0; i < depth; i++)
163     printf (" ");
164
165   printf ("%c", (char)glob_hash_node->character);
166   if (glob_hash_node->mime_type)
167     printf (" - %s\n", glob_hash_node->mime_type);
168   else
169     printf ("\n");
170   if (glob_hash_node->child)
171     _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
172   if (glob_hash_node->next)
173     _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
174 }
175
176 static XdgGlobHashNode *
177 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
178                             const char      *text,
179                             const char      *mime_type)
180 {
181   XdgGlobHashNode *node;
182   xdg_unichar_t character;
183
184   character = _xdg_utf8_to_ucs4 (text);
185
186   if ((glob_hash_node == NULL) ||
187       (character < glob_hash_node->character))
188     {
189       node = _xdg_glob_hash_node_new ();
190       node->character = character;
191       node->next = glob_hash_node;
192       glob_hash_node = node;
193     }
194   else if (character == glob_hash_node->character)
195     {
196       node = glob_hash_node;
197     }
198   else
199     {
200       XdgGlobHashNode *prev_node;
201       int found_node = FALSE;
202
203       /* Look for the first character of text in glob_hash_node, and insert it if we
204        * have to.*/
205       prev_node = glob_hash_node;
206       node = prev_node->next;
207
208       while (node != NULL)
209         {
210           if (character < node->character)
211             {
212               node = _xdg_glob_hash_node_new ();
213               node->character = character;
214               node->next = prev_node->next;
215               prev_node->next = node;
216
217               found_node = TRUE;
218               break;
219             }
220           else if (character == node->character)
221             {
222               found_node = TRUE;
223               break;
224             }
225           prev_node = node;
226           node = node->next;
227         }
228
229       if (! found_node)
230         {
231           node = _xdg_glob_hash_node_new ();
232           node->character = character;
233           node->next = prev_node->next;
234           prev_node->next = node;
235         }
236     }
237
238   text = _xdg_utf8_next_char (text);
239   if (*text == '\000')
240     {
241       node->mime_type = mime_type;
242     }
243   else
244     {
245       node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
246     }
247   return glob_hash_node;
248 }
249
250 static const char *
251 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
252                                       const char      *file_name,
253                                       int              ignore_case)
254 {
255   XdgGlobHashNode *node;
256   xdg_unichar_t character;
257
258   if (glob_hash_node == NULL)
259     return NULL;
260
261   character = _xdg_utf8_to_ucs4 (file_name);
262   if (ignore_case)
263     character = _xdg_ucs4_to_upper(character);
264
265   for (node = glob_hash_node;
266        node && character >= (ignore_case?_xdg_ucs4_to_upper (node->character):node->character);
267        node = node->next)
268     {
269       if (character == (ignore_case?_xdg_ucs4_to_upper (node->character):node->character))
270         {
271           file_name = _xdg_utf8_next_char (file_name);
272           if (*file_name == '\000')
273             return node->mime_type;
274           else
275             return _xdg_glob_hash_node_lookup_file_name (node->child,
276                                                          file_name,
277                                                          ignore_case);
278         }
279     }
280   return NULL;
281 }
282
283 const char *
284 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
285                                  const char  *file_name)
286 {
287   XdgGlobList *list;
288   const char *mime_type;
289   const char *ptr;
290   /* First, check the literals */
291
292   assert (file_name != NULL);
293
294   for (list = glob_hash->literal_list; list; list = list->next)
295     if (strcmp ((const char *)list->data, file_name) == 0)
296       return list->mime_type;
297
298   for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
299     {
300       if (*ptr == '.')
301         {
302           mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE));
303           if (mime_type != NULL)
304             return mime_type;
305         }
306     }
307
308   for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
309     {
310       if (*ptr == '.')
311         {
312           mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE));
313           if (mime_type != NULL)
314             return mime_type;
315         }
316     }
317
318   /* FIXME: Not UTF-8 safe */
319   for (list = glob_hash->full_list; list; list = list->next)
320     if (fnmatch ((const char *)list->data, file_name, 0) == 0)
321       return list->mime_type;
322
323   return NULL;
324 }
325
326
327
328 /* XdgGlobHash
329  */
330
331 XdgGlobHash *
332 _xdg_glob_hash_new (void)
333 {
334   XdgGlobHash *glob_hash;
335
336   glob_hash = calloc (1, sizeof (XdgGlobHash));
337
338   return glob_hash;
339 }
340
341
342 static void
343 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
344 {
345   if (node)
346     {
347       if (node->child)
348        _xdg_glob_hash_free_nodes (node->child);
349       if (node->next)
350        _xdg_glob_hash_free_nodes (node->next);
351       free (node);
352     }
353 }
354
355 void
356 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
357 {
358   _xdg_glob_list_free (glob_hash->literal_list);
359   _xdg_glob_list_free (glob_hash->full_list);
360   _xdg_glob_hash_free_nodes (glob_hash->simple_node);
361   free (glob_hash);
362 }
363
364 XdgGlobType
365 _xdg_glob_determine_type (const char *glob)
366 {
367   const char *ptr;
368   int maybe_in_simple_glob = FALSE;
369   int first_char = TRUE;
370
371   ptr = glob;
372
373   while (*ptr != '\000')
374     {
375       if (*ptr == '*' && first_char)
376         maybe_in_simple_glob = TRUE;
377       else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
378           return XDG_GLOB_FULL;
379
380       first_char = FALSE;
381       ptr = _xdg_utf8_next_char (ptr);
382     }
383   if (maybe_in_simple_glob)
384     return XDG_GLOB_SIMPLE;
385   else
386     return XDG_GLOB_LITERAL;
387 }
388
389 /* glob must be valid UTF-8 */
390 void
391 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
392                             const char  *glob,
393                             const char  *mime_type)
394 {
395   XdgGlobType type;
396
397   assert (glob_hash != NULL);
398   assert (glob != NULL);
399
400   type = _xdg_glob_determine_type (glob);
401
402   switch (type)
403     {
404     case XDG_GLOB_LITERAL:
405       glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
406       break;
407     case XDG_GLOB_SIMPLE:
408       glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, strdup (mime_type));
409       break;
410     case XDG_GLOB_FULL:
411       glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
412       break;
413     }
414 }
415
416 void
417 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
418 {
419   XdgGlobList *list;
420   printf ("LITERAL STRINGS\n");
421   if (glob_hash->literal_list == NULL)
422     {
423       printf ("    None\n");
424     }
425   else
426     {
427       for (list = glob_hash->literal_list; list; list = list->next)
428         printf ("    %s - %s\n", (char *)list->data, list->mime_type);
429     }
430   printf ("\nSIMPLE GLOBS\n");
431   _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
432
433   printf ("\nFULL GLOBS\n");
434   if (glob_hash->full_list == NULL)
435     {
436       printf ("    None\n");
437     }
438   else
439     {
440       for (list = glob_hash->full_list; list; list = list->next)
441         printf ("    %s - %s\n", (char *)list->data, list->mime_type);
442     }
443 }
444
445
446 void
447 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
448                                const char  *file_name)
449 {
450   FILE *glob_file;
451   char line[255];
452
453   glob_file = fopen (file_name, "r");
454
455   if (glob_file == NULL)
456     return;
457
458   /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
459    * Blah */
460   while (fgets (line, 255, glob_file) != NULL)
461     {
462       char *colon;
463       if (line[0] == '#')
464         continue;
465
466       colon = strchr (line, ':');
467       if (colon == NULL)
468         continue;
469       *(colon++) = '\000';
470       colon[strlen (colon) -1] = '\000';
471       _xdg_glob_hash_append_glob (glob_hash, colon, line);
472     }
473
474   fclose (glob_file);
475 }