summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO1
-rw-r--r--configure.ac2
-rw-r--r--include/libmsi-record.h11
-rw-r--r--libmsi/Makefile.am1
-rw-r--r--libmsi/libmsi-istream.c155
-rw-r--r--libmsi/libmsi-istream.h47
-rw-r--r--libmsi/libmsi-record.c123
-rw-r--r--libmsi/msipriv.h5
-rw-r--r--tests/testdatabase.c63
-rw-r--r--tests/testrecord.c89
-rw-r--r--tools/msiinfo.c25
11 files changed, 387 insertions, 135 deletions
diff --git a/TODO b/TODO
index 14f04d3..1e66c72 100644
--- a/TODO
+++ b/TODO
@@ -5,7 +5,6 @@
statement in the passed string
- add a SQL tool with readline
- add API to import from a string
-- add API to write a stream field to a GIO stream or back
- split regression tests into many smaller harnesses, possibly using
gtester
- add tests for msiinfo
diff --git a/configure.ac b/configure.ac
index 899daf1..01465b2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@ AC_PROG_CC
AC_PROG_YACC
AM_PATH_GLIB_2_0([2.12.0])
-PKG_CHECK_MODULES([GOBJECT], [gobject-2.0])
+PKG_CHECK_MODULES([GOBJECT], [gobject-2.0 gio-2.0 >= 2.14])
PKG_CHECK_MODULES([GSF], [libgsf-1])
PKG_CHECK_MODULES([UUID], [uuid], [uuid=yes], [uuid=no])
AS_IF([test "$uuid" = yes],
diff --git a/include/libmsi-record.h b/include/libmsi-record.h
index 986e76e..ef0546e 100644
--- a/include/libmsi-record.h
+++ b/include/libmsi-record.h
@@ -20,6 +20,7 @@
#define _LIBMSI_RECORD_H
#include <glib-object.h>
+#include <gio/gio.h>
#include "libmsi-types.h"
@@ -59,10 +60,14 @@ gchar * libmsi_record_get_string (const LibmsiRecord *record,
gboolean libmsi_record_load_stream (LibmsiRecord *record,
guint field,
const gchar *filename);
-gboolean libmsi_record_save_stream (LibmsiRecord *rec,
+gboolean libmsi_record_set_stream (LibmsiRecord *record,
guint field,
- gchar *buf,
- guint *sz);
+ GInputStream *input,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error);
+GInputStream * libmsi_record_get_stream (LibmsiRecord *record,
+ guint field);
G_END_DECLS
diff --git a/libmsi/Makefile.am b/libmsi/Makefile.am
index c03e7ab..38dbe2e 100644
--- a/libmsi/Makefile.am
+++ b/libmsi/Makefile.am
@@ -17,6 +17,7 @@ libmsi_la_SOURCES = \
drop.c \
insert.c \
libmsi-database.c \
+ libmsi-istream.c \
libmsi-query.c \
libmsi-record.c \
libmsi-summary-info.c \
diff --git a/libmsi/libmsi-istream.c b/libmsi/libmsi-istream.c
new file mode 100644
index 0000000..85653e5
--- /dev/null
+++ b/libmsi/libmsi-istream.c
@@ -0,0 +1,155 @@
+/*
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "msipriv.h"
+#include "libmsi-istream.h"
+
+struct _LibmsiIStream
+{
+ GInputStream parent;
+
+ GsfInput *input;
+};
+
+static void libmsi_seekable_iface_init (GSeekableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (LibmsiIStream, libmsi_istream, G_TYPE_INPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+ libmsi_seekable_iface_init);
+ )
+
+static goffset
+libmsi_tell (GSeekable *seekable)
+{
+ g_return_val_if_fail (LIBMSI_IS_ISTREAM(seekable), FALSE);
+
+ return gsf_input_tell (LIBMSI_ISTREAM(seekable)->input);
+}
+
+static gboolean
+libmsi_can_seek (GSeekable *seekable)
+{
+ return TRUE;
+}
+
+static gboolean
+libmsi_seek (GSeekable *seekable, goffset offset,
+ GSeekType type, GCancellable *cancellable)
+{
+ g_return_val_if_fail (LIBMSI_IS_ISTREAM(seekable), FALSE);
+
+ return !gsf_input_seek (LIBMSI_ISTREAM(seekable)->input, offset, type);
+}
+
+static gboolean
+libmsi_can_truncate (GSeekable *seekable)
+{
+ return FALSE;
+}
+
+static void
+libmsi_seekable_iface_init (GSeekableIface *iface)
+{
+ iface->tell = libmsi_tell;
+ iface->can_seek = libmsi_can_seek;
+ iface->seek = libmsi_seek;
+ iface->can_truncate = libmsi_can_truncate;
+}
+
+static void
+libmsi_istream_init (LibmsiIStream *self)
+{
+}
+
+static void
+libmsi_istream_finalize (GObject *object)
+{
+ LibmsiIStream *self = LIBMSI_ISTREAM (object);
+
+ if (self->input)
+ g_object_unref (self->input);
+
+ G_OBJECT_CLASS (libmsi_istream_parent_class)->finalize (object);
+}
+
+static gssize
+input_stream_read (GInputStream *stream,
+ void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ LibmsiIStream *self = LIBMSI_ISTREAM (stream);
+ gssize remaining = gsf_input_remaining (self->input);
+
+ if (remaining == 0)
+ return 0;
+
+ count = MIN(count, remaining);
+ if (!gsf_input_read (self->input, count, buffer))
+ return -1;
+
+ return count;
+}
+
+static gssize
+input_stream_skip (GInputStream *stream,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ LibmsiIStream *self = LIBMSI_ISTREAM (stream);
+
+ count = MIN (count, gsf_input_remaining (self->input));
+ if (!gsf_input_seek (self->input, count, G_SEEK_CUR))
+ return -1;
+
+ return count;
+}
+
+static gboolean
+input_stream_close (GInputStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return TRUE;
+}
+
+static void
+libmsi_istream_class_init (LibmsiIStreamClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+ GInputStreamClass *istream_class;
+
+ object_class->finalize = libmsi_istream_finalize;
+
+ istream_class = G_INPUT_STREAM_CLASS (klass);
+ istream_class->read_fn = input_stream_read;
+ istream_class->skip = input_stream_skip;
+ istream_class->close_fn = input_stream_close;
+}
+
+G_GNUC_INTERNAL LibmsiIStream *
+libmsi_istream_new (GsfInput *input)
+{
+ GsfInput *dup = gsf_input_dup (input, NULL);
+ g_return_val_if_fail (dup, NULL);
+
+ LibmsiIStream *self = g_object_new (LIBMSI_TYPE_ISTREAM, NULL);
+ self->input = dup;
+
+ return self;
+}
diff --git a/libmsi/libmsi-istream.h b/libmsi/libmsi-istream.h
new file mode 100644
index 0000000..c835bd9
--- /dev/null
+++ b/libmsi/libmsi-istream.h
@@ -0,0 +1,47 @@
+/*
+ * 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 St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef _LIBMSI_ISTREAM_H
+#define _LIBMSI_ISTREAM_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "libmsi-types.h"
+
+G_BEGIN_DECLS
+
+#define LIBMSI_TYPE_ISTREAM (libmsi_istream_get_type ())
+#define LIBMSI_ISTREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LIBMSI_TYPE_ISTREAM, LibmsiIStream))
+#define LIBMSI_ISTREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LIBMSI_TYPE_ISTREAM, LibmsiIStreamClass))
+#define LIBMSI_IS_ISTREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LIBMSI_TYPE_ISTREAM))
+#define LIBMSI_IS_ISTREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LIBMSI_TYPE_ISTREAM))
+#define LIBMSI_ISTREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LIBMSI_TYPE_ISTREAM, LibmsiIStreamClass))
+
+typedef struct _LibmsiIStreamClass LibmsiIStreamClass;
+
+struct _LibmsiIStreamClass
+{
+ GOutputStreamClass parent_class;
+};
+
+GType libmsi_istream_get_type (void) G_GNUC_CONST;
+
+gssize libmsi_istream_get_size (LibmsiIStream *ostream);
+
+G_END_DECLS
+
+#endif /* _LIBMSI_ISTREAM_H */
diff --git a/libmsi/libmsi-record.c b/libmsi/libmsi-record.c
index aa77ad6..da5d8bf 100644
--- a/libmsi/libmsi-record.c
+++ b/libmsi/libmsi-record.c
@@ -577,80 +577,101 @@ libmsi_record_load_stream(LibmsiRecord *rec, unsigned field, const char *szFilen
return ret == LIBMSI_RESULT_SUCCESS;
}
-unsigned _libmsi_record_save_stream(const LibmsiRecord *rec, unsigned field, char *buf, unsigned *sz)
+/**
+ * libmsi_record_set_stream:
+ * @record: a #LibmsiRecord
+ * @field: a field identifier
+ * @input: a #GInputStream
+ * @count: the number of bytes to read from @input
+ * @cancellable: (allow-none): optional GCancellable object, %NULL to ignore
+ * @error: (allow-none): #GError to set on error, or %NULL
+ *
+ * Set the stream content from @input stream.
+ *
+ * Returns: %TRUE on success
+ **/
+gboolean
+libmsi_record_set_stream (LibmsiRecord *rec, guint field,
+ GInputStream *input, gsize count,
+ GCancellable *cancellable, GError **error)
{
- uint64_t left;
- GsfInput *stm;
-
- TRACE("%p %d %p %p\n", rec, field, buf, sz);
-
- if( !sz )
- return LIBMSI_RESULT_INVALID_PARAMETER;
-
- if( field > rec->count)
- return LIBMSI_RESULT_INVALID_PARAMETER;
-
- if ( rec->fields[field].type == LIBMSI_FIELD_TYPE_NULL )
- {
- *sz = 0;
- return LIBMSI_RESULT_INVALID_DATA;
+ g_return_val_if_fail (LIBMSI_IS_RECORD (rec), FALSE);
+ g_return_val_if_fail (G_IS_INPUT_STREAM (input), FALSE);
+ g_return_val_if_fail (field > 0 && field <= rec->count, FALSE);
+ g_return_val_if_fail (count > 0, FALSE);
+ g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
+ g_return_val_if_fail (!error || *error == NULL, FALSE);
+
+ gsize bytes_read = 0;
+ GsfInput *stm = NULL;
+ guint8 *data = g_malloc (count);
+
+ if (!g_input_stream_read_all (input, data, count, &bytes_read,
+ cancellable, error) ||
+ bytes_read != count) {
+ g_free (data);
+ return FALSE;
}
- if( rec->fields[field].type != LIBMSI_FIELD_TYPE_STREAM )
- return LIBMSI_RESULT_INVALID_DATATYPE;
+ stm = gsf_input_memory_new (data, count, TRUE);
+ if (_libmsi_record_load_stream (rec, field, stm) != LIBMSI_RESULT_SUCCESS) {
+ g_object_unref (stm);
+ return FALSE;
+ }
- stm = rec->fields[field].u.stream;
- if( !stm )
- return LIBMSI_RESULT_INVALID_PARAMETER;
+ return TRUE;
+}
- left = gsf_input_size(stm) - gsf_input_tell(stm);
+static GsfInput *
+_libmsi_record_get_stream (LibmsiRecord *rec, unsigned field, GError **error)
+{
+ GsfInput *stm;
- /* if there's no buffer pointer, calculate the length to the end */
- if( !buf )
- {
- *sz = left;
+ if (field > rec->count) {
+ g_set_error (error, LIBMSI_RESULT_ERROR, LIBMSI_RESULT_INVALID_PARAMETER, G_STRFUNC);
+ return NULL;
+ }
- return LIBMSI_RESULT_SUCCESS;
+ if (rec->fields[field].type == LIBMSI_FIELD_TYPE_NULL) {
+ g_set_error (error, LIBMSI_RESULT_ERROR, LIBMSI_RESULT_INVALID_DATA, G_STRFUNC);
+ return NULL;
}
- /* read the data */
- if (*sz > left)
- *sz = left;
+ if (rec->fields[field].type != LIBMSI_FIELD_TYPE_STREAM) {
+ g_set_error (error, LIBMSI_RESULT_ERROR, LIBMSI_RESULT_INVALID_DATATYPE, G_STRFUNC);
+ return NULL;
+ }
- if (*sz > 0 && !gsf_input_read( stm, *sz, buf ))
- {
- *sz = 0;
- return LIBMSI_RESULT_FUNCTION_FAILED;
+ stm = rec->fields[field].u.stream;
+ if (!stm) {
+ g_set_error (error, LIBMSI_RESULT_ERROR, LIBMSI_RESULT_INVALID_PARAMETER, G_STRFUNC);
+ return NULL;
}
- return LIBMSI_RESULT_SUCCESS;
+ return stm;
}
/**
- * libmsi_record_save_stream:
- * @rec: a %LibmsiRecord
+ * libmsi_record_get_stream:
+ * @record: a #LibmsiRecord
* @field: a field identifier
- * @buf: a buffer of size specified by %sz, or %NULL to return size
- * @sz: a pointer to %buf size
*
- * Read the stream data into %buf from record %field.
+ * Get the stream associated with the given record @field.
*
- * Returns: %TRUE on success.
+ * Returns: (transfer full): a new #GInputStream
**/
-gboolean
-libmsi_record_save_stream(LibmsiRecord *rec, unsigned field, char *buf, unsigned *sz)
+GInputStream *
+libmsi_record_get_stream (LibmsiRecord *rec, guint field)
{
- unsigned ret;
-
- TRACE("%d %d %p %p\n", rec, field, buf, sz);
+ GsfInput *stm;
- g_return_val_if_fail (LIBMSI_IS_RECORD (rec), FALSE);
+ g_return_val_if_fail (LIBMSI_IS_RECORD (rec), NULL);
- g_object_ref(rec);
- ret = _libmsi_record_save_stream( rec, field, buf, sz );
- g_object_unref(rec);
+ stm = _libmsi_record_get_stream (rec, field, NULL);
+ if (!stm)
+ return NULL;
- return ret == LIBMSI_RESULT_SUCCESS;
+ return G_INPUT_STREAM (libmsi_istream_new (stm));
}
unsigned _libmsi_record_set_gsf_input( LibmsiRecord *rec, unsigned field, GsfInput *stm )
diff --git a/libmsi/msipriv.h b/libmsi/msipriv.h
index a74f9a5..4c52718 100644
--- a/libmsi/msipriv.h
+++ b/libmsi/msipriv.h
@@ -304,6 +304,8 @@ struct _LibmsiSummaryInfo
LibmsiOLEVariant property[MSI_MAX_PROPS];
};
+typedef struct _LibmsiIStream LibmsiIStream;
+
extern const char clsid_msi_transform[16];
extern const char clsid_msi_database[16];
extern const char clsid_msi_patch[16];
@@ -415,6 +417,9 @@ extern unsigned msi_view_get_row(LibmsiDatabase *, LibmsiView *, unsigned, Libms
/* summary information */
extern unsigned msi_add_suminfo( LibmsiDatabase *db, char ***records, int num_records, int num_columns );
+/* IStream internals */
+LibmsiIStream * libmsi_istream_new (GsfInput *input);
+
/* Helpers */
extern char *msi_dup_record_field(LibmsiRecord *row, int index);
extern char *msi_dup_property( LibmsiDatabase *db, const char *prop );
diff --git a/tests/testdatabase.c b/tests/testdatabase.c
index 58bce0a..381e178 100644
--- a/tests/testdatabase.c
+++ b/tests/testdatabase.c
@@ -1088,6 +1088,7 @@ static void create_file_data(const char *name, const char *data, unsigned size)
static void test_streamtable(void)
{
GError *error = NULL;
+ GInputStream *in;
LibmsiDatabase *hdb = 0;
LibmsiRecord *rec;
LibmsiQuery *query;
@@ -1233,9 +1234,11 @@ static void test_streamtable(void)
size = sizeof(buf);
memset(buf, 0, sizeof(buf));
- r = libmsi_record_save_stream( rec, 2, buf, &size );
- ok(r, "Failed to get stream: %d\n", r);
+ in = libmsi_record_get_stream(rec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok( g_str_equal(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf);
+ g_object_unref(in);
g_object_unref( rec );
libmsi_query_close(query, NULL);
@@ -1255,9 +1258,11 @@ static void test_streamtable(void)
size = sizeof(buf);
memset(buf, 0, sizeof(buf));
- r = libmsi_record_save_stream( rec, 2, buf, &size );
- ok( r, "Failed to get stream: %d\n", r);
+ in = libmsi_record_get_stream(rec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok( g_str_equal(buf, "test1.txt\n"), "Expected 'test1.txt\\n', got %s\n", buf);
+ g_object_unref(in);
g_object_unref( rec );
libmsi_query_close(query, NULL);
@@ -1297,9 +1302,11 @@ static void test_streamtable(void)
size = sizeof(buf);
memset(buf, 0, sizeof(buf));
- r = libmsi_record_save_stream( rec, 2, buf, &size );
- ok(r, "Failed to get stream: %d\n", r);
+ in = libmsi_record_get_stream(rec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
todo_wine ok( g_str_equal(buf, "test2.txt\n"), "Expected 'test2.txt\\n', got %s\n", buf);
+ g_object_unref(in);
g_object_unref( rec );
libmsi_query_close(query, NULL);
@@ -1325,6 +1332,7 @@ static void test_streamtable(void)
static void test_binary(void)
{
+ GInputStream *in;
LibmsiDatabase *hdb = 0;
LibmsiRecord *rec;
char file[256];
@@ -1370,9 +1378,11 @@ static void test_binary(void)
size = sizeof(buf);
memset( buf, 0, sizeof(buf) );
- r = libmsi_record_save_stream( rec, 2, buf, &size );
- ok(r, "Failed to get stream: %d\n", r );
+ in = libmsi_record_get_stream(rec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok( g_str_equal(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
+ g_object_unref(in);
g_object_unref( rec );
@@ -1385,9 +1395,11 @@ static void test_binary(void)
size = sizeof(buf);
memset( buf, 0, sizeof(buf) );
- r = libmsi_record_save_stream( rec, 3, buf, &size );
- ok(r, "Failed to get stream: %d\n", r );
+ in = libmsi_record_get_stream(rec, 3);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok( g_str_equal(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf );
+ g_object_unref(in);
g_object_unref( rec );
@@ -2010,6 +2022,7 @@ static const char bin_import_dat[] = "Name\tData\r\n"
static void test_binary_import(void)
{
+ GInputStream *in;
LibmsiDatabase *hdb = 0;
LibmsiRecord *rec;
char file[256];
@@ -2040,11 +2053,12 @@ static void test_binary_import(void)
size = sizeof(buf);
memset(buf, 0, size);
- r = libmsi_record_save_stream(rec, 2, buf, &size);
- ok(r, "Failed to get stream: %d\n", r);
+ in = libmsi_record_get_stream(rec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok(g_str_equal(buf, "just some words"),
"Expected 'just some words', got %s\n", buf);
-
+ g_object_unref(in);
g_object_unref(rec);
g_object_unref(hdb);
@@ -2573,12 +2587,13 @@ static void test_try_transform(void)
ok(r == LIBMSI_RESULT_SUCCESS, "select query failed\n");
/* check the contents of the stream */
- sz = sizeof buffer;
- r = libmsi_record_save_stream( hrec, 1, buffer, &sz );
- ok(r, "read stream failed\n");
+ in = libmsi_record_get_stream(rec, 1);
+ ok(in, "Failed to get stream\n");
+ sz = g_input_stream_read(in, buffer, sizeof(buffer), NULL, NULL);
ok(!memcmp(buffer, "naengmyon", 9), "stream data was wrong\n");
ok(sz == 9, "stream data was wrong size\n");
if (hrec) g_object_unref(hrec);
+ g_object_unref(in);
/* check the validity of the table with a deleted row */
hrec = 0;
@@ -5819,12 +5834,8 @@ static void test_storages_table(void)
check_record_string(hrec, 1, "stgname");
- size = sizeof(buf);
- strcpy(buf, "apple");
- r = libmsi_record_save_stream(hrec, 2, buf, &size);
- ok(!r, "Expected ERROR_INVALID_DATA, got %d\n", r);
- ok(g_str_equal(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf);
- ok(size == 0, "Expected 0, got %d\n", size);
+ in = libmsi_record_get_stream(hrec, 2);
+ ok(!in, "Expected ERROR_INVALID_DATA\n");
g_object_unref(hrec);
@@ -6059,6 +6070,7 @@ static void test_droptable(void)
static void test_dbmerge(void)
{
GError *error = NULL;
+ GInputStream *in;
LibmsiDatabase *hdb;
LibmsiDatabase *href;
LibmsiQuery *hquery;
@@ -6554,11 +6566,12 @@ static void test_dbmerge(void)
size = sizeof(buf);
memset(buf, 0, sizeof(buf));
- r = libmsi_record_save_stream(hrec, 2, buf, &size);
- ok(r, "Expected LIBMSI_RESULT_SUCCESS, got %d\n", r);
+ in = libmsi_record_get_stream(hrec, 2);
+ ok(in, "Failed to get stream\n");
+ size = g_input_stream_read(in, buf, sizeof(buf), NULL, NULL);
ok(g_str_equal(buf, "binary.dat\n"),
"Expected \"binary.dat\\n\", got \"%s\"\n", buf);
-
+ g_object_unref(in);
g_object_unref(hrec);
/* nothing in MergeErrors */
diff --git a/tests/testrecord.c b/tests/testrecord.c
index ef9d26d..286c791 100644
--- a/tests/testrecord.c
+++ b/tests/testrecord.c
@@ -50,6 +50,7 @@ static bool create_temp_file (char *name)
static void test_msirecord (void)
{
+ GInputStream *in, *in2;
unsigned r, sz;
int i;
LibmsiRecord *h;
@@ -193,9 +194,8 @@ static void test_msirecord (void)
r = libmsi_record_load_stream (h, 0, NULL);
ok (!r, "set NULL stream\n");
sz = sizeof buf;
- r = libmsi_record_save_stream (h, 0, buf, &sz);
- ok (!r, "read non-stream type\n");
- ok (sz == sizeof buf, "set sz\n");
+ in = libmsi_record_get_stream (h, 0);
+ ok (!in, "read non-stream type\n");
/* same record, now close it */
g_object_unref (h);
@@ -210,47 +210,54 @@ static void test_msirecord (void)
ok (!r, "added stream to field 0\n");
r = libmsi_record_load_stream (h, 1, filename);
ok (r, "failed to add stream to record\n");
- r = libmsi_record_save_stream (h, 1, buf, NULL);
- ok (!r, "should return error\n");
unlink (filename); /* Windows 98 doesn't like this at all, so don't check return. */
- r = libmsi_record_save_stream (h, 1, NULL, NULL);
- ok (!r, "should return error\n");
- sz = sizeof buf;
- r = libmsi_record_save_stream (h, 1, NULL, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == 26, "couldn't get size of stream\n");
- sz = 0;
- r = libmsi_record_save_stream (h, 1, buf, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == 0, "short read\n");
- sz = sizeof buf;
- r = libmsi_record_save_stream (h, 1, buf, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == sizeof buf, "short read\n");
+
+ in = libmsi_record_get_stream (h, 1);
+ ok (in, "failed to get stream\n");
+ in2 = libmsi_record_get_stream (h, 1);
+ ok (in2, "failed to get stream\n");
+ sz = g_input_stream_read (in, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 10, "failed to read stream\n");
ok (!strncmp (buf, "abcdefghij", 10), "read the wrong thing\n");
- sz = sizeof buf;
- r = libmsi_record_save_stream (h, 1, buf, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == sizeof buf, "short read\n");
+ sz = g_input_stream_read (in2, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 10, "failed to read stream\n");
+ ok (!strncmp (buf, "abcdefghij", 10), "read the wrong thing\n");
+ sz = g_input_stream_read (in, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 10, "failed to read stream\n");
ok (!strncmp (buf, "klmnopqrst", 10), "read the wrong thing\n");
- memset (buf, 0, sizeof buf);
- sz = sizeof buf;
- r = libmsi_record_save_stream (h, 1, buf, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == 6, "short read\n");
- ok (!strcmp (buf, "uvwxyz"), "read the wrong thing\n");
- memset (buf, 0, sizeof buf);
- sz = sizeof buf;
- r = libmsi_record_save_stream (h, 1, buf, &sz);
- ok (r, "failed to read stream\n");
- ok (sz == 0, "size non-zero at end of stream\n");
- ok (buf[0] == 0, "read something at end of the stream\n");
- r = libmsi_record_load_stream (h, 1, NULL);
- ok (r, "failed to reset stream\n");
- sz = 0;
- r = libmsi_record_save_stream (h, 1, NULL, &sz);
- ok (r, "bytes left wrong after reset\n");
- ok (sz == 26, "couldn't get size of stream\n");
+ sz = g_input_stream_read (in, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 6, "failed to read stream\n");
+ ok (!strncmp (buf, "uvwxyz", 6), "read the wrong thing\n");
+ g_object_unref (in);
+
+ sz = g_input_stream_read (in2, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 10, "failed to read stream\n");
+ ok (!strncmp (buf, "klmnopqrst", 10), "read the wrong thing\n");
+ sz = g_seekable_tell (G_SEEKABLE (in2));
+ ok (sz == 20, "failed to get current position\n");
+ r = g_seekable_seek (G_SEEKABLE (in2), 0, G_SEEK_END, NULL, NULL);
+ ok (r, "failed to seek\n");
+ sz = g_seekable_tell (G_SEEKABLE (in2));
+ ok (sz == 26, "failed to get current position\n");
+ r = g_seekable_seek (G_SEEKABLE (in2), 0, G_SEEK_SET, NULL, NULL);
+ ok (r, "failed to seek\n");
+ sz = g_seekable_tell (G_SEEKABLE (in2));
+ ok (sz == 0, "failed to get current position\n");
+ sz = g_input_stream_read (in2, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 10, "failed to read stream\n");
+ ok (!strncmp (buf, "abcdefghij", 10), "read the wrong thing\n");
+ g_object_unref (in2);
+
+ in = g_memory_input_stream_new_from_data ("12345", 5, NULL);
+ r = libmsi_record_set_stream (h, 1, in, 5, NULL, NULL);
+ ok (r, "failed to set stream to record\n");
+ g_object_unref(in);
+ in = libmsi_record_get_stream (h, 1);
+ ok (in, "failed to get stream\n");
+ sz = g_input_stream_read (in, buf, sizeof(buf), NULL, NULL);
+ ok (sz == 5, "failed to read stream\n");
+ ok (!strncmp (buf, "12345", 5), "read the wrong thing\n");
+ g_object_unref(in);
/* now close the stream record */
g_object_unref (h);
diff --git a/tools/msiinfo.c b/tools/msiinfo.c
index eb44035..9448ce0 100644
--- a/tools/msiinfo.c
+++ b/tools/msiinfo.c
@@ -371,6 +371,8 @@ static int cmd_extract(struct Command *cmd, int argc, char **argv, GError **erro
LibmsiDatabase *db = NULL;
LibmsiQuery *query = NULL;
LibmsiRecord *rec = NULL;
+ GOutputStream *out = NULL;
+ GInputStream *in = NULL;
int r = 1;
char *buf;
unsigned size, bufsize;
@@ -398,26 +400,23 @@ static int cmd_extract(struct Command *cmd, int argc, char **argv, GError **erro
if (*error)
goto end;
- if (!libmsi_record_save_stream(rec, 1, NULL, &size))
- exit(1);
-
- bufsize = (size > 1048576 ? 1048576 : size);
- buf = g_malloc(bufsize);
-
#if O_BINARY
_setmode(STDOUT_FILENO, O_BINARY);
#endif
- while (size > 0) {
- r = libmsi_record_save_stream(rec, 1, buf, &bufsize);
- assert(size >= bufsize);
- full_write(STDOUT_FILENO, buf, bufsize);
- size -= bufsize;
- }
+ out = g_unix_output_stream_new(STDOUT_FILENO, FALSE);
+ in = G_INPUT_STREAM (libmsi_record_get_stream(rec, 1));
+ if (g_output_stream_splice(out, in, 0, NULL, error) == -1)
+ goto end;
- r = 0;
+ if (!*error)
+ r = 0;
end:
+ if (out)
+ g_object_unref(out);
+ if (in)
+ g_object_unref(in);
if (rec)
g_object_unref(rec);
if (query)