Coverage Report

Created: 2025-07-01 07:09

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