[gnome-autoar/wip/razvan/extract-refactor2: 3/3] AutoarExtract: add signal for name conflicts



commit eb33f6cd80a70dc6fd9c93751b211fd6ceda2cad
Author: Razvan Chitu <razvan ch95 gmail com>
Date:   Sun May 29 13:13:01 2016 +0300

    AutoarExtract: add signal for name conflicts
    
    Extraction was previously done with overwrite as the default option for existing
    files. A better solution would be to detect conflicts and notify the user
    application, offering the possibility to change the destination of the extracted
    file.
    
    In order to fix this, add a signal for notifying conflicts. This signal gets
    emitted before actually extracting a file.

 gnome-autoar/autoar-extract.c |  122 +++++++++++++++++++++++++++++++++++++++--
 tests/test-extract.c          |   17 ++++++
 2 files changed, 134 insertions(+), 5 deletions(-)
---
diff --git a/gnome-autoar/autoar-extract.c b/gnome-autoar/autoar-extract.c
index 36ffadd..d8df6ed 100644
--- a/gnome-autoar/autoar-extract.c
+++ b/gnome-autoar/autoar-extract.c
@@ -173,6 +173,7 @@ enum
   SCANNED,
   CONFIRM_DESTINATION,
   PROGRESS,
+  CONFLICT,
   CANCELLED,
   COMPLETED,
   AR_ERROR,
@@ -838,6 +839,31 @@ autoar_extract_signal_progress (AutoarExtract *arextract)
   }
 }
 
