Coverage Report

Created: 2025-06-22 06:29

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