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