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
));
148 cache
->minor
= minor
;
149 cache
->ref_count
= 1;
150 cache
->buffer
= buffer
;
151 cache
->size
= st
.st_size
;
157 #endif /* HAVE_MMAP */
163 cache_magic_matchlet_compare_to_data (XdgMimeCache
*cache
,
168 xdg_uint32_t range_start
= GET_UINT32 (cache
->buffer
, offset
);
169 xdg_uint32_t range_length
= GET_UINT32 (cache
->buffer
, offset
+ 4);
170 xdg_uint32_t data_length
= GET_UINT32 (cache
->buffer
, offset
+ 12);
171 xdg_uint32_t data_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16);
172 xdg_uint32_t mask_offset
= GET_UINT32 (cache
->buffer
, offset
+ 20);
176 for (i
= range_start
; i
<= range_start
+ range_length
; i
++)
178 int valid_matchlet
= TRUE
;
180 if (i
+ data_length
> len
)
185 for (j
= 0; j
< data_length
; j
++)
187 if ((((unsigned char *)cache
->buffer
)[data_offset
+ j
] & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]) !=
188 ((((unsigned char *) data
)[j
+ i
]) & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]))
190 valid_matchlet
= FALSE
;
197 for (j
= 0; j
< data_length
; j
++)
199 if (((unsigned char *)cache
->buffer
)[data_offset
+ j
] != ((unsigned char *) data
)[j
+ i
])
201 valid_matchlet
= FALSE
;
215 cache_magic_matchlet_compare (XdgMimeCache
*cache
,
220 xdg_uint32_t n_children
= GET_UINT32 (cache
->buffer
, offset
+ 24);
221 xdg_uint32_t child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 28);
225 if (cache_magic_matchlet_compare_to_data (cache
, offset
, data
, len
))
230 for (i
= 0; i
< n_children
; i
++)
232 if (cache_magic_matchlet_compare (cache
, child_offset
+ 32 * i
,
242 cache_magic_compare_to_data (XdgMimeCache
*cache
,
248 xdg_uint32_t priority
= GET_UINT32 (cache
->buffer
, offset
);
249 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
250 xdg_uint32_t n_matchlets
= GET_UINT32 (cache
->buffer
, offset
+ 8);
251 xdg_uint32_t matchlet_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
255 for (i
= 0; i
< n_matchlets
; i
++)
257 if (cache_magic_matchlet_compare (cache
, matchlet_offset
+ i
* 32,
262 return cache
->buffer
+ mimetype_offset
;
270 cache_magic_lookup_data (XdgMimeCache
*cache
,
274 const char *mime_types
[],
277 xdg_uint32_t list_offset
;
278 xdg_uint32_t n_entries
;
285 list_offset
= GET_UINT32 (cache
->buffer
, 24);
286 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
287 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 8);
289 for (j
= 0; j
< n_entries
; j
++)
293 match
= cache_magic_compare_to_data (cache
, offset
+ 16 * j
,
299 xdg_uint32_t mimetype_offset
;
300 const char *non_match
;
302 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * j
+ 4);
303 non_match
= cache
->buffer
+ mimetype_offset
;
305 for (n
= 0; n
< n_mime_types
; n
++)
308 _xdg_mime_mime_type_equal (mime_types
[n
], non_match
))
309 mime_types
[n
] = NULL
;
318 cache_alias_lookup (const char *alias
)
321 int i
, min
, max
, mid
, cmp
;
323 for (i
= 0; _caches
[i
]; i
++)
325 XdgMimeCache
*cache
= _caches
[i
];
326 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 4);
327 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
334 mid
= (min
+ max
) / 2;
336 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
337 ptr
= cache
->buffer
+ offset
;
338 cmp
= strcmp (ptr
, alias
);
346 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
347 return cache
->buffer
+ offset
;
361 cache_glob_lookup_literal (const char *file_name
,
362 const char *mime_types
[],
366 int i
, min
, max
, mid
, cmp
;
368 for (i
= 0; _caches
[i
]; i
++)
370 XdgMimeCache
*cache
= _caches
[i
];
371 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 12);
372 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
379 mid
= (min
+ max
) / 2;
381 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * mid
);
382 ptr
= cache
->buffer
+ offset
;
383 cmp
= strcmp (ptr
, file_name
);
391 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * mid
+ 4);
392 mime_types
[0] = (const char *)(cache
->buffer
+ offset
);
403 cache_glob_lookup_fnmatch (const char *file_name
,
404 MimeWeight mime_types
[],
407 const char *mime_type
;
413 for (i
= 0; _caches
[i
]; i
++)
415 XdgMimeCache
*cache
= _caches
[i
];
417 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 20);
418 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
420 for (j
= 0; j
< n_entries
&& n
< n_mime_types
; j
++)
422 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
);
423 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
+ 4);
424 int weight
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 12 * j
+ 8);
425 ptr
= cache
->buffer
+ offset
;
426 mime_type
= cache
->buffer
+ mimetype_offset
;
428 /* FIXME: Not UTF-8 safe */
429 if (fnmatch (ptr
, file_name
, 0) == 0)
431 mime_types
[n
].mime
= mime_type
;
432 mime_types
[n
].weight
= weight
;
445 cache_glob_node_lookup_suffix (XdgMimeCache
*cache
,
446 xdg_uint32_t n_entries
,
448 xdg_unichar_t
*file_name
,
451 MimeWeight mime_types
[],
454 xdg_unichar_t character
;
455 xdg_unichar_t match_char
;
456 xdg_uint32_t mimetype_offset
;
457 xdg_uint32_t n_children
;
458 xdg_uint32_t child_offset
;
461 int min
, max
, mid
, n
, i
;
463 character
= file_name
[len
- 1];
465 character
= _xdg_ucs4_to_lower (character
);
467 assert (character
!= 0);
473 mid
= (min
+ max
) / 2;
474 match_char
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
);
475 if (match_char
< character
)
477 else if (match_char
> character
)
483 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
+ 4);
484 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12 * mid
+ 8);
488 n
= cache_glob_node_lookup_suffix (cache
,
489 n_children
, child_offset
,
498 while (n
< n_mime_types
&& i
< n_children
)
500 match_char
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
);
504 mimetype_offset
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
+ 4);
505 weight
= GET_UINT32 (cache
->buffer
, child_offset
+ 12 * i
+ 8);
507 mime_types
[n
].mime
= cache
->buffer
+ mimetype_offset
;
508 mime_types
[n
].weight
= weight
;
520 cache_glob_lookup_suffix (xdg_unichar_t
*file_name
,
523 MimeWeight mime_types
[],
528 for (i
= 0; _caches
[i
]; i
++)
530 XdgMimeCache
*cache
= _caches
[i
];
532 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
533 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
534 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
536 n
= cache_glob_node_lookup_suffix (cache
,
549 static int compare_mime_weight (const void *a
, const void *b
)
551 const MimeWeight
*aa
= (const MimeWeight
*)a
;
552 const MimeWeight
*bb
= (const MimeWeight
*)b
;
554 return aa
->weight
- bb
->weight
;
558 cache_glob_lookup_file_name (const char *file_name
,
559 const char *mime_types
[],
563 MimeWeight mimes
[10];
569 assert (file_name
!= NULL
&& n_mime_types
> 0);
571 /* First, check the literals */
572 n
= cache_glob_lookup_literal (file_name
, mime_types
, n_mime_types
);
576 ucs4
= _xdg_convert_to_ucs4 (file_name
, &len
);
577 n
= cache_glob_lookup_suffix (ucs4
, len
, FALSE
, mimes
, n_mimes
);
580 n
= cache_glob_lookup_suffix (ucs4
, len
, TRUE
, mimes
, n_mimes
);
583 /* Last, try fnmatch */
585 n
= cache_glob_lookup_fnmatch (file_name
, mimes
, n_mimes
);
587 qsort (mimes
, n
, sizeof (MimeWeight
), compare_mime_weight
);
589 if (n_mime_types
< n
)
592 for (i
= 0; i
< n
; i
++)
593 mime_types
[i
] = mimes
[i
].mime
;
599 _xdg_mime_cache_get_max_buffer_extents (void)
602 xdg_uint32_t max_extent
;
606 for (i
= 0; _caches
[i
]; i
++)
608 XdgMimeCache
*cache
= _caches
[i
];
610 offset
= GET_UINT32 (cache
->buffer
, 24);
611 max_extent
= MAX (max_extent
, GET_UINT32 (cache
->buffer
, offset
+ 4));
618 cache_get_mime_type_for_data (const void *data
,
621 const char *mime_types
[],
624 const char *mime_type
;
629 for (i
= 0; _caches
[i
]; i
++)
631 XdgMimeCache
*cache
= _caches
[i
];
636 match
= cache_magic_lookup_data (cache
, data
, len
, &prio
,
637 mime_types
, n_mime_types
);
646 *result_prio
= priority
;
651 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
,
666 return cache_get_mime_type_for_data (data
, len
, result_prio
, NULL
, 0);
670 _xdg_mime_cache_get_mime_type_for_file (const char *file_name
,
671 struct stat
*statbuf
)
673 const char *mime_type
;
674 const char *mime_types
[10];
680 const char *base_name
;
683 if (file_name
== NULL
)
686 if (! _xdg_utf8_validate (file_name
))
689 base_name
= _xdg_get_base_name (file_name
);
690 n
= cache_glob_lookup_file_name (base_name
, mime_types
, 10);
693 return mime_types
[0];
697 if (stat (file_name
, &buf
) != 0)
698 return XDG_MIME_TYPE_UNKNOWN
;
703 if (!S_ISREG (statbuf
->st_mode
))
704 return XDG_MIME_TYPE_UNKNOWN
;
706 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
707 * be large and need getting from a stream instead of just reading it all
709 max_extent
= _xdg_mime_cache_get_max_buffer_extents ();
710 data
= malloc (max_extent
);
712 return XDG_MIME_TYPE_UNKNOWN
;
714 file
= fopen (file_name
, "r");
718 return XDG_MIME_TYPE_UNKNOWN
;
721 bytes_read
= fread (data
, 1, max_extent
, file
);
726 return XDG_MIME_TYPE_UNKNOWN
;
729 mime_type
= cache_get_mime_type_for_data (data
, bytes_read
, NULL
,
739 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name
)
741 const char *mime_type
;
743 if (cache_glob_lookup_file_name (file_name
, &mime_type
, 1))
746 return XDG_MIME_TYPE_UNKNOWN
;
750 _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name
,
751 const char *mime_types
[],
754 return cache_glob_lookup_file_name (file_name
, mime_types
, n_mime_types
);
759 is_super_type (const char *mime
)
764 length
= strlen (mime
);
765 type
= &(mime
[length
- 2]);
767 if (strcmp (type
, "/*") == 0)
775 _xdg_mime_cache_mime_type_subclass (const char *mime
,
778 const char *umime
, *ubase
;
780 int i
, j
, min
, max
, med
, cmp
;
782 umime
= _xdg_mime_cache_unalias_mime_type (mime
);
783 ubase
= _xdg_mime_cache_unalias_mime_type (base
);
785 if (strcmp (umime
, ubase
) == 0)
788 /* We really want to handle text/ * in GtkFileFilter, so we just
789 * turn on the supertype matching
792 /* Handle supertypes */
793 if (is_super_type (ubase
) &&
794 xdg_mime_media_type_equal (umime
, ubase
))
798 /* Handle special cases text/plain and application/octet-stream */
799 if (strcmp (ubase
, "text/plain") == 0 &&
800 strncmp (umime
, "text/", 5) == 0)
803 if (strcmp (ubase
, "application/octet-stream") == 0)
806 for (i
= 0; _caches
[i
]; i
++)
808 XdgMimeCache
*cache
= _caches
[i
];
810 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
811 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
812 xdg_uint32_t offset
, n_parents
, parent_offset
;
820 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
);
821 cmp
= strcmp (cache
->buffer
+ offset
, umime
);
828 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
+ 4);
829 n_parents
= GET_UINT32 (cache
->buffer
, offset
);
831 for (j
= 0; j
< n_parents
; j
++)
833 parent_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4 + 4 * j
);
834 if (_xdg_mime_cache_mime_type_subclass (cache
->buffer
+ parent_offset
, ubase
))
847 _xdg_mime_cache_unalias_mime_type (const char *mime
)
851 lookup
= cache_alias_lookup (mime
);
860 _xdg_mime_cache_list_mime_parents (const char *mime
)
863 char *all_parents
[128]; /* we'll stop at 128 */
866 mime
= xdg_mime_unalias_mime_type (mime
);
869 for (i
= 0; _caches
[i
]; i
++)
871 XdgMimeCache
*cache
= _caches
[i
];
873 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
874 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
876 for (j
= 0; j
< n_entries
; j
++)
878 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
);
879 xdg_uint32_t parents_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
+ 4);
881 if (strcmp (cache
->buffer
+ mimetype_offset
, mime
) == 0)
883 xdg_uint32_t parent_mime_offset
;
884 xdg_uint32_t n_parents
= GET_UINT32 (cache
->buffer
, parents_offset
);
886 for (k
= 0; k
< n_parents
&& p
< 127; k
++)
888 parent_mime_offset
= GET_UINT32 (cache
->buffer
, parents_offset
+ 4 + 4 * k
);
890 /* Don't add same parent multiple times.
891 * This can happen for instance if the same type is listed in multiple directories
893 for (l
= 0; l
< p
; l
++)
895 if (strcmp (all_parents
[l
], cache
->buffer
+ parent_mime_offset
) == 0)
900 all_parents
[p
++] = cache
->buffer
+ parent_mime_offset
;
907 all_parents
[p
++] = NULL
;
909 result
= (char **) malloc (p
* sizeof (char *));
910 memcpy (result
, all_parents
, p
* sizeof (char *));
916 cache_lookup_icon (const char *mime
, int header
)
919 int i
, min
, max
, mid
, cmp
;
921 for (i
= 0; _caches
[i
]; i
++)
923 XdgMimeCache
*cache
= _caches
[i
];
924 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, header
);
925 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
932 mid
= (min
+ max
) / 2;
934 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
935 ptr
= cache
->buffer
+ offset
;
936 cmp
= strcmp (ptr
, mime
);
944 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
945 return cache
->buffer
+ offset
;
954 _xdg_mime_cache_get_generic_icon (const char *mime
)
956 return cache_lookup_icon (mime
, 36);
960 _xdg_mime_cache_get_icon (const char *mime
)
964 icon
= cache_lookup_icon (mime
, 32);
967 icon
= _xdg_mime_cache_get_generic_icon (mime
);
973 dump_glob_node (XdgMimeCache
*cache
,
977 xdg_unichar_t character
;
978 xdg_uint32_t mime_offset
;
979 xdg_uint32_t n_children
;
980 xdg_uint32_t child_offset
;
983 character
= GET_UINT32 (cache
->buffer
, offset
);
984 mime_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
985 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 8);
986 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
987 for (i
= 0; i
< depth
; i
++)
989 printf ("%c", character
);
991 printf (" - %s", cache
->buffer
+ mime_offset
);
995 for (i
= 0; i
< n_children
; i
++)
996 dump_glob_node (cache
, child_offset
+ 20 * i
, depth
+ 1);
1001 _xdg_mime_cache_glob_dump (void)
1004 for (i
= 0; _caches
[i
]; i
++)
1006 XdgMimeCache
*cache
= _caches
[i
];
1007 xdg_uint32_t list_offset
;
1008 xdg_uint32_t n_entries
;
1009 xdg_uint32_t offset
;
1010 list_offset
= GET_UINT32 (cache
->buffer
, 16);
1011 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
1012 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
1013 for (j
= 0; j
< n_entries
; j
++)
1014 dump_glob_node (cache
, offset
+ 20 * j
, 0);