• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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