Coverage Report

Created: 2018-09-25 13:52

/src/glib/gio/xdgmime/xdgmime.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: C; c-file-style: "gnu" -*- */
2
/* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
3
 *
4
 * More info can be found at http://www.freedesktop.org/standards/
5
 * 
6
 * Copyright (C) 2003,2004  Red Hat, Inc.
7
 * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
8
 *
9
 * Licensed under the Academic Free License version 2.0
10
 * Or under the following terms:
11
 * 
12
 * This library is free software; you can redistribute it and/or
13
 * modify it under the terms of the GNU Lesser General Public
14
 * License as published by the Free Software Foundation; either
15
 * version 2.1 of the License, or (at your option) any later version.
16
 *
17
 * This library is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
 * Lesser General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Lesser General Public
23
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24
 */
25
26
#include "config.h"
27
28
#include "xdgmime.h"
29
#include "xdgmimeint.h"
30
#include "xdgmimeglob.h"
31
#include "xdgmimemagic.h"
32
#include "xdgmimealias.h"
33
#include "xdgmimeicon.h"
34
#include "xdgmimeparent.h"
35
#include "xdgmimecache.h"
36
#include <stdio.h>
37
#include <string.h>
38
#include <sys/stat.h>
39
#include <sys/types.h>
40
#include <sys/time.h>
41
#include <unistd.h>
42
#include <assert.h>
43
44
typedef struct XdgDirTimeList XdgDirTimeList;
45
typedef struct XdgCallbackList XdgCallbackList;
46
47
static int need_reread = TRUE;
48
static time_t last_stat_time = 0;
49
50
static XdgGlobHash *global_hash = NULL;
51
static XdgMimeMagic *global_magic = NULL;
52
static XdgAliasList *alias_list = NULL;
53
static XdgParentList *parent_list = NULL;
54
static XdgDirTimeList *dir_time_list = NULL;
55
static XdgCallbackList *callback_list = NULL;
56
static XdgIconList *icon_list = NULL;
57
static XdgIconList *generic_icon_list = NULL;
58
59
XdgMimeCache **_caches = NULL;
60
static int n_caches = 0;
61
62
const char xdg_mime_type_unknown[] = "application/octet-stream";
63
const char xdg_mime_type_empty[] = "application/x-zerosize";
64
const char xdg_mime_type_textplain[] = "text/plain";
65
66
67
enum
68
{
69
  XDG_CHECKED_UNCHECKED,
70
  XDG_CHECKED_VALID,
71
  XDG_CHECKED_INVALID
72
};
73
74
struct XdgDirTimeList
75
{
76
  time_t mtime;
77
  char *directory_name;
78
  int checked;
79
  XdgDirTimeList *next;
80
};
81
82
struct XdgCallbackList
83
{
84
  XdgCallbackList *next;
85
  XdgCallbackList *prev;
86
  int              callback_id;
87
  XdgMimeCallback  callback;
88
  void            *data;
89
  XdgMimeDestroy   destroy;
90
};
91
92
/* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
93
 * directories aren't looked at */
94
typedef int (*XdgDirectoryFunc) (const char *directory,
95
         void       *user_data);
96
97
static void
98
xdg_dir_time_list_add (char   *file_name, 
99
           time_t  mtime)
100
0
{
101
0
  XdgDirTimeList *list;
102
0
103
0
  for (list = dir_time_list; list; list = list->next) 
104
0
    {
105
0
      if (strcmp (list->directory_name, file_name) == 0)
106
0
        {
107
0
          free (file_name);
108
0
          return;
109
0
        }
110
0
    }
111
0
  
112
0
  list = calloc (1, sizeof (XdgDirTimeList));
113
0
  list->checked = XDG_CHECKED_UNCHECKED;
114
0
  list->directory_name = file_name;
115
0
  list->mtime = mtime;
116
0
  list->next = dir_time_list;
117
0
  dir_time_list = list;
118
0
}
119
 
