Coverage Report

Created: 2026-02-26 06:23

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