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))
68 #define MAJOR_VERSION 1
69 #define MINOR_VERSION 0
71 extern XdgMimeCache
**caches
;
82 #define GET_UINT16(cache,offset) (ntohs(*(uint16_t*)((cache) + (offset))))
83 #define GET_UINT32(cache,offset) (ntohl(*(uint32_t*)((cache) + (offset))))
86 _xdg_mime_cache_ref (XdgMimeCache
*cache
)
93 _xdg_mime_cache_unref (XdgMimeCache
*cache
)
97 if (cache
->ref_count
== 0)
100 munmap (cache
->buffer
, cache
->size
);
107 _xdg_mime_cache_new_from_file (const char *file_name
)
109 XdgMimeCache
*cache
= NULL
;
116 /* Open the file and map it into memory */
117 fd
= open (file_name
, O_RDONLY
|_O_BINARY
, 0);
122 if (fstat (fd
, &st
) < 0 || st
.st_size
< 4)
125 buffer
= (char *) mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
127 if (buffer
== MAP_FAILED
)
131 if (GET_UINT16 (buffer
, 0) != MAJOR_VERSION
||
132 GET_UINT16 (buffer
, 2) != MINOR_VERSION
)
134 munmap (buffer
, st
.st_size
);
139 cache
= (XdgMimeCache
*) malloc (sizeof (XdgMimeCache
));
140 cache
->ref_count
= 1;
141 cache
->buffer
= buffer
;
142 cache
->size
= st
.st_size
;
148 #endif /* HAVE_MMAP */
154 cache_magic_matchlet_compare_to_data (XdgMimeCache
*cache
,
159 xdg_uint32_t range_start
= GET_UINT32 (cache
->buffer
, offset
);
160 xdg_uint32_t range_length
= GET_UINT32 (cache
->buffer
, offset
+ 4);
161 xdg_uint32_t data_length
= GET_UINT32 (cache
->buffer
, offset
+ 12);
162 xdg_uint32_t data_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16);
163 xdg_uint32_t mask_offset
= GET_UINT32 (cache
->buffer
, offset
+ 20);
167 for (i
= range_start
; i
<= range_start
+ range_length
; i
++)
169 int valid_matchlet
= TRUE
;
171 if (i
+ data_length
> len
)
176 for (j
= 0; j
< data_length
; j
++)
178 if ((cache
->buffer
[data_offset
+ j
] & cache
->buffer
[mask_offset
+ j
]) !=
179 ((((unsigned char *) data
)[j
+ i
]) & cache
->buffer
[mask_offset
+ j
]))
181 valid_matchlet
= FALSE
;
188 for (j
= 0; j
< data_length
; j
++)
190 if (cache
->buffer
[data_offset
+ j
] != ((unsigned char *) data
)[j
+ i
])
192 valid_matchlet
= FALSE
;
206 cache_magic_matchlet_compare (XdgMimeCache
*cache
,
211 xdg_uint32_t n_children
= GET_UINT32 (cache
->buffer
, offset
+ 24);
212 xdg_uint32_t child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 28);
216 if (cache_magic_matchlet_compare_to_data (cache
, offset
, data
, len
))
221 for (i
= 0; i
< n_children
; i
++)
223 if (cache_magic_matchlet_compare (cache
, child_offset
+ 32 * i
,
233 cache_magic_compare_to_data (XdgMimeCache
*cache
,
239 xdg_uint32_t priority
= GET_UINT32 (cache
->buffer
, offset
);
240 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4);
241 xdg_uint32_t n_matchlets
= GET_UINT32 (cache
->buffer
, offset
+ 8);
242 xdg_uint32_t matchlet_offset
= GET_UINT32 (cache
->buffer
, offset
+ 12);
246 for (i
= 0; i
< n_matchlets
; i
++)
248 if (cache_magic_matchlet_compare (cache
, matchlet_offset
+ i
* 32,
253 return cache
->buffer
+ mimetype_offset
;
261 cache_magic_lookup_data (XdgMimeCache
*cache
,
266 xdg_uint32_t list_offset
;
267 xdg_uint32_t n_entries
;
274 list_offset
= GET_UINT32 (cache
->buffer
, 24);
275 n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
276 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 8);
278 for (j
= 0; j
< n_entries
; j
++)
280 const char *match
= cache_magic_compare_to_data (cache
, offset
+ 16 * j
,
290 cache_alias_lookup (const char *alias
)
293 int i
, min
, max
, mid
, cmp
;
295 for (i
= 0; i
< n_caches
; i
++)
297 XdgMimeCache
*cache
= caches
[i
];
298 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 4 );
299 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
306 mid
= (min
+ max
) / 2;
308 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
309 ptr
= cache
->buffer
+ offset
;
310 cmp
= strcmp (ptr
, alias
);
318 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
319 return cache
->buffer
+ offset
;
328 cache_glob_lookup_literal (const char *file_name
)
331 int i
, min
, max
, mid
, cmp
;
333 for (i
= 0; i
< n_caches
; i
++)
335 XdgMimeCache
*cache
= caches
[i
];
336 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 12);
337 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
344 mid
= (min
+ max
) / 2;
346 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
);
347 ptr
= cache
->buffer
+ offset
;
348 cmp
= strcmp (ptr
, file_name
);
356 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * mid
+ 4);
357 return cache
->buffer
+ offset
;
366 cache_glob_lookup_fnmatch (const char *file_name
)
368 const char *mime_type
;
373 for (i
= 0; i
< n_caches
; i
++)
375 XdgMimeCache
*cache
= caches
[i
];
377 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 20);
378 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
380 for (j
= 0; j
< n_entries
; j
++)
382 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
);
383 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * j
+ 4);
384 ptr
= cache
->buffer
+ offset
;
385 mime_type
= cache
->buffer
+ mimetype_offset
;
387 /* FIXME: Not UTF-8 safe */
388 if (fnmatch (ptr
, file_name
, 0) == 0)
397 cache_glob_node_lookup_suffix (XdgMimeCache
*cache
,
398 xdg_uint32_t n_entries
,
403 xdg_unichar_t character
;
404 xdg_unichar_t match_char
;
405 xdg_uint32_t mimetype_offset
;
406 xdg_uint32_t n_children
;
407 xdg_uint32_t child_offset
;
411 character
= _xdg_utf8_to_ucs4 (suffix
);
413 character
= _xdg_ucs4_to_lower (character
);
419 mid
= (min
+ max
) / 2;
421 match_char
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
);
423 if (match_char
< character
)
425 else if (match_char
> character
)
429 suffix
= _xdg_utf8_next_char (suffix
);
432 mimetype_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 4);
434 return cache
->buffer
+ mimetype_offset
;
438 n_children
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 8);
439 child_offset
= GET_UINT32 (cache
->buffer
, offset
+ 16 * mid
+ 12);
441 return cache_glob_node_lookup_suffix (cache
,
442 n_children
, child_offset
,
443 suffix
, ignore_case
);
452 cache_glob_lookup_suffix (const char *suffix
,
455 const char *mime_type
;
459 for (i
= 0; i
< n_caches
; i
++)
461 XdgMimeCache
*cache
= caches
[i
];
463 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
464 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
465 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
467 mime_type
= cache_glob_node_lookup_suffix (cache
,
469 suffix
, ignore_case
);
478 find_stopchars (char *stopchars
)
483 for (i
= 0; i
< n_caches
; i
++)
485 XdgMimeCache
*cache
= caches
[i
];
487 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 16);
488 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
489 xdg_uint32_t offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4);
491 for (j
= 0; j
< n_entries
; j
++)
493 xdg_uint32_t match_char
= GET_UINT32 (cache
->buffer
, offset
);
495 if (match_char
< 128)
497 for (l
= 0; l
< k
; l
++)
498 if (stopchars
[l
] == match_char
)
502 stopchars
[k
] = (char) match_char
;
515 cache_glob_lookup_file_name (const char *file_name
)
517 const char *mime_type
;
521 assert (file_name
!= NULL
);
523 /* First, check the literals */
524 mime_type
= cache_glob_lookup_literal (file_name
);
528 find_stopchars (stopchars
);
530 /* Next, check suffixes */
531 ptr
= strpbrk (file_name
, stopchars
);
534 mime_type
= cache_glob_lookup_suffix (ptr
, FALSE
);
535 if (mime_type
!= NULL
)
538 mime_type
= cache_glob_lookup_suffix (ptr
, TRUE
);
539 if (mime_type
!= NULL
)
542 ptr
= strpbrk (ptr
+ 1, stopchars
);
545 /* Last, try fnmatch */
546 return cache_glob_lookup_fnmatch (file_name
);
550 _xdg_mime_cache_get_max_buffer_extents (void)
553 xdg_uint32_t max_extent
;
557 for (i
= 0; i
< n_caches
; i
++)
559 XdgMimeCache
*cache
= caches
[i
];
561 offset
= GET_UINT32 (cache
->buffer
, 24);
562 max_extent
= MAX (max_extent
, GET_UINT32 (cache
->buffer
, offset
+ 4));
569 _xdg_mime_cache_get_mime_type_for_data (const void *data
,
572 const char *mime_type
;
577 for (i
= 0; i
< n_caches
; i
++)
579 XdgMimeCache
*cache
= caches
[i
];
584 match
= cache_magic_lookup_data (cache
, data
, len
, &prio
);
595 return XDG_MIME_TYPE_UNKNOWN
;
599 _xdg_mime_cache_get_mime_type_for_file (const char *file_name
)
601 const char *mime_type
;
607 const char *base_name
;
609 if (file_name
== NULL
)
612 if (! _xdg_utf8_validate (file_name
))
615 base_name
= _xdg_get_base_name (file_name
);
616 mime_type
= _xdg_mime_cache_get_mime_type_from_file_name (base_name
);
618 if (mime_type
!= XDG_MIME_TYPE_UNKNOWN
)
621 if (stat (file_name
, &statbuf
) != 0)
622 return XDG_MIME_TYPE_UNKNOWN
;
624 if (!S_ISREG (statbuf
.st_mode
))
625 return XDG_MIME_TYPE_UNKNOWN
;
627 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
628 * be large and need getting from a stream instead of just reading it all
630 max_extent
= _xdg_mime_cache_get_max_buffer_extents ();
631 data
= malloc (max_extent
);
633 return XDG_MIME_TYPE_UNKNOWN
;
635 file
= fopen (file_name
, "r");
639 return XDG_MIME_TYPE_UNKNOWN
;
642 bytes_read
= fread (data
, 1, max_extent
, file
);
647 return XDG_MIME_TYPE_UNKNOWN
;
650 mime_type
= _xdg_mime_cache_get_mime_type_for_data (data
, bytes_read
);
659 _xdg_mime_cache_get_mime_type_from_file_name (const char *file_name
)
661 const char *mime_type
;
663 mime_type
= cache_glob_lookup_file_name (file_name
);
668 return XDG_MIME_TYPE_UNKNOWN
;
673 is_super_type (const char *mime
)
678 length
= strlen (mime
);
679 type
= &(mime
[length
- 2]);
681 if (strcmp (type
, "/*") == 0)
689 _xdg_mime_cache_mime_type_subclass (const char *mime
,
692 const char *umime
, *ubase
;
694 int i
, j
, min
, max
, med
, cmp
;
696 umime
= _xdg_mime_cache_unalias_mime_type (mime
);
697 ubase
= _xdg_mime_cache_unalias_mime_type (base
);
699 if (strcmp (umime
, ubase
) == 0)
702 /* We really want to handle text/ * in GtkFileFilter, so we just
703 * turn on the supertype matching
706 /* Handle supertypes */
707 if (is_super_type (ubase
) &&
708 xdg_mime_media_type_equal (umime
, ubase
))
712 /* Handle special cases text/plain and application/octet-stream */
713 if (strcmp (ubase
, "text/plain") == 0 &&
714 strncmp (umime
, "text/", 5) == 0)
717 if (strcmp (ubase
, "application/octet-stream") == 0)
720 for (i
= 0; i
< n_caches
; i
++)
722 XdgMimeCache
*cache
= caches
[i
];
724 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
725 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
726 xdg_uint32_t offset
, n_parents
, parent_offset
;
734 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
);
735 cmp
= strcmp (cache
->buffer
+ offset
, umime
);
742 offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * med
+ 4);
743 n_parents
= GET_UINT32 (cache
->buffer
, offset
);
745 for (j
= 0; j
< n_parents
; j
++)
747 parent_offset
= GET_UINT32 (cache
->buffer
, offset
+ 4 + 4 * j
);
748 if (_xdg_mime_cache_mime_type_subclass (cache
->buffer
+ parent_offset
, ubase
))
761 _xdg_mime_cache_unalias_mime_type (const char *mime
)
765 lookup
= cache_alias_lookup (mime
);
774 _xdg_mime_cache_list_mime_parents (const char *mime
)
777 char *all_parents
[128]; /* we'll stop at 128 */
781 for (i
= 0; i
< n_caches
; i
++)
783 XdgMimeCache
*cache
= caches
[i
];
785 xdg_uint32_t list_offset
= GET_UINT32 (cache
->buffer
, 8);
786 xdg_uint32_t n_entries
= GET_UINT32 (cache
->buffer
, list_offset
);
788 for (j
= 0; j
< n_entries
; j
++)
790 xdg_uint32_t mimetype_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * i
);
791 xdg_uint32_t parents_offset
= GET_UINT32 (cache
->buffer
, list_offset
+ 4 + 8 * i
+ 4);
793 if (strcmp (cache
->buffer
+ mimetype_offset
, mime
) == 0)
795 xdg_uint32_t n_parents
= GET_UINT32 (cache
->buffer
, parents_offset
);
797 for (j
= 0; j
< n_parents
; j
++)
798 all_parents
[p
++] = cache
->buffer
+ parents_offset
+ 4 + 4 * j
;
804 all_parents
[p
++] = 0;
806 result
= (char **) malloc (p
* sizeof (char *));
807 memcpy (result
, all_parents
, p
* sizeof (char *));