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