120
static void
121
xdg_dir_time_list_free (XdgDirTimeList *list)
122
0
{
123
0
  XdgDirTimeList *next;
124
0
125
0
  while (list)
126
0
    {
127
0
      next = list->next;
128
0
      free (list->directory_name);
129
0
      free (list);
130
0
      list = next;
131
0
    }
132
0
}
133
134
static int
135
xdg_mime_init_from_directory (const char *directory)
136
0
{
137
0
  char *file_name;
138
0
  struct stat st;
139
0
140
0
  assert (directory != NULL);
141
0
142
0
  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
143
0
  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
144
0
  if (stat (file_name, &st) == 0)
145
0
    {
146
0
      XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
147
0
148
0
      if (cache != NULL)
149
0
  {
150
0
    xdg_dir_time_list_add (file_name, st.st_mtime);
151
0
152
0
    _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
153
0
    _caches[n_caches] = cache;
154
0
          _caches[n_caches + 1] = NULL;
155
0
    n_caches++;
156
0
157
0
    return FALSE;
158
0
  }
159
0
    }
160
0
  free (file_name);
161
0
162
0
  file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1);
163
0
  strcpy (file_name, directory); strcat (file_name, "/mime/globs2");
164
0
  if (stat (file_name, &st) == 0)
165
0
    {
166
0
      _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE);
167
0
      xdg_dir_time_list_add (file_name, st.st_mtime);
168
0
    }
169
0
  else
170
0
    {
171
0
      free (file_name);
172
0
      file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
173
0
      strcpy (file_name, directory); strcat (file_name, "/mime/globs");
174
0
      if (stat (file_name, &st) == 0)
175
0
        {
176
0
          _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE);
177
0
          xdg_dir_time_list_add (file_name, st.st_mtime);
178
0
        }
179
0
      else
180
0
        {
181
0
          free (file_name);
182
0
        }
183
0
    }
184
0
185
0
  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
186
0
  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
187
0
  if (stat (file_name, &st) == 0)
188
0
    {
189
0
      _xdg_mime_magic_read_from_file (global_magic, file_name);
190
0
      xdg_dir_time_list_add (file_name, st.st_mtime);
191
0
    }
192
0
  else
193
0
    {
194
0
      free (file_name);
195
0
    }
196
0
197
0
  file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
198
0
  strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
199
0
  _xdg_mime_alias_read_from_file (alias_list, file_name);
200
0
  free (file_name);
201
0
202
0
  file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
203
0
  strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
204
0
  _xdg_mime_parent_read_from_file (parent_list, file_name);
205
0
  free (file_name);
206
0
207
0
  file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1);
208
0
  strcpy (file_name, directory); strcat (file_name, "/mime/icons");
209
0
  _xdg_mime_icon_read_from_file (icon_list, file_name);
210
0
  free (file_name);
211
0
212
0
  file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1);
213
0
  strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons");
214
0
  _xdg_mime_icon_read_from_file (generic_icon_list, file_name);
215
0
  free (file_name);
216
0
217
0
  return FALSE; /* Keep processing */
218
0
}
219
220
/* Runs a command on all the directories in the search path */
221
static void
222
xdg_run_command_on_dirs (XdgDirectoryFunc  func,
223
       void             *user_data)
