[gcab] Implement listing files



commit a0f79f442229b6ca0413cb4eec45fce9f7b9143a
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Sat Dec 22 03:34:22 2012 +0100

    Implement listing files

 gcab.c                 |   41 +++++++-
 libgcab.syms           |    1 +
 libgcab/cabinet.c      |  266 ++++++++++++++++++++++++++++++++++++++++++++++++
 libgcab/cabinet.h      |   39 +++++++-
 libgcab/gcab-cabinet.c |  105 +++++++++++++++++++-
 libgcab/gcab-cabinet.h |   16 +++-
 libgcab/gcab-file.c    |   16 +++
 libgcab/gcab-folder.c  |   73 ++++++++++++--
 libgcab/gcab-folder.h  |    1 +
 libgcab/gcab-priv.h    |    4 +
 tests/testsuite.at     |    6 +-
 11 files changed, 547 insertions(+), 21 deletions(-)
---
diff --git a/gcab.c b/gcab.c
index cdc7dc8..930640d 100644
--- a/gcab.c
+++ b/gcab.c
@@ -69,8 +69,12 @@ main (int argc, char *argv[])
     gchar **args = NULL;
     int nopath = 0;
     int compress = 0;
+    int list = 0;
+    int create = 0;
     GOptionEntry entries[] = {
         { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, N_("Be verbose"), 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 },
         { "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...") },
@@ -89,7 +93,8 @@ main (int argc, char *argv[])
     g_option_context_set_description (context, s);
     g_free(s);
     g_option_context_set_summary (context, "\
-gcab saves many files together into a cabinet archive.\
+gcab saves many files together into a cabinet archive, and can restore\n\
+individual files from the archive.\
 ");
     g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
     g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
@@ -97,9 +102,35 @@ gcab saves many files together into a cabinet archive.\
         gcab_error (_("option parsing failed: %s\n"), error->message);
     g_option_context_free(context);
 
+    if ((list + create) != 1)
+        gcab_error (_("Please specify a single operation."));
 
     if (!args || args[0] == NULL)
-        gcab_error (_("output cabinet file must be specified."));
+        gcab_error (_("cabinet file must be specified."));
+
+    GCancellable *cancellable = g_cancellable_new ();
+    GCabCabinet *cabinet = gcab_cabinet_new ();
+
+    if (list) {
+        GFile *file = g_file_new_for_commandline_arg (args[0]);
+        GInputStream *in = G_INPUT_STREAM (g_file_read (file, cancellable, &error));
+
+        if (!in)
+            gcab_error (_("can't open %s for reading: %s\n"), args[0], error->message);
+        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++) {
+            GList *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_list_free (list);
+        }
+        g_object_unref (in);
+        g_object_unref (file);
+        goto end;
+    }
 
     if (args[1] == NULL)
         gcab_error (_("please specify input files."));
@@ -132,7 +163,6 @@ gcab saves many files together into a cabinet archive.\
         gcab_error (_("can't create cab file %s: %s"), args[0], error->message);
 
     GFile *cwd = g_file_new_for_commandline_arg (".");
-    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);
 
@@ -144,10 +174,13 @@ gcab saves many files together into a cabinet archive.\
                              &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);
 
+end:
+    g_object_unref (cabinet);
+    g_object_unref (cancellable);
+
     return 0;
 }
diff --git a/libgcab.syms b/libgcab.syms
index 48f90e4..25e2317 100644
--- a/libgcab.syms
+++ b/libgcab.syms
@@ -6,6 +6,7 @@ LIBGCAB1_0.0 {
         gcab_cabinet_new;
         gcab_cabinet_write;
         gcab_compression_get_type;
+        gcab_error_quark;
         gcab_file_get_file;
         gcab_file_get_name;
         gcab_file_get_type;
diff --git a/libgcab/cabinet.c b/libgcab/cabinet.c
index a946968..fb0b370 100644
--- a/libgcab/cabinet.c
+++ b/libgcab/cabinet.c
@@ -43,6 +43,98 @@ cdata_set (cdata_t *cd, int type, guint8 *data, size_t size)
     }
 }
 
