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.
40 #include <netinet/in.h> /* for ntohl/ntohs */
45 #warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
49 #include <sys/types.h>
51 #include "xdgmimecache.h"
52 #include "xdgmimeint.h"
55 #define MAX(a,b) ((a) > (b) ? (a) : (b))
71 #define MAP_FAILED ((void *) -1)
74 #define MAJOR_VERSION 1
75 #define MINOR_VERSION_MIN 1
76 #define MINOR_VERSION_MAX 2
87 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
88 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
91 _xdg_mime_cache_ref (XdgMimeCache *cache)
98 _xdg_mime_cache_unref (XdgMimeCache *cache)
102 if (cache->ref_count == 0)
105 munmap (cache->buffer, cache->size);
112 _xdg_mime_cache_new_from_file (const char *file_name)
114 XdgMimeCache *cache = NULL;
122 /* Open the file and map it into memory */
123 fd = open (file_name, O_RDONLY|_O_BINARY, 0);
128 if (fstat (fd, &st) < 0 || st.st_size < 4)
131 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
133 if (buffer == MAP_FAILED)
136 minor = GET_UINT16 (buffer, 2);
138 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
139 (minor < MINOR_VERSION_MIN ||
140 minor > MINOR_VERSION_MAX))
142 munmap (buffer, st.st_size);
147 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
151 cache->minor = minor;
152 cache->ref_count = 1;
153 cache->buffer = buffer;
154 cache->size = st.st_size;
160 #endif /* HAVE_MMAP */
166 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
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);
179 for (i = range_start; i < range_start + range_length; i++)
181 int valid_matchlet = TRUE;
183 if (i + data_length > len)
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;
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;
218 cache_magic_matchlet_compare (XdgMimeCache *cache,
223 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
224 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
228 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
233 for (i = 0; i < n_children; i++)
235 if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
245 cache_magic_compare_to_data (XdgMimeCache *cache,
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);
258 for (i = 0; i < n_matchlets; i++)
260 if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
265 return cache->buffer + mimetype_offset;
273 cache_magic_lookup_data (XdgMimeCache *cache,
277 const char *mime_types[],
280 xdg_uint32_t list_offset;
281 xdg_uint32_t n_entries;
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++)
296 match = cache_magic_compare_to_data (cache, offset + 16 * j,
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++)
311 _xdg_mime_mime_type_equal (mime_types[n], non_match))
312 mime_types[n] = NULL;
321 cache_alias_lookup (const char *alias)
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);
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);
349 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
350 return cache->buffer + offset;
364 cache_glob_lookup_literal (const char *file_name,
365 const char *mime_types[],
367 int case_sensitive_check)
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);
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);
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);
415 cache_glob_lookup_fnmatch (const char *file_name,
416 MimeWeight mime_types[],
419 const char *mime_type;
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;
458 cache_glob_node_lookup_suffix (XdgMimeCache *cache,
459 xdg_uint32_t n_entries,
461 const char *file_name,
463 int case_sensitive_check,
464 MimeWeight 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;
475 int min, max, mid, n, i;
477 character = file_name[len - 1];
479 assert (character != 0);
485 mid = (min + max) / 2;
486 match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
487 if (match_char < character)
489 else if (match_char > character)
495 n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
496 child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
500 n = cache_glob_node_lookup_suffix (cache,
501 n_children, child_offset,
503 case_sensitive_check,
510 while (n < n_mime_types && i < n_children)
512 match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
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;
537 cache_glob_lookup_suffix (const char *file_name,
540 MimeWeight mime_types[],
545 for (i = 0; _caches[i]; i++)
547 XdgMimeCache *cache = _caches[i];
549 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
550 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
551 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
553 n = cache_glob_node_lookup_suffix (cache,
566 static int compare_mime_weight (const void *a, const void *b)
568 const MimeWeight *aa = (const MimeWeight *)a;
569 const MimeWeight *bb = (const MimeWeight *)b;
571 return bb->weight - aa->weight;
574 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
576 ascii_tolower (const char *str)
580 lower = strdup (str);
585 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
591 cache_glob_lookup_file_name (const char *file_name,
592 const char *mime_types[],
596 MimeWeight mimes[10];
602 assert (file_name != NULL && n_mime_types > 0);
604 /* First, check the literals */
606 lower_case = ascii_tolower (file_name);
608 n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
615 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
622 len = strlen (file_name);
623 n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
625 n = cache_glob_lookup_suffix (file_name, len, TRUE, mimes, n_mimes);
629 /* Last, try fnmatch */
631 n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes);
633 qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight);
635 if (n_mime_types < n)
638 for (i = 0; i < n; i++)
639 mime_types[i] = mimes[i].mime;
645 _xdg_mime_cache_get_max_buffer_extents (void)
648 xdg_uint32_t max_extent;
652 for (i = 0; _caches[i]; i++)
654 XdgMimeCache *cache = _caches[i];
656 offset = GET_UINT32 (cache->buffer, 24);
657 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
664 cache_get_mime_type_for_data (const void *data,
667 const char *mime_types[],
670 const char *mime_type;
675 for (i = 0; _caches[i]; i++)
677 XdgMimeCache *cache = _caches[i];
682 match = cache_magic_lookup_data (cache, data, len, &prio,
683 mime_types, n_mime_types);
692 *result_prio = priority;
697 for (n = 0; n < n_mime_types; n++)
701 return mime_types[n];
704 return XDG_MIME_TYPE_UNKNOWN;
708 _xdg_mime_cache_get_mime_type_for_data (const void *data,
712 return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
716 _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
717 struct stat *statbuf)
719 const char *mime_type;
720 const char *mime_types[10];
726 const char *base_name;
729 if (file_name == NULL)
732 if (! _xdg_utf8_validate (file_name))
735 base_name = _xdg_get_base_name (file_name);
736 n = cache_glob_lookup_file_name (base_name, mime_types, 10);
739 return mime_types[0];
743 if (stat (file_name, &buf) != 0)
744 return XDG_MIME_TYPE_UNKNOWN;
749 if (!S_ISREG (statbuf->st_mode))
750 return XDG_MIME_TYPE_UNKNOWN;
752 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
753 * be large and need getting from a stream instead of just reading it all
755 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
756 data = malloc (max_extent);
758 return XDG_MIME_TYPE_UNKNOWN;
760 file = fopen (file_name, "r");
764 return XDG_MIME_TYPE_UNKNOWN;
767 bytes_read = fread (data, 1, max_extent, file);
772 return XDG_MIME_TYPE_UNKNOWN;
775 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
785 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
787 const char *mime_type;
789 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
792 return XDG_MIME_TYPE_UNKNOWN;
796 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
797 const char *mime_types[],
800 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
805 is_super_type (const char *mime)
810 length = strlen (mime);
811 type = &(mime[length - 2]);
813 if (strcmp (type, "/*") == 0)
821 _xdg_mime_cache_mime_type_subclass (const char *mime,
824 const char *umime, *ubase;
826 int i, j, min, max, med, cmp;
828 umime = _xdg_mime_cache_unalias_mime_type (mime);
829 ubase = _xdg_mime_cache_unalias_mime_type (base);
831 if (strcmp (umime, ubase) == 0)
834 /* We really want to handle text/ * in GtkFileFilter, so we just
835 * turn on the supertype matching
838 /* Handle supertypes */
839 if (is_super_type (ubase) &&
840 xdg_mime_media_type_equal (umime, ubase))
844 /* Handle special cases text/plain and application/octet-stream */
845 if (strcmp (ubase, "text/plain") == 0 &&
846 strncmp (umime, "text/", 5) == 0)
849 if (strcmp (ubase, "application/octet-stream") == 0)
852 for (i = 0; _caches[i]; i++)
854 XdgMimeCache *cache = _caches[i];
856 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
857 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
858 xdg_uint32_t offset, n_parents, parent_offset;
866 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
867 cmp = strcmp (cache->buffer + offset, umime);
874 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
875 n_parents = GET_UINT32 (cache->buffer, offset);
877 for (j = 0; j < n_parents; j++)
879 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
880 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
893 _xdg_mime_cache_unalias_mime_type (const char *mime)
897 lookup = cache_alias_lookup (mime);
906 _xdg_mime_cache_list_mime_parents (const char *mime)
909 char *all_parents[128]; /* we'll stop at 128 */
912 mime = xdg_mime_unalias_mime_type (mime);
915 for (i = 0; _caches[i]; i++)
917 XdgMimeCache *cache = _caches[i];
919 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
920 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
922 for (j = 0; j < n_entries; j++)
924 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
925 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
927 if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
929 xdg_uint32_t parent_mime_offset;
930 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
932 for (k = 0; k < n_parents && p < 127; k++)
934 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
936 /* Don't add same parent multiple times.
937 * This can happen for instance if the same type is listed in multiple directories
939 for (l = 0; l < p; l++)
941 if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0)
946 all_parents[p++] = cache->buffer + parent_mime_offset;
953 all_parents[p++] = NULL;
955 result = (char **) malloc (p * sizeof (char *));
956 memcpy (result, all_parents, p * sizeof (char *));
962 cache_lookup_icon (const char *mime, int header)
965 int i, min, max, mid, cmp;
967 for (i = 0; _caches[i]; i++)
969 XdgMimeCache *cache = _caches[i];
970 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header);
971 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
978 mid = (min + max) / 2;
980 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
981 ptr = cache->buffer + offset;
982 cmp = strcmp (ptr, mime);
990 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
991 return cache->buffer + offset;
1000 _xdg_mime_cache_get_generic_icon (const char *mime)
1002 return cache_lookup_icon (mime, 36);
1006 _xdg_mime_cache_get_icon (const char *mime)
1008 return cache_lookup_icon (mime, 32);
1012 dump_glob_node (XdgMimeCache *cache,
1013 xdg_uint32_t offset,
1016 xdg_unichar_t character;
1017 xdg_uint32_t mime_offset;
1018 xdg_uint32_t n_children;
1019 xdg_uint32_t child_offset;
1022 character = GET_UINT32 (cache->buffer, offset);
1023 mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1024 n_children = GET_UINT32 (cache->buffer, offset + 8);
1025 child_offset = GET_UINT32 (cache->buffer, offset + 12);
1026 for (i = 0; i < depth; i++)
1028 printf ("%c", character);
1030 printf (" - %s", cache->buffer + mime_offset);
1034 for (i = 0; i < n_children; i++)
1035 dump_glob_node (cache, child_offset + 20 * i, depth + 1);
1040 _xdg_mime_cache_glob_dump (void)
1043 for (i = 0; _caches[i]; i++)
1045 XdgMimeCache *cache = _caches[i];
1046 xdg_uint32_t list_offset;
1047 xdg_uint32_t n_entries;
1048 xdg_uint32_t offset;
1049 list_offset = GET_UINT32 (cache->buffer, 16);
1050 n_entries = GET_UINT32 (cache->buffer, list_offset);
1051 offset = GET_UINT32 (cache->buffer, list_offset + 4);
1052 for (j = 0; j < n_entries; j++)
1053 dump_glob_node (cache, offset + 20 * j, 0);