• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
xdg_dir_time_list_add(char * file_name,time_t mtime)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
xdg_dir_time_list_free(XdgDirTimeList * list)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
xdg_mime_init_from_directory(const char * directory)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);
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);
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
xdg_run_command_on_dirs(XdgDirectoryFunc func,void * user_data)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
xdg_check_file(const char * file_path,int * exists)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
xdg_check_dir(const char * directory,int * invalid_dir_list)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
xdg_check_dirs(void)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
xdg_check_time_and_dirs(void)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
xdg_mime_init(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 *
xdg_mime_get_mime_type_for_data(const void * data,size_t len,int * result_prio)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 const char *
xdg_mime_get_mime_type_for_file(const char * file_name,struct stat * statbuf)484 xdg_mime_get_mime_type_for_file (const char  *file_name,
485                                  struct stat *statbuf)
486 {
487   const char *mime_type;
488   /* currently, only a few globs occur twice, and none
489    * more often, so 5 seems plenty.
490    */
491   const char *mime_types[5];
492   FILE *file;
493   unsigned char *data;
494   int max_extent;
495   int bytes_read;
496   struct stat buf;
497   const char *base_name;
498   int n;
499 
500   if (file_name == NULL)
501     return NULL;
502   if (! _xdg_utf8_validate (file_name))
503     return NULL;
504 
505   xdg_mime_init ();
506 
507   if (_caches)
508     return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
509 
510   base_name = _xdg_get_base_name (file_name);
511   n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
512 
513   if (n == 1)
514     return mime_types[0];
515 
516   if (!statbuf)
517     {
518       if (stat (file_name, &buf) != 0)
519 	return XDG_MIME_TYPE_UNKNOWN;
520 
521       statbuf = &buf;
522     }
523 
524   if (!S_ISREG (statbuf->st_mode))
525     return XDG_MIME_TYPE_UNKNOWN;
526 
527   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
528    * be large and need getting from a stream instead of just reading it all
529    * in. */
530   max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
531   data = malloc (max_extent);
532   if (data == NULL)
533     return XDG_MIME_TYPE_UNKNOWN;
534 
535   file = fopen (file_name, "r");
536   if (file == NULL)
537     {
538       free (data);
539       return XDG_MIME_TYPE_UNKNOWN;
540     }
541 
542   bytes_read = fread (data, 1, max_extent, file);
543   if (ferror (file))
544     {
545       free (data);
546       fclose (file);
547       return XDG_MIME_TYPE_UNKNOWN;
548     }
549 
550   mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
551 					   mime_types, n);
552 
553   free (data);
554   fclose (file);
555 
556   if (mime_type)
557     return mime_type;
558 
559   return XDG_MIME_TYPE_UNKNOWN;
560 }
561 
562 const char *
xdg_mime_get_mime_type_from_file_name(const char * file_name)563 xdg_mime_get_mime_type_from_file_name (const char *file_name)
564 {
565   const char *mime_type;
566 
567   xdg_mime_init ();
568 
569   if (_caches)
570     return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
571 
572   if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
573     return mime_type;
574   else
575     return XDG_MIME_TYPE_UNKNOWN;
576 }
577 
578 int
xdg_mime_get_mime_types_from_file_name(const char * file_name,const char * mime_types[],int n_mime_types)579 xdg_mime_get_mime_types_from_file_name (const char *file_name,
580 					const char  *mime_types[],
581 					int          n_mime_types)
582 {
583   xdg_mime_init ();
584 
585   if (_caches)
586     return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
587 
588   return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
589 }
590 
591 int
xdg_mime_is_valid_mime_type(const char * mime_type)592 xdg_mime_is_valid_mime_type (const char *mime_type)
593 {
594   /* FIXME: We should make this a better test
595    */
596   return _xdg_utf8_validate (mime_type);
597 }
598 
599 void
xdg_mime_shutdown(void)600 xdg_mime_shutdown (void)
601 {
602   XdgCallbackList *list;
603 
604   /* FIXME: Need to make this (and the whole library) thread safe */
605   if (dir_time_list)
606     {
607       xdg_dir_time_list_free (dir_time_list);
608       dir_time_list = NULL;
609     }
610 
611   if (global_hash)
612     {
613       _xdg_glob_hash_free (global_hash);
614       global_hash = NULL;
615     }
616   if (global_magic)
617     {
618       _xdg_mime_magic_free (global_magic);
619       global_magic = NULL;
620     }
621 
622   if (alias_list)
623     {
624       _xdg_mime_alias_list_free (alias_list);
625       alias_list = NULL;
626     }
627 
628   if (parent_list)
629     {
630       _xdg_mime_parent_list_free (parent_list);
631       parent_list = NULL;
632     }
633 
634   if (icon_list)
635     {
636       _xdg_mime_icon_list_free (icon_list);
637       icon_list = NULL;
638     }
639 
640   if (generic_icon_list)
641     {
642       _xdg_mime_icon_list_free (generic_icon_list);
643       generic_icon_list = NULL;
644     }
645 
646   if (_caches)
647     {
648       int i;
649 
650       for (i = 0; i < n_caches; i++)
651         _xdg_mime_cache_unref (_caches[i]);
652       free (_caches);
653       _caches = NULL;
654       n_caches = 0;
655     }
656 
657   for (list = callback_list; list; list = list->next)
658     (list->callback) (list->data);
659 
660   need_reread = TRUE;
661 }
662 
663 int
xdg_mime_get_max_buffer_extents(void)664 xdg_mime_get_max_buffer_extents (void)
665 {
666   xdg_mime_init ();
667 
668   if (_caches)
669     return _xdg_mime_cache_get_max_buffer_extents ();
670 
671   return _xdg_mime_magic_get_buffer_extents (global_magic);
672 }
673 
674 const char *
_xdg_mime_unalias_mime_type(const char * mime_type)675 _xdg_mime_unalias_mime_type (const char *mime_type)
676 {
677   const char *lookup;
678 
679   if (_caches)
680     return _xdg_mime_cache_unalias_mime_type (mime_type);
681 
682   if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
683     return lookup;
684 
685   return mime_type;
686 }
687 
688 const char *
xdg_mime_unalias_mime_type(const char * mime_type)689 xdg_mime_unalias_mime_type (const char *mime_type)
690 {
691   xdg_mime_init ();
692 
693   return _xdg_mime_unalias_mime_type (mime_type);
694 }
695 
696 int
_xdg_mime_mime_type_equal(const char * mime_a,const char * mime_b)697 _xdg_mime_mime_type_equal (const char *mime_a,
698 			   const char *mime_b)
699 {
700   const char *unalias_a, *unalias_b;
701 
702   unalias_a = _xdg_mime_unalias_mime_type (mime_a);
703   unalias_b = _xdg_mime_unalias_mime_type (mime_b);
704 
705   if (strcmp (unalias_a, unalias_b) == 0)
706     return 1;
707 
708   return 0;
709 }
710 
711 int
xdg_mime_mime_type_equal(const char * mime_a,const char * mime_b)712 xdg_mime_mime_type_equal (const char *mime_a,
713 			  const char *mime_b)
714 {
715   xdg_mime_init ();
716 
717   return _xdg_mime_mime_type_equal (mime_a, mime_b);
718 }
719 
720 int
xdg_mime_media_type_equal(const char * mime_a,const char * mime_b)721 xdg_mime_media_type_equal (const char *mime_a,
722 			   const char *mime_b)
723 {
724   char *sep;
725 
726   sep = strchr (mime_a, '/');
727 
728   if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
729     return 1;
730 
731   return 0;
732 }
733 
734 #if 1
735 static int
xdg_mime_is_super_type(const char * mime)736 xdg_mime_is_super_type (const char *mime)
737 {
738   int length;
739   const char *type;
740 
741   length = strlen (mime);
742   type = &(mime[length - 2]);
743 
744   if (strcmp (type, "/*") == 0)
745     return 1;
746 
747   return 0;
748 }
749 #endif
750 
751 int
_xdg_mime_mime_type_subclass(const char * mime,const char * base)752 _xdg_mime_mime_type_subclass (const char *mime,
753 			      const char *base)
754 {
755   const char *umime, *ubase;
756   const char **parents;
757 
758   if (_caches)
759     return _xdg_mime_cache_mime_type_subclass (mime, base);
760 
761   umime = _xdg_mime_unalias_mime_type (mime);
762   ubase = _xdg_mime_unalias_mime_type (base);
763 
764   if (strcmp (umime, ubase) == 0)
765     return 1;
766 
767 #if 1
768   /* Handle supertypes */
769   if (xdg_mime_is_super_type (ubase) &&
770       xdg_mime_media_type_equal (umime, ubase))
771     return 1;
772 #endif
773 
774   /*  Handle special cases text/plain and application/octet-stream */
775   if (strcmp (ubase, "text/plain") == 0 &&
776       strncmp (umime, "text/", 5) == 0)
777     return 1;
778 
779   if (strcmp (ubase, "application/octet-stream") == 0)
780     return 1;
781 
782   parents = _xdg_mime_parent_list_lookup (parent_list, umime);
783   for (; parents && *parents; parents++)
784     {
785       if (_xdg_mime_mime_type_subclass (*parents, ubase))
786 	return 1;
787     }
788 
789   return 0;
790 }
791 
792 int
xdg_mime_mime_type_subclass(const char * mime,const char * base)793 xdg_mime_mime_type_subclass (const char *mime,
794 			     const char *base)
795 {
796   xdg_mime_init ();
797 
798   return _xdg_mime_mime_type_subclass (mime, base);
799 }
800 
801 char **
xdg_mime_list_mime_parents(const char * mime)802 xdg_mime_list_mime_parents (const char *mime)
803 {
804   const char **parents;
805   char **result;
806   int i, n;
807 
808   if (_caches)
809     return _xdg_mime_cache_list_mime_parents (mime);
810 
811   parents = xdg_mime_get_mime_parents (mime);
812 
813   if (!parents)
814     return NULL;
815 
816   for (i = 0; parents[i]; i++) ;
817 
818   n = (i + 1) * sizeof (char *);
819   result = (char **) malloc (n);
820   memcpy (result, parents, n);
821 
822   return result;
823 }
824 
825 const char **
xdg_mime_get_mime_parents(const char * mime)826 xdg_mime_get_mime_parents (const char *mime)
827 {
828   const char *umime;
829 
830   xdg_mime_init ();
831 
832   umime = _xdg_mime_unalias_mime_type (mime);
833 
834   return _xdg_mime_parent_list_lookup (parent_list, umime);
835 }
836 
837 void
xdg_mime_dump(void)838 xdg_mime_dump (void)
839 {
840   xdg_mime_init();
841 
842   printf ("*** ALIASES ***\n\n");
843   _xdg_mime_alias_list_dump (alias_list);
844   printf ("\n*** PARENTS ***\n\n");
845   _xdg_mime_parent_list_dump (parent_list);
846   printf ("\n*** CACHE ***\n\n");
847   _xdg_glob_hash_dump (global_hash);
848   printf ("\n*** GLOBS ***\n\n");
849   _xdg_glob_hash_dump (global_hash);
850   printf ("\n*** GLOBS REVERSE TREE ***\n\n");
851   _xdg_mime_cache_glob_dump ();
852 }
853 
854 
855 /* Registers a function to be called every time the mime database reloads its files
856  */
857 int
xdg_mime_register_reload_callback(XdgMimeCallback callback,void * data,XdgMimeDestroy destroy)858 xdg_mime_register_reload_callback (XdgMimeCallback  callback,
859 				   void            *data,
860 				   XdgMimeDestroy   destroy)
861 {
862   XdgCallbackList *list_el;
863   static int callback_id = 1;
864 
865   /* Make a new list element */
866   list_el = calloc (1, sizeof (XdgCallbackList));
867   list_el->callback_id = callback_id;
868   list_el->callback = callback;
869   list_el->data = data;
870   list_el->destroy = destroy;
871   list_el->next = callback_list;
872   if (list_el->next)
873     list_el->next->prev = list_el;
874 
875   callback_list = list_el;
876   callback_id ++;
877 
878   return callback_id - 1;
879 }
880 
881 void
xdg_mime_remove_callback(int callback_id)882 xdg_mime_remove_callback (int callback_id)
883 {
884   XdgCallbackList *list;
885 
886   for (list = callback_list; list; list = list->next)
887     {
888       if (list->callback_id == callback_id)
889 	{
890 	  if (list->next)
891 	    list->next = list->prev;
892 
893 	  if (list->prev)
894 	    list->prev->next = list->next;
895 	  else
896 	    callback_list = list->next;
897 
898 	  /* invoke the destroy handler */
899 	  (list->destroy) (list->data);
900 	  free (list);
901 	  return;
902 	}
903     }
904 }
905 
906 const char *
xdg_mime_get_icon(const char * mime)907 xdg_mime_get_icon (const char *mime)
908 {
909   xdg_mime_init ();
910 
911   if (_caches)
912     return _xdg_mime_cache_get_icon (mime);
913 
914   return _xdg_mime_icon_list_lookup (icon_list, mime);
915 }
916 
917 const char *
xdg_mime_get_generic_icon(const char * mime)918 xdg_mime_get_generic_icon (const char *mime)
919 {
920   xdg_mime_init ();
921 
922   if (_caches)
923     return _xdg_mime_cache_get_generic_icon (mime);
924 
925   return _xdg_mime_icon_list_lookup (generic_icon_list, mime);
926 }
927