[file-roller: 60/123] libarchive: implemented file extraction



commit d50a4ec6646af5cdaf20ff6554181a28fb03ad2b
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Tue Jul 24 09:20:28 2012 +0200

    libarchive: implemented file extraction

 src/fr-archive-libarchive.c |  251 ++++++++++++++++++++++++++++++++++++++++++-
 src/glib-utils.c            |   23 ++++
 src/glib-utils.h            |    3 +
 3 files changed, 276 insertions(+), 1 deletions(-)
---
diff --git a/src/fr-archive-libarchive.c b/src/fr-archive-libarchive.c
index 5e83004..0d4db84 100644
--- a/src/fr-archive-libarchive.c
+++ b/src/fr-archive-libarchive.c
@@ -94,6 +94,7 @@ fr_archive_libarchive_get_packages (FrArchive  *archive,
 
 
 #define BUFFER_SIZE_FOR_READING (10 * 1024)
+#define LOAD_DATA(x) ((LoadData *)(x))
 
 
 typedef struct {
@@ -303,8 +304,222 @@ fr_archive_libarchive_remove_files (FrArchive           *archive,
 /* -- extract -- */
 
 
+typedef struct {
+	LoadData    parent;
+	GList      *file_list;
+	GFile      *destination;
+	char       *base_dir;
+	gboolean    skip_older;
+	gboolean    overwrite;
+	gboolean    junk_paths;
+	GHashTable *files_to_extract;
+	int         n_files_to_extract;
+} ExtractData;
+
+
+static void
+extract_data_free (ExtractData *extract_data)
+{
+	g_free (extract_data->base_dir);
+	_g_object_unref (extract_data->destination);
+	_g_string_list_free (extract_data->file_list);
+	g_hash_table_unref (extract_data->files_to_extract);
+	load_data_free (LOAD_DATA (extract_data));
+}
+
+
+static gboolean
+extract_data_get_extraction_requested (ExtractData *extract_data,
+				       const char  *pathname)
+{
+	if (extract_data->file_list != NULL)
+		return g_hash_table_lookup (extract_data->files_to_extract, pathname) != NULL;
+	else
+		return TRUE;
+}
+
+
+static void
+extract_archive_thread (GSimpleAsyncResult *result,
+			GObject            *object,
+			GCancellable       *cancellable)
+{
+	ExtractData          *extract_data;
+	LoadData             *load_data;
+	GHashTable           *checked_folders;
+	struct archive       *a;
+	struct archive_entry *entry;
+	int                   r;
+
+	extract_data = g_simple_async_result_get_op_res_gpointer (result);
+	load_data = LOAD_DATA (extract_data);
+
+	checked_folders = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
+	fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract);
+
+	a = archive_read_new ();
+	archive_read_support_filter_all (a);
+	archive_read_support_format_all (a);
+	archive_read_open (a, load_data, load_data_open, load_data_read, load_data_close);
+	while ((r = archive_read_next_header (a, &entry)) == ARCHIVE_OK) {
+		const char    *pathname;
+		char          *fullpath;
+		GFile         *file;
+		GFile         *parent;
+		GOutputStream *ostream;
+		const void    *buffer;
+		size_t         buffer_size;
+		int64_t        offset;
+		GError        *local_error = NULL;
+
+		if (g_cancellable_is_cancelled (cancellable))
+			break;
+
+		pathname = archive_entry_pathname (entry);
+		if (! extract_data_get_extraction_requested (extract_data, pathname)) {
+			archive_read_data_skip (a);
+			continue;
+		}
+
+		fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
+		file = g_file_get_child (extract_data->destination, _g_path_get_base_name (fullpath, extract_data->base_dir, extract_data->junk_paths));
+
+		/* honor the skip_older and overwrite options */
+
+		if (extract_data->skip_older || ! extract_data->overwrite) {
+			GFileInfo *info;
+
+			info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, cancellable, &local_error);
+			if (info != NULL) {
+				gboolean skip = FALSE;
+
+				if (! extract_data->overwrite) {
+					skip = TRUE;
+				}
+				else if (extract_data->skip_older) {
+					GTimeVal modification_time;
+
+					g_file_info_get_modification_time (info, &modification_time);
+					if (archive_entry_mtime (entry) < modification_time.tv_sec)
+						skip = TRUE;
+				}
+
+				g_object_unref (info);
+
+				if (skip) {
+					g_object_unref (file);
+
+					archive_read_data_skip (a);
+					fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
+
+					if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
+						r = ARCHIVE_EOF;
+						break;
+					}
+
+					continue;
+				}
+			}
+			else {
+				if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+					load_data->error = local_error;
+					g_object_unref (info);
+					break;
+				}
+				g_error_free (local_error);
+			}
+		}
+
+		fr_archive_progress_inc_completed_files (load_data->archive, 1);
+
+		/* create the file parents */
+
+		parent = g_file_get_parent (file);
+		if ((parent != NULL)
+		    && (g_hash_table_lookup (checked_folders, parent) == NULL)
+		    && ! g_file_query_exists (parent, cancellable))
+		{
+			if (g_file_make_directory_with_parents (parent, cancellable, &load_data->error)) {
+				GFile *grandparent;
+
+				grandparent = g_object_ref (parent);
+				while (grandparent != NULL) {
+					if (g_hash_table_lookup (checked_folders, grandparent) == NULL)
+						g_hash_table_insert (checked_folders, grandparent, GINT_TO_POINTER (1));
+					grandparent = g_file_get_parent (grandparent);
+				}
+			}
+		}
+		g_object_unref (parent);
+
+		/* create the file */
+
+		if (load_data->error == NULL) {
+			switch (archive_entry_filetype (entry)) {
+			case AE_IFDIR:
+				if (! g_file_make_directory (file, cancellable, &local_error)) {
+					if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+						load_data->error = g_error_copy (local_error);
+					g_error_free (local_error);
+				}
+				archive_read_data_skip (a);
+				break;
+
+			case AE_IFREG:
+				ostream = (GOutputStream *) g_file_replace (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, cancellable, &load_data->error);
+				if (ostream == NULL)
+					break;
+
+				while ((r = archive_read_data_block (a, &buffer, &buffer_size, &offset)) == ARCHIVE_OK) {
+					if (g_output_stream_write (ostream, buffer, buffer_size, cancellable, &load_data->error) == -1)
+						break;
+					fr_archive_progress_inc_completed_bytes (load_data->archive, buffer_size);
+				}
+
+				if (r != ARCHIVE_EOF)
+					load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (a));
+
+				_g_object_unref (ostream);
+				break;
+
+			case AE_IFLNK:
+				if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error)) {
+					if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+						load_data->error = g_error_copy (local_error);
+					g_error_free (local_error);
+				}
+				archive_read_data_skip (a);
+				break;
+			}
+		}
+
+		g_object_unref (file);
+		g_free (fullpath);
+
+		if (load_data->error != NULL)
+			break;
+
+		if ((extract_data->file_list != NULL) && (--extract_data->n_files_to_extract == 0)) {
+			r = ARCHIVE_EOF;
+			break;
+		}
+	}
+
+	if ((load_data->error == NULL) && (r != ARCHIVE_EOF))
+		load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (a));
+	if (load_data->error == NULL)
+		g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
+	if (load_data->error != NULL)
+		g_simple_async_result_set_from_error (result, load_data->error);
+
+	g_hash_table_unref (checked_folders);
+	archive_read_free (a);
+	extract_data_free (extract_data);
+}
+
+
 static void
