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