[file-roller: 60/123] libarchive: implemented file extraction
- From: Paolo Bacchilega <paobac src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [file-roller: 60/123] libarchive: implemented file extraction
- Date: Mon, 6 Aug 2012 13:45:54 +0000 (UTC)
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]