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