summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRazvan Chitu <razvan.ch95@gmail.com>2016-07-17 21:21:04 +0300
committerRazvan Chitu <razvan.ch95@gmail.com>2016-07-26 00:52:22 +0300
commit42ecbaf57bcfc5ca341609f226c2f24b9adf9e61 (patch)
tree7c195ac7261713f1bae52906679e9759d4b97c0f
parente40847a45236f121b503bd30388eedd7681ff787 (diff)
downloadnautilus-42ecbaf57bcfc5ca341609f226c2f24b9adf9e61.tar.gz
file-operations: refactor deleting operation
Both the delete and copy operations in Nautilus deal with deleting files recursively. Each operation has its own implementation of the same functionality, mixed with specific logic, like error reporting through dialogs. In order to remove duplicated code, extract delete logic to a separate function which can be used by both operations. Implement a function for deleting files recursively which reports errors and success through an user provided callback.
-rw-r--r--src/nautilus-file-operations.c646
1 files changed, 303 insertions, 343 deletions
diff --git a/src/nautilus-file-operations.c b/src/nautilus-file-operations.c
index 496fa7c79..f06371cb5 100644
--- a/src/nautilus-file-operations.c
+++ b/src/nautilus-file-operations.c
@@ -1605,229 +1605,212 @@ report_delete_progress (CommonJob *job,
}
}
-static void delete_file (CommonJob *job, GFile *file,
- gboolean *skipped_file,
- SourceInfo *source_info,
- TransferInfo *transfer_info,
- gboolean toplevel);
+typedef gboolean (*DeleteCallback) (GFile *file,
+ GError *error,
+ gpointer error_func,
+ gpointer callback_data);
-static void
-delete_dir (CommonJob *job, GFile *dir,
- gboolean *skipped_file,
- SourceInfo *source_info,
- TransferInfo *transfer_info,
- gboolean toplevel)
+static gboolean
+delete_file_recursively (GFile *file,
+ GCancellable *cancellable,
+ DeleteCallback callback,
+ gpointer callback_data)
{
- GFileInfo *info;
- GError *error;
- GFile *file;
- GFileEnumerator *enumerator;
- char *primary, *secondary, *details;
- int response;
- gboolean skip_error;
- gboolean local_skipped_file;
+ gboolean retry;
+ gboolean success;
- local_skipped_file = FALSE;
-
- skip_error = should_skip_readdir_error (job, dir);
- retry:
- error = NULL;
- enumerator = g_file_enumerate_children (dir,
- G_FILE_ATTRIBUTE_STANDARD_NAME,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- job->cancellable,
- &error);
- if (enumerator) {
- error = NULL;
-
- while (!job_aborted (job) &&
- (info = g_file_enumerator_next_file (enumerator, job->cancellable, skip_error?NULL:&error)) != NULL) {
- file = g_file_get_child (dir,
- g_file_info_get_name (info));
- delete_file (job, file, &local_skipped_file, source_info, transfer_info, FALSE);
- g_object_unref (file);
- g_object_unref (info);
- }
- g_file_enumerator_close (enumerator, job->cancellable, NULL);
- g_object_unref (enumerator);
-
- if (error && IS_IO_ERROR (error, CANCELLED)) {
- g_error_free (error);
- } else if (error) {
- primary = f (_("Error while deleting."));
- details = NULL;
-
- if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
- secondary = f (_("Files in the folder “%B” cannot be deleted because you do "
- "not have permissions to see them."), dir);
- } else {
- secondary = f (_("There was an error getting information about the files in the folder “%B”."), dir);
- details = error->message;
- }
-
- response = run_warning (job,
- primary,
- secondary,
- details,
- FALSE,
- CANCEL, _("_Skip files"),
- NULL);
-
- g_error_free (error);
-
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) {
- /* Skip: Do Nothing */
- local_skipped_file = TRUE;
- } else {
- g_assert_not_reached ();
- }
- }
-
- } else if (IS_IO_ERROR (error, CANCELLED)) {
- g_error_free (error);
- } else {
- primary = f (_("Error while deleting."));
- details = NULL;
- if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
- secondary = f (_("The folder “%B” cannot be deleted because you do not have "
- "permissions to read it."), dir);
- } else {
- secondary = f (_("There was an error reading the folder “%B”."), dir);
- details = error->message;
- }
-
- response = run_warning (job,
- primary,
- secondary,
- details,
- FALSE,
- CANCEL, SKIP, RETRY,
- NULL);
+ do {
+ g_autoptr (GError) error = NULL;
+ g_autoptr (GFileEnumerator) enumerator = NULL;
- g_error_free (error);
+ retry = FALSE;
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) {
- /* Skip: Do Nothing */
- local_skipped_file = TRUE;
- } else if (response == 2) {
- goto retry;
- } else {
- g_assert_not_reached ();
- }
- }
+ success = g_file_delete (file, cancellable, &error);
+ if (success) {
+ if (callback) {
+ callback (file, NULL, NULL, callback_data);
+ }
+ break;
+ }
- if (!job_aborted (job) &&
- /* Don't delete dir if there was a skipped file */
- !local_skipped_file) {
- if (!g_file_delete (dir, job->cancellable, &error)) {
- if (job->skip_all_error) {
- goto skip;
- }
- primary = f (_("Error while deleting."));
- secondary = f (_("Could not remove the folder %B."), dir);
- details = error->message;
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY)) {
+ if (callback) {
+ retry = callback (file,
+ error,
+ g_file_delete,
+ callback_data);
+ }
+ continue;
+ }
- response = run_cancel_or_skip_warning (job,
- primary,
- secondary,
- details,
- source_info->num_files,
- source_info->num_files - transfer_info->num_files);
-
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) { /* skip all */
- job->skip_all_error = TRUE;
- local_skipped_file = TRUE;
- } else if (response == 2) { /* skip */
- local_skipped_file = TRUE;
- } else {
- g_assert_not_reached ();
- }
-
- skip:
- g_error_free (error);
- } else {
- nautilus_file_changes_queue_file_removed (dir);
- transfer_info->num_files ++;
- report_delete_progress (job, source_info, transfer_info);
- return;
- }
- }
+ g_clear_error (&error);
- if (local_skipped_file) {
- *skipped_file = TRUE;
- }
+ enumerator = g_file_enumerate_children (file,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable, &error);
+
+ if (enumerator) {
+ GFileInfo *info;
+
+ success = TRUE;
+
+ info = g_file_enumerator_next_file (enumerator,
+ cancellable,
+ &error);
+
+ while (info != NULL) {
+ g_autoptr (GFile) child;
+
+ child = g_file_get_child (file,
+ g_file_info_get_name (info));
+
+ success = success && delete_file_recursively (child,
+ cancellable,
+ callback,
+ callback_data);
+
+ g_object_unref (info);
+
+ info = g_file_enumerator_next_file (enumerator,
+ cancellable,
+ &error);
+ }
+
+ if (error != NULL) {
+ success = FALSE;
+ if (callback) {
+ retry = callback (file,
+ error,
+ g_file_enumerator_next_file,
+ callback_data);
+ }
+ }
+
+ /* Retry deleting the directory only if all its children
+ * were deleted successfully
+ */
+ retry = success;
+ } else {
+ if (callback) {
+ retry = callback (file,
+ error,
+ g_file_enumerate_children,
+ callback_data);
+ }
+ }
+ } while (retry);
+
+ return success;
}
-static void
-delete_file (CommonJob *job, GFile *file,
- gboolean *skipped_file,
- SourceInfo *source_info,
- TransferInfo *transfer_info,
- gboolean toplevel)
-{
- GError *error;
- char *primary, *secondary, *details;
- int response;
+typedef struct {
+ CommonJob *job;
+ SourceInfo *source_info;
+ TransferInfo *transfer_info;
+} DeleteData;
- if (should_skip_file (job, file)) {
- *skipped_file = TRUE;
- return;
- }
-
- error = NULL;
- if (g_file_delete (file, job->cancellable, &error)) {
- nautilus_file_changes_queue_file_removed (file);
- transfer_info->num_files ++;
- report_delete_progress (job, source_info, transfer_info);
- return;
- }
+static gboolean
+file_deleted_callback (GFile *file,
+ GError *error,
+ gpointer error_func,
+ gpointer callback_data)
+{
+ DeleteData *data = callback_data;
+ CommonJob *job;
+ SourceInfo *source_info;
+ TransferInfo *transfer_info;
+ char *primary;
+ char *secondary;
+ char *details = NULL;
+ int response;
+
+ job = data->job;
+ source_info = data->source_info;
+ transfer_info = data->transfer_info;
+
+ if (error == NULL) {
+ nautilus_file_changes_queue_file_removed (file);
+ data->transfer_info->num_files ++;
+ report_delete_progress (data->job, data->source_info, data->transfer_info);
+
+ return FALSE;
+ }
- if (IS_IO_ERROR (error, NOT_EMPTY)) {
- g_error_free (error);
- delete_dir (job, file,
- skipped_file,
- source_info, transfer_info,
- toplevel);
- return;
-
- } else if (IS_IO_ERROR (error, CANCELLED)) {
- g_error_free (error);
-
- } else {
- if (job->skip_all_error) {
- goto skip;
- }
- primary = f (_("Error while deleting."));
- secondary = f (_("There was an error deleting %B."), file);
- details = error->message;
-
- response = run_cancel_or_skip_warning (job,
- primary,
- secondary,
- details,
- source_info->num_files,
- source_info->num_files - transfer_info->num_files);
+ if (job_aborted (job) || job->skip_all_error || should_skip_file (job, file)) {
+ return FALSE;
+ }
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) { /* skip all */
- job->skip_all_error = TRUE;
- } else if (response == 2) { /* skip */
- /* do nothing */
- } else {
- g_assert_not_reached ();
- }
- skip:
- g_error_free (error);
- }
-
- *skipped_file = TRUE;
+ if (error_func == g_file_delete) {
+ primary = f (_("Error while deleting."));
+ secondary = f (_("There was an error deleting %B."), file);
+ details = error->message;
+
+ response = run_cancel_or_skip_warning (job,
+ primary,
+ secondary,
+ details,
+ source_info->num_files,
+ source_info->num_files - transfer_info->num_files);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ abort_job (job);
+ } else if (response == 1) {
+ /* skip all */
+ job->skip_all_error = TRUE;
+ }
+ } else if (error_func == g_file_enumerate_children) {
+ primary = f (_("Error while deleting."));
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("The folder “%B” cannot be deleted because you do not have "
+ "permissions to read it."), file);
+ } else {
+ secondary = f (_("There was an error reading the folder “%B”."), file);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ FALSE,
+ CANCEL, SKIP, RETRY,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ abort_job (job);
+ } else if (response == 2) {
+ /* Retry operation */
+ return TRUE;
+ }
+ } else if (error_func == g_file_enumerator_next_file) {
+ if (should_skip_readdir_error (job, file)) {
+ return FALSE;
+ }
+
+ primary = f (_("Error while deleting."));
+ if (IS_IO_ERROR (error, PERMISSION_DENIED)) {
+ secondary = f (_("Files in the folder “%B” cannot be deleted because you do "
+ "not have permissions to see them."), file);
+ } else {
+ secondary = f (_("There was an error getting information about the files in the folder “%B”."), file);
+ details = error->message;
+ }
+
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ FALSE,
+ CANCEL, _("_Skip files"),
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ abort_job (job);
+ }
+ }
+
+ return FALSE;
}
static void
@@ -1837,7 +1820,7 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
GFile *file;
SourceInfo source_info;
TransferInfo transfer_info;
- gboolean skipped_file;
+ DeleteData data;
if (job_aborted (job)) {
return;
@@ -1855,20 +1838,30 @@ delete_files (CommonJob *job, GList *files, int *files_skipped)
memset (&transfer_info, 0, sizeof (transfer_info));
report_delete_progress (job, &source_info, &transfer_info);
+
+ data.job = job;
+ data.source_info = &source_info;
+ data.transfer_info = &transfer_info;
for (l = files;
l != NULL && !job_aborted (job);
l = l->next) {
+ gboolean success;
+
file = l->data;
- skipped_file = FALSE;
- delete_file (job, file,
- &skipped_file,
- &source_info, &transfer_info,
- TRUE);
- if (skipped_file) {
- (*files_skipped)++;
- }
+ if (should_skip_file (job, file)) {
+ (*files_skipped)++;
+ continue;
+ }
+
+ success = delete_file_recursively (file, job->cancellable,
+ file_deleted_callback,
+ &data);
+
+ if (!success) {
+ (*files_skipped)++;
+ }
}
}
@@ -4139,131 +4132,87 @@ copy_move_directory (CopyMoveJob *copy_job,
return TRUE;
}
-static gboolean
-remove_target_recursively (CommonJob *job,
- GFile *src,
- GFile *toplevel_dest,
- GFile *file)
-{
- GFileEnumerator *enumerator;
- GError *error;
- GFile *child;
- gboolean stop;
- char *primary, *secondary, *details;
- int response;
- GFileInfo *info;
- stop = FALSE;
-
- error = NULL;
- enumerator = g_file_enumerate_children (file,
- G_FILE_ATTRIBUTE_STANDARD_NAME,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- job->cancellable,
- &error);
- if (enumerator) {
- error = NULL;
-
- while (!job_aborted (job) &&
- (info = g_file_enumerator_next_file (enumerator, job->cancellable, &error)) != NULL) {
- child = g_file_get_child (file,
- g_file_info_get_name (info));
- if (!remove_target_recursively (job, src, toplevel_dest, child)) {
- stop = TRUE;
- break;
- }
- g_object_unref (child);
- g_object_unref (info);
- }
- g_file_enumerator_close (enumerator, job->cancellable, NULL);
- g_object_unref (enumerator);
-
- } else if (IS_IO_ERROR (error, NOT_DIRECTORY)) {
- /* Not a dir, continue */
- g_error_free (error);
-
- } else if (IS_IO_ERROR (error, CANCELLED)) {
- g_error_free (error);
- } else {
- if (job->skip_all_error) {
- goto skip1;
- }
-
- primary = f (_("Error while copying “%B”."), src);
- secondary = f (_("Could not remove files from the already existing folder %F."), file);
- details = error->message;
-
- /* set show_all to TRUE here, as we don't know how many
- * files we'll end up processing yet.
- */
- response = run_warning (job,
- primary,
- secondary,
- details,
- TRUE,
- CANCEL, SKIP_ALL, SKIP,
- NULL);
-
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) { /* skip all */
- job->skip_all_error = TRUE;
- } else if (response == 2) { /* skip */
- /* do nothing */
- } else {
- g_assert_not_reached ();
- }
- skip1:
- g_error_free (error);
-
- stop = TRUE;
- }
+typedef struct {
+ CommonJob *job;
+ GFile *source;
+} DeleteExistingFileData;
- if (stop) {
- return FALSE;
- }
+static gboolean
+existing_file_removed_callback (GFile *file,
+ GError *error,
+ gpointer error_func,
+ gpointer callback_data)
+{
+ DeleteExistingFileData *data = callback_data;
+ CommonJob *job;
+ GFile *source;
+ char *primary;
+ char *secondary;
+ char *details = NULL;
+ int response;
+
+ job = data->job;
+ source = data->source;
+
+ if (error == NULL) {
+ nautilus_file_changes_queue_file_removed (file);
+
+ return FALSE;
+ }
- error = NULL;
-
- if (!g_file_delete (file, job->cancellable, &error)) {
- if (job->skip_all_error ||
- IS_IO_ERROR (error, CANCELLED)) {
- goto skip2;
- }
- primary = f (_("Error while copying “%B”."), src);
- secondary = f (_("Could not remove the already existing file %F."), file);
- details = error->message;
+ if (job_aborted (job) || job->skip_all_error) {
+ return FALSE;
+ }
- /* set show_all to TRUE here, as we don't know how many
- * files we'll end up processing yet.
- */
- response = run_warning (job,
- primary,
- secondary,
- details,
- TRUE,
- CANCEL, SKIP_ALL, SKIP,
- NULL);
-
- if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
- abort_job (job);
- } else if (response == 1) { /* skip all */
- job->skip_all_error = TRUE;
- } else if (response == 2) { /* skip */
- /* do nothing */
- } else {
- g_assert_not_reached ();
- }
+ if (error_func == g_file_delete) {
+ primary = f (_("Error while copying “%B”."), source);
+ secondary = f (_("Could not remove the already existing file %F."),
+ file);
+ details = error->message;
+
+ /* set show_all to TRUE here, as we don't know how many
+ * files we'll end up processing yet.
+ */
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ TRUE,
+ CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ abort_job (job);
+ } else if (response == 1) { /* skip all */
+ job->skip_all_error = TRUE;
+ }
+ } else if (error_func == g_file_enumerate_children) {
+ primary = f (_("Error while copying “%B”."), source);
+ secondary = f (_("Could not remove files from the already existing folder %F."),
+ file);
+ details = error->message;
+
+ /* set show_all to TRUE here, as we don't know how many
+ * files we'll end up processing yet.
+ */
+ response = run_warning (job,
+ primary,
+ secondary,
+ details,
+ TRUE,
+ CANCEL, SKIP_ALL, SKIP,
+ NULL);
+
+ if (response == 0 || response == GTK_RESPONSE_DELETE_EVENT) {
+ abort_job (job);
+ } else if (response == 1) {
+ /* skip all */
+ job->skip_all_error = TRUE;
+ }
+ }
- skip2:
- g_error_free (error);
-
- return FALSE;
- }
- nautilus_file_changes_queue_file_removed (file);
-
- return TRUE;
-
+ return FALSE;
}
typedef struct {
@@ -4743,12 +4692,23 @@ copy_move_file (CopyMoveJob *copy_job,
else if (overwrite &&
IS_IO_ERROR (error, IS_DIRECTORY)) {
+ gboolean existing_file_deleted;
+ DeleteExistingFileData data;
g_error_free (error);
-
- if (remove_target_recursively (job, src, dest, dest)) {
- goto retry;
- }
+
+ data.job = job;
+ data.source = src;
+
+ existing_file_deleted =
+ delete_file_recursively (dest,
+ job->cancellable,
+ existing_file_removed_callback,
+ &data);
+
+ if (existing_file_deleted) {
+ goto retry;
+ }
}
/* Needs to recurse */