+ system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+
+ handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE_UNIX, NULL);
+ handle->file_system = file_system;
+
+ g_assert (g_hash_table_lookup (system_unix->handles, handle) == NULL);
+ g_hash_table_insert (system_unix->handles, handle, handle);
+
+ return handle;
+}
+
+
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_get_info (GtkFileSystem *file_system,
+ const GtkFilePath *path,
+ GtkFileInfoType types,
+ GtkFileSystemGetInfoCallback callback,
+ gpointer data)
+{
+ GError *error = NULL;
+ GtkFileSystemUnix *system_unix;
+ GtkFileSystemHandle *handle;
+ const char *filename;
+ GtkFileInfo *info;
+ gchar *basename;
+ struct stat statbuf;
+ const char *mime_type;
+
+ system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+ handle = create_handle (file_system);
+
+ filename = gtk_file_path_get_string (path);
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (g_path_is_absolute (filename), NULL);
+
+ if (!stat_with_error (filename, &statbuf, &error))
+ {
+ g_object_ref (handle);
+ queue_get_info_callback (callback, handle, NULL, error, data);
+ return handle;
+ }
+
+ if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
+ mime_type = xdg_mime_get_mime_type_for_file (filename, &statbuf);
+ else
+ mime_type = NULL;
+
+ basename = g_path_get_basename (filename);
+
+ info = create_file_info (NULL, filename, basename, types, &statbuf,
+ mime_type);
+ g_free (basename);
+ g_object_ref (handle);
+ queue_get_info_callback (callback, handle, info, NULL, data);
+
+ return handle;
+}
+
+static gboolean
+load_folder (gpointer data)
+{
+ GtkFileFolderUnix *folder_unix = data;
+ GSList *children;
+
+ if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
+ fill_in_stats (folder_unix);
+
+ if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
+ fill_in_mime_type (folder_unix);
+
+ if (gtk_file_folder_unix_list_children (GTK_FILE_FOLDER (folder_unix), &children, NULL))
+ {
+ folder_unix->is_finished_loading = TRUE;
+ g_signal_emit_by_name (folder_unix, "files-added", children);
+ gtk_file_paths_free (children);
+ }
+
+ folder_unix->load_folder_id = 0;
+
+ g_signal_emit_by_name (folder_unix, "finished-loading", 0);
+
+ return FALSE;
+}
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_get_folder (GtkFileSystem *file_system,
+ const GtkFilePath *path,
+ GtkFileInfoType types,
+ GtkFileSystemGetFolderCallback callback,
+ gpointer data)
+{
+ GError *error = NULL;
+ GtkFileSystemUnix *system_unix;
+ GtkFileFolderUnix *folder_unix;
+ GtkFileSystemHandle *handle;
+ const char *filename;
+ char *filename_copy;
+ gboolean set_asof = FALSE;
+
+ system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+
+ filename = gtk_file_path_get_string (path);
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (g_path_is_absolute (filename), NULL);
+
+ handle = create_handle (file_system);
+
+ filename_copy = remove_trailing_slash (filename);
+ folder_unix = g_hash_table_lookup (system_unix->folder_hash, filename_copy);
+
+ if (folder_unix)
+ {
+ g_free (filename_copy);
+ if (folder_unix->stat_info &&
+ time (NULL) - folder_unix->asof >= FOLDER_CACHE_LIFETIME)
+ {
+#if 0
+ g_print ("Cleaning out cached directory %s\n", filename);
+#endif
+ g_hash_table_destroy (folder_unix->stat_info);
+ folder_unix->stat_info = NULL;
+ folder_unix->have_mime_type = FALSE;
+ folder_unix->have_stat = FALSE;
+ folder_unix->have_hidden = FALSE;
+ set_asof = TRUE;
+ }
+
+ g_object_ref (folder_unix);
+ folder_unix->types |= types;
+ types = folder_unix->types;
+ }
+ else
+ {
+ struct stat statbuf;
+ int result;
+ int code;
+ int my_errno;
+
+ code = my_errno = 0; /* shut up GCC */
+
+ result = stat (filename, &statbuf);
+
+ if (result == 0)
+ {
+ if (!S_ISDIR (statbuf.st_mode))
+ {
+ result = -1;
+ code = GTK_FILE_SYSTEM_ERROR_NOT_FOLDER;
+ my_errno = ENOTDIR;
+ }
+ }
+ else
+ {
+ my_errno = errno;
+
+ if (my_errno == ENOENT)
+ code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
+ else
+ code = GTK_FILE_SYSTEM_ERROR_FAILED;
+ }
+
+ if (result != 0)
+ {
+ gchar *display_name = g_filename_display_name (filename);
+ g_set_error (&error,
+ GTK_FILE_SYSTEM_ERROR,
+ code,
+ _("Error getting information for '%s': %s"),
+ display_name,
+ g_strerror (my_errno));
+
+ g_object_ref (handle);
+ queue_get_folder_callback (callback, handle, NULL, error, data);
+
+ g_free (display_name);
+ g_free (filename_copy);
+ return handle;
+ }
+
+ folder_unix = g_object_new (GTK_TYPE_FILE_FOLDER_UNIX, NULL);
+ folder_unix->system_unix = system_unix;
+ folder_unix->filename = filename_copy;
+ folder_unix->types = types;
+ folder_unix->stat_info = NULL;
+ folder_unix->load_folder_id = 0;
+ folder_unix->have_mime_type = FALSE;
+ folder_unix->have_stat = FALSE;
+ folder_unix->have_hidden = FALSE;
+ folder_unix->is_finished_loading = FALSE;
+ set_asof = TRUE;
+
+ if ((system_unix->have_afs &&
+ system_unix->afs_statbuf.st_dev == statbuf.st_dev &&
+ system_unix->afs_statbuf.st_ino == statbuf.st_ino) ||
+ (system_unix->have_net &&
+ system_unix->net_statbuf.st_dev == statbuf.st_dev &&
+ system_unix->net_statbuf.st_ino == statbuf.st_ino))
+ folder_unix->is_network_dir = TRUE;
+ else
+ folder_unix->is_network_dir = FALSE;
+
+ g_hash_table_insert (system_unix->folder_hash,
+ folder_unix->filename,
+ folder_unix);
+ }
+
+ if (set_asof)
+ folder_unix->asof = time (NULL);
+
+ g_object_ref (handle);
+ queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_unix), NULL, data);
+
+ /* Start loading the folder contents in an idle */
+ if (!folder_unix->load_folder_id)
+ folder_unix->load_folder_id =
+ gdk_threads_add_idle ((GSourceFunc) load_folder, folder_unix);
+
+ return handle;
+}
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_create_folder (GtkFileSystem *file_system,
+ const GtkFilePath *path,
+ GtkFileSystemCreateFolderCallback callback,
+ gpointer data)
+{
+ GError *error = NULL;
+ GtkFileSystemUnix *system_unix;
+ GtkFileSystemHandle *handle;
+ const char *filename;
+ gboolean result;
+ char *parent, *tmp;
+ int save_errno = errno;
+
+ system_unix = GTK_FILE_SYSTEM_UNIX (file_system);
+
+ filename = gtk_file_path_get_string (path);
+ g_return_val_if_fail (filename != NULL, NULL);
+ g_return_val_if_fail (g_path_is_absolute (filename), NULL);
+
+ handle = create_handle (file_system);
+
+ tmp = remove_trailing_slash (filename);
+ errno = 0;
+ result = mkdir (tmp, 0777) == 0;
+ save_errno = errno;
+ g_free (tmp);
+
+ if (!result)
+ {
+ gchar *display_name = g_filename_display_name (filename);
+ g_set_error (&error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
+ _("Error creating folder '%s': %s"),
+ display_name,
+ g_strerror (save_errno));
+
+ g_object_ref (handle);
+ queue_create_folder_callback (callback, handle, path, error, data);
+
+ g_free (display_name);
+ return handle;
+ }
+
+ g_object_ref (handle);
+ queue_create_folder_callback (callback, handle, path, NULL, data);
+
+ parent = get_parent_dir (filename);
+ if (parent)
+ {
+ GtkFileFolderUnix *folder_unix;
+
+ folder_unix = g_hash_table_lookup (system_unix->folder_hash, parent);
+ if (folder_unix)
+ {
+ GSList *paths;
+ char *basename;
+ struct stat_info_entry *entry;
+
+ /* Make sure the new folder exists in the parent's folder */
+ entry = g_new0 (struct stat_info_entry, 1);
+ if (folder_unix->is_network_dir)
+ {
+ entry->statbuf.st_mode = S_IFDIR;
+ entry->mime_type = g_strdup ("x-directory/normal");
+ }
+
+ basename = g_path_get_basename (filename);
+ g_hash_table_insert (folder_unix->stat_info,
+ basename,
+ entry);
+
+ if (folder_unix->have_stat)
+ {
+ /* Cheating */
+ if ((folder_unix->types & STAT_NEEDED_MASK) != 0)
+ cb_fill_in_stats (basename, entry, folder_unix);
+
+ if ((folder_unix->types & GTK_FILE_INFO_MIME_TYPE) != 0)
+ cb_fill_in_mime_type (basename, entry, folder_unix);
+ }
+
+ paths = g_slist_append (NULL, (GtkFilePath *) path);
+ g_signal_emit_by_name (folder_unix, "files-added", paths);
+ g_slist_free (paths);
+ }
+
+ g_free (parent);
+ }
+
+ return handle;
+}
+
+static void
+gtk_file_system_unix_cancel_operation (GtkFileSystemHandle *handle)
+{
+ /* We don't set "cancelled" to TRUE here, since the actual operation
+ * is executed in the function itself and not in a callback. So
+ * the operations can never be cancelled (since they will be already
+ * completed at this point.
+ */
+}
+
+static void
+gtk_file_system_unix_volume_free (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume)
+{
+ GtkFilePath *path;
+
+ path = (GtkFilePath *) volume;
+ gtk_file_path_free (path);
+}
+
+static GtkFilePath *
+gtk_file_system_unix_volume_get_base_path (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume)
+{
+ return gtk_file_path_new_dup ("/");
+}
+
+static gboolean
+gtk_file_system_unix_volume_get_is_mounted (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume)
+{
+ return TRUE;
+}
+
+static GtkFileSystemHandle *
+gtk_file_system_unix_volume_mount (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume,
+ GtkFileSystemVolumeMountCallback callback,
+ gpointer data)
+{
+ GError *error = NULL;
+ GtkFileSystemHandle *handle = create_handle (file_system);
+
+ g_set_error (&error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_FAILED,
+ _("This file system does not support mounting"));
+
+ g_object_ref (handle);
+ queue_volume_mount_callback (callback, handle, volume, error, data);
+
+ return handle;
+}
+
+static gchar *
+gtk_file_system_unix_volume_get_display_name (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume)
+{
+ return g_strdup (_("File System")); /* Same as Nautilus */
+}
+
+static IconType
+get_icon_type_from_stat (struct stat *statp)
+{
+ if (S_ISBLK (statp->st_mode))
+ return ICON_BLOCK_DEVICE;
+ else if (S_ISLNK (statp->st_mode))
+ return ICON_BROKEN_SYMBOLIC_LINK; /* See get_icon_type */
+ else if (S_ISCHR (statp->st_mode))
+ return ICON_CHARACTER_DEVICE;
+ else if (S_ISDIR (statp->st_mode))
+ return ICON_DIRECTORY;
+#ifdef S_ISFIFO
+ else if (S_ISFIFO (statp->st_mode))
+ return ICON_FIFO;
+#endif
+#ifdef S_ISSOCK
+ else if (S_ISSOCK (statp->st_mode))
+ return ICON_SOCKET;
+#endif
+ else
+ return ICON_REGULAR;
+}
+
+static IconType
+get_icon_type (const char *filename,
+ GError **error)
+{
+ struct stat statbuf;
+
+ /* If stat fails, try to fall back to lstat to catch broken links
+ */
+ if (stat (filename, &statbuf) != 0)
+ {
+ if (errno != ENOENT || lstat (filename, &statbuf) != 0)
+ {
+ int save_errno = errno;
+ gchar *display_name = g_filename_display_name (filename);
+ g_set_error (error,
+ GTK_FILE_SYSTEM_ERROR,
+ GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
+ _("Error getting information for '%s': %s"),
+ display_name,
+ g_strerror (save_errno));
+ g_free (display_name);
+
+ return ICON_NONE;
+ }
+ }
+
+ return get_icon_type_from_stat (&statbuf);
+}
+
+/* Renders a fallback icon from the stock system */
+static const gchar *
+get_fallback_icon_name (IconType icon_type)
+{
+ const char *stock_name;
+
+ switch (icon_type)
+ {
+ case ICON_BLOCK_DEVICE:
+ stock_name = GTK_STOCK_HARDDISK;
+ break;
+
+ case ICON_DIRECTORY:
+ stock_name = GTK_STOCK_DIRECTORY;
+ break;
+
+ case ICON_EXECUTABLE:
+ stock_name = GTK_STOCK_EXECUTE;
+ break;
+
+ default:
+ stock_name = GTK_STOCK_FILE;
+ break;
+ }
+
+ return stock_name;
+}
+
+static gchar *
+gtk_file_system_unix_volume_get_icon_name (GtkFileSystem *file_system,
+ GtkFileSystemVolume *volume,
+ GError **error)
+{
+ /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
+ * or the new tango icon name?
+ */
+ return g_strdup ("gnome-dev-harddisk");
+}
+
+static char *
+get_parent_dir (const char *filename)
+{
+ int len;
+
+ len = strlen (filename);
+
+ /* Ignore trailing slashes */
+ if (len > 1 && filename[len - 1] == '/')
+ {
+ char *tmp, *parent;
+
+ tmp = g_strndup (filename, len - 1);
+
+ parent = g_path_get_dirname (tmp);
+ g_free (tmp);
+
+ return parent;
+ }
+ else
+ return g_path_get_dirname (filename);
+}
+
+static gboolean
+gtk_file_system_unix_get_parent (GtkFileSystem *file_system,
+ const GtkFilePath *path,
+ GtkFilePath **parent,
+ GError **error)
+{
+ const char *filename;
+
+ filename = gtk_file_path_get_string (path);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (g_path_is_absolute (filename), FALSE);