Hit.cs: Remove FIXME by using binary search while searching properties.
[beagle.git] / chooser-fu / xdgmimeglob.c
blobd9e6ba98e4bc0249337d22a77d28e13ad904c798
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmimeglob.c: Private file. Datastructure for storing the globs.
4 * More info can be found at http://www.freedesktop.org/standards/
6 * Copyright (C) 2003 Red Hat, Inc.
7 * Copyright (C) 2003 Jonathan Blandford <jrb@alum.mit.edu>
9 * Licensed under the Academic Free License version 2.0
10 * Or under the following terms:
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.
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.
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.
28 #ifdef CONFIG_H
29 #include <config.h>
30 #endif
31 #include "xdgmimeglob.h"
32 #include "xdgmimeint.h"
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <fnmatch.h>
39 #ifndef FALSE
40 #define FALSE (0)
41 #endif
43 #ifndef TRUE
44 #define TRUE (!FALSE)
45 #endif
47 typedef struct XdgGlobHashNode XdgGlobHashNode;
48 typedef struct XdgGlobList XdgGlobList;
50 struct XdgGlobHashNode
52 xdg_unichar_t character;
53 const char *mime_type;
54 XdgGlobHashNode *next;
55 XdgGlobHashNode *child;
57 struct XdgGlobList
59 const char *data;
60 const char *mime_type;
61 XdgGlobList *next;
64 struct XdgGlobHash
66 XdgGlobList *literal_list;
67 XdgGlobHashNode *simple_node;
68 XdgGlobList *full_list;
72 /* XdgGlobList
74 static XdgGlobList *
75 _xdg_glob_list_new (void)
77 XdgGlobList *new_element;
79 new_element = calloc (1, sizeof (XdgGlobList));
81 return new_element;
84 /* Frees glob_list and all of it's children */
85 static void
86 _xdg_glob_list_free (XdgGlobList *glob_list)
88 XdgGlobList *ptr, *next;
90 ptr = glob_list;
92 while (ptr != NULL)
94 next = ptr->next;
96 if (ptr->data)
97 free ((void *) ptr->data);
98 if (ptr->mime_type)
99 free ((void *) ptr->mime_type);
100 free (ptr);
102 ptr = next;
106 static XdgGlobList *
107 _xdg_glob_list_append (XdgGlobList *glob_list,
108 void *data,
109 const char *mime_type)
111 XdgGlobList *new_element;
112 XdgGlobList *tmp_element;
114 new_element = _xdg_glob_list_new ();
115 new_element->data = data;
116 new_element->mime_type = mime_type;
117 if (glob_list == NULL)
118 return new_element;
120 tmp_element = glob_list;
121 while (tmp_element->next != NULL)
122 tmp_element = tmp_element->next;
124 tmp_element->next = new_element;
126 return glob_list;
129 #if 0
130 static XdgGlobList *
131 _xdg_glob_list_prepend (XdgGlobList *glob_list,
132 void *data,
133 const char *mime_type)
135 XdgGlobList *new_element;
137 new_element = _xdg_glob_list_new ();
138 new_element->data = data;
139 new_element->next = glob_list;
140 new_element->mime_type = mime_type;
142 return new_element;
144 #endif
146 /* XdgGlobHashNode
149 static XdgGlobHashNode *
150 _xdg_glob_hash_node_new (void)
152 XdgGlobHashNode *glob_hash_node;
154 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
156 return glob_hash_node;
159 static void
160 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
161 int depth)
163 int i;
164 for (i = 0; i < depth; i++)
165 printf (" ");
167 printf ("%c", (char)glob_hash_node->character);
168 if (glob_hash_node->mime_type)
169 printf (" - %s\n", glob_hash_node->mime_type);
170 else
171 printf ("\n");
172 if (glob_hash_node->child)
173 _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
174 if (glob_hash_node->next)
175 _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
178 static XdgGlobHashNode *
179 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
180 const char *text,
181 const char *mime_type)
183 XdgGlobHashNode *node;
184 xdg_unichar_t character;
186 character = _xdg_utf8_to_ucs4 (text);
188 if ((glob_hash_node == NULL) ||
189 (character < glob_hash_node->character))
191 node = _xdg_glob_hash_node_new ();
192 node->character = character;
193 node->next = glob_hash_node;
194 glob_hash_node = node;
196 else if (character == glob_hash_node->character)
198 node = glob_hash_node;
200 else
202 XdgGlobHashNode *prev_node;
203 int found_node = FALSE;
205 /* Look for the first character of text in glob_hash_node, and insert it if we
206 * have to.*/
207 prev_node = glob_hash_node;
208 node = prev_node->next;
210 while (node != NULL)
212 if (character < node->character)
214 node = _xdg_glob_hash_node_new ();
215 node->character = character;
216 node->next = prev_node->next;
217 prev_node->next = node;
219 found_node = TRUE;
220 break;
222 else if (character == node->character)
224 found_node = TRUE;
225 break;
227 prev_node = node;
228 node = node->next;
231 if (! found_node)
233 node = _xdg_glob_hash_node_new ();
234 node->character = character;
235 node->next = prev_node->next;
236 prev_node->next = node;
240 text = _xdg_utf8_next_char (text);
241 if (*text == '\000')
243 node->mime_type = mime_type;
245 else
247 node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
249 return glob_hash_node;
252 static const char *
253 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
254 const char *file_name,
255 int ignore_case)
257 XdgGlobHashNode *node;
258 xdg_unichar_t character;
260 if (glob_hash_node == NULL)
261 return NULL;
263 character = _xdg_utf8_to_ucs4 (file_name);
264 if (ignore_case)
265 character = _xdg_ucs4_to_upper(character);
267 for (node = glob_hash_node;
268 node && character >= (ignore_case?_xdg_ucs4_to_upper (node->character):node->character);
269 node = node->next)
271 if (character == (ignore_case?_xdg_ucs4_to_upper (node->character):node->character))
273 file_name = _xdg_utf8_next_char (file_name);
274 if (*file_name == '\000')
275 return node->mime_type;
276 else
277 return _xdg_glob_hash_node_lookup_file_name (node->child,
278 file_name,
279 ignore_case);
282 return NULL;
285 const char *
286 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
287 const char *file_name)
289 XdgGlobList *list;
290 const char *mime_type;
291 const char *ptr;
292 /* First, check the literals */
294 assert (file_name != NULL);
296 for (list = glob_hash->literal_list; list; list = list->next)
297 if (strcmp ((const char *)list->data, file_name) == 0)
298 return list->mime_type;
300 for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
302 if (*ptr == '.')
304 mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE));
305 if (mime_type != NULL)
306 return mime_type;
310 for (ptr = file_name; *ptr != '\000'; ptr = _xdg_utf8_next_char (ptr))
312 if (*ptr == '.')
314 mime_type = (_xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE));
315 if (mime_type != NULL)
316 return mime_type;
320 /* FIXME: Not UTF-8 safe */
321 for (list = glob_hash->full_list; list; list = list->next)
322 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
323 return list->mime_type;
325 return NULL;
330 /* XdgGlobHash
333 XdgGlobHash *
334 _xdg_glob_hash_new (void)
336 XdgGlobHash *glob_hash;
338 glob_hash = calloc (1, sizeof (XdgGlobHash));
340 return glob_hash;
344 static void
345 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
347 if (node)
349 if (node->child)
350 _xdg_glob_hash_free_nodes (node->child);
351 if (node->next)
352 _xdg_glob_hash_free_nodes (node->next);
353 free (node);
357 void
358 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
360 _xdg_glob_list_free (glob_hash->literal_list);
361 _xdg_glob_list_free (glob_hash->full_list);
362 _xdg_glob_hash_free_nodes (glob_hash->simple_node);
363 free (glob_hash);
366 XdgGlobType
367 _xdg_glob_determine_type (const char *glob)
369 const char *ptr;
370 int maybe_in_simple_glob = FALSE;
371 int first_char = TRUE;
373 ptr = glob;
375 while (*ptr != '\000')
377 if (*ptr == '*' && first_char)
378 maybe_in_simple_glob = TRUE;
379 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
380 return XDG_GLOB_FULL;
382 first_char = FALSE;
383 ptr = _xdg_utf8_next_char (ptr);
385 if (maybe_in_simple_glob)
386 return XDG_GLOB_SIMPLE;
387 else
388 return XDG_GLOB_LITERAL;
391 /* glob must be valid UTF-8 */
392 void
393 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
394 const char *glob,
395 const char *mime_type)
397 XdgGlobType type;
399 assert (glob_hash != NULL);
400 assert (glob != NULL);
402 type = _xdg_glob_determine_type (glob);
404 switch (type)
406 case XDG_GLOB_LITERAL:
407 glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
408 break;
409 case XDG_GLOB_SIMPLE:
410 glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, strdup (mime_type));
411 break;
412 case XDG_GLOB_FULL:
413 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
414 break;
418 void
419 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
421 XdgGlobList *list;
422 printf ("LITERAL STRINGS\n");
423 if (glob_hash->literal_list == NULL)
425 printf (" None\n");
427 else
429 for (list = glob_hash->literal_list; list; list = list->next)
430 printf (" %s - %s\n", (char *)list->data, list->mime_type);
432 printf ("\nSIMPLE GLOBS\n");
433 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
435 printf ("\nFULL GLOBS\n");
436 if (glob_hash->full_list == NULL)
438 printf (" None\n");
440 else
442 for (list = glob_hash->full_list; list; list = list->next)
443 printf (" %s - %s\n", (char *)list->data, list->mime_type);
448 void
449 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
450 const char *file_name)
452 FILE *glob_file;
453 char line[255];
455 glob_file = fopen (file_name, "r");
457 if (glob_file == NULL)
458 return;
460 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
461 * Blah */
462 while (fgets (line, 255, glob_file) != NULL)
464 char *colon;
465 if (line[0] == '#')
466 continue;
468 colon = strchr (line, ':');
469 if (colon == NULL)
470 continue;
471 *(colon++) = '\000';
472 colon[strlen (colon) -1] = '\000';
473 _xdg_glob_hash_append_glob (glob_hash, colon, line);
476 fclose (glob_file);