[gcab] Map Cabinet/Folder/File concepts to objects
- From: Marc-Andre Lureau <malureau src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gcab] Map Cabinet/Folder/File concepts to objects
- Date: Fri, 14 Dec 2012 13:05:07 +0000 (UTC)
commit 95e02c24a8d5f01f0eac7e4a70854227edd1ceef
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date: Fri Dec 14 13:53:05 2012 +0100
Map Cabinet/Folder/File concepts to objects
Makefile.am | 8 +-
gcab.c | 65 +++++++--
libgcab.h | 2 +
libgcab.syms | 17 ++-
libgcab/cabinet.c | 13 +-
libgcab/cabinet.h | 1 -
libgcab/gcab-cabinet.c | 238 +++++++++++++++++++++++++++++++
libgcab/gcab-cabinet.h | 58 ++++++++
libgcab/gcab-file.c | 371 +++++++++++++-----------------------------------
libgcab/gcab-file.h | 26 +---
libgcab/gcab-folder.c | 210 +++++++++++++++++++++++++++
libgcab/gcab-folder.h | 57 ++++++++
libgcab/gcab-priv.h | 34 +++++
13 files changed, 780 insertions(+), 320 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index d6c75c5..f5bffd2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,6 +21,10 @@ libgcab_priv_include_HEADERS = \
lib_LTLIBRARIES = libgcab-1.0.la
libgcab_1_0_la_SOURCES = \
libgcab/gcab-enums.c \
+ libgcab/gcab-cabinet.c \
+ libgcab/gcab-cabinet.h \
+ libgcab/gcab-folder.c \
+ libgcab/gcab-folder.h \
libgcab/gcab-file.c \
libgcab/gcab-file.h \
libgcab/cabinet.c \
@@ -51,7 +55,7 @@ GCAB_ENUMS = \
BUILT_SOURCES += $(GCAB_ENUMS)
ENUMS_FILES = \
- libgcab/gcab-file.h \
+ libgcab/gcab-folder.h \
$(NULL)
$(GCAB_ENUMS): %: %.etemplate $(ENUMS_FILES)
@@ -79,7 +83,9 @@ GCab_1_0_gir_INCLUDES = GObject-2.0 GLib-2.0 Gio-2.0
GCab_1_0_gir_LIBS = libgcab-1.0.la
GCab_1_0_gir_FILES = \
$(top_srcdir)/libgcab.h \
+ $(top_srcdir)/libgcab/gcab-folder.h \
$(top_srcdir)/libgcab/gcab-file.h \
+ $(top_srcdir)/libgcab/gcab-cabinet.h \
$(top_srcdir)/libgcab/gcab-enums.h \
$(NULL)
GCab_1_0_gir_EXPORT_PACKAGES = libgcab-1.0
diff --git a/gcab.c b/gcab.c
index be72da5..5ca9e66 100644
--- a/gcab.c
+++ b/gcab.c
@@ -21,9 +21,10 @@ gcab_error (const gchar *format, ...)
int verbose = 0;
static void
-file_callback (GFile *file, gpointer data)
+file_callback (GCabFile *cabfile, gpointer data)
{
- GFile *cwd = data;
+ GFile *file = gcab_file_get_file (cabfile);
+ GFile *cwd = G_FILE (data);
if (verbose) {
gchar *path = g_file_get_relative_path (cwd, file);
@@ -34,6 +35,28 @@ file_callback (GFile *file, gpointer data)
}
}
+static const gchar *
+remove_leading_path (gchar *name)
+{
+ int i;
+
+ i = 0;
+ if (name[0] == G_DIR_SEPARATOR)
+ i = 1;
+ while (name[i] == '.' && name[i + 1] == '.' && name[i + 2] == G_DIR_SEPARATOR)
+ i += 3;
+
+ if (i != 0) {
+ gchar c = name[i];
+
+ name[i] = '\0';
+ g_warning ("Removing leading '%s' from member names", name);
+ name[i] = c;
+ }
+
+ return name + i;
+}
+
int
main (int argc, char *argv[])
{
@@ -69,36 +92,50 @@ main (int argc, char *argv[])
if (args[1] == NULL)
gcab_error ("please specify input files.");
- GCabFile *gcab = gcab_file_new ();
+ GCabFolder *folder = gcab_folder_new ();
+ g_object_set (folder, "compression", compress ? GCAB_COMPRESSION_MSZIP : 0, NULL);
+
for (i = 1; args[i]; i++) {
GFile *file = g_file_new_for_commandline_arg (args[i]);
gchar *name = nopath ? g_path_get_basename (args[i]) : g_strdup (args[i]);
- if (!gcab_file_add (gcab, file, name, TRUE, NULL, &error)) {
+ GCabFile *cabfile = gcab_file_new_with_file (
+ remove_leading_path (name), file);
+
+ if (!gcab_folder_add_file (folder, cabfile, TRUE, NULL, &error)) {
g_warning ("Can't add file %s: %s", args[i], error->message);
g_clear_error (&error);
}
+
+ g_object_unref (cabfile);
g_free (name);
g_object_unref (file);
}
+ if (gcab_folder_get_nfiles (folder) == 0)
+ gcab_error ("No files to be archived.");
+
GFile *outputfile = g_file_new_for_commandline_arg (args[0]);
GOutputStream *output = G_OUTPUT_STREAM (g_file_create (outputfile, 0, NULL, &error));
if (error)
gcab_error ("Can't create cab file %s: %s", args[0], error->message);
GFile *cwd = g_file_new_for_commandline_arg (".");
- if (!gcab_file_save (gcab, output,
- compress ? GCAB_COMPRESSION_MSZIP : 0,
- file_callback,
- NULL,
- cwd,
- NULL,
- &error))
- gcab_error ("Can't save cab file %s: %s", args[0], error->message);
-
+ GCabCabinet *cabinet = gcab_cabinet_new ();
+ if (!gcab_cabinet_add_folder (cabinet, folder, &error))
+ gcab_error ("Can't add folder to cabinet: %s", args[0], error->message);
+
+ if (!gcab_cabinet_write (cabinet, output,
+ file_callback,
+ NULL,
+ cwd,
+ NULL,
+ &error))
+ gcab_error ("Can't write cab file %s: %s", args[0], error->message);
+
+ g_object_unref (cabinet);
+ g_object_unref (cwd);
g_object_unref (output);
g_object_unref (outputfile);
- g_object_unref (gcab);
return 0;
}
diff --git a/libgcab.h b/libgcab.h
index fe603bf..58b6147 100644
--- a/libgcab.h
+++ b/libgcab.h
@@ -20,6 +20,8 @@
#ifndef _LIBGCAB_H_
#define _LIBGCAB_H_
+#include <libgcab/gcab-cabinet.h>
#include <libgcab/gcab-file.h>
+#include <libgcab/gcab-folder.h>
#endif /* _LIBGCAB_H_ */
diff --git a/libgcab.syms b/libgcab.syms
index 19768be..48f90e4 100644
--- a/libgcab.syms
+++ b/libgcab.syms
@@ -1,8 +1,17 @@
LIBGCAB1_0.0 {
global:
- gcab_file_get_type;
- gcab_file_new;
- gcab_file_add;
- gcab_file_save;
+ gcab_cabinet_add_folder;
+ gcab_cabinet_get_folders;
+ gcab_cabinet_get_type;
+ gcab_cabinet_new;
+ gcab_cabinet_write;
gcab_compression_get_type;
+ gcab_file_get_file;
+ gcab_file_get_name;
+ gcab_file_get_type;
+ gcab_file_new_with_file;
+ gcab_folder_add_file;
+ gcab_folder_get_nfiles;
+ gcab_folder_get_type;
+ gcab_folder_new;
};
diff --git a/libgcab/cabinet.c b/libgcab/cabinet.c
index e62ab75..0aeb959 100644
--- a/libgcab/cabinet.c
+++ b/libgcab/cabinet.c
@@ -1,6 +1,5 @@
#include <zlib.h>
-#include "gcab-file.h"
-#include "cabinet.h"
+#include "gcab-priv.h"
static voidpf
zalloc (voidpf opaque, uInt items, uInt size)
@@ -47,7 +46,7 @@ cdata_set (cdata_t *cd, int type, guint8 *data, size_t size)
#define W(data, count) \
g_output_stream_write (out, data, count, NULL, error)
-gssize
+G_GNUC_INTERNAL gssize
cheader_write (cheader_t *ch, GOutputStream *out, GError **error)
{
ch->versionMIN = 3;
@@ -71,7 +70,7 @@ cheader_write (cheader_t *ch, GOutputStream *out, GError **error)
return 4 + 4 + 4 + 4 + 4 + 4 + 1 + 1 + 2 + 2 + 2 + 2 + 2;
}
-gssize
+G_GNUC_INTERNAL gssize
cfolder_write (cfolder_t *cf, GOutputStream *out, GError **error)
{
if ((W (&cf->offsetdata, 4) == -1) ||
@@ -83,7 +82,7 @@ cfolder_write (cfolder_t *cf, GOutputStream *out, GError **error)
}
-gssize
+G_GNUC_INTERNAL gssize
cfile_write (cfile_t *cf, GOutputStream *out, GError **error)
{
const gchar *name = cf->name;
@@ -102,7 +101,7 @@ cfile_write (cfile_t *cf, GOutputStream *out, GError **error)
typedef unsigned long int CHECKSUM;
-CHECKSUM
+static CHECKSUM
compute_checksum (guint8 *in, u2 ncbytes, CHECKSUM seed)
{
int no_ulongs;
@@ -134,7 +133,7 @@ compute_checksum (guint8 *in, u2 ncbytes, CHECKSUM seed)
return csum;
}
-gssize
+G_GNUC_INTERNAL gssize
cdata_write(cdata_t *cd, GOutputStream *out, int type, guint8 *data, size_t size, GError **error)
{
cdata_set(cd, type, data, size);
diff --git a/libgcab/cabinet.h b/libgcab/cabinet.h
index 1154791..1eabc0c 100644
--- a/libgcab/cabinet.h
+++ b/libgcab/cabinet.h
@@ -70,7 +70,6 @@ struct cfile
u2 time;
u2 fattr;
gchar *name;
- GFile *file;
};
struct cdata
diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c
new file mode 100644
index 0000000..d9d9404
--- /dev/null
+++ b/libgcab/gcab-cabinet.c
@@ -0,0 +1,238 @@
+#include "gcab-priv.h"
+
+struct _GCabCabinetClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GCabCabinet
+{
+ GObject parent_instance;
+
+ GPtrArray *folders;
+};
+
+enum {
+ PROP_0,
+};
+
+G_DEFINE_TYPE (GCabCabinet, gcab_cabinet, G_TYPE_OBJECT);
+
+static void
+gcab_cabinet_init (GCabCabinet *self)
+{
+ self->folders = g_ptr_array_new_with_free_func (g_object_unref);
+}
+
+static void
+gcab_cabinet_finalize (GObject *object)
+{
+ GCabCabinet *self = GCAB_CABINET (object);
+
+ g_ptr_array_unref (self->folders);
+
+ G_OBJECT_CLASS (gcab_cabinet_parent_class)->finalize (object);
+}
+
+static void
+gcab_cabinet_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ g_return_if_fail (GCAB_IS_CABINET (object));
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcab_cabinet_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ g_return_if_fail (GCAB_IS_CABINET (object));
+
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcab_cabinet_class_init (GCabCabinetClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gcab_cabinet_finalize;
+ object_class->set_property = gcab_cabinet_set_property;
+ object_class->get_property = gcab_cabinet_get_property;
+}
+
+/**
+ * gcab_cabinet_add_folder:
+ * @cabinet: a #GCabCabinet
+ * @folder:
+ * @error:
+ *
+ * Add @folder to @cabinet.
+ *
+ * Returns: %TRUE on success.
+ **/
+gboolean
+gcab_cabinet_add_folder (GCabCabinet *self,
+ GCabFolder *folder,
+ GError **error)
+{
+ g_return_val_if_fail (GCAB_IS_CABINET (self), FALSE);
+ g_return_val_if_fail (GCAB_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (!error || *error == NULL, FALSE);
+
+ g_ptr_array_add (self->folders, g_object_ref (folder));
+
+ return TRUE;
+}
+
+/**
+ * gcab_cabinet_get_folders:
+ * @cabinet:
+ *
+ *
+ *
+ * Returns:
+ **/
+GPtrArray *
+gcab_cabinet_get_folders (GCabCabinet *self)
+{
+ g_return_val_if_fail (GCAB_IS_FOLDER (self), NULL);
+
+ return self->folders;
+}
+
+/**
+ * gcab_cabinet_write:
+ * @cabinet: a #GCabCabinet
+ * @out: a #GOutputStream also #GSeekable
+ * @file_callback: (allow-none) (scope call): report current file being saved
+ * @progress_callback: (allow-none) (scope call): report saving progress
+ * @callback_data: (closure): user data to pass to callbacks
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @error: (allow-none): #GError to set on error, or %NULL
+ *
+ * Save @cabinet to the output stream @out. @out must be a #GSeekable.
+ *
+ * Returns: %TRUE on success.
+ **/
+gboolean
+gcab_cabinet_write (GCabCabinet *self,
+ GOutputStream *out,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ cheader_t header = {
+ .offsetfiles = CFI_START, // CFHEADER + 1 * CFFOLDER
+ .nfolders = 1, // a single CAB folder is enough
+ };
+ cfolder_t folder = { 0, };
+
+ g_return_val_if_fail (GCAB_IS_CABINET (self), FALSE);
+ g_return_val_if_fail (G_IS_OUTPUT_STREAM (out), FALSE);
+ g_return_val_if_fail (G_IS_SEEKABLE (out), FALSE);
+ g_return_val_if_fail (!error || *error == NULL, FALSE);
+ /* FIXME: current limitation of 1 folder */
+ g_return_val_if_fail (self->folders->len == 1, FALSE);
+
+ GHashTableIter iter;
+ GCabFolder *cabfolder = g_ptr_array_index (self->folders, 0);
+ GCabFile *file;
+ gsize nfiles = gcab_folder_get_nfiles (cabfolder);
+
+ size_t sumstr = 0;
+ g_hash_table_iter_init (&iter, cabfolder->files);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&file))
+ sumstr += strlen (file->name) + 1;
+
+ folder.typecomp = cabfolder->compression;
+ folder.offsetdata = CFI_START + nfiles * 16 + sumstr;
+ folder.ndatab = gcab_folder_get_ndatablocks (cabfolder);
+
+ if (!g_seekable_seek (G_SEEKABLE (out), folder.offsetdata,
+ G_SEEK_SET, NULL, error))
+ return FALSE;
+
+ gssize len, offset = 0;
+ cdata_t block = { 0, };
+ guint8 data[DATABLOCKSIZE];
+
+ g_hash_table_iter_init (&iter, cabfolder->files);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&file)) {
+ if (file_callback)
+ file_callback (file, callback_data);
+
+ GInputStream *in = G_INPUT_STREAM (g_file_read (file->file, cancellable, error));
+ if (*error) {
+ g_object_unref (in);
+ return FALSE;
+ }
+
+ while ((len = g_input_stream_read (in,
+ &data[offset], DATABLOCKSIZE - offset,
+ cancellable, error)) == (DATABLOCKSIZE - offset)) {
+ ssize_t written = cdata_write (&block, out, folder.typecomp, data, DATABLOCKSIZE, error);
+ header.size += written;
+ offset = 0;
+ }
+
+ if (len == -1) {
+ g_object_unref (in);
+ return FALSE;
+ }
+
+ offset += len;
+ g_object_unref (in);
+ }
+ if (offset != 0) {
+ ssize_t written = cdata_write (&block, out, folder.typecomp, data, offset, error);
+ if (written == -1)
+ return FALSE;
+ header.size += written;
+ }
+
+ if (!g_output_stream_flush (out, cancellable, error))
+ return FALSE;
+
+ if (!g_seekable_seek (G_SEEKABLE (out), 0,
+ G_SEEK_SET, cancellable, error))
+ return FALSE;
+
+ header.nfiles = nfiles;
+ header.size += CFI_START + nfiles * 16; /* 1st part cfile struct = 16 bytes */
+ header.size += sumstr;
+
+ if (cheader_write (&header, out, error) == -1)
+ return FALSE;
+
+ if (cfolder_write (&folder, out, error) == -1)
+ return FALSE;
+
+ /* for (i = 0; i < cfiles->len; ++i) */
+ /* if (cfile_write (&g_array_index (cfiles, cfile_t, i), out, error) == -1) */
+ /* return FALSE; */
+
+ return TRUE;
+
+}
+
+/**
+ * gcab_cabinet_new:
+ *
+ * Returns: a new #GCabCabinet
+ **/
+GCabCabinet *
+gcab_cabinet_new (void)
+{
+ return g_object_new (GCAB_TYPE_CABINET, NULL);
+}
diff --git a/libgcab/gcab-cabinet.h b/libgcab/gcab-cabinet.h
new file mode 100644
index 0000000..7b2f48c
--- /dev/null
+++ b/libgcab/gcab-cabinet.h
@@ -0,0 +1,58 @@
+/*
+ * LibGCab
+ * Copyright (c) 2012, Marc-Andrà Lureau <marcandre lureau gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef _GCAB_CABINET_H_
+#define _GCAB_CABINET_H_
+
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include "gcab-folder.h"
+#include "gcab-file.h"
+
+G_BEGIN_DECLS
+
+#define GCAB_TYPE_CABINET (gcab_cabinet_get_type ())
+#define GCAB_CABINET(cabinet) (G_TYPE_CHECK_INSTANCE_CAST ((cabinet), GCAB_TYPE_CABINET, GCabCabinet))
+#define GCAB_CABINET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCAB_TYPE_CABINET, GCabCabinetClass))
+#define GCAB_IS_CABINET(cabinet) (G_TYPE_CHECK_INSTANCE_TYPE ((cabinet), GCAB_TYPE_CABINET))
+#define GCAB_IS_CABINET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCAB_TYPE_CABINET))
+#define GCAB_CABINET_GET_CLASS(cabinet) (G_TYPE_INSTANCE_GET_CLASS ((cabinet), GCAB_TYPE_CABINET, GCabCabinetClass))
+
+typedef struct _GCabCabinetClass GCabCabinetClass;
+typedef struct _GCabCabinet GCabCabinet;
+
+GType gcab_cabinet_get_type (void) G_GNUC_CONST;
+
+GCabCabinet * gcab_cabinet_new (void);
+gboolean gcab_cabinet_add_folder (GCabCabinet *cabinet,
+ GCabFolder *folder,
+ GError **error);
+GPtrArray * gcab_cabinet_get_folders (GCabCabinet *cabinet);
+gboolean gcab_cabinet_write (GCabCabinet *cabinet,
+ GOutputStream *stream,
+ GCabFileCallback file_callback,
+ GFileProgressCallback progress_callback,
+ gpointer callback_data,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _GCAB_CABINET_H_ */
diff --git a/libgcab/gcab-file.c b/libgcab/gcab-file.c
index 2b94ed9..d2e2038 100644
--- a/libgcab/gcab-file.c
+++ b/libgcab/gcab-file.c
@@ -1,4 +1,4 @@
-#include "cabinet.h"
+#include "gcab-priv.h"
#include "gcab-file.h"
struct _GCabFileClass
@@ -6,39 +6,18 @@ struct _GCabFileClass
GObjectClass parent_class;
};
-struct _GCabFile
-{
- GObject parent_instance;
-
- GArray *cfiles;
-};
-
-
enum {
PROP_0,
+
+ PROP_NAME,
+ PROP_FILE,
};
G_DEFINE_TYPE (GCabFile, gcab_file, G_TYPE_OBJECT);
static void
-cfile_clear (gpointer data)
-{
- cfile_t *f = data;
-
- g_free (f->name);
- f->name = NULL;
-
- if (f->file) {
- g_object_unref (f->file);
- f->file = NULL;
- }
-}
-
-static void
gcab_file_init (GCabFile *self)
{
- self->cfiles = g_array_new (FALSE, TRUE, sizeof (cfile_t));
- g_array_set_clear_func (self->cfiles, cfile_clear);
}
static void
@@ -46,17 +25,44 @@ gcab_file_finalize (GObject *object)
{
GCabFile *self = GCAB_FILE (object);
- g_array_unref (self->cfiles);
+ g_object_unref (self->file);
+ g_free (self->name);
G_OBJECT_CLASS (gcab_file_parent_class)->finalize (object);
}
static void
+gcab_file_set_name (GCabFile *self, const gchar *name)
+{
+ gchar *fname = g_strdup (name);
+
+ /* assuming that on win32 we don't get unix paths */
+#ifndef G_OS_WIN32
+ if (fname) {
+ int i, len = strlen (fname);
+ for (i = 0; i < len; i++)
+ if (fname[i] == '/')
+ fname[i] = '\\';
+ }
+#endif
+
+ g_free (self->name);
+ self->name = fname;
+}
+
+static void
gcab_file_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
g_return_if_fail (GCAB_IS_FILE (object));
+ GCabFile *self = GCAB_FILE (object);
switch (prop_id) {
+ case PROP_NAME:
+ gcab_file_set_name (self, g_value_get_string (value));
+ break;
+ case PROP_FILE:
+ self->file = g_value_dup_object (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -67,8 +73,15 @@ static void
gcab_file_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
g_return_if_fail (GCAB_IS_FILE (object));
+ GCabFile *self = GCAB_FILE(object);
switch (prop_id) {
+ case PROP_NAME:
+ g_value_set_string (value, self->name);
+ break;
+ case PROP_FILE:
+ g_value_set_object (value, self->file);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -83,281 +96,97 @@ gcab_file_class_init (GCabFileClass *klass)
object_class->finalize = gcab_file_finalize;
object_class->set_property = gcab_file_set_property;
object_class->get_property = gcab_file_get_property;
+
+ g_object_class_install_property (object_class, PROP_NAME,
+ g_param_spec_string ("name", "name", "name", NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (object_class, PROP_FILE,
+ g_param_spec_object ("file", "file", "file", G_TYPE_FILE,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
-/* calculate the number of datablocks we will need:
- cabinet files are written in blocks of 32768 bytes */
-static size_t
-number_of_datablocks (GArray *cfiles)
+G_GNUC_INTERNAL gboolean
+gcab_file_update_info (GCabFile *self, GFileInfo *info)
{
- size_t total_size = 0;
- int i;
+ GTimeVal tv;
+ time_t time;
+ struct tm *m; // Use GDateTime when 2.26 in RHEL6
- for (i = 0; i < cfiles->len; i++)
- total_size += g_array_index (cfiles, cfile_t, i).usize;
+ g_return_val_if_fail (GCAB_IS_FILE (self), FALSE);
+ g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
- return total_size / DATABLOCKSIZE + 1 ;
-}
+ g_file_info_get_modification_time (info, &tv);
+ time = tv.tv_sec;
+ m = localtime (&time);
-#define FILE_ATTRS "standard::*,time::modified"
+ self->cfile.name = self->name;
+ self->cfile.usize = g_file_info_get_size (info);
+ self->cfile.fattr = CABINET_ARCH;
+ self->cfile.date = ((m->tm_year + 1900 - 1980 ) << 9 ) +
+ ((m->tm_mon+1) << 5 ) + (m->tm_mday);
+ self->cfile.time = (m->tm_hour << 11) + (m->tm_min << 5) + (m->tm_sec / 2);
-static gboolean
-add_file_info (GArray *cfiles, GFile *file, GFileInfo *info,
- const gchar *name, gboolean recurse, GError **error)
-{
- GFileType file_type = g_file_info_get_file_type (info);
-
- if (file_type == G_FILE_TYPE_DIRECTORY) {
- if (!recurse)
- return TRUE;
-
- GFileEnumerator *dir = g_file_enumerate_children (file, FILE_ATTRS, 0, NULL, error);
- if (*error) {
- g_warning ("Couldn't enumerate directory %s: %s", name, (*error)->message);
- g_clear_error (error);
- return TRUE;
- }
-
- while ((info = g_file_enumerator_next_file (dir, NULL, error)) != NULL) {
- GFile *child = g_file_get_child (file, g_file_info_get_name (info));
- gchar *child_name = g_build_filename (name, g_file_info_get_name (info), NULL);
- add_file_info (cfiles, child, info, child_name, recurse, error);
-
- if (*error) {
- g_warning ("Couldn't add file %s: %s",
- child_name, (*error)->message);
- g_clear_error (error);
- }
-
- g_free (child_name);
- g_object_unref (info);
- g_object_unref (child);
- }
-
- g_object_unref (dir);
-
- } else if (file_type == G_FILE_TYPE_REGULAR) {
- cfile_t f = { 0, };
- GTimeVal tv;
- time_t time;
- struct tm *m; // Use GDateTime when 2.26 in RHEL6
-
- g_file_info_get_modification_time (info, &tv);
- time = tv.tv_sec;
- m = localtime (&time);
-
- f.name = g_strdup (name);
-#ifndef G_OS_WIN32
- int i, len = strlen (f.name);
- for (i = 0; i < len; i++)
- if (f.name[i] == G_DIR_SEPARATOR)
- f.name[i] = '\\';
-#endif
- f.usize = g_file_info_get_size (info);
- f.fattr = CABINET_ARCH;
- f.date = ((m->tm_year + 1900 - 1980 ) << 9 ) +
- ((m->tm_mon+1) << 5 ) + (m->tm_mday);
- f.time = (m->tm_hour << 11) + (m->tm_min << 5) + (m->tm_sec / 2);
- f.file = g_object_ref (file);
- if (cfiles->len != 0) {
- cfile_t *prevf = &g_array_index (cfiles, cfile_t, cfiles->len - 1);
- f.uoffset = prevf->uoffset + prevf->usize;
- }
-
- g_array_append_val (cfiles, f);
-
- } else {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
- "Unhandled file type: %d", file_type);
- return FALSE;
- }
+ /* if (cfiles->len != 0) { */
+ /* cfile_t *prevf = &g_array_index (cfiles, cfile_t, cfiles->len - 1); */
+ /* self->cfile.uoffset = prevf->uoffset + prevf->usize; */
+ /* } */
return TRUE;
}
-static gchar *
-remove_leading_path (gchar *name)
+G_GNUC_INTERNAL gboolean
+gcab_file_set_uoffset (GCabFile *self, u4 uoffset)
{
- int i;
-
- i = 0;
- if (name[0] == G_DIR_SEPARATOR)
- i = 1;
- while (name[i] == '.' && name[i + 1] == '.' && name[i + 2] == G_DIR_SEPARATOR)
- i += 3;
-
- if (i != 0) {
- gchar c = name[i];
+ g_return_val_if_fail (GCAB_IS_FILE (self), FALSE);
- name[i] = '\0';
- g_warning ("Removing leading '%s' from member names", name);
- name[i] = c;
- }
+ self->cfile.uoffset = uoffset;
- return name + i;
+ return TRUE;
}
/**
- * gcab_file_add:
- * @cabfile: a #GCabfile
- * @file: file to be added
- * @name: name given in the archive file
- * @recurse: whether to recurse through subdirectories
- * @cancellable: (allow-none): optional #GCancellable object,
- * %NULL to ignore
- * @error: (allow-none): #GError to set on error, or %NULL
- *
- * Add @file to the #GCabFile.
+ * gcab_file_get_name:
+ * @file: a #GCabFile
*
- * Returns: %TRUE on succes
+ * Returns: the cabinet file name
**/
-gboolean
-gcab_file_add (GCabFile *self, GFile *file, const gchar *name,
- gboolean recurse, GCancellable *cancellable,
- GError **error)
+const gchar *
+gcab_file_get_name (GCabFile *self)
{
- gboolean success;
+ g_return_val_if_fail (GCAB_IS_FILE (self), NULL);
- g_return_val_if_fail (GCAB_IS_FILE (self), FALSE);
- g_return_val_if_fail (G_IS_FILE (file), FALSE);
- g_return_val_if_fail (!error || *error == NULL, FALSE);
-
- GFileInfo *info = g_file_query_info (file, FILE_ATTRS, 0, NULL, error);
- if (*error)
- return FALSE;
-
- gchar *tmpname = strdup (name);
-
- success = add_file_info (self->cfiles, file, info,
- remove_leading_path (tmpname), recurse, error);
-
- g_free (tmpname);
- g_object_unref (info);
-
- return success;
+ return self->name;
}
-
/**
- * gcab_file_save:
- * @cabfile: a #GCabFile
- * @out: a #GOutputStream #GSeekable
- * @compression: compression method
- * @file_callback: (allow-none) (scope call): report current file being saved
- * @progress_callback: (allow-none) (scope call): report saving progress
- * @callback_data: (closure): user data to pass to callbacks
- * @cancellable: (allow-none): optional #GCancellable object,
- * %NULL to ignore
- * @error: (allow-none): #GError to set on error, or %NULL
- *
- * Save @cabfile to the output stream @out. @out must be a #GSeekable.
+ * gcab_file_get_file:
+ * @file: a #GCabFile
*
- * Returns: %TRUE on success.
+ * Returns: the associated #GFile or %NULL
**/
-gboolean
-gcab_file_save (GCabFile *self,
- GOutputStream *out,
- GCabCompression compression,
- GCabFileSaveCallback file_callback,
- GFileProgressCallback progress_callback,
- gpointer callback_data,
- GCancellable *cancellable,
- GError **error)
+GFile *
+gcab_file_get_file (GCabFile *self)
{
- int i;
- cheader_t header = {
- .offsetfiles = CFI_START, // CFHEADER + 1 * CFFOLDER
- .nfolders = 1, // a single CAB folder is enough
- };
- cfolder_t folder = { 0, };
- g_return_val_if_fail (GCAB_IS_FILE (self), FALSE);
- g_return_val_if_fail (G_IS_OUTPUT_STREAM (out), FALSE);
- g_return_val_if_fail (G_IS_SEEKABLE (out), FALSE);
- g_return_val_if_fail (!error || *error == NULL, FALSE);
-
- GArray *cfiles = self->cfiles;
- if (cfiles->len == 0) {
- g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "No file");
- return FALSE;
- }
-
- size_t sumstr = 0;
- for (i = 0; i < cfiles->len; ++i) {
- const gchar *name = g_array_index (cfiles, cfile_t, i).name;
- sumstr += strlen (name) + 1;
- }
-
- folder.typecomp = compression;
- folder.offsetdata = CFI_START + cfiles->len * 16 + sumstr;
- folder.ndatab = number_of_datablocks (cfiles);
-
- if (!g_seekable_seek (G_SEEKABLE (out), folder.offsetdata,
- G_SEEK_SET, NULL, error))
- return FALSE;
-
- gssize len, offset = 0;
- cdata_t block = { 0, };
- guint8 data[DATABLOCKSIZE];
- for (i = 0; i < cfiles->len; ++i) {
- cfile_t *file = &g_array_index (cfiles, cfile_t, i);
- if (file_callback)
- file_callback (file->file, callback_data);
-
- GInputStream *in = G_INPUT_STREAM (g_file_read (file->file, cancellable, error));
- if (*error) {
- g_object_unref (in);
- return FALSE;
- }
-
- while ((len = g_input_stream_read (in,
- &data[offset], DATABLOCKSIZE - offset,
- cancellable, error)) == (DATABLOCKSIZE - offset)) {
- ssize_t written = cdata_write (&block, out, compression, data, DATABLOCKSIZE, error);
- header.size += written;
- offset = 0;
- }
-
- if (len == -1) {
- g_object_unref (in);
- return FALSE;
- }
-
- offset += len;
- g_object_unref (in);
- }
- if (offset != 0) {
- ssize_t written = cdata_write (&block, out, compression, data, offset, error);
- if (written == -1)
- return FALSE;
- header.size += written;
- }
-
- if (!g_output_stream_flush (out, cancellable, error))
- return FALSE;
-
- if (!g_seekable_seek (G_SEEKABLE (out), 0,
- G_SEEK_SET, cancellable, error))
- return FALSE;
+ g_return_val_if_fail (GCAB_IS_FILE (self), NULL);
- header.nfiles = cfiles->len;
- header.size += CFI_START + cfiles->len * 16; /* 1st part cfile struct = 16 bytes */
- header.size += sumstr;
-
- if (cheader_write (&header, out, error) == -1)
- return FALSE;
-
- if (cfolder_write (&folder, out, error) == -1)
- return FALSE;
-
- for (i = 0; i < cfiles->len; ++i)
- if (cfile_write (&g_array_index (cfiles, cfile_t, i), out, error) == -1)
- return FALSE;
-
- return TRUE;
+ return self->file;
}
+/**
+ * gcab_file_new_with_file:
+ * @name: name of the file within the cabinet
+ * @file: a #GFile to be added to the cabinet
+ *
+ * Returns: a new #GCabFile
+ **/
GCabFile *
-gcab_file_new (void)
+gcab_file_new_with_file (const gchar *name, GFile *file)
{
- return g_object_new (GCAB_TYPE_FILE, NULL);
+ return g_object_new (GCAB_TYPE_FILE,
+ "name", name,
+ "file", file,
+ NULL);
}
diff --git a/libgcab/gcab-file.h b/libgcab/gcab-file.h
index 9f69091..e477824 100644
--- a/libgcab/gcab-file.h
+++ b/libgcab/gcab-file.h
@@ -35,31 +35,13 @@ G_BEGIN_DECLS
typedef struct _GCabFileClass GCabFileClass;
typedef struct _GCabFile GCabFile;
-typedef enum
-{
- GCAB_COMPRESSION_NONE = 0,
- GCAB_COMPRESSION_MSZIP = 1,
-} GCabCompression;
-
-typedef void (*GCabFileSaveCallback) (GFile *current, gpointer data);
+typedef void (*GCabFileCallback) (GCabFile *current, gpointer data);
GType gcab_file_get_type (void) G_GNUC_CONST;
-GCabFile * gcab_file_new (void);
-gboolean gcab_file_add (GCabFile *cabfile,
- GFile *file,
- const gchar *name,
- gboolean recurse,
- GCancellable *cancellable,
- GError **error);
-gboolean gcab_file_save (GCabFile *cabfile,
- GOutputStream *stream,
- GCabCompression compression,
- GCabFileSaveCallback file_callback,
- GFileProgressCallback progress_callback,
- gpointer callback_data,
- GCancellable *cancellable,
- GError **error);
+GCabFile * gcab_file_new_with_file (const gchar *name, GFile *file);
+GFile * gcab_file_get_file (GCabFile *file);
+const gchar * gcab_file_get_name (GCabFile *file);
G_END_DECLS
diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c
new file mode 100644
index 0000000..c7de05c
--- /dev/null
+++ b/libgcab/gcab-folder.c
@@ -0,0 +1,210 @@
+#include "gcab-priv.h"
+
+struct _GCabFolderClass
+{
+ GObjectClass parent_class;
+};
+
+enum {
+ PROP_0,
+
+ PROP_COMPRESSION,
+};
+
+G_DEFINE_TYPE (GCabFolder, gcab_folder, G_TYPE_OBJECT);
+
+static void
+gcab_folder_init (GCabFolder *self)
+{
+ self->files = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+}
+
+static void
+gcab_folder_finalize (GObject *object)
+{
+ GCabFolder *self = GCAB_FOLDER (object);
+
+ g_hash_table_unref (self->files);
+
+ G_OBJECT_CLASS (gcab_folder_parent_class)->finalize (object);
+}
+
+static void
+gcab_folder_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ g_return_if_fail (GCAB_IS_FOLDER (object));
+ GCabFolder *self = GCAB_FOLDER (object);
+
+ switch (prop_id) {
+ case PROP_COMPRESSION:
+ self->compression = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcab_folder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ g_return_if_fail (GCAB_IS_FOLDER (object));
+ GCabFolder *self = GCAB_FOLDER (object);
+
+ switch (prop_id) {
+ case PROP_COMPRESSION:
+ g_value_set_enum (value, self->compression);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gcab_folder_class_init (GCabFolderClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gcab_folder_finalize;
+ object_class->set_property = gcab_folder_set_property;
+ object_class->get_property = gcab_folder_get_property;
+
+ g_object_class_install_property (object_class, PROP_COMPRESSION,
+ g_param_spec_enum ("compression", "compression", "compression",
+ GCAB_TYPE_COMPRESSION, 0,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/* calculate the number of datablocks we will need:
+ cabinet files are written in blocks of 32768 bytes */
+G_GNUC_INTERNAL gsize
+gcab_folder_get_ndatablocks (GCabFolder *self)
+{
+ gsize total_size = 0;
+ GHashTableIter iter;
+ GCabFile *file;
+
+ g_hash_table_iter_init (&iter, self->files);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&file))
+ total_size += file->cfile.usize;
+
+ return total_size / DATABLOCKSIZE + 1 ;
+}
+
+#define FILE_ATTRS "standard::*,time::modified"
+
+static gboolean
+add_file_info (GCabFolder *self, GCabFile *file, GFileInfo *info,
+ const gchar *name, gboolean recurse, GError **error)
+{
+ GFileType file_type = g_file_info_get_file_type (info);
+
+ if (file_type == G_FILE_TYPE_DIRECTORY) {
+ if (!recurse)
+ return TRUE;
+
+ GFileEnumerator *dir = g_file_enumerate_children (file->file, FILE_ATTRS, 0, NULL, error);
+ if (*error) {
+ g_warning ("Couldn't enumerate directory %s: %s", name, (*error)->message);
+ g_clear_error (error);
+ return TRUE;
+ }
+
+ while ((info = g_file_enumerator_next_file (dir, NULL, error)) != NULL) {
+ GFile *child = g_file_get_child (file->file, g_file_info_get_name (info));
+ gchar *child_name = g_build_path ("\\", name, g_file_info_get_name (info), NULL);
+ GCabFile *child_file = gcab_file_new_with_file (child_name, child);
+
+ add_file_info (self, child_file, info, child_name, recurse, error);
+ if (*error) {
+ g_warning ("Couldn't add file %s: %s",
+ child_name, (*error)->message);
+ g_clear_error (error);
+ }
+
+ g_object_unref (child_file);
+ g_free (child_name);
+ g_object_unref (child);
+ g_object_unref (info);
+ }
+
+ g_object_unref (dir);
+
+ } else if (file_type == G_FILE_TYPE_REGULAR) {
+ gcab_file_update_info (file, info);
+ g_hash_table_insert (self->files,
+ (gpointer)gcab_file_get_name (file), g_object_ref (file));
+
+ } else {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ "Unhandled file type: %d", file_type);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * gcab_folder_add_file:
+ * @cabfile: a #GCabfile
+ * @file: file to be added
+ * @recurse: whether to recurse through subdirectories
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @error: (allow-none): #GError to set on error, or %NULL
+ *
+ * Add @file to the #GCabFolder.
+ *
+ * Returns: %TRUE on succes
+ **/
+gboolean
+gcab_folder_add_file (GCabFolder *self, GCabFile *file,
+ gboolean recurse, GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success;
+
+ g_return_val_if_fail (GCAB_IS_FOLDER (self), FALSE);
+ g_return_val_if_fail (GCAB_IS_FILE (file), FALSE);
+ g_return_val_if_fail (!error || *error == NULL, FALSE);
+
+ GFile *gfile = gcab_file_get_file (file);
+ g_return_val_if_fail (G_IS_FILE (gfile), FALSE);
+
+ GFileInfo *info = g_file_query_info (gfile, FILE_ATTRS, 0, NULL, error);
+ if (*error)
+ return FALSE;
+
+ success = add_file_info (self, file, info,
+ gcab_file_get_name (file), recurse, error);
+ g_object_unref (info);
+
+ return success;
+}
+
+/**
+ * gcab_folder_get_nfiles:
+ * @folder: a #GCabFolder
+ *
+ * Returns: Number of files in this @folder.
+ **/
+guint
+gcab_folder_get_nfiles (GCabFolder *self)
+{
+ g_return_val_if_fail (GCAB_IS_FOLDER (self), 0);
+
+ return g_hash_table_size (self->files);
+}
+
+/**
+ * gcab_folder_new:
+ *
+ * Returns: a new #GCabFolder
+ **/
+GCabFolder *
+gcab_folder_new (void)
+{
+ return g_object_new (GCAB_TYPE_FOLDER, NULL);
+}
diff --git a/libgcab/gcab-folder.h b/libgcab/gcab-folder.h
new file mode 100644
index 0000000..3a903b6
--- /dev/null
+++ b/libgcab/gcab-folder.h
@@ -0,0 +1,57 @@
+/*
+ * LibGCab
+ * Copyright (c) 2012, Marc-Andrà Lureau <marcandre lureau gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef _GCAB_FOLDER_H_
+#define _GCAB_FOLDER_H_
+
+#include <gio/gio.h>
+#include <glib-object.h>
+#include "gcab-file.h"
+
+G_BEGIN_DECLS
+
+#define GCAB_TYPE_FOLDER (gcab_folder_get_type ())
+#define GCAB_FOLDER(folder) (G_TYPE_CHECK_INSTANCE_CAST ((folder), GCAB_TYPE_FOLDER, GCabFolder))
+#define GCAB_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GCAB_TYPE_FOLDER, GCabFolderClass))
+#define GCAB_IS_FOLDER(folder) (G_TYPE_CHECK_INSTANCE_TYPE ((folder), GCAB_TYPE_FOLDER))
+#define GCAB_IS_FOLDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCAB_TYPE_FOLDER))
+#define GCAB_FOLDER_GET_CLASS(folder) (G_TYPE_INSTANCE_GET_CLASS ((folder), GCAB_TYPE_FOLDER, GCabFolderClass))
+
+typedef struct _GCabFolderClass GCabFolderClass;
+typedef struct _GCabFolder GCabFolder;
+
+typedef enum
+{
+ GCAB_COMPRESSION_NONE = 0,
+ GCAB_COMPRESSION_MSZIP = 1,
+} GCabCompression;
+
+GType gcab_folder_get_type (void) G_GNUC_CONST;
+
+GCabFolder * gcab_folder_new (void);
+gboolean gcab_folder_add_file (GCabFolder *cabfolder,
+ GCabFile *cabfile,
+ gboolean recurse,
+ GCancellable *cancellable,
+ GError **error);
+guint gcab_folder_get_nfiles (GCabFolder *cabfolder);
+
+G_END_DECLS
+
+#endif /* _GCAB_FILE_H_ */
diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h
new file mode 100644
index 0000000..7c8a545
--- /dev/null
+++ b/libgcab/gcab-priv.h
@@ -0,0 +1,34 @@
+#ifndef GCAB_PRIV_H
+#define GCAB_PRIV_H
+
+#include <glib-object.h>
+
+#include "cabinet.h"
+#include "gcab-file.h"
+#include "gcab-folder.h"
+#include "gcab-cabinet.h"
+#include "gcab-enums.h"
+
+struct _GCabFile
+{
+ GObject parent_instance;
+
+ gchar *name;
+ GFile *file;
+ cfile_t cfile;
+};
+
+struct _GCabFolder
+{
+ GObject parent_instance;
+
+ GHashTable *files;
+ GCabCompression compression;
+};
+
+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);
+
+#endif /* GCAB_PRIV_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]