224
0
{
225
0
  const char *xdg_data_home;
226
0
  const char *xdg_data_dirs;
227
0
  const char *ptr;
228
0
229
0
  xdg_data_home = getenv ("XDG_DATA_HOME");
230
0
  if (xdg_data_home)
231
0
    {
232
0
      if ((func) (xdg_data_home, user_data))
233
0
  return;
234
0
    }
235
0
  else
236
0
    {
237
0
      const char *home;
238
0
239
0
      home = getenv ("HOME");
240
0
      if (home != NULL)
241
0
  {
242
0
    char *guessed_xdg_home;
243
0
    int stop_processing;
244
0
245
0
    guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
246
0
    strcpy (guessed_xdg_home, home);
247
0
    strcat (guessed_xdg_home, "/.local/share/");
248
0
    stop_processing = (func) (guessed_xdg_home, user_data);
249
0
    free (guessed_xdg_home);
250
0
251
0
    if (stop_processing)
252
0
      return;
253
0
  }
254
0
    }
255
0
256
0
  xdg_data_dirs = getenv ("XDG_DATA_DIRS");
257
0
  if (xdg_data_dirs == NULL)
258
0
    xdg_data_dirs = "/usr/local/share/:/usr/share/";
259
0
260
0
  ptr = xdg_data_dirs;
261
0
262
0
  while (*ptr != '\000')
263
0
    {
264
0
      const char *end_ptr;
265
0
      char *dir;
266
0
      int len;
267
0
      int stop_processing;
268
0
269
0
      end_ptr = ptr;
270
0
      while (*end_ptr != ':' && *end_ptr != '\000')
271
0
  end_ptr ++;
272
0
273
0
      if (end_ptr == ptr)
274
0
  {
275
0
    ptr++;
276
0
    continue;
277
0
  }
278
0
279
0
      if (*end_ptr == ':')
280
0
  len = end_ptr - ptr;
281
0
      else
282
0
  len = end_ptr - ptr + 1;
283
0
      dir = malloc (len + 1);
284
0
      strncpy (dir, ptr, len);
285
0
      dir[len] = '\0';
286
0
      stop_processing = (func) (dir, user_data);
287
0
      free (dir);
288
0
289
0
      if (stop_processing)
290
0
  return;
291
0
292
0
      ptr = end_ptr;
293
0
    }
294
0
}
295
296
/* Checks file_path to make sure it has the same mtime as last time it was
297
 * checked.  If it has a different mtime, or if the file doesn't exist, it
298
 * returns FALSE.
299
 *
300
 * FIXME: This doesn't protect against permission changes.
301
 */
302
static int
303
xdg_check_file (const char *file_path,
304
                int        *exists)
305
0
{
306
0
  struct stat st;
307
0
308
0
  /* If the file exists */
309
0
  if (stat (file_path, &st) == 0)
310
0
    {
311
0
      XdgDirTimeList *list;
312
0
313
0
      if (exists)
314
0
        *exists = TRUE;
315
0
316
0
      for (list = dir_time_list; list; list = list->next)
317
0
  {
318
0
    if (! strcmp (list->directory_name, file_path))
319
0
      {
320
0
        if (st.st_mtime == list->mtime)
321
0
    list->checked = XDG_CHECKED_VALID;
322
0
        else 
323
0
    list->checked = XDG_CHECKED_INVALID;
324
0
325
0
        return (list->checked != XDG_CHECKED_VALID);
326
0
      }
327
0
  }
328
0
      return TRUE;
329
0
    }
330
0
331
0
  if (exists)
332
0
    *exists = FALSE;
333
0
334
0
  return FALSE;
335
0
}
336
337
static int
338
xdg_check_dir (const char *directory,
339
         int        *invalid_dir_list)
340
0
{
341
0
  int invalid, exists;
342
0
  char *file_name;
343
0
344
0
  assert (directory != NULL);
345
0
346
0
  /* Check the mime.cache file */
347
0
  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
348
0
  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
349
0
  invalid = xdg_check_file (file_name, &exists);
350
0
  free (file_name);
351
0
  if (invalid)
352
0
    {
353
0
      *invalid_dir_list = TRUE;
354
0
      return TRUE;
355
0
    }
356
0
  else if (exists)
357
0
    {
358
0
      return FALSE;
359
0
    }
360
0
361
0
  /* Check the globs file */
362
0
  file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
363
0
  strcpy (file_name, directory); strcat (file_name, "/mime/globs");
364
0
  invalid = xdg_check_file (file_name, NULL);
365
0
  free (file_name);
366
0
  if (invalid)
367
0
    {
368
0
      *invalid_dir_list = TRUE;
369
0
      return TRUE;
370
0
    }
371
0
372
0
  /* Check the magic file */
373
0
  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
374
0
  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
375
0
  invalid = xdg_check_file (file_name, NULL);
376
0
  free (file_name);
377
0
  if (invalid)
378
0
    {
379
0
      *invalid_dir_list = TRUE;
380
0
      return TRUE;
381
0
    }
382
0
383
0
  return FALSE; /* Keep processing */
384
0
}
385
386
/* Walks through all the mime files stat()ing them to see if they've changed.
387
 * Returns TRUE if they have. */
