[gcab] Map Cabinet/Folder/File concepts to objects



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]