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