Back to index

libcitadel  8.12
xdgmime.c
Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
00003  *
00004  * More info can be found at http://www.freedesktop.org/standards/
00005  * 
00006  * Copyright (C) 2003,2004  Red Hat, Inc.
00007  * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
00008  *
00009  * Licensed under the Academic Free License version 2.0
00010  * Or under the following terms:
00011  * 
00012  * This library is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 2 of the License, or (at your option) any later version.
00016  *
00017  * This library is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the
00024  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00025  * Boston, MA 02111-1307, USA.
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include "xdgmime.h"
00033 #include "xdgmimeint.h"
00034 #include "xdgmimeglob.h"
00035 #include "xdgmimemagic.h"
00036 #include "xdgmimealias.h"
00037 #include "xdgmimeparent.h"
00038 #include "xdgmimecache.h"
00039 #include <stdio.h>
00040 #include <string.h>
00041 #include <sys/stat.h>
00042 #include <sys/types.h>
00043 #include <sys/time.h>
00044 #include <unistd.h>
00045 #include <assert.h>
00046 
00047 typedef struct XdgDirTimeList XdgDirTimeList;
00048 typedef struct XdgCallbackList XdgCallbackList;
00049 
00050 static int need_reread = TRUE;
00051 static time_t last_stat_time = 0;
00052 
00053 static XdgGlobHash *global_hash = NULL;
00054 static XdgMimeMagic *global_magic = NULL;
00055 static XdgAliasList *alias_list = NULL;
00056 static XdgParentList *parent_list = NULL;
00057 static XdgDirTimeList *dir_time_list = NULL;
00058 static XdgCallbackList *callback_list = NULL;
00059 
00060 XdgMimeCache **_xdg_mime_caches = NULL;
00061 static int n_caches = 0;
00062 
00063 const char xdg_mime_type_unknown[] = "application/octet-stream";
00064 
00065 
00066 enum
00067 {
00068   XDG_CHECKED_UNCHECKED,
00069   XDG_CHECKED_VALID,
00070   XDG_CHECKED_INVALID
00071 };
00072 
00073 struct XdgDirTimeList
00074 {
00075   time_t mtime;
00076   char *directory_name;
00077   int checked;
00078   XdgDirTimeList *next;
00079   XdgMimeCache *cache;
00080 };
00081 
00082 struct XdgCallbackList
00083 {
00084   XdgCallbackList *next;
00085   XdgCallbackList *prev;
00086   int              callback_id;
00087   XdgMimeCallback  callback;
00088   void            *data;
00089   XdgMimeDestroy   destroy;
00090 };
00091 
00092 /* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
00093  * directories aren't looked at */
00094 typedef int (*XdgDirectoryFunc) (const char *directory,
00095                              void       *user_data);
00096 
00097 static XdgDirTimeList *
00098 xdg_dir_time_list_new (void)
00099 {
00100   XdgDirTimeList *retval;
00101 
00102   retval = calloc (1, sizeof (XdgDirTimeList));
00103   retval->checked = XDG_CHECKED_UNCHECKED;
00104 
00105   return retval;
00106 }
00107 
00108 static void
00109 xdg_dir_time_list_free (XdgDirTimeList *list)
00110 {
00111   XdgDirTimeList *next;
00112 
00113   while (list)
00114     {
00115       next = list->next;
00116       free (list->directory_name);
00117       free (list);
00118       list = next;
00119     }
00120 }
00121 
00122 static int
00123 xdg_mime_init_from_directory (const char *directory)
00124 {
00125   char *file_name;
00126   struct stat st;
00127   XdgDirTimeList *list;
00128 
00129   assert (directory != NULL);
00130 
00131   file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
00132   strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
00133   if (stat (file_name, &st) == 0)
00134     {
00135       XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
00136 
00137       if (cache != NULL)
00138        {
00139          list = xdg_dir_time_list_new ();
00140          list->directory_name = file_name;
00141          list->mtime = st.st_mtime;
00142          list->next = dir_time_list;
00143          list->cache = cache;
00144          dir_time_list = list;
00145 
00146          _xdg_mime_caches = realloc (_xdg_mime_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
00147          _xdg_mime_caches[n_caches] = cache;
00148           _xdg_mime_caches[n_caches + 1] = NULL;
00149          n_caches++;
00150 
00151          return FALSE;
00152        }
00153     }
00154   free (file_name);
00155 
00156   file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
00157   strcpy (file_name, directory); strcat (file_name, "/mime/globs");
00158   if (stat (file_name, &st) == 0)
00159     {
00160       _xdg_mime_glob_read_from_file (global_hash, file_name);
00161 
00162       list = xdg_dir_time_list_new ();
00163       list->directory_name = file_name;
00164       list->mtime = st.st_mtime;
00165       list->next = dir_time_list;
00166       dir_time_list = list;
00167     }
00168   else
00169     {
00170       free (file_name);
00171     }
00172 
00173   file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
00174   strcpy (file_name, directory); strcat (file_name, "/mime/magic");
00175   if (stat (file_name, &st) == 0)
00176     {
00177       _xdg_mime_magic_read_from_file (global_magic, file_name);
00178 
00179       list = xdg_dir_time_list_new ();
00180       list->directory_name = file_name;
00181       list->mtime = st.st_mtime;
00182       list->next = dir_time_list;
00183       dir_time_list = list;
00184     }
00185   else
00186     {
00187       free (file_name);
00188     }
00189 
00190   file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
00191   strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
00192   _xdg_mime_alias_read_from_file (alias_list, file_name);
00193   free (file_name);
00194 
00195   file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
00196   strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
00197   _xdg_mime_parent_read_from_file (parent_list, file_name);
00198   free (file_name);
00199 
00200   return FALSE; /* Keep processing */
00201 }
00202 
00203 /* Runs a command on all the directories in the search path */
00204 static void
00205 xdg_run_command_on_dirs (XdgDirectoryFunc  func,
00206                       void             *user_data)
00207 {
00208   const char *xdg_data_home;
00209   const char *xdg_data_dirs;
00210   const char *ptr;
00211 
00212   xdg_data_home = getenv ("XDG_DATA_HOME");
00213   if (xdg_data_home)
00214     {
00215       if ((func) (xdg_data_home, user_data))
00216        return;
00217     }
00218   else
00219     {
00220       const char *home;
00221 
00222       home = getenv ("HOME");
00223       if (home != NULL)
00224        {
00225          char *guessed_xdg_home;
00226          int stop_processing;
00227 
00228          guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
00229          strcpy (guessed_xdg_home, home);
00230          strcat (guessed_xdg_home, "/.local/share/");
00231          stop_processing = (func) (guessed_xdg_home, user_data);
00232          free (guessed_xdg_home);
00233 
00234          if (stop_processing)
00235            return;
00236        }
00237     }
00238 
00239   xdg_data_dirs = getenv ("XDG_DATA_DIRS");
00240   if (xdg_data_dirs == NULL)
00241     xdg_data_dirs = "/usr/local/share/:/usr/share/";
00242 
00243   ptr = xdg_data_dirs;
00244 
00245   while (*ptr != '\000')
00246     {
00247       const char *end_ptr;
00248       char *dir;
00249       int len;
00250       int stop_processing;
00251 
00252       end_ptr = ptr;
00253       while (*end_ptr != ':' && *end_ptr != '\000')
00254        end_ptr ++;
00255 
00256       if (end_ptr == ptr)
00257        {
00258          ptr++;
00259          continue;
00260        }
00261 
00262       if (*end_ptr == ':')
00263        len = end_ptr - ptr;
00264       else
00265        len = end_ptr - ptr + 1;
00266       dir = malloc (len + 1);
00267       strncpy (dir, ptr, len);
00268       dir[len] = '\0';
00269       stop_processing = (func) (dir, user_data);
00270       free (dir);
00271 
00272       if (stop_processing)
00273        return;
00274 
00275       ptr = end_ptr;
00276     }
00277 }
00278 
00279 static XdgMimeCache *
00280 xdg_lookup_cache_for_file (const char *file_path)
00281 {
00282   XdgDirTimeList *list;
00283 
00284   for (list = dir_time_list; list; list = list->next)
00285       if (! strcmp (list->directory_name, file_path))
00286         return list->cache;
00287 
00288   return NULL;
00289 }
00290 
00291 /* Checks file_path to make sure it has the same mtime as last time it was
00292  * checked.  If it has a different mtime, or if the file doesn't exist, it
00293  * returns FALSE.
00294  *
00295  * FIXME: This doesn't protect against permission changes.
00296  */
00297 static int
00298 xdg_check_file (const char *file_path)
00299 {
00300   struct stat st;
00301 
00302   /* If the file exists */
00303   if (stat (file_path, &st) == 0)
00304     {
00305       XdgDirTimeList *list;
00306 
00307       for (list = dir_time_list; list; list = list->next)
00308        {
00309          if (! strcmp (list->directory_name, file_path) &&
00310              st.st_mtime == list->mtime)
00311            {
00312              if (list->checked == XDG_CHECKED_UNCHECKED)
00313               list->checked = XDG_CHECKED_VALID;
00314              else if (list->checked == XDG_CHECKED_VALID)
00315               list->checked = XDG_CHECKED_INVALID;
00316 
00317              return (list->checked != XDG_CHECKED_VALID);
00318            }
00319        }
00320       return TRUE;
00321     }
00322 
00323   return FALSE;
00324 }
00325 
00326 static int
00327 xdg_check_dir (const char *directory,
00328               int        *invalid_dir_list)
00329 {
00330   int invalid, has_cache;
00331   char *file_name;
00332 
00333   assert (directory != NULL);
00334 
00335   /* Check the mime.cache file */
00336   file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
00337   strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
00338   invalid = xdg_check_file (file_name);
00339   has_cache = xdg_lookup_cache_for_file (file_name) != NULL;
00340   free (file_name);
00341 
00342   if (has_cache)
00343     {
00344       if (invalid)
00345        {
00346          *invalid_dir_list = TRUE;
00347          return TRUE;
00348        }
00349 
00350       return FALSE;
00351     }
00352 
00353   /* Check the globs file */
00354   file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
00355   strcpy (file_name, directory); strcat (file_name, "/mime/globs");
00356   invalid = xdg_check_file (file_name);
00357   free (file_name);
00358   if (invalid)
00359     {
00360       *invalid_dir_list = TRUE;
00361       return TRUE;
00362     }
00363 
00364   /* Check the magic file */
00365   file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
00366   strcpy (file_name, directory); strcat (file_name, "/mime/magic");
00367   invalid = xdg_check_file (file_name);
00368   free (file_name);
00369   if (invalid)
00370     {
00371       *invalid_dir_list = TRUE;
00372       return TRUE;
00373     }
00374 
00375   return FALSE; /* Keep processing */
00376 }
00377 
00378 /* Walks through all the mime files stat()ing them to see if they've changed.
00379  * Returns TRUE if they have. */
00380 static int
00381 xdg_check_dirs (void)
00382 {
00383   XdgDirTimeList *list;
00384   int invalid_dir_list = FALSE;
00385 
00386   for (list = dir_time_list; list; list = list->next)
00387     list->checked = XDG_CHECKED_UNCHECKED;
00388 
00389   xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
00390                         &invalid_dir_list);
00391 
00392   if (invalid_dir_list)
00393     return TRUE;
00394 
00395   for (list = dir_time_list; list; list = list->next)
00396     {
00397       if (list->checked != XDG_CHECKED_VALID)
00398        return TRUE;
00399     }
00400 
00401   return FALSE;
00402 }
00403 
00404 /* We want to avoid stat()ing on every single mime call, so we only look for
00405  * newer files every 5 seconds.  This will return TRUE if we need to reread the
00406  * mime data from disk.
00407  */
00408 static int
00409 xdg_check_time_and_dirs (void)
00410 {
00411   struct timeval tv;
00412   time_t current_time;
00413   int retval = FALSE;
00414 
00415   gettimeofday (&tv, NULL);
00416   current_time = tv.tv_sec;
00417 
00418   if (current_time >= last_stat_time + 5)
00419     {
00420       retval = xdg_check_dirs ();
00421       last_stat_time = current_time;
00422     }
00423 
00424   return retval;
00425 }
00426 
00427 /* Called in every public function.  It reloads the hash function if need be.
00428  */
00429 static void
00430 xdg_mime_init (void)
00431 {
00432   if (xdg_check_time_and_dirs ())
00433     {
00434       xdg_mime_shutdown ();
00435     }
00436 
00437   if (need_reread)
00438     {
00439       global_hash = _xdg_glob_hash_new ();
00440       global_magic = _xdg_mime_magic_new ();
00441       alias_list = _xdg_mime_alias_list_new ();
00442       parent_list = _xdg_mime_parent_list_new ();
00443 
00444       xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
00445                             NULL);
00446 
00447       need_reread = FALSE;
00448     }
00449 }
00450 
00451 const char *
00452 xdg_mime_get_mime_type_for_data (const void *data,
00453                              size_t      len)
00454 {
00455   const char *mime_type;
00456 
00457   xdg_mime_init ();
00458 
00459   if (_xdg_mime_caches)
00460     return _xdg_mime_cache_get_mime_type_for_data (data, len);
00461 
00462   mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, NULL, 0);
00463 
00464   if (mime_type)
00465     return mime_type;
00466 
00467   return XDG_MIME_TYPE_UNKNOWN;
00468 }
00469 
00470 const char *
00471 xdg_mime_get_mime_type_for_file (const char  *file_name,
00472                                  struct stat *statbuf)
00473 {
00474   const char *mime_type;
00475   /* Used to detect whether multiple MIME types match file_name */
00476   const char *mime_types[2];
00477   FILE *file;
00478   unsigned char *data;
00479   int max_extent;
00480   int bytes_read;
00481   struct stat buf;
00482   const char *base_name;
00483   int n;
00484 
00485   if (file_name == NULL)
00486     return NULL;
00487   if (! _xdg_utf8_validate (file_name))
00488     return NULL;
00489 
00490   xdg_mime_init ();
00491 
00492   if (_xdg_mime_caches)
00493     return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
00494 
00495   base_name = _xdg_get_base_name (file_name);
00496   n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 2);
00497 
00498   if (n == 1)
00499     return mime_types[0];
00500 
00501   if (!statbuf)
00502     {
00503       if (stat (file_name, &buf) != 0)
00504        return XDG_MIME_TYPE_UNKNOWN;
00505 
00506       statbuf = &buf;
00507     }
00508 
00509   if (!S_ISREG (statbuf->st_mode))
00510     return XDG_MIME_TYPE_UNKNOWN;
00511 
00512   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
00513    * be large and need getting from a stream instead of just reading it all
00514    * in. */
00515   max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
00516   data = malloc (max_extent);
00517   if (data == NULL)
00518     return XDG_MIME_TYPE_UNKNOWN;
00519         
00520   file = fopen (file_name, "r");
00521   if (file == NULL)
00522     {
00523       free (data);
00524       return XDG_MIME_TYPE_UNKNOWN;
00525     }
00526 
00527   bytes_read = fread (data, 1, max_extent, file);
00528   if (ferror (file))
00529     {
00530       free (data);
00531       fclose (file);
00532       return XDG_MIME_TYPE_UNKNOWN;
00533     }
00534 
00535   mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read,
00536                                       mime_types, n);
00537 
00538   free (data);
00539   fclose (file);
00540 
00541   if (mime_type)
00542     return mime_type;
00543 
00544   return XDG_MIME_TYPE_UNKNOWN;
00545 }
00546 
00547 const char *
00548 xdg_mime_get_mime_type_from_file_name (const char *file_name)
00549 {
00550   const char *mime_types[2];
00551 
00552   xdg_mime_init ();
00553 
00554   if (_xdg_mime_caches)
00555     return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
00556 
00557   if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, 2) == 1)
00558     return mime_types[0];
00559   else
00560     return XDG_MIME_TYPE_UNKNOWN;
00561 }
00562 
00563 int
00564 xdg_mime_is_valid_mime_type (const char *mime_type)
00565 {
00566   /* FIXME: We should make this a better test
00567    */
00568   return _xdg_utf8_validate (mime_type);
00569 }
00570 
00571 void
00572 xdg_mime_shutdown (void)
00573 {
00574   XdgCallbackList *list;
00575 
00576   /* FIXME: Need to make this (and the whole library) thread safe */
00577   if (dir_time_list)
00578     {
00579       xdg_dir_time_list_free (dir_time_list);
00580       dir_time_list = NULL;
00581     }
00582        
00583   if (global_hash)
00584     {
00585       _xdg_glob_hash_free (global_hash);
00586       global_hash = NULL;
00587     }
00588   if (global_magic)
00589     {
00590       _xdg_mime_magic_free (global_magic);
00591       global_magic = NULL;
00592     }
00593 
00594   if (alias_list)
00595     {
00596       _xdg_mime_alias_list_free (alias_list);
00597       alias_list = NULL;
00598     }
00599 
00600   if (parent_list)
00601     {
00602       _xdg_mime_parent_list_free (parent_list);
00603       parent_list = NULL;
00604     }
00605 
00606   if (_xdg_mime_caches)
00607     {
00608       int i;
00609       for (i = 0; i < n_caches; i++)
00610         _xdg_mime_cache_unref (_xdg_mime_caches[i]);
00611       free (_xdg_mime_caches);
00612       _xdg_mime_caches = NULL;
00613       n_caches = 0;
00614     }
00615 
00616   for (list = callback_list; list; list = list->next)
00617     (list->callback) (list->data);
00618 
00619   need_reread = TRUE;
00620 }
00621 
00622 int
00623 xdg_mime_get_max_buffer_extents (void)
00624 {
00625   xdg_mime_init ();
00626   
00627   if (_xdg_mime_caches)
00628     return _xdg_mime_cache_get_max_buffer_extents ();
00629 
00630   return _xdg_mime_magic_get_buffer_extents (global_magic);
00631 }
00632 
00633 static const char *
00634 _xdg_mime_unalias_mime_type (const char *mime_type)
00635 {
00636   const char *lookup;
00637 
00638   if (_xdg_mime_caches)
00639     return _xdg_mime_cache_unalias_mime_type (mime_type);
00640 
00641   if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
00642     return lookup;
00643 
00644   return mime_type;
00645 }
00646 
00647 const char *
00648 xdg_mime_unalias_mime_type (const char *mime_type)
00649 {
00650   xdg_mime_init ();
00651 
00652   return _xdg_mime_unalias_mime_type (mime_type);
00653 }
00654 
00655 int
00656 _xdg_mime_mime_type_equal (const char *mime_a,
00657                         const char *mime_b)
00658 {
00659   const char *unalias_a, *unalias_b;
00660 
00661   unalias_a = _xdg_mime_unalias_mime_type (mime_a);
00662   unalias_b = _xdg_mime_unalias_mime_type (mime_b);
00663 
00664   if (strcmp (unalias_a, unalias_b) == 0)
00665     return 1;
00666 
00667   return 0;
00668 }
00669 
00670 int
00671 xdg_mime_mime_type_equal (const char *mime_a,
00672                        const char *mime_b)
00673 {
00674   xdg_mime_init ();
00675 
00676   return _xdg_mime_mime_type_equal (mime_a, mime_b);
00677 }
00678 
00679 int
00680 _xdg_mime_media_type_equal (const char *mime_a,
00681                          const char *mime_b)
00682 {
00683   char *sep;
00684 
00685   xdg_mime_init ();
00686 
00687   sep = strchr (mime_a, '/');
00688   
00689   if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
00690     return 1;
00691 
00692   return 0;
00693 }
00694 
00695 int
00696 xdg_mime_media_type_equal (const char *mime_a,
00697                         const char *mime_b)
00698 {
00699   xdg_mime_init ();
00700 
00701   return _xdg_mime_media_type_equal (mime_a, mime_b);
00702 }
00703 
00704 #if 0
00705 static int
00706 xdg_mime_is_super_type (const char *mime)
00707 {
00708   int length;
00709   const char *type;
00710 
00711   length = strlen (mime);
00712   type = &(mime[length - 2]);
00713 
00714   if (strcmp (type, "/*") == 0)
00715     return 1;
00716 
00717   return 0;
00718 }
00719 #endif
00720 
00721 int
00722 _xdg_mime_mime_type_subclass (const char *mime,
00723                            const char *base)
00724 {
00725   const char *umime, *ubase;
00726   const char **parents;
00727 
00728   if (_xdg_mime_caches)
00729     return _xdg_mime_cache_mime_type_subclass (mime, base);
00730 
00731   umime = _xdg_mime_unalias_mime_type (mime);
00732   ubase = _xdg_mime_unalias_mime_type (base);
00733 
00734   if (strcmp (umime, ubase) == 0)
00735     return 1;
00736 
00737 #if 0  
00738   /* Handle supertypes */
00739   if (xdg_mime_is_super_type (ubase) &&
00740       _xdg_mime_media_type_equal (umime, ubase))
00741     return 1;
00742 #endif
00743 
00744   /*  Handle special cases text/plain and application/octet-stream */
00745   if (strcmp (ubase, "text/plain") == 0 && 
00746       strncmp (umime, "text/", 5) == 0)
00747     return 1;
00748 
00749   if (strcmp (ubase, "application/octet-stream") == 0)
00750     return 1;
00751   
00752   parents = _xdg_mime_parent_list_lookup (parent_list, umime);
00753   for (; parents && *parents; parents++)
00754     {
00755       if (_xdg_mime_mime_type_subclass (*parents, ubase))
00756        return 1;
00757     }
00758 
00759   return 0;
00760 }
00761 
00762 int
00763 xdg_mime_mime_type_subclass (const char *mime,
00764                           const char *base)
00765 {
00766   xdg_mime_init ();
00767 
00768   return _xdg_mime_mime_type_subclass (mime, base);
00769 }
00770 
00771 char **
00772 xdg_mime_list_mime_parents (const char *mime)
00773 {
00774   const char **parents;
00775   char **result;
00776   int i, n;
00777 
00778   if (_xdg_mime_caches)
00779     return _xdg_mime_cache_list_mime_parents (mime);
00780 
00781   parents = xdg_mime_get_mime_parents (mime);
00782 
00783   if (!parents)
00784     return NULL;
00785 
00786   for (i = 0; parents[i]; i++) ;
00787   
00788   n = (i + 1) * sizeof (char *);
00789   result = (char **) malloc (n);
00790   memcpy (result, parents, n);
00791 
00792   return result;
00793 }
00794 
00795 const char **
00796 xdg_mime_get_mime_parents (const char *mime)
00797 {
00798   const char *umime;
00799 
00800   xdg_mime_init ();
00801 
00802   umime = _xdg_mime_unalias_mime_type (mime);
00803 
00804   return _xdg_mime_parent_list_lookup (parent_list, umime);
00805 }
00806 
00807 void 
00808 xdg_mime_dump (void)
00809 {
00810   printf ("*** ALIASES ***\n\n");
00811   _xdg_mime_alias_list_dump (alias_list);
00812   printf ("\n*** PARENTS ***\n\n");
00813   _xdg_mime_parent_list_dump (parent_list);
00814   printf ("\n*** CACHE ***\n\n");
00815   _xdg_glob_hash_dump (global_hash);
00816 }
00817 
00818 
00819 /* Registers a function to be called every time the mime database reloads its files
00820  */
00821 int
00822 xdg_mime_register_reload_callback (XdgMimeCallback  callback,
00823                                void            *data,
00824                                XdgMimeDestroy   destroy)
00825 {
00826   XdgCallbackList *list_el;
00827   static int callback_id = 1;
00828 
00829   /* Make a new list element */
00830   list_el = calloc (1, sizeof (XdgCallbackList));
00831   list_el->callback_id = callback_id;
00832   list_el->callback = callback;
00833   list_el->data = data;
00834   list_el->destroy = destroy;
00835   list_el->next = callback_list;
00836   if (list_el->next)
00837     list_el->next->prev = list_el;
00838 
00839   callback_list = list_el;
00840   callback_id ++;
00841 
00842   return callback_id - 1;
00843 }
00844 
00845 void
00846 xdg_mime_remove_callback (int callback_id)
00847 {
00848   XdgCallbackList *list;
00849 
00850   for (list = callback_list; list; list = list->next)
00851     {
00852       if (list->callback_id == callback_id)
00853        {
00854          if (list->next)
00855            list->next = list->prev;
00856 
00857          if (list->prev)
00858            list->prev->next = list->next;
00859          else
00860            callback_list = list->next;
00861 
00862          /* invoke the destroy handler */
00863          (list->destroy) (list->data);
00864          free (list);
00865          return;
00866        }
00867     }
00868 }