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
75 struct _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
87 XdgMimeCache *
_xdg_mime_cache_ref(XdgMimeCache * cache)88 _xdg_mime_cache_ref (XdgMimeCache *cache)
89 {
90 cache->ref_count++;
91 return cache;
92 }
93
94 void
_xdg_mime_cache_unref(XdgMimeCache * cache)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 (cache->buffer, cache->size);
103 #endif
104 free (cache);
105 }
106 }
107
108 XdgMimeCache *
_xdg_mime_cache_new_from_file(const char * file_name)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_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, &st) < 0 || st.st_size < 4)
128 goto done;
129
130 buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 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 (buffer, st.st_size);
142
143 goto done;
144 }
145
146 cache = (XdgMimeCache *) malloc (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);
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
167 static int
cache_magic_matchlet_compare_to_data(XdgMimeCache * cache,xdg_uint32_t offset,const void * data,size_t len)168 cache_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
219 static int
cache_magic_matchlet_compare(XdgMimeCache * cache,xdg_uint32_t offset,const void * data,size_t len)220 cache_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, child_offset + 32 * i,
238 data, len))
239 return TRUE;
240 }
241 }
242
243 return FALSE;
244 }
245
246 static const char *
cache_magic_compare_to_data(XdgMimeCache * cache,xdg_uint32_t offset,const void * data,size_t len,int * prio)247 cache_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, 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
274 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)275 cache_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 + 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_types[n], non_match))
314 mime_types[n] = NULL;
315 }
316 }
317 }
318
319 return NULL;
320 }
321
322 static const char *
cache_alias_lookup(const char * alias)323 cache_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 (ptr, 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
366 typedef struct {
367 const char *mime;
368 int weight;
369 } MimeWeight;
370
371 static int
cache_glob_lookup_literal(const char * file_name,const char * mime_types[],int n_mime_types,int case_sensitive_check)372 cache_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 (ptr, 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
428 static int
cache_glob_lookup_fnmatch(const char * file_name,MimeWeight mime_types[],int n_mime_types)429 cache_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 (ptr, file_name, 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
477 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 case_sensitive_check,MimeWeight mime_types[],int n_mime_types)478 cache_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_children, 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
556 static int
cache_glob_lookup_suffix(const char * file_name,int len,int ignore_case,MimeWeight mime_types[],int n_mime_types)557 cache_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 ignore_case,
585 mime_types + n,
586 n_mime_types - n);
587 if (n == n_mime_types)
588 break;
589 }
590
591 return n;
592 }
593
compare_mime_weight(const void * a,const void * b)594 static 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')
603 static char *
ascii_tolower(const char * str)604 ascii_tolower (const char *str)
605 {
606 char *p, *lower;
607
608 lower = strdup (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
618 static int
filter_out_dupes(MimeWeight mimes[],int n_mimes)619 filter_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 (mimes[i].mime, 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
646 static int
cache_glob_lookup_file_name(const char * file_name,const char * mime_types[],int n_mime_types)647 cache_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 (file_name);
663
664 n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE);
665 if (n > 0)
666 {
667 free (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 (lower_case);
675 return n;
676 }
677
678 len = strlen (file_name);
679 n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes);
680 if (n < 2)
681 n += cache_glob_lookup_suffix (file_name, len, TRUE, mimes + n, n_mimes - n);
682
683 free (lower_case);
684
685 /* Last, try fnmatch */
686 if (n < 2)
687 n += cache_glob_lookup_fnmatch (file_name, mimes + n, n_mimes - n);
688
689 n = filter_out_dupes (mimes, n);
690
691 qsort (mimes, n, sizeof (MimeWeight), 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
702 int
_xdg_mime_cache_get_max_buffer_extents(void)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
724 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)725 cache_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,
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
770 const char *
_xdg_mime_cache_get_mime_type_for_data(const void * data,size_t len,int * result_prio)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, 0);
776 }
777
778 #ifdef NOT_USED_IN_GIO
779
780 const char *
_xdg_mime_cache_get_mime_type_for_file(const char * file_name,struct stat * statbuf)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
855 const char *
_xdg_mime_cache_get_mime_type_from_file_name(const char * file_name)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
868 int
_xdg_mime_cache_get_mime_types_from_file_name(const char * file_name,const char * mime_types[],int n_mime_types)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
877 static int
ends_with(const char * str,const char * suffix)878 ends_with (const char *str,
879 const char *suffix)
880 {
881 int length;
882 int suffix_length;
883
884 length = strlen (str);
885 suffix_length = strlen (suffix);
886 if (length < suffix_length)
887 return 0;
888
889 if (strcmp (str + length - suffix_length, suffix) == 0)
890 return 1;
891
892 return 0;
893 }
894
895 static int
is_super_type(const char * mime)896 is_super_type (const char *mime)
897 {
898 return ends_with (mime, "/*");
899 }
900 #endif
901
902 int
_xdg_mime_cache_mime_type_subclass(const char * mime,const char * base)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 (base);
912
913 if (strcmp (umime, 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 (ubase) &&
922 xdg_mime_media_type_equal (umime, ubase))
923 return 1;
924 #endif
925
926 /* Handle special cases text/plain and application/octet-stream */
927 if (strcmp (ubase, "text/plain") == 0 &&
928 strncmp (umime, "text/", 5) == 0)
929 return 1;
930
931 if (strcmp (ubase, "application/octet-stream") == 0 &&
932 strncmp (umime, "inode/", 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 (cache->buffer + offset, 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 (cache->buffer + parent_offset, mime) != 0 &&
969 strcmp (cache->buffer + parent_offset, umime) != 0 &&
970 _xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
971 return 1;
972 }
973
974 break;
975 }
976 }
977 }
978
979 return 0;
980 }
981
982 const char *
_xdg_mime_cache_unalias_mime_type(const char * mime)983 _xdg_mime_cache_unalias_mime_type (const char *mime)
984 {
985 const char *lookup;
986
987 lookup = cache_alias_lookup (mime);
988
989 if (lookup)
990 return lookup;
991
992 return mime;
993 }
994
995 char **
_xdg_mime_cache_list_mime_parents(const char * mime)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 (cache->buffer + mimetype_offset, 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 (all_parents[l], 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 (p * sizeof (char *));
1051 memcpy (result, all_parents, p * sizeof (char *));
1052
1053 return result;
1054 }
1055
1056 static const char *
cache_lookup_icon(const char * mime,int header)1057 cache_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 (ptr, 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
1100 const char *
_xdg_mime_cache_get_generic_icon(const char * mime)1101 _xdg_mime_cache_get_generic_icon (const char *mime)
1102 {
1103 return cache_lookup_icon (mime, 36);
1104 }
1105
1106 const char *
_xdg_mime_cache_get_icon(const char * mime)1107 _xdg_mime_cache_get_icon (const char *mime)
1108 {
1109 return cache_lookup_icon (mime, 32);
1110 }
1111
1112 #ifdef NOT_USED_IN_GIO
1113
1114 static void
dump_glob_node(XdgMimeCache * cache,xdg_uint32_t offset,int depth)1115 dump_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
1142 void
_xdg_mime_cache_glob_dump(void)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