1 /* GTK - The GIMP Toolkit
2 * gtkfilesystemunix.c: Default implementation of GtkFileSystem for UNIX-like systems
3 * Copyright (C) 2003, Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
23 #include "gtkfilesystem.h"
24 #include "gtkfilesystemunix.h"
25 #include "gtkicontheme.h"
28 #define XDG_PREFIX _gtk_xdg
29 #include "xdgmime/xdgmime.h"
34 #include <sys/types.h>
39 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
40 #define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
42 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
44 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
46 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
47 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
48 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
50 struct _GtkFileSystemUnixClass
52 GObjectClass parent_class;
55 struct _GtkFileSystemUnix
57 GObject parent_instance;
59 GHashTable *folder_hash;
62 /* Icon type, supplemented by MIME type
67 ICON_REGULAR, /* Use mime type for icon */
69 ICON_BROKEN_SYMBOLIC_LINK,
70 ICON_CHARACTER_DEVICE,
78 #define GTK_TYPE_FILE_FOLDER_UNIX (gtk_file_folder_unix_get_type ())
79 #define GTK_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
80 #define GTK_IS_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
81 #define GTK_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
82 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
83 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
85 typedef struct _GtkFileFolderUnix GtkFileFolderUnix;
86 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
88 struct _GtkFileFolderUnixClass
90 GObjectClass parent_class;
93 struct _GtkFileFolderUnix
95 GObject parent_instance;
97 GtkFileSystemUnix *system_unix;
98 GtkFileInfoType types;
100 GHashTable *stat_info;
101 unsigned int have_stat : 1;
102 unsigned int have_mime_type : 1;
106 struct stat_info_entry {
112 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
113 GTK_FILE_INFO_IS_HIDDEN |
114 GTK_FILE_INFO_MODIFICATION_TIME |
117 static GObjectClass *system_parent_class;
118 static GObjectClass *folder_parent_class;
120 static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
121 static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
122 static void gtk_file_system_unix_init (GtkFileSystemUnix *impl);
123 static void gtk_file_system_unix_finalize (GObject *object);
125 static GSList * gtk_file_system_unix_list_volumes (GtkFileSystem *file_system);
126 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
127 const GtkFilePath *path);
129 static GtkFileFolder *gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
130 const GtkFilePath *path,
131 GtkFileInfoType types,
133 static gboolean gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
134 const GtkFilePath *path,
137 static void gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
138 GtkFileSystemVolume *volume);
139 static GtkFilePath *gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
140 GtkFileSystemVolume *volume);
141 static gboolean gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
142 GtkFileSystemVolume *volume);
143 static gboolean gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
144 GtkFileSystemVolume *volume,
146 static gchar * gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
147 GtkFileSystemVolume *volume);
148 static GdkPixbuf * gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
149 GtkFileSystemVolume *volume,
154 static gboolean gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
155 const GtkFilePath *path,
156 GtkFilePath **parent,
158 static GtkFilePath * gtk_file_system_unix_make_path (GtkFileSystem *file_system,
159 const GtkFilePath *base_path,
160 const gchar *display_name,
162 static gboolean gtk_file_system_unix_parse (GtkFileSystem *file_system,
163 const GtkFilePath *base_path,
165 GtkFilePath **folder,
169 static gchar * gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
170 const GtkFilePath *path);
171 static gchar * gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
172 const GtkFilePath *path);
173 static GtkFilePath *gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
175 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
176 const gchar *filename);
178 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
179 const GtkFilePath *path,
184 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
185 const GtkFilePath *path,
188 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
189 const GtkFilePath *path,
191 static GSList * gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system);
193 static GType gtk_file_folder_unix_get_type (void);
194 static void gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
195 static void gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface);
196 static void gtk_file_folder_unix_init (GtkFileFolderUnix *impl);
197 static void gtk_file_folder_unix_finalize (GObject *object);
199 static GtkFileInfo *gtk_file_folder_unix_get_info (GtkFileFolder *folder,
200 const GtkFilePath *path,
202 static gboolean gtk_file_folder_unix_list_children (GtkFileFolder *folder,
206 static gboolean gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder);
208 static GtkFilePath *filename_to_path (const gchar *filename);
210 static gboolean filename_is_root (const char *filename);
212 static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
213 static gboolean fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error);
214 static gboolean fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error);
216 static char * get_parent_dir (const char *filename);
222 gtk_file_system_unix_get_type (void)
224 static GType file_system_unix_type = 0;
226 if (!file_system_unix_type)
228 static const GTypeInfo file_system_unix_info =
230 sizeof (GtkFileSystemUnixClass),
231 NULL, /* base_init */
232 NULL, /* base_finalize */
233 (GClassInitFunc) gtk_file_system_unix_class_init,
234 NULL, /* class_finalize */
235 NULL, /* class_data */
236 sizeof (GtkFileSystemUnix),
238 (GInstanceInitFunc) gtk_file_system_unix_init,
241 static const GInterfaceInfo file_system_info =
243 (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
244 NULL, /* interface_finalize */
245 NULL /* interface_data */
248 file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
250 &file_system_unix_info, 0);
251 g_type_add_interface_static (file_system_unix_type,
252 GTK_TYPE_FILE_SYSTEM,
256 return file_system_unix_type;
260 * gtk_file_system_unix_new:
262 * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
263 * implements the #GtkFileSystem interface with direct access to
264 * the filesystem using Unix/Linux API calls
266 * Return value: the new #GtkFileSystemUnix object
269 gtk_file_system_unix_new (void)
271 return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
275 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
277 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
279 system_parent_class = g_type_class_peek_parent (class);
281 gobject_class->finalize = gtk_file_system_unix_finalize;
285 gtk_file_system_unix_iface_init (GtkFileSystemIface *iface)
287 iface->list_volumes = gtk_file_system_unix_list_volumes;
288 iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
289 iface->get_folder = gtk_file_system_unix_get_folder;
290 iface->create_folder = gtk_file_system_unix_create_folder;
291 iface->volume_free = gtk_file_system_unix_volume_free;
292 iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
293 iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
294 iface->volume_mount = gtk_file_system_unix_volume_mount;
295 iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
296 iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
297 iface->get_parent = gtk_file_system_unix_get_parent;
298 iface->make_path = gtk_file_system_unix_make_path;
299 iface->parse = gtk_file_system_unix_parse;
300 iface->path_to_uri = gtk_file_system_unix_path_to_uri;
301 iface->path_to_filename = gtk_file_system_unix_path_to_filename;
302 iface->uri_to_path = gtk_file_system_unix_uri_to_path;
303 iface->filename_to_path = gtk_file_system_unix_filename_to_path;
304 iface->render_icon = gtk_file_system_unix_render_icon;
305 iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
306 iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
307 iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
311 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
313 system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
317 gtk_file_system_unix_finalize (GObject *object)
319 GtkFileSystemUnix *system_unix;
321 system_unix = GTK_FILE_SYSTEM_UNIX (object);
323 /* FIXME: assert that the hash is empty? */
324 g_hash_table_destroy (system_unix->folder_hash);
326 system_parent_class->finalize (object);
329 /* Returns our single root volume */
330 static GtkFileSystemVolume *
331 get_root_volume (void)
333 return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
337 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
339 return g_slist_append (NULL, get_root_volume ());
342 static GtkFileSystemVolume *
343 gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
344 const GtkFilePath *path)
346 return get_root_volume ();
350 remove_trailing_slash (const char *filename)
354 len = strlen (filename);
356 if (len > 1 && filename[len - 1] == '/')
357 return g_strndup (filename, len - 1);
359 return g_memdup (filename, len + 1);
362 static GtkFileFolder *
363 gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
364 const GtkFilePath *path,
365 GtkFileInfoType types,
368 GtkFileSystemUnix *system_unix;
369 GtkFileFolderUnix *folder_unix;
370 const char *filename;
372 time_t now = time (NULL);
374 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
376 filename = gtk_file_path_get_string (path);
377 g_return_val_if_fail (filename != NULL, NULL);
378 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
380 filename_copy = remove_trailing_slash (filename);
381 folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
385 g_free (filename_copy);
386 if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
387 folder_unix->stat_info)
390 g_print ("Cleaning out cached directory %s\n", filename);
392 g_hash_table_destroy (folder_unix->stat_info);
393 folder_unix->stat_info = NULL;
394 folder_unix->have_mime_type = FALSE;
395 folder_unix->have_stat = FALSE;
398 g_object_ref (folder_unix);
399 folder_unix->types |= types;
400 types = folder_unix->types;
404 if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
406 int save_errno = errno;
407 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
409 /* If g_file_test() returned FALSE but not due to an error, it means
410 * that the filename is not a directory.
415 GTK_FILE_SYSTEM_ERROR,
416 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
418 filename_utf8 ? filename_utf8 : "???",
419 g_strerror (ENOTDIR));
422 GTK_FILE_SYSTEM_ERROR,
423 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
424 _("error getting information for '%s': %s"),
425 filename_utf8 ? filename_utf8 : "???",
426 g_strerror (save_errno));
428 g_free (filename_utf8);
429 g_free (filename_copy);
433 folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
434 folder_unix->system_unix = system_unix;
435 folder_unix->filename = filename_copy;
436 folder_unix->types = types;
437 folder_unix->stat_info = NULL;
438 folder_unix->asof = now;
439 folder_unix->have_mime_type = FALSE;
440 folder_unix->have_stat = FALSE;
442 g_hash_table_insert (system_unix->folder_hash,
443 folder_unix->filename,
447 if ((types & STAT_NEEDED_MASK) && !fill_in_stats (folder_unix, error))
449 g_object_unref (folder_unix);
452 if ((types & GTK_FILE_INFO_MIME_TYPE) && !fill_in_mime_type (folder_unix, error))
454 g_object_unref (folder_unix);
458 return GTK_FILE_FOLDER (folder_unix);
462 gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
463 const GtkFilePath *path,
466 GtkFileSystemUnix *system_unix;
467 const char *filename;
471 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
473 filename = gtk_file_path_get_string (path);
474 g_return_val_if_fail (filename != NULL, FALSE);
475 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
477 tmp = remove_trailing_slash (filename);
478 result = mkdir (tmp, 0777) == 0;
483 int save_errno = errno;
484 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
486 GTK_FILE_SYSTEM_ERROR,
487 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
488 _("error creating directory '%s': %s"),
489 filename_utf8 ? filename_utf8 : "???",
490 g_strerror (save_errno));
491 g_free (filename_utf8);
495 if (filename_is_root (filename))
496 return TRUE; /* hmmm, but with no notification */
498 parent = get_parent_dir (filename);
501 GtkFileFolderUnix *folder_unix;
503 folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
506 GtkFileInfoType types;
507 GtkFilePath *parent_path;
509 GtkFileFolder *folder;
511 /* This is sort of a hack. We re-get the folder, to ensure that the
512 * newly-created directory gets read into the folder's info hash table.
515 types = folder_unix->types;
517 parent_path = gtk_file_path_new_dup (parent);
518 folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
519 gtk_file_path_free (parent_path);
523 paths = g_slist_append (NULL, (GtkFilePath *) path);
524 g_signal_emit_by_name (folder, "files-added", paths);
525 g_slist_free (paths);
526 g_object_unref (folder);
537 gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
538 GtkFileSystemVolume *volume)
542 path = (GtkFilePath *) volume;
543 gtk_file_path_free (path);
547 gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
548 GtkFileSystemVolume *volume)
550 return gtk_file_path_new_dup ("/");
554 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
555 GtkFileSystemVolume *volume)
561 gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
562 GtkFileSystemVolume *volume,
566 GTK_FILE_SYSTEM_ERROR,
567 GTK_FILE_SYSTEM_ERROR_FAILED,
568 _("This file system does not support mounting"));
573 gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
574 GtkFileSystemVolume *volume)
576 return g_strdup (_("Filesystem")); /* Same as Nautilus */
580 get_icon_type_from_stat (struct stat *statp)
582 if (S_ISBLK (statp->st_mode))
583 return ICON_BLOCK_DEVICE;
584 else if (S_ISLNK (statp->st_mode))
585 return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
586 else if (S_ISCHR (statp->st_mode))
587 return ICON_CHARACTER_DEVICE;
588 else if (S_ISDIR (statp->st_mode))
589 return ICON_DIRECTORY;
590 else if (S_ISFIFO (statp->st_mode))
592 else if (S_ISSOCK (statp->st_mode))
599 get_icon_type (const char *filename,
604 /* If stat fails, try to fall back to lstat to catch broken links
606 if (stat (filename, &statbuf) != 0)
608 if (errno != ENOENT || lstat (filename, &statbuf) != 0)
610 int save_errno = errno;
611 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
613 GTK_FILE_SYSTEM_ERROR,
614 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
615 _("error getting information for '%s': %s"),
616 filename_utf8 ? filename_utf8 : "???",
617 g_strerror (save_errno));
618 g_free (filename_utf8);
624 return get_icon_type_from_stat (&statbuf);
634 icon_cache_element_free (IconCacheElement *element)
637 g_object_unref (element->pixbuf);
642 icon_theme_changed (GtkIconTheme *icon_theme)
646 /* Difference from the initial creation is that we don't
647 * reconnect the signal
649 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
650 (GDestroyNotify)g_free,
651 (GDestroyNotify)icon_cache_element_free);
652 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
653 cache, (GDestroyNotify)g_hash_table_destroy);
657 get_cached_icon (GtkWidget *widget,
661 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
662 GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
663 IconCacheElement *element;
667 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
668 (GDestroyNotify)g_free,
669 (GDestroyNotify)icon_cache_element_free);
671 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
672 cache, (GDestroyNotify)g_hash_table_destroy);
673 g_signal_connect (icon_theme, "changed",
674 G_CALLBACK (icon_theme_changed), NULL);
677 element = g_hash_table_lookup (cache, name);
680 element = g_new0 (IconCacheElement, 1);
681 g_hash_table_insert (cache, g_strdup (name), element);
684 if (element->size != pixel_size)
687 g_object_unref (element->pixbuf);
688 element->size = pixel_size;
689 element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
690 pixel_size, 0, NULL);
693 return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
697 gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
698 GtkFileSystemVolume *volume,
703 /* FIXME: set the GError if we can't load the icon */
704 return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
708 get_parent_dir (const char *filename)
712 len = strlen (filename);
714 /* Ignore trailing slashes */
715 if (len > 1 && filename[len - 1] == '/')
719 tmp = g_strndup (filename, len - 1);
721 parent = g_path_get_dirname (tmp);
727 return g_path_get_dirname (filename);
731 gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
732 const GtkFilePath *path,
733 GtkFilePath **parent,
736 const char *filename;
738 filename = gtk_file_path_get_string (path);
739 g_return_val_if_fail (filename != NULL, FALSE);
740 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
742 if (filename_is_root (filename))
748 gchar *parent_filename = get_parent_dir (filename);
749 *parent = filename_to_path (parent_filename);
750 g_free (parent_filename);
757 gtk_file_system_unix_make_path (GtkFileSystem *file_system,
758 const GtkFilePath *base_path,
759 const gchar *display_name,
762 const char *base_filename;
764 gchar *full_filename;
765 GError *tmp_error = NULL;
768 base_filename = gtk_file_path_get_string (base_path);
769 g_return_val_if_fail (base_filename != NULL, NULL);
770 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
772 filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
776 GTK_FILE_SYSTEM_ERROR,
777 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
781 g_error_free (tmp_error);
786 full_filename = g_build_filename (base_filename, filename, NULL);
787 result = filename_to_path (full_filename);
789 g_free (full_filename);
794 /* If this was a publically exported function, it should return
795 * a dup'ed result, but we make it modify-in-place for efficiency
796 * here, and because it works for us.
799 canonicalize_filename (gchar *filename)
802 gboolean last_was_slash = FALSE;
809 if (*p == G_DIR_SEPARATOR)
812 *q++ = G_DIR_SEPARATOR;
814 last_was_slash = TRUE;
818 if (last_was_slash && *p == '.')
820 if (*(p + 1) == G_DIR_SEPARATOR ||
823 if (*(p + 1) == '\0')
828 else if (*(p + 1) == '.' &&
829 (*(p + 2) == G_DIR_SEPARATOR ||
832 if (q > filename + 1)
835 while (q > filename + 1 &&
836 *(q - 1) != G_DIR_SEPARATOR)
840 if (*(p + 2) == '\0')
848 last_was_slash = FALSE;
854 last_was_slash = FALSE;
861 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
868 gtk_file_system_unix_parse (GtkFileSystem *file_system,
869 const GtkFilePath *base_path,
871 GtkFilePath **folder,
875 const char *base_filename;
877 gboolean result = FALSE;
879 base_filename = gtk_file_path_get_string (base_path);
880 g_return_val_if_fail (base_filename != NULL, FALSE);
881 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
883 last_slash = strrchr (str, G_DIR_SEPARATOR);
886 *folder = gtk_file_path_copy (base_path);
887 *file_part = g_strdup (str);
894 GError *tmp_error = NULL;
896 if (last_slash == str)
897 folder_part = g_strdup ("/");
899 folder_part = g_filename_from_utf8 (str, last_slash - str,
900 NULL, NULL, &tmp_error);
905 GTK_FILE_SYSTEM_ERROR,
906 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
909 g_error_free (tmp_error);
913 if (folder_part[0] == G_DIR_SEPARATOR)
914 folder_path = folder_part;
917 folder_path = g_build_filename (base_filename, folder_part, NULL);
918 g_free (folder_part);
921 canonicalize_filename (folder_path);
923 *folder = filename_to_path (folder_path);
924 *file_part = g_strdup (last_slash + 1);
926 g_free (folder_path);
936 gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
937 const GtkFilePath *path)
939 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
943 gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
944 const GtkFilePath *path)
946 return g_strdup (gtk_file_path_get_string (path));
950 gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
953 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
955 return gtk_file_path_new_steal (filename);
961 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
962 const gchar *filename)
964 return gtk_file_path_new_dup (filename);
968 get_icon_for_directory (const char *path)
970 static char *desktop_path = NULL;
972 if (!g_get_home_dir ())
973 return "gnome-fs-directory";
976 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
978 if (strcmp (g_get_home_dir (), path) == 0)
979 return "gnome-fs-home";
980 else if (strcmp (desktop_path, path) == 0)
981 return "gnome-fs-desktop";
983 return "gnome-fs-directory";
987 gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
988 const GtkFilePath *path,
993 const char *filename;
995 const char *mime_type = NULL;
997 GtkFileSystemUnix *system_unix;
998 GtkFileFolderUnix *folder_unix;
1000 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1001 filename = gtk_file_path_get_string (path);
1002 dirname = g_path_get_dirname (filename);
1003 folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
1009 struct stat_info_entry *entry;
1011 if (!fill_in_stats (folder_unix, error))
1014 basename = g_path_get_basename (filename);
1015 entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1019 if (entry->icon_type == ICON_UNDECIDED)
1020 entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1021 icon_type = entry->icon_type;
1022 if (icon_type == ICON_REGULAR)
1024 (void)fill_in_mime_type (folder_unix, NULL);
1025 mime_type = entry->mime_type;
1029 icon_type = ICON_NONE;
1034 g_print ("No folder open for %s\n", filename);
1037 icon_type = get_icon_type (filename, error);
1038 if (icon_type == ICON_REGULAR)
1039 mime_type = xdg_mime_get_mime_type_for_file (filename);
1043 /* FIXME: this function should not return NULL without setting the GError; we
1044 * should perhaps provide a "never fails" generic stock icon for when all else
1048 if (icon_type == ICON_NONE)
1051 if (icon_type != ICON_REGULAR)
1057 case ICON_BLOCK_DEVICE:
1058 name = "gnome-fs-blockdev";
1060 case ICON_BROKEN_SYMBOLIC_LINK:
1061 name = "gnome-fs-symlink";
1063 case ICON_CHARACTER_DEVICE:
1064 name = "gnome-fs-chardev";
1066 case ICON_DIRECTORY:
1067 name = get_icon_for_directory (filename);
1069 case ICON_EXECUTABLE:
1070 name ="gnome-fs-executable";
1073 name = "gnome-fs-fifo";
1076 name = "gnome-fs-socket";
1079 g_assert_not_reached ();
1083 return get_cached_icon (widget, name, pixel_size);
1088 const char *separator;
1092 separator = strchr (mime_type, '/');
1096 icon_name = g_string_new ("gnome-mime-");
1097 g_string_append_len (icon_name, mime_type, separator - mime_type);
1098 g_string_append_c (icon_name, '-');
1099 g_string_append (icon_name, separator + 1);
1100 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1101 g_string_free (icon_name, TRUE);
1105 icon_name = g_string_new ("gnome-mime-");
1106 g_string_append_len (icon_name, mime_type, separator - mime_type);
1107 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1108 g_string_free (icon_name, TRUE);
1113 return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1117 bookmark_list_free (GSList *list)
1121 for (l = list; l; l = l->next)
1124 g_slist_free (list);
1127 /* Returns whether a URI is a local file:// */
1129 is_local_uri (const char *uri)
1135 /* This is rather crude, but hey */
1136 filename = g_filename_from_uri (uri, &hostname, NULL);
1138 result = (filename && !hostname);
1147 bookmark_get_filename (gboolean tmp_file)
1151 filename = g_build_filename (g_get_home_dir (),
1152 tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
1154 g_assert (filename != NULL);
1159 bookmark_list_read (GSList **bookmarks, GError **error)
1163 gboolean result = FALSE;
1165 filename = bookmark_get_filename (FALSE);
1168 if (g_file_get_contents (filename, &contents, NULL, error))
1170 gchar **lines = g_strsplit (contents, "\n", -1);
1174 table = g_hash_table_new (g_str_hash, g_str_equal);
1176 for (i = 0; lines[i]; i++)
1178 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1180 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1181 g_hash_table_insert (table, lines[i], lines[i]);
1186 g_hash_table_destroy (table);
1189 *bookmarks = g_slist_reverse (*bookmarks);
1199 bookmark_list_write (GSList *bookmarks, GError **error)
1203 gboolean result = TRUE;
1208 /* First, write a temporary file */
1210 tmp_filename = bookmark_get_filename (TRUE);
1211 filename = bookmark_get_filename (FALSE);
1213 fd = g_mkstemp (tmp_filename);
1216 saved_errno = errno;
1220 if ((file = fdopen (fd, "w")) != NULL)
1224 for (l = bookmarks; l; l = l->next)
1225 if (fputs (l->data, file) == EOF
1226 || fputs ("\n", file) == EOF)
1228 saved_errno = errno;
1232 if (fclose (file) == EOF)
1234 saved_errno = errno;
1238 if (rename (tmp_filename, filename) == -1)
1240 saved_errno = errno;
1249 saved_errno = errno;
1251 /* fdopen() failed, so we can't do much error checking here anyway */
1258 GTK_FILE_SYSTEM_ERROR,
1259 GTK_FILE_SYSTEM_ERROR_FAILED,
1260 _("Bookmark saving failed (%s)"),
1261 g_strerror (saved_errno));
1265 unlink (tmp_filename); /* again, not much error checking we can do here */
1270 g_free (tmp_filename);
1276 gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
1277 const GtkFilePath *path,
1289 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1291 g_propagate_error (error, err);
1295 num_bookmarks = g_slist_length (bookmarks);
1296 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1300 uri = gtk_file_system_unix_path_to_uri (file_system, path);
1302 for (l = bookmarks; l; l = l->next)
1304 const char *bookmark;
1307 if (strcmp (bookmark, uri) == 0)
1310 GTK_FILE_SYSTEM_ERROR,
1311 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1312 "%s already exists in the bookmarks list",
1318 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1319 if (bookmark_list_write (bookmarks, error))
1322 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1328 bookmark_list_free (bookmarks);
1334 gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
1335 const GtkFilePath *path,
1343 if (!bookmark_list_read (&bookmarks, error))
1348 uri = gtk_file_system_path_to_uri (file_system, path);
1350 for (l = bookmarks; l; l = l->next)
1352 const char *bookmark;
1355 if (strcmp (bookmark, uri) == 0)
1358 bookmarks = g_slist_remove_link (bookmarks, l);
1361 if (bookmark_list_write (bookmarks, error))
1364 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1372 GTK_FILE_SYSTEM_ERROR,
1373 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1374 "%s does not exist in the bookmarks list",
1380 bookmark_list_free (bookmarks);
1386 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1392 if (!bookmark_list_read (&bookmarks, NULL))
1397 for (l = bookmarks; l; l = l->next)
1403 if (is_local_uri (name))
1404 result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1407 bookmark_list_free (bookmarks);
1409 result = g_slist_reverse (result);
1417 gtk_file_folder_unix_get_type (void)
1419 static GType file_folder_unix_type = 0;
1421 if (!file_folder_unix_type)
1423 static const GTypeInfo file_folder_unix_info =
1425 sizeof (GtkFileFolderUnixClass),
1426 NULL, /* base_init */
1427 NULL, /* base_finalize */
1428 (GClassInitFunc) gtk_file_folder_unix_class_init,
1429 NULL, /* class_finalize */
1430 NULL, /* class_data */
1431 sizeof (GtkFileFolderUnix),
1432 0, /* n_preallocs */
1433 (GInstanceInitFunc) gtk_file_folder_unix_init,
1436 static const GInterfaceInfo file_folder_info =
1438 (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1439 NULL, /* interface_finalize */
1440 NULL /* interface_data */
1443 file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1444 "GtkFileFolderUnix",
1445 &file_folder_unix_info, 0);
1446 g_type_add_interface_static (file_folder_unix_type,
1447 GTK_TYPE_FILE_FOLDER,
1451 return file_folder_unix_type;
1455 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1457 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1459 folder_parent_class = g_type_class_peek_parent (class);
1461 gobject_class->finalize = gtk_file_folder_unix_finalize;
1465 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1467 iface->get_info = gtk_file_folder_unix_get_info;
1468 iface->list_children = gtk_file_folder_unix_list_children;
1469 iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;
1473 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1478 gtk_file_folder_unix_finalize (GObject *object)
1480 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1482 g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1484 if (folder_unix->stat_info)
1487 g_print ("Releasing information for directory %s\n", folder_unix->filename);
1489 g_hash_table_destroy (folder_unix->stat_info);
1492 g_free (folder_unix->filename);
1494 folder_parent_class->finalize (object);
1497 static GtkFileInfo *
1498 gtk_file_folder_unix_get_info (GtkFileFolder *folder,
1499 const GtkFilePath *path,
1502 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1504 gchar *dirname, *basename;
1505 const char *filename;
1506 struct stat_info_entry *entry;
1507 gboolean file_must_exist;
1508 GtkFileInfoType types;
1514 g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
1516 if (stat (folder_unix->filename, &buf) != 0)
1519 info = gtk_file_info_new ();
1520 gtk_file_info_set_display_name (info, "/");
1521 gtk_file_info_set_is_folder (info, TRUE);
1522 gtk_file_info_set_is_hidden (info, FALSE);
1523 gtk_file_info_set_mime_type (info, "x-directory/normal");
1524 gtk_file_info_set_modification_time (info, buf.st_mtime);
1525 gtk_file_info_set_size (info, buf.st_size);
1530 filename = gtk_file_path_get_string (path);
1531 g_return_val_if_fail (filename != NULL, NULL);
1532 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1534 dirname = get_parent_dir (filename);
1535 g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1538 basename = g_path_get_basename (filename);
1539 types = folder_unix->types;
1540 file_must_exist = (types & ~GTK_FILE_INFO_DISPLAY_NAME) != 0;
1541 entry = file_must_exist
1542 ? g_hash_table_lookup (folder_unix->stat_info, basename)
1544 /* basename freed later. */
1546 if (!file_must_exist || entry)
1548 info = gtk_file_info_new ();
1552 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1554 GTK_FILE_SYSTEM_ERROR,
1555 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1556 _("error getting information for '%s'"),
1557 filename_utf8 ? filename_utf8 : "???");
1558 g_free (filename_utf8);
1563 if (types & GTK_FILE_INFO_DISPLAY_NAME)
1565 gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1567 display_name = g_strescape (basename, NULL);
1569 gtk_file_info_set_display_name (info, display_name);
1571 g_free (display_name);
1574 if (types & GTK_FILE_INFO_IS_HIDDEN)
1575 gtk_file_info_set_is_hidden (info, basename[0] == '.');
1577 if (types & GTK_FILE_INFO_IS_FOLDER)
1578 gtk_file_info_set_is_folder (info, S_ISDIR (entry->statbuf.st_mode));
1580 if (types & GTK_FILE_INFO_MIME_TYPE)
1581 gtk_file_info_set_mime_type (info, entry->mime_type);
1583 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1584 gtk_file_info_set_modification_time (info, entry->statbuf.st_mtime);
1586 if (types & GTK_FILE_INFO_SIZE)
1587 gtk_file_info_set_size (info, (gint64)entry->statbuf.st_size);
1596 cb_list_children (gpointer key, gpointer value, gpointer user_data)
1598 GSList **children = user_data;
1599 *children = g_slist_prepend (*children, key);
1603 gtk_file_folder_unix_list_children (GtkFileFolder *folder,
1607 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1610 if (!fill_in_names (folder_unix, error))
1615 /* Get the list of basenames. */
1616 g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
1618 /* Turn basenames into GFilePaths. */
1619 for (l = *children; l; l = l->next)
1621 const char *basename = l->data;
1622 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1623 l->data = filename_to_path (fullname);
1630 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
1632 /* Since we don't do asynchronous loads, we are always finished loading */
1637 free_stat_info_entry (struct stat_info_entry *entry)
1639 g_free (entry->mime_type);
1644 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
1648 if (folder_unix->stat_info)
1652 g_print ("Reading directory %s\n", folder_unix->filename);
1655 folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
1656 (GDestroyNotify)g_free,
1657 (GDestroyNotify)free_stat_info_entry);
1658 dir = g_dir_open (folder_unix->filename, 0, error);
1661 int save_errno = errno;
1662 gchar *filename_utf8 = g_filename_to_utf8 (folder_unix->filename, -1, NULL, NULL, NULL);
1664 GTK_FILE_SYSTEM_ERROR,
1665 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1666 _("error getting information for '%s': %s"),
1667 filename_utf8 ? filename_utf8 : "???",
1668 g_strerror (save_errno));
1669 g_free (filename_utf8);
1675 const gchar *basename = g_dir_read_name (dir);
1679 g_hash_table_insert (folder_unix->stat_info,
1680 g_strdup (basename),
1681 g_new0 (struct stat_info_entry, 1));
1686 folder_unix->asof = time (NULL);
1691 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
1693 const char *basename = key;
1694 struct stat_info_entry *entry = value;
1695 GtkFileFolderUnix *folder_unix = user_data;
1696 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1699 if (stat (fullname, &entry->statbuf) == -1 &&
1700 (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
1701 result = TRUE; /* Couldn't stat -- remove from hash. */
1711 fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error)
1713 if (folder_unix->have_stat)
1716 if (!fill_in_names (folder_unix, error))
1720 g_print ("Stating directory %s\n", folder_unix->filename);
1722 g_hash_table_foreach_remove (folder_unix->stat_info,
1726 folder_unix->have_stat = TRUE;
1732 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
1734 const char *basename = key;
1735 struct stat_info_entry *entry = value;
1736 GtkFileFolderUnix *folder_unix = user_data;
1737 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1739 /* FIXME: Should not need to re-stat. */
1740 const char *mime_type = xdg_mime_get_mime_type_for_file (fullname);
1741 entry->mime_type = g_strdup (mime_type);
1744 /* FIXME: free on NULL? */
1749 fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error)
1751 if (folder_unix->have_mime_type)
1755 g_print ("Getting mime types for directory %s\n", folder_unix->filename);
1757 g_hash_table_foreach_remove (folder_unix->stat_info,
1758 cb_fill_in_mime_type,
1761 folder_unix->have_mime_type = TRUE;
1765 static GtkFilePath *
1766 filename_to_path (const char *filename)
1768 return gtk_file_path_new_dup (filename);
1772 filename_is_root (const char *filename)
1774 const gchar *after_root;
1776 after_root = g_path_skip_root (filename);
1778 return (after_root != NULL && *after_root == '\0');