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);
466 mime_types[n++] = cache->buffer + mimetype_offset;
468 n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
469 child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
471 while (n < n_mime_types && i < n_children)
473 match_char = GET_UINT32 (cache->buffer, child_offset + 16 * i);
474 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * i + 4);
478 mime_types[n++] = cache->buffer + mimetype_offset;
486 n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
487 child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
489 return cache_glob_node_lookup_suffix (cache,
490 n_children, child_offset,
502 cache_glob_lookup_suffix (const char *suffix,
504 const char *mime_types[],
509 for (i = 0; _caches[i]; i++)
511 XdgMimeCache *cache = _caches[i];
513 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
514 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
515 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
517 n = cache_glob_node_lookup_suffix (cache,
530 find_stopchars (char *stopchars)
535 for (i = 0; _caches[i]; i++)
537 XdgMimeCache *cache = _caches[i];
539 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
540 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
541 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
543 for (j = 0; j < n_entries; j++)
545 xdg_uint32_t match_char = GET_UINT32 (cache->buffer, offset);
547 if (match_char < 128)
549 for (l = 0; l < k; l++)
550 if (stopchars[l] == match_char)
554 stopchars[k] = (char) match_char;
567 cache_glob_lookup_file_name (const char *file_name,
568 const char *mime_types[],
575 assert (file_name != NULL);
577 /* First, check the literals */
578 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types);
582 find_stopchars (stopchars);
584 /* Next, check suffixes */
585 ptr = strpbrk (file_name, stopchars);
588 n = cache_glob_lookup_suffix (ptr, FALSE, mime_types, n_mime_types);
592 n = cache_glob_lookup_suffix (ptr, TRUE, mime_types, n_mime_types);
596 ptr = strpbrk (ptr + 1, stopchars);
599 /* Last, try fnmatch */
600 return cache_glob_lookup_fnmatch (file_name, mime_types, n_mime_types);
604 _xdg_mime_cache_get_max_buffer_extents (void)
607 xdg_uint32_t max_extent;
611 for (i = 0; _caches[i]; i++)
613 XdgMimeCache *cache = _caches[i];
615 offset = GET_UINT32 (cache->buffer, 24);
616 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
623 cache_get_mime_type_for_data (const void *data,
625 const char *mime_types[],
628 const char *mime_type;
633 for (i = 0; _caches[i]; i++)
635 XdgMimeCache *cache = _caches[i];
640 match = cache_magic_lookup_data (cache, data, len, &prio,
641 mime_types, n_mime_types);
652 for (n = 0; n < n_mime_types; n++)
655 return mime_types[n];
658 return XDG_MIME_TYPE_UNKNOWN;
662 _xdg_mime_cache_get_mime_type_for_data (const void *data,
665 return cache_get_mime_type_for_data (data, len, NULL, 0);
669 _xdg_mime_cache_get_mime_type_for_file (const char *file_name,
670 struct stat *statbuf)
672 const char *mime_type;
673 const char *mime_types[2];
679 const char *base_name;
682 if (file_name == NULL)
685 if (! _xdg_utf8_validate (file_name))
688 base_name = _xdg_get_base_name (file_name);
689 n = cache_glob_lookup_file_name (base_name, mime_types, 2);
692 return mime_types[0];
696 if (stat (file_name, &buf) != 0)
697 return XDG_MIME_TYPE_UNKNOWN;
702 if (!S_ISREG (statbuf->st_mode))
703 return XDG_MIME_TYPE_UNKNOWN;
705 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
706 * be large and need getting from a stream instead of just reading it all
708 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
709 data = malloc (max_extent);
711 return XDG_MIME_TYPE_UNKNOWN;
713 file = fopen (file_name, "r");
717 return XDG_MIME_TYPE_UNKNOWN;
720 bytes_read = fread (data, 1, max_extent, file);
725 return XDG_MIME_TYPE_UNKNOWN;
728 mime_type = cache_get_mime_type_for_data (data, bytes_read,
738 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
740 const char *mime_type;
742 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
745 return XDG_MIME_TYPE_UNKNOWN;
750 is_super_type (const char *mime)
755 length = strlen (mime);
756 type = &(mime[length - 2]);
758 if (strcmp (type, "/*") == 0)
766 _xdg_mime_cache_mime_type_subclass (const char *mime,
769 const char *umime, *ubase;
771 int i, j, min, max, med, cmp;
773 umime = _xdg_mime_cache_unalias_mime_type (mime);
774 ubase = _xdg_mime_cache_unalias_mime_type (base);
776 if (strcmp (umime, ubase) == 0)
779 /* We really want to handle text/ * in GtkFileFilter, so we just
780 * turn on the supertype matching
783 /* Handle supertypes */
784 if (is_super_type (ubase) &&
785 xdg_mime_media_type_equal (umime, ubase))
789 /* Handle special cases text/plain and application/octet-stream */
790 if (strcmp (ubase, "text/plain") == 0 &&
791 strncmp (umime, "text/", 5) == 0)
794 if (strcmp (ubase, "application/octet-stream") == 0)
797 for (i = 0; _caches[i]; i++)
799 XdgMimeCache *cache = _caches[i];
801 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
802 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
803 xdg_uint32_t offset, n_parents, parent_offset;
811 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
812 cmp = strcmp (cache->buffer + offset, umime);
819 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
820 n_parents = GET_UINT32 (cache->buffer, offset);
822 for (j = 0; j < n_parents; j++)
824 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
825 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
838 _xdg_mime_cache_unalias_mime_type (const char *mime)
842 lookup = cache_alias_lookup (mime);
851 _xdg_mime_cache_list_mime_parents (const char *mime)
854 char *all_parents[128]; /* we'll stop at 128 */
858 for (i = 0; _caches[i]; i++)
860 XdgMimeCache *cache = _caches[i];
862 xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
863 xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
865 for (j = 0; j < n_entries; j++)
867 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * i);
868 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * i + 4);
870 if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
872 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
874 for (j = 0; j < n_parents; j++)
875 all_parents[p++] = cache->buffer + parents_offset + 4 + 4 * j;
881 all_parents[p++] = 0;
883 result = (char **) malloc (p * sizeof (char *));
884 memcpy (result, all_parents, p * sizeof (char *));