388
static int
389
xdg_check_dirs (void)
390
0
{
391
0
  XdgDirTimeList *list;
392
0
  int invalid_dir_list = FALSE;
393
0
394
0
  for (list = dir_time_list; list; list = list->next)
395
0
    list->checked = XDG_CHECKED_UNCHECKED;
396
0
397
0
  xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
398
0
         &invalid_dir_list);
399
0
400
0
  if (invalid_dir_list)
401
0
    return TRUE;
402
0
403
0
  for (list = dir_time_list; list; list = list->next)
404
0
    {
405
0
      if (list->checked != XDG_CHECKED_VALID)
406
0
  return TRUE;
407
0
    }
408
0
409
0
  return FALSE;
410
0
}
411
412
/* We want to avoid stat()ing on every single mime call, so we only look for
413
 * newer files every 5 seconds.  This will return TRUE if we need to reread the
414
 * mime data from disk.
415
 */
416
static int
417
xdg_check_time_and_dirs (void)
418
0
{
419
0
  struct timeval tv;
420
0
  time_t current_time;
421
0
  int retval = FALSE;
422
0
423
0
  gettimeofday (&tv, NULL);
424
0
  current_time = tv.tv_sec;
425
0
426
0
  if (current_time >= last_stat_time + 5)
427
0
    {
428
0
      retval = xdg_check_dirs ();
429
0
      last_stat_time = current_time;
430
0
    }
431
0
432
0
  return retval;
433
0
}
434
435
/* Called in every public function.  It reloads the hash function if need be.
436
 */
437
static void
438
xdg_mime_init (void)
439
0
{
440
0
  if (xdg_check_time_and_dirs ())
441
0
    {
442
0
      xdg_mime_shutdown ();
443
0
    }
444
0
445
0
  if (need_reread)
446
0
    {
447
0
      global_hash = _xdg_glob_hash_new ();
448
0
      global_magic = _xdg_mime_magic_new ();
449
0
      alias_list = _xdg_mime_alias_list_new ();
450
0
      parent_list = _xdg_mime_parent_list_new ();
451
0
      icon_list = _xdg_mime_icon_list_new ();
452
0
      generic_icon_list = _xdg_mime_icon_list_new ();
453
0
454
0
      xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
455
0
             NULL);
456
0
457
0
      need_reread = FALSE;
458
0
    }
459
0
}
460
461
const char *
462
xdg_mime_get_mime_type_for_data (const void *data,
463
         size_t      len,
464
         int        *result_prio)
465
0
{
466
0
  const char *mime_type;
467
0
468
0
  if (len == 0)
469
0
    {
470
0
      *result_prio = 100;
471
0
      return XDG_MIME_TYPE_EMPTY;
472
0
    }
473
0
474
0
  xdg_mime_init ();
475
0
476
0
  if (_caches)
477
0
    mime_type = _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio);
478
0
  else
479
0
    mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0);
480
0
481
0
  if (mime_type)
482
0
    return mime_type;
483
0
484
0
  return _xdg_binary_or_text_fallback(data, len);
485
0
}
486
487
#ifdef NOT_USED_IN_GIO
488
489
const char *
490
xdg_mime_get_mime_type_for_file (const char  *file_name,
491
                                 struct stat *statbuf)
492
{
493
  const char *mime_type;
494
  /* currently, only a few globs occur twice, and none
495
   * more often, so 5 seems plenty.
496
   */
497
  const char *mime_types[5];
498
  FILE *file;
499
  unsigned char *data;
500
  int max_extent;
501
  int bytes_read;
502
  struct stat buf;
503
  const char *base_name;
504
  int n;
505
506
  if (file_name == NULL)
507
    return NULL;
508
  if (! _xdg_utf8_validate (file_name))
509
    return NULL;
510
511
  xdg_mime_init ();
512
513
  if (_caches)
514
    return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
515
516
  base_name = _xdg_get_base_name (file_name);
517
  n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
518
519
  if (n == 1)
520
    return mime_types[0];
521
522
  if (!statbuf)
523
    {
524
      if (stat (file_name, &buf) != 0)
525
  return XDG_MIME_TYPE_UNKNOWN;
526
527
      statbuf = &buf;
528
    }
529
530
  if (!S_ISREG (statbuf->st_mode))
531
    return XDG_MIME_TYPE_UNKNOWN;
532
533
  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
534
   * be large and need getting from a stream instead of just reading it all
535
   * in. */
536
  max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
537
  data = malloc (max_extent);
538
  if (data == NULL)
539
    return XDG_MIME_TYPE_UNKNOWN;
540
        
541
  file = fopen (file_name, "r");
542
  if (file == NULL)
543
    {
544
      free (data);
545
      return XDG_MIME_TYPE_UNKNOWN;
546
    }
547
548
  bytes_read = fread (data, 1, max_extent, file);
549
  if (ferror (file))
550
    {
551
      free (data);
552
      fclose (file);
553
      return XDG_MIME_TYPE_UNKNOWN;
554
    }
555
556
  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
557
             mime_types, n);
