Merge commit 'tony/master'
[rox-filer/translations.git] / ROX-Filer / src / xdgmimeglob.c
blob2a88a5942d8ca84f5fa184825ef96ee4de9f1a33
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 HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #include "xdgmimeglob.h"
33 #include "xdgmimeint.h"
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <string.h>
38 #include <fnmatch.h>
40 #ifndef FALSE
41 #define FALSE (0)
42 #endif
44 #ifndef TRUE
45 #define TRUE (!FALSE)
46 #endif
48 typedef struct XdgGlobHashNode XdgGlobHashNode;
49 typedef struct XdgGlobList XdgGlobList;
51 struct XdgGlobHashNode
53 xdg_unichar_t character;
54 const char *mime_type;
55 XdgGlobHashNode *next;
56 XdgGlobHashNode *child;
58 struct XdgGlobList
60 const char *data;
61 const char *mime_type;
62 XdgGlobList *next;
65 struct XdgGlobHash
67 XdgGlobList *literal_list;
68 XdgGlobHashNode *simple_node;
69 XdgGlobList *full_list;
73 /* XdgGlobList
75 static XdgGlobList *
76 _xdg_glob_list_new (void)
78 XdgGlobList *new_element;
80 new_element = calloc (1, sizeof (XdgGlobList));
82 return new_element;
85 /* Frees glob_list and all of it's children */
86 static void
87 _xdg_glob_list_free (XdgGlobList *glob_list)
89 XdgGlobList *ptr, *next;
91 ptr = glob_list;
93 while (ptr != NULL)
95 next = ptr->next;
97 if (ptr->data)
98 free ((void *) ptr->data);
99 if (ptr->mime_type)
100 free ((void *) ptr->mime_type);
101 free (ptr);
103 ptr = next;
107 static XdgGlobList *
108 _xdg_glob_list_append (XdgGlobList *glob_list,
109 void *data,
110 const char *mime_type)
112 XdgGlobList *new_element;
113 XdgGlobList *tmp_element;
115 new_element = _xdg_glob_list_new ();
116 new_element->data = data;
117 new_element->mime_type = mime_type;
118 if (glob_list == NULL)
119 return new_element;
121 tmp_element = glob_list;
122 while (tmp_element->next != NULL)
123 tmp_element = tmp_element->next;
125 tmp_element->next = new_element;
127 return glob_list;
130 #if 0
131 static XdgGlobList *
132 _xdg_glob_list_prepend (XdgGlobList *glob_list,
133 void *data,
134 const char *mime_type)
136 XdgGlobList *new_element;
138 new_element = _xdg_glob_list_new ();
139 new_element->data = data;
140 new_element->next = glob_list;
141 new_element->mime_type = mime_type;
143 return new_element;
145 #endif
147 /* XdgGlobHashNode
150 static XdgGlobHashNode *
151 _xdg_glob_hash_node_new (void)
153 XdgGlobHashNode *glob_hash_node;
155 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
157 return glob_hash_node;
160 static void
161 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
162 int depth)
164 int i;
165 for (i = 0; i < depth; i++)
166 printf (" ");
168 printf ("%c", (char)glob_hash_node->character);
169 if (glob_hash_node->mime_type)
170 printf (" - %s\n", glob_hash_node->mime_type);
171 else
172 printf ("\n");
173 if (glob_hash_node->child)
174 _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
175 if (glob_hash_node->next)
176 _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
179 static XdgGlobHashNode *
180 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
181 const char *text,
182 const char *mime_type)
184 XdgGlobHashNode *node;
185 xdg_unichar_t character;
187 character = _xdg_utf8_to_ucs4 (text);
189 if ((glob_hash_node == NULL) ||
190 (character < glob_hash_node->character))
192 node = _xdg_glob_hash_node_new ();
193 node->character = character;
194 node->next = glob_hash_node;
195 glob_hash_node = node;
197 else if (character == glob_hash_node->character)
199 node = glob_hash_node;
201 else
203 XdgGlobHashNode *prev_node;
204 int found_node = FALSE;
206 /* Look for the first character of text in glob_hash_node, and insert it if we
207 * have to.*/
208 prev_node = glob_hash_node;
209 node = prev_node->next;
211 while (node != NULL)
213 if (character < node->character)
215 node = _xdg_glob_hash_node_new ();
216 node->character = character;
217 node->next = prev_node->next;
218 prev_node->next = node;
220 found_node = TRUE;
221 break;
223 else if (character == node->character)
225 found_node = TRUE;
226 break;
228 prev_node = node;
229 node = node->next;
232 if (! found_node)
234 node = _xdg_glob_hash_node_new ();
235 node->character = character;
236 node->next = prev_node->next;
237 prev_node->next = node;
241 text = _xdg_utf8_next_char (text);
242 if (*text == '\000')
244 if (node->mime_type)
246 if (strcmp (node->mime_type, mime_type))
248 XdgGlobHashNode *child;
249 int found_node = FALSE;
251 child = node->child;
252 while (child && child->character == '\0')
254 if (strcmp (child->mime_type, mime_type) == 0)
256 found_node = TRUE;
257 break;
259 child = child->next;
262 if (!found_node)
264 child = _xdg_glob_hash_node_new ();
265 child->character = '\000';
266 child->mime_type = strdup (mime_type);
267 child->child = NULL;
268 child->next = node->child;
269 node->child = child;
273 else
275 node->mime_type = strdup (mime_type);
278 else
280 node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
282 return glob_hash_node;
285 static int
286 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
287 const char *file_name,
288 int ignore_case,
289 const char *mime_types[],
290 int n_mime_types)
292 int n;
293 XdgGlobHashNode *node;
294 xdg_unichar_t character;
296 if (glob_hash_node == NULL)
297 return 0;
299 character = _xdg_utf8_to_ucs4 (file_name);
300 if (ignore_case)
301 character = _xdg_ucs4_to_lower(character);
303 for (node = glob_hash_node; node && character >= node->character; node = node->next)
305 if (character == node->character)
307 file_name = _xdg_utf8_next_char (file_name);
308 if (*file_name == '\000')
310 n = 0;
312 if (node->mime_type != NULL)
313 mime_types[n++] = node->mime_type;
315 node = node->child;
316 while (n < n_mime_types && node && node->character == 0)
318 if (node->mime_type != NULL)
319 mime_types[n++] = node->mime_type;
321 node = node->next;
324 else
326 n = _xdg_glob_hash_node_lookup_file_name (node->child,
327 file_name,
328 ignore_case,
329 mime_types,
330 n_mime_types);
332 return n;
336 return 0;
340 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
341 const char *file_name,
342 const char *mime_types[],
343 int n_mime_types)
345 XdgGlobList *list;
346 const char *ptr;
347 char stopchars[128];
348 int i, n;
349 XdgGlobHashNode *node;
351 /* First, check the literals */
353 assert (file_name != NULL && n_mime_types > 0);
355 for (list = glob_hash->literal_list; list; list = list->next)
357 if (strcmp ((const char *)list->data, file_name) == 0)
359 mime_types[0] = list->mime_type;
360 return 1;
364 i = 0;
365 for (node = glob_hash->simple_node; node; node = node->next)
367 if (node->character < 128)
368 stopchars[i++] = (char)node->character;
370 stopchars[i] = '\0';
372 ptr = strpbrk (file_name, stopchars);
373 while (ptr)
375 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE,
376 mime_types, n_mime_types);
377 if (n > 0)
378 return n;
380 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE,
381 mime_types, n_mime_types);
382 if (n > 0)
383 return n;
385 ptr = strpbrk (ptr + 1, stopchars);
388 /* FIXME: Not UTF-8 safe */
389 n = 0;
390 for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
392 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
393 mime_types[n++] = list->mime_type;
396 return n;
401 /* XdgGlobHash
404 XdgGlobHash *
405 _xdg_glob_hash_new (void)
407 XdgGlobHash *glob_hash;
409 glob_hash = calloc (1, sizeof (XdgGlobHash));
411 return glob_hash;
415 static void
416 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
418 if (node)
420 if (node->child)
421 _xdg_glob_hash_free_nodes (node->child);
422 if (node->next)
423 _xdg_glob_hash_free_nodes (node->next);
424 if (node->mime_type)
425 free ((void *) node->mime_type);
426 free (node);
430 void
431 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
433 _xdg_glob_list_free (glob_hash->literal_list);
434 _xdg_glob_list_free (glob_hash->full_list);
435 _xdg_glob_hash_free_nodes (glob_hash->simple_node);
436 free (glob_hash);
439 XdgGlobType
440 _xdg_glob_determine_type (const char *glob)
442 const char *ptr;
443 int maybe_in_simple_glob = FALSE;
444 int first_char = TRUE;
446 ptr = glob;
448 while (*ptr != '\000')
450 if (*ptr == '*' && first_char)
451 maybe_in_simple_glob = TRUE;
452 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
453 return XDG_GLOB_FULL;
455 first_char = FALSE;
456 ptr = _xdg_utf8_next_char (ptr);
458 if (maybe_in_simple_glob)
459 return XDG_GLOB_SIMPLE;
460 else
461 return XDG_GLOB_LITERAL;
464 /* glob must be valid UTF-8 */
465 void
466 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
467 const char *glob,
468 const char *mime_type)
470 XdgGlobType type;
472 assert (glob_hash != NULL);
473 assert (glob != NULL);
475 type = _xdg_glob_determine_type (glob);
477 switch (type)
479 case XDG_GLOB_LITERAL:
480 glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
481 break;
482 case XDG_GLOB_SIMPLE:
483 glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type);
484 break;
485 case XDG_GLOB_FULL:
486 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
487 break;
491 void
492 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
494 XdgGlobList *list;
495 printf ("LITERAL STRINGS\n");
496 if (glob_hash->literal_list == NULL)
498 printf (" None\n");
500 else
502 for (list = glob_hash->literal_list; list; list = list->next)
503 printf (" %s - %s\n", (char *)list->data, list->mime_type);
505 printf ("\nSIMPLE GLOBS\n");
506 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
508 printf ("\nFULL GLOBS\n");
509 if (glob_hash->full_list == NULL)
511 printf (" None\n");
513 else
515 for (list = glob_hash->full_list; list; list = list->next)
516 printf (" %s - %s\n", (char *)list->data, list->mime_type);
521 void
522 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
523 const char *file_name)
525 FILE *glob_file;
526 char line[255];
528 glob_file = fopen (file_name, "r");
530 if (glob_file == NULL)
531 return;
533 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
534 * Blah */
535 while (fgets (line, 255, glob_file) != NULL)
537 char *colon;
538 if (line[0] == '#')
539 continue;
541 colon = strchr (line, ':');
542 if (colon == NULL)
543 continue;
544 *(colon++) = '\000';
545 colon[strlen (colon) -1] = '\000';
546 _xdg_glob_hash_append_glob (glob_hash, colon, line);
549 fclose (glob_file);