Fix the build
[glib.git] / gio / xdgmime / xdgmimeglob.c
blob9aa9ad9f224275aeacfd65408705f68cbf182cde
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 int weight;
56 XdgGlobHashNode *next;
57 XdgGlobHashNode *child;
59 struct XdgGlobList
61 const char *data;
62 const char *mime_type;
63 int weight;
64 XdgGlobList *next;
67 struct XdgGlobHash
69 XdgGlobList *literal_list;
70 XdgGlobHashNode *simple_node;
71 XdgGlobList *full_list;
75 /* XdgGlobList
77 static XdgGlobList *
78 _xdg_glob_list_new (void)
80 XdgGlobList *new_element;
82 new_element = calloc (1, sizeof (XdgGlobList));
84 return new_element;
87 /* Frees glob_list and all of it's children */
88 static void
89 _xdg_glob_list_free (XdgGlobList *glob_list)
91 XdgGlobList *ptr, *next;
93 ptr = glob_list;
95 while (ptr != NULL)
97 next = ptr->next;
99 if (ptr->data)
100 free ((void *) ptr->data);
101 if (ptr->mime_type)
102 free ((void *) ptr->mime_type);
103 free (ptr);
105 ptr = next;
109 static XdgGlobList *
110 _xdg_glob_list_append (XdgGlobList *glob_list,
111 void *data,
112 const char *mime_type,
113 int weight)
115 XdgGlobList *new_element;
116 XdgGlobList *tmp_element;
118 new_element = _xdg_glob_list_new ();
119 new_element->data = data;
120 new_element->mime_type = mime_type;
121 new_element->weight = weight;
122 if (glob_list == NULL)
123 return new_element;
125 tmp_element = glob_list;
126 while (tmp_element->next != NULL)
127 tmp_element = tmp_element->next;
129 tmp_element->next = new_element;
131 return glob_list;
134 /* XdgGlobHashNode
137 static XdgGlobHashNode *
138 _xdg_glob_hash_node_new (void)
140 XdgGlobHashNode *glob_hash_node;
142 glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
144 return glob_hash_node;
147 static void
148 _xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
149 int depth)
151 int i;
152 for (i = 0; i < depth; i++)
153 printf (" ");
155 printf ("%c", (char)glob_hash_node->character);
156 if (glob_hash_node->mime_type)
157 printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight);
158 else
159 printf ("\n");
160 if (glob_hash_node->child)
161 _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
162 if (glob_hash_node->next)
163 _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
166 static XdgGlobHashNode *
167 _xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node,
168 xdg_unichar_t *text,
169 const char *mime_type,
170 int weight)
172 XdgGlobHashNode *node;
173 xdg_unichar_t character;
175 character = text[0];
177 if ((glob_hash_node == NULL) ||
178 (character < glob_hash_node->character))
180 node = _xdg_glob_hash_node_new ();
181 node->character = character;
182 node->next = glob_hash_node;
183 glob_hash_node = node;
185 else if (character == glob_hash_node->character)
187 node = glob_hash_node;
189 else
191 XdgGlobHashNode *prev_node;
192 int found_node = FALSE;
194 /* Look for the first character of text in glob_hash_node, and insert it if we
195 * have to.*/
196 prev_node = glob_hash_node;
197 node = prev_node->next;
199 while (node != NULL)
201 if (character < node->character)
203 node = _xdg_glob_hash_node_new ();
204 node->character = character;
205 node->next = prev_node->next;
206 prev_node->next = node;
208 found_node = TRUE;
209 break;
211 else if (character == node->character)
213 found_node = TRUE;
214 break;
216 prev_node = node;
217 node = node->next;
220 if (! found_node)
222 node = _xdg_glob_hash_node_new ();
223 node->character = character;
224 node->next = prev_node->next;
225 prev_node->next = node;
229 text++;
230 if (*text == 0)
232 if (node->mime_type)
234 if (strcmp (node->mime_type, mime_type))
236 XdgGlobHashNode *child;
237 int found_node = FALSE;
239 child = node->child;
240 while (child && child->character == 0)
242 if (strcmp (child->mime_type, mime_type) == 0)
244 found_node = TRUE;
245 break;
247 child = child->next;
250 if (!found_node)
252 child = _xdg_glob_hash_node_new ();
253 child->character = 0;
254 child->mime_type = strdup (mime_type);
255 child->weight = weight;
256 child->child = NULL;
257 child->next = node->child;
258 node->child = child;
262 else
264 node->mime_type = strdup (mime_type);
265 node->weight = weight;
268 else
270 node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight);
272 return glob_hash_node;
275 /* glob must be valid UTF-8 */
276 static XdgGlobHashNode *
277 _xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
278 const char *text,
279 const char *mime_type,
280 int weight)
282 XdgGlobHashNode *node;
283 xdg_unichar_t *unitext;
284 int len;
286 unitext = _xdg_convert_to_ucs4 (text, &len);
287 _xdg_reverse_ucs4 (unitext, len);
288 node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight);
289 free (unitext);
290 return node;
293 typedef struct {
294 const char *mime;
295 int weight;
296 } MimeWeight;
298 static int
299 _xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
300 xdg_unichar_t *file_name,
301 int len,
302 int ignore_case,
303 MimeWeight mime_types[],
304 int n_mime_types)
306 int n;
307 XdgGlobHashNode *node;
308 xdg_unichar_t character;
310 if (glob_hash_node == NULL)
311 return 0;
313 character = file_name[len - 1];
314 if (ignore_case)
315 character = _xdg_ucs4_to_lower(character);
317 for (node = glob_hash_node; node && character >= node->character; node = node->next)
319 if (character == node->character)
321 len--;
322 n = 0;
323 if (len > 0)
325 n = _xdg_glob_hash_node_lookup_file_name (node->child,
326 file_name,
327 len,
328 ignore_case,
329 mime_types,
330 n_mime_types);
332 if (n == 0)
334 if (node->mime_type)
336 mime_types[n].mime = node->mime_type;
337 mime_types[n].weight = node->weight;
338 n++;
340 node = node->child;
341 while (n < n_mime_types && node && node->character == 0)
343 if (node->mime_type)
345 mime_types[n].mime = node->mime_type;
346 mime_types[n].weight = node->weight;
347 n++;
349 node = node->next;
352 return n;
356 return 0;
359 static int compare_mime_weight (const void *a, const void *b)
361 const MimeWeight *aa = (const MimeWeight *)a;
362 const MimeWeight *bb = (const MimeWeight *)b;
364 return aa->weight - bb->weight;
368 _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
369 const char *file_name,
370 const char *mime_types[],
371 int n_mime_types)
373 XdgGlobList *list;
374 int i, n;
375 MimeWeight mimes[10];
376 int n_mimes = 10;
377 xdg_unichar_t *ucs4;
378 int len;
380 /* First, check the literals */
382 assert (file_name != NULL && n_mime_types > 0);
384 n = 0;
386 for (list = glob_hash->literal_list; list; list = list->next)
388 if (strcmp ((const char *)list->data, file_name) == 0)
390 mime_types[0] = list->mime_type;
391 return 1;
395 ucs4 = _xdg_convert_to_ucs4 (file_name, &len);
396 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ucs4, len, FALSE,
397 mimes, n_mimes);
398 if (n == 0)
399 n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ucs4, len, TRUE,
400 mimes, n_mimes);
401 free(ucs4);
403 /* FIXME: Not UTF-8 safe */
404 if (n == 0)
406 for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
408 if (fnmatch ((const char *)list->data, file_name, 0) == 0)
410 mimes[n].mime = list->mime_type;
411 mimes[n].weight = list->weight;
412 n++;
417 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
419 if (n_mime_types < n)
420 n = n_mime_types;
422 for (i = 0; i < n; i++)
423 mime_types[i] = mimes[i].mime;
425 return n;
430 /* XdgGlobHash
433 XdgGlobHash *
434 _xdg_glob_hash_new (void)
436 XdgGlobHash *glob_hash;
438 glob_hash = calloc (1, sizeof (XdgGlobHash));
440 return glob_hash;
444 static void
445 _xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
447 if (node)
449 if (node->child)
450 _xdg_glob_hash_free_nodes (node->child);
451 if (node->next)
452 _xdg_glob_hash_free_nodes (node->next);
453 if (node->mime_type)
454 free ((void *) node->mime_type);
455 free (node);
459 void
460 _xdg_glob_hash_free (XdgGlobHash *glob_hash)
462 _xdg_glob_list_free (glob_hash->literal_list);
463 _xdg_glob_list_free (glob_hash->full_list);
464 _xdg_glob_hash_free_nodes (glob_hash->simple_node);
465 free (glob_hash);
468 XdgGlobType
469 _xdg_glob_determine_type (const char *glob)
471 const char *ptr;
472 int maybe_in_simple_glob = FALSE;
473 int first_char = TRUE;
475 ptr = glob;
477 while (*ptr != '\0')
479 if (*ptr == '*' && first_char)
480 maybe_in_simple_glob = TRUE;
481 else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
482 return XDG_GLOB_FULL;
484 first_char = FALSE;
485 ptr = _xdg_utf8_next_char (ptr);
487 if (maybe_in_simple_glob)
488 return XDG_GLOB_SIMPLE;
489 else
490 return XDG_GLOB_LITERAL;
493 /* glob must be valid UTF-8 */
494 void
495 _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
496 const char *glob,
497 const char *mime_type,
498 int weight)
500 XdgGlobType type;
502 assert (glob_hash != NULL);
503 assert (glob != NULL);
505 type = _xdg_glob_determine_type (glob);
507 switch (type)
509 case XDG_GLOB_LITERAL:
510 glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight);
511 break;
512 case XDG_GLOB_SIMPLE:
513 glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight);
514 break;
515 case XDG_GLOB_FULL:
516 glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight);
517 break;
521 void
522 _xdg_glob_hash_dump (XdgGlobHash *glob_hash)
524 XdgGlobList *list;
525 printf ("LITERAL STRINGS\n");
526 if (!glob_hash || glob_hash->literal_list == NULL)
528 printf (" None\n");
530 else
532 for (list = glob_hash->literal_list; list; list = list->next)
533 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
535 printf ("\nSIMPLE GLOBS\n");
536 if (!glob_hash || glob_hash->simple_node == NULL)
538 printf (" None\n");
540 else
542 _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
545 printf ("\nFULL GLOBS\n");
546 if (!glob_hash || glob_hash->full_list == NULL)
548 printf (" None\n");
550 else
552 for (list = glob_hash->full_list; list; list = list->next)
553 printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight);
558 void
559 _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
560 const char *file_name)
562 FILE *glob_file;
563 char line[255];
565 glob_file = fopen (file_name, "r");
567 if (glob_file == NULL)
568 return;
570 /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars.
571 * Blah */
572 while (fgets (line, 255, glob_file) != NULL)
574 char *colon, *colon2;
575 char *mimetype, *glob;
576 int weight;
578 if (line[0] == '#')
579 continue;
581 colon = strchr (line, ':');
582 if (colon == NULL)
583 continue;
584 *(colon++) = '\0';
585 colon[strlen (colon) -1] = '\0';
586 colon2 = strchr (colon, ':');
587 if (colon2)
589 *(colon2++) = '\000';
590 weight = atoi (line);
591 mimetype = colon;
592 glob = colon2;
594 else
596 weight = 50;
597 mimetype = line;
598 glob = colon;
600 _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight);
603 fclose (glob_file);