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