558
559
  free (data);
560
  fclose (file);
561
562
  if (mime_type)
563
    return mime_type;
564
565
  return _xdg_binary_or_text_fallback(data, bytes_read);
566
}
567
568
const char *
569
xdg_mime_get_mime_type_from_file_name (const char *file_name)
570
{
571
  const char *mime_type;
572
573
  xdg_mime_init ();
574
575
  if (_caches)
576
    return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
577
578
  if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
579
    return mime_type;
580
  else
581
    return XDG_MIME_TYPE_UNKNOWN;
582
}
583
584
#endif
585
586
int
587
xdg_mime_get_mime_types_from_file_name (const char *file_name,
588
          const char  *mime_types[],
589
          int          n_mime_types)
590
0
{
591
0
  xdg_mime_init ();
592
0
  
593
0
  if (_caches)
594
0
    return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
595
0
  
596
0
  return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
597
0
}
598
599
#ifdef NOT_USED_IN_GIO
600
601
int
602
xdg_mime_is_valid_mime_type (const char *mime_type)
603
{
604
  /* FIXME: We should make this a better test
605
   */
606
  return _xdg_utf8_validate (mime_type);
607
}
608
609
#endif
610
611
void
612
xdg_mime_shutdown (void)
613
0
{
614
0
  XdgCallbackList *list;
615
0
616
0
  /* FIXME: Need to make this (and the whole library) thread safe */
617
0
  if (dir_time_list)
618
0
    {
619
0
      xdg_dir_time_list_free (dir_time_list);
620
0
      dir_time_list = NULL;
621
0
    }
622
0
  
623
0
  if (global_hash)
624
0
    {
625
0
      _xdg_glob_hash_free (global_hash);
626
0
      global_hash = NULL;
627
0
    }
628
0
  if (global_magic)
629
0
    {
630
0
      _xdg_mime_magic_free (global_magic);
631
0
      global_magic = NULL;
632
0
    }
633
0
634
0
  if (alias_list)
635
0
    {
636
0
      _xdg_mime_alias_list_free (alias_list);
637
0
      alias_list = NULL;
638
0
    }
639
0
640
0
  if (parent_list)
641
0
    {
642
0
      _xdg_mime_parent_list_free (parent_list);
643
0
      parent_list = NULL;
644
0
    }
645
0
646
0
  if (icon_list)
647
0
    {
648
0
      _xdg_mime_icon_list_free (icon_list);
649
0
      icon_list = NULL;
650
0
    }
651
0
652
0
  if (generic_icon_list)
653
0
    {
654
0
      _xdg_mime_icon_list_free (generic_icon_list);
655
0
      generic_icon_list = NULL;
656
0
    }
657
0
  
658
0
  if (_caches)
659
0
    {
660
0
      int i;
661
0
662
0
      for (i = 0; i < n_caches; i++)
663
0
        _xdg_mime_cache_unref (_caches[i]);
664
0
      free (_caches);
665
0
      _caches = NULL;
666
0
      n_caches = 0;
667
0
    }
668
0
669
0
  for (list = callback_list; list; list = list->next)
670
0
    (list->callback) (list->data);
671
0
672
0
  need_reread = TRUE;
673
0
}
674
675
int
676
xdg_mime_get_max_buffer_extents (void)
677
0
{
678
0
  xdg_mime_init ();
679
0
  
680
0
  if (_caches)
681
0
    return _xdg_mime_cache_get_max_buffer_extents ();
682
0
683
0
  return _xdg_mime_magic_get_buffer_extents (global_magic);
684
0
}
685
686
const char *
687
_xdg_mime_unalias_mime_type (const char *mime_type)
688
0
{
689
0
  const char *lookup;
690
0
691
0
  if (_caches)
692
0
    return _xdg_mime_cache_unalias_mime_type (mime_type);
693
0
694
0
  if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
695
0
    return lookup;
696
0
697
0
  return mime_type;
698
0
}
699
700
const char *
701
xdg_mime_unalias_mime_type (const char *mime_type)
702
0
{
703
0
  xdg_mime_init ();
704
0
705
0
  return _xdg_mime_unalias_mime_type (mime_type);
706
0
}
707
708
int
709
_xdg_mime_mime_type_equal (const char *mime_a,
710
         const char *mime_b)