-fr_archive_libarchive_extract_files (FrArchive           *base,
+fr_archive_libarchive_extract_files (FrArchive           *archive,
 				     GList               *file_list,
 				     const char          *destination,
 				     const char          *base_dir,
@@ -316,6 +531,40 @@ fr_archive_libarchive_extract_files (FrArchive           *base,
 				     GAsyncReadyCallback  callback,
 				     gpointer             user_data)
 {
+	ExtractData *extract_data;
+	LoadData    *load_data;
+	GList       *scan;
+
+	extract_data = g_new0 (ExtractData, 1);
+
+	load_data = LOAD_DATA (extract_data);
+	load_data->archive = g_object_ref (archive);
+	load_data->cancellable = _g_object_ref (cancellable);
+	load_data->result = g_simple_async_result_new (G_OBJECT (archive),
+						       callback,
+						       user_data,
+						       fr_archive_load);
+	load_data->buffer_size = BUFFER_SIZE_FOR_READING;
+	load_data->buffer = g_new (char, load_data->buffer_size);
+
+	extract_data->file_list = _g_string_list_dup (file_list);
+	extract_data->destination = g_file_new_for_uri (destination);
+	extract_data->base_dir = g_strdup (base_dir);
+	extract_data->skip_older = skip_older;
+	extract_data->overwrite = overwrite;
+	extract_data->junk_paths = junk_paths;
+	extract_data->files_to_extract = g_hash_table_new (g_str_hash, g_str_equal);
+	extract_data->n_files_to_extract = 0;
+	for (scan = extract_data->file_list; scan; scan = scan->next) {
+		g_hash_table_insert (extract_data->files_to_extract, scan->data, GINT_TO_POINTER (1));
+		extract_data->n_files_to_extract++;
+	}
+
+	g_simple_async_result_set_op_res_gpointer (load_data->result, extract_data, NULL);
+	g_simple_async_result_run_in_thread (load_data->result,
+					     extract_archive_thread,
+					     G_PRIORITY_DEFAULT,
+					     cancellable);
 }
 
 
diff --git a/src/glib-utils.c b/src/glib-utils.c
index 59e8690..d32e2f0 100644
--- a/src/glib-utils.c
+++ b/src/glib-utils.c
@@ -1000,6 +1000,29 @@ _g_path_is_parent_of (const char *dirname,
 }
 
 
+const char *
+_g_path_get_base_name (const char *path,
+		       const char *base_dir,
+		       gboolean    junk_paths)
+{
+	int         base_dir_len;
+	const char *base_path;
+
+	if (junk_paths)
+		return _g_path_get_file_name (path);
+
+	base_dir_len = strlen (base_dir);
+	if (strlen (path) <= base_dir_len)
+		return NULL;
+
+	base_path = path + base_dir_len;
+	if (path[0] != '/')
+		base_path -= 1;
+
+	return base_path;
+}
+
+
 gboolean
 _g_filename_is_hidden (const gchar *name)
 {
diff --git a/src/glib-utils.h b/src/glib-utils.h
index a5a584d..54137c5 100644
--- a/src/glib-utils.h
+++ b/src/glib-utils.h
@@ -137,6 +137,9 @@ char *              _g_path_remove_ending_separator(const char          *path);
 char *              _g_path_remove_extension       (const char          *path);
 gboolean            _g_path_is_parent_of           (const char          *dirname,
 						    const char          *filename);
+const char *        _g_path_get_base_name          (const char          *path,
+						    const char          *base_dir,
+						    gboolean             junk_paths);
 gboolean            _g_filename_is_hidden          (const char          *name);
 const char *        _g_filename_get_extension      (const char          *filename);
 gboolean            _g_filename_has_extension      (const char          *filename,



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