+static GFile*
+autoar_extract_signal_conflict (AutoarExtract *arextract, GFile *file)
+{
+  GFile *new_file = NULL;
+
+  autoar_common_g_signal_emit (arextract, arextract->priv->in_thread,
+                               autoar_extract_signals[CONFLICT], 0,
+                               file,
+                               &new_file);
+
+  if (new_file) {
+    char *previous_path, *new_path;
+
+    previous_path = g_file_get_path (file);
+    new_path = g_file_get_path (new_file);
+
+    g_debug ("autoar_extract_signal_conflict: %s => %s", previous_path, new_path);
+
+    g_free (previous_path);
+    g_free (new_path);
+  }
+
+  return new_file;
+}
+
 static inline void
 autoar_extract_signal_cancelled (AutoarExtract *arextract)
 {
@@ -958,6 +984,54 @@ autoar_extract_do_pattern_check (const char *path,
 }
 
 static void
+autoar_extract_solve_conflict (AutoarExtract *arextract,
+                               mode_t extracted_filetype,
+                               GFile **dest)
+{
+  GFileType file_type;
+  GFile *new_dest;
+  gboolean is_conflict = FALSE;
+
+  file_type = g_file_query_file_type (*dest,
+                                      G_FILE_QUERY_INFO_NONE,
+                                      NULL);
+  /* If there is no file with the given name, there will be no conflict */
+  if (file_type == G_FILE_TYPE_UNKNOWN) {
+    return;
+  }
+
+  switch (extracted_filetype) {
+    case AE_IFDIR:
+      break;
+    case AE_IFREG:
+    case AE_IFLNK:
+#if defined HAVE_MKFIFO || defined HAVE_MKNOD
+    case AE_IFIFO:
+#endif
+#ifdef HAVE_MKNOD
+    case AE_IFSOCK:
+    case AE_IFBLK:
+    case AE_IFCHR:
+#endif
+      is_conflict = TRUE;
+      break;
+    default:
+      break;
+  }
+
+  if (!is_conflict) {
+    return;
+  }
+
+  new_dest = autoar_extract_signal_conflict (arextract, *dest);
+
+  if (new_dest) {
+    g_clear_object (dest);
+    *dest = new_dest;
+  }
+}
+
+static void
 autoar_extract_do_write_entry (AutoarExtract *arextract,
                                struct archive *a,
                                struct archive_entry *entry,
@@ -1098,6 +1172,7 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
 
   g_debug ("autoar_extract_do_write_entry: writing");
   r = 0;
+
   switch (filetype = archive_entry_filetype (entry)) {
     default:
     case AE_IFREG:
@@ -1108,16 +1183,18 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
         gint64 offset;
 
         g_debug ("autoar_extract_do_write_entry: case REG");
+
         ostream = (GOutputStream*)g_file_replace (dest,
                                                   NULL,
                                                   FALSE,
                                                   G_FILE_CREATE_NONE,
                                                   priv->cancellable,
                                                   &(priv->error));
-        if (arextract->priv->error != NULL) {
+        if (priv->error != NULL) {
           g_object_unref (info);
           return;
         }
+
         if (ostream != NULL) {
           /* Archive entry size may be zero if we use raw format. */
           if (archive_entry_size(entry) > 0 || priv->use_raw_format) {
@@ -1159,17 +1236,28 @@ autoar_extract_do_write_entry (AutoarExtract *arextract,
         GFileAndInfo fileandinfo;
 
         g_debug ("autoar_extract_do_write_entry: case DIR");
+
         g_file_make_directory_with_parents (dest, priv->cancellable, &(priv->error));
+
         if (priv->error != NULL) {
-          /* "File exists" is not a fatal error */
-          if (priv->error->code == G_IO_ERROR_EXISTS) {
-            g_error_free (priv->error);
-            priv->error = NULL;
+          /* "File exists" is not a fatal error, as long as the existing file
+           * is a directory
+           */
+          GFileType file_type;
+
+          file_type = g_file_query_file_type (dest,
+                                              G_FILE_QUERY_INFO_NONE,
+                                              NULL);
+
+          if (g_error_matches (priv->error, G_IO_ERROR, G_IO_ERROR_EXISTS) &&
+              file_type == G_FILE_TYPE_DIRECTORY) {
+            g_clear_error (&priv->error);
           } else {
             g_object_unref (info);
             return;
           }
         }
+
         fileandinfo.file = g_object_ref (dest);
         fileandinfo.info = g_object_ref (info);
         g_array_append_val (priv->extracted_dir_list, fileandinfo);
@@ -1440,6 +1528,26 @@ autoar_extract_class_init (AutoarExtractClass *klass)
                   G_TYPE_UINT);
 
 /**
+ * AutoarExtract::conflict:
+ * @arextract: the #AutoarExtract
+ * @file: the file that caused a conflict
+ *
+ * Returns: (transfer full): a new file to be used as target for extraction
+ *
+ * This signal is used to report and offer the possibility to solve name
+ * conflicts when extracting files.
+ **/
+  autoar_extract_signals[CONFLICT] =
+    g_signal_new ("conflict",
+                  type,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_generic,
+                  G_TYPE_OBJECT,
+                  1,
+                  G_TYPE_FILE);
+
+/**
  * AutoarExtract::cancelled:
  * @arextract: the #AutoarExtract
  *
@@ -2001,6 +2109,10 @@ autoar_extract_step_extract (AutoarExtract *arextract) {
         autoar_extract_do_sanitize_pathname (hardlink, priv->destination_dir);
     }
 
+    /* Attempt to solve any name conflict before doing any operations */
+    autoar_extract_solve_conflict (arextract, archive_entry_filetype (entry),
+                                   &extracted_filename);
+
     autoar_extract_do_write_entry (arextract, a, entry,
                                    extracted_filename, hardlink_filename);
 
diff --git a/tests/test-extract.c b/tests/test-extract.c
index 2603da7..b5f6192 100644
--- a/tests/test-extract.c
+++ b/tests/test-extract.c
@@ -53,6 +53,22 @@ my_handler_progress (AutoarExtract *arextract,
            ((double)(completed_files)) * 100 / autoar_extract_get_files (arextract));
 }
 
+static GFile*
+my_handler_conflict (AutoarExtract *arextract,
+                     GFile *file,
+                     gpointer data)
+{
+  char *path;
+
+  path = g_file_get_path (file);
+
+  g_print ("Conflict on: %s\n", path);
+
+  g_free (path);
+
+  return NULL;
+}
+
 static void
 my_handler_error (AutoarExtract *arextract,
                   GError *error,
@@ -121,6 +137,7 @@ main (int argc,
   g_signal_connect (arextract, "scanned", G_CALLBACK (my_handler_scanned), NULL);
   g_signal_connect (arextract, "confirm-dest", G_CALLBACK (my_handler_confirm_dest), NULL);
   g_signal_connect (arextract, "progress", G_CALLBACK (my_handler_progress), NULL);
+  g_signal_connect (arextract, "conflict", G_CALLBACK (my_handler_conflict), NULL);
   g_signal_connect (arextract, "error", G_CALLBACK (my_handler_error), NULL);
   g_signal_connect (arextract, "completed", G_CALLBACK (my_handler_completed), NULL);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]