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