fix a few warnings on non-Linux
[platform/upstream/glib.git] / gio / xdgmime / xdgmime.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
3  *
4  * More info can be found at http://www.freedesktop.org/standards/
5  * 
6  * Copyright (C) 2003,2004  Red Hat, Inc.
7  * Copyright (C) 2003,2004  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 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "xdgmime.h"
33 #include "xdgmimeint.h"
34 #include "xdgmimeglob.h"
35 #include "xdgmimemagic.h"
36 #include "xdgmimealias.h"
37 #include "xdgmimeicon.h"
38 #include "xdgmimeparent.h"
39 #include "xdgmimecache.h"
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/time.h>
45 #include <unistd.h>
46 #include <assert.h>
47
48 typedef struct XdgDirTimeList XdgDirTimeList;
49 typedef struct XdgCallbackList XdgCallbackList;
50
51 static int need_reread = TRUE;
52 static time_t last_stat_time = 0;
53
54 static XdgGlobHash *global_hash = NULL;
55 static XdgMimeMagic *global_magic = NULL;
56 static XdgAliasList *alias_list = NULL;
57 static XdgParentList *parent_list = NULL;
58 static XdgDirTimeList *dir_time_list = NULL;
59 static XdgCallbackList *callback_list = NULL;
60 static XdgIconList *icon_list = NULL;
61 static XdgIconList *generic_icon_list = NULL;
62
63 XdgMimeCache **_caches = NULL;
64 static int n_caches = 0;
65
66 const char xdg_mime_type_unknown[] = "application/octet-stream";
67
68
69 enum
70 {
71   XDG_CHECKED_UNCHECKED,
72   XDG_CHECKED_VALID,
73   XDG_CHECKED_INVALID
74 };
75
76 struct XdgDirTimeList
77 {
78   time_t mtime;
79   char *directory_name;
80   int checked;
81   XdgDirTimeList *next;
82 };
83
84 struct XdgCallbackList
85 {
86   XdgCallbackList *next;
87   XdgCallbackList *prev;
88   int              callback_id;
89   XdgMimeCallback  callback;
90   void            *data;
91   XdgMimeDestroy   destroy;
92 };
93
94 /* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
95  * directories aren't looked at */
96 typedef int (*XdgDirectoryFunc) (const char *directory,
97                                  void       *user_data);
98
99 static void
100 xdg_dir_time_list_add (char   *file_name, 
101                        time_t  mtime)
102 {
103   XdgDirTimeList *list;
104
105   for (list = dir_time_list; list; list = list->next) 
106     {
107       if (strcmp (list->directory_name, file_name) == 0)
108         {
109           free (file_name);
110           return;
111         }
112     }
113   
114   list = calloc (1, sizeof (XdgDirTimeList));
115   list->checked = XDG_CHECKED_UNCHECKED;
116   list->directory_name = file_name;
117   list->mtime = mtime;
118   list->next = dir_time_list;
119   dir_time_list = list;
120 }
121  
122 static void
123 xdg_dir_time_list_free (XdgDirTimeList *list)
124 {
125   XdgDirTimeList *next;
126
127   while (list)
128     {
129       next = list->next;
130       free (list->directory_name);
131       free (list);
132       list = next;
133     }
134 }
135
136 static int
137 xdg_mime_init_from_directory (const char *directory)
138 {
139   char *file_name;
140   struct stat st;
141
142   assert (directory != NULL);
143
144   file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
145   strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
146   if (stat (file_name, &st) == 0)
147     {
148       XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
149
150       if (cache != NULL)
151         {
152           xdg_dir_time_list_add (file_name, st.st_mtime);
153
154           _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
155           _caches[n_caches] = cache;
156           _caches[n_caches + 1] = NULL;
157           n_caches++;
158
159           return FALSE;
160         }
161     }
162   free (file_name);
163
164   file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1);
165   strcpy (file_name, directory); strcat (file_name, "/mime/globs2");
166   if (stat (file_name, &st) == 0)
167     {
168       _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE);
169       xdg_dir_time_list_add (file_name, st.st_mtime);
170     }
171   else
172     {
173       free (file_name);
174       file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
175       strcpy (file_name, directory); strcat (file_name, "/mime/globs");
176       if (stat (file_name, &st) == 0)
177         {
178           _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE);
179           xdg_dir_time_list_add (file_name, st.st_mtime);
180         }
181       else
182         {
183           free (file_name);
184         }
185     }
186
187   file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
188   strcpy (file_name, directory); strcat (file_name, "/mime/magic");
189   if (stat (file_name, &st) == 0)
190     {
191       _xdg_mime_magic_read_from_file (global_magic, file_name);
192       xdg_dir_time_list_add (file_name, st.st_mtime);
193     }
194   else
195     {
196       free (file_name);
197     }
198
199   file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
200   strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
201   _xdg_mime_alias_read_from_file (alias_list, file_name);
202   free (file_name);
203
204   file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
205   strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
206   _xdg_mime_parent_read_from_file (parent_list, file_name);
207   free (file_name);
208
209   file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1);
210   strcpy (file_name, directory); strcat (file_name, "/mime/icons");
211   _xdg_mime_icon_read_from_file (icon_list, file_name);
212   free (file_name);
213
214   file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1);
215   strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons");
216   _xdg_mime_icon_read_from_file (generic_icon_list, file_name);
217   free (file_name);
218
219   return FALSE; /* Keep processing */
220 }
221
222 /* Runs a command on all the directories in the search path */
223 static void
224 xdg_run_command_on_dirs (XdgDirectoryFunc  func,
225                          void             *user_data)
226 {
227   const char *xdg_data_home;
228   const char *xdg_data_dirs;
229   const char *ptr;
230
231   xdg_data_home = getenv ("XDG_DATA_HOME");
232   if (xdg_data_home)
233     {
234       if ((func) (xdg_data_home, user_data))
235         return;
236     }
237   else
238     {
239       const char *home;
240
241       home = getenv ("HOME");
242       if (home != NULL)
243         {
244           char *guessed_xdg_home;
245           int stop_processing;
246
247           guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
248           strcpy (guessed_xdg_home, home);
249           strcat (guessed_xdg_home, "/.local/share/");
250           stop_processing = (func) (guessed_xdg_home, user_data);
251           free (guessed_xdg_home);
252
253           if (stop_processing)
254             return;
255         }
256     }
257
258   xdg_data_dirs = getenv ("XDG_DATA_DIRS");
259   if (xdg_data_dirs == NULL)
260     xdg_data_dirs = "/usr/local/share/:/usr/share/";
261
262   ptr = xdg_data_dirs;
263
264   while (*ptr != '\000')
265     {
266       const char *end_ptr;
267       char *dir;
268       int len;
269       int stop_processing;
270
271       end_ptr = ptr;
272       while (*end_ptr != ':' && *end_ptr != '\000')
273         end_ptr ++;
274
275       if (end_ptr == ptr)
276         {
277           ptr++;
278           continue;
279         }
280
281       if (*end_ptr == ':')
282         len = end_ptr - ptr;
283       else
284         len = end_ptr - ptr + 1;
285       dir = malloc (len + 1);
286       strncpy (dir, ptr, len);
287       dir[len] = '\0';
288       stop_processing = (func) (dir, user_data);
289       free (dir);
290
291       if (stop_processing)
292         return;
293
294       ptr = end_ptr;
295     }
296 }
297
298 /* Checks file_path to make sure it has the same mtime as last time it was
299  * checked.  If it has a different mtime, or if the file doesn't exist, it
300  * returns FALSE.
301  *
302  * FIXME: This doesn't protect against permission changes.
303  */
304 static int
305 xdg_check_file (const char *file_path,
306                 int        *exists)
307 {
308   struct stat st;
309
310   /* If the file exists */
311   if (stat (file_path, &st) == 0)
312     {
313       XdgDirTimeList *list;
314
315       if (exists)
316         *exists = TRUE;
317
318       for (list = dir_time_list; list; list = list->next)
319         {
320           if (! strcmp (list->directory_name, file_path))
321             {
322               if (st.st_mtime == list->mtime)
323                 list->checked = XDG_CHECKED_VALID;
324               else 
325                 list->checked = XDG_CHECKED_INVALID;
326
327               return (list->checked != XDG_CHECKED_VALID);
328             }
329         }
330       return TRUE;
331     }
332
333   if (exists)
334     *exists = FALSE;
335
336   return FALSE;
337 }
338
339 static int
340 xdg_check_dir (const char *directory,
341                int        *invalid_dir_list)
342 {
343   int invalid, exists;
344   char *file_name;
345
346   assert (directory != NULL);
347
348   /* Check the mime.cache file */
349   file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
350   strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
351   invalid = xdg_check_file (file_name, &exists);
352   free (file_name);
353   if (invalid)
354     {
355       *invalid_dir_list = TRUE;
356       return TRUE;
357     }
358   else if (exists)
359     {
360       return FALSE;
361     }
362
363   /* Check the globs file */
364   file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
365   strcpy (file_name, directory); strcat (file_name, "/mime/globs");
366   invalid = xdg_check_file (file_name, NULL);
367   free (file_name);
368   if (invalid)
369     {
370       *invalid_dir_list = TRUE;
371       return TRUE;
372     }
373
374   /* Check the magic file */
375   file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
376   strcpy (file_name, directory); strcat (file_name, "/mime/magic");
377   invalid = xdg_check_file (file_name, NULL);
378   free (file_name);
379   if (invalid)
380     {
381       *invalid_dir_list = TRUE;
382       return TRUE;
383     }
384
385   return FALSE; /* Keep processing */
386 }
387
388 /* Walks through all the mime files stat()ing them to see if they've changed.
389  * Returns TRUE if they have. */
390 static int
391 xdg_check_dirs (void)
392 {
393   XdgDirTimeList *list;
394   int invalid_dir_list = FALSE;
395
396   for (list = dir_time_list; list; list = list->next)
397     list->checked = XDG_CHECKED_UNCHECKED;
398
399   xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
400                            &invalid_dir_list);
401
402   if (invalid_dir_list)
403     return TRUE;
404
405   for (list = dir_time_list; list; list = list->next)
406     {
407       if (list->checked != XDG_CHECKED_VALID)
408         return TRUE;
409     }
410
411   return FALSE;
412 }
413
414 /* We want to avoid stat()ing on every single mime call, so we only look for
415  * newer files every 5 seconds.  This will return TRUE if we need to reread the
416  * mime data from disk.
417  */
418 static int
419 xdg_check_time_and_dirs (void)
420 {
421   struct timeval tv;
422   time_t current_time;
423   int retval = FALSE;
424
425   gettimeofday (&tv, NULL);
426   current_time = tv.tv_sec;
427
428   if (current_time >= last_stat_time + 5)
429     {
430       retval = xdg_check_dirs ();
431       last_stat_time = current_time;
432     }
433
434   return retval;
435 }
436
437 /* Called in every public function.  It reloads the hash function if need be.
438  */
439 static void
440 xdg_mime_init (void)
441 {
442   if (xdg_check_time_and_dirs ())
443     {
444       xdg_mime_shutdown ();
445     }
446
447   if (need_reread)
448     {
449       global_hash = _xdg_glob_hash_new ();
450       global_magic = _xdg_mime_magic_new ();
451       alias_list = _xdg_mime_alias_list_new ();
452       parent_list = _xdg_mime_parent_list_new ();
453       icon_list = _xdg_mime_icon_list_new ();
454       generic_icon_list = _xdg_mime_icon_list_new ();
455
456       xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
457                                NULL);
458
459       need_reread = FALSE;
460     }
461 }
462
463 const char *
464 xdg_mime_get_mime_type_for_data (const void *data,
465                                  size_t      len,
466                                  int        *result_prio)
467 {
468   const char *mime_type;
469
470   xdg_mime_init ();
471
472   if (_caches)
473     return _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio);
474
475   mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0);
476
477   if (mime_type)
478     return mime_type;
479
480   return XDG_MIME_TYPE_UNKNOWN;
481 }
482
483 #ifdef NOT_USED_IN_GIO
484
485 const char *
486 xdg_mime_get_mime_type_for_file (const char  *file_name,
487                                  struct stat *statbuf)
488 {
489   const char *mime_type;
490   /* currently, only a few globs occur twice, and none
491    * more often, so 5 seems plenty.
492    */
493   const char *mime_types[5];
494   FILE *file;
495   unsigned char *data;
496   int max_extent;
497   int bytes_read;
498   struct stat buf;
499   const char *base_name;
500   int n;
501
502   if (file_name == NULL)
503     return NULL;
504   if (! _xdg_utf8_validate (file_name))
505     return NULL;
506
507   xdg_mime_init ();
508
509   if (_caches)
510     return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
511
512   base_name = _xdg_get_base_name (file_name);
513   n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
514
515   if (n == 1)
516     return mime_types[0];
517
518   if (!statbuf)
519     {
520       if (stat (file_name, &buf) != 0)
521         return XDG_MIME_TYPE_UNKNOWN;
522
523       statbuf = &buf;
524     }
525
526   if (!S_ISREG (statbuf->st_mode))
527     return XDG_MIME_TYPE_UNKNOWN;
528
529   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
530    * be large and need getting from a stream instead of just reading it all
531    * in. */
532   max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
533   data = malloc (max_extent);
534   if (data == NULL)
535     return XDG_MIME_TYPE_UNKNOWN;
536         
537   file = fopen (file_name, "r");
538   if (file == NULL)
539     {
540       free (data);
541       return XDG_MIME_TYPE_UNKNOWN;
542     }
543
544   bytes_read = fread (data, 1, max_extent, file);
545   if (ferror (file))
546     {
547       free (data);
548       fclose (file);
549       return XDG_MIME_TYPE_UNKNOWN;
550     }
551
552   mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
553                                            mime_types, n);
554
555   free (data);
556   fclose (file);
557
558   if (mime_type)
559     return mime_type;
560
561   return XDG_MIME_TYPE_UNKNOWN;
562 }
563
564 const char *
565 xdg_mime_get_mime_type_from_file_name (const char *file_name)
566 {
567   const char *mime_type;
568
569   xdg_mime_init ();
570
571   if (_caches)
572     return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
573
574   if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
575     return mime_type;
576   else
577     return XDG_MIME_TYPE_UNKNOWN;
578 }
579
580 #endif
581
582 int
583 xdg_mime_get_mime_types_from_file_name (const char *file_name,
584                                         const char  *mime_types[],
585                                         int          n_mime_types)
586 {
587   xdg_mime_init ();
588   
589   if (_caches)
590     return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
591   
592   return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
593 }
594
595 #ifdef NOT_USED_IN_GIO
596
597 int
598 xdg_mime_is_valid_mime_type (const char *mime_type)
599 {
600   /* FIXME: We should make this a better test
601    */
602   return _xdg_utf8_validate (mime_type);
603 }
604
605 #endif
606
607 void
608 xdg_mime_shutdown (void)
609 {
610   XdgCallbackList *list;
611
612   /* FIXME: Need to make this (and the whole library) thread safe */
613   if (dir_time_list)
614     {
615       xdg_dir_time_list_free (dir_time_list);
616       dir_time_list = NULL;
617     }
618         
619   if (global_hash)
620     {
621       _xdg_glob_hash_free (global_hash);
622       global_hash = NULL;
623     }
624   if (global_magic)
625     {
626       _xdg_mime_magic_free (global_magic);
627       global_magic = NULL;
628     }
629
630   if (alias_list)
631     {
632       _xdg_mime_alias_list_free (alias_list);
633       alias_list = NULL;
634     }
635
636   if (parent_list)
637     {
638       _xdg_mime_parent_list_free (parent_list);
639       parent_list = NULL;
640     }
641
642   if (icon_list)
643     {
644       _xdg_mime_icon_list_free (icon_list);
645       icon_list = NULL;
646     }
647
648   if (generic_icon_list)
649     {
650       _xdg_mime_icon_list_free (generic_icon_list);
651       generic_icon_list = NULL;
652     }
653   
654   if (_caches)
655     {
656       int i;
657
658       for (i = 0; i < n_caches; i++)
659         _xdg_mime_cache_unref (_caches[i]);
660       free (_caches);
661       _caches = NULL;
662       n_caches = 0;
663     }
664
665   for (list = callback_list; list; list = list->next)
666     (list->callback) (list->data);
667
668   need_reread = TRUE;
669 }
670
671 int
672 xdg_mime_get_max_buffer_extents (void)
673 {
674   xdg_mime_init ();
675   
676   if (_caches)
677     return _xdg_mime_cache_get_max_buffer_extents ();
678
679   return _xdg_mime_magic_get_buffer_extents (global_magic);
680 }
681
682 const char *
683 _xdg_mime_unalias_mime_type (const char *mime_type)
684 {
685   const char *lookup;
686
687   if (_caches)
688     return _xdg_mime_cache_unalias_mime_type (mime_type);
689
690   if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
691     return lookup;
692
693   return mime_type;
694 }
695
696 const char *
697 xdg_mime_unalias_mime_type (const char *mime_type)
698 {
699   xdg_mime_init ();
700
701   return _xdg_mime_unalias_mime_type (mime_type);
702 }
703
704 int
705 _xdg_mime_mime_type_equal (const char *mime_a,
706                            const char *mime_b)
707 {
708   const char *unalias_a, *unalias_b;
709
710   unalias_a = _xdg_mime_unalias_mime_type (mime_a);
711   unalias_b = _xdg_mime_unalias_mime_type (mime_b);
712
713   if (strcmp (unalias_a, unalias_b) == 0)
714     return 1;
715
716   return 0;
717 }
718
719 int
720 xdg_mime_mime_type_equal (const char *mime_a,
721                           const char *mime_b)
722 {
723   xdg_mime_init ();
724
725   return _xdg_mime_mime_type_equal (mime_a, mime_b);
726 }
727
728 int
729 xdg_mime_media_type_equal (const char *mime_a,
730                            const char *mime_b)
731 {
732   char *sep;
733
734   sep = strchr (mime_a, '/');
735   
736   if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
737     return 1;
738
739   return 0;
740 }
741
742 #if 1
743 static int
744 xdg_mime_is_super_type (const char *mime)
745 {
746   int length;
747   const char *type;
748
749   length = strlen (mime);
750   type = &(mime[length - 2]);
751
752   if (strcmp (type, "/*") == 0)
753     return 1;
754
755   return 0;
756 }
757 #endif
758
759 int
760 _xdg_mime_mime_type_subclass (const char *mime,
761                               const char *base)
762 {
763   const char *umime, *ubase;
764   const char **parents;
765
766   if (_caches)
767     return _xdg_mime_cache_mime_type_subclass (mime, base);
768
769   umime = _xdg_mime_unalias_mime_type (mime);
770   ubase = _xdg_mime_unalias_mime_type (base);
771
772   if (strcmp (umime, ubase) == 0)
773     return 1;
774
775 #if 1  
776   /* Handle supertypes */
777   if (xdg_mime_is_super_type (ubase) &&
778       xdg_mime_media_type_equal (umime, ubase))
779     return 1;
780 #endif
781
782   /*  Handle special cases text/plain and application/octet-stream */
783   if (strcmp (ubase, "text/plain") == 0 && 
784       strncmp (umime, "text/", 5) == 0)
785     return 1;
786
787   if (strcmp (ubase, "application/octet-stream") == 0)
788     return 1;
789   
790   parents = _xdg_mime_parent_list_lookup (parent_list, umime);
791   for (; parents && *parents; parents++)
792     {
793       if (_xdg_mime_mime_type_subclass (*parents, ubase))
794         return 1;
795     }
796
797   return 0;
798 }
799
800 int
801 xdg_mime_mime_type_subclass (const char *mime,
802                              const char *base)
803 {
804   xdg_mime_init ();
805
806   return _xdg_mime_mime_type_subclass (mime, base);
807 }
808
809 char **
810 xdg_mime_list_mime_parents (const char *mime)
811 {
812   const char *umime;
813   const char **parents;
814   char **result;
815   int i, n;
816
817   xdg_mime_init ();
818
819   if (_caches)
820     return _xdg_mime_cache_list_mime_parents (mime);
821
822   umime = _xdg_mime_unalias_mime_type (mime);
823
824   parents = _xdg_mime_parent_list_lookup (parent_list, umime);
825
826   if (!parents)
827     return NULL;
828
829   for (i = 0; parents[i]; i++) ;
830   
831   n = (i + 1) * sizeof (char *);
832   result = (char **) malloc (n);
833   memcpy (result, parents, n);
834
835   return result;
836 }
837
838 #ifdef NOT_USED_IN_GIO
839
840 const char **
841 xdg_mime_get_mime_parents (const char *mime)
842 {
843   const char *umime;
844
845   xdg_mime_init ();
846
847   umime = _xdg_mime_unalias_mime_type (mime);
848
849   return _xdg_mime_parent_list_lookup (parent_list, umime);
850 }
851
852 void 
853 xdg_mime_dump (void)
854 {
855   xdg_mime_init();
856
857   printf ("*** ALIASES ***\n\n");
858   _xdg_mime_alias_list_dump (alias_list);
859   printf ("\n*** PARENTS ***\n\n");
860   _xdg_mime_parent_list_dump (parent_list);
861   printf ("\n*** CACHE ***\n\n");
862   _xdg_glob_hash_dump (global_hash);
863   printf ("\n*** GLOBS ***\n\n");
864   _xdg_glob_hash_dump (global_hash);
865   printf ("\n*** GLOBS REVERSE TREE ***\n\n");
866   _xdg_mime_cache_glob_dump ();
867 }
868
869 #endif
870
871 /* Registers a function to be called every time the mime database reloads its files
872  */
873 int
874 xdg_mime_register_reload_callback (XdgMimeCallback  callback,
875                                    void            *data,
876                                    XdgMimeDestroy   destroy)
877 {
878   XdgCallbackList *list_el;
879   static int callback_id = 1;
880
881   /* Make a new list element */
882   list_el = calloc (1, sizeof (XdgCallbackList));
883   list_el->callback_id = callback_id;
884   list_el->callback = callback;
885   list_el->data = data;
886   list_el->destroy = destroy;
887   list_el->next = callback_list;
888   if (list_el->next)
889     list_el->next->prev = list_el;
890
891   callback_list = list_el;
892   callback_id ++;
893
894   return callback_id - 1;
895 }
896
897 #ifdef NOT_USED_IN_GIO
898
899 void
900 xdg_mime_remove_callback (int callback_id)
901 {
902   XdgCallbackList *list;
903
904   for (list = callback_list; list; list = list->next)
905     {
906       if (list->callback_id == callback_id)
907         {
908           if (list->next)
909             list->next = list->prev;
910
911           if (list->prev)
912             list->prev->next = list->next;
913           else
914             callback_list = list->next;
915
916           /* invoke the destroy handler */
917           (list->destroy) (list->data);
918           free (list);
919           return;
920         }
921     }
922 }
923
924 #endif
925
926 const char *
927 xdg_mime_get_icon (const char *mime)
928 {
929   xdg_mime_init ();
930   
931   if (_caches)
932     return _xdg_mime_cache_get_icon (mime);
933
934   return _xdg_mime_icon_list_lookup (icon_list, mime);
935 }
936
937 const char *
938 xdg_mime_get_generic_icon (const char *mime)
939 {
940   xdg_mime_init ();
941   
942   if (_caches)
943     return _xdg_mime_cache_get_generic_icon (mime);
944
945   return _xdg_mime_icon_list_lookup (generic_icon_list, mime);
946 }