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.
24 #include "gtkfilesystem.h"
25 #include "gtkfilesystemunix.h"
26 #include "gtkicontheme.h"
30 #define XDG_PREFIX _gtk_xdg
31 #include "xdgmime/xdgmime.h"
36 #include <sys/types.h>
44 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
45 #define BOOKMARKS_TMP_FILENAME ".gtk-bookmarks-XXXXXX"
47 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
49 typedef struct _GtkFileSystemUnixClass GtkFileSystemUnixClass;
51 #define GTK_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
52 #define GTK_IS_FILE_SYSTEM_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_UNIX))
53 #define GTK_FILE_SYSTEM_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_UNIX, GtkFileSystemUnixClass))
55 struct _GtkFileSystemUnixClass
57 GObjectClass parent_class;
60 struct _GtkFileSystemUnix
62 GObject parent_instance;
64 GHashTable *folder_hash;
67 /* Icon type, supplemented by MIME type
70 ICON_UNDECIDED, /* Only used while we have not yet computed the icon in a struct stat_info_entry */
71 ICON_NONE, /* "Could not compute the icon type" */
72 ICON_REGULAR, /* Use mime type for icon */
74 ICON_BROKEN_SYMBOLIC_LINK,
75 ICON_CHARACTER_DEVICE,
83 #define GTK_TYPE_FILE_FOLDER_UNIX (gtk_file_folder_unix_get_type ())
84 #define GTK_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnix))
85 #define GTK_IS_FILE_FOLDER_UNIX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_UNIX))
86 #define GTK_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
87 #define GTK_IS_FILE_FOLDER_UNIX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_UNIX))
88 #define GTK_FILE_FOLDER_UNIX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_UNIX, GtkFileFolderUnixClass))
90 typedef struct _GtkFileFolderUnix GtkFileFolderUnix;
91 typedef struct _GtkFileFolderUnixClass GtkFileFolderUnixClass;
93 struct _GtkFileFolderUnixClass
95 GObjectClass parent_class;
98 struct _GtkFileFolderUnix
100 GObject parent_instance;
102 GtkFileSystemUnix *system_unix;
103 GtkFileInfoType types;
105 GHashTable *stat_info;
106 unsigned int have_stat : 1;
107 unsigned int have_mime_type : 1;
111 struct stat_info_entry {
117 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
118 GTK_FILE_INFO_IS_HIDDEN |
119 GTK_FILE_INFO_MODIFICATION_TIME |
122 static GObjectClass *system_parent_class;
123 static GObjectClass *folder_parent_class;
125 static void gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class);
126 static void gtk_file_system_unix_iface_init (GtkFileSystemIface *iface);
127 static void gtk_file_system_unix_init (GtkFileSystemUnix *impl);
128 static void gtk_file_system_unix_finalize (GObject *object);
130 static GSList * gtk_file_system_unix_list_volumes (GtkFileSystem *file_system);
131 static GtkFileSystemVolume *gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
132 const GtkFilePath *path);
134 static GtkFileFolder *gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
135 const GtkFilePath *path,
136 GtkFileInfoType types,
138 static gboolean gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
139 const GtkFilePath *path,
142 static void gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
143 GtkFileSystemVolume *volume);
144 static GtkFilePath *gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
145 GtkFileSystemVolume *volume);
146 static gboolean gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
147 GtkFileSystemVolume *volume);
148 static gboolean gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
149 GtkFileSystemVolume *volume,
151 static gchar * gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
152 GtkFileSystemVolume *volume);
153 static GdkPixbuf * gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
154 GtkFileSystemVolume *volume,
159 static gboolean gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
160 const GtkFilePath *path,
161 GtkFilePath **parent,
163 static GtkFilePath * gtk_file_system_unix_make_path (GtkFileSystem *file_system,
164 const GtkFilePath *base_path,
165 const gchar *display_name,
167 static gboolean gtk_file_system_unix_parse (GtkFileSystem *file_system,
168 const GtkFilePath *base_path,
170 GtkFilePath **folder,
174 static gchar * gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
175 const GtkFilePath *path);
176 static gchar * gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
177 const GtkFilePath *path);
178 static GtkFilePath *gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
180 static GtkFilePath *gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
181 const gchar *filename);
183 static GdkPixbuf *gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
184 const GtkFilePath *path,
189 static gboolean gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
190 const GtkFilePath *path,
193 static gboolean gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
194 const GtkFilePath *path,
196 static GSList * gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system);
198 static GType gtk_file_folder_unix_get_type (void);
199 static void gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class);
200 static void gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface);
201 static void gtk_file_folder_unix_init (GtkFileFolderUnix *impl);
202 static void gtk_file_folder_unix_finalize (GObject *object);
204 static GtkFileInfo *gtk_file_folder_unix_get_info (GtkFileFolder *folder,
205 const GtkFilePath *path,
207 static gboolean gtk_file_folder_unix_list_children (GtkFileFolder *folder,
211 static gboolean gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder);
213 static GtkFilePath *filename_to_path (const gchar *filename);
215 static gboolean filename_is_root (const char *filename);
217 static gboolean fill_in_names (GtkFileFolderUnix *folder_unix, GError **error);
218 static gboolean fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error);
219 static gboolean fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error);
221 static char * get_parent_dir (const char *filename);
227 gtk_file_system_unix_get_type (void)
229 static GType file_system_unix_type = 0;
231 if (!file_system_unix_type)
233 static const GTypeInfo file_system_unix_info =
235 sizeof (GtkFileSystemUnixClass),
236 NULL, /* base_init */
237 NULL, /* base_finalize */
238 (GClassInitFunc) gtk_file_system_unix_class_init,
239 NULL, /* class_finalize */
240 NULL, /* class_data */
241 sizeof (GtkFileSystemUnix),
243 (GInstanceInitFunc) gtk_file_system_unix_init,
246 static const GInterfaceInfo file_system_info =
248 (GInterfaceInitFunc) gtk_file_system_unix_iface_init, /* interface_init */
249 NULL, /* interface_finalize */
250 NULL /* interface_data */
253 file_system_unix_type = g_type_register_static (G_TYPE_OBJECT,
255 &file_system_unix_info, 0);
256 g_type_add_interface_static (file_system_unix_type,
257 GTK_TYPE_FILE_SYSTEM,
261 return file_system_unix_type;
265 * gtk_file_system_unix_new:
267 * Creates a new #GtkFileSystemUnix object. #GtkFileSystemUnix
268 * implements the #GtkFileSystem interface with direct access to
269 * the filesystem using Unix/Linux API calls
271 * Return value: the new #GtkFileSystemUnix object
274 gtk_file_system_unix_new (void)
276 return g_object_new (GTK_TYPE_FILE_SYSTEM_UNIX, NULL);
280 gtk_file_system_unix_class_init (GtkFileSystemUnixClass *class)
282 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
284 system_parent_class = g_type_class_peek_parent (class);
286 gobject_class->finalize = gtk_file_system_unix_finalize;
290 gtk_file_system_unix_iface_init (GtkFileSystemIface *iface)
292 iface->list_volumes = gtk_file_system_unix_list_volumes;
293 iface->get_volume_for_path = gtk_file_system_unix_get_volume_for_path;
294 iface->get_folder = gtk_file_system_unix_get_folder;
295 iface->create_folder = gtk_file_system_unix_create_folder;
296 iface->volume_free = gtk_file_system_unix_volume_free;
297 iface->volume_get_base_path = gtk_file_system_unix_volume_get_base_path;
298 iface->volume_get_is_mounted = gtk_file_system_unix_volume_get_is_mounted;
299 iface->volume_mount = gtk_file_system_unix_volume_mount;
300 iface->volume_get_display_name = gtk_file_system_unix_volume_get_display_name;
301 iface->volume_render_icon = gtk_file_system_unix_volume_render_icon;
302 iface->get_parent = gtk_file_system_unix_get_parent;
303 iface->make_path = gtk_file_system_unix_make_path;
304 iface->parse = gtk_file_system_unix_parse;
305 iface->path_to_uri = gtk_file_system_unix_path_to_uri;
306 iface->path_to_filename = gtk_file_system_unix_path_to_filename;
307 iface->uri_to_path = gtk_file_system_unix_uri_to_path;
308 iface->filename_to_path = gtk_file_system_unix_filename_to_path;
309 iface->render_icon = gtk_file_system_unix_render_icon;
310 iface->insert_bookmark = gtk_file_system_unix_insert_bookmark;
311 iface->remove_bookmark = gtk_file_system_unix_remove_bookmark;
312 iface->list_bookmarks = gtk_file_system_unix_list_bookmarks;
316 gtk_file_system_unix_init (GtkFileSystemUnix *system_unix)
318 system_unix->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
322 gtk_file_system_unix_finalize (GObject *object)
324 GtkFileSystemUnix *system_unix;
326 system_unix = GTK_FILE_SYSTEM_UNIX (object);
328 /* FIXME: assert that the hash is empty? */
329 g_hash_table_destroy (system_unix->folder_hash);
331 system_parent_class->finalize (object);
334 /* Returns our single root volume */
335 static GtkFileSystemVolume *
336 get_root_volume (void)
338 return (GtkFileSystemVolume *) gtk_file_path_new_dup ("/");
342 gtk_file_system_unix_list_volumes (GtkFileSystem *file_system)
344 return g_slist_append (NULL, get_root_volume ());
347 static GtkFileSystemVolume *
348 gtk_file_system_unix_get_volume_for_path (GtkFileSystem *file_system,
349 const GtkFilePath *path)
351 return get_root_volume ();
355 remove_trailing_slash (const char *filename)
359 len = strlen (filename);
361 if (len > 1 && filename[len - 1] == '/')
362 return g_strndup (filename, len - 1);
364 return g_memdup (filename, len + 1);
367 static GtkFileFolder *
368 gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
369 const GtkFilePath *path,
370 GtkFileInfoType types,
373 GtkFileSystemUnix *system_unix;
374 GtkFileFolderUnix *folder_unix;
375 const char *filename;
377 time_t now = time (NULL);
379 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
381 filename = gtk_file_path_get_string (path);
382 g_return_val_if_fail (filename != NULL, NULL);
383 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
385 filename_copy = remove_trailing_slash (filename);
386 folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
390 g_free (filename_copy);
391 if (now - folder_unix->asof >= FOLDER_CACHE_LIFETIME &&
392 folder_unix->stat_info)
395 g_print ("Cleaning out cached directory %s\n", filename);
397 g_hash_table_destroy (folder_unix->stat_info);
398 folder_unix->stat_info = NULL;
399 folder_unix->have_mime_type = FALSE;
400 folder_unix->have_stat = FALSE;
403 g_object_ref (folder_unix);
404 folder_unix->types |= types;
405 types = folder_unix->types;
409 if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
411 int save_errno = errno;
412 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
414 /* If g_file_test() returned FALSE but not due to an error, it means
415 * that the filename is not a directory.
420 GTK_FILE_SYSTEM_ERROR,
421 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
423 filename_utf8 ? filename_utf8 : "???",
424 g_strerror (ENOTDIR));
427 GTK_FILE_SYSTEM_ERROR,
428 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
429 _("error getting information for '%s': %s"),
430 filename_utf8 ? filename_utf8 : "???",
431 g_strerror (save_errno));
433 g_free (filename_utf8);
434 g_free (filename_copy);
438 folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
439 folder_unix->system_unix = system_unix;
440 folder_unix->filename = filename_copy;
441 folder_unix->types = types;
442 folder_unix->stat_info = NULL;
443 folder_unix->asof = now;
444 folder_unix->have_mime_type = FALSE;
445 folder_unix->have_stat = FALSE;
447 g_hash_table_insert (system_unix->folder_hash,
448 folder_unix->filename,
452 if ((types & STAT_NEEDED_MASK) && !fill_in_stats (folder_unix, error))
454 g_object_unref (folder_unix);
457 if ((types & GTK_FILE_INFO_MIME_TYPE) && !fill_in_mime_type (folder_unix, error))
459 g_object_unref (folder_unix);
463 return GTK_FILE_FOLDER (folder_unix);
467 gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
468 const GtkFilePath *path,
471 GtkFileSystemUnix *system_unix;
472 const char *filename;
476 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
478 filename = gtk_file_path_get_string (path);
479 g_return_val_if_fail (filename != NULL, FALSE);
480 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
482 tmp = remove_trailing_slash (filename);
483 result = mkdir (tmp, 0777) == 0;
488 int save_errno = errno;
489 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
491 GTK_FILE_SYSTEM_ERROR,
492 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
493 _("error creating directory '%s': %s"),
494 filename_utf8 ? filename_utf8 : "???",
495 g_strerror (save_errno));
496 g_free (filename_utf8);
500 if (filename_is_root (filename))
501 return TRUE; /* hmmm, but with no notification */
503 parent = get_parent_dir (filename);
506 GtkFileFolderUnix *folder_unix;
508 folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
511 GtkFileInfoType types;
512 GtkFilePath *parent_path;
514 GtkFileFolder *folder;
516 /* This is sort of a hack. We re-get the folder, to ensure that the
517 * newly-created directory gets read into the folder's info hash table.
520 types = folder_unix->types;
522 parent_path = gtk_file_path_new_dup (parent);
523 folder = gtk_file_system_get_folder (file_system, parent_path, types, NULL);
524 gtk_file_path_free (parent_path);
528 paths = g_slist_append (NULL, (GtkFilePath *) path);
529 g_signal_emit_by_name (folder, "files-added", paths);
530 g_slist_free (paths);
531 g_object_unref (folder);
542 gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
543 GtkFileSystemVolume *volume)
547 path = (GtkFilePath *) volume;
548 gtk_file_path_free (path);
552 gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
553 GtkFileSystemVolume *volume)
555 return gtk_file_path_new_dup ("/");
559 gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
560 GtkFileSystemVolume *volume)
566 gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
567 GtkFileSystemVolume *volume,
571 GTK_FILE_SYSTEM_ERROR,
572 GTK_FILE_SYSTEM_ERROR_FAILED,
573 _("This file system does not support mounting"));
578 gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
579 GtkFileSystemVolume *volume)
581 return g_strdup (_("Filesystem")); /* Same as Nautilus */
585 get_icon_type_from_stat (struct stat *statp)
587 if (S_ISBLK (statp->st_mode))
588 return ICON_BLOCK_DEVICE;
589 else if (S_ISLNK (statp->st_mode))
590 return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
591 else if (S_ISCHR (statp->st_mode))
592 return ICON_CHARACTER_DEVICE;
593 else if (S_ISDIR (statp->st_mode))
594 return ICON_DIRECTORY;
596 else if (S_ISFIFO (statp->st_mode))
600 else if (S_ISSOCK (statp->st_mode))
608 get_icon_type (const char *filename,
613 /* If stat fails, try to fall back to lstat to catch broken links
615 if (stat (filename, &statbuf) != 0)
617 if (errno != ENOENT || lstat (filename, &statbuf) != 0)
619 int save_errno = errno;
620 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
622 GTK_FILE_SYSTEM_ERROR,
623 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
624 _("error getting information for '%s': %s"),
625 filename_utf8 ? filename_utf8 : "???",
626 g_strerror (save_errno));
627 g_free (filename_utf8);
633 return get_icon_type_from_stat (&statbuf);
643 icon_cache_element_free (IconCacheElement *element)
646 g_object_unref (element->pixbuf);
651 icon_theme_changed (GtkIconTheme *icon_theme)
655 /* Difference from the initial creation is that we don't
656 * reconnect the signal
658 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
659 (GDestroyNotify)g_free,
660 (GDestroyNotify)icon_cache_element_free);
661 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
662 cache, (GDestroyNotify)g_hash_table_destroy);
666 get_cached_icon (GtkWidget *widget,
670 GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
671 GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
672 IconCacheElement *element;
676 cache = g_hash_table_new_full (g_str_hash, g_str_equal,
677 (GDestroyNotify)g_free,
678 (GDestroyNotify)icon_cache_element_free);
680 g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
681 cache, (GDestroyNotify)g_hash_table_destroy);
682 g_signal_connect (icon_theme, "changed",
683 G_CALLBACK (icon_theme_changed), NULL);
686 element = g_hash_table_lookup (cache, name);
689 element = g_new0 (IconCacheElement, 1);
690 g_hash_table_insert (cache, g_strdup (name), element);
693 if (element->size != pixel_size)
696 g_object_unref (element->pixbuf);
697 element->size = pixel_size;
698 element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
699 pixel_size, 0, NULL);
702 return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
705 /* Renders a fallback icon from the stock system */
707 get_fallback_icon (GtkWidget *widget,
711 const char *stock_name;
716 case ICON_BLOCK_DEVICE:
717 stock_name = GTK_STOCK_HARDDISK;
721 stock_name = GTK_STOCK_DIRECTORY;
724 case ICON_EXECUTABLE:
725 stock_name = GTK_STOCK_EXECUTE;
729 stock_name = GTK_STOCK_FILE;
733 pixbuf = gtk_widget_render_icon (widget, stock_name, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL);
736 GTK_FILE_SYSTEM_ERROR,
737 GTK_FILE_SYSTEM_ERROR_FAILED,
738 _("Could not get a stock icon for %s"),
745 gtk_file_system_unix_volume_render_icon (GtkFileSystem *file_system,
746 GtkFileSystemVolume *volume,
753 pixbuf = get_cached_icon (widget, "gnome-fs-blockdev", pixel_size);
757 pixbuf = get_fallback_icon (widget, ICON_BLOCK_DEVICE, error);
758 g_assert (pixbuf != NULL);
764 get_parent_dir (const char *filename)
768 len = strlen (filename);
770 /* Ignore trailing slashes */
771 if (len > 1 && filename[len - 1] == '/')
775 tmp = g_strndup (filename, len - 1);
777 parent = g_path_get_dirname (tmp);
783 return g_path_get_dirname (filename);
787 gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
788 const GtkFilePath *path,
789 GtkFilePath **parent,
792 const char *filename;
794 filename = gtk_file_path_get_string (path);
795 g_return_val_if_fail (filename != NULL, FALSE);
796 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
798 if (filename_is_root (filename))
804 gchar *parent_filename = get_parent_dir (filename);
805 *parent = filename_to_path (parent_filename);
806 g_free (parent_filename);
813 gtk_file_system_unix_make_path (GtkFileSystem *file_system,
814 const GtkFilePath *base_path,
815 const gchar *display_name,
818 const char *base_filename;
820 gchar *full_filename;
821 GError *tmp_error = NULL;
824 base_filename = gtk_file_path_get_string (base_path);
825 g_return_val_if_fail (base_filename != NULL, NULL);
826 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
828 if (strchr (display_name, G_DIR_SEPARATOR))
831 GTK_FILE_SYSTEM_ERROR,
832 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
833 _("The name \"%s\" is not valid because it contains the character \"%s\". "
834 "Please use a different name."),
840 filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
844 GTK_FILE_SYSTEM_ERROR,
845 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
849 g_error_free (tmp_error);
854 full_filename = g_build_filename (base_filename, filename, NULL);
855 result = filename_to_path (full_filename);
857 g_free (full_filename);
862 /* If this was a publically exported function, it should return
863 * a dup'ed result, but we make it modify-in-place for efficiency
864 * here, and because it works for us.
867 canonicalize_filename (gchar *filename)
870 gboolean last_was_slash = FALSE;
877 if (*p == G_DIR_SEPARATOR)
880 *q++ = G_DIR_SEPARATOR;
882 last_was_slash = TRUE;
886 if (last_was_slash && *p == '.')
888 if (*(p + 1) == G_DIR_SEPARATOR ||
891 if (*(p + 1) == '\0')
896 else if (*(p + 1) == '.' &&
897 (*(p + 2) == G_DIR_SEPARATOR ||
900 if (q > filename + 1)
903 while (q > filename + 1 &&
904 *(q - 1) != G_DIR_SEPARATOR)
908 if (*(p + 2) == '\0')
916 last_was_slash = FALSE;
922 last_was_slash = FALSE;
929 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
935 /* Takes a user-typed filename and expands a tilde at the beginning of the string */
937 expand_tilde (const char *filename)
943 if (filename[0] != '~')
944 return g_strdup (filename);
946 notilde = filename + 1;
948 slash = strchr (notilde, G_DIR_SEPARATOR);
952 if (slash == notilde)
954 home = g_get_home_dir ();
957 return g_strdup (filename);
962 struct passwd *passwd;
964 username = g_strndup (notilde, slash - notilde);
965 passwd = getpwnam (username);
969 return g_strdup (filename);
971 home = passwd->pw_dir;
974 return g_build_filename (home, G_DIR_SEPARATOR_S, slash + 1, NULL);
978 gtk_file_system_unix_parse (GtkFileSystem *file_system,
979 const GtkFilePath *base_path,
981 GtkFilePath **folder,
985 const char *base_filename;
988 gboolean result = FALSE;
990 base_filename = gtk_file_path_get_string (base_path);
991 g_return_val_if_fail (base_filename != NULL, FALSE);
992 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
994 filename = expand_tilde (str);
998 GTK_FILE_SYSTEM_ERROR,
999 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1000 "%s", ""); /* nothing for now, as we are string-frozen */
1004 last_slash = strrchr (filename, G_DIR_SEPARATOR);
1007 *folder = gtk_file_path_copy (base_path);
1008 *file_part = g_strdup (filename);
1015 GError *tmp_error = NULL;
1017 if (last_slash == filename)
1018 folder_part = g_strdup ("/");
1020 folder_part = g_filename_from_utf8 (filename, last_slash - filename,
1021 NULL, NULL, &tmp_error);
1026 GTK_FILE_SYSTEM_ERROR,
1027 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1029 tmp_error->message);
1030 g_error_free (tmp_error);
1034 if (folder_part[0] == G_DIR_SEPARATOR)
1035 folder_path = folder_part;
1038 folder_path = g_build_filename (base_filename, folder_part, NULL);
1039 g_free (folder_part);
1042 canonicalize_filename (folder_path);
1044 *folder = filename_to_path (folder_path);
1045 *file_part = g_strdup (last_slash + 1);
1047 g_free (folder_path);
1059 gtk_file_system_unix_path_to_uri (GtkFileSystem *file_system,
1060 const GtkFilePath *path)
1062 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1066 gtk_file_system_unix_path_to_filename (GtkFileSystem *file_system,
1067 const GtkFilePath *path)
1069 return g_strdup (gtk_file_path_get_string (path));
1072 static GtkFilePath *
1073 gtk_file_system_unix_uri_to_path (GtkFileSystem *file_system,
1077 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1081 path = filename_to_path (filename);
1090 static GtkFilePath *
1091 gtk_file_system_unix_filename_to_path (GtkFileSystem *file_system,
1092 const gchar *filename)
1094 return filename_to_path (filename);
1097 /* Returns the name of the icon to be used for a path which is known to be a
1098 * directory. This can vary for Home, Desktop, etc.
1101 get_icon_name_for_directory (const char *path)
1103 static char *desktop_path = NULL;
1105 if (!g_get_home_dir ())
1106 return "gnome-fs-directory";
1109 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1111 if (strcmp (g_get_home_dir (), path) == 0)
1112 return "gnome-fs-home";
1113 else if (strcmp (desktop_path, path) == 0)
1114 return "gnome-fs-desktop";
1116 return "gnome-fs-directory";
1119 /* Computes our internal icon type based on a path name; also returns the MIME
1120 * type in case we come up with ICON_REGULAR.
1123 get_icon_type_from_path (GtkFileSystemUnix *system_unix,
1124 const GtkFilePath *path,
1125 const char **mime_type)
1127 const char *filename;
1129 GtkFileFolderUnix *folder_unix;
1132 filename = gtk_file_path_get_string (path);
1133 dirname = g_path_get_dirname (filename);
1134 folder_unix = g_hash_table_lookup (system_unix->folder_hash, dirname);
1142 struct stat_info_entry *entry;
1144 if (!fill_in_stats (folder_unix, NULL))
1147 basename = g_path_get_basename (filename);
1148 entry = g_hash_table_lookup (folder_unix->stat_info, basename);
1152 if (entry->icon_type == ICON_UNDECIDED)
1154 entry->icon_type = get_icon_type_from_stat (&entry->statbuf);
1155 g_assert (entry->icon_type != ICON_UNDECIDED);
1157 icon_type = entry->icon_type;
1158 if (icon_type == ICON_REGULAR)
1160 (void)fill_in_mime_type (folder_unix, NULL);
1161 *mime_type = entry->mime_type;
1165 icon_type = ICON_NONE;
1169 icon_type = get_icon_type (filename, NULL);
1170 if (icon_type == ICON_REGULAR)
1171 *mime_type = xdg_mime_get_mime_type_for_file (filename);
1177 /* Renders an icon for a non-ICON_REGULAR file */
1179 get_special_icon (IconType icon_type,
1180 const GtkFilePath *path,
1186 g_assert (icon_type != ICON_REGULAR);
1190 case ICON_BLOCK_DEVICE:
1191 name = "gnome-fs-blockdev";
1193 case ICON_BROKEN_SYMBOLIC_LINK:
1194 name = "gnome-fs-symlink";
1196 case ICON_CHARACTER_DEVICE:
1197 name = "gnome-fs-chardev";
1199 case ICON_DIRECTORY: {
1200 const char *filename;
1202 filename = gtk_file_path_get_string (path);
1203 name = get_icon_name_for_directory (filename);
1206 case ICON_EXECUTABLE:
1207 name ="gnome-fs-executable";
1210 name = "gnome-fs-fifo";
1213 name = "gnome-fs-socket";
1216 g_assert_not_reached ();
1220 return get_cached_icon (widget, name, pixel_size);
1224 get_icon_for_mime_type (GtkWidget *widget,
1225 const char *mime_type,
1228 const char *separator;
1232 separator = strchr (mime_type, '/');
1234 return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1236 icon_name = g_string_new ("gnome-mime-");
1237 g_string_append_len (icon_name, mime_type, separator - mime_type);
1238 g_string_append_c (icon_name, '-');
1239 g_string_append (icon_name, separator + 1);
1240 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1241 g_string_free (icon_name, TRUE);
1245 icon_name = g_string_new ("gnome-mime-");
1246 g_string_append_len (icon_name, mime_type, separator - mime_type);
1247 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1248 g_string_free (icon_name, TRUE);
1254 gtk_file_system_unix_render_icon (GtkFileSystem *file_system,
1255 const GtkFilePath *path,
1260 GtkFileSystemUnix *system_unix;
1262 const char *mime_type;
1265 system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
1267 icon_type = get_icon_type_from_path (system_unix, path, &mime_type);
1269 switch (icon_type) {
1274 pixbuf = get_icon_for_mime_type (widget, mime_type, pixel_size);
1278 pixbuf = get_special_icon (icon_type, path, widget, pixel_size);
1286 pixbuf = get_cached_icon (widget, "gnome-fs-regular", pixel_size);
1290 pixbuf = get_fallback_icon (widget, icon_type, error);
1298 bookmark_list_free (GSList *list)
1302 for (l = list; l; l = l->next)
1305 g_slist_free (list);
1308 /* Returns whether a URI is a local file:// */
1310 is_local_uri (const char *uri)
1316 /* This is rather crude, but hey */
1317 filename = g_filename_from_uri (uri, &hostname, NULL);
1319 result = (filename && !hostname);
1328 bookmark_get_filename (gboolean tmp_file)
1332 filename = g_build_filename (g_get_home_dir (),
1333 tmp_file ? BOOKMARKS_TMP_FILENAME : BOOKMARKS_FILENAME,
1335 g_assert (filename != NULL);
1340 bookmark_list_read (GSList **bookmarks, GError **error)
1344 gboolean result = FALSE;
1346 filename = bookmark_get_filename (FALSE);
1349 if (g_file_get_contents (filename, &contents, NULL, error))
1351 gchar **lines = g_strsplit (contents, "\n", -1);
1355 table = g_hash_table_new (g_str_hash, g_str_equal);
1357 for (i = 0; lines[i]; i++)
1359 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
1361 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
1362 g_hash_table_insert (table, lines[i], lines[i]);
1367 g_hash_table_destroy (table);
1370 *bookmarks = g_slist_reverse (*bookmarks);
1380 bookmark_list_write (GSList *bookmarks, GError **error)
1384 gboolean result = TRUE;
1389 /* First, write a temporary file */
1391 tmp_filename = bookmark_get_filename (TRUE);
1392 filename = bookmark_get_filename (FALSE);
1394 fd = g_mkstemp (tmp_filename);
1397 saved_errno = errno;
1401 if ((file = fdopen (fd, "w")) != NULL)
1405 for (l = bookmarks; l; l = l->next)
1406 if (fputs (l->data, file) == EOF
1407 || fputs ("\n", file) == EOF)
1409 saved_errno = errno;
1413 if (fclose (file) == EOF)
1415 saved_errno = errno;
1419 if (rename (tmp_filename, filename) == -1)
1421 saved_errno = errno;
1430 saved_errno = errno;
1432 /* fdopen() failed, so we can't do much error checking here anyway */
1439 GTK_FILE_SYSTEM_ERROR,
1440 GTK_FILE_SYSTEM_ERROR_FAILED,
1441 _("Bookmark saving failed (%s)"),
1442 g_strerror (saved_errno));
1446 unlink (tmp_filename); /* again, not much error checking we can do here */
1451 g_free (tmp_filename);
1457 gtk_file_system_unix_insert_bookmark (GtkFileSystem *file_system,
1458 const GtkFilePath *path,
1470 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
1472 g_propagate_error (error, err);
1476 num_bookmarks = g_slist_length (bookmarks);
1477 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
1481 uri = gtk_file_system_unix_path_to_uri (file_system, path);
1483 for (l = bookmarks; l; l = l->next)
1485 const char *bookmark;
1488 if (strcmp (bookmark, uri) == 0)
1491 GTK_FILE_SYSTEM_ERROR,
1492 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
1493 "%s already exists in the bookmarks list",
1499 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
1500 if (bookmark_list_write (bookmarks, error))
1503 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1509 bookmark_list_free (bookmarks);
1515 gtk_file_system_unix_remove_bookmark (GtkFileSystem *file_system,
1516 const GtkFilePath *path,
1524 if (!bookmark_list_read (&bookmarks, error))
1529 uri = gtk_file_system_path_to_uri (file_system, path);
1531 for (l = bookmarks; l; l = l->next)
1533 const char *bookmark;
1536 if (strcmp (bookmark, uri) == 0)
1539 bookmarks = g_slist_remove_link (bookmarks, l);
1542 if (bookmark_list_write (bookmarks, error))
1545 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1553 GTK_FILE_SYSTEM_ERROR,
1554 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1555 "%s does not exist in the bookmarks list",
1561 bookmark_list_free (bookmarks);
1567 gtk_file_system_unix_list_bookmarks (GtkFileSystem *file_system)
1573 if (!bookmark_list_read (&bookmarks, NULL))
1578 for (l = bookmarks; l; l = l->next)
1584 if (is_local_uri (name))
1585 result = g_slist_prepend (result, gtk_file_system_unix_uri_to_path (file_system, name));
1588 bookmark_list_free (bookmarks);
1590 result = g_slist_reverse (result);
1598 gtk_file_folder_unix_get_type (void)
1600 static GType file_folder_unix_type = 0;
1602 if (!file_folder_unix_type)
1604 static const GTypeInfo file_folder_unix_info =
1606 sizeof (GtkFileFolderUnixClass),
1607 NULL, /* base_init */
1608 NULL, /* base_finalize */
1609 (GClassInitFunc) gtk_file_folder_unix_class_init,
1610 NULL, /* class_finalize */
1611 NULL, /* class_data */
1612 sizeof (GtkFileFolderUnix),
1613 0, /* n_preallocs */
1614 (GInstanceInitFunc) gtk_file_folder_unix_init,
1617 static const GInterfaceInfo file_folder_info =
1619 (GInterfaceInitFunc) gtk_file_folder_unix_iface_init, /* interface_init */
1620 NULL, /* interface_finalize */
1621 NULL /* interface_data */
1624 file_folder_unix_type = g_type_register_static (G_TYPE_OBJECT,
1625 "GtkFileFolderUnix",
1626 &file_folder_unix_info, 0);
1627 g_type_add_interface_static (file_folder_unix_type,
1628 GTK_TYPE_FILE_FOLDER,
1632 return file_folder_unix_type;
1636 gtk_file_folder_unix_class_init (GtkFileFolderUnixClass *class)
1638 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1640 folder_parent_class = g_type_class_peek_parent (class);
1642 gobject_class->finalize = gtk_file_folder_unix_finalize;
1646 gtk_file_folder_unix_iface_init (GtkFileFolderIface *iface)
1648 iface->get_info = gtk_file_folder_unix_get_info;
1649 iface->list_children = gtk_file_folder_unix_list_children;
1650 iface->is_finished_loading = gtk_file_folder_unix_is_finished_loading;
1654 gtk_file_folder_unix_init (GtkFileFolderUnix *impl)
1659 gtk_file_folder_unix_finalize (GObject *object)
1661 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (object);
1663 g_hash_table_remove (folder_unix->system_unix->folder_hash, folder_unix->filename);
1665 if (folder_unix->stat_info)
1668 g_print ("Releasing information for directory %s\n", folder_unix->filename);
1670 g_hash_table_destroy (folder_unix->stat_info);
1673 g_free (folder_unix->filename);
1675 folder_parent_class->finalize (object);
1678 static GtkFileInfo *
1679 gtk_file_folder_unix_get_info (GtkFileFolder *folder,
1680 const GtkFilePath *path,
1683 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1685 gchar *dirname, *basename;
1686 const char *filename;
1687 struct stat_info_entry *entry;
1688 gboolean file_must_exist;
1689 GtkFileInfoType types;
1695 g_return_val_if_fail (filename_is_root (folder_unix->filename), NULL);
1697 if (stat (folder_unix->filename, &buf) != 0)
1700 info = gtk_file_info_new ();
1701 gtk_file_info_set_display_name (info, "/");
1702 gtk_file_info_set_is_folder (info, TRUE);
1703 gtk_file_info_set_is_hidden (info, FALSE);
1704 gtk_file_info_set_mime_type (info, "x-directory/normal");
1705 gtk_file_info_set_modification_time (info, buf.st_mtime);
1706 gtk_file_info_set_size (info, buf.st_size);
1711 filename = gtk_file_path_get_string (path);
1712 g_return_val_if_fail (filename != NULL, NULL);
1713 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1715 dirname = get_parent_dir (filename);
1716 g_return_val_if_fail (strcmp (dirname, folder_unix->filename) == 0, NULL);
1719 basename = g_path_get_basename (filename);
1720 types = folder_unix->types;
1721 file_must_exist = (types & ~GTK_FILE_INFO_DISPLAY_NAME) != 0;
1722 entry = file_must_exist
1723 ? g_hash_table_lookup (folder_unix->stat_info, basename)
1725 /* basename freed later. */
1727 if (!file_must_exist || entry)
1729 info = gtk_file_info_new ();
1733 gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1735 GTK_FILE_SYSTEM_ERROR,
1736 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1737 _("error getting information for '%s'"),
1738 filename_utf8 ? filename_utf8 : "???");
1739 g_free (filename_utf8);
1744 if (types & GTK_FILE_INFO_DISPLAY_NAME)
1746 gchar *display_name = g_filename_display_basename (filename);
1748 gtk_file_info_set_display_name (info, display_name);
1750 g_free (display_name);
1753 if (types & GTK_FILE_INFO_IS_HIDDEN)
1754 gtk_file_info_set_is_hidden (info, basename[0] == '.');
1756 if (types & GTK_FILE_INFO_IS_FOLDER)
1757 gtk_file_info_set_is_folder (info, S_ISDIR (entry->statbuf.st_mode));
1759 if (types & GTK_FILE_INFO_MIME_TYPE)
1760 gtk_file_info_set_mime_type (info, entry->mime_type);
1762 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1763 gtk_file_info_set_modification_time (info, entry->statbuf.st_mtime);
1765 if (types & GTK_FILE_INFO_SIZE)
1766 gtk_file_info_set_size (info, (gint64)entry->statbuf.st_size);
1775 cb_list_children (gpointer key, gpointer value, gpointer user_data)
1777 GSList **children = user_data;
1778 *children = g_slist_prepend (*children, key);
1782 gtk_file_folder_unix_list_children (GtkFileFolder *folder,
1786 GtkFileFolderUnix *folder_unix = GTK_FILE_FOLDER_UNIX (folder);
1789 if (!fill_in_names (folder_unix, error))
1794 /* Get the list of basenames. */
1795 g_hash_table_foreach (folder_unix->stat_info, cb_list_children, children);
1797 /* Turn basenames into GFilePaths. */
1798 for (l = *children; l; l = l->next)
1800 const char *basename = l->data;
1801 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1802 l->data = filename_to_path (fullname);
1805 g_signal_emit_by_name (folder_unix, "finished-loading");
1811 gtk_file_folder_unix_is_finished_loading (GtkFileFolder *folder)
1813 /* Since we don't do asynchronous loads, we are always finished loading */
1818 free_stat_info_entry (struct stat_info_entry *entry)
1820 g_free (entry->mime_type);
1825 fill_in_names (GtkFileFolderUnix *folder_unix, GError **error)
1829 if (folder_unix->stat_info)
1833 g_print ("Reading directory %s\n", folder_unix->filename);
1836 folder_unix->stat_info = g_hash_table_new_full (g_str_hash, g_str_equal,
1837 (GDestroyNotify)g_free,
1838 (GDestroyNotify)free_stat_info_entry);
1839 dir = g_dir_open (folder_unix->filename, 0, error);
1845 const gchar *basename = g_dir_read_name (dir);
1849 g_hash_table_insert (folder_unix->stat_info,
1850 g_strdup (basename),
1851 g_new0 (struct stat_info_entry, 1));
1856 folder_unix->asof = time (NULL);
1861 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
1863 const char *basename = key;
1864 struct stat_info_entry *entry = value;
1865 GtkFileFolderUnix *folder_unix = user_data;
1866 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1869 if (stat (fullname, &entry->statbuf) == -1 &&
1870 (errno != ENOENT || lstat (fullname, &entry->statbuf) == -1))
1871 result = TRUE; /* Couldn't stat -- remove from hash. */
1881 fill_in_stats (GtkFileFolderUnix *folder_unix, GError **error)
1883 if (folder_unix->have_stat)
1886 if (!fill_in_names (folder_unix, error))
1890 g_print ("Stating directory %s\n", folder_unix->filename);
1892 g_hash_table_foreach_remove (folder_unix->stat_info,
1896 folder_unix->have_stat = TRUE;
1902 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
1904 const char *basename = key;
1905 struct stat_info_entry *entry = value;
1906 GtkFileFolderUnix *folder_unix = user_data;
1907 char *fullname = g_build_filename (folder_unix->filename, basename, NULL);
1909 /* FIXME: Should not need to re-stat. */
1910 const char *mime_type = xdg_mime_get_mime_type_for_file (fullname);
1911 entry->mime_type = g_strdup (mime_type);
1914 /* FIXME: free on NULL? */
1919 fill_in_mime_type (GtkFileFolderUnix *folder_unix, GError **error)
1921 if (folder_unix->have_mime_type)
1925 g_print ("Getting mime types for directory %s\n", folder_unix->filename);
1927 g_hash_table_foreach_remove (folder_unix->stat_info,
1928 cb_fill_in_mime_type,
1931 folder_unix->have_mime_type = TRUE;
1935 static GtkFilePath *
1936 filename_to_path (const char *filename)
1940 tmp = remove_trailing_slash (filename);
1941 return gtk_file_path_new_steal (tmp);
1945 filename_is_root (const char *filename)
1947 const gchar *after_root;
1949 after_root = g_path_skip_root (filename);
1951 return (after_root != NULL && *after_root == '\0');