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 */
47 #include <sys/types.h>
49 #include "xdgmimecache.h"
50 #include "xdgmimeint.h"
53 #define MAX(a,b) ((a) > (b) ? (a) : (b))
69 #define MAP_FAILED ((void *) -1)
72 #define MAJOR_VERSION 1
73 #define MINOR_VERSION 0
83 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
84 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
87 _xdg_mime_cache_ref (XdgMimeCache *cache)
94 _xdg_mime_cache_unref (XdgMimeCache *cache)
98 if (cache->ref_count == 0)
101 munmap (cache->buffer, cache->size);
108 _xdg_mime_cache_new_from_file (const char *file_name)
110 XdgMimeCache *cache = NULL;
117 /* Open the file and map it into memory */
118 fd = open (file_name, O_RDONLY|_O_BINARY, 0);
123 if (fstat (fd, &st) < 0 || st.st_size < 4)
126 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
128 if (buffer == MAP_FAILED)
132 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
133 GET_UINT16 (buffer, 2) != MINOR_VERSION)
135 munmap (buffer, st.st_size);
140 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
141 cache->ref_count = 1;
142 cache->buffer = buffer;
143 cache->size = st.st_size;
149 #endif /* HAVE_MMAP */
155 cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
160 xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
161 xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
162 xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
163 xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
164 xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
168 for (i = range_start; i <= range_start + range_length; i++)
170 int valid_matchlet = TRUE;
172 if (i + data_length > len)
177 for (j = 0; j < data_length; j++)
179 if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
180 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
182 valid_matchlet = FALSE;
189 for (j = 0; j < data_length; j++)
191 if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
193 valid_matchlet = FALSE;
207 cache_magic_matchlet_compare (XdgMimeCache *cache,
212 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
213 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
217 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
222 for (i = 0; i < n_children; i++)
224 if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
234 cache_magic_compare_to_data (XdgMimeCache *cache,
240 xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
241 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
242 xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
243 xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
247 for (i = 0; i < n_matchlets; i++)
249 if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32,
254 return cache->buffer + mimetype_offset;
262 cache_magic_lookup_data (XdgMimeCache *cache,
266 const char *mime_types[],
269 xdg_uint32_t list_offset;
270 xdg_uint32_t n_entries;
277 list_offset = GET_UINT32 (cache->buffer, 24);
278 n_entries = GET_UINT32 (cache->buffer, list_offset);
279 offset = GET_UINT32 (cache->buffer, list_offset + 8);
281 for (j = 0; j < n_entries; j++)
285 match = cache_magic_compare_to_data (cache, offset + 16 * j,
291 xdg_uint32_t mimetype_offset;
292 const char *non_match;
294 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
295 non_match = cache->buffer + mimetype_offset;
297 for (n = 0; n < n_mime_types; n++)
300 xdg_mime_mime_type_equal (mime_types[n], non_match))
301 mime_types[n] = NULL;
310 cache_alias_lookup (const char *alias)
313 int i, min, max, mid, cmp;
315 for (i = 0; _caches[i]; i++)
317 XdgMimeCache *cache = _caches[i];
318 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
319 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
326 mid = (min + max) / 2;
328 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
329 ptr = cache->buffer + offset;
330 cmp = strcmp (ptr, alias);
338 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
339 return cache->buffer + offset;
348 cache_glob_lookup_literal (const char *file_name,
349 const char *mime_types[],
353 int i, min, max, mid, cmp;
355 for (i = 0; _caches[i]; i++)
357 XdgMimeCache *cache = _caches[i];
358 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
359 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
366 mid = (min + max) / 2;
368 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
369 ptr = cache->buffer + offset;
370 cmp = strcmp (ptr, file_name);
378 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
379 mime_types[0] = (const char *)(cache->buffer + offset);
390 cache_glob_lookup_fnmatch (const char *file_name,
391 const char *mime_types[],
394 const char *mime_type;
400 for (i = 0; _caches[i]; i++)
402 XdgMimeCache *cache = _caches[i];
404 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
405 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
407 for (j = 0; j < n_entries && n < n_mime_types; j++)
409 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
410 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
411 ptr = cache->buffer + offset;
412 mime_type = cache->buffer + mimetype_offset;
414 /* FIXME: Not UTF-8 safe */
415 if (fnmatch (ptr, file_name, 0) == 0)
416 mime_types[n++] = mime_type;
427 cache_glob_node_lookup_suffix (XdgMimeCache *cache,
428 xdg_uint32_t n_entries,
432 const char *mime_types[],
435 xdg_unichar_t character;
436 xdg_unichar_t match_char;
437 xdg_uint32_t mimetype_offset;
438 xdg_uint32_t n_children;
439 xdg_uint32_t child_offset;
441 int min, max, mid, n, i;
443 character = _xdg_utf8_to_ucs4 (suffix);
445 character = _xdg_ucs4_to_lower (character);
451 mid = (min + max) / 2;
453 match_char = GET_UINT32 (cache->buffer, offset + 16 * mid);
455 if (match_char < character)
457 else if (match_char > character)
461 suffix = _xdg_utf8_next_char (suffix);
464 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 4);
467 mime_types[n++] = cache->buffer + mimetype_offset;
469 n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
470 child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
472 while (n < n_mime_types && i < n_children)
474 match_char = GET_UINT32 (cache->buffer, child_offset + 16 * i);
475 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * i + 4);
479 mime_types[n++] = cache->buffer + mimetype_offset;
487 n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
488 child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
490 return cache_glob_node_lookup_suffix (cache,
491 n_children, child_offset,
503 cache_glob_lookup_suffix (const char *suffix,
505 const char *mime_types[],
510 for (i = 0; _caches[i]; i++)
512 XdgMimeCache *cache = _caches[i];
514 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
515 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
516 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
518 n = cache_glob_node_lookup_suffix (cache,
531 find_stopchars (char *stopchars)
536 for (i = 0; _caches[i]; i++)
538 XdgMimeCache *cache = _caches[i];
540 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
541 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
542 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
544 for (j = 0; j < n_entries; j++)
546 xdg_uint32_t match_char = GET_UINT32 (cache->buffer, offset);
548 if (match_char < 128)
550 for (l = 0; l < k; l++)
551 if (stopchars[l] == match_char)
555 stopchars[k] = (char) match_char;
568 cache_glob_lookup_file_name (const char *file_name,
569 const char *mime_types[],
576 assert (file_name != NULL);
578 /* First, check the literals */
579 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types);
583 find_stopchars (stopchars);
585 /* Next, check suffixes */
586 ptr = strpbrk (file_name, stopchars);
589 n = cache_glob_lookup_suffix (ptr, FALSE, mime_types, n_mime_types);
593 n = cache_glob_lookup_suffix (ptr, TRUE, mime_types, n_mime_types);
597 ptr = strpbrk (ptr + 1, stopchars);
600 /* Last, try fnmatch */
601 return cache_glob_lookup_fnmatch (file_name, mime_types, n_mime_types);
605 _xdg_mime_cache_get_max_buffer_extents (void)
608 xdg_uint32_t max_extent;
612 for (i = 0; _caches[i]; i++)
614 XdgMimeCache *cache = _caches[i];
616 offset = GET_UINT32 (cache->buffer, 24);
617 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
624 cache_get_mime_type_for_data (const void *data,
627 const char *mime_types[],
630 const char *mime_type;
635 for (i = 0; _caches[i]; i++)
637 XdgMimeCache *cache = _caches[i];
642 match = cache_magic_lookup_data (cache, data, len, &prio,
643 mime_types, n_mime_types);
652 *result_prio = priority;
657 for (n = 0; n < n_mime_types; n++)
661 return mime_types[n];
664 return XDG_MIME_TYPE_UNKNOWN;
668 _xdg_mime_cache_get_mime_type_for_data (const void *data,
672 return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
676 _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
677 struct stat *statbuf)
679 const char *mime_type;
680 const char *mime_types[2];
686 const char *base_name;
689 if (file_name == NULL)
692 if (! _xdg_utf8_validate (file_name))
695 base_name = _xdg_get_base_name (file_name);
696 n = cache_glob_lookup_file_name (base_name, mime_types, 2);
699 return mime_types[0];
703 if (stat (file_name, &buf) != 0)
704 return XDG_MIME_TYPE_UNKNOWN;
709 if (!S_ISREG (statbuf->st_mode))
710 return XDG_MIME_TYPE_UNKNOWN;
712 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
713 * be large and need getting from a stream instead of just reading it all
715 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
716 data = malloc (max_extent);
718 return XDG_MIME_TYPE_UNKNOWN;
720 file = fopen (file_name, "r");
724 return XDG_MIME_TYPE_UNKNOWN;
727 bytes_read = fread (data, 1, max_extent, file);
732 return XDG_MIME_TYPE_UNKNOWN;
735 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
745 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
747 const char *mime_type;
749 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
752 return XDG_MIME_TYPE_UNKNOWN;
756 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
757 const char *mime_types[],
760 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
765 is_super_type (const char *mime)
770 length = strlen (mime);
771 type = &(mime[length - 2]);
773 if (strcmp (type, "/*") == 0)
781 _xdg_mime_cache_mime_type_subclass (const char *mime,
784 const char *umime, *ubase;
786 int i, j, min, max, med, cmp;
788 umime = _xdg_mime_cache_unalias_mime_type (mime);
789 ubase = _xdg_mime_cache_unalias_mime_type (base);
791 if (strcmp (umime, ubase) == 0)
794 /* We really want to handle text/ * in GtkFileFilter, so we just
795 * turn on the supertype matching
798 /* Handle supertypes */
799 if (is_super_type (ubase) &&
800 xdg_mime_media_type_equal (umime, ubase))
804 /* Handle special cases text/plain and application/octet-stream */
805 if (strcmp (ubase, "text/plain") == 0 &&
806 strncmp (umime, "text/", 5) == 0)
809 if (strcmp (ubase, "application/octet-stream") == 0)
812 for (i = 0; _caches[i]; i++)
814 XdgMimeCache *cache = _caches[i];
816 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
817 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
818 xdg_uint32_t offset, n_parents, parent_offset;
826 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
827 cmp = strcmp (cache->buffer + offset, umime);
834 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
835 n_parents = GET_UINT32 (cache->buffer, offset);
837 for (j = 0; j < n_parents; j++)
839 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
840 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
853 _xdg_mime_cache_unalias_mime_type (const char *mime)
857 lookup = cache_alias_lookup (mime);
866 _xdg_mime_cache_list_mime_parents (const char *mime)
869 char *all_parents[128]; /* we'll stop at 128 */
872 mime = xdg_mime_unalias_mime_type (mime);
875 for (i = 0; _caches[i]; i++)
877 XdgMimeCache *cache = _caches[i];
879 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
880 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
882 for (j = 0; j < n_entries; j++)
884 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
885 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
887 if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
889 xdg_uint32_t parent_mime_offset;
890 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
892 for (k = 0; k < n_parents && p < 127; k++)
894 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
895 all_parents[p++] = cache->buffer + parent_mime_offset;
902 all_parents[p++] = 0;
904 result = (char **) malloc (p * sizeof (char *));
905 memcpy (result, all_parents, p * sizeof (char *));