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