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>
40 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
41 #define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
43 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
45 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
47 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
48 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
49 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
51 struct _GtkFileSystemUnixClass
53 GObjectClass parent_class;
56 struct _GtkFileSystemUnix
58 GObject parent_instance;
60 GHashTable *folder_hash;
63 /* Icon type, supplemented by MIME type
68 ICON_REGULAR, /* Use mime type for icon */
70 ICON_BROKEN_SYMBOLIC_LINK,
71 ICON_CHARACTER_DEVICE,
79 #define GTK_TYPE_FILE_FOLDER_UNIX (gtk_file_folder_unix_get_type ())
80 #define GTK_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
81 #define GTK_IS_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
82 #define GTK_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
83 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
84 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
86 typedef struct _GtkFileFolderUnix GtkFileFolderUnix;
87 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
89 struct _GtkFileFolderUnixClass
91 GObjectClass parent_class;
94 struct _GtkFileFolderUnix
96 GObject parent_instance;
98 GtkFileSystemUnix *system_unix;
99 GtkFileInfoType types;
101 GHashTable *stat_info;
102 unsigned int have_stat : 1;
103 unsigned int have_mime_type : 1;
107 struct stat_info_entry {
113 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
114 GTK_FILE_INFO_IS_HIDDEN |
115 GTK_FILE_INFO_MODIFICATION_TIME |
118 static GObjectClass *system_parent_class;
119 static GObjectClass *folder_parent_class;
121 static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
122 static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
123 static void gtk_file_system_unix_init (GtkFileSystemUnix *impl);
124 static void gtk_file_system_unix_finalize (GObject *object);
126 static GSList * gtk_file_system_unix_list_volumes (GtkFileSystem *file_system);
127 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
128 const GtkFilePath *path);
130 static GtkFileFolder *gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
131 const GtkFilePath *path,
132 GtkFileInfoType types,
134 static gboolean gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
135 const GtkFilePath *path,
138 static void gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
139 GtkFileSystemVolume *volume);
140 static GtkFilePath *gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
141 GtkFileSystemVolume *volume);
142 static gboolean gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
143 GtkFileSystemVolume *volume);
144 static gboolean gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
145 GtkFileSystemVolume *volume,
147 static gchar * gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
148 GtkFileSystemVolume *volume);
149 static GdkPixbuf * gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
150 GtkFileSystemVolume *volume,
155 static gboolean gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
156 const GtkFilePath *path,
157 GtkFilePath **parent,
159 static GtkFilePath * gtk_file_system_unix_make_path (GtkFileSystem *file_system,
160 const GtkFilePath *base_path,
161 const gchar *display_name,
163 static gboolean gtk_file_system_unix_parse (GtkFileSystem *file_system,
164 const GtkFilePath *base_path,
166 GtkFilePath **folder,
170 static gchar * gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
171 const GtkFilePath *path);
172 static gchar * gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
173 const GtkFilePath *path);
174 static GtkFilePath *gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
176 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
177 const gchar *filename);
179 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
180 const GtkFilePath *path,
185 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
186 const GtkFilePath *path,
189 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
190 const GtkFilePath *path,
192 static GSList * gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system);
194 static GType gtk_file_folder_unix_get_type (void);
195 static void gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
196 static void gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface);
197 static void gtk_file_folder_unix_init (GtkFileFolderUnix *impl);
198 static void gtk_file_folder_unix_finalize (GObject *object);
200 static GtkFileInfo *gtk_file_folder_unix_get_info (GtkFileFolder *folder,
201 const GtkFilePath *path,
203 static gboolean gtk_file_folder_unix_list_children (GtkFileFolder *folder,
207 static gboolean gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder);
209 static GtkFilePath *filename_to_path (const gchar *filename);
211 static gboolean filename_is_root (const char *filename);
213 static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
214 static gboolean fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error);
215 static gboolean fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error);
217 static char * get_parent_dir (const char *filename);
223 gtk_file_system_unix_get_type (void)
225 static GType file_system_unix_type = 0;
227 if (!file_system_unix_type)
229 static const GTypeInfo file_system_unix_info =
231 sizeof (GtkFileSystemUnixClass),
232 NULL, /* base_init */
233 NULL, /* base_finalize */
234 (GClassInitFunc) gtk_file_system_unix_class_init,
235 NULL, /* class_finalize */
236 NULL, /* class_data */
237 sizeof (GtkFileSystemUnix),
239 (GInstanceInitFunc) gtk_file_system_unix_init,
242 static const GInterfaceInfo file_system_info =
244 (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
245 NULL, /* interface_finalize */
246 NULL /* interface_data */
249 file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
251 &file_system_unix_info, 0);
252 g_type_add_interface_static (file_system_unix_type,
253 GTK_TYPE_FILE_SYSTEM,
257 return file_system_unix_type;
261 * gtk_file_system_unix_new:
263 * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
264 * implements the #GtkFileSystem interface with direct access to
265 * the filesystem using Unix/Linux API calls
267 * Return value: the new #GtkFileSystemUnix object
270 gtk_file_system_unix_new (void)
272 return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
276 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
278 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
280 system_parent_class = g_type_class_peek_parent (class);
282 gobject_class->finalize = gtk_file_system_unix_finalize;
286 gtk_file_system_unix_iface_init (GtkFileSystemIface *iface)
288 iface->list_volumes = gtk_file_system_unix_list_volumes;
289 iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
290 iface->get_folder = gtk_file_system_unix_get_folder;
291 iface->create_folder = gtk_file_system_unix_create_folder;
292 iface->volume_free = gtk_file_system_unix_volume_free;
293 iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
294 iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
295 iface->volume_mount = gtk_file_system_unix_volume_mount;
296 iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
297 iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
298 iface->get_parent = gtk_file_system_unix_get_parent;
299 iface->make_path = gtk_file_system_unix_make_path;
300 iface->parse = gtk_file_system_unix_parse;
301 iface->path_to_uri = gtk_file_system_unix_path_to_uri;
302 iface->path_to_filename = gtk_file_system_unix_path_to_filename;
303 iface->uri_to_path = gtk_file_system_unix_uri_to_path;
304 iface->filename_to_path = gtk_file_system_unix_filename_to_path;
305 iface->render_icon = gtk_file_system_unix_render_icon;
306 iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
307 iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
308 iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
312 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
314 system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
318 gtk_file_system_unix_finalize (GObject *object)
320 GtkFileSystemUnix *system_unix;
322 system_unix = GTK_FILE_SYSTEM_UNIX (object);
324 /* FIXME: assert that the hash is empty? */
325 g_hash_table_destroy (system_unix->folder_hash);
327 system_parent_class->finalize (object);
330 /* Returns our single root volume */
331 static GtkFileSystemVolume *
332 get_root_volume (void)
334 return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
338 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
340 return g_slist_append (NULL, get_root_volume ());
343 static GtkFileSystemVolume *
344 gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
345 const GtkFilePath *path)
347 return get_root_volume ();
351 remove_trailing_slash (const char *filename)
355 len = strlen (filename);
357 if (len > 1 && filename[len - 1] == '/')
358 return g_strndup (filename, len - 1);
360 return g_memdup (filename, len + 1);
363 static GtkFileFolder *
364 gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
365 const GtkFilePath *path,
366 GtkFileInfoType types,
369 GtkFileSystemUnix *system_unix;
370 GtkFileFolderUnix *folder_unix;
371 const char *filename;
373 time_t now = time (NULL);
375 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
377 filename = gtk_file_path_get_string (path);
378 g_return_val_if_fail (filename != NULL, NULL);
379 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
381 filename_copy = remove_trailing_slash (filename);
382 folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
386 g_free (filename_copy);
387 if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
388 folder_unix->stat_info)
391 g_print ("Cleaning out cached directory %s\n", filename);
393 g_hash_table_destroy (folder_unix->stat_info);
394 folder_unix->stat_info = NULL;
395 folder_unix->have_mime_type = FALSE;
396 folder_unix->have_stat = FALSE;
399 g_object_ref (folder_unix);
400 folder_unix->types |= types;
401 types = folder_unix->types;
405 if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
407 int save_errno = errno;
408 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
410 /* If g_file_test() returned FALSE but not due to an error, it means
411 * that the filename is not a directory.
416 GTK_FILE_SYSTEM_ERROR,
417 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
419 filename_utf8 ? filename_utf8 : "???",
420 g_strerror (ENOTDIR));
423 GTK_FILE_SYSTEM_ERROR,
424 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
425 _("error getting information for '%s': %s"),
426 filename_utf8 ? filename_utf8 : "???",
427 g_strerror (save_errno));
429 g_free (filename_utf8);
430 g_free (filename_copy);
434 folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
435 folder_unix->system_unix = system_unix;
436 folder_unix->filename = filename_copy;
437 folder_unix->types = types;
438 folder_unix->stat_info = NULL;
439 folder_unix->asof = now;
440 folder_unix->have_mime_type = FALSE;
441 folder_unix->have_stat = FALSE;
443 g_hash_table_insert (system_unix->folder_hash,
444 folder_unix->filename,
448 if ((types & STAT_NEEDED_MASK) && !fill_in_stats (folder_unix, error))
450 g_object_unref (folder_unix);
453 if ((types & GTK_FILE_INFO_MIME_TYPE) && !fill_in_mime_type (folder_unix, error))
455 g_object_unref (folder_unix);
459 return GTK_FILE_FOLDER (folder_unix);
463 gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
464 const GtkFilePath *path,
467 GtkFileSystemUnix *system_unix;
468 const char *filename;
472 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
474 filename = gtk_file_path_get_string (path);
475 g_return_val_if_fail (filename != NULL, FALSE);
476 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
478 tmp = remove_trailing_slash (filename);
479 result = mkdir (tmp, 0777) == 0;
484 int save_errno = errno;
485 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
487 GTK_FILE_SYSTEM_ERROR,
488 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
489 _("error creating directory '%s': %s"),
490 filename_utf8 ? filename_utf8 : "???",
491 g_strerror (save_errno));
492 g_free (filename_utf8);
496 if (filename_is_root (filename))
497 return TRUE; /* hmmm, but with no notification */
499 parent = get_parent_dir (filename);
502 GtkFileFolderUnix *folder_unix;
504 folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
507 GtkFileInfoType types;
508 GtkFilePath *parent_path;
510 GtkFileFolder *folder;
512 /* This is sort of a hack. We re-get the folder, to ensure that the
513 * newly-created directory gets read into the folder's info hash table.
516 types = folder_unix->types;
518 parent_path = gtk_file_path_new_dup (parent);
519 folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
520 gtk_file_path_free (parent_path);
524 paths = g_slist_append (NULL, (GtkFilePath *) path);
525 g_signal_emit_by_name (folder, "files-added", paths);
526 g_slist_free (paths);
527 g_object_unref (folder);
538 gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
539 GtkFileSystemVolume *volume)
543 path = (GtkFilePath *) volume;
544 gtk_file_path_free (path);
548 gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
549 GtkFileSystemVolume *volume)
551 return gtk_file_path_new_dup ("/");
555 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
556 GtkFileSystemVolume *volume)
562 gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
563 GtkFileSystemVolume *volume,
567 GTK_FILE_SYSTEM_ERROR,
568 GTK_FILE_SYSTEM_ERROR_FAILED,
569 _("This file system does not support mounting"));
574 gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
575 GtkFileSystemVolume *volume)
577 return g_strdup (_("Filesystem")); /* Same as Nautilus */
581 get_icon_type_from_stat (struct stat *statp)
583 if (S_ISBLK (statp->st_mode))
584 return ICON_BLOCK_DEVICE;
585 else if (S_ISLNK (statp->st_mode))
586 return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
587 else if (S_ISCHR (statp->st_mode))
588 return ICON_CHARACTER_DEVICE;
589 else if (S_ISDIR (statp->st_mode))
590 return ICON_DIRECTORY;
591 else if (S_ISFIFO (statp->st_mode))
593 else if (S_ISSOCK (statp->st_mode))
600 get_icon_type (const char *filename,
605 /* If stat fails, try to fall back to lstat to catch broken links
607 if (stat (filename, &statbuf) != 0)
609 if (errno != ENOENT || lstat (filename, &statbuf) != 0)
611 int save_errno = errno;
612 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
614 GTK_FILE_SYSTEM_ERROR,
615 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
616 _("error getting information for '%s': %s"),
617 filename_utf8 ? filename_utf8 : "???",
618 g_strerror (save_errno));
619 g_free (filename_utf8);
625 return get_icon_type_from_stat (&statbuf);
635 icon_cache_element_free (IconCacheElement *element)
638 g_object_unref (element->pixbuf);
643 icon_theme_changed (GtkIconTheme *icon_theme)
647 /* Difference from the initial creation is that we don't
648 * reconnect the signal
650 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
651 (GDestroyNotify)g_free,
652 (GDestroyNotify)icon_cache_element_free);
653 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
654 cache, (GDestroyNotify)g_hash_table_destroy);
658 get_cached_icon (GtkWidget *widget,
662 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
663 GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
664 IconCacheElement *element;
668 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
669 (GDestroyNotify)g_free,
670 (GDestroyNotify)icon_cache_element_free);
672 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
673 cache, (GDestroyNotify)g_hash_table_destroy);
674 g_signal_connect (icon_theme, "changed",
675 G_CALLBACK (icon_theme_changed), NULL);
678 element = g_hash_table_lookup (cache, name);
681 element = g_new0 (IconCacheElement, 1);
682 g_hash_table_insert (cache, g_strdup (name), element);
685 if (element->size != pixel_size)
688 g_object_unref (element->pixbuf);
689 element->size = pixel_size;
690 element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
691 pixel_size, 0, NULL);
694 return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
698 gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
699 GtkFileSystemVolume *volume,
704 /* FIXME: set the GError if we can't load the icon */
705 return get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
709 get_parent_dir (const char *filename)
713 len = strlen (filename);
715 /* Ignore trailing slashes */
716 if (len > 1 && filename[len - 1] == '/')
720 tmp = g_strndup (filename, len - 1);
722 parent = g_path_get_dirname (tmp);
728 return g_path_get_dirname (filename);
732 gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
733 const GtkFilePath *path,
734 GtkFilePath **parent,
737 const char *filename;
739 filename = gtk_file_path_get_string (path);
740 g_return_val_if_fail (filename != NULL, FALSE);
741 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
743 if (filename_is_root (filename))
749 gchar *parent_filename = get_parent_dir (filename);
750 *parent = filename_to_path (parent_filename);
751 g_free (parent_filename);
758 gtk_file_system_unix_make_path (GtkFileSystem *file_system,
759 const GtkFilePath *base_path,
760 const gchar *display_name,
763 const char *base_filename;
765 gchar *full_filename;
766 GError *tmp_error = NULL;
769 base_filename = gtk_file_path_get_string (base_path);
770 g_return_val_if_fail (base_filename != NULL, NULL);
771 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
773 if (strchr (display_name, G_DIR_SEPARATOR))
776 GTK_FILE_SYSTEM_ERROR,
777 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
778 _("The name \"%s\" is not valid because it contains the character \"%s\". "
779 "Please use a different name."),
785 filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
789 GTK_FILE_SYSTEM_ERROR,
790 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
794 g_error_free (tmp_error);
799 full_filename = g_build_filename (base_filename, filename, NULL);
800 result = filename_to_path (full_filename);
802 g_free (full_filename);
807 /* If this was a publically exported function, it should return
808 * a dup'ed result, but we make it modify-in-place for efficiency
809 * here, and because it works for us.
812 canonicalize_filename (gchar *filename)
815 gboolean last_was_slash = FALSE;
822 if (*p == G_DIR_SEPARATOR)
825 *q++ = G_DIR_SEPARATOR;
827 last_was_slash = TRUE;
831 if (last_was_slash && *p == '.')
833 if (*(p + 1) == G_DIR_SEPARATOR ||
836 if (*(p + 1) == '\0')
841 else if (*(p + 1) == '.' &&
842 (*(p + 2) == G_DIR_SEPARATOR ||
845 if (q > filename + 1)
848 while (q > filename + 1 &&
849 *(q - 1) != G_DIR_SEPARATOR)
853 if (*(p + 2) == '\0')
861 last_was_slash = FALSE;
867 last_was_slash = FALSE;
874 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
880 /* Takes a user-typed filename and expands a tilde at the beginning of the string */
882 expand_tilde (const char *filename)
888 if (filename[0] != '~')
889 return g_strdup (filename);
891 notilde = filename + 1;
893 slash = strchr (notilde, G_DIR_SEPARATOR);
897 if (slash == notilde)
899 home = g_get_home_dir ();
902 return g_strdup (filename);
907 struct passwd *passwd;
909 username = g_strndup (notilde, slash - notilde);
910 passwd = getpwnam (username);
914 return g_strdup (filename);
916 home = passwd->pw_dir;
919 return g_build_filename (home, G_DIR_SEPARATOR_S, slash + 1, NULL);
923 gtk_file_system_unix_parse (GtkFileSystem *file_system,
924 const GtkFilePath *base_path,
926 GtkFilePath **folder,
930 const char *base_filename;
933 gboolean result = FALSE;
935 base_filename = gtk_file_path_get_string (base_path);
936 g_return_val_if_fail (base_filename != NULL, FALSE);
937 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
939 filename = expand_tilde (str);
943 GTK_FILE_SYSTEM_ERROR,
944 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
945 "%s", ""); /* nothing for now, as we are string-frozen */
949 last_slash = strrchr (filename, G_DIR_SEPARATOR);
952 *folder = gtk_file_path_copy (base_path);
953 *file_part = g_strdup (filename);
960 GError *tmp_error = NULL;
962 if (last_slash == filename)
963 folder_part = g_strdup ("/");
965 folder_part = g_filename_from_utf8 (filename, last_slash - filename,
966 NULL, NULL, &tmp_error);
971 GTK_FILE_SYSTEM_ERROR,
972 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
975 g_error_free (tmp_error);
979 if (folder_part[0] == G_DIR_SEPARATOR)
980 folder_path = folder_part;
983 folder_path = g_build_filename (base_filename, folder_part, NULL);
984 g_free (folder_part);
987 canonicalize_filename (folder_path);
989 *folder = filename_to_path (folder_path);
990 *file_part = g_strdup (last_slash + 1);
992 g_free (folder_path);
1004 gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
1005 const GtkFilePath *path)
1007 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1011 gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
1012 const GtkFilePath *path)
1014 return g_strdup (gtk_file_path_get_string (path));
1017 static GtkFilePath *
1018 gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
1021 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1023 return gtk_file_path_new_steal (filename);
1028 static GtkFilePath *
1029 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
1030 const gchar *filename)
1032 return gtk_file_path_new_dup (filename);
1036 get_icon_for_directory (const char *path)
1038 static char *desktop_path = NULL;
1040 if (!g_get_home_dir ())
1041 return "gnome-fs-directory";
1044 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1046 if (strcmp (g_get_home_dir (), path) == 0)
1047 return "gnome-fs-home";
1048 else if (strcmp (desktop_path, path) == 0)
1049 return "gnome-fs-desktop";
1051 return "gnome-fs-directory";
1055 gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
1056 const GtkFilePath *path,
1061 const char *filename;
1063 const char *mime_type = NULL;
1065 GtkFileSystemUnix *system_unix;
1066 GtkFileFolderUnix *folder_unix;
1068 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1069 filename = gtk_file_path_get_string (path);
1070 dirname = g_path_get_dirname (filename);
1071 folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
1077 struct stat_info_entry *entry;
1079 if (!fill_in_stats (folder_unix, error))
1082 basename = g_path_get_basename (filename);
1083 entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1087 if (entry->icon_type == ICON_UNDECIDED)
1088 entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1089 icon_type = entry->icon_type;
1090 if (icon_type == ICON_REGULAR)
1092 (void)fill_in_mime_type (folder_unix, NULL);
1093 mime_type = entry->mime_type;
1097 icon_type = ICON_NONE;
1102 g_print ("No folder open for %s\n", filename);
1105 icon_type = get_icon_type (filename, error);
1106 if (icon_type == ICON_REGULAR)
1107 mime_type = xdg_mime_get_mime_type_for_file (filename);
1111 /* FIXME: this function should not return NULL without setting the GError; we
1112 * should perhaps provide a "never fails" generic stock icon for when all else
1116 if (icon_type == ICON_NONE)
1119 if (icon_type != ICON_REGULAR)
1125 case ICON_BLOCK_DEVICE:
1126 name = "gnome-fs-blockdev";
1128 case ICON_BROKEN_SYMBOLIC_LINK:
1129 name = "gnome-fs-symlink";
1131 case ICON_CHARACTER_DEVICE:
1132 name = "gnome-fs-chardev";
1134 case ICON_DIRECTORY:
1135 name = get_icon_for_directory (filename);
1137 case ICON_EXECUTABLE:
1138 name ="gnome-fs-executable";
1141 name = "gnome-fs-fifo";
1144 name = "gnome-fs-socket";
1147 g_assert_not_reached ();
1151 return get_cached_icon (widget, name, pixel_size);
1156 const char *separator;
1160 separator = strchr (mime_type, '/');
1164 icon_name = g_string_new ("gnome-mime-");
1165 g_string_append_len (icon_name, mime_type, separator - mime_type);
1166 g_string_append_c (icon_name, '-');
1167 g_string_append (icon_name, separator + 1);
1168 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1169 g_string_free (icon_name, TRUE);
1173 icon_name = g_string_new ("gnome-mime-");
1174 g_string_append_len (icon_name, mime_type, separator - mime_type);
1175 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1176 g_string_free (icon_name, TRUE);
1181 return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1185 bookmark_list_free (GSList *list)
1189 for (l = list; l; l = l->next)
1192 g_slist_free (list);
1195 /* Returns whether a URI is a local file:// */
1197 is_local_uri (const char *uri)
1203 /* This is rather crude, but hey */
1204 filename = g_filename_from_uri (uri, &hostname, NULL);
1206 result = (filename && !hostname);
1215 bookmark_get_filename (gboolean tmp_file)
1219 filename = g_build_filename (g_get_home_dir (),
1220 tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
1222 g_assert (filename != NULL);
1227 bookmark_list_read (GSList **bookmarks, GError **error)
1231 gboolean result = FALSE;
1233 filename = bookmark_get_filename (FALSE);
1236 if (g_file_get_contents (filename, &contents, NULL, error))
1238 gchar **lines = g_strsplit (contents, "\n", -1);
1242 table = g_hash_table_new (g_str_hash, g_str_equal);
1244 for (i = 0; lines[i]; i++)
1246 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1248 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1249 g_hash_table_insert (table, lines[i], lines[i]);
1254 g_hash_table_destroy (table);
1257 *bookmarks = g_slist_reverse (*bookmarks);
1267 bookmark_list_write (GSList *bookmarks, GError **error)
1271 gboolean result = TRUE;
1276 /* First, write a temporary file */
1278 tmp_filename = bookmark_get_filename (TRUE);
1279 filename = bookmark_get_filename (FALSE);
1281 fd = g_mkstemp (tmp_filename);
1284 saved_errno = errno;
1288 if ((file = fdopen (fd, "w")) != NULL)
1292 for (l = bookmarks; l; l = l->next)
1293 if (fputs (l->data, file) == EOF
1294 || fputs ("\n", file) == EOF)
1296 saved_errno = errno;
1300 if (fclose (file) == EOF)
1302 saved_errno = errno;
1306 if (rename (tmp_filename, filename) == -1)
1308 saved_errno = errno;
1317 saved_errno = errno;
1319 /* fdopen() failed, so we can't do much error checking here anyway */
1326 GTK_FILE_SYSTEM_ERROR,
1327 GTK_FILE_SYSTEM_ERROR_FAILED,
1328 _("Bookmark saving failed (%s)"),
1329 g_strerror (saved_errno));
1333 unlink (tmp_filename); /* again, not much error checking we can do here */
1338 g_free (tmp_filename);
1344 gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
1345 const GtkFilePath *path,
1357 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1359 g_propagate_error (error, err);
1363 num_bookmarks = g_slist_length (bookmarks);
1364 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1368 uri = gtk_file_system_unix_path_to_uri (file_system, path);
1370 for (l = bookmarks; l; l = l->next)
1372 const char *bookmark;
1375 if (strcmp (bookmark, uri) == 0)
1378 GTK_FILE_SYSTEM_ERROR,
1379 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1380 "%s already exists in the bookmarks list",
1386 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1387 if (bookmark_list_write (bookmarks, error))
1390 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1396 bookmark_list_free (bookmarks);
1402 gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
1403 const GtkFilePath *path,
1411 if (!bookmark_list_read (&bookmarks, error))
1416 uri = gtk_file_system_path_to_uri (file_system, path);
1418 for (l = bookmarks; l; l = l->next)
1420 const char *bookmark;
1423 if (strcmp (bookmark, uri) == 0)
1426 bookmarks = g_slist_remove_link (bookmarks, l);
1429 if (bookmark_list_write (bookmarks, error))
1432 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1440 GTK_FILE_SYSTEM_ERROR,
1441 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1442 "%s does not exist in the bookmarks list",
1448 bookmark_list_free (bookmarks);
1454 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1460 if (!bookmark_list_read (&bookmarks, NULL))
1465 for (l = bookmarks; l; l = l->next)
1471 if (is_local_uri (name))
1472 result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1475 bookmark_list_free (bookmarks);
1477 result = g_slist_reverse (result);
1485 gtk_file_folder_unix_get_type (void)
1487 static GType file_folder_unix_type = 0;
1489 if (!file_folder_unix_type)
1491 static const GTypeInfo file_folder_unix_info =
1493 sizeof (GtkFileFolderUnixClass),
1494 NULL, /* base_init */
1495 NULL, /* base_finalize */
1496 (GClassInitFunc) gtk_file_folder_unix_class_init,
1497 NULL, /* class_finalize */
1498 NULL, /* class_data */
1499 sizeof (GtkFileFolderUnix),
1500 0, /* n_preallocs */
1501 (GInstanceInitFunc) gtk_file_folder_unix_init,
1504 static const GInterfaceInfo file_folder_info =
1506 (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1507 NULL, /* interface_finalize */
1508 NULL /* interface_data */
1511 file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1512 "GtkFileFolderUnix",
1513 &file_folder_unix_info, 0);
1514 g_type_add_interface_static (file_folder_unix_type,
1515 GTK_TYPE_FILE_FOLDER,
1519 return file_folder_unix_type;
1523 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1525 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1527 folder_parent_class = g_type_class_peek_parent (class);
1529 gobject_class->finalize = gtk_file_folder_unix_finalize;
1533 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1535 iface->get_info = gtk_file_folder_unix_get_info;
1536 iface->list_children = gtk_file_folder_unix_list_children;
1537 iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;
1541 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1546 gtk_file_folder_unix_finalize (GObject *object)
1548 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1550 g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1552 if (folder_unix->stat_info)
1555 g_print ("Releasing information for directory %s\n", folder_unix->filename);
1557 g_hash_table_destroy (folder_unix->stat_info);
1560 g_free (folder_unix->filename);
1562 folder_parent_class->finalize (object);
1565 static GtkFileInfo *
1566 gtk_file_folder_unix_get_info (GtkFileFolder *folder,
1567 const GtkFilePath *path,
1570 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1572 gchar *dirname, *basename;
1573 const char *filename;
1574 struct stat_info_entry *entry;
1575 gboolean file_must_exist;
1576 GtkFileInfoType types;
1582 g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
1584 if (stat (folder_unix->filename, &buf) != 0)
1587 info = gtk_file_info_new ();
1588 gtk_file_info_set_display_name (info, "/");
1589 gtk_file_info_set_is_folder (info, TRUE);
1590 gtk_file_info_set_is_hidden (info, FALSE);
1591 gtk_file_info_set_mime_type (info, "x-directory/normal");
1592 gtk_file_info_set_modification_time (info, buf.st_mtime);
1593 gtk_file_info_set_size (info, buf.st_size);
1598 filename = gtk_file_path_get_string (path);
1599 g_return_val_if_fail (filename != NULL, NULL);
1600 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1602 dirname = get_parent_dir (filename);
1603 g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1606 basename = g_path_get_basename (filename);
1607 types = folder_unix->types;
1608 file_must_exist = (types & ~GTK_FILE_INFO_DISPLAY_NAME) != 0;
1609 entry = file_must_exist
1610 ? g_hash_table_lookup (folder_unix->stat_info, basename)
1612 /* basename freed later. */
1614 if (!file_must_exist || entry)
1616 info = gtk_file_info_new ();
1620 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1622 GTK_FILE_SYSTEM_ERROR,
1623 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1624 _("error getting information for '%s'"),
1625 filename_utf8 ? filename_utf8 : "???");
1626 g_free (filename_utf8);
1631 if (types & GTK_FILE_INFO_DISPLAY_NAME)
1633 gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1635 display_name = g_strescape (basename, NULL);
1637 gtk_file_info_set_display_name (info, display_name);
1639 g_free (display_name);
1642 if (types & GTK_FILE_INFO_IS_HIDDEN)
1643 gtk_file_info_set_is_hidden (info, basename[0] == '.');
1645 if (types & GTK_FILE_INFO_IS_FOLDER)
1646 gtk_file_info_set_is_folder (info, S_ISDIR (entry->statbuf.st_mode));
1648 if (types & GTK_FILE_INFO_MIME_TYPE)
1649 gtk_file_info_set_mime_type (info, entry->mime_type);
1651 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1652 gtk_file_info_set_modification_time (info, entry->statbuf.st_mtime);
1654 if (types & GTK_FILE_INFO_SIZE)
1655 gtk_file_info_set_size (info, (gint64)entry->statbuf.st_size);
1664 cb_list_children (gpointer key, gpointer value, gpointer user_data)
1666 GSList **children = user_data;
1667 *children = g_slist_prepend (*children, key);
1671 gtk_file_folder_unix_list_children (GtkFileFolder *folder,
1675 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1678 if (!fill_in_names (folder_unix, error))
1683 /* Get the list of basenames. */
1684 g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
1686 /* Turn basenames into GFilePaths. */
1687 for (l = *children; l; l = l->next)
1689 const char *basename = l->data;
1690 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1691 l->data = filename_to_path (fullname);
1698 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
1700 /* Since we don't do asynchronous loads, we are always finished loading */
1705 free_stat_info_entry (struct stat_info_entry *entry)
1707 g_free (entry->mime_type);
1712 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
1716 if (folder_unix->stat_info)
1720 g_print ("Reading directory %s\n", folder_unix->filename);
1723 folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
1724 (GDestroyNotify)g_free,
1725 (GDestroyNotify)free_stat_info_entry);
1726 dir = g_dir_open (folder_unix->filename, 0, error);
1729 int save_errno = errno;
1730 gchar *filename_utf8 = g_filename_to_utf8 (folder_unix->filename, -1, NULL, NULL, NULL);
1732 GTK_FILE_SYSTEM_ERROR,
1733 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1734 _("error getting information for '%s': %s"),
1735 filename_utf8 ? filename_utf8 : "???",
1736 g_strerror (save_errno));
1737 g_free (filename_utf8);
1743 const gchar *basename = g_dir_read_name (dir);
1747 g_hash_table_insert (folder_unix->stat_info,
1748 g_strdup (basename),
1749 g_new0 (struct stat_info_entry, 1));
1754 folder_unix->asof = time (NULL);
1759 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
1761 const char *basename = key;
1762 struct stat_info_entry *entry = value;
1763 GtkFileFolderUnix *folder_unix = user_data;
1764 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1767 if (stat (fullname, &entry->statbuf) == -1 &&
1768 (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
1769 result = TRUE; /* Couldn't stat -- remove from hash. */
1779 fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error)
1781 if (folder_unix->have_stat)
1784 if (!fill_in_names (folder_unix, error))
1788 g_print ("Stating directory %s\n", folder_unix->filename);
1790 g_hash_table_foreach_remove (folder_unix->stat_info,
1794 folder_unix->have_stat = TRUE;
1800 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
1802 const char *basename = key;
1803 struct stat_info_entry *entry = value;
1804 GtkFileFolderUnix *folder_unix = user_data;
1805 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1807 /* FIXME: Should not need to re-stat. */
1808 const char *mime_type = xdg_mime_get_mime_type_for_file (fullname);
1809 entry->mime_type = g_strdup (mime_type);
1812 /* FIXME: free on NULL? */
1817 fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error)
1819 if (folder_unix->have_mime_type)
1823 g_print ("Getting mime types for directory %s\n", folder_unix->filename);
1825 g_hash_table_foreach_remove (folder_unix->stat_info,
1826 cb_fill_in_mime_type,
1829 folder_unix->have_mime_type = TRUE;
1833 static GtkFilePath *
1834 filename_to_path (const char *filename)
1836 return gtk_file_path_new_dup (filename);
1840 filename_is_root (const char *filename)
1842 const gchar *after_root;
1844 after_root = g_path_skip_root (filename);
1846 return (after_root != NULL && *after_root == '\0');