1/* -*- mode: C; c-file-style: "gnu" -*- */
2/* xdgmimealias.c: Private file. mmappable caches for mime data
3 *
4 * More info can be found at http://www.freedesktop.org/standards/
5 *
6 * Copyright (C) 2005 Matthias Clasen <mclasen@redhat.com>
7 *
8 * Licensed under the Academic Free License version 2.0
9 * Or under the following terms:
10 *
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.1 of the License, or (at your option) any later version.
15 *
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.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25#include "config.h"
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include <fcntl.h>
32#include <unistd.h>
33#include <errno.h>
34#include <fnmatch.h>
35#include <assert.h>
36
37#include <netinet/in.h> /* for ntohl/ntohs */
38
39#ifdef HAVE_MMAP
40#include <sys/mman.h>
41#else
42#warning Building xdgmime without MMAP support. Binary "mime.info" cache files will not be used.
43#endif
44
45#include <sys/stat.h>
46#include <sys/types.h>
47
48#include "xdgmimecache.h"
49#include "xdgmimeint.h"
50
51#ifndef MAX
52#define MAX(a,b) ((a) > (b) ? (a) : (b))
53#endif
54
55#ifndef FALSE
56#define FALSE (0)
57#endif
58
59#ifndef TRUE
60#define TRUE (!FALSE)
61#endif
62
63#ifndef _O_BINARY
64#define _O_BINARY 0
65#endif
66
67#ifndef MAP_FAILED
68#define MAP_FAILED ((void *) -1)
69#endif
70
71#define MAJOR_VERSION 1
72#define MINOR_VERSION_MIN 1
73#define MINOR_VERSION_MAX 2
74
75struct _XdgMimeCache
76{
77 int ref_count;
78 int minor;
79
80 size_t size;
81 char *buffer;
82};
83
84#define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
85#define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
86
87XdgMimeCache *
88_xdg_mime_cache_ref (XdgMimeCache *cache)
89{
90 cache->ref_count++;
91 return cache;
92}
93
94void
95_xdg_mime_cache_unref (XdgMimeCache *cache)
96{
97 cache->ref_count--;
98
99 if (cache->ref_count == 0)
100 {
101#ifdef HAVE_MMAP
102 munmap (addr: cache->buffer, len: cache->size);
103#endif
104 free (ptr: cache);
105 }
106}
107
108XdgMimeCache *
109_xdg_mime_cache_new_from_file (const char *file_name)
110{
111 XdgMimeCache *cache = NULL;
112
113#ifdef HAVE_MMAP
114 int fd = -1;
115 struct stat st;
116 char *buffer = NULL;
117 int minor;
118
119 /* Open the file and map it into memory */
120 do
121 fd = open (file: file_name, O_RDONLY|_O_BINARY, 0);
122 while (fd == -1 && errno == EINTR);
123
124 if (fd < 0)
125 return NULL;
126
127 if (fstat (fd: fd, buf: &st) < 0 || st.st_size < 4)
128 goto done;
129
130 buffer = (char *) mmap (NULL, len: st.st_size, PROT_READ, MAP_SHARED, fd: fd, offset: 0);
131
132 if (buffer == MAP_FAILED)
133 goto done;
134
135 minor = GET_UINT16 (buffer, 2);
136 /* Verify version */
137 if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
138 (minor < MINOR_VERSION_MIN ||
139 minor > MINOR_VERSION_MAX))
140 {
141 munmap (addr: buffer, len: st.st_size);
142
143 goto done;
144 }
145
146 cache = (XdgMimeCache *) malloc (size: sizeof (XdgMimeCache));
147 cache->minor = minor;
148 cache->ref_count = 1;
149 cache->buffer = buffer;
150 cache->size = st.st_size;
151
152 done:
153 if (fd != -1)
154 close (fd: fd);
155
156#else /* HAVE_MMAP */
157 cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
158 cache->minor = 0;
159 cache->ref_count = 1;
160 cache->buffer = NULL;
161 cache->size = 0;
162#endif /* HAVE_MMAP */
163
164 return cache;
165}
166
167static int
168cache_magic_matchlet_compare_to_data (XdgMimeCache *cache,
169 xdg_uint32_t offset,
170 const void *data,
171 size_t len)
172{
173 xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
174 xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
175 xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
176 xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
177 xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
178
179 int i, j;
180
181 for (i = range_start; i < range_start + range_length; i++)
182 {
183 int valid_matchlet = TRUE;
184
185 if (i + data_length > len)
186 return FALSE;
187
188 if (mask_offset)
189 {
190 for (j = 0; j < data_length; j++)
191 {
192 if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
193 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
194 {
195 valid_matchlet = FALSE;
196 break;
197 }
198 }
199 }
200 else
201 {
202 for (j = 0; j < data_length; j++)
203 {
204 if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
205 {
206 valid_matchlet = FALSE;
207 break;
208 }
209 }
210 }
211
212 if (valid_matchlet)
213 return TRUE;
214 }
215
216 return FALSE;
217}
218
219static int
220cache_magic_matchlet_compare (XdgMimeCache *cache,
221 xdg_uint32_t offset,
222 const void *data,
223 size_t len)
224{
225 xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
226 xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
227
228 int i;
229
230 if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
231 {
232 if (n_children == 0)
233 return TRUE;
234
235 for (i = 0; i < n_children; i++)
236 {
237 if (cache_magic_matchlet_compare (cache, offset: child_offset + 32 * i,
238 data, len))
239 return TRUE;
240 }
241 }
242
243 return FALSE;
244}
245
246static const char *
247cache_magic_compare_to_data (XdgMimeCache *cache,
248 xdg_uint32_t offset,
249 const void *data,
250 size_t len,
251 int *prio)
252{
253 xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
254 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
255 xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
256 xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
257
258 int i;
259
260 for (i = 0; i < n_matchlets; i++)
261 {
262 if (cache_magic_matchlet_compare (cache, offset: matchlet_offset + i * 32,
263 data, len))
264 {
265 *prio = priority;
266
267 return cache->buffer + mimetype_offset;
268 }
269 }
270
271 return NULL;
272}
273
274static const char *
275cache_magic_lookup_data (XdgMimeCache *cache,
276 const void *data,
277 size_t len,
278 int *prio,
279 const char *mime_types[],
280 int n_mime_types)
281{
282 xdg_uint32_t list_offset;
283 xdg_uint32_t n_entries;
284 xdg_uint32_t offset;
285
286 int j, n;
287
288 *prio = 0;
289
290 list_offset = GET_UINT32 (cache->buffer, 24);
291 n_entries = GET_UINT32 (cache->buffer, list_offset);
292 offset = GET_UINT32 (cache->buffer, list_offset + 8);
293
294 for (j = 0; j < n_entries; j++)
295 {
296 const char *match;
297
298 match = cache_magic_compare_to_data (cache, offset: offset + 16 * j,
299 data, len, prio);
300 if (match)
301 return match;
302 else
303 {
304 xdg_uint32_t mimetype_offset;
305 const char *non_match;
306
307 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
308 non_match = cache->buffer + mimetype_offset;
309
310 for (n = 0; n < n_mime_types; n++)
311 {
312 if (mime_types[n] &&
313 _xdg_mime_mime_type_equal (mime_a: mime_types[n], mime_b: non_match))
314 mime_types[n] = NULL;
315 }
316 }
317 }
318
319 return NULL;
320}
321
322static const char *
323cache_alias_lookup (const char *alias)
324{
325 const char *ptr;
326 int i, min, max, mid, cmp;
327
328 for (i = 0; _caches[i]; i++)
329 {
330 XdgMimeCache *cache = _caches[i];
331 xdg_uint32_t list_offset;
332 xdg_uint32_t n_entries;
333 xdg_uint32_t offset;
334
335 if (cache->buffer == NULL)
336 continue;
337
338 list_offset = GET_UINT32 (cache->buffer, 4);
339 n_entries = GET_UINT32 (cache->buffer, list_offset);
340
341 min = 0;
342 max = n_entries - 1;
343 while (max >= min)
344 {
345 mid = (min + max) / 2;
346
347 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
348 ptr = cache->buffer + offset;
349 cmp = strcmp (s1: ptr, s2: alias);
350
351 if (cmp < 0)
352 min = mid + 1;
353 else if (cmp > 0)
354 max = mid - 1;
355 else
356 {
357 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
358 return cache->buffer + offset;
359 }
360 }
361 }
362
363 return NULL;
364}
365
366typedef struct {
367 const char *mime;
368 int weight;
369} MimeWeight;
370
371static int
372cache_glob_lookup_literal (const char *file_name,
373 const char *mime_types[],
374 int n_mime_types,
375 int case_sensitive_check)
376{
377 const char *ptr;
378 int i, min, max, mid, cmp;
379
380 for (i = 0; _caches[i]; i++)
381 {
382 XdgMimeCache *cache = _caches[i];
383 xdg_uint32_t list_offset;
384 xdg_uint32_t n_entries;
385 xdg_uint32_t offset;
386
387 if (cache->buffer == NULL)
388 continue;
389
390 list_offset = GET_UINT32 (cache->buffer, 12);
391 n_entries = GET_UINT32 (cache->buffer, list_offset);
392
393 min = 0;
394 max = n_entries - 1;
395 while (max >= min)
396 {
397 mid = (min + max) / 2;
398
399 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid);
400 ptr = cache->buffer + offset;
401 cmp = strcmp (s1: ptr, s2: file_name);
402
403 if (cmp < 0)
404 min = mid + 1;
405 else if (cmp > 0)
406 max = mid - 1;
407 else
408 {
409 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8);
410 int case_sensitive = weight & 0x100;
411 weight = weight & 0xff;
412
413 if (case_sensitive_check || !case_sensitive)
414 {
415 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4);
416 mime_types[0] = (const char *)(cache->buffer + offset);
417
418 return 1;
419 }
420 return 0;
421 }
422 }
423 }
424
425 return 0;
426}
427
428static int
429cache_glob_lookup_fnmatch (const char *file_name,
430 MimeWeight mime_types[],
431 int n_mime_types)
432{
433 const char *mime_type;
434 const char *ptr;
435
436 int i, j, n;
437
438 n = 0;
439 for (i = 0; _caches[i]; i++)
440 {
441 XdgMimeCache *cache = _caches[i];
442
443 xdg_uint32_t list_offset;
444 xdg_uint32_t n_entries;
445
446 if (cache->buffer == NULL)
447 continue;
448
449 list_offset = GET_UINT32 (cache->buffer, 20);
450 n_entries = GET_UINT32 (cache->buffer, list_offset);
451
452 for (j = 0; j < n_entries && n < n_mime_types; j++)
453 {
454 xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j);
455 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4);
456 int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8);
457 weight = weight & 0xff;
458 ptr = cache->buffer + offset;
459 mime_type = cache->buffer + mimetype_offset;
460
461 /* FIXME: Not UTF-8 safe */
462 if (fnmatch (pattern: ptr, name: file_name, flags: 0) == 0)
463 {
464 mime_types[n].mime = mime_type;
465 mime_types[n].weight = weight;
466 n++;
467 }
468 }
469
470 if (n == n_mime_types)
471 break;
472 }
473
474 return n;
475}
476
477static int
478cache_glob_node_lookup_suffix (XdgMimeCache *cache,
479 xdg_uint32_t n_entries,
480 xdg_uint32_t offset,
481 const char *file_name,
482 int len,
483 int case_sensitive_check,
484 MimeWeight mime_types[],
485 int n_mime_types)
486{
487 xdg_unichar_t character;
488 xdg_unichar_t match_char;
489 xdg_uint32_t mimetype_offset;
490 xdg_uint32_t n_children;
491 xdg_uint32_t child_offset;
492 int weight;
493 int case_sensitive;
494
495 int min, max, mid, n, i;
496
497 character = file_name[len - 1];
498
499 assert (character != 0);
500
501 min = 0;
502 max = n_entries - 1;
503 while (max >= min)
504 {
505 mid = (min + max) / 2;
506 match_char = GET_UINT32 (cache->buffer, offset + 12 * mid);
507 if (match_char < character)
508 min = mid + 1;
509 else if (match_char > character)
510 max = mid - 1;
511 else
512 {
513 len--;
514 n = 0;
515 n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4);
516 child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8);
517
518 if (len > 0)
519 {
520 n = cache_glob_node_lookup_suffix (cache,
521 n_entries: n_children, offset: child_offset,
522 file_name, len,
523 case_sensitive_check,
524 mime_types,
525 n_mime_types);
526 }
527 if (n == 0)
528 {
529 i = 0;
530 while (n < n_mime_types && i < n_children)
531 {
532 match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i);
533 if (match_char != 0)
534 break;
535
536 mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4);
537 weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8);
538 case_sensitive = weight & 0x100;
539 weight = weight & 0xff;
540
541 if (case_sensitive_check || !case_sensitive)
542 {
543 mime_types[n].mime = cache->buffer + mimetype_offset;
544 mime_types[n].weight = weight;
545 n++;
546 }
547 i++;
548 }
549 }
550 return n;
551 }
552 }
553 return 0;
554}
555
556static int
557cache_glob_lookup_suffix (const char *file_name,
558 int len,
559 int ignore_case,
560 MimeWeight mime_types[],
561 int n_mime_types)
562{
563 int i, n;
564
565 n = 0;
566 for (i = 0; _caches[i]; i++)
567 {
568 XdgMimeCache *cache = _caches[i];
569
570 xdg_uint32_t list_offset;
571 xdg_uint32_t n_entries;
572 xdg_uint32_t offset;
573
574 if (cache->buffer == NULL)
575 continue;
576
577 list_offset = GET_UINT32 (cache->buffer, 16);
578 n_entries = GET_UINT32 (cache->buffer, list_offset);
579 offset = GET_UINT32 (cache->buffer, list_offset + 4);
580
581 n += cache_glob_node_lookup_suffix (cache,
582 n_entries, offset,
583 file_name, len,
584 case_sensitive_check: ignore_case,
585 mime_types: mime_types + n,
586 n_mime_types: n_mime_types - n);
587 if (n == n_mime_types)
588 break;
589 }
590
591 return n;
592}
593
594static int compare_mime_weight (const void *a, const void *b)
595{
596 const MimeWeight *aa = (const MimeWeight *)a;
597 const MimeWeight *bb = (const MimeWeight *)b;
598
599 return bb->weight - aa->weight;
600}
601
602#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z')
603static char *
604ascii_tolower (const char *str)
605{
606 char *p, *lower;
607
608 lower = strdup (s: str);
609 p = lower;
610 while (*p != 0)
611 {
612 char c = *p;
613 *p++ = ISUPPER (c) ? c - 'A' + 'a' : c;
614 }
615 return lower;
616}
617
618static int
619filter_out_dupes (MimeWeight mimes[], int n_mimes)
620{
621 int last;
622 int i, j;
623
624 last = n_mimes;
625
626 for (i = 0; i < last; i++)
627 {
628 j = i + 1;
629 while (j < last)
630 {
631 if (strcmp (s1: mimes[i].mime, s2: mimes[j].mime) == 0)
632 {
633 mimes[i].weight = MAX (mimes[i].weight, mimes[j].weight);
634 last--;
635 mimes[j].mime = mimes[last].mime;
636 mimes[j].weight = mimes[last].weight;
637 }
638 else
639 j++;
640 }
641 }
642
643 return last;
644}
645
646static int
647cache_glob_lookup_file_name (const char *file_name,
648 const char *mime_types[],
649 int n_mime_types)
650{
651 int n;
652 MimeWeight mimes[10];
653 int n_mimes = 10;
654 int i;
655 int len;
656 char *lower_case;
657
658 assert (file_name != NULL && n_mime_types > 0);
659
660 /* First, check the literals */
661
662 lower_case = ascii_tolower (str: file_name);
663
664 n = cache_glob_lookup_literal (file_name: lower_case, mime_types, n_mime_types, FALSE);
665 if (n > 0)
666 {
667 free (ptr: lower_case);
668 return n;
669 }
670
671 n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE);
672 if (n > 0)
673 {
674 free (ptr: lower_case);
675 return n;
676 }
677
678 len = strlen (s: file_name);
679 n = cache_glob_lookup_suffix (file_name: lower_case, len, FALSE, mime_types: mimes, n_mime_types: n_mimes);
680 if (n < 2)
681 n += cache_glob_lookup_suffix (file_name, len, TRUE, mime_types: mimes + n, n_mime_types: n_mimes - n);
682
683 free (ptr: lower_case);
684
685 /* Last, try fnmatch */
686 if (n < 2)
687 n += cache_glob_lookup_fnmatch (file_name, mime_types: mimes + n, n_mime_types: n_mimes - n);
688
689 n = filter_out_dupes (mimes, n_mimes: n);
690
691 qsort (base: mimes, nmemb: n, size: sizeof (MimeWeight), compar: compare_mime_weight);
692
693 if (n_mime_types < n)
694 n = n_mime_types;
695
696 for (i = 0; i < n; i++)
697 mime_types[i] = mimes[i].mime;
698
699 return n;
700}
701
702int
703_xdg_mime_cache_get_max_buffer_extents (void)
704{
705 xdg_uint32_t offset;
706 xdg_uint32_t max_extent;
707 int i;
708
709 max_extent = 0;
710 for (i = 0; _caches[i]; i++)
711 {
712 XdgMimeCache *cache = _caches[i];
713
714 if (cache->buffer == NULL)
715 continue;
716
717 offset = GET_UINT32 (cache->buffer, 24);
718 max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
719 }
720
721 return max_extent;
722}
723
724static const char *
725cache_get_mime_type_for_data (const void *data,
726 size_t len,
727 int *result_prio,
728 const char *mime_types[],
729 int n_mime_types)
730{
731 const char *mime_type;
732 int i, n, priority;
733
734 priority = 0;
735 mime_type = NULL;
736 for (i = 0; _caches[i]; i++)
737 {
738 XdgMimeCache *cache = _caches[i];
739
740 int prio;
741 const char *match;
742
743 if (cache->buffer == NULL)
744 continue;
745
746 match = cache_magic_lookup_data (cache, data, len, prio: &prio,
747 mime_types, n_mime_types);
748 if (prio > priority)
749 {
750 priority = prio;
751 mime_type = match;
752 }
753 }
754
755 if (result_prio)
756 *result_prio = priority;
757
758 if (priority > 0)
759 return mime_type;
760
761 for (n = 0; n < n_mime_types; n++)
762 {
763 if (mime_types[n])
764 return mime_types[n];
765 }
766
767 return NULL;
768}
769
770const char *
771_xdg_mime_cache_get_mime_type_for_data (const void *data,
772 size_t len,
773 int *result_prio)
774{
775 return cache_get_mime_type_for_data (data, len, result_prio, NULL, n_mime_types: 0);
776}
777
778#ifdef NOT_USED_IN_GIO
779
780const char *
781_xdg_mime_cache_get_mime_type_for_file (const char *file_name,
782 struct stat *statbuf)
783{
784 const char *mime_type;
785 const char *mime_types[10];
786 FILE *file;
787 unsigned char *data;
788 int max_extent;
789 int bytes_read;
790 struct stat buf;
791 const char *base_name;
792 int n;
793
794 if (file_name == NULL)
795 return NULL;
796
797 if (! _xdg_utf8_validate (file_name))
798 return NULL;
799
800 base_name = _xdg_get_base_name (file_name);
801 n = cache_glob_lookup_file_name (base_name, mime_types, 10);
802
803 if (n == 1)
804 return mime_types[0];
805
806 if (!statbuf)
807 {
808 if (stat (file_name, &buf) != 0)
809 return XDG_MIME_TYPE_UNKNOWN;
810
811 statbuf = &buf;
812 }
813
814 if (statbuf->st_size == 0)
815 return XDG_MIME_TYPE_EMPTY;
816
817 if (!S_ISREG (statbuf->st_mode))
818 return XDG_MIME_TYPE_UNKNOWN;
819
820 /* FIXME: Need to make sure that max_extent isn't totally broken. This could
821 * be large and need getting from a stream instead of just reading it all
822 * in. */
823 max_extent = _xdg_mime_cache_get_max_buffer_extents ();
824 data = malloc (max_extent);
825 if (data == NULL)
826 return XDG_MIME_TYPE_UNKNOWN;
827
828 file = fopen (file_name, "r");
829 if (file == NULL)
830 {
831 free (data);
832 return XDG_MIME_TYPE_UNKNOWN;
833 }
834
835 bytes_read = fread (data, 1, max_extent, file);
836 if (ferror (file))
837 {
838 free (data);
839 fclose (file);
840 return XDG_MIME_TYPE_UNKNOWN;
841 }
842
843 mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
844 mime_types, n);
845
846 if (!mime_type)
847 mime_type = _xdg_binary_or_text_fallback(data, bytes_read);
848
849 free (data);
850 fclose (file);
851
852 return mime_type;
853}
854
855const char *
856_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
857{
858 const char *mime_type;
859
860 if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
861 return mime_type;
862 else
863 return XDG_MIME_TYPE_UNKNOWN;
864}
865
866#endif
867
868int
869_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
870 const char *mime_types[],
871 int n_mime_types)
872{
873 return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
874}
875
876#if 1
877static int
878ends_with (const char *str,
879 const char *suffix)
880{
881 int length;
882 int suffix_length;
883
884 length = strlen (s: str);
885 suffix_length = strlen (s: suffix);
886 if (length < suffix_length)
887 return 0;
888
889 if (strcmp (s1: str + length - suffix_length, s2: suffix) == 0)
890 return 1;
891
892 return 0;
893}
894
895static int
896is_super_type (const char *mime)
897{
898 return ends_with (str: mime, suffix: "/*");
899}
900#endif
901
902int
903_xdg_mime_cache_mime_type_subclass (const char *mime,
904 const char *base)
905{
906 const char *umime, *ubase;
907
908 int i, j, min, max, med, cmp;
909
910 umime = _xdg_mime_cache_unalias_mime_type (mime);
911 ubase = _xdg_mime_cache_unalias_mime_type (mime: base);
912
913 if (strcmp (s1: umime, s2: ubase) == 0)
914 return 1;
915
916 /* We really want to handle text/ * in GtkFileFilter, so we just
917 * turn on the supertype matching
918 */
919#if 1
920 /* Handle supertypes */
921 if (is_super_type (mime: ubase) &&
922 xdg_mime_media_type_equal (mime_a: umime, mime_b: ubase))
923 return 1;
924#endif
925
926 /* Handle special cases text/plain and application/octet-stream */
927 if (strcmp (s1: ubase, s2: "text/plain") == 0 &&
928 strncmp (s1: umime, s2: "text/", n: 5) == 0)
929 return 1;
930
931 if (strcmp (s1: ubase, s2: "application/octet-stream") == 0 &&
932 strncmp (s1: umime, s2: "inode/", n: 6) != 0)
933 return 1;
934
935 for (i = 0; _caches[i]; i++)
936 {
937 XdgMimeCache *cache = _caches[i];
938 xdg_uint32_t list_offset;
939 xdg_uint32_t n_entries;
940 xdg_uint32_t offset, n_parents, parent_offset;
941
942 if (cache->buffer == NULL)
943 continue;
944
945 list_offset = GET_UINT32 (cache->buffer, 8);
946 n_entries = GET_UINT32 (cache->buffer, list_offset);
947
948 min = 0;
949 max = n_entries - 1;
950 while (max >= min)
951 {
952 med = (min + max)/2;
953
954 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
955 cmp = strcmp (s1: cache->buffer + offset, s2: umime);
956 if (cmp < 0)
957 min = med + 1;
958 else if (cmp > 0)
959 max = med - 1;
960 else
961 {
962 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
963 n_parents = GET_UINT32 (cache->buffer, offset);
964
965 for (j = 0; j < n_parents; j++)
966 {
967 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
968 if (strcmp (s1: cache->buffer + parent_offset, s2: mime) != 0 &&
969 strcmp (s1: cache->buffer + parent_offset, s2: umime) != 0 &&
970 _xdg_mime_cache_mime_type_subclass (mime: cache->buffer + parent_offset, base: ubase))
971 return 1;
972 }
973
974 break;
975 }
976 }
977 }
978
979 return 0;
980}
981
982const char *
983_xdg_mime_cache_unalias_mime_type (const char *mime)
984{
985 const char *lookup;
986
987 lookup = cache_alias_lookup (alias: mime);
988
989 if (lookup)
990 return lookup;
991
992 return mime;
993}
994
995char **
996_xdg_mime_cache_list_mime_parents (const char *mime)
997{
998 int i, j, k, l, p;
999 char *all_parents[128]; /* we'll stop at 128 */
1000 char **result;
1001
1002 mime = xdg_mime_unalias_mime_type (mime);
1003
1004 p = 0;
1005 for (i = 0; _caches[i]; i++)
1006 {
1007 XdgMimeCache *cache = _caches[i];
1008 xdg_uint32_t list_offset;
1009 xdg_uint32_t n_entries;
1010
1011 if (cache->buffer == NULL)
1012 continue;
1013
1014 list_offset = GET_UINT32 (cache->buffer, 8);
1015 n_entries = GET_UINT32 (cache->buffer, list_offset);
1016
1017 for (j = 0; j < n_entries; j++)
1018 {
1019 xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
1020 xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
1021
1022 if (strcmp (s1: cache->buffer + mimetype_offset, s2: mime) == 0)
1023 {
1024 xdg_uint32_t parent_mime_offset;
1025 xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
1026
1027 for (k = 0; k < n_parents && p < 127; k++)
1028 {
1029 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
1030
1031 /* Don't add same parent multiple times.
1032 * This can happen for instance if the same type is listed in multiple directories
1033 */
1034 for (l = 0; l < p; l++)
1035 {
1036 if (strcmp (s1: all_parents[l], s2: cache->buffer + parent_mime_offset) == 0)
1037 break;
1038 }
1039
1040 if (l == p)
1041 all_parents[p++] = cache->buffer + parent_mime_offset;
1042 }
1043
1044 break;
1045 }
1046 }
1047 }
1048 all_parents[p++] = NULL;
1049
1050 result = (char **) malloc (size: p * sizeof (char *));
1051 memcpy (dest: result, src: all_parents, n: p * sizeof (char *));
1052
1053 return result;
1054}
1055
1056static const char *
1057cache_lookup_icon (const char *mime, int header)
1058{
1059 const char *ptr;
1060 int i, min, max, mid, cmp;
1061
1062 for (i = 0; _caches[i]; i++)
1063 {
1064 XdgMimeCache *cache = _caches[i];
1065 xdg_uint32_t list_offset;
1066 xdg_uint32_t n_entries;
1067 xdg_uint32_t offset;
1068
1069 if (cache->buffer == NULL)
1070 continue;
1071
1072 list_offset = GET_UINT32 (cache->buffer, header);
1073 n_entries = GET_UINT32 (cache->buffer, list_offset);
1074
1075 min = 0;
1076 max = n_entries - 1;
1077 while (max >= min)
1078 {
1079 mid = (min + max) / 2;
1080
1081 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
1082 ptr = cache->buffer + offset;
1083 cmp = strcmp (s1: ptr, s2: mime);
1084
1085 if (cmp < 0)
1086 min = mid + 1;
1087 else if (cmp > 0)
1088 max = mid - 1;
1089 else
1090 {
1091 offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
1092 return cache->buffer + offset;
1093 }
1094 }
1095 }
1096
1097 return NULL;
1098}
1099
1100const char *
1101_xdg_mime_cache_get_generic_icon (const char *mime)
1102{
1103 return cache_lookup_icon (mime, header: 36);
1104}
1105
1106const char *
1107_xdg_mime_cache_get_icon (const char *mime)
1108{
1109 return cache_lookup_icon (mime, header: 32);
1110}
1111
1112#ifdef NOT_USED_IN_GIO
1113
1114static void
1115dump_glob_node (XdgMimeCache *cache,
1116 xdg_uint32_t offset,
1117 int depth)
1118{
1119 xdg_unichar_t character;
1120 xdg_uint32_t mime_offset;
1121 xdg_uint32_t n_children;
1122 xdg_uint32_t child_offset;
1123 int i;
1124
1125 character = GET_UINT32 (cache->buffer, offset);
1126 mime_offset = GET_UINT32 (cache->buffer, offset + 4);
1127 n_children = GET_UINT32 (cache->buffer, offset + 8);
1128 child_offset = GET_UINT32 (cache->buffer, offset + 12);
1129 for (i = 0; i < depth; i++)
1130 printf (" ");
1131 printf ("%c", character);
1132 if (mime_offset)
1133 printf (" - %s", cache->buffer + mime_offset);
1134 printf ("\n");
1135 if (child_offset)
1136 {
1137 for (i = 0; i < n_children; i++)
1138 dump_glob_node (cache, child_offset + 20 * i, depth + 1);
1139 }
1140}
1141
1142void
1143_xdg_mime_cache_glob_dump (void)
1144{
1145 int i, j;
1146 for (i = 0; _caches[i]; i++)
1147 {
1148 XdgMimeCache *cache = _caches[i];
1149 xdg_uint32_t list_offset;
1150 xdg_uint32_t n_entries;
1151 xdg_uint32_t offset;
1152
1153 if (cache->buffer == NULL)
1154 continue;
1155
1156 list_offset = GET_UINT32 (cache->buffer, 16);
1157 n_entries = GET_UINT32 (cache->buffer, list_offset);
1158 offset = GET_UINT32 (cache->buffer, list_offset + 4);
1159 for (j = 0; j < n_entries; j++)
1160 dump_glob_node (cache, offset + 20 * j, 0);
1161 }
1162}
1163
1164#endif
1165

source code of gtk/subprojects/glib/gio/xdgmime/xdgmimecache.c