]>
Commit | Line | Data |
---|---|---|
a1d229d1 DM |
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 "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 | free (data); | |
562 | fclose (file); | |
563 | ||
564 | if (mime_type) | |
565 | return mime_type; | |
566 | ||
567 | return _xdg_binary_or_text_fallback(data, bytes_read); | |
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 | xdg_mime_is_super_type (const char *mime) | |
745 | { | |
746 | int length; | |
747 | const char *type; | |
748 | ||
749 | length = strlen (mime); | |
750 | type = &(mime[length - 2]); | |
751 | ||
752 | if (strcmp (type, "/*") == 0) | |
753 | return 1; | |
754 | ||
755 | return 0; | |
756 | } | |
757 | #endif | |
758 | ||
759 | int | |
760 | _xdg_mime_mime_type_subclass (const char *mime, | |
761 | const char *base) | |
762 | { | |
763 | const char *umime, *ubase; | |
764 | const char **parents; | |
765 | ||
766 | if (_caches) | |
767 | return _xdg_mime_cache_mime_type_subclass (mime, base); | |
768 | ||
769 | umime = _xdg_mime_unalias_mime_type (mime); | |
770 | ubase = _xdg_mime_unalias_mime_type (base); | |
771 | ||
772 | if (strcmp (umime, ubase) == 0) | |
773 | return 1; | |
774 | ||
775 | #if 1 | |
776 | /* Handle supertypes */ | |
777 | if (xdg_mime_is_super_type (ubase) && | |
778 | xdg_mime_media_type_equal (umime, ubase)) | |
779 | return 1; | |
780 | #endif | |
781 | ||
782 | /* Handle special cases text/plain and application/octet-stream */ | |
783 | if (strcmp (ubase, "text/plain") == 0 && | |
784 | strncmp (umime, "text/", 5) == 0) | |
785 | return 1; | |
786 | ||
787 | if (strcmp (ubase, "application/octet-stream") == 0) | |
788 | return 1; | |
789 | ||
790 | parents = _xdg_mime_parent_list_lookup (parent_list, umime); | |
791 | for (; parents && *parents; parents++) | |
792 | { | |
793 | if (_xdg_mime_mime_type_subclass (*parents, ubase)) | |
794 | return 1; | |
795 | } | |
796 | ||
797 | return 0; | |
798 | } | |
799 | ||
800 | int | |
801 | xdg_mime_mime_type_subclass (const char *mime, | |
802 | const char *base) | |
803 | { | |
804 | xdg_mime_init (); | |
805 | ||
806 | return _xdg_mime_mime_type_subclass (mime, base); | |
807 | } | |
808 | ||
809 | char ** | |
810 | xdg_mime_list_mime_parents (const char *mime) | |
811 | { | |
812 | const char **parents; | |
813 | char **result; | |
814 | int i, n; | |
815 | ||
816 | if (_caches) | |
817 | return _xdg_mime_cache_list_mime_parents (mime); | |
818 | ||
819 | parents = xdg_mime_get_mime_parents (mime); | |
820 | ||
821 | if (!parents) | |
822 | return NULL; | |
823 | ||
824 | for (i = 0; parents[i]; i++) ; | |
825 | ||
826 | n = (i + 1) * sizeof (char *); | |
827 | result = (char **) malloc (n); | |
828 | memcpy (result, parents, n); | |
829 | ||
830 | return result; | |
831 | } | |
832 | ||
833 | const char ** | |
834 | xdg_mime_get_mime_parents (const char *mime) | |
835 | { | |
836 | const char *umime; | |
837 | ||
838 | xdg_mime_init (); | |
839 | ||
840 | umime = _xdg_mime_unalias_mime_type (mime); | |
841 | ||
842 | return _xdg_mime_parent_list_lookup (parent_list, umime); | |
843 | } | |
844 | ||
845 | void | |
846 | xdg_mime_dump (void) | |
847 | { | |
848 | xdg_mime_init(); | |
849 | ||
850 | printf ("*** ALIASES ***\n\n"); | |
851 | _xdg_mime_alias_list_dump (alias_list); | |
852 | printf ("\n*** PARENTS ***\n\n"); | |
853 | _xdg_mime_parent_list_dump (parent_list); | |
854 | printf ("\n*** CACHE ***\n\n"); | |
855 | _xdg_glob_hash_dump (global_hash); | |
856 | printf ("\n*** GLOBS ***\n\n"); | |
857 | _xdg_glob_hash_dump (global_hash); | |
858 | printf ("\n*** GLOBS REVERSE TREE ***\n\n"); | |
859 | _xdg_mime_cache_glob_dump (); | |
860 | } | |
861 | ||
862 | ||
863 | /* Registers a function to be called every time the mime database reloads its files | |
864 | */ | |
865 | int | |
866 | xdg_mime_register_reload_callback (XdgMimeCallback callback, | |
867 | void *data, | |
868 | XdgMimeDestroy destroy) | |
869 | { | |
870 | XdgCallbackList *list_el; | |
871 | static int callback_id = 1; | |
872 | ||
873 | /* Make a new list element */ | |
874 | list_el = calloc (1, sizeof (XdgCallbackList)); | |
875 | list_el->callback_id = callback_id; | |
876 | list_el->callback = callback; | |
877 | list_el->data = data; | |
878 | list_el->destroy = destroy; | |
879 | list_el->next = callback_list; | |
880 | if (list_el->next) | |
881 | list_el->next->prev = list_el; | |
882 | ||
883 | callback_list = list_el; | |
884 | callback_id ++; | |
885 | ||
886 | return callback_id - 1; | |
887 | } | |
888 | ||
889 | void | |
890 | xdg_mime_remove_callback (int callback_id) | |
891 | { | |
892 | XdgCallbackList *list; | |
893 | ||
894 | for (list = callback_list; list; list = list->next) | |
895 | { | |
896 | if (list->callback_id == callback_id) | |
897 | { | |
898 | if (list->next) | |
899 | list->next = list->prev; | |
900 | ||
901 | if (list->prev) | |
902 | list->prev->next = list->next; | |
903 | else | |
904 | callback_list = list->next; | |
905 | ||
906 | /* invoke the destroy handler */ | |
907 | (list->destroy) (list->data); | |
908 | free (list); | |
909 | return; | |
910 | } | |
911 | } | |
912 | } | |
913 | ||
914 | const char * | |
915 | xdg_mime_get_icon (const char *mime) | |
916 | { | |
917 | xdg_mime_init (); | |
918 | ||
919 | if (_caches) | |
920 | return _xdg_mime_cache_get_icon (mime); | |
921 | ||
922 | return _xdg_mime_icon_list_lookup (icon_list, mime); | |
923 | } | |
924 | ||
925 | const char * | |
926 | xdg_mime_get_generic_icon (const char *mime) | |
927 | { | |
928 | xdg_mime_init (); | |
929 | ||
930 | if (_caches) | |
931 | return _xdg_mime_cache_get_generic_icon (mime); | |
932 | ||
933 | return _xdg_mime_icon_list_lookup (generic_icon_list, mime); | |
934 | } |