711
0
{
712
0
  const char *unalias_a, *unalias_b;
713
0
714
0
  unalias_a = _xdg_mime_unalias_mime_type (mime_a);
715
0
  unalias_b = _xdg_mime_unalias_mime_type (mime_b);
716
0
717
0
  if (strcmp (unalias_a, unalias_b) == 0)
718
0
    return 1;
719
0
720
0
  return 0;
721
0
}
722
723
int
724
xdg_mime_mime_type_equal (const char *mime_a,
725
        const char *mime_b)
726
0
{
727
0
  xdg_mime_init ();
728
0
729
0
  return _xdg_mime_mime_type_equal (mime_a, mime_b);
730
0
}
731
732
int
733
xdg_mime_media_type_equal (const char *mime_a,
734
         const char *mime_b)
735
0
{
736
0
  char *sep;
737
0
738
0
  sep = strchr (mime_a, '/');
739
0
  
740
0
  if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
741
0
    return 1;
742
0
743
0
  return 0;
744
0
}
745
746
#if 1
747
static int
748
ends_with (const char *str,
749
           const char *suffix)
750
0
{
751
0
  int length;
752
0
  int suffix_length;
753
0
754
0
  length = strlen (str);
755
0
  suffix_length = strlen (suffix);
756
0
  if (length < suffix_length)
757
0
    return 0;
758
0
759
0
  if (strcmp (str + length - suffix_length, suffix) == 0)
760
0
    return 1;
761
0
762
0
  return 0;
763
0
}
764
765
static int
766
xdg_mime_is_super_type (const char *mime)
767
0
{
768
0
  return ends_with (mime, "/*");
769
0
}
770
#endif
771
772
int
773
_xdg_mime_mime_type_subclass (const char *mime,
774
            const char *base)
775
{
776
  const char *umime, *ubase;
777
  const char **parents;
778
779
  if (_caches)
780
    return _xdg_mime_cache_mime_type_subclass (mime, base);
781
782
  umime = _xdg_mime_unalias_mime_type (mime);
783
  ubase = _xdg_mime_unalias_mime_type (base);
784
785
  if (strcmp (umime, ubase) == 0)
786
    return 1;
787
788
#if 1  
789
  /* Handle supertypes */
790
  if (xdg_mime_is_super_type (ubase) &&
791
      xdg_mime_media_type_equal (umime, ubase))
792
    return 1;
793
#endif
794
795
  /*  Handle special cases text/plain and application/octet-stream */
796
  if (strcmp (ubase, "text/plain") == 0 && 
797
      strncmp (umime, "text/", 5) == 0)
798
    return 1;
799
800
  if (strcmp (ubase, "application/octet-stream") == 0 &&
801
      strncmp (umime, "inode/", 6) != 0)
802
    return 1;
803
  
804
  parents = _xdg_mime_parent_list_lookup (parent_list, umime);
805
  for (; parents && *parents; parents++)
806
    {
807
      if (_xdg_mime_mime_type_subclass (*parents, ubase))
808
  return 1;
809
    }
810
811
  return 0;
812
}
813
814
int
815
xdg_mime_mime_type_subclass (const char *mime,
816
           const char *base)
