summaryrefslogtreecommitdiff
path: root/src/gio-utils.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-11-09 22:53:33 -0300
committerPerberos <[email protected]>2011-11-09 22:53:33 -0300
commit70438138096a47b2505ac55634cd94947ce378b6 (patch)
treee45e49dda10a71616466500a4ab65d1c54b5f6c1 /src/gio-utils.c
downloadengrampa-70438138096a47b2505ac55634cd94947ce378b6.tar.bz2
engrampa-70438138096a47b2505ac55634cd94947ce378b6.tar.xz
initial
Diffstat (limited to 'src/gio-utils.c')
-rw-r--r--src/gio-utils.c1497
1 files changed, 1497 insertions, 0 deletions
diff --git a/src/gio-utils.c b/src/gio-utils.c
new file mode 100644
index 0000000..1123865
--- /dev/null
+++ b/src/gio-utils.c
@@ -0,0 +1,1497 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "gio-utils.h"
+
+
+#define N_FILES_PER_REQUEST 128
+
+
+/* -- filter -- */
+
+
+typedef enum {
+ FILTER_DEFAULT = 0,
+ FILTER_NODOTFILES = 1 << 1,
+ FILTER_IGNORECASE = 1 << 2,
+ FILTER_NOBACKUPFILES = 1 << 3
+} FilterOptions;
+
+
+typedef struct {
+ char *pattern;
+ FilterOptions options;
+ GRegex **regexps;
+} Filter;
+
+
+static Filter *
+filter_new (const char *pattern,
+ FilterOptions options)
+{
+ Filter *filter;
+ GRegexCompileFlags flags;
+
+ filter = g_new0 (Filter, 1);
+
+ if ((pattern != NULL) && (strcmp (pattern, "*") != 0))
+ filter->pattern = g_strdup (pattern);
+
+ filter->options = options;
+ if (filter->options & FILTER_IGNORECASE)
+ flags = G_REGEX_CASELESS;
+ else
+ flags = 0;
+ filter->regexps = search_util_get_regexps (pattern, flags);
+
+ return filter;
+}
+
+
+static void
+filter_destroy (Filter *filter)
+{
+ if (filter == NULL)
+ return;
+
+ g_free (filter->pattern);
+ if (filter->regexps != NULL)
+ free_regexps (filter->regexps);
+ g_free (filter);
+}
+
+
+static gboolean
+filter_matches (Filter *filter,
+ const char *name)
+{
+ const char *file_name;
+ char *utf8_name;
+ gboolean matched;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ file_name = file_name_from_path (name);
+
+ if ((filter->options & FILTER_NODOTFILES)
+ && ((file_name[0] == '.') || (strstr (file_name, "/.") != NULL)))
+ return FALSE;
+
+ if ((filter->options & FILTER_NOBACKUPFILES)
+ && (file_name[strlen (file_name) - 1] == '~'))
+ return FALSE;
+
+ if (filter->pattern == NULL)
+ return TRUE;
+
+ utf8_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
+ matched = match_regexps (filter->regexps, utf8_name, 0);
+ g_free (utf8_name);
+
+ return matched;
+}
+
+
+static gboolean
+filter_empty (Filter *filter)
+{
+ return ((filter->pattern == NULL) || (strcmp (filter->pattern, "*") == 0));
+}
+
+
+/* -- g_directory_foreach_child -- */
+
+
+typedef struct {
+ GFile *base_directory;
+ gboolean recursive;
+ gboolean follow_links;
+ StartDirCallback start_dir_func;
+ ForEachChildCallback for_each_file_func;
+ ForEachDoneCallback done_func;
+ gpointer user_data;
+
+ /* private */
+
+ GFile *current;
+ GHashTable *already_visited;
+ GList *to_visit;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ GError *error;
+ guint source_id;
+} ForEachChildData;
+
+
+static void
+for_each_child_data_free (ForEachChildData *fec)
+{
+ if (fec == NULL)
+ return;
+
+ if (fec->base_directory != NULL)
+ g_object_unref (fec->base_directory);
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ if (fec->already_visited)
+ g_hash_table_destroy (fec->already_visited);
+ if (fec->to_visit != NULL)
+ g_list_free (fec->to_visit);
+ g_free (fec);
+}
+
+
+static gboolean
+for_each_child_done_cb (gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ g_source_remove (fec->source_id);
+ if (fec->current != NULL) {
+ g_object_unref (fec->current);
+ fec->current = NULL;
+ }
+ if (fec->done_func)
+ fec->done_func (fec->error, fec->user_data);
+ for_each_child_data_free (fec);
+
+ return FALSE;
+}
+
+
+static void
+for_each_child_done (ForEachChildData *fec)
+{
+ fec->source_id = g_idle_add (for_each_child_done_cb, fec);
+}
+
+
+static void for_each_child_start_current (ForEachChildData *fec);
+
+
+static gboolean
+for_each_child_start_cb (gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ g_source_remove (fec->source_id);
+ for_each_child_start_current (fec);
+
+ return FALSE;
+}
+
+
+static void
+for_each_child_start (ForEachChildData *fec)
+{
+ fec->source_id = g_idle_add (for_each_child_start_cb, fec);
+}
+
+
+static void
+for_each_child_set_current_uri (ForEachChildData *fec,
+ const char *directory)
+{
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ fec->current = g_file_new_for_uri (directory);
+}
+
+
+static void
+for_each_child_set_current (ForEachChildData *fec,
+ GFile *directory)
+{
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ fec->current = g_file_dup (directory);
+}
+
+static void
+for_each_child_start_next_sub_directory (ForEachChildData *fec)
+{
+ char *sub_directory = NULL;
+
+ if (fec->to_visit != NULL) {
+ GList *tmp;
+
+ sub_directory = (char*) fec->to_visit->data;
+ tmp = fec->to_visit;
+ fec->to_visit = g_list_remove_link (fec->to_visit, tmp);
+ g_list_free (tmp);
+ }
+
+ if (sub_directory != NULL) {
+ for_each_child_set_current_uri (fec, sub_directory);
+ for_each_child_start (fec);
+ }
+ else
+ for_each_child_done (fec);
+}
+
+
+static void
+for_each_child_close_enumerator (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+ GError *error = NULL;
+
+ if (! g_file_enumerator_close_finish (fec->enumerator,
+ result,
+ &error))
+ {
+ if (fec->error == NULL)
+ fec->error = g_error_copy (error);
+ else
+ g_clear_error (&error);
+ }
+
+ if ((fec->error == NULL) && fec->recursive)
+ for_each_child_start_next_sub_directory (fec);
+ else
+ for_each_child_done (fec);
+}
+
+
+static void
+for_each_child_next_files_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+ GList *children, *scan;
+
+ children = g_file_enumerator_next_files_finish (fec->enumerator,
+ result,
+ &(fec->error));
+
+ if (children == NULL) {
+ g_file_enumerator_close_async (fec->enumerator,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_close_enumerator,
+ fec);
+ return;
+ }
+
+ for (scan = children; scan; scan = scan->next) {
+ GFileInfo *child_info = scan->data;
+ GFile *f;
+ char *uri;
+
+ f = g_file_get_child (fec->current, g_file_info_get_name (child_info));
+ uri = g_file_get_uri (f);
+
+ if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) {
+ /* avoid to visit a directory more than once */
+
+ if (g_hash_table_lookup (fec->already_visited, uri) == NULL) {
+ char *sub_directory;
+
+ sub_directory = g_strdup (uri);
+ g_hash_table_insert (fec->already_visited, sub_directory, GINT_TO_POINTER (1));
+ fec->to_visit = g_list_append (fec->to_visit, sub_directory);
+ }
+ }
+
+ fec->for_each_file_func (uri, child_info, fec->user_data);
+
+ g_free (uri);
+ g_object_unref (f);
+ }
+
+ g_file_enumerator_next_files_async (fec->enumerator,
+ N_FILES_PER_REQUEST,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_next_files_ready,
+ fec);
+}
+
+static void
+for_each_child_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ fec->enumerator = g_file_enumerate_children_finish (fec->current, result, &(fec->error));
+ if (fec->enumerator == NULL) {
+ for_each_child_done (fec);
+ return;
+ }
+
+ g_file_enumerator_next_files_async (fec->enumerator,
+ N_FILES_PER_REQUEST,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_next_files_ready,
+ fec);
+}
+
+
+static void
+for_each_child_start_current (ForEachChildData *fec)
+{
+ if (fec->start_dir_func != NULL) {
+ char *directory;
+ DirOp op;
+
+ directory = g_file_get_uri (fec->current);
+ op = fec->start_dir_func (directory, &(fec->error), fec->user_data);
+ g_free (directory);
+
+ switch (op) {
+ case DIR_OP_SKIP:
+ for_each_child_start_next_sub_directory (fec);
+ return;
+ case DIR_OP_STOP:
+ for_each_child_done (fec);
+ return;
+ case DIR_OP_CONTINUE:
+ break;
+ }
+ }
+
+ g_file_enumerate_children_async (fec->current,
+ "standard::name,standard::type",
+ fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_ready,
+ fec);
+}
+
+/**
+ * g_directory_foreach_child:
+ * @directory: The directory to visit.
+ * @recursive: Whether to traverse the @directory recursively.
+ * @follow_links: Whether to dereference the symbolic links.
+ * @cancellable: An optional @GCancellable object, used to cancel the process.
+ * @start_dir_func: the function called for each sub-directory, or %NULL if
+ * not needed.
+ * @for_each_file_func: the function called for each file. Can't be %NULL.
+ * @done_func: the function called at the end of the traversing process.
+ * Can't be %NULL.
+ * @user_data: data to pass to @done_func
+ *
+ * Traverse the @directory's filesystem structure calling the
+ * @for_each_file_func function for each file in the directory; the
+ * @start_dir_func function on each directory before it's going to be
+ * traversed, this includes @directory too; the @done_func function is
+ * called at the end of the process.
+ * Some traversing options are available: if @recursive is TRUE the
+ * directory is traversed recursively; if @follow_links is TRUE, symbolic
+ * links are dereferenced, otherwise they are returned as links.
+ * Each callback uses the same @user_data additional parameter.
+ */
+void
+g_directory_foreach_child (GFile *directory,
+ gboolean recursive,
+ gboolean follow_links,
+ GCancellable *cancellable,
+ StartDirCallback start_dir_func,
+ ForEachChildCallback for_each_file_func,
+ ForEachDoneCallback done_func,
+ gpointer user_data)
+{
+ ForEachChildData *fec;
+
+ g_return_if_fail (for_each_file_func != NULL);
+
+ fec = g_new0 (ForEachChildData, 1);
+
+ fec->base_directory = g_object_ref (directory);
+ fec->recursive = recursive;
+ fec->follow_links = follow_links;
+ fec->cancellable = cancellable;
+ fec->start_dir_func = start_dir_func;
+ fec->for_each_file_func = for_each_file_func;
+ fec->done_func = done_func;
+ fec->user_data = user_data;
+ fec->already_visited = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ for_each_child_set_current (fec, fec->base_directory);
+ for_each_child_start_current (fec);
+}
+
+
+/* -- get_file_list_data -- */
+
+
+typedef struct {
+ GList *files;
+ GList *dirs;
+ GFile *directory;
+ GFile *base_dir;
+ GCancellable *cancellable;
+ ListReadyCallback done_func;
+ gpointer done_data;
+ GList *to_visit;
+ GList *current_dir;
+ Filter *include_filter;
+ Filter *exclude_filter;
+ Filter *exclude_folders_filter;
+ guint visit_timeout;
+} GetFileListData;
+
+
+static void
+get_file_list_data_free (GetFileListData *gfl)
+{
+ if (gfl == NULL)
+ return;
+
+ filter_destroy (gfl->include_filter);
+ filter_destroy (gfl->exclude_filter);
+ filter_destroy (gfl->exclude_folders_filter);
+ path_list_free (gfl->files);
+ path_list_free (gfl->dirs);
+ path_list_free (gfl->to_visit);
+ if (gfl->directory != NULL)
+ g_object_unref (gfl->directory);
+ if (gfl->base_dir != NULL)
+ g_object_unref (gfl->base_dir);
+ g_free (gfl);
+}
+
+
+/* -- g_directory_list_async -- */
+
+
+static GList*
+get_relative_file_list (GList *rel_list,
+ GList *file_list,
+ GFile *base_dir)
+{
+ GList *scan;
+
+ if (base_dir == NULL)
+ return NULL;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *full_path = scan->data;
+ GFile *f;
+ char *relative_path;
+
+ f = g_file_new_for_commandline_arg (full_path);
+ relative_path = g_file_get_relative_path (base_dir, f);
+ if (relative_path != NULL)
+ rel_list = g_list_prepend (rel_list, relative_path);
+ g_object_unref (f);
+ }
+
+ return rel_list;
+}
+
+
+static GList*
+get_dir_list_from_file_list (GHashTable *h_dirs,
+ const char *base_dir,
+ GList *files,
+ gboolean is_dir_list)
+{
+ GList *scan;
+ GList *dir_list = NULL;
+ int base_dir_len;
+
+ if (base_dir == NULL)
+ base_dir = "";
+ base_dir_len = strlen (base_dir);
+
+ for (scan = files; scan; scan = scan->next) {
+ char *filename = scan->data;
+ char *dir_name;
+
+ if (strlen (filename) <= base_dir_len)
+ continue;
+
+ if (is_dir_list)
+ dir_name = g_strdup (filename + base_dir_len + 1);
+ else
+ dir_name = remove_level_from_path (filename + base_dir_len + 1);
+
+ while ((dir_name != NULL) && (dir_name[0] != '\0') && (strcmp (dir_name, "/") != 0)) {
+ char *tmp;
+ char *dir;
+
+ /* avoid to insert duplicated folders */
+
+ dir = g_strconcat (base_dir, "/", dir_name, NULL);
+ if (g_hash_table_lookup (h_dirs, dir) == NULL) {
+ g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
+ dir_list = g_list_prepend (dir_list, dir);
+ }
+ else
+ g_free (dir);
+
+ tmp = dir_name;
+ dir_name = remove_level_from_path (tmp);
+ g_free (tmp);
+ }
+
+ g_free (dir_name);
+ }
+
+ return dir_list;
+}
+
+
+static void
+get_file_list_done (GError *error,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+ GHashTable *h_dirs;
+ GList *scan;
+ char *uri;
+
+ gfl->files = g_list_reverse (gfl->files);
+ gfl->dirs = g_list_reverse (gfl->dirs);
+
+ if (! filter_empty (gfl->include_filter) || (gfl->exclude_filter->pattern != NULL)) {
+ path_list_free (gfl->dirs);
+ gfl->dirs = NULL;
+ }
+
+ h_dirs = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Always include the base directory, this way empty base
+ * directories are added to the archive as well. */
+
+ if (gfl->base_dir != NULL) {
+ char *dir;
+
+ dir = g_file_get_uri (gfl->base_dir);
+ gfl->dirs = g_list_prepend (gfl->dirs, dir);
+ g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
+ }
+
+ /* Add all the parent directories in gfl->files/gfl->dirs to the
+ * gfl->dirs list, the hash table is used to avoid duplicated
+ * entries. */
+
+ for (scan = gfl->dirs; scan; scan = scan->next)
+ g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1));
+
+ uri = g_file_get_uri (gfl->base_dir);
+ gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE));
+
+ if (filter_empty (gfl->include_filter))
+ gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE));
+
+ g_free (uri);
+ /**/
+
+ if (error == NULL) {
+ GList *rel_files, *rel_dirs;
+
+ if (gfl->base_dir != NULL) {
+ rel_files = get_relative_file_list (NULL, gfl->files, gfl->base_dir);
+ rel_dirs = get_relative_file_list (NULL, gfl->dirs, gfl->base_dir);
+ }
+ else {
+ rel_files = gfl->files;
+ rel_dirs = gfl->dirs;
+ gfl->files = NULL;
+ gfl->dirs = NULL;
+ }
+
+ /* rel_files/rel_dirs must be deallocated in done_func */
+ gfl->done_func (rel_files, rel_dirs, NULL, gfl->done_data);
+ }
+ else
+ gfl->done_func (NULL, NULL, error, gfl->done_data);
+
+ g_hash_table_destroy (h_dirs);
+ get_file_list_data_free (gfl);
+}
+
+
+static void
+get_file_list_for_each_file (const char *uri,
+ GFileInfo *info,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+
+ switch (g_file_info_get_file_type (info)) {
+ case G_FILE_TYPE_REGULAR:
+ if (filter_matches (gfl->include_filter, uri))
+ if ((gfl->exclude_filter->pattern == NULL) || ! filter_matches (gfl->exclude_filter, uri))
+ gfl->files = g_list_prepend (gfl->files, g_strdup (uri));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static DirOp
+get_file_list_start_dir (const char *uri,
+ GError **error,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+
+ if ((gfl->exclude_folders_filter->pattern == NULL) || ! filter_matches (gfl->exclude_folders_filter, uri)) {
+ gfl->dirs = g_list_prepend (gfl->dirs, g_strdup (uri));
+ return DIR_OP_CONTINUE;
+ }
+ else
+ return DIR_OP_SKIP;
+}
+
+
+void
+g_directory_list_async (const char *directory,
+ const char *base_dir,
+ gboolean recursive,
+ gboolean follow_links,
+ gboolean no_backup_files,
+ gboolean no_dot_files,
+ const char *include_files,
+ const char *exclude_files,
+ const char *exclude_folders,
+ gboolean ignorecase,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data)
+{
+ GetFileListData *gfl;
+ FilterOptions filter_options;
+
+ gfl = g_new0 (GetFileListData, 1);
+ gfl->directory = g_file_new_for_commandline_arg (directory);
+ gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
+ gfl->done_func = done_func;
+ gfl->done_data = done_data;
+
+ filter_options = FILTER_DEFAULT;
+ if (no_backup_files)
+ filter_options |= FILTER_NOBACKUPFILES;
+ if (no_dot_files)
+ filter_options |= FILTER_NODOTFILES;
+ if (ignorecase)
+ filter_options |= FILTER_IGNORECASE;
+ gfl->include_filter = filter_new (include_files, filter_options);
+ gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
+ gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
+
+ g_directory_foreach_child (gfl->directory,
+ recursive,
+ follow_links,
+ cancellable,
+ get_file_list_start_dir,
+ get_file_list_for_each_file,
+ get_file_list_done,
+ gfl);
+}
+
+
+/* -- g_list_items_async -- */
+
+
+static void get_items_for_current_dir (GetFileListData *gfl);
+
+
+static gboolean
+get_items_for_next_dir_idle_cb (gpointer data)
+{
+ GetFileListData *gfl = data;
+
+ g_source_remove (gfl->visit_timeout);
+ gfl->visit_timeout = 0;
+
+ gfl->current_dir = g_list_next (gfl->current_dir);
+ get_items_for_current_dir (gfl);
+
+ return FALSE;
+}
+
+
+static void
+get_items_for_current_dir_done (GList *files,
+ GList *dirs,
+ GError *error,
+ gpointer data)
+{
+ GetFileListData *gfl = data;
+
+ if (error != NULL) {
+ if (gfl->done_func)
+ gfl->done_func (NULL, NULL, error, gfl->done_data);
+ path_list_free (files);
+ path_list_free (dirs);
+ get_file_list_data_free (gfl);
+ return;
+ }
+
+ gfl->files = g_list_concat (gfl->files, files);
+ gfl->dirs = g_list_concat (gfl->dirs, dirs);
+
+ gfl->visit_timeout = g_idle_add (get_items_for_next_dir_idle_cb, gfl);
+}
+
+
+static void
+get_items_for_current_dir (GetFileListData *gfl)
+{
+ GFile *current_dir;
+ char *directory_name;
+ GFile *directory_file;
+ char *directory_uri;
+ char *base_dir_uri;
+
+ if (gfl->current_dir == NULL) {
+ if (gfl->done_func) {
+ /* gfl->files/gfl->dirs must be deallocated in gfl->done_func */
+ gfl->done_func (gfl->files, gfl->dirs, NULL, gfl->done_data);
+ gfl->files = NULL;
+ gfl->dirs = NULL;
+ }
+ get_file_list_data_free (gfl);
+ return;
+ }
+
+ current_dir = g_file_new_for_uri ((char*) gfl->current_dir->data);
+ directory_name = g_file_get_basename (current_dir);
+ directory_file = g_file_get_child (gfl->base_dir, directory_name);
+ directory_uri = g_file_get_uri (directory_file);
+ base_dir_uri = g_file_get_uri (gfl->base_dir);
+
+ g_directory_list_all_async (directory_uri,
+ base_dir_uri,
+ TRUE,
+ gfl->cancellable,
+ get_items_for_current_dir_done,
+ gfl);
+
+ g_free (base_dir_uri);
+ g_free (directory_uri);
+ g_object_unref (directory_file);
+ g_free (directory_name);
+ g_object_unref (current_dir);
+}
+
+
+void
+g_list_items_async (GList *items,
+ const char *base_dir,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data)
+{
+ GetFileListData *gfl;
+ int base_len;
+ GList *scan;
+
+ g_return_if_fail (base_dir != NULL);
+
+ gfl = g_new0 (GetFileListData, 1);
+ gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
+ gfl->cancellable = cancellable;
+ gfl->done_func = done_func;
+ gfl->done_data = done_data;
+
+ base_len = 0;
+ if (strcmp (base_dir, "/") != 0)
+ base_len = strlen (base_dir);
+
+ for (scan = items; scan; scan = scan->next) {
+ char *uri = scan->data;
+
+ /* FIXME: this is not async */
+ if (uri_is_dir (uri)) {
+ gfl->to_visit = g_list_prepend (gfl->to_visit, g_strdup (uri));
+ }
+ else {
+ char *rel_path = g_uri_unescape_string (uri + base_len + 1, NULL);
+ gfl->files = g_list_prepend (gfl->files, rel_path);
+ }
+ }
+
+ gfl->current_dir = gfl->to_visit;
+ get_items_for_current_dir (gfl);
+}
+
+
+/* -- g_copy_files_async -- */
+
+
+typedef struct {
+ GList *sources;
+ GList *destinations;
+ GFileCopyFlags flags;
+ int io_priority;
+ GCancellable *cancellable;
+ CopyProgressCallback progress_callback;
+ gpointer progress_callback_data;
+ CopyDoneCallback callback;
+ gpointer user_data;
+
+ GList *source;
+ GList *destination;
+ int n_file;
+ int tot_files;
+} CopyFilesData;
+
+
+static CopyFilesData*
+copy_files_data_new (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ CopyFilesData *cfd;
+
+ cfd = g_new0 (CopyFilesData, 1);
+ cfd->sources = gio_file_list_dup (sources);
+ cfd->destinations = gio_file_list_dup (destinations);
+ cfd->flags = flags;
+ cfd->io_priority = io_priority;
+ cfd->cancellable = cancellable;
+ cfd->progress_callback = progress_callback;
+ cfd->progress_callback_data = progress_callback_data;
+ cfd->callback = callback;
+ cfd->user_data = user_data;
+
+ cfd->source = cfd->sources;
+ cfd->destination = cfd->destinations;
+ cfd->n_file = 1;
+ cfd->tot_files = g_list_length (cfd->sources);
+
+ return cfd;
+}
+
+
+static void
+copy_files_data_free (CopyFilesData *cfd)
+{
+ if (cfd == NULL)
+ return;
+ gio_file_list_free (cfd->sources);
+ gio_file_list_free (cfd->destinations);
+ g_free (cfd);
+}
+
+
+static void g_copy_current_file (CopyFilesData *cfd);
+
+
+static void
+g_copy_next_file (CopyFilesData *cfd)
+{
+ cfd->source = g_list_next (cfd->source);
+ cfd->destination = g_list_next (cfd->destination);
+ cfd->n_file++;
+
+ g_copy_current_file (cfd);
+}
+
+
+static void
+g_copy_files_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CopyFilesData *cfd = user_data;
+ GFile *source = cfd->source->data;
+ GError *error = NULL;
+
+ if (! g_file_copy_finish (source, result, &error)) {
+ /* source and target are directories, ignore the error */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE))
+ g_clear_error (&error);
+ /* source is directory, create target directory */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) {
+ g_clear_error (&error);
+ g_file_make_directory ((GFile*) cfd->destination->data,
+ cfd->cancellable,
+ &error);
+ }
+ }
+
+ if (error) {
+ if (cfd->callback)
+ cfd->callback (error, cfd->user_data);
+ g_clear_error (&error);
+ copy_files_data_free (cfd);
+ return;
+ }
+
+ g_copy_next_file (cfd);
+}
+
+
+static void
+g_copy_files_progess_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ CopyFilesData *cfd = user_data;
+
+ if (cfd->progress_callback)
+ cfd->progress_callback (cfd->n_file,
+ cfd->tot_files,
+ (GFile*) cfd->source->data,
+ (GFile*) cfd->destination->data,
+ current_num_bytes,
+ total_num_bytes,
+ cfd->progress_callback_data);
+}
+
+
+static void
+g_copy_current_file (CopyFilesData *cfd)
+{
+ if ((cfd->source == NULL) || (cfd->destination == NULL)) {
+ if (cfd->callback)
+ cfd->callback (NULL, cfd->user_data);
+ copy_files_data_free (cfd);
+ return;
+ }
+
+ g_file_copy_async ((GFile*) cfd->source->data,
+ (GFile*) cfd->destination->data,
+ cfd->flags,
+ cfd->io_priority,
+ cfd->cancellable,
+ g_copy_files_progess_cb,
+ cfd,
+ g_copy_files_ready_cb,
+ cfd);
+}
+
+
+void
+g_copy_files_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ CopyFilesData *cfd;
+
+ cfd = copy_files_data_new (sources,
+ destinations,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+ g_copy_current_file (cfd);
+}
+
+
+void
+g_copy_file_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_files;
+ GList *destination_files;
+
+ source_files = g_list_append (NULL, (gpointer) source);
+ destination_files = g_list_append (NULL, (gpointer) destination);
+
+ g_copy_files_async (source_files,
+ destination_files,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ g_list_free (source_files);
+ g_list_free (destination_files);
+}
+
+
+void
+g_copy_uris_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_files, *destination_files;
+
+ source_files = gio_file_list_new_from_uri_list (sources);
+ destination_files = gio_file_list_new_from_uri_list (destinations);
+
+ g_copy_files_async (source_files,
+ destination_files,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ gio_file_list_free (source_files);
+ gio_file_list_free (destination_files);
+}
+
+
+void
+g_copy_uri_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_list;
+ GList *destination_list;
+
+ source_list = g_list_append (NULL, (gpointer)source);
+ destination_list = g_list_append (NULL, (gpointer)destination);
+
+ g_copy_uris_async (source_list,
+ destination_list,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ g_list_free (source_list);
+ g_list_free (destination_list);
+}
+
+
+/* -- g_directory_copy_async -- */
+
+
+typedef struct {
+ char *uri;
+ GFileInfo *info;
+} ChildData;
+
+
+static ChildData*
+child_data_new (const char *uri,
+ GFileInfo *info)
+{
+ ChildData *data;
+
+ data = g_new0 (ChildData, 1);
+ data->uri = g_strdup (uri);
+ data->info = g_file_info_dup (info);
+
+ return data;
+}
+
+
+static void
+child_data_free (ChildData *child)
+{
+ if (child == NULL)
+ return;
+ g_free (child->uri);
+ g_object_unref (child->info);
+ g_free (child);
+}
+
+
+typedef struct {
+ GFile *source;
+ GFile *destination;
+ GFileCopyFlags flags;
+ int io_priority;
+ GCancellable *cancellable;
+ CopyProgressCallback progress_callback;
+ gpointer progress_callback_data;
+ CopyDoneCallback callback;
+ gpointer user_data;
+ GError *error;
+
+ GList *to_copy;
+ GList *current;
+ GFile *current_source;
+ GFile *current_destination;
+ int n_file, tot_files;
+ guint source_id;
+} DirectoryCopyData;
+
+
+static void
+directory_copy_data_free (DirectoryCopyData *dcd)
+{
+ if (dcd == NULL)
+ return;
+
+ if (dcd->source != NULL)
+ g_object_unref (dcd->source);
+ if (dcd->destination != NULL)
+ g_object_unref (dcd->destination);
+ if (dcd->current_source != NULL) {
+ g_object_unref (dcd->current_source);
+ dcd->current_source = NULL;
+ }
+ if (dcd->current_destination != NULL) {
+ g_object_unref (dcd->current_destination);
+ dcd->current_destination = NULL;
+ }
+ g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL);
+ g_list_free (dcd->to_copy);
+ g_free (dcd);
+}
+
+
+static gboolean
+g_directory_copy_done (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ if (dcd->callback)
+ dcd->callback (dcd->error, dcd->user_data);
+ if (dcd->error != NULL)
+ g_clear_error (&(dcd->error));
+ directory_copy_data_free (dcd);
+
+ return FALSE;
+}
+
+
+static GFile *
+get_destination_for_uri (DirectoryCopyData *dcd,
+ const char *uri)
+{
+ GFile *f_uri;
+ GFile *destination_file;
+ char *relative_path;
+
+ f_uri = g_file_new_for_uri (uri);
+ relative_path = g_file_get_relative_path (dcd->source, f_uri);
+ if (relative_path != NULL)
+ destination_file = g_file_resolve_relative_path (dcd->destination, relative_path);
+ else
+ destination_file = g_file_dup (dcd->destination);
+
+ g_free (relative_path);
+ g_object_unref (f_uri);
+
+ return destination_file;
+}
+
+
+static void g_directory_copy_current_child (DirectoryCopyData *dcd);
+
+
+static gboolean
+g_directory_copy_next_child (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ dcd->current = g_list_next (dcd->current);
+ dcd->n_file++;
+ g_directory_copy_current_child (dcd);
+
+ return FALSE;
+}
+
+
+static void
+g_directory_copy_child_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (! g_file_copy_finish ((GFile*)source_object, result, &(dcd->error))) {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+}
+
+
+static void
+g_directory_copy_child_progess_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (dcd->progress_callback)
+ dcd->progress_callback (dcd->n_file,
+ dcd->tot_files,
+ dcd->current_source,
+ dcd->current_destination,
+ current_num_bytes,
+ total_num_bytes,
+ dcd->progress_callback_data);
+}
+
+
+static void
+g_directory_copy_current_child (DirectoryCopyData *dcd)
+{
+ ChildData *child;
+ gboolean async_op = FALSE;
+
+ if (dcd->current == NULL) {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ if (dcd->current_source != NULL) {
+ g_object_unref (dcd->current_source);
+ dcd->current_source = NULL;
+ }
+ if (dcd->current_destination != NULL) {
+ g_object_unref (dcd->current_destination);
+ dcd->current_destination = NULL;
+ }
+
+ child = dcd->current->data;
+ dcd->current_source = g_file_new_for_uri (child->uri);
+ dcd->current_destination = get_destination_for_uri (dcd, child->uri);
+ if (dcd->current_destination == NULL) {
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+ return;
+ }
+
+ switch (g_file_info_get_file_type (child->info)) {
+ case G_FILE_TYPE_DIRECTORY:
+ /* FIXME: how to make a directory asynchronously ? */
+
+ /* doesn't check the returned error for now, because when an
+ * error occurs the code is not returned (for example when
+ * a directory already exists the G_IO_ERROR_EXISTS code is
+ * *not* returned), so we cannot discriminate between warnings
+ * and fatal errors. (see bug #525155) */
+
+ g_file_make_directory (dcd->current_destination,
+ NULL,
+ NULL);
+
+ /*if (! g_file_make_directory (dcd->current_destination,
+ dcd->cancellable,
+ &(dcd->error)))
+ {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }*/
+ break;
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ /* FIXME: how to make a link asynchronously ? */
+
+ g_file_make_symbolic_link (dcd->current_destination,
+ g_file_info_get_symlink_target (child->info),
+ NULL,
+ NULL);
+
+ /*if (! g_file_make_symbolic_link (dcd->current_destination,
+ g_file_info_get_symlink_target (child->info),
+ dcd->cancellable,
+ &(dcd->error)))
+ {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }*/
+ break;
+ case G_FILE_TYPE_REGULAR:
+ g_file_copy_async (dcd->current_source,
+ dcd->current_destination,
+ dcd->flags,
+ dcd->io_priority,
+ dcd->cancellable,
+ g_directory_copy_child_progess_cb,
+ dcd,
+ g_directory_copy_child_done_cb,
+ dcd);
+ async_op = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (! async_op)
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+}
+
+
+static gboolean
+g_directory_copy_start_copying (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ dcd->to_copy = g_list_reverse (dcd->to_copy);
+ dcd->current = dcd->to_copy;
+ dcd->n_file = 1;
+ g_directory_copy_current_child (dcd);
+
+ return FALSE;
+}
+
+
+static void
+g_directory_copy_list_ready (GError *error,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (error != NULL) {
+ dcd->error = g_error_copy (error);
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ dcd->source_id = g_idle_add (g_directory_copy_start_copying, dcd);
+}
+
+
+static void
+g_directory_copy_for_each_file (const char *uri,
+ GFileInfo *info,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
+ dcd->tot_files++;
+}
+
+
+static DirOp
+g_directory_copy_start_dir (const char *uri,
+ GError **error,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+ GFileInfo *info;
+
+ info = g_file_info_new ();
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+ dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
+ g_object_unref (info);
+
+ dcd->tot_files++;
+
+ return DIR_OP_CONTINUE;
+}
+
+
+void
+g_directory_copy_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd;
+
+ /* Creating GFile objects here will save us lot of effort in path construction */
+ dcd = g_new0 (DirectoryCopyData, 1);
+ dcd->source = g_file_new_for_commandline_arg (source);
+ dcd->destination = g_file_new_for_commandline_arg (destination);
+ dcd->flags = flags;
+ dcd->io_priority = io_priority;
+ dcd->cancellable = cancellable;
+ dcd->progress_callback = progress_callback;
+ dcd->progress_callback_data = progress_callback_data;
+ dcd->callback = callback;
+ dcd->user_data = user_data;
+
+ g_directory_foreach_child (dcd->source,
+ TRUE,
+ TRUE,
+ dcd->cancellable,
+ g_directory_copy_start_dir,
+ g_directory_copy_for_each_file,
+ g_directory_copy_list_ready,
+ dcd);
+}
+
+
+gboolean
+g_load_file_in_buffer (GFile *file,
+ void *buffer,
+ gsize size,
+ GError **error)
+{
+ GFileInputStream *istream;
+ int n;
+
+ istream = g_file_read (file, NULL, error);
+ if (istream == NULL)
+ return FALSE;
+
+ n = g_input_stream_read (G_INPUT_STREAM (istream), buffer, size, NULL, error);
+ g_object_unref (istream);
+
+ return (n >= 0);
+}
+