diff options
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/libmsi-record.h | 11 | ||||
-rw-r--r-- | libmsi/Makefile.am | 1 | ||||
-rw-r--r-- | libmsi/libmsi-istream.c | 155 | ||||
-rw-r--r-- | libmsi/libmsi-istream.h | 47 | ||||
-rw-r--r-- | libmsi/libmsi-record.c | 123 | ||||
-rw-r--r-- | libmsi/msipriv.h | 5 | ||||
-rw-r--r-- | tests/testdatabase.c | 63 | ||||
-rw-r--r-- | tests/testrecord.c | 89 | ||||
-rw-r--r-- | tools/msiinfo.c | 25 |
11 files changed, 387 insertions, 135 deletions
@@ -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) |