+static char *
+_data_input_stream_read_until (GDataInputStream  *stream,
+                               const gchar        *stop_chars,
+                               gsize              stop_len,
+                               GCancellable       *cancellable,
+                               GError            **error)
+{
+  GBufferedInputStream *bstream;
+  gchar *result;
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+
+  result = g_data_input_stream_read_upto (stream, stop_chars, stop_len,
+                                          NULL, cancellable, error);
+
+  /* If we're not at end of stream then we have a stop_char to consume. */
+  if (result != NULL && g_buffered_input_stream_get_available (bstream) > 0)
+    {
+      gsize res;
+      gchar b;
+
+      res = g_input_stream_read (G_INPUT_STREAM (stream), &b, 1, NULL, NULL);
+      g_assert (res == 1);
+    }
+
+  return result;
+}
+
+#define R1(val) G_STMT_START{                                           \
+    val = g_data_input_stream_read_byte (in, cancellable, error);       \
+    if (*error)                                                         \
+        goto end;                                                       \
+}G_STMT_END
+#define R2(val) G_STMT_START{                                           \
+    val = g_data_input_stream_read_uint16 (in, cancellable, error);     \
+    if (*error)                                                         \
+        goto end;                                                       \
+}G_STMT_END
+#define R4(val) G_STMT_START{                                           \
+    val = g_data_input_stream_read_uint32 (in, cancellable, error);     \
+    if (*error)                                                         \
+        goto end;                                                       \
+}G_STMT_END
+#define RS(val) G_STMT_START{                                   \
+    val = _data_input_stream_read_until (in, "\0", 1,           \
+                                         cancellable, error);   \
+    if (*error)                                                 \
+        goto end;                                               \
+}G_STMT_END
+#define RN(buff, size) G_STMT_START{                             \
+    if (size) {                                                         \
+        g_input_stream_read (G_INPUT_STREAM (in), buff, size, cancellable, error); \
+        if (*error)                                                     \
+            goto end;                                                   \
+    }                                                                   \
+}G_STMT_END
+
+static void
+hexdump (guchar *p, gsize s)
+{
+    gsize i;
+
+    for (i = 0; i < s; i++) {
+        if (i != 0) {
+            if (i % 16 == 0)
+                g_printerr ("\n");
+            else if (i % 8 == 0)
+                g_printerr ("  ");
+            else
+                g_printerr (" ");
+        }
+
+        if (i % 16 == 0)
+            g_printerr ("%.8x  ", i);
+
+        g_printerr ("%.2x", p[i]);
+    }
+
+    g_printerr ("\n");
+}
+
+#define P1(p, field) \
+    g_debug ("%15s: %.2x", #field, p->field)
+#define P2(p, field) \
+    g_debug ("%15s: %.4x", #field, p->field)
+#define P4(p, field) \
+    g_debug ("%15s: %.8x", #field, p->field)
+#define PS(p, field) \
+    g_debug ("%15s: %s", #field, p->field)
+#define PN(p, field, size) \
+    g_debug ("%15s:", #field), hexdump (p->field, size)
+
 #define W1(val) \
     g_data_output_stream_put_byte (out, val, cancellable, error)
 #define W2(val) \
@@ -75,6 +167,92 @@ cheader_write (cheader_t *ch, GDataOutputStream *out,
 }
 
 G_GNUC_INTERNAL gboolean
+cheader_read (cheader_t *ch, GDataInputStream *in,
+              GCancellable *cancellable, GError **error)
+{
+    gboolean success = FALSE;
+    guint8 sig[4];
+
+    R1 (sig[0]);
+    R1 (sig[1]);
+    R1 (sig[2]);
+    R1 (sig[3]);
+    if (memcmp (sig, "MSCF", 4)) {
+        g_set_error (error, GCAB_ERROR, GCAB_ERROR_FORMAT,
+                     "The input is not of cabinet format");
+        goto end;
+    }
+
+    R4 (ch->res1);
+    R4 (ch->size);
+    R4 (ch->res2);
+    R4 (ch->offsetfiles);
+    R4 (ch->res3);
+    R1 (ch->versionMIN);
+    R1 (ch->versionMAJ);
+    R2 (ch->nfolders);
+    R2 (ch->nfiles);
+    R2 (ch->flags);
+    R2 (ch->setID);
+    R2 (ch->cabID);
+
+    if (ch->flags & CABINET_HEADER_RESERVE) {
+        R2 (ch->res_header);
+        R1 (ch->res_folder);
+        R1 (ch->res_data);
+        ch->reserved = g_malloc (ch->res_header);
+        RN (ch->reserved, ch->res_header);
+    }
+
+    if (ch->flags & CABINET_HEADER_PREV) {
+        RS (ch->cab_prev);
+        RS (ch->disk_prev);
+    }
+
+    if (ch->flags & CABINET_HEADER_NEXT) {
+        RS (ch->cab_next);
+        RS (ch->disk_next);
+    }
+
+    if (g_getenv ("GCAB_DEBUG")) {
+        g_debug ("CFHEADER");
+        P4 (ch, res1);
+        P4 (ch, size);
+        P4 (ch, res2);
+        P4 (ch, offsetfiles);
+        P4 (ch, res3);
+        P1 (ch, versionMIN);
+        P1 (ch, versionMAJ);
+        P2 (ch, nfolders);
+        P2 (ch, nfiles);
+        P2 (ch, flags);
+        P2 (ch, setID);
+        P2 (ch, cabID);
+        if (ch->flags & CABINET_HEADER_RESERVE) {
+            P2 (ch, res_header);
+            P1 (ch, res_folder);
+            P1 (ch, res_data);
+            if (ch->res_header)
+                PN (ch, reserved, ch->res_header);
+        }
+        if (ch->flags & CABINET_HEADER_PREV) {
+            PS (ch, cab_prev);
+            PS (ch, disk_prev);
+        }
+        if (ch->flags & CABINET_HEADER_NEXT) {
+            PS (ch, cab_next);
+            PS (ch, disk_next);
+        }
+
+    }
+
+    success = TRUE;
+
+end:
+    return success;
+}
+
+G_GNUC_INTERNAL gboolean
 cfolder_write (cfolder_t *cf, GDataOutputStream *out,
                GCancellable *cancellable, GError **error)
 {
@@ -86,6 +264,32 @@ cfolder_write (cfolder_t *cf, GDataOutputStream *out,
     return TRUE;
 }
 
+G_GNUC_INTERNAL gboolean
+cfolder_read (cfolder_t *cf, u1 res_size, GDataInputStream *in,
+              GCancellable *cancellable, GError **error)
+{
+    gboolean success = FALSE;
+
+    R4 (cf->offsetdata);
+    R2 (cf->ndatab);
+    R2 (cf->typecomp);
+    cf->reserved = g_malloc (res_size);
+    RN (cf->reserved, res_size);
+
+    if (g_getenv ("GCAB_DEBUG")) {
+        g_debug ("CFOLDER");
+        P4 (cf, offsetdata);
+        P2 (cf, ndatab);
+        P2 (cf, typecomp);
+        if (res_size)
+            PN (cf, reserved, res_size);
+    }
+
+    success = TRUE;
+
+end:
+    return success;
+}
 
 G_GNUC_INTERNAL gboolean
 cfile_write (cfile_t *cf, GDataOutputStream *out,
@@ -103,6 +307,38 @@ cfile_write (cfile_t *cf, GDataOutputStream *out,
     return TRUE;
 }
 
+
+G_GNUC_INTERNAL gboolean
+cfile_read (cfile_t *cf, GDataInputStream *in,
+            GCancellable *cancellable, GError **error)
+{
+    gboolean success = FALSE;
+
+    R4 (cf->usize);
+    R4 (cf->uoffset);
+    R2 (cf->index);
+    R2 (cf->date);
+    R2 (cf->time);
+    R2 (cf->fattr);
+    RS (cf->name);
+
+    if (g_getenv ("GCAB_DEBUG")) {
+        g_debug ("CFILE");
+        P4 (cf, usize);
+        P4 (cf, uoffset);
+        P2 (cf, index);
+        P2 (cf, date);
+        P2 (cf, time);
+        P2 (cf, fattr);
+        PS (cf, name);
+    }
+
+    success = TRUE;
+
+end:
+    return success;
+}
+
 typedef unsigned long int CHECKSUM;
 
 static CHECKSUM
@@ -159,3 +395,33 @@ cdata_write (cdata_t *cd, GDataOutputStream *out, int type,
 
     return TRUE;
 }
+
+G_GNUC_INTERNAL gboolean
+cdata_read (cdata_t *cd, u1 res_data, GDataInputStream *in,
+            GCancellable *cancellable, GError **error)
+
+{
+    gboolean success = FALSE;
+
+    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);
+
+    if (g_getenv ("GCAB_DEBUG")) {
+        g_debug ("CDATA");
+        P4 (cd, checksum);
+        P2 (cd, ncbytes);
+        P2 (cd, nubytes);
+        if (res_data)
+            PN (cd, reserved, res_data);
+        PN (cd, data, 64);
+    }
+
+    success = TRUE;
+
+end:
+    return success;
+}
diff --git a/libgcab/cabinet.h b/libgcab/cabinet.h
index 3d575c9..264df46 100644
--- a/libgcab/cabinet.h
+++ b/libgcab/cabinet.h
@@ -37,7 +37,6 @@ typedef struct cdata cdata_t;
 #define CFO_START               0x24		/* folder offset */
 #define CFI_START               0x2C  	/* file offset */
 
-
 struct cheader
 {
     u4 res1;
@@ -52,13 +51,28 @@ struct cheader
     u2 flags;
     u2 setID;
     u2 cabID;
+    u2 res_header;
+    u1 res_folder;
+    u1 res_data;
+    guint8 *reserved;
+    gchar *cab_prev;
+    gchar *disk_prev;
+    gchar *cab_next;
+    gchar *disk_next;
 };
 
+typedef enum {
+    CABINET_HEADER_PREV = 0x0001,
+    CABINET_HEADER_NEXT = 0x0002,
+    CABINET_HEADER_RESERVE = 0x0004,
+} CabinetHeaderFlags;
+
 struct cfolder
 {
     u4 offsetdata;
     u2 ndatab;
     u2 typecomp;
+    guint8 *reserved;
 };
 
 struct cfile
@@ -77,6 +91,7 @@ struct cdata
     u4 checksum;
     u2 ncbytes;
     u2 nubytes;
+    guint8 *reserved;
     guint8 data[DATABLOCKSIZE*2];
 };
 
@@ -84,10 +99,27 @@ gboolean     cheader_write                      (cheader_t *ch,
                                                  GDataOutputStream *out,
                                                  GCancellable *cancellable,
                                                  GError **error);
+gboolean     cheader_read                       (cheader_t *ch,
+                                                 GDataInputStream *in,
+                                                 GCancellable *cancellable,
+                                                 GError **error);
 gboolean     cfolder_write                      (cfolder_t *cf,
                                                  GDataOutputStream *out,
                                                  GCancellable *cancellable,
                                                  GError **error);
+gboolean     cfolder_read                       (cfolder_t *cf,
+                                                 u1 res_folder,
+                                                 GDataInputStream *in,
+                                                 GCancellable *cancellable,
+                                                 GError **error);
+gboolean     cfile_write                        (cfile_t *cf,
+                                                 GDataOutputStream *out,
+                                                 GCancellable *cancellable,
+                                                 GError **error);
+gboolean     cfile_read                         (cfile_t *cf,
+                                                 GDataInputStream *in,
+                                                 GCancellable *cancellable,
+                                                 GError **error);
 gboolean     cdata_write                        (cdata_t *cd,
                                                  GDataOutputStream *out,
                                                  int type,
@@ -96,8 +128,9 @@ gboolean     cdata_write                        (cdata_t *cd,
                                                  gsize *bytes_written,
                                                  GCancellable *cancellable,
                                                  GError **error);
-gboolean     cfile_write                        (cfile_t *cf,
-                                                 GDataOutputStream *out,
+gboolean     cdata_read                         (cdata_t *cd,
+                                                 u1 res_data,
+                                                 GDataInputStream *in,
                                                  GCancellable *cancellable,
                                                  GError **error);
 
diff --git a/libgcab/gcab-cabinet.c b/libgcab/gcab-cabinet.c
index 3d1a0c7..20c638a 100644
--- a/libgcab/gcab-cabinet.c
+++ b/libgcab/gcab-cabinet.c
@@ -10,14 +10,23 @@ struct _GCabCabinet
     GObject parent_instance;
 
     GPtrArray *folders;
+    GByteArray *reserved;
 };
 
 enum {
     PROP_0,
+
+    PROP_RESERVED
 };
 
 G_DEFINE_TYPE (GCabCabinet, gcab_cabinet, G_TYPE_OBJECT);
 
+GQuark
+gcab_error_quark (void)
+{
+    return g_quark_from_static_string ("gcab-error-quark");
+}
+
 static void
 gcab_cabinet_init (GCabCabinet *self)
 {
@@ -30,6 +39,8 @@ gcab_cabinet_finalize (GObject *object)
     GCabCabinet *self = GCAB_CABINET (object);
 
     g_ptr_array_unref (self->folders);
+    if (self->reserved)
+        g_byte_array_unref (self->reserved);
 
     G_OBJECT_CLASS (gcab_cabinet_parent_class)->finalize (object);
 }
@@ -38,8 +49,14 @@ static void
 gcab_cabinet_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
     g_return_if_fail (GCAB_IS_CABINET (object));
+    GCabCabinet *self = GCAB_CABINET (object);
 
     switch (prop_id) {
+    case PROP_RESERVED:
+        if (self->reserved)
+            g_byte_array_unref (self->reserved);
+        self->reserved = g_value_dup_boxed (value);
+	break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -50,8 +67,12 @@ static void
 gcab_cabinet_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
     g_return_if_fail (GCAB_IS_CABINET (object));
+    GCabCabinet *self = GCAB_CABINET (object);
 
     switch (prop_id) {
+    case PROP_RESERVED:
+        g_value_set_boxed (value, self->reserved);
+	break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -66,6 +87,11 @@ gcab_cabinet_class_init (GCabCabinetClass *klass)
     object_class->finalize = gcab_cabinet_finalize;
     object_class->set_property = gcab_cabinet_set_property;
     object_class->get_property = gcab_cabinet_get_property;
+
+    g_object_class_install_property (object_class, PROP_RESERVED,
+         g_param_spec_boxed ("reserved", "Reserved", "Reserved",
+                            G_TYPE_BYTE_ARRAY,
+                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -101,7 +127,7 @@ gcab_cabinet_add_folder (GCabCabinet *self,
 GPtrArray *
 gcab_cabinet_get_folders (GCabCabinet *self)
 {
-    g_return_val_if_fail (GCAB_IS_FOLDER (self), NULL);
+    g_return_val_if_fail (GCAB_IS_CABINET (self), NULL);
 
     return self->folders;
 }
@@ -139,6 +165,7 @@ gcab_cabinet_write (GCabCabinet *self,
     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 (!cancellable || G_IS_CANCELLABLE (cancellable), 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);
@@ -231,6 +258,8 @@ gcab_cabinet_write (GCabCabinet *self,
 end:
     g_clear_object (&dstream);
     g_clear_object (&in);
+
+    return success;
 }
 
 /**
@@ -243,3 +272,77 @@ gcab_cabinet_new (void)
 {
     return g_object_new (GCAB_TYPE_CABINET, NULL);
 }
+
+/**
+ * gcab_cabinet_load:
+ * @cabinet: a #GCabCabinet
+ * @stream: a #GInputStream
+ * @cancellable: (allow-none): optional #GCancellable object,
+ *     %NULL to ignore
+ * @error: (allow-none): #GError to set on error, or %NULL
+ *
+ * Load a cabinet archive.
+ *
+ * Returns: %TRUE on success
+ **/
+gboolean
+gcab_cabinet_load (GCabCabinet *self,
+                   GInputStream *stream,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+    g_return_val_if_fail (GCAB_IS_CABINET (self), FALSE);
+    g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+    g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+    g_return_val_if_fail (!error || *error == NULL, FALSE);
+    g_return_val_if_fail (self->folders->len == 0, FALSE);
+
+    gboolean success = FALSE;
+    cheader_t cheader;
+    int i;
+    GDataInputStream *in = g_data_input_stream_new (stream);
+    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))
+        goto end;
+
+    if (cheader.reserved)
+        g_object_set (self, "reserved",
+                      g_byte_array_new_take (cheader.reserved, cheader.res_header),
+                      NULL);
+
+    for (i = 0; i < cheader.nfolders; i++) {
+        cfolder_t cfolder = { 0, };
+        if (!cfolder_read (&cfolder, cheader.res_folder, in, cancellable, error))
+            goto end;
+
+        GCabFolder *folder = gcab_folder_new_with_cfolder (&cfolder);
+        if (cfolder.reserved)
+            g_object_set (folder, "reserved",
+                          g_byte_array_new_take (cfolder.reserved, cheader.res_folder),
+                          NULL);
+        g_ptr_array_add (folders, folder);
+        cfolder.reserved = NULL;
+    }
+
+    for (i = 0; i < cheader.nfiles; i++) {
+        cfile_t cfile = { 0, };
+        if (!cfile_read (&cfile, in, cancellable, error))
+            goto end;
+
+        GCabFile *file = gcab_file_new_with_cfile (&cfile);
+        GCabFolder *folder = g_ptr_array_index (folders, cfile.index);
+        if (!gcab_folder_add_file (folder, file, FALSE, cancellable, error)) {
+            g_object_unref (file);
+            goto end;
+        }
+    }
+
+    success = TRUE;
+
+end:
+    if (in)
+        g_object_unref (in);
+    return success;
+}
diff --git a/libgcab/gcab-cabinet.h b/libgcab/gcab-cabinet.h
index 7b2f48c..e32f347 100644
--- a/libgcab/gcab-cabinet.h
+++ b/libgcab/gcab-cabinet.h
@@ -35,16 +35,30 @@ G_BEGIN_DECLS
 #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))
 
+#define GCAB_ERROR gcab_error_quark ()
+GQuark gcab_error_quark (void);
+
+typedef enum GCabError
+{
+    GCAB_ERROR_FORMAT
+} GCabError;
+
 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_load          (GCabCabinet *cabinet,
+                                               GInputStream *stream,
+                                               GCancellable *cancellable,
+                                               GError **error);
+
+GPtrArray *        gcab_cabinet_get_folders   (GCabCabinet *cabinet);
+
 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,
diff --git a/libgcab/gcab-file.c b/libgcab/gcab-file.c
index 93b28e8..8b652cc 100644
--- a/libgcab/gcab-file.c
+++ b/libgcab/gcab-file.c
@@ -180,8 +180,24 @@ gcab_file_get_file (GCabFile *self)
 GCabFile *
 gcab_file_new_with_file (const gchar *name, GFile *file)
 {
+    g_return_val_if_fail (name != NULL, NULL);
+    g_return_val_if_fail (G_IS_FILE (file), NULL);
+
     return g_object_new (GCAB_TYPE_FILE,
                          "name", name,
                          "file", file,
                          NULL);
 }
+
+G_GNUC_INTERNAL GCabFile *
+gcab_file_new_with_cfile (const cfile_t *cfile)
+{
+    g_return_val_if_fail (cfile != NULL, NULL);
+
+    GCabFile *file = g_object_new (GCAB_TYPE_FILE,
+                                   "name", cfile->name,
+                                   NULL);
+    file->cfile = *cfile;
+
+    return file;
+}
diff --git a/libgcab/gcab-folder.c b/libgcab/gcab-folder.c
index 35c856d..ec3fe47 100644
--- a/libgcab/gcab-folder.c
+++ b/libgcab/gcab-folder.c
@@ -9,6 +9,7 @@ enum {
     PROP_0,
 
     PROP_COMPRESSION,
+    PROP_RESERVED
 };
 
 G_DEFINE_TYPE (GCabFolder, gcab_folder, G_TYPE_OBJECT);
@@ -25,6 +26,8 @@ gcab_folder_finalize (GObject *object)
     GCabFolder *self = GCAB_FOLDER (object);
 
     g_hash_table_unref (self->files);
+    if (self->reserved)
+        g_byte_array_unref (self->reserved);
 
     G_OBJECT_CLASS (gcab_folder_parent_class)->finalize (object);
 }
@@ -39,6 +42,11 @@ gcab_folder_set_property (GObject *object, guint prop_id, const GValue *value, G
     case PROP_COMPRESSION:
         self->compression = g_value_get_enum (value);
         break;
+    case PROP_RESERVED:
+        if (self->reserved)
+            g_byte_array_unref (self->reserved);
+        self->reserved = g_value_dup_boxed (value);
+	break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -55,6 +63,9 @@ gcab_folder_get_property (GObject *object, guint prop_id, GValue *value, GParamS
     case PROP_COMPRESSION:
         g_value_set_enum (value, self->compression);
         break;
+    case PROP_RESERVED:
+        g_value_set_boxed (value, self->reserved);
+	break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -75,6 +86,12 @@ gcab_folder_class_init (GCabFolderClass *klass)
                            GCAB_TYPE_COMPRESSION, 0,
                            G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
                            G_PARAM_STATIC_STRINGS));
+
+    g_object_class_install_property (object_class, PROP_RESERVED,
+         g_param_spec_boxed ("reserved", "Reserved", "Reserved",
+                            G_TYPE_BYTE_ARRAY,
+                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
 }
 
 /* calculate the number of datablocks we will need:
@@ -93,6 +110,18 @@ gcab_folder_get_ndatablocks (GCabFolder *self)
     return total_size / DATABLOCKSIZE + 1 ;
 }
 
+static gboolean
+add_file (GCabFolder *self, GCabFile *file)
+{
+    if (g_hash_table_lookup (self->files, (gpointer)gcab_file_get_name (file)))
+        return FALSE;
+
+    g_hash_table_insert (self->files,
+                         (gpointer)gcab_file_get_name (file), g_object_ref (file));
+
+    return TRUE;
+}
+
 #define FILE_ATTRS "standard::*,time::modified"
 
 static gboolean
@@ -134,8 +163,8 @@ add_file_info (GCabFolder *self, GCabFile *file, GFileInfo *info,
 
     } 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));
+        if (!add_file (self, file))
+            return FALSE;
 
     } else {
         g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
@@ -171,15 +200,19 @@ gcab_folder_add_file (GCabFolder *self, GCabFile *file,
     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);
+    if (gfile) {
+        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;
+        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);
+        success = add_file_info (self, file, info,
+                                 gcab_file_get_name (file), recurse, error);
+        g_object_unref (info);
+    } else {
+        success = add_file (self, file);
+    }
 
     return success;
 }
@@ -211,3 +244,25 @@ gcab_folder_new (GCabCompression compression)
                          "compression", compression,
                          NULL);
 }
+
+G_GNUC_INTERNAL GCabFolder *
+gcab_folder_new_with_cfolder (const cfolder_t *folder)
+{
+    return g_object_new (GCAB_TYPE_FOLDER,
+                         "compression", folder->typecomp,
+                         NULL);
+}
+
+/**
+ * gcab_folder_get_files:
+ * @cabfolder: a #GCabFolder
+ *
+ * Returns: (element-type GCabFile) (transfer full): list of files
+ **/
+GList *
+gcab_folder_get_files (GCabFolder *self)
+{
+    g_return_val_if_fail (GCAB_IS_FOLDER (self), 0);
+
+    return g_hash_table_get_values (self->files);
+}
diff --git a/libgcab/gcab-folder.h b/libgcab/gcab-folder.h
index fa75897..f8a7ac4 100644
--- a/libgcab/gcab-folder.h
+++ b/libgcab/gcab-folder.h
@@ -51,6 +51,7 @@ gboolean        gcab_folder_add_file          (GCabFolder *cabfolder,
                                                GCancellable *cancellable,
                                                GError **error);
 guint           gcab_folder_get_nfiles        (GCabFolder *cabfolder);
+GList *         gcab_folder_get_files         (GCabFolder *cabfolder);
 
 G_END_DECLS
 
diff --git a/libgcab/gcab-priv.h b/libgcab/gcab-priv.h
index 7c8a545..04f798f 100644
--- a/libgcab/gcab-priv.h
+++ b/libgcab/gcab-priv.h
@@ -24,8 +24,12 @@ struct _GCabFolder
 
     GHashTable *files;
     GCabCompression compression;
+    GByteArray *reserved;
 };
 
+GCabFolder *     gcab_folder_new_with_cfolder        (const cfolder_t *folder);
+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);
 
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 3496b55..d317101 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -30,14 +30,14 @@ AT_CHECK([_gcab ]$@)])
 
 AT_SETUP([Invalid command line])
 AT_CHECK_GCAB([], [1], [ignore], [ignore])
-AT_CHECK_GCAB([out.cab], [1], [ignore], [ignore])
+AT_CHECK_GCAB([-c out.cab], [1], [ignore], [ignore])
 AT_CHECK([test -f out.cab], [1])
 AT_CLEANUP
 
 AT_SETUP([Add one file])
 AT_DATA([test.txt], [This is test.txt
 ])
-AT_CHECK_GCAB([out.cab test.txt])
+AT_CHECK_GCAB([-c out.cab test.txt])
 AT_CHECK([cabextract -ql out.cab | tail -n+3 |  cut -d'|' -f3], [0],
 [ test.txt
 ])
@@ -45,7 +45,7 @@ cp test.txt expout
 AT_CHECK([cabextract -q out.cab], [0])
 AT_CHECK([cat test.txt], [0], [expout])
 #compressed
-AT_CHECK_GCAB([-z out.cab test.txt])
+AT_CHECK_GCAB([-cz out.cab test.txt])
 AT_CHECK([cabextract -ql out.cab | tail -n+3 |  cut -d'|' -f3], [0],
 [ test.txt
 ])



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]