Merge branch '976-disable-assert-checks' into 'master'
[glib.git] / gio / xdgmime / xdgmimecache.c
blob8010f9e5b1ca2780b90b552b7078e3cb612a74ae
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmimealias.c: Private file. mmappable caches for mime data
4 * More info can be found at http://www.freedesktop.org/standards/
6 * Copyright (C) 2005 Matthias Clasen <mclasen@redhat.com>
8 * Licensed under the Academic Free License version 2.0
9 * Or under the following terms:
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.1 of the License, or (at your option) any later version.
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.
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/>.
25 #include "config.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <fnmatch.h>
35 #include <assert.h>
37 #include <netinet/in.h> /* for ntohl/ntohs */
39 #ifdef HAVE_MMAP
40 #include <sys/mman.h>
41 #else
42 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
43 #endif
45 #include <sys/stat.h>
46 #include <sys/types.h>
48 #include "xdgmimecache.h"
49 #include "xdgmimeint.h"
51 #ifndef MAX
52 #define MAX(a,b) ((a) > (b) ? (a) : (b))
53 #endif
55 #ifndef FALSE
56 #define FALSE (0)
57 #endif
59 #ifndef TRUE
60 #define TRUE (!FALSE)
61 #endif
63 #ifndef _O_BINARY
64 #define _O_BINARY 0
65 #endif
67 #ifndef MAP_FAILED
68 #define MAP_FAILED ((void *) -1)
69 #endif
71 #define MAJOR_VERSION 1
72 #define MINOR_VERSION_MIN 1
73 #define MINOR_VERSION_MAX 2
75 struct _XdgMimeCache
77 int ref_count;
78 int minor;
80 size_t size;
81 char *buffer;
84 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
85 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
87 XdgMimeCache *
88 _xdg_mime_cache_ref (XdgMimeCache *cache)
90 cache->ref_count++;
91 return cache;
94 void
95 _xdg_mime_cache_unref (XdgMimeCache *cache)
97 cache->ref_count--;
99 if (cache->ref_count == 0)
101 #ifdef HAVE_MMAP
102 munmap (cache->buffer, cache->size);
103 #endif
104 free (cache);
108 XdgMimeCache *
109 _xdg_mime_cache_new_from_file (const char *file_name)
111 XdgMimeCache *cache = NULL;
113 #ifdef HAVE_MMAP
114 int fd = -1;
115 struct stat st;
116 char *buffer = NULL;
117 int minor;
119 /* Open the file and map it into memory */
121 fd = open (file_name, O_RDONLY|_O_BINARY, 0);
122 while (fd == -1 && errno == EINTR);
124 if (fd < 0)
125 return NULL;
127 if (fstat (fd, &st) < 0 || st.st_size < 4)
128 goto done;
130 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
132 if (buffer == MAP_FAILED)
133 goto done;
135 minor = GET_UINT16 (buffer, 2);
136 /* Verify version */
137 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
138 (minor < MINOR_VERSION_MIN ||
139 minor > MINOR_VERSION_MAX))
141 munmap (buffer, st.st_size);
143 goto done;
146 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
147 cache->minor = minor;
148 cache->ref_count = 1;
149 cache->buffer = buffer;
150 cache->size = st.st_size;
152 done:
153 if (fd != -1)
154 close (fd);
156 #else /* HAVE_MMAP */
157 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
158 cache->minor = 0;
159 cache->ref_count = 1;
160 cache->buffer = NULL;
161 cache->size = 0;
162 #endif /* HAVE_MMAP */
164 return cache;
167 static int
168 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
169 xdg_uint32_t offset,
170 const void *data,
171 size_t len)
173 xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
174 xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
175 xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
176 xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
177 xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
179 int i, j;
181 for (i = range_start; i < range_start + range_length; i++)
183 int valid_matchlet = TRUE;
185 if (i + data_length > len)
186 return FALSE;
188 if (mask_offset)
190 for (j = 0; j < data_length; j++)
192 if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
193 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
195 valid_matchlet = FALSE;
196 break;
200 else
202 for (j = 0; j < data_length; j++)
204 if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
206 valid_matchlet = FALSE;
207 break;
212 if (valid_matchlet)
213 return TRUE;
216 return FALSE;
219 static int
220 cache_magic_matchlet_compare (XdgMimeCache *cache,
221 xdg_uint32_t offset,
222 const void *data,
223 size_t len)
225 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
226 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
228 int i;
230 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
232 if (n_children == 0)
233 return TRUE;
235 for (i = 0; i < n_children; i++)
237 if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
238 data, len))
239 return TRUE;
243 return FALSE;
246 static const char *
247 cache_magic_compare_to_data (XdgMimeCache *cache,
248 xdg_uint32_t offset,
249 const void *data,
250 size_t len,
251 int *prio)
253 xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
254 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
255 xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
256 xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
258 int i;
260 for (i = 0; i < n_matchlets; i++)
262 if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
263 data, len))
265 *prio = priority;
267 return cache->buffer + mimetype_offset;
271 return NULL;
274 static const char *
275 cache_magic_lookup_data (XdgMimeCache *cache,
276 const void *data,
277 size_t len,
278 int *prio,
279 const char *mime_types[],
280 int n_mime_types)
282 xdg_uint32_t list_offset;
283 xdg_uint32_t n_entries;
284 xdg_uint32_t offset;
286 int j, n;
288 *prio = 0;
290 list_offset = GET_UINT32 (cache->buffer, 24);
291 n_entries = GET_UINT32 (cache->buffer, list_offset);
292 offset = GET_UINT32 (cache->buffer, list_offset + 8);
294 for (j = 0; j < n_entries; j++)
296 const char *match;
298 match = cache_magic_compare_to_data (cache, offset + 16 * j,
299 data, len, prio);
300 if (match)
301 return match;
302 else
304 xdg_uint32_t mimetype_offset;
305 const char *non_match;
307 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
308 non_match = cache->buffer + mimetype_offset;
310 for (n = 0; n < n_mime_types; n++)
312 if (mime_types[n] &&
313 _xdg_mime_mime_type_equal (mime_types[n], non_match))
314 mime_types[n] = NULL;
319 return NULL;
322 static const char *
323 cache_alias_lookup (const char *alias)
325 const char *ptr;
326 int i, min, max, mid, cmp;
328 for (i = 0; _caches[i]; i++)
330 XdgMimeCache *cache = _caches[i];
331 xdg_uint32_t list_offset;
332 xdg_uint32_t n_entries;
333 xdg_uint32_t offset;
335 if (cache->buffer == NULL)
336 continue;
338 list_offset = GET_UINT32 (cache->buffer, 4);
339 n_entries = GET_UINT32 (cache->buffer, list_offset);
341 min = 0;
342 max = n_entries - 1;
343 while (max >= min)
345 mid = (min + max) / 2;
347 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
348 ptr = cache->buffer + offset;
349 cmp = strcmp (ptr, alias);
351 if (cmp < 0)
352 min = mid + 1;
353 else if (cmp > 0)
354 max = mid - 1;
355 else
357 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
358 return cache->buffer + offset;
363 return NULL;
366 typedef struct {
367 const char *mime;
368 int weight;
369 } MimeWeight;
371 static int
372 cache_glob_lookup_literal (const char *file_name,
373 const char *mime_types[],
374 int n_mime_types,
375 int case_sensitive_check)
377 const char *ptr;
378 int i, min, max, mid, cmp;
380 for (i = 0; _caches[i]; i++)
382 XdgMimeCache *cache = _caches[i];
383 xdg_uint32_t list_offset;
384 xdg_uint32_t n_entries;
385 xdg_uint32_t offset;
387 if (cache->buffer == NULL)
388 continue;
390 list_offset = GET_UINT32 (cache->buffer, 12);
391 n_entries = GET_UINT32 (cache->buffer, list_offset);
393 min = 0;
394 max = n_entries - 1;
395 while (max >= min)
397 mid = (min + max) / 2;
399 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
400 ptr = cache->buffer + offset;
401 cmp = strcmp (ptr, file_name);
403 if (cmp < 0)
404 min = mid + 1;
405 else if (cmp > 0)
406 max = mid - 1;
407 else
409 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
410 int case_sensitive = weight & 0x100;
411 weight = weight & 0xff;
413 if (case_sensitive_check || !case_sensitive)
415 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
416 mime_types[0] = (const char *)(cache->buffer + offset);
418 return 1;
420 return 0;
425 return 0;
428 static int
429 cache_glob_lookup_fnmatch (const char *file_name,
430 MimeWeight mime_types[],
431 int n_mime_types)
433 const char *mime_type;
434 const char *ptr;
436 int i, j, n;
438 n = 0;
439 for (i = 0; _caches[i]; i++)
441 XdgMimeCache *cache = _caches[i];
443 xdg_uint32_t list_offset;
444 xdg_uint32_t n_entries;
446 if (cache->buffer == NULL)
447 continue;
449 list_offset = GET_UINT32 (cache->buffer, 20);
450 n_entries = GET_UINT32 (cache->buffer, list_offset);
452 for (j = 0; j < n_entries && n < n_mime_types; j++)
454 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
455 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
456 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
457 weight = weight & 0xff;
458 ptr = cache->buffer + offset;
459 mime_type = cache->buffer + mimetype_offset;
461 /* FIXME: Not UTF-8 safe */
462 if (fnmatch (ptr, file_name, 0) == 0)
464 mime_types[n].mime = mime_type;
465 mime_types[n].weight = weight;
466 n++;
470 if (n == n_mime_types)
471 break;
474 return n;
477 static int
478 cache_glob_node_lookup_suffix (XdgMimeCache *cache,
479 xdg_uint32_t n_entries,
480 xdg_uint32_t offset,
481 const char *file_name,
482 int len,
483 int case_sensitive_check,
484 MimeWeight mime_types[],
485 int n_mime_types)
487 xdg_unichar_t character;
488 xdg_unichar_t match_char;
489 xdg_uint32_t mimetype_offset;
490 xdg_uint32_t n_children;
491 xdg_uint32_t child_offset;
492 int weight;
493 int case_sensitive;
495 int min, max, mid, n, i;
497 character = file_name[len - 1];
499 assert (character != 0);
501 min = 0;
502 max = n_entries - 1;
503 while (max >= min)
505 mid = (min + max) / 2;
506 match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
507 if (match_char < character)
508 min = mid + 1;
509 else if (match_char > character)
510 max = mid - 1;
511 else
513 len--;
514 n = 0;
515 n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
516 child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
518 if (len > 0)
520 n = cache_glob_node_lookup_suffix (cache,
521 n_children, child_offset,
522 file_name, len,
523 case_sensitive_check,
524 mime_types,
525 n_mime_types);
527 if (n == 0)
529 i = 0;
530 while (n < n_mime_types && i < n_children)
532 match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
533 if (match_char != 0)
534 break;
536 mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
537 weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
538 case_sensitive = weight & 0x100;
539 weight = weight & 0xff;
541 if (case_sensitive_check || !case_sensitive)
543 mime_types[n].mime = cache->buffer + mimetype_offset;
544 mime_types[n].weight = weight;
545 n++;
547 i++;
550 return n;
553 return 0;
556 static int
557 cache_glob_lookup_suffix (const char *file_name,
558 int len,
559 int ignore_case,
560 MimeWeight mime_types[],
561 int n_mime_types)
563 int i, n;
565 n = 0;
566 for (i = 0; _caches[i]; i++)
568 XdgMimeCache *cache = _caches[i];
570 xdg_uint32_t list_offset;
571 xdg_uint32_t n_entries;
572 xdg_uint32_t offset;
574 if (cache->buffer == NULL)
575 continue;
577 list_offset = GET_UINT32 (cache->buffer, 16);
578 n_entries = GET_UINT32 (cache->buffer, list_offset);
579 offset = GET_UINT32 (cache->buffer, list_offset + 4);
581 n += cache_glob_node_lookup_suffix (cache,
582 n_entries, offset,
583 file_name, len,
584 ignore_case,
585 mime_types + n,
586 n_mime_types - n);
587 if (n == n_mime_types)
588 break;
591 return n;
594 static int compare_mime_weight (const void *a, const void *b)
596 const MimeWeight *aa = (const MimeWeight *)a;
597 const MimeWeight *bb = (const MimeWeight *)b;
599 return bb->weight - aa->weight;
602 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
603 static char *
604 ascii_tolower (const char *str)
606 char *p, *lower;
608 lower = strdup (str);
609 p = lower;
610 while (*p != 0)
612 char c = *p;
613 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
615 return lower;
618 static int
619 filter_out_dupes (MimeWeight mimes[], int n_mimes)
621 int last;
622 int i, j;
624 last = n_mimes;
626 for (i = 0; i < last; i++)
628 j = i + 1;
629 while (j < last)
631 if (strcmp (mimes[i].mime, mimes[j].mime) == 0)
633 mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
634 last--;
635 mimes[j].mime = mimes[last].mime;
636 mimes[j].weight = mimes[last].weight;
638 else
639 j++;
643 return last;
646 static int
647 cache_glob_lookup_file_name (const char *file_name,
648 const char *mime_types[],
649 int n_mime_types)
651 int n;
652 MimeWeight mimes[10];
653 int n_mimes = 10;
654 int i;
655 int len;
656 char *lower_case;
658 assert (file_name != NULL && n_mime_types > 0);
660 /* First, check the literals */
662 lower_case = ascii_tolower (file_name);
664 n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
665 if (n > 0)
667 free (lower_case);
668 return n;
671 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
672 if (n > 0)
674 free (lower_case);
675 return n;
678 len = strlen (file_name);
679 n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
680 if (n < 2)
681 n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
683 free (lower_case);
685 /* Last, try fnmatch */
686 if (n < 2)
687 n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n);
689 n = filter_out_dupes (mimes, n);
691 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
693 if (n_mime_types < n)
694 n = n_mime_types;
696 for (i = 0; i < n; i++)
697 mime_types[i] = mimes[i].mime;
699 return n;
703 _xdg_mime_cache_get_max_buffer_extents (void)
705 xdg_uint32_t offset;
706 xdg_uint32_t max_extent;
707 int i;
709 max_extent = 0;
710 for (i = 0; _caches[i]; i++)
712 XdgMimeCache *cache = _caches[i];
714 if (cache->buffer == NULL)
715 continue;
717 offset = GET_UINT32 (cache->buffer, 24);
718 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
721 return max_extent;
724 static const char *
725 cache_get_mime_type_for_data (const void *data,
726 size_t len,
727 int *result_prio,
728 const char *mime_types[],
729 int n_mime_types)
731 const char *mime_type;
732 int i, n, priority;
734 priority = 0;
735 mime_type = NULL;
736 for (i = 0; _caches[i]; i++)
738 XdgMimeCache *cache = _caches[i];
740 int prio;
741 const char *match;
743 if (cache->buffer == NULL)
744 continue;
746 match = cache_magic_lookup_data (cache, data, len, &prio,
747 mime_types, n_mime_types);
748 if (prio > priority)
750 priority = prio;
751 mime_type = match;
755 if (result_prio)
756 *result_prio = priority;
758 if (priority > 0)
759 return mime_type;
761 for (n = 0; n < n_mime_types; n++)
763 if (mime_types[n])
764 return mime_types[n];
767 return NULL;
770 const char *
771 _xdg_mime_cache_get_mime_type_for_data (const void *data,
772 size_t len,
773 int *result_prio)
775 return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
778 #ifdef NOT_USED_IN_GIO
780 const char *
781 _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
782 struct stat *statbuf)
784 const char *mime_type;
785 const char *mime_types[10];
786 FILE *file;
787 unsigned char *data;
788 int max_extent;
789 int bytes_read;
790 struct stat buf;
791 const char *base_name;
792 int n;
794 if (file_name == NULL)
795 return NULL;
797 if (! _xdg_utf8_validate (file_name))
798 return NULL;
800 base_name = _xdg_get_base_name (file_name);
801 n = cache_glob_lookup_file_name (base_name, mime_types, 10);
803 if (n == 1)
804 return mime_types[0];
806 if (!statbuf)
808 if (stat (file_name, &buf) != 0)
809 return XDG_MIME_TYPE_UNKNOWN;
811 statbuf = &buf;
814 if (statbuf->st_size == 0)
815 return XDG_MIME_TYPE_EMPTY;
817 if (!S_ISREG (statbuf->st_mode))
818 return XDG_MIME_TYPE_UNKNOWN;
820 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
821 * be large and need getting from a stream instead of just reading it all
822 * in. */
823 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
824 data = malloc (max_extent);
825 if (data == NULL)
826 return XDG_MIME_TYPE_UNKNOWN;
828 file = fopen (file_name, "r");
829 if (file == NULL)
831 free (data);
832 return XDG_MIME_TYPE_UNKNOWN;
835 bytes_read = fread (data, 1, max_extent, file);
836 if (ferror (file))
838 free (data);
839 fclose (file);
840 return XDG_MIME_TYPE_UNKNOWN;
843 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
844 mime_types, n);
846 if (!mime_type)
847 mime_type = _xdg_binary_or_text_fallback(data, bytes_read);
849 free (data);
850 fclose (file);
852 return mime_type;
855 const char *
856 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
858 const char *mime_type;
860 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
861 return mime_type;
862 else
863 return XDG_MIME_TYPE_UNKNOWN;
866 #endif
869 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
870 const char *mime_types[],
871 int n_mime_types)
873 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
876 #if 1
877 static int
878 ends_with (const char *str,
879 const char *suffix)
881 int length;
882 int suffix_length;
884 length = strlen (str);
885 suffix_length = strlen (suffix);
886 if (length < suffix_length)
887 return 0;
889 if (strcmp (str + length - suffix_length, suffix) == 0)
890 return 1;
892 return 0;
895 static int
896 is_super_type (const char *mime)
898 return ends_with (mime, "/*");
900 #endif
903 _xdg_mime_cache_mime_type_subclass (const char *mime,
904 const char *base)
906 const char *umime, *ubase;
908 int i, j, min, max, med, cmp;
910 umime = _xdg_mime_cache_unalias_mime_type (mime);
911 ubase = _xdg_mime_cache_unalias_mime_type (base);
913 if (strcmp (umime, ubase) == 0)
914 return 1;
916 /* We really want to handle text/ * in GtkFileFilter, so we just
917 * turn on the supertype matching
919 #if 1
920 /* Handle supertypes */
921 if (is_super_type (ubase) &&
922 xdg_mime_media_type_equal (umime, ubase))
923 return 1;
924 #endif
926 /* Handle special cases text/plain and application/octet-stream */
927 if (strcmp (ubase, "text/plain") == 0 &&
928 strncmp (umime, "text/", 5) == 0)
929 return 1;
931 if (strcmp (ubase, "application/octet-stream") == 0 &&
932 strncmp (umime, "inode/", 6) != 0)
933 return 1;
935 for (i = 0; _caches[i]; i++)
937 XdgMimeCache *cache = _caches[i];
938 xdg_uint32_t list_offset;
939 xdg_uint32_t n_entries;
940 xdg_uint32_t offset, n_parents, parent_offset;
942 if (cache->buffer == NULL)
943 continue;
945 list_offset = GET_UINT32 (cache->buffer, 8);
946 n_entries = GET_UINT32 (cache->buffer, list_offset);
948 min = 0;
949 max = n_entries - 1;
950 while (max >= min)
952 med = (min + max)/2;
954 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
955 cmp = strcmp (cache->buffer + offset, umime);
956 if (cmp < 0)
957 min = med + 1;
958 else if (cmp > 0)
959 max = med - 1;
960 else
962 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
963 n_parents = GET_UINT32 (cache->buffer, offset);
965 for (j = 0; j < n_parents; j++)
967 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
968 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
969 return 1;
972 break;
977 return 0;
980 const char *
981 _xdg_mime_cache_unalias_mime_type (const char *mime)
983 const char *lookup;
985 lookup = cache_alias_lookup (mime);
987 if (lookup)
988 return lookup;
990 return mime;
993 char **
994 _xdg_mime_cache_list_mime_parents (const char *mime)
996 int i, j, k, l, p;
997 char *all_parents[128]; /* we'll stop at 128 */
998 char **result;
1000 mime = xdg_mime_unalias_mime_type (mime);
1002 p = 0;
1003 for (i = 0; _caches[i]; i++)
1005 XdgMimeCache *cache = _caches[i];
1006 xdg_uint32_t list_offset;
1007 xdg_uint32_t n_entries;
1009 if (cache->buffer == NULL)
1010 continue;
1012 list_offset = GET_UINT32 (cache->buffer, 8);
1013 n_entries = GET_UINT32 (cache->buffer, list_offset);
1015 for (j = 0; j < n_entries; j++)
1017 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
1018 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
1020 if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
1022 xdg_uint32_t parent_mime_offset;
1023 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
1025 for (k = 0; k < n_parents && p < 127; k++)
1027 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
1029 /* Don't add same parent multiple times.
1030 * This can happen for instance if the same type is listed in multiple directories
1032 for (l = 0; l < p; l++)
1034 if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
1035 break;
1038 if (l == p)
1039 all_parents[p++] = cache->buffer + parent_mime_offset;
1042 break;
1046 all_parents[p++] = NULL;
1048 result = (char **) malloc (p * sizeof (char *));
1049 memcpy (result, all_parents, p * sizeof (char *));
1051 return result;
1054 static const char *
1055 cache_lookup_icon (const char *mime, int header)
1057 const char *ptr;
1058 int i, min, max, mid, cmp;
1060 for (i = 0; _caches[i]; i++)
1062 XdgMimeCache *cache = _caches[i];
1063 xdg_uint32_t list_offset;
1064 xdg_uint32_t n_entries;
1065 xdg_uint32_t offset;
1067 if (cache->buffer == NULL)
1068 continue;
1070 list_offset = GET_UINT32 (cache->buffer, header);
1071 n_entries = GET_UINT32 (cache->buffer, list_offset);
1073 min = 0;
1074 max = n_entries - 1;
1075 while (max >= min)
1077 mid = (min + max) / 2;
1079 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
1080 ptr = cache->buffer + offset;
1081 cmp = strcmp (ptr, mime);
1083 if (cmp < 0)
1084 min = mid + 1;
1085 else if (cmp > 0)
1086 max = mid - 1;
1087 else
1089 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
1090 return cache->buffer + offset;
1095 return NULL;
1098 const char *
1099 _xdg_mime_cache_get_generic_icon (const char *mime)
1101 return cache_lookup_icon (mime, 36);
1104 const char *
1105 _xdg_mime_cache_get_icon (const char *mime)
1107 return cache_lookup_icon (mime, 32);
1110 #ifdef NOT_USED_IN_GIO
1112 static void
1113 dump_glob_node (XdgMimeCache *cache,
1114 xdg_uint32_t offset,
1115 int depth)
1117 xdg_unichar_t character;
1118 xdg_uint32_t mime_offset;
1119 xdg_uint32_t n_children;
1120 xdg_uint32_t child_offset;
1121 int i;
1123 character = GET_UINT32 (cache->buffer, offset);
1124 mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1125 n_children = GET_UINT32 (cache->buffer, offset + 8);
1126 child_offset = GET_UINT32 (cache->buffer, offset + 12);
1127 for (i = 0; i < depth; i++)
1128 printf (" ");
1129 printf ("%c", character);
1130 if (mime_offset)
1131 printf (" - %s", cache->buffer + mime_offset);
1132 printf ("\n");
1133 if (child_offset)
1135 for (i = 0; i < n_children; i++)
1136 dump_glob_node (cache, child_offset + 20 * i, depth + 1);
1140 void
1141 _xdg_mime_cache_glob_dump (void)
1143 int i, j;
1144 for (i = 0; _caches[i]; i++)
1146 XdgMimeCache *cache = _caches[i];
1147 xdg_uint32_t list_offset;
1148 xdg_uint32_t n_entries;
1149 xdg_uint32_t offset;
1151 if (cache->buffer == NULL)
1152 continue;
1154 list_offset = GET_UINT32 (cache->buffer, 16);
1155 n_entries = GET_UINT32 (cache->buffer, list_offset);
1156 offset = GET_UINT32 (cache->buffer, list_offset + 4);
1157 for (j = 0; j < n_entries; j++)
1158 dump_glob_node (cache, offset + 20 * j, 0);
1162 #endif