Back to index

libcitadel  8.12
xdgmimecache.c
Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* xdgmimealias.c: Private file.  mmappable caches for mime data
00003  *
00004  * More info can be found at http://www.freedesktop.org/standards/
00005  *
00006  * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
00007  *
00008  * Licensed under the Academic Free License version 2.0
00009  * Or under the following terms:
00010  *
00011  * This library is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU Lesser General Public
00013  * License as published by the Free Software Foundation; either
00014  * version 2 of the License, or (at your option) any later version.
00015  *
00016  * This library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with this library; if not, write to the
00023  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00024  * Boston, MA 02111-1307, USA.
00025  */
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #include <config.h>
00029 #endif
00030 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 
00035 #include <fcntl.h>
00036 #include <unistd.h>
00037 #include <fnmatch.h>
00038 #include <assert.h>
00039 
00040 #include <netinet/in.h> /* for ntohl/ntohs */
00041 
00042 #ifdef HAVE_MMAP
00043 #include <sys/mman.h>
00044 #else
00045 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
00046 #endif
00047 
00048 #include <sys/stat.h>
00049 #include <sys/types.h>
00050 
00051 #include "xdgmimecache.h"
00052 #include "xdgmimeint.h"
00053 
00054 #ifndef MAX
00055 #define MAX(a,b) ((a) > (b) ? (a) : (b))
00056 #endif
00057 
00058 #ifndef       FALSE
00059 #define       FALSE  (0)
00060 #endif
00061 
00062 #ifndef       TRUE
00063 #define       TRUE   (!FALSE)
00064 #endif
00065 
00066 #ifndef _O_BINARY
00067 #define _O_BINARY 0
00068 #endif
00069 
00070 #ifndef MAP_FAILED
00071 #define MAP_FAILED ((void *) -1)
00072 #endif
00073 
00074 #define MAJOR_VERSION 1
00075 #define MINOR_VERSION 0
00076 
00077 struct _XdgMimeCache
00078 {
00079   int ref_count;
00080 
00081   size_t  size;
00082   char   *buffer;
00083 };
00084 
00085 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
00086 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
00087 
00088 XdgMimeCache *
00089 _xdg_mime_cache_ref (XdgMimeCache *cache)
00090 {
00091   cache->ref_count++;
00092   return cache;
00093 }
00094 
00095 void
00096 _xdg_mime_cache_unref (XdgMimeCache *cache)
00097 {
00098   cache->ref_count--;
00099 
00100   if (cache->ref_count == 0)
00101     {
00102 #ifdef HAVE_MMAP
00103       munmap (cache->buffer, cache->size);
00104 #endif
00105       free (cache);
00106     }
00107 }
00108 
00109 XdgMimeCache *
00110 _xdg_mime_cache_new_from_file (const char *file_name)
00111 {
00112   XdgMimeCache *cache = NULL;
00113 
00114 #ifdef HAVE_MMAP
00115   int fd = -1;
00116   struct stat st;
00117   char *buffer = NULL;
00118 
00119   /* Open the file and map it into memory */
00120   fd = open (file_name, O_RDONLY|_O_BINARY, 0);
00121 
00122   if (fd < 0)
00123     return NULL;
00124   
00125   if (fstat (fd, &st) < 0 || st.st_size < 4)
00126     goto done;
00127 
00128   buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
00129 
00130   if (buffer == MAP_FAILED)
00131     goto done;
00132 
00133   /* Verify version */
00134   if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
00135       GET_UINT16 (buffer, 2) != MINOR_VERSION)
00136     {
00137       munmap (buffer, st.st_size);
00138 
00139       goto done;
00140     }
00141   
00142   cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
00143   cache->ref_count = 1;
00144   cache->buffer = buffer;
00145   cache->size = st.st_size;
00146 
00147  done:
00148   if (fd != -1)
00149     close (fd);
00150 
00151 #endif  /* HAVE_MMAP */
00152 
00153   return cache;
00154 }
00155 
00156 static int
00157 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, 
00158                                   xdg_uint32_t  offset,
00159                                   const void   *data,
00160                                   size_t        len)
00161 {
00162   xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
00163   xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
00164   xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
00165   xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
00166   xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
00167   
00168   int i, j;
00169 
00170   for (i = range_start; i <= range_start + range_length; i++)
00171     {
00172       int valid_matchlet = TRUE;
00173       
00174       if (i + data_length > len)
00175        return FALSE;
00176 
00177       if (mask_offset)
00178        {
00179          for (j = 0; j < data_length; j++)
00180            {
00181              if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
00182                 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
00183               {
00184                 valid_matchlet = FALSE;
00185                 break;
00186               }
00187            }
00188        }
00189       else
00190        {
00191          for (j = 0; j < data_length; j++)
00192            {
00193              if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
00194               {
00195                 valid_matchlet = FALSE;
00196                 break;
00197               }
00198            }
00199        }
00200       
00201       if (valid_matchlet)
00202        return TRUE;
00203     }
00204   
00205   return FALSE;  
00206 }
00207 
00208 static int
00209 cache_magic_matchlet_compare (XdgMimeCache *cache, 
00210                            xdg_uint32_t  offset,
00211                            const void   *data,
00212                            size_t        len)
00213 {
00214   xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
00215   xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
00216 
00217   int i;
00218   
00219   if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
00220     {
00221       if (n_children == 0)
00222        return TRUE;
00223       
00224       for (i = 0; i < n_children; i++)
00225        {
00226          if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
00227                                        data, len))
00228            return TRUE;
00229        }
00230     }
00231   
00232   return FALSE;  
00233 }
00234 
00235 static const char *
00236 cache_magic_compare_to_data (XdgMimeCache *cache, 
00237                           xdg_uint32_t  offset,
00238                           const void   *data, 
00239                           size_t        len, 
00240                           int          *prio)
00241 {
00242   xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
00243   xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
00244   xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
00245   xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
00246 
00247   int i;
00248 
00249   for (i = 0; i < n_matchlets; i++)
00250     {
00251       if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 
00252                                    data, len))
00253        {
00254          *prio = priority;
00255          
00256          return cache->buffer + mimetype_offset;
00257        }
00258     }
00259 
00260   return NULL;
00261 }
00262 
00263 static const char *
00264 cache_magic_lookup_data (XdgMimeCache *cache, 
00265                       const void   *data, 
00266                       size_t        len, 
00267                       int          *prio,
00268                       const char   *mime_types[],
00269                       int           n_mime_types)
00270 {
00271   xdg_uint32_t list_offset;
00272   xdg_uint32_t n_entries;
00273   xdg_uint32_t offset;
00274 
00275   int j, n;
00276 
00277   *prio = 0;
00278 
00279   list_offset = GET_UINT32 (cache->buffer, 24);
00280   n_entries = GET_UINT32 (cache->buffer, list_offset);
00281   offset = GET_UINT32 (cache->buffer, list_offset + 8);
00282   
00283   for (j = 0; j < n_entries; j++)
00284     {
00285       const char *match;
00286 
00287       match = cache_magic_compare_to_data (cache, offset + 16 * j, 
00288                                       data, len, prio);
00289       if (match)
00290        return match;
00291       else
00292        {
00293          xdg_uint32_t mimetype_offset;
00294          const char *non_match;
00295          
00296          mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
00297          non_match = cache->buffer + mimetype_offset;
00298 
00299          for (n = 0; n < n_mime_types; n++)
00300            {
00301              if (mime_types[n] && 
00302                 xdg_mime_mime_type_equal (mime_types[n], non_match))
00303               mime_types[n] = NULL;
00304            }
00305        }
00306     }
00307 
00308   return NULL;
00309 }
00310 
00311 static const char *
00312 cache_alias_lookup (const char *alias)
00313 {
00314   const char *ptr;
00315   int i, min, max, mid, cmp;
00316 
00317   for (i = 0; _xdg_mime_caches[i]; i++)
00318     {
00319       XdgMimeCache *cache = _xdg_mime_caches[i];
00320       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
00321       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00322       xdg_uint32_t offset;
00323 
00324       min = 0; 
00325       max = n_entries - 1;
00326       while (max >= min) 
00327        {
00328          mid = (min + max) / 2;
00329 
00330          offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
00331          ptr = cache->buffer + offset;
00332          cmp = strcmp (ptr, alias);
00333          
00334          if (cmp < 0)
00335            min = mid + 1;
00336          else if (cmp > 0)
00337            max = mid - 1;
00338          else
00339            {
00340              offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
00341              return cache->buffer + offset;
00342            }
00343        }
00344     }
00345 
00346   return NULL;
00347 }
00348 
00349 static int
00350 cache_glob_lookup_literal (const char *file_name,
00351                         const char *mime_types[],
00352                         int         n_mime_types)
00353 {
00354   const char *ptr;
00355   int i, min, max, mid, cmp;
00356 
00357   for (i = 0; _xdg_mime_caches[i]; i++)
00358     {
00359       XdgMimeCache *cache = _xdg_mime_caches[i];
00360       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
00361       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00362       xdg_uint32_t offset;
00363 
00364       min = 0; 
00365       max = n_entries - 1;
00366       while (max >= min) 
00367        {
00368          mid = (min + max) / 2;
00369 
00370          offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
00371          ptr = cache->buffer + offset;
00372          cmp = strcmp (ptr, file_name);
00373          
00374          if (cmp < 0)
00375            min = mid + 1;
00376          else if (cmp > 0)
00377            max = mid - 1;
00378          else
00379            {
00380              offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
00381              mime_types[0] = (const char *)(cache->buffer + offset);
00382              
00383              return 1;
00384            }
00385        }
00386     }
00387 
00388   return 0;
00389 }
00390 
00391 static int
00392 cache_glob_lookup_fnmatch (const char *file_name,
00393                         const char *mime_types[],
00394                         int         n_mime_types)
00395 {
00396   const char *mime_type;
00397   const char *ptr;
00398 
00399   int i, j, n;
00400 
00401   n = 0;
00402   for (i = 0; _xdg_mime_caches[i]; i++)
00403     {
00404       XdgMimeCache *cache = _xdg_mime_caches[i];
00405 
00406       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
00407       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00408 
00409       for (j = 0; j < n_entries && n < n_mime_types; j++)
00410        {
00411          xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
00412          xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
00413          ptr = cache->buffer + offset;
00414          mime_type = cache->buffer + mimetype_offset;
00415 
00416          /* FIXME: Not UTF-8 safe */
00417          if (fnmatch (ptr, file_name, 0) == 0)
00418            mime_types[n++] = mime_type;
00419        }
00420 
00421       if (n > 0)
00422        return n;
00423     }
00424   
00425   return 0;
00426 }
00427 
00428 static int
00429 cache_glob_node_lookup_suffix (XdgMimeCache *cache,
00430                             xdg_uint32_t  n_entries,
00431                             xdg_uint32_t  offset,
00432                             const char   *suffix, 
00433                             int           ignore_case,
00434                             const char   *mime_types[],
00435                             int           n_mime_types)
00436 {
00437   xdg_unichar_t character;
00438   xdg_unichar_t match_char;
00439   xdg_uint32_t mimetype_offset;
00440   xdg_uint32_t n_children;
00441   xdg_uint32_t child_offset; 
00442 
00443   int min, max, mid, n, i;
00444 
00445   character = _xdg_utf8_to_ucs4 (suffix);
00446   if (ignore_case)
00447     character = _xdg_ucs4_to_lower (character);
00448 
00449   min = 0;
00450   max = n_entries - 1;
00451   while (max >= min)
00452     {
00453       mid = (min + max) /  2;
00454 
00455       match_char = GET_UINT32 (cache->buffer, offset + 16 * mid);
00456 
00457       if (match_char < character)
00458        min = mid + 1;
00459       else if (match_char > character)
00460        max = mid - 1;
00461       else 
00462        {
00463          suffix = _xdg_utf8_next_char (suffix);
00464          if (*suffix == '\0')
00465            {
00466              mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 4);
00467              n = 0;
00468               if (cache->buffer[mimetype_offset])
00469                 mime_types[n++] = cache->buffer + mimetype_offset;
00470 
00471              n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
00472              child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
00473              i = 0;
00474              while (n < n_mime_types && i < n_children)
00475               {
00476                 match_char = GET_UINT32 (cache->buffer, child_offset + 16 * i);
00477                 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * i + 4);
00478                 if (match_char != 0)
00479                   break;
00480 
00481                 mime_types[n++] = cache->buffer + mimetype_offset;
00482                 i++;
00483               }
00484 
00485              return n;
00486            }
00487          else
00488            {
00489              n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
00490              child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
00491       
00492              return cache_glob_node_lookup_suffix (cache, 
00493                                               n_children, child_offset,
00494                                               suffix, ignore_case,
00495                                               mime_types,
00496                                               n_mime_types);
00497            }
00498        }
00499     }
00500 
00501   return 0;
00502 }
00503 
00504 static int
00505 cache_glob_lookup_suffix (const char *suffix, 
00506                        int         ignore_case,
00507                        const char *mime_types[],
00508                        int         n_mime_types)
00509 {
00510   int i, n;
00511 
00512   for (i = 0; _xdg_mime_caches[i]; i++)
00513     {
00514       XdgMimeCache *cache = _xdg_mime_caches[i];
00515 
00516       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
00517       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00518       xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
00519 
00520       n = cache_glob_node_lookup_suffix (cache, 
00521                                     n_entries, offset, 
00522                                     suffix, ignore_case,
00523                                     mime_types,
00524                                     n_mime_types);
00525       if (n > 0)
00526        return n;
00527     }
00528 
00529   return 0;
00530 }
00531 
00532 static void
00533 find_stopchars (char *stopchars)
00534 {
00535   int i, j, k, l;
00536  
00537   k = 0;
00538   for (i = 0; _xdg_mime_caches[i]; i++)
00539     {
00540       XdgMimeCache *cache = _xdg_mime_caches[i];
00541 
00542       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
00543       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00544       xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
00545 
00546       for (j = 0; j < n_entries; j++)
00547        {
00548          xdg_uint32_t match_char = GET_UINT32 (cache->buffer, offset);
00549          
00550          if (match_char < 128)
00551            {
00552              for (l = 0; l < k; l++)
00553               if (stopchars[l] == match_char)
00554                 break;
00555              if (l == k)
00556               {
00557                 stopchars[k] = (char) match_char;
00558                 k++;
00559               }
00560            }
00561 
00562          offset += 16;
00563        }
00564     }
00565 
00566   stopchars[k] = '\0';
00567 }
00568 
00569 static int
00570 cache_glob_lookup_file_name (const char *file_name, 
00571                           const char *mime_types[],
00572                           int         n_mime_types)
00573 {
00574   const char *ptr;
00575   char stopchars[128];
00576   int n;
00577   
00578   assert (file_name != NULL);
00579 
00580   /* First, check the literals */
00581   n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types);
00582   if (n > 0)
00583     return n;
00584 
00585   find_stopchars (stopchars);
00586 
00587   /* Next, check suffixes */
00588   ptr = strpbrk (file_name, stopchars);
00589   while (ptr)
00590     {
00591       n = cache_glob_lookup_suffix (ptr, FALSE, mime_types, n_mime_types);
00592       if (n > 0)
00593        return n;
00594       
00595       n = cache_glob_lookup_suffix (ptr, TRUE, mime_types, n_mime_types);
00596       if (n > 0)
00597        return n;
00598 
00599       ptr = strpbrk (ptr + 1, stopchars);
00600     }
00601   
00602   /* Last, try fnmatch */
00603   return cache_glob_lookup_fnmatch (file_name, mime_types, n_mime_types);
00604 }
00605 
00606 int
00607 _xdg_mime_cache_get_max_buffer_extents (void)
00608 {
00609   xdg_uint32_t offset;
00610   xdg_uint32_t max_extent;
00611   int i;
00612 
00613   max_extent = 0;
00614   for (i = 0; _xdg_mime_caches[i]; i++)
00615     {
00616       XdgMimeCache *cache = _xdg_mime_caches[i];
00617 
00618       offset = GET_UINT32 (cache->buffer, 24);
00619       max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
00620     }
00621 
00622   return max_extent;
00623 }
00624 
00625 static const char *
00626 cache_get_mime_type_for_data (const void *data,
00627                            size_t      len,
00628                            const char *mime_types[],
00629                            int         n_mime_types)
00630 {
00631   const char *mime_type;
00632   int i, n, priority;
00633 
00634   priority = 0;
00635   mime_type = NULL;
00636   for (i = 0; _xdg_mime_caches[i]; i++)
00637     {
00638       XdgMimeCache *cache = _xdg_mime_caches[i];
00639 
00640       int prio;
00641       const char *match;
00642 
00643       match = cache_magic_lookup_data (cache, data, len, &prio, 
00644                                    mime_types, n_mime_types);
00645       if (prio > priority)
00646        {
00647          priority = prio;
00648          mime_type = match;
00649        }
00650     }
00651 
00652   if (priority > 0)
00653     return mime_type;
00654 
00655   for (n = 0; n < n_mime_types; n++)
00656     {
00657       if (mime_types[n])
00658        return mime_types[n];
00659     }
00660 
00661   return XDG_MIME_TYPE_UNKNOWN;
00662 }
00663 
00664 const char *
00665 _xdg_mime_cache_get_mime_type_for_data (const void *data,
00666                                    size_t      len)
00667 {
00668   return cache_get_mime_type_for_data (data, len, NULL, 0);
00669 }
00670 
00671 const char *
00672 _xdg_mime_cache_get_mime_type_for_file (const char  *file_name,
00673                                    struct stat *statbuf)
00674 {
00675   const char *mime_type;
00676   const char *mime_types[2];
00677   FILE *file;
00678   unsigned char *data;
00679   int max_extent;
00680   int bytes_read;
00681   struct stat buf;
00682   const char *base_name;
00683   int n;
00684 
00685   if (file_name == NULL)
00686     return NULL;
00687 
00688   if (! _xdg_utf8_validate (file_name))
00689     return NULL;
00690 
00691   base_name = _xdg_get_base_name (file_name);
00692   n = cache_glob_lookup_file_name (base_name, mime_types, 2);
00693 
00694   if (n == 1)
00695     return mime_types[0];
00696 
00697   if (!statbuf)
00698     {
00699       if (stat (file_name, &buf) != 0)
00700        return XDG_MIME_TYPE_UNKNOWN;
00701 
00702       statbuf = &buf;
00703     }
00704 
00705   if (!S_ISREG (statbuf->st_mode))
00706     return XDG_MIME_TYPE_UNKNOWN;
00707 
00708   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
00709    * be large and need getting from a stream instead of just reading it all
00710    * in. */
00711   max_extent = _xdg_mime_cache_get_max_buffer_extents ();
00712   data = malloc (max_extent);
00713   if (data == NULL)
00714     return XDG_MIME_TYPE_UNKNOWN;
00715         
00716   file = fopen (file_name, "r");
00717   if (file == NULL)
00718     {
00719       free (data);
00720       return XDG_MIME_TYPE_UNKNOWN;
00721     }
00722 
00723   bytes_read = fread (data, 1, max_extent, file);
00724   if (ferror (file))
00725     {
00726       free (data);
00727       fclose (file);
00728       return XDG_MIME_TYPE_UNKNOWN;
00729     }
00730 
00731   mime_type = cache_get_mime_type_for_data (data, bytes_read,
00732                                        mime_types, n);
00733 
00734   free (data);
00735   fclose (file);
00736 
00737   return mime_type;
00738 }
00739 
00740 const char *
00741 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
00742 {
00743   const char *mime_types[2];
00744 
00745   if (cache_glob_lookup_file_name (file_name, mime_types, 2) == 1)
00746     return mime_types[0];
00747   else
00748     return XDG_MIME_TYPE_UNKNOWN;
00749 }
00750 
00751 #if 1
00752 static int
00753 is_super_type (const char *mime)
00754 {
00755   int length;
00756   const char *type;
00757 
00758   length = strlen (mime);
00759   type = &(mime[length - 2]);
00760 
00761   if (strcmp (type, "/*") == 0)
00762     return 1;
00763 
00764   return 0;
00765 }
00766 #endif
00767 
00768 int
00769 _xdg_mime_cache_mime_type_subclass (const char *mime,
00770                                 const char *base)
00771 {
00772   const char *umime, *ubase;
00773 
00774   int i, j, min, max, med, cmp;
00775   
00776   umime = _xdg_mime_cache_unalias_mime_type (mime);
00777   ubase = _xdg_mime_cache_unalias_mime_type (base);
00778 
00779   if (strcmp (umime, ubase) == 0)
00780     return 1;
00781 
00782   /* We really want to handle text/ * in GtkFileFilter, so we just
00783    * turn on the supertype matching
00784    */
00785 #if 1
00786   /* Handle supertypes */
00787   if (is_super_type (ubase) &&
00788       xdg_mime_media_type_equal (umime, ubase))
00789     return 1;
00790 #endif
00791 
00792   /*  Handle special cases text/plain and application/octet-stream */
00793   if (strcmp (ubase, "text/plain") == 0 && 
00794       strncmp (umime, "text/", 5) == 0)
00795     return 1;
00796 
00797   if (strcmp (ubase, "application/octet-stream") == 0)
00798     return 1;
00799  
00800   for (i = 0; _xdg_mime_caches[i]; i++)
00801     {
00802       XdgMimeCache *cache = _xdg_mime_caches[i];
00803       
00804       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
00805       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00806       xdg_uint32_t offset, n_parents, parent_offset;
00807 
00808       min = 0; 
00809       max = n_entries - 1;
00810       while (max >= min)
00811        {
00812          med = (min + max)/2;
00813          
00814          offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
00815          cmp = strcmp (cache->buffer + offset, umime);
00816          if (cmp < 0)
00817            min = med + 1;
00818          else if (cmp > 0)
00819            max = med - 1;
00820          else
00821            {
00822              offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
00823              n_parents = GET_UINT32 (cache->buffer, offset);
00824              
00825              for (j = 0; j < n_parents; j++)
00826               {
00827                 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
00828                 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
00829                   return 1;
00830               }
00831 
00832              break;
00833            }
00834        }
00835     }
00836 
00837   return 0;
00838 }
00839 
00840 const char *
00841 _xdg_mime_cache_unalias_mime_type (const char *mime)
00842 {
00843   const char *lookup;
00844   
00845   lookup = cache_alias_lookup (mime);
00846   
00847   if (lookup)
00848     return lookup;
00849   
00850   return mime;  
00851 }
00852 
00853 char **
00854 _xdg_mime_cache_list_mime_parents (const char *mime)
00855 {
00856   int i, j, p;
00857   char *all_parents[128]; /* we'll stop at 128 */ 
00858   char **result;
00859 
00860   mime = xdg_mime_unalias_mime_type (mime);
00861 
00862   p = 0;
00863   for (i = 0; _xdg_mime_caches[i]; i++)
00864     {
00865       XdgMimeCache *cache = _xdg_mime_caches[i];
00866 
00867       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
00868       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
00869 
00870       for (j = 0; j < n_entries; j++)
00871        {
00872          xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
00873          xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
00874 
00875          if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
00876            {
00877              int k;
00878              xdg_uint32_t parent_mime_offset;
00879              xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
00880 
00881              for (k = 0; k < n_parents && p < 127; k++)
00882               {
00883                 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
00884                 all_parents[p++] = cache->buffer + parent_mime_offset;
00885               }
00886 
00887              break;
00888            }
00889        }
00890     }
00891   all_parents[p++] = 0;
00892   
00893   result = (char **) malloc (p * sizeof (char *));
00894   memcpy (result, all_parents, p * sizeof (char *));
00895 
00896   return result;
00897 }
00898