Coverage Report

Created: 2026-07-16 06:32

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