~ubuntu-branches/ubuntu/quantal/xarchiver/quantal

« back to all changes in this revision

Viewing changes to src/xdgmime/xdgmime.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Baumann
  • Date: 2008-11-07 14:54:00 UTC
  • mfrom: (1.1.7 upstream) (2.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20081107145400-z0j3jmgads8coae2
Tags: 0.5.1-1
MergingĀ upstreamĀ versionĀ 0.5.1.

Show diffs side-by-side

added added

removed removed

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