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