diff options
author | Ting-Wei Lan <lantw44@gmail.com> | 2013-07-13 23:00:56 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2013-07-13 23:11:19 +0800 |
commit | cb972af21832ededc9534253a86bbf913d7c1368 (patch) | |
tree | 517286bc81492de8af6e713fc2dd045c2f9469ea | |
parent | bb257548a99b9027cef90e5acdefb3b7da9769fa (diff) | |
download | gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.gz gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.bz2 gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.lz gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.xz gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.tar.zst gsoc2013-epiphany-cb972af21832ededc9534253a86bbf913d7c1368.zip |
Add new function autoar_extract_start to do the extraction
This function is not tested and does not write files to disks currently.
-rw-r--r-- | autoarchive/autoar-extract.c | 230 | ||||
-rw-r--r-- | autoarchive/autoar-extract.h | 2 |
2 files changed, 230 insertions, 2 deletions
diff --git a/autoarchive/autoar-extract.c b/autoarchive/autoar-extract.c index ecaf20d8a..1dce79008 100644 --- a/autoarchive/autoar-extract.c +++ b/autoarchive/autoar-extract.c @@ -27,10 +27,10 @@ #include "autoar-extract.h" -#include <gio/gio.h> -#include <string.h> #include <archive.h> #include <archive_entry.h> +#include <gio/gio.h> +#include <string.h> G_DEFINE_TYPE (AutoarExtract, autoar_extract, G_TYPE_OBJECT) @@ -331,6 +331,49 @@ libarchive_read_read_cb (struct archive *ar_read, return read_size; } +static char* +_g_filename_basename_remove_extension (const char *filename) +{ + char *dot_location; + char *basename; + + if (filename == NULL) { + return NULL; + } + + /* filename must not be directory, so we do not get a bad basename. */ + basename = g_path_get_basename (filename); + + dot_location = strrchr (basename, '.'); + if (dot_location == NULL || dot_location == basename) { + return basename; + } + + if (dot_location - 4 > basename) { + if (strcmp (dot_location - 4, ".tar") == 0) { + dot_location -= 4; + } + } + + *dot_location = '\0'; + + return basename; +} + +static gboolean +archive_extract_do_pattern_check (const char *pathname) +{ + return TRUE; +} + +static void +archive_extract_do_write_entry (AutoarExtract *arextract, + struct archive *a, + struct archive_entry *entry, + GFile *dest) +{ +} + /* Additional marshaller generated by glib-genmarshal * Command: echo "VOID:DOUBLE,DOUBLE" | glib-genmarshal --header */ @@ -574,3 +617,186 @@ autoar_extract_new (const char *source, output, NULL); } + +void +autoar_extract_start (AutoarExtract* arextract) +{ + struct archive *a; + struct archive_entry *entry; + + char *pathname_prefix; + int pathname_prefix_len; + + gboolean has_top_level_dir; + char *top_level_dir_basename; + char *top_level_dir_basename_modified; + GFile *top_level_parent_dir; + GFile *top_level_dir; + + GFile *source; + + int i; + + g_return_if_fail (AUTOAR_IS_EXTRACT (arextract)); + g_return_if_fail (arextract->priv->source != NULL); + g_return_if_fail (arextract->priv->output != NULL); + + a = archive_read_new (); + archive_read_support_filter_all (a); + archive_read_support_format_all (a); + + /* Reset all counter variables */ + arextract->priv->size = 0; + arextract->priv->completed_size = 0; + arextract->priv->files = 0; + arextract->priv->completed_files = 0; + + /* Step 1: Scan all file names in the archive + * We have to check whether the archive contains a top-level directory + * before performing the extraction. We emit the "scanned" signal when + * the checking is completed. */ + archive_read_open (a, + arextract, + libarchive_read_open_cb, + libarchive_read_read_cb, + libarchive_read_close_cb); + pathname_prefix = NULL; + has_top_level_dir = TRUE; + while (archive_read_next_header (a, &entry) == ARCHIVE_OK) { + const char *pathname, *dir_sep_location; + size_t skip_len, prefix_len; + + pathname = archive_entry_pathname (entry); + + /* TODO: Add file name pattern check here */ + + if (pathname_prefix == NULL) { + skip_len = strspn (pathname, "./"); + dir_sep_location = strchr (pathname + skip_len, '/'); + if (dir_sep_location == NULL) { + prefix_len = strlen (pathname); + } else { + prefix_len = dir_sep_location - pathname; + } + pathname_prefix = g_strndup (pathname, prefix_len); + pathname_prefix_len = prefix_len; + } else { + if (!g_str_has_prefix (pathname, pathname_prefix)) { + has_top_level_dir = FALSE; + } + } + archive_read_data_skip (a); + arextract->priv->files++; + } + g_free (pathname_prefix); + archive_read_close (a); + if (arextract->priv->error != NULL) { + g_signal_emit_by_name (arextract, "error", arextract->priv->error); + archive_read_free (a); + return; + } + g_signal_emit_by_name (arextract, "scanned"); + + /* Step 2: Create necessary directories */ + top_level_dir_basename = _g_filename_basename_remove_extension (arextract->priv->source); + top_level_parent_dir = g_file_new_for_commandline_arg (arextract->priv->output); + top_level_dir = g_file_get_child (top_level_parent_dir, top_level_dir_basename); + + top_level_dir_basename_modified = NULL; + for (i=1; g_file_query_exists (top_level_dir, NULL); i++) { + g_free (top_level_dir_basename_modified); + g_object_unref (top_level_dir); + top_level_dir_basename_modified = g_strdup_printf ("%s (%d)", + top_level_dir_basename, + i); + top_level_dir = g_file_get_child (top_level_parent_dir, + top_level_dir_basename_modified); + } + + g_file_make_directory_with_parents (top_level_dir, NULL, &(arextract->priv->error)); + + g_free (top_level_dir_basename); + g_free (top_level_dir_basename_modified); + g_object_unref (top_level_parent_dir); + + if (arextract->priv->error != NULL) { + g_signal_emit_by_name (arextract, "error", arextract->priv->error); + g_object_unref (top_level_dir); + archive_read_free (a); + return; + } + + /* Step 3: Extract files + * We have to re-open the archive to extract files */ + archive_read_open (a, + arextract, + libarchive_read_open_cb, + libarchive_read_read_cb, + libarchive_read_close_cb); + while (archive_read_next_header (a, &entry) == ARCHIVE_OK) { + const char *pathname; + const char *pathname_skip_prefix; + char **pathname_chunks; + + GFile *extracted_filename; + GFileInfo *restored_fileinfo; + + pathname = archive_entry_pathname (entry); + if (has_top_level_dir) + pathname_skip_prefix = pathname + pathname_prefix_len; + else + pathname_skip_prefix = pathname + strspn (pathname, "./"); + + extracted_filename = g_file_get_child (top_level_dir, pathname_skip_prefix); + + /* Extracted file should not be located outside the top level directory. */ + if (!g_file_has_prefix (extracted_filename, top_level_dir)) { + pathname_chunks = g_strsplit (pathname_skip_prefix, "/", G_MAXINT); + for (i = 0; pathname_chunks[i] != NULL; i++) { + if (strcmp (pathname_chunks[i], "..") == 0) { + char *pathname_sanitized; + + *pathname_chunks[i] = '\0'; + pathname_sanitized = g_strjoinv ("/", pathname_chunks); + + g_object_unref (extracted_filename); + extracted_filename = g_file_get_child (top_level_dir, pathname_sanitized); + + g_free (pathname_sanitized); + + if (g_file_has_prefix (extracted_filename, top_level_dir)) + break; + } + } + g_strfreev (pathname_chunks); + } + + /* TODO: Add file name pattern check here */ + archive_extract_do_pattern_check (pathname_skip_prefix); + + /* TODO: Write entry to disk */ + archive_extract_do_write_entry (arextract, a, entry, extracted_filename); + + arextract->priv->completed_files++; + g_signal_emit_by_name (arextract, + "progress", + ((double)(arextract->priv->size)) / ((double)(arextract->priv->completed_size)), + ((double)(arextract->priv->files)) / ((double)(arextract->priv->completed_files))); + g_object_unref (extracted_filename); + } + + g_object_unref (top_level_dir); + archive_read_close (a); + archive_read_free (a); + if (arextract->priv->error != NULL) { + g_signal_emit_by_name (arextract, "error", arextract->priv->error); + return; + } + + /* If the extraction is completed successfully, remove the source file. + * Errors are not fatal because we have completed our work. */ + source = g_file_new_for_commandline_arg (arextract->priv->source); + g_file_delete (source, NULL, NULL); + g_object_unref (source); + g_signal_emit_by_name (arextract, "completed"); +} diff --git a/autoarchive/autoar-extract.h b/autoarchive/autoar-extract.h index 2d93228f2..66bdbd367 100644 --- a/autoarchive/autoar-extract.h +++ b/autoarchive/autoar-extract.h @@ -67,6 +67,8 @@ GType autoar_extract_get_type (void) G_GNUC_CONST; AutoarExtract *autoar_extract_new (const char *source, const char *output); +void autoar_extract_start (AutoarExtract* arextract); + char *autoar_extract_get_source (AutoarExtract *arextract); char *autoar_extract_get_output (AutoarExtract *arextract); guint64 autoar_extract_get_size (AutoarExtract *arextract); |