Home | History | Annotate | Download | only in xdgmime
      1 /* -*- mode: C; c-file-style: "gnu" -*- */
      2 /* xdgmimealias.c: Private file.  mmappable caches for mime data
      3  *
      4  * More info can be found at http://www.freedesktop.org/standards/
      5  *
      6  * Copyright (C) 2005  Matthias Clasen <mclasen (at) redhat.com>
      7  *
      8  * Licensed under the Academic Free License version 2.0
      9  * Or under the following terms:
     10  *
     11  * This library is free software; you can redistribute it and/or
     12  * modify it under the terms of the GNU Lesser General Public
     13  * License as published by the Free Software Foundation; either
     14  * version 2 of the License, or (at your option) any later version.
     15  *
     16  * This library is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
     19  * Lesser General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU Lesser General Public
     22  * License along with this library; if not, write to the
     23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     24  * Boston, MA 02111-1307, USA.
     25  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 #include "config.h"
     29 #endif
     30 
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <ctype.h>
     35 
     36 #include <fcntl.h>
     37 #include <unistd.h>
     38 #include <fnmatch.h>
     39 #include <assert.h>
     40 
     41 #include <netinet/in.h> /* for ntohl/ntohs */
     42 
     43 #ifdef HAVE_MMAP
     44 #include <sys/mman.h>
     45 #else
     46 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
     47 #endif
     48 
     49 #include <sys/stat.h>
     50 #include <sys/types.h>
     51 
     52 #include "xdgmimecache.h"
     53 #include "xdgmimeint.h"
     54 
     55 #ifndef MAX
     56 #define MAX(a,b) ((a) > (b) ? (a) : (b))
     57 #endif
     58 
     59 #ifndef	FALSE
     60 #define	FALSE	(0)
     61 #endif
     62 
     63 #ifndef	TRUE
     64 #define	TRUE	(!FALSE)
     65 #endif
     66 
     67 #ifndef _O_BINARY
     68 #define _O_BINARY 0
     69 #endif
     70 
     71 #ifndef MAP_FAILED
     72 #define MAP_FAILED ((void *) -1)
     73 #endif
     74 
     75 #define MAJOR_VERSION 1
     76 #define MINOR_VERSION 1
     77 
     78 struct _XdgMimeCache
     79 {
     80   int ref_count;
     81 
     82   size_t  size;
     83   char   *buffer;
     84 };
     85 
     86 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
     87 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
     88 
     89 XdgMimeCache *
     90 _xdg_mime_cache_ref (XdgMimeCache *cache)
     91 {
     92   cache->ref_count++;
     93   return cache;
     94 }
     95 
     96 void
     97 _xdg_mime_cache_unref (XdgMimeCache *cache)
     98 {
     99   cache->ref_count--;
    100 
    101   if (cache->ref_count == 0)
    102     {
    103 #ifdef HAVE_MMAP
    104       munmap (cache->buffer, cache->size);
    105 #endif
    106       free (cache);
    107     }
    108 }
    109 
    110 XdgMimeCache *
    111 _xdg_mime_cache_new_from_file (const char *file_name)
    112 {
    113   XdgMimeCache *cache = NULL;
    114 
    115 #ifdef HAVE_MMAP
    116   int fd = -1;
    117   struct stat st;
    118   char *buffer = NULL;
    119 
    120   /* Open the file and map it into memory */
    121   fd = open (file_name, O_RDONLY|_O_BINARY, 0);
    122 
    123   if (fd < 0)
    124     return NULL;
    125 
    126   if (fstat (fd, &st) < 0 || st.st_size < 4)
    127     goto done;
    128 
    129   buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
    130 
    131   if (buffer == MAP_FAILED)
    132     goto done;
    133 
    134   /* Verify version */
    135   if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
    136       GET_UINT16 (buffer, 2) != MINOR_VERSION)
    137     {
    138       munmap (buffer, st.st_size);
    139 
    140       goto done;
    141     }
    142 
    143   cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
    144   cache->ref_count = 1;
    145   cache->buffer = buffer;
    146   cache->size = st.st_size;
    147 
    148  done:
    149   if (fd != -1)
    150     close (fd);
    151 
    152 #endif  /* HAVE_MMAP */
    153 
    154   return cache;
    155 }
    156 
    157 static int
    158 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
    159 				      xdg_uint32_t  offset,
    160 				      const void   *data,
    161 				      size_t        len)
    162 {
    163   xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
    164   xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
    165   xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
    166   xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
    167   xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
    168 
    169   int i, j;
    170 
    171   for (i = range_start; i <= range_start + range_length; i++)
    172     {
    173       int valid_matchlet = TRUE;
    174 
    175       if (i + data_length > len)
    176 	return FALSE;
    177 
    178       if (mask_offset)
    179 	{
    180 	  for (j = 0; j < data_length; j++)
    181 	    {
    182 	      if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
    183 		  ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
    184 		{
    185 		  valid_matchlet = FALSE;
    186 		  break;
    187 		}
    188 	    }
    189 	}
    190       else
    191 	{
    192 	  for (j = 0; j < data_length; j++)
    193 	    {
    194 	      if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
    195 		{
    196 		  valid_matchlet = FALSE;
    197 		  break;
    198 		}
    199 	    }
    200 	}
    201 
    202       if (valid_matchlet)
    203 	return TRUE;
    204     }
    205 
    206   return FALSE;
    207 }
    208 
    209 static int
    210 cache_magic_matchlet_compare (XdgMimeCache *cache,
    211 			      xdg_uint32_t  offset,
    212 			      const void   *data,
    213 			      size_t        len)
    214 {
    215   xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
    216   xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
    217 
    218   int i;
    219 
    220   if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
    221     {
    222       if (n_children == 0)
    223 	return TRUE;
    224 
    225       for (i = 0; i < n_children; i++)
    226 	{
    227 	  if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
    228 					    data, len))
    229 	    return TRUE;
    230 	}
    231     }
    232 
    233   return FALSE;
    234 }
    235 
    236 static const char *
    237 cache_magic_compare_to_data (XdgMimeCache *cache,
    238 			     xdg_uint32_t  offset,
    239 			     const void   *data,
    240 			     size_t        len,
    241 			     int          *prio)
    242 {
    243   xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
    244   xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
    245   xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
    246   xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
    247 
    248   int i;
    249 
    250   for (i = 0; i < n_matchlets; i++)
    251     {
    252       if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
    253 					data, len))
    254 	{
    255 	  *prio = priority;
    256 
    257 	  return cache->buffer + mimetype_offset;
    258 	}
    259     }
    260 
    261   return NULL;
    262 }
    263 
    264 static const char *
    265 cache_magic_lookup_data (XdgMimeCache *cache,
    266 			 const void   *data,
    267 			 size_t        len,
    268 			 int          *prio,
    269 			 const char   *mime_types[],
    270 			 int           n_mime_types)
    271 {
    272   xdg_uint32_t list_offset;
    273   xdg_uint32_t n_entries;
    274   xdg_uint32_t offset;
    275 
    276   int j, n;
    277 
    278   *prio = 0;
    279 
    280   list_offset = GET_UINT32 (cache->buffer, 24);
    281   n_entries = GET_UINT32 (cache->buffer, list_offset);
    282   offset = GET_UINT32 (cache->buffer, list_offset + 8);
    283 
    284   for (j = 0; j < n_entries; j++)
    285     {
    286       const char *match;
    287 
    288       match = cache_magic_compare_to_data (cache, offset + 16 * j,
    289 					   data, len, prio);
    290       if (match)
    291 	return match;
    292       else
    293 	{
    294 	  xdg_uint32_t mimetype_offset;
    295 	  const char *non_match;
    296 
    297 	  mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
    298 	  non_match = cache->buffer + mimetype_offset;
    299 
    300 	  for (n = 0; n < n_mime_types; n++)
    301 	    {
    302 	      if (mime_types[n] &&
    303 		  _xdg_mime_mime_type_equal (mime_types[n], non_match))
    304 		mime_types[n] = NULL;
    305 	    }
    306 	}
    307     }
    308 
    309   return NULL;
    310 }
    311 
    312 static const char *
    313 cache_alias_lookup (const char *alias)
    314 {
    315   const char *ptr;
    316   int i, min, max, mid, cmp;
    317 
    318   for (i = 0; _caches[i]; i++)
    319     {
    320       XdgMimeCache *cache = _caches[i];
    321       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
    322       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    323       xdg_uint32_t offset;
    324 
    325       min = 0;
    326       max = n_entries - 1;
    327       while (max >= min)
    328 	{
    329 	  mid = (min + max) / 2;
    330 
    331 	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
    332 	  ptr = cache->buffer + offset;
    333 	  cmp = strcmp (ptr, alias);
    334 
    335 	  if (cmp < 0)
    336 	    min = mid + 1;
    337 	  else if (cmp > 0)
    338 	    max = mid - 1;
    339 	  else
    340 	    {
    341 	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
    342 	      return cache->buffer + offset;
    343 	    }
    344 	}
    345     }
    346 
    347   return NULL;
    348 }
    349 
    350 typedef struct {
    351   const char *mime;
    352   int weight;
    353 } MimeWeight;
    354 
    355 static int
    356 cache_glob_lookup_literal (const char *file_name,
    357 			   const char *mime_types[],
    358 			   int         n_mime_types)
    359 {
    360   const char *ptr;
    361   int i, min, max, mid, cmp;
    362 
    363   for (i = 0; _caches[i]; i++)
    364     {
    365       XdgMimeCache *cache = _caches[i];
    366       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
    367       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    368       xdg_uint32_t offset;
    369 
    370       min = 0;
    371       max = n_entries - 1;
    372       while (max >= min)
    373 	{
    374 	  mid = (min + max) / 2;
    375 
    376 	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
    377 	  ptr = cache->buffer + offset;
    378 	  cmp = strcmp (ptr, file_name);
    379 
    380 	  if (cmp < 0)
    381 	    min = mid + 1;
    382 	  else if (cmp > 0)
    383 	    max = mid - 1;
    384 	  else
    385 	    {
    386 	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
    387 	      mime_types[0] = (const char *)(cache->buffer + offset);
    388 
    389 	      return 1;
    390 	    }
    391 	}
    392     }
    393 
    394   return 0;
    395 }
    396 
    397 static int
    398 cache_glob_lookup_fnmatch (const char *file_name,
    399 			   MimeWeight  mime_types[],
    400 			   int         n_mime_types)
    401 {
    402   const char *mime_type;
    403   const char *ptr;
    404 
    405   int i, j, n;
    406 
    407   n = 0;
    408   for (i = 0; _caches[i]; i++)
    409     {
    410       XdgMimeCache *cache = _caches[i];
    411 
    412       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
    413       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    414 
    415       for (j = 0; j < n_entries && n < n_mime_types; j++)
    416 	{
    417 	  xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
    418 	  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
    419 	  int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
    420 	  ptr = cache->buffer + offset;
    421 	  mime_type = cache->buffer + mimetype_offset;
    422 
    423 	  /* FIXME: Not UTF-8 safe */
    424 	  if (fnmatch (ptr, file_name, 0) == 0)
    425 	    {
    426 	      mime_types[n].mime = mime_type;
    427 	      mime_types[n].weight = weight;
    428 	      n++;
    429 	    }
    430 	}
    431 
    432       if (n > 0)
    433 	return n;
    434     }
    435 
    436   return 0;
    437 }
    438 
    439 static int
    440 cache_glob_node_lookup_suffix (XdgMimeCache  *cache,
    441 			       xdg_uint32_t   n_entries,
    442 			       xdg_uint32_t   offset,
    443 			       const char    *file_name,
    444 			       int            len,
    445 			       int            ignore_case,
    446 			       MimeWeight     mime_types[],
    447 			       int            n_mime_types)
    448 {
    449   xdg_unichar_t character;
    450   xdg_unichar_t match_char;
    451   xdg_uint32_t mimetype_offset;
    452   xdg_uint32_t n_children;
    453   xdg_uint32_t child_offset;
    454   int weight;
    455 
    456   int min, max, mid, n, i;
    457 
    458   character = file_name[len - 1];
    459   if (ignore_case)
    460     character = tolower (character);
    461 
    462   assert (character != 0);
    463 
    464   min = 0;
    465   max = n_entries - 1;
    466   while (max >= min)
    467     {
    468       mid = (min + max) /  2;
    469       match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
    470       if (match_char < character)
    471 	min = mid + 1;
    472       else if (match_char > character)
    473 	max = mid - 1;
    474       else
    475 	{
    476           len--;
    477           n = 0;
    478           n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
    479           child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
    480 
    481           if (len > 0)
    482             {
    483               n = cache_glob_node_lookup_suffix (cache,
    484                                                  n_children, child_offset,
    485                                                  file_name, len,
    486                                                  ignore_case,
    487                                                  mime_types,
    488                                                  n_mime_types);
    489             }
    490           if (n == 0)
    491             {
    492 	      i = 0;
    493 	      while (n < n_mime_types && i < n_children)
    494 		{
    495 		  match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
    496 		  if (match_char != 0)
    497 		    break;
    498 
    499 		  mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
    500 		  weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
    501 
    502 		  mime_types[n].mime = cache->buffer + mimetype_offset;
    503 		  mime_types[n].weight = weight;
    504 		  n++;
    505 		  i++;
    506 		}
    507 	    }
    508 	  return n;
    509 	}
    510     }
    511   return 0;
    512 }
    513 
    514 static int
    515 cache_glob_lookup_suffix (const char *file_name,
    516 			  int         len,
    517 			  int         ignore_case,
    518 			  MimeWeight  mime_types[],
    519 			  int         n_mime_types)
    520 {
    521   int i, n;
    522 
    523   for (i = 0; _caches[i]; i++)
    524     {
    525       XdgMimeCache *cache = _caches[i];
    526 
    527       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
    528       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    529       xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
    530 
    531       n = cache_glob_node_lookup_suffix (cache,
    532 					 n_entries, offset,
    533 					 file_name, len,
    534 					 ignore_case,
    535 					 mime_types,
    536 					 n_mime_types);
    537       if (n > 0)
    538 	return n;
    539     }
    540 
    541   return 0;
    542 }
    543 
    544 static int compare_mime_weight (const void *a, const void *b)
    545 {
    546   const MimeWeight *aa = (const MimeWeight *)a;
    547   const MimeWeight *bb = (const MimeWeight *)b;
    548 
    549   return aa->weight - bb->weight;
    550 }
    551 
    552 static int
    553 cache_glob_lookup_file_name (const char *file_name,
    554 			     const char *mime_types[],
    555 			     int         n_mime_types)
    556 {
    557   int n;
    558   MimeWeight mimes[10];
    559   int n_mimes = 10;
    560   int i;
    561   int len;
    562 
    563   assert (file_name != NULL && n_mime_types > 0);
    564 
    565   /* First, check the literals */
    566   n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types);
    567   if (n > 0)
    568     return n;
    569 
    570   len = strlen (file_name);
    571   n = cache_glob_lookup_suffix (file_name, len, FALSE, mimes, n_mimes);
    572 
    573   if (n == 0)
    574     n = cache_glob_lookup_suffix (file_name, len, TRUE, mimes, n_mimes);
    575 
    576   /* Last, try fnmatch */
    577   if (n == 0)
    578     n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes);
    579 
    580   qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
    581 
    582   if (n_mime_types < n)
    583     n = n_mime_types;
    584 
    585   for (i = 0; i < n; i++)
    586     mime_types[i] = mimes[i].mime;
    587 
    588   return n;
    589 }
    590 
    591 int
    592 _xdg_mime_cache_get_max_buffer_extents (void)
    593 {
    594   xdg_uint32_t offset;
    595   xdg_uint32_t max_extent;
    596   int i;
    597 
    598   max_extent = 0;
    599   for (i = 0; _caches[i]; i++)
    600     {
    601       XdgMimeCache *cache = _caches[i];
    602 
    603       offset = GET_UINT32 (cache->buffer, 24);
    604       max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
    605     }
    606 
    607   return max_extent;
    608 }
    609 
    610 static const char *
    611 cache_get_mime_type_for_data (const void *data,
    612 			      size_t      len,
    613 			      int        *result_prio,
    614 			      const char *mime_types[],
    615 			      int         n_mime_types)
    616 {
    617   const char *mime_type;
    618   int i, n, priority;
    619 
    620   priority = 0;
    621   mime_type = NULL;
    622   for (i = 0; _caches[i]; i++)
    623     {
    624       XdgMimeCache *cache = _caches[i];
    625 
    626       int prio;
    627       const char *match;
    628 
    629       match = cache_magic_lookup_data (cache, data, len, &prio,
    630 				       mime_types, n_mime_types);
    631       if (prio > priority)
    632 	{
    633 	  priority = prio;
    634 	  mime_type = match;
    635 	}
    636     }
    637 
    638   if (result_prio)
    639     *result_prio = priority;
    640 
    641   if (priority > 0)
    642     return mime_type;
    643 
    644   for (n = 0; n < n_mime_types; n++)
    645     {
    646 
    647       if (mime_types[n])
    648 	return mime_types[n];
    649     }
    650 
    651   return XDG_MIME_TYPE_UNKNOWN;
    652 }
    653 
    654 const char *
    655 _xdg_mime_cache_get_mime_type_for_data (const void *data,
    656 					size_t      len,
    657 					int        *result_prio)
    658 {
    659   return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
    660 }
    661 
    662 const char *
    663 _xdg_mime_cache_get_mime_type_for_file (const char  *file_name,
    664 					struct stat *statbuf)
    665 {
    666   const char *mime_type;
    667   const char *mime_types[10];
    668   FILE *file;
    669   unsigned char *data;
    670   int max_extent;
    671   int bytes_read;
    672   struct stat buf;
    673   const char *base_name;
    674   int n;
    675 
    676   if (file_name == NULL)
    677     return NULL;
    678 
    679   if (! _xdg_utf8_validate (file_name))
    680     return NULL;
    681 
    682   base_name = _xdg_get_base_name (file_name);
    683   n = cache_glob_lookup_file_name (base_name, mime_types, 10);
    684 
    685   if (n == 1)
    686     return mime_types[0];
    687 
    688   if (!statbuf)
    689     {
    690       if (stat (file_name, &buf) != 0)
    691 	return XDG_MIME_TYPE_UNKNOWN;
    692 
    693       statbuf = &buf;
    694     }
    695 
    696   if (!S_ISREG (statbuf->st_mode))
    697     return XDG_MIME_TYPE_UNKNOWN;
    698 
    699   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
    700    * be large and need getting from a stream instead of just reading it all
    701    * in. */
    702   max_extent = _xdg_mime_cache_get_max_buffer_extents ();
    703   data = malloc (max_extent);
    704   if (data == NULL)
    705     return XDG_MIME_TYPE_UNKNOWN;
    706 
    707   file = fopen (file_name, "r");
    708   if (file == NULL)
    709     {
    710       free (data);
    711       return XDG_MIME_TYPE_UNKNOWN;
    712     }
    713 
    714   bytes_read = fread (data, 1, max_extent, file);
    715   if (ferror (file))
    716     {
    717       free (data);
    718       fclose (file);
    719       return XDG_MIME_TYPE_UNKNOWN;
    720     }
    721 
    722   mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
    723 					    mime_types, n);
    724 
    725   free (data);
    726   fclose (file);
    727 
    728   return mime_type;
    729 }
    730 
    731 const char *
    732 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
    733 {
    734   const char *mime_type;
    735 
    736   if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
    737     return mime_type;
    738   else
    739     return XDG_MIME_TYPE_UNKNOWN;
    740 }
    741 
    742 int
    743 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
    744 					       const char  *mime_types[],
    745 					       int          n_mime_types)
    746 {
    747   return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
    748 }
    749 
    750 #if 1
    751 static int
    752 is_super_type (const char *mime)
    753 {
    754   int length;
    755   const char *type;
    756 
    757   length = strlen (mime);
    758   type = &(mime[length - 2]);
    759 
    760   if (strcmp (type, "/*") == 0)
    761     return 1;
    762 
    763   return 0;
    764 }
    765 #endif
    766 
    767 int
    768 _xdg_mime_cache_mime_type_subclass (const char *mime,
    769 				    const char *base)
    770 {
    771   const char *umime, *ubase;
    772 
    773   int i, j, min, max, med, cmp;
    774 
    775   umime = _xdg_mime_cache_unalias_mime_type (mime);
    776   ubase = _xdg_mime_cache_unalias_mime_type (base);
    777 
    778   if (strcmp (umime, ubase) == 0)
    779     return 1;
    780 
    781   /* We really want to handle text/ * in GtkFileFilter, so we just
    782    * turn on the supertype matching
    783    */
    784 #if 1
    785   /* Handle supertypes */
    786   if (is_super_type (ubase) &&
    787       xdg_mime_media_type_equal (umime, ubase))
    788     return 1;
    789 #endif
    790 
    791   /*  Handle special cases text/plain and application/octet-stream */
    792   if (strcmp (ubase, "text/plain") == 0 &&
    793       strncmp (umime, "text/", 5) == 0)
    794     return 1;
    795 
    796   if (strcmp (ubase, "application/octet-stream") == 0)
    797     return 1;
    798 
    799   for (i = 0; _caches[i]; i++)
    800     {
    801       XdgMimeCache *cache = _caches[i];
    802 
    803       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
    804       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    805       xdg_uint32_t offset, n_parents, parent_offset;
    806 
    807       min = 0;
    808       max = n_entries - 1;
    809       while (max >= min)
    810 	{
    811 	  med = (min + max)/2;
    812 
    813 	  offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
    814 	  cmp = strcmp (cache->buffer + offset, umime);
    815 	  if (cmp < 0)
    816 	    min = med + 1;
    817 	  else if (cmp > 0)
    818 	    max = med - 1;
    819 	  else
    820 	    {
    821 	      offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
    822 	      n_parents = GET_UINT32 (cache->buffer, offset);
    823 
    824 	      for (j = 0; j < n_parents; j++)
    825 		{
    826 		  parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
    827 		  if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
    828 		    return 1;
    829 		}
    830 
    831 	      break;
    832 	    }
    833 	}
    834     }
    835 
    836   return 0;
    837 }
    838 
    839 const char *
    840 _xdg_mime_cache_unalias_mime_type (const char *mime)
    841 {
    842   const char *lookup;
    843 
    844   lookup = cache_alias_lookup (mime);
    845 
    846   if (lookup)
    847     return lookup;
    848 
    849   return mime;
    850 }
    851 
    852 char **
    853 _xdg_mime_cache_list_mime_parents (const char *mime)
    854 {
    855   int i, j, k, l, p;
    856   char *all_parents[128]; /* we'll stop at 128 */
    857   char **result;
    858 
    859   mime = xdg_mime_unalias_mime_type (mime);
    860 
    861   p = 0;
    862   for (i = 0; _caches[i]; i++)
    863     {
    864       XdgMimeCache *cache = _caches[i];
    865 
    866       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
    867       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    868 
    869       for (j = 0; j < n_entries; j++)
    870 	{
    871 	  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
    872 	  xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
    873 
    874 	  if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
    875 	    {
    876 	      xdg_uint32_t parent_mime_offset;
    877 	      xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
    878 
    879 	      for (k = 0; k < n_parents && p < 127; k++)
    880 		{
    881 		  parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
    882 
    883 		  /* Don't add same parent multiple times.
    884 		   * This can happen for instance if the same type is listed in multiple directories
    885 		   */
    886 		  for (l = 0; l < p; l++)
    887 		    {
    888 		      if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
    889 			break;
    890 		    }
    891 
    892 		  if (l == p)
    893 		    all_parents[p++] = cache->buffer + parent_mime_offset;
    894 		}
    895 
    896 	      break;
    897 	    }
    898 	}
    899     }
    900   all_parents[p++] = NULL;
    901 
    902   result = (char **) malloc (p * sizeof (char *));
    903   memcpy (result, all_parents, p * sizeof (char *));
    904 
    905   return result;
    906 }
    907 
    908 static const char *
    909 cache_lookup_icon (const char *mime, int header)
    910 {
    911   const char *ptr;
    912   int i, min, max, mid, cmp;
    913 
    914   for (i = 0; _caches[i]; i++)
    915     {
    916       XdgMimeCache *cache = _caches[i];
    917       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header);
    918       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
    919       xdg_uint32_t offset;
    920 
    921       min = 0;
    922       max = n_entries - 1;
    923       while (max >= min)
    924         {
    925           mid = (min + max) / 2;
    926 
    927           offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
    928           ptr = cache->buffer + offset;
    929           cmp = strcmp (ptr, mime);
    930 
    931           if (cmp < 0)
    932             min = mid + 1;
    933           else if (cmp > 0)
    934             max = mid - 1;
    935           else
    936             {
    937               offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
    938               return cache->buffer + offset;
    939             }
    940         }
    941     }
    942 
    943   return NULL;
    944 }
    945 
    946 const char *
    947 _xdg_mime_cache_get_generic_icon (const char *mime)
    948 {
    949   return cache_lookup_icon (mime, 36);
    950 }
    951 
    952 const char *
    953 _xdg_mime_cache_get_icon (const char *mime)
    954 {
    955   return cache_lookup_icon (mime, 32);
    956 }
    957 
    958 static void
    959 dump_glob_node (XdgMimeCache *cache,
    960 		xdg_uint32_t  offset,
    961 		int           depth)
    962 {
    963   xdg_unichar_t character;
    964   xdg_uint32_t mime_offset;
    965   xdg_uint32_t n_children;
    966   xdg_uint32_t child_offset;
    967   int i;
    968 
    969   character = GET_UINT32 (cache->buffer, offset);
    970   mime_offset = GET_UINT32 (cache->buffer, offset + 4);
    971   n_children = GET_UINT32 (cache->buffer, offset + 8);
    972   child_offset = GET_UINT32 (cache->buffer, offset + 12);
    973   for (i = 0; i < depth; i++)
    974     printf (" ");
    975   printf ("%c", character);
    976   if (mime_offset)
    977     printf (" - %s", cache->buffer + mime_offset);
    978   printf ("\n");
    979   if (child_offset)
    980   {
    981     for (i = 0; i < n_children; i++)
    982       dump_glob_node (cache, child_offset + 20 * i, depth + 1);
    983   }
    984 }
    985 
    986 void
    987 _xdg_mime_cache_glob_dump (void)
    988 {
    989   int i, j;
    990   for (i = 0; _caches[i]; i++)
    991   {
    992     XdgMimeCache *cache = _caches[i];
    993     xdg_uint32_t list_offset;
    994     xdg_uint32_t n_entries;
    995     xdg_uint32_t offset;
    996     list_offset = GET_UINT32 (cache->buffer, 16);
    997     n_entries = GET_UINT32 (cache->buffer, list_offset);
    998     offset = GET_UINT32 (cache->buffer, list_offset + 4);
    999     for (j = 0; j < n_entries; j++)
   1000 	    dump_glob_node (cache, offset + 20 * j, 0);
   1001   }
   1002 }
   1003 
   1004 
   1005