Don't use "cp -u" in build script; doesn't work on FreeBSD
[rox-filer.git] / ROX-Filer / src / xdgmime.c
blob51d38d69b8ec5a4192e3eb9f008471e79fabf6e7
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec.
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>
9 * Licensed under the Academic Free License version 2.0
10 * Or under the following terms:
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.
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.
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.
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
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>
47 /* ROX: */
48 #include "global.h"
49 #include "main.h"
51 typedef struct XdgDirTimeList XdgDirTimeList;
52 typedef struct XdgCallbackList XdgCallbackList;
54 static int need_reread = TRUE;
55 static time_t last_stat_time = 0;
57 static XdgGlobHash *global_hash = NULL;
58 static XdgMimeMagic *global_magic = NULL;
59 static XdgAliasList *alias_list = NULL;
60 static XdgParentList *parent_list = NULL;
61 static XdgDirTimeList *dir_time_list = NULL;
62 static XdgCallbackList *callback_list = NULL;
64 XdgMimeCache **_xdg_mime_caches = NULL;
65 static int n_caches = 0;
67 const char xdg_mime_type_unknown[] = "application/octet-stream";
68 const char xdg_mime_type_unknown_text[] = "text/plain"; /* ROX: */
71 enum
73 XDG_CHECKED_UNCHECKED,
74 XDG_CHECKED_VALID,
75 XDG_CHECKED_INVALID
78 struct XdgDirTimeList
80 time_t mtime;
81 char *directory_name;
82 int checked;
83 XdgDirTimeList *next;
84 XdgMimeCache *cache;
87 struct XdgCallbackList
89 XdgCallbackList *next;
90 XdgCallbackList *prev;
91 int callback_id;
92 XdgMimeCallback callback;
93 void *data;
94 XdgMimeDestroy destroy;
97 /* Function called by xdg_run_command_on_dirs. If it returns TRUE, further
98 * directories aren't looked at */
99 typedef int (*XdgDirectoryFunc) (const char *directory,
100 void *user_data);
102 static XdgDirTimeList *
103 xdg_dir_time_list_new (void)
105 XdgDirTimeList *retval;
107 retval = calloc (1, sizeof (XdgDirTimeList));
108 retval->checked = XDG_CHECKED_UNCHECKED;
110 return retval;
113 static void
114 xdg_dir_time_list_free (XdgDirTimeList *list)
116 XdgDirTimeList *next;
118 while (list)
120 next = list->next;
121 free (list->directory_name);
122 free (list);
123 list = next;
127 static int
128 xdg_mime_init_from_directory (const char *directory)
130 char *file_name;
131 struct stat st;
132 XdgDirTimeList *list;
134 assert (directory != NULL);
136 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
137 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
138 if (stat (file_name, &st) == 0)
140 XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
142 if (cache != NULL)
144 list = xdg_dir_time_list_new ();
145 list->directory_name = file_name;
146 list->mtime = st.st_mtime;
147 list->next = dir_time_list;
148 list->cache = cache;
149 dir_time_list = list;
151 _xdg_mime_caches = realloc (_xdg_mime_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
152 _xdg_mime_caches[n_caches] = cache;
153 _xdg_mime_caches[n_caches + 1] = NULL;
154 n_caches++;
156 return FALSE;
159 free (file_name);
161 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
162 strcpy (file_name, directory); strcat (file_name, "/mime/globs");
163 if (stat (file_name, &st) == 0)
165 _xdg_mime_glob_read_from_file (global_hash, file_name);
167 list = xdg_dir_time_list_new ();
168 list->directory_name = file_name;
169 list->mtime = st.st_mtime;
170 list->next = dir_time_list;
171 dir_time_list = list;
173 else
175 free (file_name);
178 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
179 strcpy (file_name, directory); strcat (file_name, "/mime/magic");
180 if (stat (file_name, &st) == 0)
182 _xdg_mime_magic_read_from_file (global_magic, file_name);
184 list = xdg_dir_time_list_new ();
185 list->directory_name = file_name;
186 list->mtime = st.st_mtime;
187 list->next = dir_time_list;
188 dir_time_list = list;
190 else
192 free (file_name);
195 file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
196 strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
197 _xdg_mime_alias_read_from_file (alias_list, file_name);
198 free (file_name);
200 file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
201 strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
202 _xdg_mime_parent_read_from_file (parent_list, file_name);
203 free (file_name);
205 return FALSE; /* Keep processing */
208 /* Runs a command on all the directories in the search path */
209 static void
210 xdg_run_command_on_dirs (XdgDirectoryFunc func,
211 void *user_data)
213 const char *xdg_data_home;
214 const char *xdg_data_dirs;
215 const char *ptr;
217 xdg_data_home = getenv ("XDG_DATA_HOME");
218 if (xdg_data_home)
220 if ((func) (xdg_data_home, user_data))
221 return;
223 else
225 const char *home;
227 home = getenv ("HOME");
228 if (home != NULL)
230 char *guessed_xdg_home;
231 int stop_processing;
233 guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
234 strcpy (guessed_xdg_home, home);
235 strcat (guessed_xdg_home, "/.local/share/");
236 stop_processing = (func) (guessed_xdg_home, user_data);
237 free (guessed_xdg_home);
239 if (stop_processing)
240 return;
244 xdg_data_dirs = getenv ("XDG_DATA_DIRS");
245 if (xdg_data_dirs == NULL)
246 xdg_data_dirs = "/usr/local/share/:/usr/share/";
248 ptr = xdg_data_dirs;
250 while (*ptr != '\000')
252 const char *end_ptr;
253 char *dir;
254 int len;
255 int stop_processing;
257 end_ptr = ptr;
258 while (*end_ptr != ':' && *end_ptr != '\000')
259 end_ptr ++;
261 if (end_ptr == ptr)
263 ptr++;
264 continue;
267 if (*end_ptr == ':')
268 len = end_ptr - ptr;
269 else
270 len = end_ptr - ptr + 1;
271 dir = malloc (len + 1);
272 strncpy (dir, ptr, len);
273 dir[len] = '\0';
274 stop_processing = (func) (dir, user_data);
275 free (dir);
277 if (stop_processing)
278 return;
280 ptr = end_ptr;
284 static XdgMimeCache *
285 xdg_lookup_cache_for_file (const char *file_path)
287 XdgDirTimeList *list;
289 for (list = dir_time_list; list; list = list->next)
290 if (! strcmp (list->directory_name, file_path))
291 return list->cache;
293 return NULL;
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.
300 * FIXME: This doesn't protect against permission changes.
302 static int
303 xdg_check_file (const char *file_path)
305 struct stat st;
307 /* If the file exists */
308 if (stat (file_path, &st) == 0)
310 XdgDirTimeList *list;
312 for (list = dir_time_list; list; list = list->next)
314 if (! strcmp (list->directory_name, file_path) &&
315 st.st_mtime == list->mtime)
317 if (list->checked == XDG_CHECKED_UNCHECKED)
318 list->checked = XDG_CHECKED_VALID;
319 else if (list->checked == XDG_CHECKED_VALID)
320 list->checked = XDG_CHECKED_INVALID;
322 return (list->checked != XDG_CHECKED_VALID);
325 return TRUE;
328 return FALSE;
331 static int
332 xdg_check_dir (const char *directory,
333 int *invalid_dir_list)
335 int invalid, has_cache;
336 char *file_name;
338 assert (directory != NULL);
340 /* Check the mime.cache file */
341 file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
342 strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
343 invalid = xdg_check_file (file_name);
344 has_cache = xdg_lookup_cache_for_file (file_name) != NULL;
345 free (file_name);
347 if (has_cache)
349 if (invalid)
351 *invalid_dir_list = TRUE;
352 return TRUE;
355 return FALSE;
358 /* Check the globs file */
359 file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
360 strcpy (file_name, directory); strcat (file_name, "/mime/globs");
361 invalid = xdg_check_file (file_name);
362 free (file_name);
363 if (invalid)
365 *invalid_dir_list = TRUE;
366 return TRUE;
369 /* Check the magic file */
370 file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
371 strcpy (file_name, directory); strcat (file_name, "/mime/magic");
372 invalid = xdg_check_file (file_name);
373 free (file_name);
374 if (invalid)
376 *invalid_dir_list = TRUE;
377 return TRUE;
380 return FALSE; /* Keep processing */
383 /* Walks through all the mime files stat()ing them to see if they've changed.
384 * Returns TRUE if they have. */
385 static int
386 xdg_check_dirs (void)
388 XdgDirTimeList *list;
389 int invalid_dir_list = FALSE;
391 for (list = dir_time_list; list; list = list->next)
392 list->checked = XDG_CHECKED_UNCHECKED;
394 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
395 &invalid_dir_list);
397 if (invalid_dir_list)
398 return TRUE;
400 for (list = dir_time_list; list; list = list->next)
402 if (list->checked != XDG_CHECKED_VALID)
403 return TRUE;
406 return FALSE;
409 /* We want to avoid stat()ing on every single mime call, so we only look for
410 * newer files every 5 seconds. This will return TRUE if we need to reread the
411 * mime data from disk.
413 static int
414 xdg_check_time_and_dirs (void)
416 struct timeval tv;
417 time_t current_time;
418 int retval = FALSE;
420 gettimeofday (&tv, NULL);
421 current_time = tv.tv_sec;
423 if (current_time >= last_stat_time + 5)
425 retval = xdg_check_dirs ();
426 last_stat_time = current_time;
429 return retval;
432 /* Called in every public function. It reloads the hash function if need be.
434 static void
435 xdg_mime_init (void)
437 if (xdg_check_time_and_dirs ())
439 xdg_mime_shutdown ();
442 if (need_reread)
444 char *mime_parents;
445 int l;
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 ();
452 xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
453 NULL);
455 /* ROX: We want to support shared-mime-database < 0.16, where we can't
456 * do this with the rox.xml file.
458 mime_parents = g_build_filename(app_dir, "subclasses", NULL);
459 _xdg_mime_parent_read_from_file(parent_list, mime_parents);
460 g_free(mime_parents);
462 need_reread = FALSE;
466 const char *
467 xdg_mime_get_mime_type_for_data (const void *data,
468 size_t len)
470 const char *mime_type;
472 xdg_mime_init ();
474 if (_xdg_mime_caches)
475 return _xdg_mime_cache_get_mime_type_for_data (data, len);
477 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, NULL, 0);
479 if (mime_type)
480 return mime_type;
482 return XDG_MIME_TYPE_UNKNOWN;
485 const char *
486 xdg_mime_get_mime_type_for_file (const char *file_name,
487 struct stat *statbuf)
489 const char *mime_type;
490 /* Used to detect whether multiple MIME types match file_name */
491 const char *mime_types[2];
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;
500 if (file_name == NULL)
501 return NULL;
502 if (! _xdg_utf8_validate (file_name))
503 return NULL;
505 xdg_mime_init ();
507 if (_xdg_mime_caches)
508 return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
510 base_name = _xdg_get_base_name (file_name);
511 n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 2);
513 if (n == 1)
514 return mime_types[0];
516 if (!statbuf)
518 if (stat (file_name, &buf) != 0)
519 return XDG_MIME_TYPE_UNKNOWN;
521 statbuf = &buf;
524 if (!S_ISREG (statbuf->st_mode))
525 return XDG_MIME_TYPE_UNKNOWN;
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;
535 file = fopen (file_name, "r");
536 if (file == NULL)
538 free (data);
539 return XDG_MIME_TYPE_UNKNOWN;
542 bytes_read = fread (data, 1, max_extent, file);
543 if (ferror (file))
545 free (data);
546 fclose (file);
547 return XDG_MIME_TYPE_UNKNOWN;
550 mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read,
551 mime_types, n);
553 free (data);
554 fclose (file);
556 if (mime_type)
557 return mime_type;
559 return XDG_MIME_TYPE_UNKNOWN;
562 const char *
563 xdg_mime_get_mime_type_from_file_name (const char *file_name)
565 const char *mime_types[2];
567 xdg_mime_init ();
569 if (_xdg_mime_caches)
570 return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
572 if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, 2) == 1)
573 return mime_types[0];
574 else
575 return XDG_MIME_TYPE_UNKNOWN;
579 xdg_mime_is_valid_mime_type (const char *mime_type)
581 /* FIXME: We should make this a better test
583 return _xdg_utf8_validate (mime_type);
586 void
587 xdg_mime_shutdown (void)
589 XdgCallbackList *list;
591 /* FIXME: Need to make this (and the whole library) thread safe */
592 if (dir_time_list)
594 xdg_dir_time_list_free (dir_time_list);
595 dir_time_list = NULL;
598 if (global_hash)
600 _xdg_glob_hash_free (global_hash);
601 global_hash = NULL;
603 if (global_magic)
605 _xdg_mime_magic_free (global_magic);
606 global_magic = NULL;
609 if (alias_list)
611 _xdg_mime_alias_list_free (alias_list);
612 alias_list = NULL;
615 if (parent_list)
617 _xdg_mime_parent_list_free (parent_list);
618 parent_list = NULL;
621 if (_xdg_mime_caches)
623 int i;
624 for (i = 0; i < n_caches; i++)
625 _xdg_mime_cache_unref (_xdg_mime_caches[i]);
626 free (_xdg_mime_caches);
627 _xdg_mime_caches = NULL;
628 n_caches = 0;
631 for (list = callback_list; list; list = list->next)
632 (list->callback) (list->data);
634 need_reread = TRUE;
638 xdg_mime_get_max_buffer_extents (void)
640 xdg_mime_init ();
642 if (_xdg_mime_caches)
643 return _xdg_mime_cache_get_max_buffer_extents ();
645 return _xdg_mime_magic_get_buffer_extents (global_magic);
648 static const char *
649 _xdg_mime_unalias_mime_type (const char *mime_type)
651 const char *lookup;
653 if (_xdg_mime_caches)
654 return _xdg_mime_cache_unalias_mime_type (mime_type);
656 if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
657 return lookup;
659 return mime_type;
662 const char *
663 xdg_mime_unalias_mime_type (const char *mime_type)
665 xdg_mime_init ();
667 return _xdg_mime_unalias_mime_type (mime_type);
671 _xdg_mime_mime_type_equal (const char *mime_a,
672 const char *mime_b)
674 const char *unalias_a, *unalias_b;
676 unalias_a = _xdg_mime_unalias_mime_type (mime_a);
677 unalias_b = _xdg_mime_unalias_mime_type (mime_b);
679 if (strcmp (unalias_a, unalias_b) == 0)
680 return 1;
682 return 0;
686 xdg_mime_mime_type_equal (const char *mime_a,
687 const char *mime_b)
689 xdg_mime_init ();
691 return _xdg_mime_mime_type_equal (mime_a, mime_b);
695 _xdg_mime_media_type_equal (const char *mime_a,
696 const char *mime_b)
698 char *sep;
700 xdg_mime_init ();
702 sep = strchr (mime_a, '/');
704 if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
705 return 1;
707 return 0;
711 xdg_mime_media_type_equal (const char *mime_a,
712 const char *mime_b)
714 xdg_mime_init ();
716 return _xdg_mime_media_type_equal (mime_a, mime_b);
719 #if 0
720 static int
721 xdg_mime_is_super_type (const char *mime)
723 int length;
724 const char *type;
726 length = strlen (mime);
727 type = &(mime[length - 2]);
729 if (strcmp (type, "/*") == 0)
730 return 1;
732 return 0;
734 #endif
737 _xdg_mime_mime_type_subclass (const char *mime,
738 const char *base)
740 const char *umime, *ubase;
741 const char **parents;
743 if (_xdg_mime_caches)
744 return _xdg_mime_cache_mime_type_subclass (mime, base);
746 umime = _xdg_mime_unalias_mime_type (mime);
747 ubase = _xdg_mime_unalias_mime_type (base);
749 if (strcmp (umime, ubase) == 0)
750 return 1;
752 #if 0
753 /* Handle supertypes */
754 if (xdg_mime_is_super_type (ubase) &&
755 _xdg_mime_media_type_equal (umime, ubase))
756 return 1;
757 #endif
759 /* Handle special cases text/plain and application/octet-stream */
760 if (strcmp (ubase, "text/plain") == 0 &&
761 strncmp (umime, "text/", 5) == 0)
762 return 1;
764 if (strcmp (ubase, "application/octet-stream") == 0)
765 return 1;
767 parents = _xdg_mime_parent_list_lookup (parent_list, umime);
768 for (; parents && *parents; parents++)
770 if (_xdg_mime_mime_type_subclass (*parents, ubase))
771 return 1;
774 return 0;
778 xdg_mime_mime_type_subclass (const char *mime,
779 const char *base)
781 xdg_mime_init ();
783 return _xdg_mime_mime_type_subclass (mime, base);
786 char **
787 xdg_mime_list_mime_parents (const char *mime)
789 const char **parents;
790 char **result;
791 int i, n;
793 if (_xdg_mime_caches)
794 return _xdg_mime_cache_list_mime_parents (mime);
796 parents = xdg_mime_get_mime_parents (mime);
798 if (!parents)
799 return NULL;
801 for (i = 0; parents[i]; i++) ;
803 n = (i + 1) * sizeof (char *);
804 result = (char **) malloc (n);
805 memcpy (result, parents, n);
807 return result;
810 const char **
811 xdg_mime_get_mime_parents (const char *mime)
813 const char *umime;
815 xdg_mime_init ();
817 umime = _xdg_mime_unalias_mime_type (mime);
819 return _xdg_mime_parent_list_lookup (parent_list, umime);
822 void
823 xdg_mime_dump (void)
825 printf ("*** ALIASES ***\n\n");
826 _xdg_mime_alias_list_dump (alias_list);
827 printf ("\n*** PARENTS ***\n\n");
828 _xdg_mime_parent_list_dump (parent_list);
829 printf ("\n*** CACHE ***\n\n");
830 _xdg_glob_hash_dump (global_hash);
834 /* Registers a function to be called every time the mime database reloads its files
837 xdg_mime_register_reload_callback (XdgMimeCallback callback,
838 void *data,
839 XdgMimeDestroy destroy)
841 XdgCallbackList *list_el;
842 static int callback_id = 1;
844 /* Make a new list element */
845 list_el = calloc (1, sizeof (XdgCallbackList));
846 list_el->callback_id = callback_id;
847 list_el->callback = callback;
848 list_el->data = data;
849 list_el->destroy = destroy;
850 list_el->next = callback_list;
851 if (list_el->next)
852 list_el->next->prev = list_el;
854 callback_list = list_el;
855 callback_id ++;
857 return callback_id - 1;
860 void
861 xdg_mime_remove_callback (int callback_id)
863 XdgCallbackList *list;
865 for (list = callback_list; list; list = list->next)
867 if (list->callback_id == callback_id)
869 if (list->next)
870 list->next = list->prev;
872 if (list->prev)
873 list->prev->next = list->next;
874 else
875 callback_list = list->next;
877 /* invoke the destroy handler */
878 (list->destroy) (list->data);
879 free (list);
880 return;