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