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 0
85 #define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
86 #define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
89 _xdg_mime_cache_ref (XdgMimeCache
*cache
)
96 _xdg_mime_cache_unref (XdgMimeCache
*cache
)
100 if (cache
->ref_count
== 0)
103 munmap (cache
->buffer
, cache
->size
);
110 _xdg_mime_cache_new_from_file (const char *file_name
)
112 XdgMimeCache
*cache
= NULL
;
119 /* Open the file and map it into memory */
120 fd
= open (file_name
, O_RDONLY
|_O_BINARY
, 0);
125 if (fstat (fd
, &st
) < 0 || st
.st_size
< 4)
128 buffer
= (char *) mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
130 if (buffer
== MAP_FAILED
)
134 if (GET_UINT16 (buffer
, 0) != MAJOR_VERSION
||
135 GET_UINT16 (buffer
, 2) != MINOR_VERSION
)
137 munmap (buffer
, st
.st_size
);
142 cache
= (XdgMimeCache
*) malloc (sizeof (XdgMimeCache
));
143 cache
->ref_count
= 1;
144 cache
->buffer
= buffer
;
145 cache
->size
= st
.st_size
;
151 #endif /* HAVE_MMAP */
157 cache_magic_matchlet_compare_to_data (XdgMimeCache
*cache
,
162 xdg_uint32_t range_start
= GET_UINT32 (cache
->buffer
, offset
);
163 xdg_uint32_t range_length
= GET_UINT32 (cache
->buffer
, offset
+ 4);
164 xdg_uint32_t data_length
= GET_UINT32 (cache
->buffer
, offset
+ 12);
165 xdg_uint32_t data_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16);
166 xdg_uint32_t mask_offset
= GET_UINT32 (cache
->buffer
, offset
+ 20);
170 for (i
= range_start
; i
<= range_start
+ range_length
; i
++)
172 int valid_matchlet
= TRUE
;
174 if (i
+ data_length
> len
)
179 for (j
= 0; j
< data_length
; j
++)
181 if ((((unsigned char *)cache
->buffer
)[data_offset
+ j
] & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]) !=
182 ((((unsigned char *) data
)[j
+ i
]) & ((unsigned char *)cache
->buffer
)[mask_offset
+ j
]))
184 valid_matchlet
= FALSE
;
191 for (j
= 0; j
< data_length
; j
++)
193 if (((unsigned char *)cache
->buffer
)[data_offset
+ j
] != ((unsigned char *) data
)[j
+ i
])
195 valid_matchlet
= FALSE
;
209 cache_magic_matchlet_compare (XdgMimeCache
*cache
,
214 xdg_uint32_t n_children
= GET_UINT32 (cache
->buffer
, offset
+ 24);
215 xdg_uint32_t child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 28);
219 if (cache_magic_matchlet_compare_to_data (cache
, offset
, data
, len
))
224 for (i
= 0; i
< n_children
; i
++)
226 if (cache_magic_matchlet_compare (cache
, child_offset
+ 32 * i
,
236 cache_magic_compare_to_data (XdgMimeCache
*cache
,
242 xdg_uint32_t priority
= GET_UINT32 (cache
->buffer
, offset
);
243 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
244 xdg_uint32_t n_matchlets
= GET_UINT32 (cache
->buffer
, offset
+ 8);
245 xdg_uint32_t matchlet_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
249 for (i
= 0; i
< n_matchlets
; i
++)
251 if (cache_magic_matchlet_compare (cache
, matchlet_offset
+ i
* 32,
256 return cache
->buffer
+ mimetype_offset
;
264 cache_magic_lookup_data (XdgMimeCache
*cache
,
268 const char *mime_types
[],
271 xdg_uint32_t list_offset
;
272 xdg_uint32_t n_entries
;
279 list_offset
= GET_UINT32 (cache
->buffer
, 24);
280 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
281 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 8);
283 for (j
= 0; j
< n_entries
; j
++)
287 match
= cache_magic_compare_to_data (cache
, offset
+ 16 * j
,
293 xdg_uint32_t mimetype_offset
;
294 const char *non_match
;
296 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * j
+ 4);
297 non_match
= cache
->buffer
+ mimetype_offset
;
299 for (n
= 0; n
< n_mime_types
; n
++)
302 xdg_mime_mime_type_equal (mime_types
[n
], non_match
))
303 mime_types
[n
] = NULL
;
312 cache_alias_lookup (const char *alias
)
315 int i
, min
, max
, mid
, cmp
;
317 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
319 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
320 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 4);
321 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
328 mid
= (min
+ max
) / 2;
330 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
331 ptr
= cache
->buffer
+ offset
;
332 cmp
= strcmp (ptr
, alias
);
340 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
341 return cache
->buffer
+ offset
;
350 cache_glob_lookup_literal (const char *file_name
,
351 const char *mime_types
[],
355 int i
, min
, max
, mid
, cmp
;
357 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
359 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
360 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 12);
361 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
368 mid
= (min
+ max
) / 2;
370 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
371 ptr
= cache
->buffer
+ offset
;
372 cmp
= strcmp (ptr
, file_name
);
380 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
381 mime_types
[0] = (const char *)(cache
->buffer
+ offset
);
392 cache_glob_lookup_fnmatch (const char *file_name
,
393 const char *mime_types
[],
396 const char *mime_type
;
402 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
404 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
406 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 20);
407 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
409 for (j
= 0; j
< n_entries
&& n
< n_mime_types
; j
++)
411 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
);
412 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
+ 4);
413 ptr
= cache
->buffer
+ offset
;
414 mime_type
= cache
->buffer
+ mimetype_offset
;
416 /* FIXME: Not UTF-8 safe */
417 if (fnmatch (ptr
, file_name
, 0) == 0)
418 mime_types
[n
++] = mime_type
;
429 cache_glob_node_lookup_suffix (XdgMimeCache
*cache
,
430 xdg_uint32_t n_entries
,
434 const char *mime_types
[],
437 xdg_unichar_t character
;
438 xdg_unichar_t match_char
;
439 xdg_uint32_t mimetype_offset
;
440 xdg_uint32_t n_children
;
441 xdg_uint32_t child_offset
;
443 int min
, max
, mid
, n
, i
;
445 character
= _xdg_utf8_to_ucs4 (suffix
);
447 character
= _xdg_ucs4_to_lower (character
);
453 mid
= (min
+ max
) / 2;
455 match_char
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
);
457 if (match_char
< character
)
459 else if (match_char
> character
)
463 suffix
= _xdg_utf8_next_char (suffix
);
466 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 4);
468 if (cache
->buffer
[mimetype_offset
])
469 mime_types
[n
++] = cache
->buffer
+ mimetype_offset
;
471 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 8);
472 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 12);
474 while (n
< n_mime_types
&& i
< n_children
)
476 match_char
= GET_UINT32 (cache
->buffer
, child_offset
+ 16 * i
);
477 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * i
+ 4);
481 mime_types
[n
++] = cache
->buffer
+ mimetype_offset
;
489 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 8);
490 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 12);
492 return cache_glob_node_lookup_suffix (cache
,
493 n_children
, child_offset
,
505 cache_glob_lookup_suffix (const char *suffix
,
507 const char *mime_types
[],
512 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
514 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
516 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
517 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
518 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
520 n
= cache_glob_node_lookup_suffix (cache
,
533 find_stopchars (char *stopchars
)
538 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
540 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
542 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
543 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
544 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
546 for (j
= 0; j
< n_entries
; j
++)
548 xdg_uint32_t match_char
= GET_UINT32 (cache
->buffer
, offset
);
550 if (match_char
< 128)
552 for (l
= 0; l
< k
; l
++)
553 if (stopchars
[l
] == match_char
)
557 stopchars
[k
] = (char) match_char
;
570 cache_glob_lookup_file_name (const char *file_name
,
571 const char *mime_types
[],
578 assert (file_name
!= NULL
);
580 /* First, check the literals */
581 n
= cache_glob_lookup_literal (file_name
, mime_types
, n_mime_types
);
585 find_stopchars (stopchars
);
587 /* Next, check suffixes */
588 ptr
= strpbrk (file_name
, stopchars
);
591 n
= cache_glob_lookup_suffix (ptr
, FALSE
, mime_types
, n_mime_types
);
595 n
= cache_glob_lookup_suffix (ptr
, TRUE
, mime_types
, n_mime_types
);
599 ptr
= strpbrk (ptr
+ 1, stopchars
);
602 /* Last, try fnmatch */
603 return cache_glob_lookup_fnmatch (file_name
, mime_types
, n_mime_types
);
607 _xdg_mime_cache_get_max_buffer_extents (void)
610 xdg_uint32_t max_extent
;
614 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
616 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
618 offset
= GET_UINT32 (cache
->buffer
, 24);
619 max_extent
= MAX (max_extent
, GET_UINT32 (cache
->buffer
, offset
+ 4));
626 cache_get_mime_type_for_data (const void *data
,
628 const char *mime_types
[],
631 const char *mime_type
;
636 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
638 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
643 match
= cache_magic_lookup_data (cache
, data
, len
, &prio
,
644 mime_types
, n_mime_types
);
655 for (n
= 0; n
< n_mime_types
; n
++)
658 return mime_types
[n
];
661 return XDG_MIME_TYPE_UNKNOWN
;
665 _xdg_mime_cache_get_mime_type_for_data (const void *data
,
668 return cache_get_mime_type_for_data (data
, len
, NULL
, 0);
672 _xdg_mime_cache_get_mime_type_for_file (const char *file_name
,
673 struct stat
*statbuf
)
675 const char *mime_type
;
676 const char *mime_types
[2];
682 const char *base_name
;
685 if (file_name
== NULL
)
688 if (! _xdg_utf8_validate (file_name
))
691 base_name
= _xdg_get_base_name (file_name
);
692 n
= cache_glob_lookup_file_name (base_name
, mime_types
, 2);
695 return mime_types
[0];
699 if (stat (file_name
, &buf
) != 0)
700 return XDG_MIME_TYPE_UNKNOWN
;
705 if (!S_ISREG (statbuf
->st_mode
))
706 return XDG_MIME_TYPE_UNKNOWN
;
708 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
709 * be large and need getting from a stream instead of just reading it all
711 max_extent
= _xdg_mime_cache_get_max_buffer_extents ();
712 data
= malloc (max_extent
);
714 return XDG_MIME_TYPE_UNKNOWN
;
716 file
= fopen (file_name
, "r");
720 return XDG_MIME_TYPE_UNKNOWN
;
723 bytes_read
= fread (data
, 1, max_extent
, file
);
728 return XDG_MIME_TYPE_UNKNOWN
;
731 mime_type
= cache_get_mime_type_for_data (data
, bytes_read
,
741 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name
)
743 const char *mime_types
[2];
745 if (cache_glob_lookup_file_name (file_name
, mime_types
, 2) == 1)
746 return mime_types
[0];
748 return XDG_MIME_TYPE_UNKNOWN
;
753 is_super_type (const char *mime
)
758 length
= strlen (mime
);
759 type
= &(mime
[length
- 2]);
761 if (strcmp (type
, "/*") == 0)
769 _xdg_mime_cache_mime_type_subclass (const char *mime
,
772 const char *umime
, *ubase
;
774 int i
, j
, min
, max
, med
, cmp
;
776 umime
= _xdg_mime_cache_unalias_mime_type (mime
);
777 ubase
= _xdg_mime_cache_unalias_mime_type (base
);
779 if (strcmp (umime
, ubase
) == 0)
782 /* We really want to handle text/ * in GtkFileFilter, so we just
783 * turn on the supertype matching
786 /* Handle supertypes */
787 if (is_super_type (ubase
) &&
788 xdg_mime_media_type_equal (umime
, ubase
))
792 /* Handle special cases text/plain and application/octet-stream */
793 if (strcmp (ubase
, "text/plain") == 0 &&
794 strncmp (umime
, "text/", 5) == 0)
797 if (strcmp (ubase
, "application/octet-stream") == 0)
800 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
802 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
804 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
805 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
806 xdg_uint32_t offset
, n_parents
, parent_offset
;
814 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
);
815 cmp
= strcmp (cache
->buffer
+ offset
, umime
);
822 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
+ 4);
823 n_parents
= GET_UINT32 (cache
->buffer
, offset
);
825 for (j
= 0; j
< n_parents
; j
++)
827 parent_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4 + 4 * j
);
828 if (_xdg_mime_cache_mime_type_subclass (cache
->buffer
+ parent_offset
, ubase
))
841 _xdg_mime_cache_unalias_mime_type (const char *mime
)
845 lookup
= cache_alias_lookup (mime
);
854 _xdg_mime_cache_list_mime_parents (const char *mime
)
857 char *all_parents
[128]; /* we'll stop at 128 */
860 mime
= xdg_mime_unalias_mime_type (mime
);
863 for (i
= 0; _xdg_mime_caches
[i
]; i
++)
865 XdgMimeCache
*cache
= _xdg_mime_caches
[i
];
867 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
868 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
870 for (j
= 0; j
< n_entries
; j
++)
872 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
);
873 xdg_uint32_t parents_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
+ 4);
875 if (strcmp (cache
->buffer
+ mimetype_offset
, mime
) == 0)
878 xdg_uint32_t parent_mime_offset
;
879 xdg_uint32_t n_parents
= GET_UINT32 (cache
->buffer
, parents_offset
);
881 for (k
= 0; k
< n_parents
&& p
< 127; k
++)
883 parent_mime_offset
= GET_UINT32 (cache
->buffer
, parents_offset
+ 4 + 4 * k
);
884 all_parents
[p
++] = cache
->buffer
+ parent_mime_offset
;
891 all_parents
[p
++] = 0;
893 result
= (char **) malloc (p
* sizeof (char *));
894 memcpy (result
, all_parents
, p
* sizeof (char *));