[gcab] Add extract support for MSZIP
- From: Marc-Andre Lureau <malureau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gcab] Add extract support for MSZIP
- Date: Thu, 17 Jan 2013 22:14:20 +0000 (UTC)
commit 6caabad5b3dda23607a5e7eaf2faed920f2c2cc8
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date: Mon Jan 14 01:22:30 2013 +0100
Add extract support for MSZIP
gcab.c | 31 +++++++++---
libgcab/cabinet.c | 68 +++++++++++++++++++++++++--
libgcab/cabinet.h | 6 ++-
libgcab/gcab-cabinet.c | 52 ++++++++++++++++++++-
libgcab/gcab-cabinet.h | 10 ++++-
libgcab/gcab-folder.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++--
libgcab/gcab-priv.h | 12 ++++-
7 files changed, 281 insertions(+), 20 deletions(-)
---
diff --git a/gcab.c b/gcab.c
index 821a509..2d82b4f 100644
--- a/gcab.c
+++ b/gcab.c
@@ -74,14 +74,18 @@ main (int argc, char *argv[])
int i;
gchar **args = NULL;
+ gchar *change = NULL;
int nopath = 0;
int compress = 0;
int list = 0;
int create = 0;
+ int extract = 0;
GOptionEntry entries[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, N_("Be verbose"), NULL },
+ { "change", 'C', 0, G_OPTION_ARG_FILENAME, &change, N_("Change to directory"), NULL },
{ "list", 't', 0, G_OPTION_ARG_NONE, &list, N_("List content"), NULL },
{ "create", 'c', 0, G_OPTION_ARG_NONE, &create, N_("Create archive"), NULL },
+ { "extract", 'x', 0, G_OPTION_ARG_NONE, &extract, N_("Extract all files"), NULL },
{ "zip", 'z', 0, G_OPTION_ARG_NONE, &compress, N_("Use zip compression"), NULL },
{ "nopath", 'n', 0, G_OPTION_ARG_NONE, &nopath, N_("Do not include path"), NULL },
{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("FILE INPUT_FILES...") },
@@ -109,7 +113,7 @@ individual files from the archive.\
gcab_error (_("option parsing failed: %s\n"), error->message);
g_option_context_free(context);
- if ((list + create) != 1)
+ if ((list + extract + create) != 1)
gcab_error (_("Please specify a single operation."));
if (!args || args[0] == NULL)
@@ -118,7 +122,7 @@ individual files from the archive.\
GCancellable *cancellable = g_cancellable_new ();
GCabCabinet *cabinet = gcab_cabinet_new ();
- if (list) {
+ if (list || extract) {
GFile *file = g_file_new_for_commandline_arg (args[0]);
GInputStream *in = G_INPUT_STREAM (g_file_read (file, cancellable, &error));
@@ -127,13 +131,24 @@ individual files from the archive.\
if (!gcab_cabinet_load (cabinet, in, cancellable, &error))
gcab_error (_("error reading %s: %s\n"), args[0], error->message);
- GPtrArray *folders = gcab_cabinet_get_folders (cabinet);
- for (i = 0; i < folders->len; i++) {
- GSList *l, *list = gcab_folder_get_files (g_ptr_array_index (folders, i));
- for (l = list; l != NULL; l = l->next)
- g_print ("%s\n", gcab_file_get_name (GCAB_FILE (l->data)));
- g_slist_free (list);
+ if (list) {
+ GPtrArray *folders = gcab_cabinet_get_folders (cabinet);
+ for (i = 0; i < folders->len; i++) {
+ GSList *l, *list = gcab_folder_get_files (g_ptr_array_index (folders, i));
+ for (l = list; l != NULL; l = l->next)
+ g_print ("%s\n", gcab_file_get_name (GCAB_FILE (l->data)));
+ g_slist_free (list);
+ }
+ } else if (extract) {
+ g_object_unref (file);
+ if (change == NULL)
+ change = g_get_current_dir ();
+ file = g_file_new_for_path (change);
+
+ if (!gcab_cabinet_extract (cabinet, file, file_callback, NULL, NULL, cancellable, &error))
+ gcab_error (_("error during extraction: %s"), error->message);
}
+
g_object_unref (in);
g_object_unref (file);
goto end;
diff --git a/libgcab/cabinet.c b/libgcab/cabinet.c
index 1bd17a8..b38c0b9 100644
--- a/libgcab/cabinet.c
+++ b/libgcab/cabinet.c
@@ -1,4 +1,3 @@
-#include <zlib.h>
#include "gcab-priv.h"
static voidpf
@@ -136,6 +135,8 @@ hexdump (guchar *p, gsize s)
g_debug ("%15s: %s", #field, p->field)
#define PN(p, field, size) \
g_debug ("%15s:", #field), hexdump (p->field, size)
+#define PND(p, field, size) \
+ g_debug ("%15s:", #field), hexdump (field, size)
#define W1(val) \
g_data_output_stream_put_byte (out, val, cancellable, error)
@@ -400,21 +401,29 @@ cdata_write (cdata_t *cd, GDataOutputStream *out, int type,
return TRUE;
}
+G_GNUC_INTERNAL void
+cdata_finish (cdata_t *cd)
+{
+
+}
+
G_GNUC_INTERNAL gboolean
cdata_read (cdata_t *cd, u1 res_data, GCabCompression compression,
GDataInputStream *in, GCancellable *cancellable, GError **error)
{
gboolean success = FALSE;
+ int zret = Z_OK;
+ gchar *data = compression == GCAB_COMPRESSION_NONE ? cd->out : cd->data;
R4 (cd->checksum);
R2 (cd->ncbytes);
R2 (cd->nubytes);
cd->reserved = g_malloc (res_data);
RN (cd->reserved, res_data);
- RN (cd->data, cd->ncbytes);
+ RN (data, cd->ncbytes);
- CHECKSUM datacsum = compute_checksum(cd->data, cd->ncbytes, 0);
+ CHECKSUM datacsum = compute_checksum(data, cd->ncbytes, 0);
g_return_val_if_fail (cd->checksum == compute_checksum ((guint8*)&cd->ncbytes, 4, datacsum), FALSE);
if (g_getenv ("GCAB_DEBUG")) {
@@ -424,11 +433,62 @@ cdata_read (cdata_t *cd, u1 res_data, GCabCompression compression,
P2 (cd, nubytes);
if (res_data)
PN (cd, reserved, res_data);
- PN (cd, data, 64);
+ PND (cd, data, 64);
+ }
+
+ if (compression == GCAB_COMPRESSION_MSZIP) {
+ z_stream *z = &cd->z;
+
+ if (cd->data[0] != 'C' || cd->data[1] != 'K')
+ goto end;
+
+ z->avail_in = cd->ncbytes - 2;
+ z->next_in = cd->data + 2;
+ z->avail_out = cd->nubytes;
+ z->next_out = cd->out;
+
+ if (!z->opaque) {
+ z->zalloc = zalloc;
+ z->zfree = zfree;
+ z->opaque = cd;
+
+ zret = inflateInit2 (z, -15);
+ if (zret != Z_OK)
+ goto end;
+ }
+
+ while (1) {
+ zret = inflate (z, Z_BLOCK);
+ if (zret != Z_OK)
+ break;
+ }
+
+ if (zret != Z_STREAM_END)
+ goto end;
+
+ g_warn_if_fail (z->avail_in == 0);
+ g_warn_if_fail (z->avail_out == 0);
+ if (z->avail_in != 0 || z->avail_out != 0)
+ goto end;
+
+ zret = inflateReset (z);
+ if (zret != Z_OK)
+ goto end;
+
+ zret = inflateSetDictionary (z, cd->out, cd->nubytes);
+ if (zret != Z_OK)
+ goto end;
}
success = TRUE;
end:
+ if (zret != Z_OK)
+ g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED,
+ "zlib failed: %s", zError(zret));
+ if (!*error && !success)
+ g_set_error (error, GCAB_ERROR, GCAB_ERROR_FAILED,
+ "Invalid cabint chunk");
+
return success;
}
diff --git a/libgcab/cabinet.h b/libgcab/cabinet.h
index 07d1418..c7ea983 100644
--- a/libgcab/cabinet.h
+++ b/libgcab/cabinet.h
@@ -12,6 +12,7 @@
#include <dirent.h>
#include <unistd.h>
#include <time.h>
+#include <zlib.h>
#include "gcab-folder.h"
/* based on the spec
@@ -93,7 +94,9 @@ struct cdata
u2 ncbytes;
u2 nubytes;
guint8 *reserved;
- guint8 data[DATABLOCKSIZE*2];
+ guint8 data[DATABLOCKSIZE];
+ guint8 out[DATABLOCKSIZE];
+ z_stream z;
};
gboolean cheader_write (cheader_t *ch,
@@ -135,5 +138,6 @@ gboolean cdata_read (cdata_t *cd,
GDataInputStream *in,
GCancellable *cancellable,
GError **error);
+void cdata_finish (cdata_t *cd);
#endif /* CABINET_H */
diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c
index 12fad3a..104b53d 100644
--- a/libgcab/gcab-cabinet.c
+++ b/libgcab/gcab-cabinet.c
@@ -11,6 +11,7 @@ struct _GCabCabinet
GPtrArray *folders;
GByteArray *reserved;
+ cheader_t cheader;
};
enum {
@@ -303,7 +304,9 @@ gcab_cabinet_load (GCabCabinet *self,
cheader_t cheader;
int i;
GDataInputStream *in = g_data_input_stream_new (stream);
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (in), FALSE);
g_data_input_stream_set_byte_order (in, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
+
GPtrArray *folders = self->folders;
if (!cheader_read (&cheader, in, cancellable, error))
@@ -319,7 +322,7 @@ gcab_cabinet_load (GCabCabinet *self,
if (!cfolder_read (&cfolder, cheader.res_folder, in, cancellable, error))
goto end;
- GCabFolder *folder = gcab_folder_new_with_cfolder (&cfolder);
+ GCabFolder *folder = gcab_folder_new_with_cfolder (&cfolder, stream);
if (cfolder.reserved)
g_object_set (folder, "reserved",
g_byte_array_new_take (cfolder.reserved, cheader.res_folder),
@@ -348,3 +351,50 @@ end:
g_object_unref (in);
return success;
}
+
+/**
+ * gcab_cabinet_extract:
+ * @cabinet: a #GCabCabinet
+ * @path: the path to extract files
+ * @file_callback: (allow-none) (scope call): an optionnal #GCabFile callback,
+ * return %FALSE to filter out or skip files.
+ * @progress_callback: (allow-none) (scope call): a progress callback
+ * @callback_data: callback data
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @error: (allow-none): #GError to set on error, or %NULL
+ *
+ * Extract files to given path.
+ *
+ * Returns: %TRUE on success.
+ **/
+gboolean
+gcab_cabinet_extract (GCabCabinet *self,
+ GFile *path,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = TRUE;
+ int i;
+
+ g_return_val_if_fail (GCAB_IS_CABINET (self), FALSE);
+ g_return_val_if_fail (G_IS_FILE (path), FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (!error || *error == NULL, FALSE);
+
+ for (i = 0; i < self->folders->len; ++i) {
+ GCabFolder *folder = g_ptr_array_index (self->folders, i);
+ if (!gcab_folder_extract (folder, path, self->cheader.res_data,
+ file_callback, progress_callback, callback_data,
+ cancellable, error)) {
+ success = FALSE;
+ break;
+ }
+ }
+
+end:
+ return success;
+}
diff --git a/libgcab/gcab-cabinet.h b/libgcab/gcab-cabinet.h
index e32f347..e6208d1 100644
--- a/libgcab/gcab-cabinet.h
+++ b/libgcab/gcab-cabinet.h
@@ -40,7 +40,8 @@ GQuark gcab_error_quark (void);
typedef enum GCabError
{
- GCAB_ERROR_FORMAT
+ GCAB_ERROR_FORMAT,
+ GCAB_ERROR_FAILED,
} GCabError;
typedef struct _GCabCabinetClass GCabCabinetClass;
@@ -66,6 +67,13 @@ gboolean gcab_cabinet_write (GCabCabinet *cabinet,
gpointer callback_data,
GCancellable *cancellable,
GError **error);
+gboolean gcab_cabinet_extract (GCabCabinet *cabinet,
+ GFile *path,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c
index 07f0df1..b642557 100644
--- a/libgcab/gcab-folder.c
+++ b/libgcab/gcab-folder.c
@@ -30,6 +30,8 @@ gcab_folder_finalize (GObject *object)
g_hash_table_unref (self->hash);
if (self->reserved)
g_byte_array_unref (self->reserved);
+ if (self->stream)
+ g_object_unref (self->stream);
G_OBJECT_CLASS (gcab_folder_parent_class)->finalize (object);
}
@@ -247,11 +249,15 @@ gcab_folder_new (GCabCompression compression)
}
G_GNUC_INTERNAL GCabFolder *
-gcab_folder_new_with_cfolder (const cfolder_t *folder)
+gcab_folder_new_with_cfolder (const cfolder_t *folder, GInputStream *stream)
{
- return g_object_new (GCAB_TYPE_FOLDER,
- "compression", folder->typecomp,
- NULL);
+ GCabFolder *self = g_object_new (GCAB_TYPE_FOLDER,
+ "compression", folder->typecomp,
+ NULL);
+ self->stream = g_object_ref (stream);
+ self->cfolder = *folder;
+
+ return self;
}
/**
@@ -267,3 +273,111 @@ gcab_folder_get_files (GCabFolder *self)
return g_slist_reverse (g_slist_copy (self->files));
}
+
+static gint
+sort_by_offset (GCabFile *a, GCabFile *b)
+{
+ g_return_val_if_fail (a != NULL, 0);
+ g_return_val_if_fail (b != NULL, 0);
+
+ return (gint64)a->cfile.uoffset - (gint64)b->cfile.uoffset;
+}
+
+G_GNUC_INTERNAL gboolean
+gcab_folder_extract (GCabFolder *self,
+ GFile *path,
+ u1 res_data,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GError *my_error = NULL;
+ gboolean success = FALSE;
+ GDataInputStream *data = NULL;
+ GOutputStream *out = NULL;
+ GSList *f, *files = NULL;
+ cdata_t cdata = { 0, };
+
+ data = g_data_input_stream_new (self->stream);
+ g_data_input_stream_set_byte_order (data, G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
+ g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data), FALSE);
+
+ if (!g_seekable_seek (G_SEEKABLE (data), self->cfolder.offsetdata, G_SEEK_SET, cancellable, error))
+ goto end;
+
+ files = g_slist_sort (g_slist_copy (self->files), (GCompareFunc)sort_by_offset);
+
+ u4 nubytes = 0;
+ for (f = files; f != NULL; f = f->next) {
+ GCabFile *file = f->data;
+
+ if (file_callback && !file_callback (file, callback_data))
+ continue;
+
+ int i = 0, len = strlen (file->name);
+ gchar *fname = g_strdup (file->name);
+ for (i = 0; i < len; i++)
+ if (fname[i] == '\\')
+ fname[i] = '/';
+
+ GFileOutputStream *out;
+ GFile *gfile = g_file_resolve_relative_path (path, fname);
+ GFile *parent = g_file_get_parent (gfile);
+ g_free (fname);
+
+ if (!g_file_make_directory_with_parents (parent, cancellable, &my_error)) {
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&my_error);
+ else {
+ g_object_unref (gfile);
+ g_object_unref (parent);
+ g_propagate_error (error, my_error);
+ goto end;
+ }
+ }
+ g_object_unref (parent);
+
+ out = g_file_replace (gfile, NULL, FALSE, 0, cancellable, error);
+ if (!out) {
+ g_object_unref (gfile);
+ goto end;
+ }
+
+ u4 usize = file->cfile.usize;
+ u4 uoffset = file->cfile.uoffset;
+ do {
+ if ((nubytes + cdata.nubytes) <= uoffset) {
+ nubytes += cdata.nubytes;
+ if (!cdata_read (&cdata, res_data, self->compression,
+ data, cancellable, error))
+ goto end;
+ continue;
+ } else {
+ gsize offset = file->cfile.uoffset > nubytes ?
+ file->cfile.uoffset - nubytes : 0;
+ const void *p = &cdata.out[offset];
+ gsize count = MIN (usize, cdata.nubytes - offset);
+ if (!g_output_stream_write_all (G_OUTPUT_STREAM (out), p, count,
+ NULL, cancellable, error))
+ goto end;
+ usize -= count;
+ uoffset += count;
+ }
+ } while (usize > 0);
+ }
+
+ success = TRUE;
+
+end:
+ if (files)
+ g_slist_free (files);
+ if (data)
+ g_object_unref (data);
+ if (out)
+ g_object_unref (out);
+ cdata_finish (&cdata);
+
+ return success;
+}
diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h
index af2784f..82ce81f 100644
--- a/libgcab/gcab-priv.h
+++ b/libgcab/gcab-priv.h
@@ -27,14 +27,24 @@ struct _GCabFolder
GHashTable *hash;
GCabCompression compression;
GByteArray *reserved;
+ cfolder_t cfolder;
+ GInputStream *stream;
};
-GCabFolder * gcab_folder_new_with_cfolder (const cfolder_t *folder);
+GCabFolder * gcab_folder_new_with_cfolder (const cfolder_t *folder, GInputStream *stream);
GCabFile * gcab_file_new_with_cfile (const cfile_t *file);
gboolean gcab_file_update_info (GCabFile *file, GFileInfo *info);
gboolean gcab_file_set_uoffset (GCabFile *file, u4 uoffset);
gsize gcab_folder_get_ndatablocks (GCabFolder *folder);
+gboolean gcab_folder_extract (GCabFolder *self,
+ GFile *path,
+ u1 res_data,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error);
#endif /* GCAB_PRIV_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]