d78d0223aa54be5dbfc208c69111219125b9dcc6
[WebKit-https.git] / Source / ThirdParty / 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 "cmakeconfig.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 <errno.h>
38 #include <fnmatch.h>
39 #include <assert.h>
40
41 #include <netinet/in.h> /* for ntohl/ntohs */
42
43 #ifdef HAVE_MMAP
44 #include <sys/mman.h>
45 #else
46 #warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used.
47 #endif
48
49 #include <sys/stat.h>
50 #include <sys/types.h>
51
52 #include "xdgmimecache.h"
53 #include "xdgmimeint.h"
54
55 #ifndef MAX
56 #define MAX(a,b) ((a) > (b) ? (a) : (b))
57 #endif
58
59 #ifndef FALSE
60 #define FALSE   (0)
61 #endif
62
63 #ifndef TRUE
64 #define TRUE    (!FALSE)
65 #endif
66
67 #ifndef _O_BINARY
68 #define _O_BINARY 0
69 #endif
70
71 #ifndef MAP_FAILED
72 #define MAP_FAILED ((void *) -1)
73 #endif
74
75 #define MAJOR_VERSION 1
76 #define MINOR_VERSION_MIN 1
77 #define MINOR_VERSION_MAX 2
78
79 struct _XdgMimeCache
80 {
81   int ref_count;
82   int minor;
83
84   size_t  size;
85   char   *buffer;
86 };
87
88 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
89 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
90
91 XdgMimeCache *
92 _xdg_mime_cache_ref (XdgMimeCache *cache)
93 {
94   cache->ref_count++;
95   return cache;
96 }
97
98 void
99 _xdg_mime_cache_unref (XdgMimeCache *cache)
100 {
101   cache->ref_count--;
102
103   if (cache->ref_count == 0)
104     {
105 #ifdef HAVE_MMAP
106       munmap (cache->buffer, cache->size);
107 #endif
108       free (cache);
109     }
110 }
111
112 XdgMimeCache *
113 _xdg_mime_cache_new_from_file (const char *file_name)
114 {
115   XdgMimeCache *cache = NULL;
116
117 #ifdef HAVE_MMAP
118   int fd = -1;
119   struct stat st;
120   char *buffer = NULL;
121   int minor;
122
123   /* Open the file and map it into memory */
124   do
125     fd = open (file_name, O_RDONLY|_O_BINARY, 0);
126   while (fd == -1 && errno == EINTR);
127
128   if (fd < 0)
129     return NULL;
130   
131   if (fstat (fd, &st) < 0 || st.st_size < 4)
132     goto done;
133
134   buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
135
136   if (buffer == MAP_FAILED)
137     goto done;
138
139   minor = GET_UINT16 (buffer, 2);
140   /* Verify version */
141   if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
142       (minor < MINOR_VERSION_MIN ||
143        minor > MINOR_VERSION_MAX))
144     {
145       munmap (buffer, st.st_size);
146
147       goto done;
148     }
149   
150   cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
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           valid_matchlet = memcmp(cache->buffer + data_offset, data + i, data_length) == 0;
201         }
202
203       if (valid_matchlet)
204         return TRUE;
205     }
206   
207   return FALSE;  
208 }
209
210 static int
211 cache_magic_matchlet_compare (XdgMimeCache *cache, 
212                               xdg_uint32_t  offset,
213                               const void   *data,
214                               size_t        len)
215 {
216   xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
217   xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
218
219   int i;
220   
221   if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
222     {
223       if (n_children == 0)
224         return TRUE;
225       
226       for (i = 0; i < n_children; i++)
227         {
228           if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
229                                             data, len))
230             return TRUE;
231         }
232     }
233   
234   return FALSE;  
235 }
236
237 static const char *
238 cache_magic_compare_to_data (XdgMimeCache *cache, 
239                              xdg_uint32_t  offset,
240                              const void   *data, 
241                              size_t        len, 
242                              int          *prio)
243 {
244   xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
245   xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
246   xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
247   xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
248
249   int i;
250
251   for (i = 0; i < n_matchlets; i++)
252     {
253       if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 
254                                         data, len))
255         {
256           *prio = priority;
257           
258           return cache->buffer + mimetype_offset;
259         }
260     }
261
262   return NULL;
263 }
264
265 static const char *
266 cache_magic_lookup_data (XdgMimeCache *cache, 
267                          const void   *data, 
268                          size_t        len, 
269                          int          *prio,
270                          const char   *mime_types[],
271                          int           n_mime_types)
272 {
273   xdg_uint32_t list_offset;
274   xdg_uint32_t n_entries;
275   xdg_uint32_t offset;
276
277   int j, n;
278
279   *prio = 0;
280
281   list_offset = GET_UINT32 (cache->buffer, 24);
282   n_entries = GET_UINT32 (cache->buffer, list_offset);
283   offset = GET_UINT32 (cache->buffer, list_offset + 8);
284   
285   for (j = 0; j < n_entries; j++)
286     {
287       const char *match;
288
289       match = cache_magic_compare_to_data (cache, offset + 16 * j, 
290                                            data, len, prio);
291       if (match)
292         return match;
293       else
294         {
295           xdg_uint32_t mimetype_offset;
296           const char *non_match;
297           
298           mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
299           non_match = cache->buffer + mimetype_offset;
300
301           for (n = 0; n < n_mime_types; n++)
302             {
303               if (mime_types[n] && 
304                   _xdg_mime_mime_type_equal (mime_types[n], non_match))
305                 mime_types[n] = NULL;
306             }
307         }
308     }
309
310   return NULL;
311 }
312
313 static const char *
314 cache_alias_lookup (const char *alias)
315 {
316   const char *ptr;
317   int i, min, max, mid, cmp;
318
319   for (i = 0; _caches[i]; i++)
320     {
321       XdgMimeCache *cache = _caches[i];
322       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
323       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
324       xdg_uint32_t offset;
325
326       min = 0; 
327       max = n_entries - 1;
328       while (max >= min) 
329         {
330           mid = (min + max) / 2;
331
332           offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
333           ptr = cache->buffer + offset;
334           cmp = strcmp (ptr, alias);
335           
336           if (cmp < 0)
337             min = mid + 1;
338           else if (cmp > 0)
339             max = mid - 1;
340           else
341             {
342               offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
343               return cache->buffer + offset;
344             }
345         }
346     }
347
348   return NULL;
349 }
350
351 typedef struct {
352   const char *mime;
353   int weight;
354 } MimeWeight;
355
356 static int
357 cache_glob_lookup_literal (const char *file_name,
358                            const char *mime_types[],
359                            int         n_mime_types,
360                            int         case_sensitive_check)
361 {
362   const char *ptr;
363   int i, min, max, mid, cmp;
364
365   for (i = 0; _caches[i]; i++)
366     {
367       XdgMimeCache *cache = _caches[i];
368       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
369       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
370       xdg_uint32_t offset;
371
372       min = 0; 
373       max = n_entries - 1;
374       while (max >= min) 
375         {
376           mid = (min + max) / 2;
377
378           offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
379           ptr = cache->buffer + offset;
380           cmp = strcmp (ptr, file_name);
381
382           if (cmp < 0)
383             min = mid + 1;
384           else if (cmp > 0)
385             max = mid - 1;
386           else
387             {
388               int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
389               int case_sensitive = weight & 0x100;
390               weight = weight & 0xff;
391
392               if (case_sensitive_check || !case_sensitive)
393                 {
394                   offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
395                   mime_types[0] = (const char *)(cache->buffer + offset);
396
397                   return 1;
398                 }
399               return 0;
400             }
401         }
402     }
403
404   return 0;
405 }
406
407 static int
408 cache_glob_lookup_fnmatch (const char *file_name,
409                            MimeWeight  mime_types[],
410                            int         n_mime_types,
411                            int         case_sensitive_check)
412 {
413   const char *mime_type;
414   const char *ptr;
415
416   int i, j, n;
417
418   n = 0;
419   for (i = 0; _caches[i]; i++)
420     {
421       XdgMimeCache *cache = _caches[i];
422
423       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
424       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
425
426       for (j = 0; j < n_entries && n < n_mime_types; j++)
427         {
428           xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
429           xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
430           int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
431           int case_sensitive = weight & 0x100;
432           weight = weight & 0xff;
433           ptr = cache->buffer + offset;
434           mime_type = cache->buffer + mimetype_offset;
435           if (case_sensitive_check || !case_sensitive)
436             {
437               /* FIXME: Not UTF-8 safe */
438               if (fnmatch (ptr, file_name, 0) == 0)
439                 {
440                   mime_types[n].mime = mime_type;
441                   mime_types[n].weight = weight;
442                   n++;
443                 }
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   /* Last, try fnmatch */
625   if (n == 0)
626     n = cache_glob_lookup_fnmatch (lower_case, mimes, n_mimes, FALSE);
627   if (n == 0)
628     n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes, TRUE);
629
630   free (lower_case);
631
632   qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
633
634   if (n_mime_types < n)
635     n = n_mime_types;
636
637   for (i = 0; i < n; i++)
638     mime_types[i] = mimes[i].mime;
639
640   return n;
641 }
642
643 int
644 _xdg_mime_cache_get_max_buffer_extents (void)
645 {
646   xdg_uint32_t offset;
647   xdg_uint32_t max_extent;
648   int i;
649
650   max_extent = 0;
651   for (i = 0; _caches[i]; i++)
652     {
653       XdgMimeCache *cache = _caches[i];
654
655       offset = GET_UINT32 (cache->buffer, 24);
656       max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
657     }
658
659   return max_extent;
660 }
661
662 static const char *
663 cache_get_mime_type_for_data (const void *data,
664                               size_t      len,
665                               int        *result_prio,
666                               const char *mime_types[],
667                               int         n_mime_types)
668 {
669   const char *mime_type;
670   int i, n, priority;
671
672   priority = 0;
673   mime_type = NULL;
674   for (i = 0; _caches[i]; i++)
675     {
676       XdgMimeCache *cache = _caches[i];
677
678       int prio;
679       const char *match;
680
681       match = cache_magic_lookup_data (cache, data, len, &prio, 
682                                        mime_types, n_mime_types);
683       if (prio > priority)
684         {
685           priority = prio;
686           mime_type = match;
687         }
688     }
689
690   if (result_prio)
691     *result_prio = priority;
692
693   if (priority > 0)
694     {
695       /* Pick glob-result R where mime_type inherits from R */
696       for (n = 0; n < n_mime_types; n++)
697         {
698           if (mime_types[n] && _xdg_mime_cache_mime_type_subclass(mime_types[n], mime_type))
699               return mime_types[n];
700         }
701
702       /* Return magic match */
703       return mime_type;
704     }
705
706   /* Pick first glob result, as fallback */
707   for (n = 0; n < n_mime_types; n++)
708     {
709       if (mime_types[n])
710         return mime_types[n];
711     }
712
713   return NULL;
714 }
715
716 const char *
717 _xdg_mime_cache_get_mime_type_for_data (const void *data,
718                                         size_t      len,
719                                         int        *result_prio)
720 {
721   return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
722 }
723
724 const char *
725 _xdg_mime_cache_get_mime_type_for_file (const char  *file_name,
726                                         struct stat *statbuf)
727 {
728   const char *mime_type;
729   const char *mime_types[10];
730   FILE *file;
731   unsigned char *data;
732   int max_extent;
733   int bytes_read;
734   struct stat buf;
735   const char *base_name;
736   int n;
737
738   if (file_name == NULL)
739     return NULL;
740
741   if (! _xdg_utf8_validate (file_name))
742     return NULL;
743
744   base_name = _xdg_get_base_name (file_name);
745   n = cache_glob_lookup_file_name (base_name, mime_types, 10);
746
747   if (n == 1)
748     return mime_types[0];
749
750   if (!statbuf)
751     {
752       if (stat (file_name, &buf) != 0)
753         return XDG_MIME_TYPE_UNKNOWN;
754
755       statbuf = &buf;
756     }
757
758   if (statbuf->st_size == 0)
759     return XDG_MIME_TYPE_EMPTY;
760
761   if (!S_ISREG (statbuf->st_mode))
762     return XDG_MIME_TYPE_UNKNOWN;
763
764   /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
765    * be large and need getting from a stream instead of just reading it all
766    * in. */
767   max_extent = _xdg_mime_cache_get_max_buffer_extents ();
768   data = malloc (max_extent);
769   if (data == NULL)
770     return XDG_MIME_TYPE_UNKNOWN;
771         
772   file = fopen (file_name, "r");
773   if (file == NULL)
774     {
775       free (data);
776       return XDG_MIME_TYPE_UNKNOWN;
777     }
778
779   bytes_read = fread (data, 1, max_extent, file);
780   if (ferror (file))
781     {
782       free (data);
783       fclose (file);
784       return XDG_MIME_TYPE_UNKNOWN;
785     }
786
787   mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
788                                             mime_types, n);
789
790   if (!mime_type)
791     mime_type = _xdg_binary_or_text_fallback(data, bytes_read);
792
793   free (data);
794   fclose (file);
795
796   return mime_type;
797 }
798
799 const char *
800 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
801 {
802   const char *mime_type;
803
804   if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
805     return mime_type;
806   else
807     return XDG_MIME_TYPE_UNKNOWN;
808 }
809
810 int
811 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
812                                                const char  *mime_types[],
813                                                int          n_mime_types)
814 {
815   return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
816 }
817
818 #if 1
819 static int
820 is_super_type (const char *mime)
821 {
822   int length;
823   const char *type;
824
825   length = strlen (mime);
826   type = &(mime[length - 2]);
827
828   if (strcmp (type, "/*") == 0)
829     return 1;
830
831   return 0;
832 }
833 #endif
834
835 int
836 _xdg_mime_cache_mime_type_subclass (const char *mime,
837                                     const char *base)
838 {
839   const char *umime, *ubase;
840
841   int i, j, min, max, med, cmp;
842   
843   umime = _xdg_mime_cache_unalias_mime_type (mime);
844   ubase = _xdg_mime_cache_unalias_mime_type (base);
845
846   if (strcmp (umime, ubase) == 0)
847     return 1;
848
849   /* We really want to handle text/ * in GtkFileFilter, so we just
850    * turn on the supertype matching
851    */
852 #if 1
853   /* Handle supertypes */
854   if (is_super_type (ubase) &&
855       xdg_mime_media_type_equal (umime, ubase))
856     return 1;
857 #endif
858
859   /*  Handle special cases text/plain and application/octet-stream */
860   if (strcmp (ubase, "text/plain") == 0 && 
861       strncmp (umime, "text/", 5) == 0)
862     return 1;
863
864   if (strcmp (ubase, "application/octet-stream") == 0)
865     return 1;
866  
867   for (i = 0; _caches[i]; i++)
868     {
869       XdgMimeCache *cache = _caches[i];
870       
871       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
872       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
873       xdg_uint32_t offset, n_parents, parent_offset;
874
875       min = 0; 
876       max = n_entries - 1;
877       while (max >= min)
878         {
879           med = (min + max)/2;
880           
881           offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
882           cmp = strcmp (cache->buffer + offset, umime);
883           if (cmp < 0)
884             min = med + 1;
885           else if (cmp > 0)
886             max = med - 1;
887           else
888             {
889               offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
890               n_parents = GET_UINT32 (cache->buffer, offset);
891               
892               for (j = 0; j < n_parents; j++)
893                 {
894                   parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
895                   if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
896                     return 1;
897                 }
898
899               break;
900             }
901         }
902     }
903
904   return 0;
905 }
906
907 const char *
908 _xdg_mime_cache_unalias_mime_type (const char *mime)
909 {
910   const char *lookup;
911   
912   lookup = cache_alias_lookup (mime);
913   
914   if (lookup)
915     return lookup;
916   
917   return mime;  
918 }
919
920 char **
921 _xdg_mime_cache_list_mime_parents (const char *mime)
922 {
923   int i, j, k, l, p;
924   char *all_parents[128]; /* we'll stop at 128 */ 
925   char **result;
926
927   mime = xdg_mime_unalias_mime_type (mime);
928
929   p = 0;
930   for (i = 0; _caches[i]; i++)
931     {
932       XdgMimeCache *cache = _caches[i];
933   
934       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
935       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
936
937       for (j = 0; j < n_entries; j++)
938         {
939           xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
940           xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
941
942           if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
943             {
944               xdg_uint32_t parent_mime_offset;
945               xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
946
947               for (k = 0; k < n_parents && p < 127; k++)
948                 {
949                   parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
950
951                   /* Don't add same parent multiple times.
952                    * This can happen for instance if the same type is listed in multiple directories
953                    */
954                   for (l = 0; l < p; l++)
955                     {
956                       if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
957                         break;
958                     }
959
960                   if (l == p)
961                     all_parents[p++] = cache->buffer + parent_mime_offset;
962                 }
963
964               break;
965             }
966         }
967     }
968   all_parents[p++] = NULL;
969   
970   result = (char **) malloc (p * sizeof (char *));
971   memcpy (result, all_parents, p * sizeof (char *));
972
973   return result;
974 }
975
976 static const char *
977 cache_lookup_icon (const char *mime, int header)
978 {
979   const char *ptr;
980   int i, min, max, mid, cmp;
981
982   for (i = 0; _caches[i]; i++)
983     {
984       XdgMimeCache *cache = _caches[i];
985       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header);
986       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
987       xdg_uint32_t offset;
988
989       min = 0; 
990       max = n_entries - 1;
991       while (max >= min) 
992         {
993           mid = (min + max) / 2;
994
995           offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
996           ptr = cache->buffer + offset;
997           cmp = strcmp (ptr, mime);
998          
999           if (cmp < 0)
1000             min = mid + 1;
1001           else if (cmp > 0)
1002             max = mid - 1;
1003           else
1004             {
1005               offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
1006               return cache->buffer + offset;
1007             }
1008         }
1009     }
1010
1011   return NULL;
1012 }
1013
1014 const char *
1015 _xdg_mime_cache_get_generic_icon (const char *mime)
1016 {
1017   return cache_lookup_icon (mime, 36);
1018 }
1019
1020 const char *
1021 _xdg_mime_cache_get_icon (const char *mime)
1022 {
1023   return cache_lookup_icon (mime, 32);
1024 }
1025
1026 static int
1027 get_simple_globs (XdgMimeCache  *cache,
1028                   const char    *mime,
1029                   char          *globs[],
1030                   int            n_globs,
1031                   xdg_uint32_t   offset,
1032                   int           *n,
1033                   xdg_unichar_t *prefix,
1034                   int            depth)
1035 {
1036   xdg_unichar_t character = GET_UINT32 (cache->buffer, offset);
1037   xdg_uint32_t n_children;
1038   xdg_uint32_t child_offset;
1039   int i;
1040
1041   if (*n >= n_globs)
1042     return FALSE;
1043
1044   if (character == 0)
1045     {
1046       xdg_uint32_t mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1047
1048       if (strcasecmp (cache->buffer + mime_offset, mime) == 0) {
1049         globs[*n] = malloc (depth * sizeof (char));
1050         for (i = 0; i < depth; i++)
1051           globs[*n][depth - i - 1] = prefix[i];
1052         globs[*n][depth] = '\0';
1053
1054         (*n)++;
1055       }
1056
1057       return *n < n_globs;
1058     }
1059
1060   prefix[depth] = character;
1061   n_children = GET_UINT32 (cache->buffer, offset + 4);
1062   child_offset = GET_UINT32 (cache->buffer, offset + 8);
1063   for (i = 0; i < n_children; i++)
1064     {
1065       if (!get_simple_globs (cache, mime, globs, n_globs, child_offset + 12 * i, n, prefix, depth + 1))
1066         return FALSE;
1067     }
1068
1069   return *n < n_globs;
1070 }
1071
1072 int
1073 _xdg_mime_cache_get_simple_globs (const char *mime,
1074                                   char       *globs[],
1075                                   int         n_globs)
1076 {
1077   int i, j, n;
1078   xdg_unichar_t prefix[25];
1079
1080   mime = xdg_mime_unalias_mime_type (mime);
1081
1082   n = 0;
1083   for (i = 0; _caches[i]; i++)
1084     {
1085       XdgMimeCache *cache = _caches[i];
1086       xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
1087       xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
1088       xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
1089
1090       for (j = 0; j < n_entries && n < n_globs; j++)
1091         {
1092           if (!get_simple_globs (cache, mime, globs, n_globs, offset + 12 * j, &n, prefix, 0))
1093             break;
1094         }
1095
1096       if (n > 0)
1097         return n;
1098     }
1099
1100   return 0;
1101 }
1102
1103 static void
1104 dump_glob_node (XdgMimeCache *cache,
1105                 xdg_uint32_t  offset,
1106                 int           depth)
1107 {
1108   xdg_unichar_t character;
1109   xdg_uint32_t mime_offset;
1110   xdg_uint32_t n_children;
1111   xdg_uint32_t child_offset;
1112   int i;
1113
1114   character = GET_UINT32 (cache->buffer, offset);
1115   mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1116   n_children = GET_UINT32 (cache->buffer, offset + 8);
1117   child_offset = GET_UINT32 (cache->buffer, offset + 12);
1118   for (i = 0; i < depth; i++)
1119     printf (" ");
1120   printf ("%c", character);
1121   if (mime_offset)
1122     printf (" - %s", cache->buffer + mime_offset);
1123   printf ("\n");
1124   if (child_offset)
1125   {
1126     for (i = 0; i < n_children; i++)
1127       dump_glob_node (cache, child_offset + 20 * i, depth + 1);
1128   }
1129 }
1130
1131 void
1132 _xdg_mime_cache_glob_dump (void)
1133 {
1134   int i, j;
1135   for (i = 0; _caches[i]; i++)
1136   {
1137     XdgMimeCache *cache = _caches[i];
1138     xdg_uint32_t list_offset;
1139     xdg_uint32_t n_entries;
1140     xdg_uint32_t offset;
1141     list_offset = GET_UINT32 (cache->buffer, 16);
1142     n_entries = GET_UINT32 (cache->buffer, list_offset);
1143     offset = GET_UINT32 (cache->buffer, list_offset + 4);
1144     for (j = 0; j < n_entries; j++)
1145             dump_glob_node (cache, offset + 20 * j, 0);
1146   }
1147 }
1148
1149