817
0
{
818
0
  xdg_mime_init ();
819
0
820
0
  return _xdg_mime_mime_type_subclass (mime, base);
821
0
}
822
823
char **
824
xdg_mime_list_mime_parents (const char *mime)
825
0
{
826
0
  const char *umime;
827
0
  const char **parents;
828
0
  char **result;
829
0
  int i, n;
830
0
831
0
  xdg_mime_init ();
832
0
833
0
  if (_caches)
834
0
    return _xdg_mime_cache_list_mime_parents (mime);
835
0
836
0
  umime = _xdg_mime_unalias_mime_type (mime);
837
0
838
0
  parents = _xdg_mime_parent_list_lookup (parent_list, umime);
839
0
840
0
  if (!parents)
841
0
    return NULL;
842
0
843
0
  for (i = 0; parents[i]; i++) ;
844
0
  
845
0
  n = (i + 1) * sizeof (char *);
846
0
  result = (char **) malloc (n);
847
0
  memcpy (result, parents, n);
848
0
849
0
  return result;
850
0
}
851
852
#ifdef NOT_USED_IN_GIO
853
854
const char **
855
xdg_mime_get_mime_parents (const char *mime)
856
{
857
  const char *umime;
858
859
  xdg_mime_init ();
860
861
  umime = _xdg_mime_unalias_mime_type (mime);
862
863
  return _xdg_mime_parent_list_lookup (parent_list, umime);
864
}
865
866
void 
867
xdg_mime_dump (void)
868
{
869
  xdg_mime_init();
870
871
  printf ("*** ALIASES ***\n\n");
872
  _xdg_mime_alias_list_dump (alias_list);
873
  printf ("\n*** PARENTS ***\n\n");
874
  _xdg_mime_parent_list_dump (parent_list);
875
  printf ("\n*** CACHE ***\n\n");
876
  _xdg_glob_hash_dump (global_hash);
877
  printf ("\n*** GLOBS ***\n\n");
878
  _xdg_glob_hash_dump (global_hash);
879
  printf ("\n*** GLOBS REVERSE TREE ***\n\n");
880
  _xdg_mime_cache_glob_dump ();
881
}
882
883
#endif
884
885
/* Registers a function to be called every time the mime database reloads its files
886
 */
887
int
888
xdg_mime_register_reload_callback (XdgMimeCallback  callback,
889
           void            *data,
890
           XdgMimeDestroy   destroy)
891
0
{
892
0
  XdgCallbackList *list_el;
893
0
  static int callback_id = 1;
894
0
895
0
  /* Make a new list element */
896
0
  list_el = calloc (1, sizeof (XdgCallbackList));
897
0
  list_el->callback_id = callback_id;
898
0
  list_el->callback = callback;
899
0
  list_el->data = data;
900
0
  list_el->destroy = destroy;
901
0
  list_el->next = callback_list;
902
0
  if (list_el->next)
903
0
    list_el->next->prev = list_el;
904
0
905
0
  callback_list = list_el;
906
0
  callback_id ++;
907
0
908
0
  return callback_id - 1;
909
0
}
910
911
#ifdef NOT_USED_IN_GIO
912
913
void
914
xdg_mime_remove_callback (int callback_id)
915
{
916
  XdgCallbackList *list;
917
918
  for (list = callback_list; list; list = list->next)
919
    {
920
      if (list->callback_id == callback_id)
921
  {
922
    if (list->next)
923
      list->next = list->prev;
924
925
    if (list->prev)
926
      list->prev->next = list->next;
927
    else
928
      callback_list = list->next;
929
930
    /* invoke the destroy handler */
931
    (list->destroy) (list->data);
932
    free (list);
933
    return;
934
  }
935
    }
936
}
937
938
#endif
939
940
const char *
941
xdg_mime_get_icon (const char *mime)
942
0
{
943
0
  xdg_mime_init ();
944
0
  
945
0
  if (_caches)
946
0
    return _xdg_mime_cache_get_icon (mime);
947
0
948
0
  return _xdg_mime_icon_list_lookup (icon_list, mime);
949
0
}
950
951
const char *
952
xdg_mime_get_generic_icon (const char *mime)
953
0
{
954
0
  xdg_mime_init ();
955
0
  
956
0
  if (_caches)
957
0
    return _xdg_mime_cache_get_generic_icon (mime);
958
0
959
0
  return _xdg_mime_icon_list_lookup (generic_icon_list, mime);
960
0
}