GSeekable: document seek-past-end semantics
[glib.git] / gio / xdgmime / xdgmimecache.c
blob3a64debd76b8155c10afd81c0a44b1335a5d4f36
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 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, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <fnmatch.h>
39 #include <assert.h>
41 #include <netinet/in.h> /* for ntohl/ntohs */
43 #ifdef HAVE_MMAP
44 #include <sys/mman.h>
45 #else
46 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
47 #endif
49 #include <sys/stat.h>
50 #include <sys/types.h>
52 #include "xdgmimecache.h"
53 #include "xdgmimeint.h"
55 #ifndef MAX
56 #define MAX(a,b) ((a) > (b) ? (a) : (b))
57 #endif
59 #ifndef FALSE
60 #define FALSE (0)
61 #endif
63 #ifndef TRUE
64 #define TRUE (!FALSE)
65 #endif
67 #ifndef _O_BINARY
68 #define _O_BINARY 0
69 #endif
71 #ifndef MAP_FAILED
72 #define MAP_FAILED ((void *) -1)
73 #endif
75 #define MAJOR_VERSION 1
76 #define MINOR_VERSION_MIN 1
77 #define MINOR_VERSION_MAX 2
79 struct _XdgMimeCache
81 int ref_count;
82 int minor;
84 size_t size;
85 char *buffer;
88 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
89 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
91 XdgMimeCache *
92 _xdg_mime_cache_ref (XdgMimeCache *cache)
94 cache->ref_count++;
95 return cache;
98 void
99 _xdg_mime_cache_unref (XdgMimeCache *cache)
101 cache->ref_count--;
103 if (cache->ref_count == 0)
105 #ifdef HAVE_MMAP
106 munmap (cache->buffer, cache->size);
107 #endif
108 free (cache);
112 XdgMimeCache *
113 _xdg_mime_cache_new_from_file (const char *file_name)
115 XdgMimeCache *cache = NULL;
117 #ifdef HAVE_MMAP
118 int fd = -1;
119 struct stat st;
120 char *buffer = NULL;
121 int minor;
123 /* Open the file and map it into memory */
125 fd = open (file_name, O_RDONLY|_O_BINARY, 0);
126 while (fd == -1 && errno == EINTR);
128 if (fd < 0)
129 return NULL;
131 if (fstat (fd, &st) < 0 || st.st_size < 4)
132 goto done;
134 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
136 if (buffer == MAP_FAILED)
137 goto done;
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))
145 munmap (buffer, st.st_size);
147 goto done;
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;
156 done:
157 if (fd != -1)
158 close (fd);
160 #endif /* HAVE_MMAP */
162 return cache;
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)
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);
177 int i, j;
179 for (i = range_start; i < range_start + range_length; i++)
181 int valid_matchlet = TRUE;
183 if (i + data_length > len)
184 return FALSE;
186 if (mask_offset)
188 for (j = 0; j < data_length; j++)
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]))
193 valid_matchlet = FALSE;
194 break;
198 else
200 for (j = 0; j < data_length; j++)
202 if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
204 valid_matchlet = FALSE;
205 break;
210 if (valid_matchlet)
211 return TRUE;
214 return FALSE;
217 static int
218 cache_magic_matchlet_compare (XdgMimeCache *cache,
219 xdg_uint32_t offset,
220 const void *data,
221 size_t len)
223 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
224 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
226 int i;
228 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
230 if (n_children == 0)
231 return TRUE;
233 for (i = 0; i < n_children; i++)
235 if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
236 data, len))
237 return TRUE;
241 return FALSE;
244 static const char *
245 cache_magic_compare_to_data (XdgMimeCache *cache,
246 xdg_uint32_t offset,
247 const void *data,
248 size_t len,
249 int *prio)
251 xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
252 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
253 xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
254 xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
256 int i;
258 for (i = 0; i < n_matchlets; i++)
260 if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
261 data, len))
263 *prio = priority;
265 return cache->buffer + mimetype_offset;
269 return NULL;
272 static const char *
273 cache_magic_lookup_data (XdgMimeCache *cache,
274 const void *data,
275 size_t len,
276 int *prio,
277 const char *mime_types[],
278 int n_mime_types)
280 xdg_uint32_t list_offset;
281 xdg_uint32_t n_entries;
282 xdg_uint32_t offset;
284 int j, n;
286 *prio = 0;
288 list_offset = GET_UINT32 (cache->buffer, 24);
289 n_entries = GET_UINT32 (cache->buffer, list_offset);
290 offset = GET_UINT32 (cache->buffer, list_offset + 8);
292 for (j = 0; j < n_entries; j++)
294 const char *match;
296 match = cache_magic_compare_to_data (cache, offset + 16 * j,
297 data, len, prio);
298 if (match)
299 return match;
300 else
302 xdg_uint32_t mimetype_offset;
303 const char *non_match;
305 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
306 non_match = cache->buffer + mimetype_offset;
308 for (n = 0; n < n_mime_types; n++)
310 if (mime_types[n] &&
311 _xdg_mime_mime_type_equal (mime_types[n], non_match))
312 mime_types[n] = NULL;
317 return NULL;
320 static const char *
321 cache_alias_lookup (const char *alias)
323 const char *ptr;
324 int i, min, max, mid, cmp;
326 for (i = 0; _caches[i]; i++)
328 XdgMimeCache *cache = _caches[i];
329 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
330 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
331 xdg_uint32_t offset;
333 min = 0;
334 max = n_entries - 1;
335 while (max >= min)
337 mid = (min + max) / 2;
339 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
340 ptr = cache->buffer + offset;
341 cmp = strcmp (ptr, alias);
343 if (cmp < 0)
344 min = mid + 1;
345 else if (cmp > 0)
346 max = mid - 1;
347 else
349 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
350 return cache->buffer + offset;
355 return NULL;
358 typedef struct {
359 const char *mime;
360 int weight;
361 } MimeWeight;
363 static int
364 cache_glob_lookup_literal (const char *file_name,
365 const char *mime_types[],
366 int n_mime_types,
367 int case_sensitive_check)
369 const char *ptr;
370 int i, min, max, mid, cmp;
372 for (i = 0; _caches[i]; i++)
374 XdgMimeCache *cache = _caches[i];
375 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
376 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
377 xdg_uint32_t offset;
379 min = 0;
380 max = n_entries - 1;
381 while (max >= min)
383 mid = (min + max) / 2;
385 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
386 ptr = cache->buffer + offset;
387 cmp = strcmp (ptr, file_name);
389 if (cmp < 0)
390 min = mid + 1;
391 else if (cmp > 0)
392 max = mid - 1;
393 else
395 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
396 int case_sensitive = weight & 0x100;
397 weight = weight & 0xff;
399 if (case_sensitive_check || !case_sensitive)
401 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
402 mime_types[0] = (const char *)(cache->buffer + offset);
404 return 1;
406 return 0;
411 return 0;
414 static int
415 cache_glob_lookup_fnmatch (const char *file_name,
416 MimeWeight mime_types[],
417 int n_mime_types)
419 const char *mime_type;
420 const char *ptr;
422 int i, j, n;
424 n = 0;
425 for (i = 0; _caches[i]; i++)
427 XdgMimeCache *cache = _caches[i];
429 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
430 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
432 for (j = 0; j < n_entries && n < n_mime_types; j++)
434 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
435 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
436 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
437 weight = weight & 0xff;
438 ptr = cache->buffer + offset;
439 mime_type = cache->buffer + mimetype_offset;
441 /* FIXME: Not UTF-8 safe */
442 if (fnmatch (ptr, file_name, 0) == 0)
444 mime_types[n].mime = mime_type;
445 mime_types[n].weight = weight;
446 n++;
450 if (n == n_mime_types)
451 break;
454 return n;
457 static int
458 cache_glob_node_lookup_suffix (XdgMimeCache *cache,
459 xdg_uint32_t n_entries,
460 xdg_uint32_t offset,
461 const char *file_name,
462 int len,
463 int case_sensitive_check,
464 MimeWeight mime_types[],
465 int n_mime_types)
467 xdg_unichar_t character;
468 xdg_unichar_t match_char;
469 xdg_uint32_t mimetype_offset;
470 xdg_uint32_t n_children;
471 xdg_uint32_t child_offset;
472 int weight;
473 int case_sensitive;
475 int min, max, mid, n, i;
477 character = file_name[len - 1];
479 assert (character != 0);
481 min = 0;
482 max = n_entries - 1;
483 while (max >= min)
485 mid = (min + max) / 2;
486 match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
487 if (match_char < character)
488 min = mid + 1;
489 else if (match_char > character)
490 max = mid - 1;
491 else
493 len--;
494 n = 0;
495 n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
496 child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
498 if (len > 0)
500 n = cache_glob_node_lookup_suffix (cache,
501 n_children, child_offset,
502 file_name, len,
503 case_sensitive_check,
504 mime_types,
505 n_mime_types);
507 if (n == 0)
509 i = 0;
510 while (n < n_mime_types && i < n_children)
512 match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
513 if (match_char != 0)
514 break;
516 mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
517 weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
518 case_sensitive = weight & 0x100;
519 weight = weight & 0xff;
521 if (case_sensitive_check || !case_sensitive)
523 mime_types[n].mime = cache->buffer + mimetype_offset;
524 mime_types[n].weight = weight;
525 n++;
527 i++;
530 return n;
533 return 0;
536 static int
537 cache_glob_lookup_suffix (const char *file_name,
538 int len,
539 int ignore_case,
540 MimeWeight mime_types[],
541 int n_mime_types)
543 int i, n;
545 n = 0;
546 for (i = 0; _caches[i]; i++)
548 XdgMimeCache *cache = _caches[i];
550 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
551 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
552 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
554 n += cache_glob_node_lookup_suffix (cache,
555 n_entries, offset,
556 file_name, len,
557 ignore_case,
558 mime_types + n,
559 n_mime_types - n);
560 if (n == n_mime_types)
561 break;
564 return n;
567 static int compare_mime_weight (const void *a, const void *b)
569 const MimeWeight *aa = (const MimeWeight *)a;
570 const MimeWeight *bb = (const MimeWeight *)b;
572 return bb->weight - aa->weight;
575 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
576 static char *
577 ascii_tolower (const char *str)
579 char *p, *lower;
581 lower = strdup (str);
582 p = lower;
583 while (*p != 0)
585 char c = *p;
586 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
588 return lower;
591 static int
592 filter_out_dupes (MimeWeight mimes[], int n_mimes)
594 int last;
595 int i, j;
597 last = n_mimes;
599 for (i = 0; i < last; i++)
601 j = i + 1;
602 while (j < last)
604 if (strcmp (mimes[i].mime, mimes[j].mime) == 0)
606 mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
607 last--;
608 mimes[j].mime = mimes[last].mime;
609 mimes[j].weight = mimes[last].weight;
611 else
612 j++;
616 return last;
619 static int
620 cache_glob_lookup_file_name (const char *file_name,
621 const char *mime_types[],
622 int n_mime_types)
624 int n;
625 MimeWeight mimes[10];
626 int n_mimes = 10;
627 int i;
628 int len;
629 char *lower_case;
631 assert (file_name != NULL && n_mime_types > 0);
633 /* First, check the literals */
635 lower_case = ascii_tolower (file_name);
637 n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
638 if (n > 0)
640 free (lower_case);
641 return n;
644 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
645 if (n > 0)
647 free (lower_case);
648 return n;
651 len = strlen (file_name);
652 n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
653 if (n < 2)
654 n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
656 free (lower_case);
658 /* Last, try fnmatch */
659 if (n < 2)
660 n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n);
662 n = filter_out_dupes (mimes, n);
664 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
666 if (n_mime_types < n)
667 n = n_mime_types;
669 for (i = 0; i < n; i++)
670 mime_types[i] = mimes[i].mime;
672 return n;
676 _xdg_mime_cache_get_max_buffer_extents (void)
678 xdg_uint32_t offset;
679 xdg_uint32_t max_extent;
680 int i;
682 max_extent = 0;
683 for (i = 0; _caches[i]; i++)
685 XdgMimeCache *cache = _caches[i];
687 offset = GET_UINT32 (cache->buffer, 24);
688 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
691 return max_extent;
694 static const char *
695 cache_get_mime_type_for_data (const void *data,
696 size_t len,
697 int *result_prio,
698 const char *mime_types[],
699 int n_mime_types)
701 const char *mime_type;
702 int i, n, priority;
704 priority = 0;
705 mime_type = NULL;
706 for (i = 0; _caches[i]; i++)
708 XdgMimeCache *cache = _caches[i];
710 int prio;
711 const char *match;
713 match = cache_magic_lookup_data (cache, data, len, &prio,
714 mime_types, n_mime_types);
715 if (prio > priority)
717 priority = prio;
718 mime_type = match;
722 if (result_prio)
723 *result_prio = priority;
725 if (priority > 0)
726 return mime_type;
728 for (n = 0; n < n_mime_types; n++)
731 if (mime_types[n])
732 return mime_types[n];
735 return XDG_MIME_TYPE_UNKNOWN;
738 const char *
739 _xdg_mime_cache_get_mime_type_for_data (const void *data,
740 size_t len,
741 int *result_prio)
743 return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
746 #ifdef NOT_USED_IN_GIO
748 const char *
749 _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
750 struct stat *statbuf)
752 const char *mime_type;
753 const char *mime_types[10];
754 FILE *file;
755 unsigned char *data;
756 int max_extent;
757 int bytes_read;
758 struct stat buf;
759 const char *base_name;
760 int n;
762 if (file_name == NULL)
763 return NULL;
765 if (! _xdg_utf8_validate (file_name))
766 return NULL;
768 base_name = _xdg_get_base_name (file_name);
769 n = cache_glob_lookup_file_name (base_name, mime_types, 10);
771 if (n == 1)
772 return mime_types[0];
774 if (!statbuf)
776 if (stat (file_name, &buf) != 0)
777 return XDG_MIME_TYPE_UNKNOWN;
779 statbuf = &buf;
782 if (!S_ISREG (statbuf->st_mode))
783 return XDG_MIME_TYPE_UNKNOWN;
785 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
786 * be large and need getting from a stream instead of just reading it all
787 * in. */
788 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
789 data = malloc (max_extent);
790 if (data == NULL)
791 return XDG_MIME_TYPE_UNKNOWN;
793 file = fopen (file_name, "r");
794 if (file == NULL)
796 free (data);
797 return XDG_MIME_TYPE_UNKNOWN;
800 bytes_read = fread (data, 1, max_extent, file);
801 if (ferror (file))
803 free (data);
804 fclose (file);
805 return XDG_MIME_TYPE_UNKNOWN;
808 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
809 mime_types, n);
811 free (data);
812 fclose (file);
814 return mime_type;
817 const char *
818 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
820 const char *mime_type;
822 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
823 return mime_type;
824 else
825 return XDG_MIME_TYPE_UNKNOWN;
828 #endif
831 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
832 const char *mime_types[],
833 int n_mime_types)
835 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
838 #if 1
839 static int
840 ends_with (const char *str,
841 const char *suffix)
843 int length;
844 int suffix_length;
846 length = strlen (str);
847 suffix_length = strlen (suffix);
848 if (length < suffix_length)
849 return 0;
851 if (strcmp (str + length - suffix_length, suffix) == 0)
852 return 1;
854 return 0;
857 static int
858 is_super_type (const char *mime)
860 return ends_with (mime, "/*");
862 #endif
865 _xdg_mime_cache_mime_type_subclass (const char *mime,
866 const char *base)
868 const char *umime, *ubase;
870 int i, j, min, max, med, cmp;
872 umime = _xdg_mime_cache_unalias_mime_type (mime);
873 ubase = _xdg_mime_cache_unalias_mime_type (base);
875 if (strcmp (umime, ubase) == 0)
876 return 1;
878 /* We really want to handle text/ * in GtkFileFilter, so we just
879 * turn on the supertype matching
881 #if 1
882 /* Handle supertypes */
883 if (is_super_type (ubase) &&
884 xdg_mime_media_type_equal (umime, ubase))
885 return 1;
886 #endif
888 /* Handle special cases text/plain and application/octet-stream */
889 if (strcmp (ubase, "text/plain") == 0 &&
890 strncmp (umime, "text/", 5) == 0)
891 return 1;
893 if (strcmp (ubase, "application/octet-stream") == 0)
894 return 1;
896 for (i = 0; _caches[i]; i++)
898 XdgMimeCache *cache = _caches[i];
900 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
901 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
902 xdg_uint32_t offset, n_parents, parent_offset;
904 min = 0;
905 max = n_entries - 1;
906 while (max >= min)
908 med = (min + max)/2;
910 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
911 cmp = strcmp (cache->buffer + offset, umime);
912 if (cmp < 0)
913 min = med + 1;
914 else if (cmp > 0)
915 max = med - 1;
916 else
918 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
919 n_parents = GET_UINT32 (cache->buffer, offset);
921 for (j = 0; j < n_parents; j++)
923 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
924 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
925 return 1;
928 break;
933 return 0;
936 const char *
937 _xdg_mime_cache_unalias_mime_type (const char *mime)
939 const char *lookup;
941 lookup = cache_alias_lookup (mime);
943 if (lookup)
944 return lookup;
946 return mime;
949 char **
950 _xdg_mime_cache_list_mime_parents (const char *mime)
952 int i, j, k, l, p;
953 char *all_parents[128]; /* we'll stop at 128 */
954 char **result;
956 mime = xdg_mime_unalias_mime_type (mime);
958 p = 0;
959 for (i = 0; _caches[i]; i++)
961 XdgMimeCache *cache = _caches[i];
963 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
964 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
966 for (j = 0; j < n_entries; j++)
968 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
969 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
971 if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
973 xdg_uint32_t parent_mime_offset;
974 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
976 for (k = 0; k < n_parents && p < 127; k++)
978 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
980 /* Don't add same parent multiple times.
981 * This can happen for instance if the same type is listed in multiple directories
983 for (l = 0; l < p; l++)
985 if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
986 break;
989 if (l == p)
990 all_parents[p++] = cache->buffer + parent_mime_offset;
993 break;
997 all_parents[p++] = NULL;
999 result = (char **) malloc (p * sizeof (char *));
1000 memcpy (result, all_parents, p * sizeof (char *));
1002 return result;
1005 static const char *
1006 cache_lookup_icon (const char *mime, int header)
1008 const char *ptr;
1009 int i, min, max, mid, cmp;
1011 for (i = 0; _caches[i]; i++)
1013 XdgMimeCache *cache = _caches[i];
1014 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header);
1015 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
1016 xdg_uint32_t offset;
1018 min = 0;
1019 max = n_entries - 1;
1020 while (max >= min)
1022 mid = (min + max) / 2;
1024 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
1025 ptr = cache->buffer + offset;
1026 cmp = strcmp (ptr, mime);
1028 if (cmp < 0)
1029 min = mid + 1;
1030 else if (cmp > 0)
1031 max = mid - 1;
1032 else
1034 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
1035 return cache->buffer + offset;
1040 return NULL;
1043 const char *
1044 _xdg_mime_cache_get_generic_icon (const char *mime)
1046 return cache_lookup_icon (mime, 36);
1049 const char *
1050 _xdg_mime_cache_get_icon (const char *mime)
1052 return cache_lookup_icon (mime, 32);
1055 #ifdef NOT_USED_IN_GIO
1057 static void
1058 dump_glob_node (XdgMimeCache *cache,
1059 xdg_uint32_t offset,
1060 int depth)
1062 xdg_unichar_t character;
1063 xdg_uint32_t mime_offset;
1064 xdg_uint32_t n_children;
1065 xdg_uint32_t child_offset;
1066 int i;
1068 character = GET_UINT32 (cache->buffer, offset);
1069 mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1070 n_children = GET_UINT32 (cache->buffer, offset + 8);
1071 child_offset = GET_UINT32 (cache->buffer, offset + 12);
1072 for (i = 0; i < depth; i++)
1073 printf (" ");
1074 printf ("%c", character);
1075 if (mime_offset)
1076 printf (" - %s", cache->buffer + mime_offset);
1077 printf ("\n");
1078 if (child_offset)
1080 for (i = 0; i < n_children; i++)
1081 dump_glob_node (cache, child_offset + 20 * i, depth + 1);
1085 void
1086 _xdg_mime_cache_glob_dump (void)
1088 int i, j;
1089 for (i = 0; _caches[i]; i++)
1091 XdgMimeCache *cache = _caches[i];
1092 xdg_uint32_t list_offset;
1093 xdg_uint32_t n_entries;
1094 xdg_uint32_t offset;
1095 list_offset = GET_UINT32 (cache->buffer, 16);
1096 n_entries = GET_UINT32 (cache->buffer, list_offset);
1097 offset = GET_UINT32 (cache->buffer, list_offset + 4);
1098 for (j = 0; j < n_entries; j++)
1099 dump_glob_node (cache, offset + 20 * j, 0);
1103 #endif