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 #warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used.
51 #include <sys/types.h>
53 #include "xdgmimecache.h"
54 #include "xdgmimeint.h"
57 #define MAX(a,b) ((a) > (b) ? (a) : (b))
73 #define MAP_FAILED ((void *) -1)
76 #define MAJOR_VERSION 1
77 #define MINOR_VERSION_MIN 1
78 #define MINOR_VERSION_MAX 2
89 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
90 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
93 _xdg_mime_cache_ref (XdgMimeCache
*cache
)
100 _xdg_mime_cache_unref (XdgMimeCache
*cache
)
104 if (cache
->ref_count
== 0)
107 munmap (cache
->buffer
, cache
->size
);
114 _xdg_mime_cache_new_from_file (const char *file_name
)
116 XdgMimeCache
*cache
= NULL
;
124 /* Open the file and map it into memory */
125 fd
= open (file_name
, O_RDONLY
|_O_BINARY
, 0);
130 if (fstat (fd
, &st
) < 0 || st
.st_size
< 4)
133 buffer
= (char *) mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
135 if (buffer
== MAP_FAILED
)
138 minor
= GET_UINT16 (buffer
, 2);
140 if (GET_UINT16 (buffer
, 0) != MAJOR_VERSION
||
141 (minor
< MINOR_VERSION_MIN
||
142 minor
> MINOR_VERSION_MAX
))
144 munmap (buffer
, st
.st_size
);
149 cache
= (XdgMimeCache
*) malloc (sizeof (XdgMimeCache
));
150 cache
->minor
= minor
;
151 cache
->ref_count
= 1;
152 cache
->buffer
= buffer
;
153 cache
->size
= st
.st_size
;
159 #endif /* HAVE_MMAP */
165 cache_magic_matchlet_compare_to_data (XdgMimeCache
*cache
,
170 xdg_uint32_t range_start
= GET_UINT32 (cache
->buffer
, offset
);
171 xdg_uint32_t range_length
= GET_UINT32 (cache
->buffer
, offset
+ 4);
172 xdg_uint32_t data_length
= GET_UINT32 (cache
->buffer
, offset
+ 12);
173 xdg_uint32_t data_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16);
174 xdg_uint32_t mask_offset
= GET_UINT32 (cache
->buffer
, offset
+ 20);
178 for (i
= range_start
; i
< range_start
+ range_length
; i
++)
180 int valid_matchlet
= TRUE
;
182 if (i
+ data_length
> len
)
187 for (j
= 0; j
< data_length
; j
++)
189 if ((((unsigned char *)cache
->buffer
)[data_offset
+ j
] & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]) !=
190 ((((unsigned char *) data
)[j
+ i
]) & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]))
192 valid_matchlet
= FALSE
;
199 valid_matchlet
= memcmp(cache
->buffer
+ data_offset
, data
+ i
, data_length
) == 0;
210 cache_magic_matchlet_compare (XdgMimeCache
*cache
,
215 xdg_uint32_t n_children
= GET_UINT32 (cache
->buffer
, offset
+ 24);
216 xdg_uint32_t child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 28);
220 if (cache_magic_matchlet_compare_to_data (cache
, offset
, data
, len
))
225 for (i
= 0; i
< n_children
; i
++)
227 if (cache_magic_matchlet_compare (cache
, child_offset
+ 32 * i
,
237 cache_magic_compare_to_data (XdgMimeCache
*cache
,
243 xdg_uint32_t priority
= GET_UINT32 (cache
->buffer
, offset
);
244 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
245 xdg_uint32_t n_matchlets
= GET_UINT32 (cache
->buffer
, offset
+ 8);
246 xdg_uint32_t matchlet_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
250 for (i
= 0; i
< n_matchlets
; i
++)
252 if (cache_magic_matchlet_compare (cache
, matchlet_offset
+ i
* 32,
257 return cache
->buffer
+ mimetype_offset
;
265 cache_magic_lookup_data (XdgMimeCache
*cache
,
269 const char *mime_types
[],
272 xdg_uint32_t list_offset
;
273 xdg_uint32_t n_entries
;
280 list_offset
= GET_UINT32 (cache
->buffer
, 24);
281 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
282 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 8);
284 for (j
= 0; j
< n_entries
; j
++)
288 match
= cache_magic_compare_to_data (cache
, offset
+ 16 * j
,
294 xdg_uint32_t mimetype_offset
;
295 const char *non_match
;
297 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * j
+ 4);
298 non_match
= cache
->buffer
+ mimetype_offset
;
300 for (n
= 0; n
< n_mime_types
; n
++)
303 _xdg_mime_mime_type_equal (mime_types
[n
], non_match
))
304 mime_types
[n
] = NULL
;
313 cache_alias_lookup (const char *alias
)
316 int i
, min
, max
, mid
, cmp
;
318 for (i
= 0; _caches
[i
]; i
++)
320 XdgMimeCache
*cache
= _caches
[i
];
321 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 4);
322 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
329 mid
= (min
+ max
) / 2;
331 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
332 ptr
= cache
->buffer
+ offset
;
333 cmp
= strcmp (ptr
, alias
);
341 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
342 return cache
->buffer
+ offset
;
356 cache_glob_lookup_literal (const char *file_name
,
357 const char *mime_types
[],
359 int case_sensitive_check
)
362 int i
, min
, max
, mid
, cmp
;
364 for (i
= 0; _caches
[i
]; i
++)
366 XdgMimeCache
*cache
= _caches
[i
];
367 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 12);
368 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
375 mid
= (min
+ max
) / 2;
377 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * mid
);
378 ptr
= cache
->buffer
+ offset
;
379 cmp
= strcmp (ptr
, file_name
);
387 int weight
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * mid
+ 8);
388 int case_sensitive
= weight
& 0x100;
389 weight
= weight
& 0xff;
391 if (case_sensitive_check
|| !case_sensitive
)
393 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * mid
+ 4);
394 mime_types
[0] = (const char *)(cache
->buffer
+ offset
);
407 cache_glob_lookup_fnmatch (const char *file_name
,
408 MimeWeight mime_types
[],
410 int case_sensitive_check
)
412 const char *mime_type
;
418 for (i
= 0; _caches
[i
]; i
++)
420 XdgMimeCache
*cache
= _caches
[i
];
422 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 20);
423 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
425 for (j
= 0; j
< n_entries
&& n
< n_mime_types
; j
++)
427 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
);
428 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
+ 4);
429 int weight
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
+ 8);
430 int case_sensitive
= weight
& 0x100;
431 weight
= weight
& 0xff;
432 ptr
= cache
->buffer
+ offset
;
433 mime_type
= cache
->buffer
+ mimetype_offset
;
434 if (case_sensitive_check
|| !case_sensitive
)
436 /* FIXME: Not UTF-8 safe */
437 if (fnmatch (ptr
, file_name
, 0) == 0)
439 mime_types
[n
].mime
= mime_type
;
440 mime_types
[n
].weight
= weight
;
454 cache_glob_node_lookup_suffix (XdgMimeCache
*cache
,
455 xdg_uint32_t n_entries
,
457 const char *file_name
,
459 int case_sensitive_check
,
460 MimeWeight mime_types
[],
463 xdg_unichar_t character
;
464 xdg_unichar_t match_char
;
465 xdg_uint32_t mimetype_offset
;
466 xdg_uint32_t n_children
;
467 xdg_uint32_t child_offset
;
471 int min
, max
, mid
, n
, i
;
473 character
= file_name
[len
- 1];
475 assert (character
!= 0);
481 mid
= (min
+ max
) / 2;
482 match_char
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
);
483 if (match_char
< character
)
485 else if (match_char
> character
)
491 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
+ 4);
492 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
+ 8);
496 n
= cache_glob_node_lookup_suffix (cache
,
497 n_children
, child_offset
,
499 case_sensitive_check
,
506 while (n
< n_mime_types
&& i
< n_children
)
508 match_char
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
);
512 mimetype_offset
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
+ 4);
513 weight
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
+ 8);
514 case_sensitive
= weight
& 0x100;
515 weight
= weight
& 0xff;
517 if (case_sensitive_check
|| !case_sensitive
)
519 mime_types
[n
].mime
= cache
->buffer
+ mimetype_offset
;
520 mime_types
[n
].weight
= weight
;
533 cache_glob_lookup_suffix (const char *file_name
,
536 MimeWeight mime_types
[],
541 for (i
= 0; _caches
[i
]; i
++)
543 XdgMimeCache
*cache
= _caches
[i
];
545 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
546 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
547 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
549 n
= cache_glob_node_lookup_suffix (cache
,
562 static int compare_mime_weight (const void *a
, const void *b
)
564 const MimeWeight
*aa
= (const MimeWeight
*)a
;
565 const MimeWeight
*bb
= (const MimeWeight
*)b
;
567 return bb
->weight
- aa
->weight
;
570 #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
572 ascii_tolower (const char *str
)
576 lower
= strdup (str
);
581 *p
++ = ISUPPER (c
) ? c
- 'A' + 'a' : c
;
587 cache_glob_lookup_file_name (const char *file_name
,
588 const char *mime_types
[],
592 MimeWeight mimes
[10];
598 assert (file_name
!= NULL
&& n_mime_types
> 0);
600 /* First, check the literals */
602 lower_case
= ascii_tolower (file_name
);
604 n
= cache_glob_lookup_literal (lower_case
, mime_types
, n_mime_types
, FALSE
);
611 n
= cache_glob_lookup_literal (file_name
, mime_types
, n_mime_types
, TRUE
);
618 len
= strlen (file_name
);
619 n
= cache_glob_lookup_suffix (lower_case
, len
, FALSE
, mimes
, n_mimes
);
621 n
= cache_glob_lookup_suffix (file_name
, len
, TRUE
, mimes
, n_mimes
);
623 /* Last, try fnmatch */
625 n
= cache_glob_lookup_fnmatch (lower_case
, mimes
, n_mimes
, FALSE
);
627 n
= cache_glob_lookup_fnmatch (file_name
, mimes
, n_mimes
, TRUE
);
631 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
633 if (n_mime_types
< n
)
636 for (i
= 0; i
< n
; i
++)
637 mime_types
[i
] = mimes
[i
].mime
;
643 _xdg_mime_cache_get_max_buffer_extents (void)
646 xdg_uint32_t max_extent
;
650 for (i
= 0; _caches
[i
]; i
++)
652 XdgMimeCache
*cache
= _caches
[i
];
654 offset
= GET_UINT32 (cache
->buffer
, 24);
655 max_extent
= MAX (max_extent
, GET_UINT32 (cache
->buffer
, offset
+ 4));
662 cache_get_mime_type_for_data (const void *data
,
665 const char *mime_types
[],
668 const char *mime_type
;
673 for (i
= 0; _caches
[i
]; i
++)
675 XdgMimeCache
*cache
= _caches
[i
];
680 match
= cache_magic_lookup_data (cache
, data
, len
, &prio
,
681 mime_types
, n_mime_types
);
690 *result_prio
= priority
;
694 /* Pick glob-result R where mime_type inherits from R */
695 for (n
= 0; n
< n_mime_types
; n
++)
697 if (mime_types
[n
] && _xdg_mime_cache_mime_type_subclass(mime_types
[n
], mime_type
))
698 return mime_types
[n
];
701 /* Return magic match */
705 /* Pick first glob result, as fallback */
706 for (n
= 0; n
< n_mime_types
; n
++)
709 return mime_types
[n
];
716 _xdg_mime_cache_get_mime_type_for_data (const void *data
,
720 return cache_get_mime_type_for_data (data
, len
, result_prio
, NULL
, 0);
724 _xdg_mime_cache_get_mime_type_for_file (const char *file_name
,
725 struct stat
*statbuf
)
727 const char *mime_type
;
728 const char *mime_types
[10];
734 const char *base_name
;
737 if (file_name
== NULL
)
740 if (! _xdg_utf8_validate (file_name
))
743 base_name
= _xdg_get_base_name (file_name
);
744 n
= cache_glob_lookup_file_name (base_name
, mime_types
, 10);
747 return mime_types
[0];
751 if (stat (file_name
, &buf
) != 0)
752 return XDG_MIME_TYPE_UNKNOWN
;
757 if (statbuf
->st_size
== 0)
758 return XDG_MIME_TYPE_EMPTY
;
760 if (!S_ISREG (statbuf
->st_mode
))
761 return XDG_MIME_TYPE_UNKNOWN
;
763 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
764 * be large and need getting from a stream instead of just reading it all
766 max_extent
= _xdg_mime_cache_get_max_buffer_extents ();
767 data
= malloc (max_extent
);
769 return XDG_MIME_TYPE_UNKNOWN
;
771 file
= fopen (file_name
, "r");
775 return XDG_MIME_TYPE_UNKNOWN
;
778 bytes_read
= fread (data
, 1, max_extent
, file
);
783 return XDG_MIME_TYPE_UNKNOWN
;
786 mime_type
= cache_get_mime_type_for_data (data
, bytes_read
, NULL
,
790 mime_type
= _xdg_binary_or_text_fallback(data
, bytes_read
);
799 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name
)
801 const char *mime_type
;
803 if (cache_glob_lookup_file_name (file_name
, &mime_type
, 1))
806 return XDG_MIME_TYPE_UNKNOWN
;
810 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name
,
811 const char *mime_types
[],
814 return cache_glob_lookup_file_name (file_name
, mime_types
, n_mime_types
);
819 is_super_type (const char *mime
)
824 length
= strlen (mime
);
825 type
= &(mime
[length
- 2]);
827 if (strcmp (type
, "/*") == 0)
835 _xdg_mime_cache_mime_type_subclass (const char *mime
,
838 const char *umime
, *ubase
;
840 int i
, j
, min
, max
, med
, cmp
;
842 umime
= _xdg_mime_cache_unalias_mime_type (mime
);
843 ubase
= _xdg_mime_cache_unalias_mime_type (base
);
845 if (strcmp (umime
, ubase
) == 0)
848 /* We really want to handle text/ * in GtkFileFilter, so we just
849 * turn on the supertype matching
852 /* Handle supertypes */
853 if (is_super_type (ubase
) &&
854 xdg_mime_media_type_equal (umime
, ubase
))
858 /* Handle special cases text/plain and application/octet-stream */
859 if (strcmp (ubase
, "text/plain") == 0 &&
860 strncmp (umime
, "text/", 5) == 0)
863 if (strcmp (ubase
, "application/octet-stream") == 0)
866 for (i
= 0; _caches
[i
]; i
++)
868 XdgMimeCache
*cache
= _caches
[i
];
870 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
871 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
872 xdg_uint32_t offset
, n_parents
, parent_offset
;
880 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
);
881 cmp
= strcmp (cache
->buffer
+ offset
, umime
);
888 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
+ 4);
889 n_parents
= GET_UINT32 (cache
->buffer
, offset
);
891 for (j
= 0; j
< n_parents
; j
++)
893 parent_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4 + 4 * j
);
894 if (_xdg_mime_cache_mime_type_subclass (cache
->buffer
+ parent_offset
, ubase
))
907 _xdg_mime_cache_unalias_mime_type (const char *mime
)
911 lookup
= cache_alias_lookup (mime
);
920 _xdg_mime_cache_list_mime_parents (const char *mime
)
923 char *all_parents
[128]; /* we'll stop at 128 */
926 mime
= xdg_mime_unalias_mime_type (mime
);
929 for (i
= 0; _caches
[i
]; i
++)
931 XdgMimeCache
*cache
= _caches
[i
];
933 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
934 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
936 for (j
= 0; j
< n_entries
; j
++)
938 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
);
939 xdg_uint32_t parents_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
+ 4);
941 if (strcmp (cache
->buffer
+ mimetype_offset
, mime
) == 0)
943 xdg_uint32_t parent_mime_offset
;
944 xdg_uint32_t n_parents
= GET_UINT32 (cache
->buffer
, parents_offset
);
946 for (k
= 0; k
< n_parents
&& p
< 127; k
++)
948 parent_mime_offset
= GET_UINT32 (cache
->buffer
, parents_offset
+ 4 + 4 * k
);
950 /* Don't add same parent multiple times.
951 * This can happen for instance if the same type is listed in multiple directories
953 for (l
= 0; l
< p
; l
++)
955 if (strcmp (all_parents
[l
], cache
->buffer
+ parent_mime_offset
) == 0)
960 all_parents
[p
++] = cache
->buffer
+ parent_mime_offset
;
967 all_parents
[p
++] = NULL
;
969 result
= (char **) malloc (p
* sizeof (char *));
970 memcpy (result
, all_parents
, p
* sizeof (char *));
976 cache_lookup_icon (const char *mime
, int header
)
979 int i
, min
, max
, mid
, cmp
;
981 for (i
= 0; _caches
[i
]; i
++)
983 XdgMimeCache
*cache
= _caches
[i
];
984 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, header
);
985 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
992 mid
= (min
+ max
) / 2;
994 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
995 ptr
= cache
->buffer
+ offset
;
996 cmp
= strcmp (ptr
, mime
);
1004 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
1005 return cache
->buffer
+ offset
;
1014 _xdg_mime_cache_get_generic_icon (const char *mime
)
1016 return cache_lookup_icon (mime
, 36);
1020 _xdg_mime_cache_get_icon (const char *mime
)
1022 return cache_lookup_icon (mime
, 32);
1026 dump_glob_node (XdgMimeCache
*cache
,
1027 xdg_uint32_t offset
,
1030 xdg_unichar_t character
;
1031 xdg_uint32_t mime_offset
;
1032 xdg_uint32_t n_children
;
1033 xdg_uint32_t child_offset
;
1036 character
= GET_UINT32 (cache
->buffer
, offset
);
1037 mime_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
1038 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 8);
1039 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
1040 for (i
= 0; i
< depth
; i
++)
1042 printf ("%c", character
);
1044 printf (" - %s", cache
->buffer
+ mime_offset
);
1048 for (i
= 0; i
< n_children
; i
++)
1049 dump_glob_node (cache
, child_offset
+ 20 * i
, depth
+ 1);
1054 _xdg_mime_cache_glob_dump (void)
1057 for (i
= 0; _caches
[i
]; i
++)
1059 XdgMimeCache
*cache
= _caches
[i
];
1060 xdg_uint32_t list_offset
;
1061 xdg_uint32_t n_entries
;
1062 xdg_uint32_t offset
;
1063 list_offset
= GET_UINT32 (cache
->buffer
, 16);
1064 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
1065 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
1066 for (j
= 0; j
< n_entries
; j
++)
1067 dump_glob_node (cache
, offset
+ 20 * j
, 0);