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