summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac1
-rw-r--r--libmsi/msipriv.h17
-rw-r--r--libmsi/msiquery.c3
-rw-r--r--libmsi/record.c47
-rw-r--r--tools/msibuild.c41
-rw-r--r--tools/msiinfo.c31
-rw-r--r--tools/sqldelim.c253
-rw-r--r--tools/sqldelim.h21
9 files changed, 347 insertions, 71 deletions
diff --git a/Makefile.am b/Makefile.am
index bb2516e..f89a867 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = libmsi tests .
-noinst_HEADERS = include/debug.h
+noinst_HEADERS = include/debug.h tools/sqldelim.h
dist_include_HEADERS = include/libmsi.h
AM_CPPFLAGS = -Iinclude -I$(srcdir)/include $(GLIB_CFLAGS) $(GSF_CFLAGS) $(UUID_CFLAGS)
@@ -10,7 +10,7 @@ AM_LDFLAGS = -Llibmsi
bin_PROGRAMS = msibuild msiinfo
-msibuild_SOURCES = tools/msibuild.c
+msibuild_SOURCES = tools/msibuild.c tools/sqldelim.c
msibuild_LDADD = -lmsi $(GLIB_LIBS) $(GSF_LIBS) $(UUID_LIBS)
msibuild_DEPENDENCIES = libmsi/libmsi.la
diff --git a/configure.ac b/configure.ac
index 47c03cf..bb8ca57 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,7 @@ AS_IF([test "$uuid" = yes],
[AC_MSG_WARN([libuuid not found, msibuild will generate packages without a GUID])])
LT_INIT([win32-dll disable-fast-install])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AM_MISSING_PROG([AUTOM4TE], [autom4te])
AC_CONFIG_TESTDIR([.])
diff --git a/libmsi/msipriv.h b/libmsi/msipriv.h
index 555075f..2c6a462 100644
--- a/libmsi/msipriv.h
+++ b/libmsi/msipriv.h
@@ -127,7 +127,6 @@ typedef struct LibmsiField
union
{
int iVal;
- intptr_t pVal;
char *szVal;
GsfInput *stream;
} u;
@@ -381,9 +380,7 @@ extern void _libmsi_record_destroy( LibmsiObject * );
extern unsigned _libmsi_record_set_gsf_input( LibmsiRecord *, unsigned, GsfInput *);
extern unsigned _libmsi_record_get_gsf_input( const LibmsiRecord *, unsigned, GsfInput **);
extern const char *_libmsi_record_get_string_raw( const LibmsiRecord *, unsigned );
-extern unsigned _libmsi_record_set_int_ptr( LibmsiRecord *, unsigned, intptr_t );
extern unsigned _libmsi_record_get_string( const LibmsiRecord *, unsigned, char *, unsigned *);
-extern intptr_t _libmsi_record_get_int_ptr( const LibmsiRecord *, unsigned );
extern unsigned _libmsi_record_save_stream( const LibmsiRecord *, unsigned, char *, unsigned *);
extern unsigned _libmsi_record_load_stream(LibmsiRecord *, unsigned, GsfInput *);
extern unsigned _libmsi_record_load_stream_from_file( LibmsiRecord *, unsigned, const char *);
@@ -444,31 +441,25 @@ static const char szData[] = "Data";
/* memory allocation macro functions */
-#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)))
-#define __WINE_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
-#else
-#define __WINE_ALLOC_SIZE(x)
-#endif
-
-static void *msi_alloc( size_t len ) __WINE_ALLOC_SIZE(1);
+static void *msi_alloc( size_t len ) G_GNUC_ALLOC_SIZE(1);
static inline void *msi_alloc( size_t len )
{
return malloc(len);
}
-static void *msi_alloc_zero( size_t len ) __WINE_ALLOC_SIZE(1);
+static void *msi_alloc_zero( size_t len ) G_GNUC_ALLOC_SIZE(1);
static inline void *msi_alloc_zero( size_t len )
{
return calloc(len, 1);
}
-static void *msi_realloc( void *mem, size_t len ) __WINE_ALLOC_SIZE(2);
+static void *msi_realloc( void *mem, size_t len ) G_GNUC_ALLOC_SIZE(2);
static inline void *msi_realloc( void *mem, size_t len )
{
return realloc(mem, len);
}
-static void *msi_realloc_zero( void *mem, size_t oldlen, size_t len ) __WINE_ALLOC_SIZE(3);
+static void *msi_realloc_zero( void *mem, size_t oldlen, size_t len ) G_GNUC_ALLOC_SIZE(3);
static inline void *msi_realloc_zero( void *mem, size_t oldlen, size_t len )
{
mem = realloc( mem, len );
diff --git a/libmsi/msiquery.c b/libmsi/msiquery.c
index 39b520c..a7182b6 100644
--- a/libmsi/msiquery.c
+++ b/libmsi/msiquery.c
@@ -309,10 +309,7 @@ LibmsiResult _libmsi_query_fetch(LibmsiQuery *query, LibmsiRecord **prec)
r = msi_view_get_row(query->db, view, query->row, prec);
if (r == LIBMSI_RESULT_SUCCESS)
- {
query->row ++;
- _libmsi_record_set_int_ptr(*prec, 0, (intptr_t)query);
- }
return r;
}
diff --git a/libmsi/record.c b/libmsi/record.c
index d1834fe..56e71db 100644
--- a/libmsi/record.c
+++ b/libmsi/record.c
@@ -31,7 +31,6 @@
#define LIBMSI_FIELD_TYPE_INT 1
#define LIBMSI_FIELD_TYPE_STR 3
#define LIBMSI_FIELD_TYPE_STREAM 4
-#define LIBMSI_FIELD_TYPE_INTPTR 5
static void _libmsi_free_field( LibmsiField *field )
{
@@ -39,7 +38,6 @@ static void _libmsi_free_field( LibmsiField *field )
{
case LIBMSI_FIELD_TYPE_NULL:
case LIBMSI_FIELD_TYPE_INT:
- case LIBMSI_FIELD_TYPE_INTPTR:
break;
case LIBMSI_FIELD_TYPE_STR:
msi_free( field->u.szVal);
@@ -131,9 +129,6 @@ unsigned _libmsi_record_copy_field( LibmsiRecord *in_rec, unsigned in_n,
case LIBMSI_FIELD_TYPE_INT:
out->u.iVal = in->u.iVal;
break;
- case LIBMSI_FIELD_TYPE_INTPTR:
- out->u.pVal = in->u.pVal;
- break;
case LIBMSI_FIELD_TYPE_STR:
str = strdup( in->u.szVal );
if ( !str )
@@ -155,32 +150,6 @@ unsigned _libmsi_record_copy_field( LibmsiRecord *in_rec, unsigned in_n,
return r;
}
-intptr_t _libmsi_record_get_int_ptr( const LibmsiRecord *rec, unsigned iField )
-{
- int ret;
-
- TRACE( "%p %d\n", rec, iField );
-
- if( iField > rec->count )
- return INTPTR_MIN;
-
- switch( rec->fields[iField].type )
- {
- case LIBMSI_FIELD_TYPE_INT:
- return rec->fields[iField].u.iVal;
- case LIBMSI_FIELD_TYPE_INTPTR:
- return rec->fields[iField].u.pVal;
- case LIBMSI_FIELD_TYPE_STR:
- if( expr_int_from_string( rec->fields[iField].u.szVal, &ret ) )
- return ret;
- return INTPTR_MIN;
- default:
- break;
- }
-
- return INTPTR_MIN;
-}
-
int libmsi_record_get_integer( const LibmsiRecord *rec, unsigned iField)
{
int ret = 0;
@@ -197,8 +166,6 @@ int libmsi_record_get_integer( const LibmsiRecord *rec, unsigned iField)
{
case LIBMSI_FIELD_TYPE_INT:
return rec->fields[iField].u.iVal;
- case LIBMSI_FIELD_TYPE_INTPTR:
- return rec->fields[iField].u.pVal;
case LIBMSI_FIELD_TYPE_STR:
if( expr_int_from_string( rec->fields[iField].u.szVal, &ret ) )
return ret;
@@ -231,20 +198,6 @@ LibmsiResult libmsi_record_clear_data( LibmsiRecord *rec )
return LIBMSI_RESULT_SUCCESS;
}
-unsigned _libmsi_record_set_int_ptr( LibmsiRecord *rec, unsigned iField, intptr_t pVal )
-{
- TRACE("%p %u %ld\n", rec, iField, pVal);
-
- if( iField > rec->count )
- return LIBMSI_RESULT_INVALID_PARAMETER;
-
- _libmsi_free_field( &rec->fields[iField] );
- rec->fields[iField].type = LIBMSI_FIELD_TYPE_INTPTR;
- rec->fields[iField].u.pVal = pVal;
-
- return LIBMSI_RESULT_SUCCESS;
-}
-
LibmsiResult libmsi_record_set_int( LibmsiRecord *rec, unsigned iField, int iVal )
{
TRACE("%p %u %d\n", rec, iField, iVal);
diff --git a/tools/msibuild.c b/tools/msibuild.c
index d5ae0fb..4baf2d3 100644
--- a/tools/msibuild.c
+++ b/tools/msibuild.c
@@ -30,6 +30,8 @@
#include <uuid.h>
#endif
+#include "sqldelim.h"
+
static void init_suminfo(LibmsiSummaryInfo *si)
{
libmsi_summary_info_set_property(si, MSI_PID_TITLE,
@@ -184,7 +186,27 @@ static int add_stream(const char *stream, const char *file)
libmsi_unref(rec);
libmsi_query_close(query);
libmsi_unref(query);
- return 0;
+ return r;
+}
+
+static int do_query(const char *sql, void *opaque)
+{
+ LibmsiResult r;
+ LibmsiQuery *query;
+
+ r = libmsi_database_open_query(db, sql, &query);
+ if (r != LIBMSI_RESULT_SUCCESS) {
+ fprintf(stderr, "failed to open query (%u)\n", r);
+ return r;
+ }
+
+ r = libmsi_query_execute(query, NULL);
+ if (r != LIBMSI_RESULT_SUCCESS)
+ fprintf(stderr, "failed to execute query (%u)\n", r);
+
+ libmsi_query_close(query);
+ libmsi_unref(query);
+ return r;
}
static void show_usage(void)
@@ -193,6 +215,7 @@ static void show_usage(void)
"Usage: msibuild MSIFILE [OPTION]...\n"
"Options:\n"
" -s name [author] [template] [uuid] Set summary information.\n"
+ " -q query Execute SQL query/queries.\n"
" -i table1.idt Import one table into the database.\n"
" -a stream file Add 'stream' to storage with contents of 'file'.\n"
"\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
@@ -246,6 +269,22 @@ int main(int argc, char *argv[])
case 'i':
do {
ret = import_table(argv[1]);
+ if (ret) {
+ break;
+ }
+ argc--, argv++;
+ } while (argv[1] && argv[1][0] != '-');
+ argc--, argv++;
+ break;
+ case 'q':
+ do {
+ ret = sql_get_statement(argv[1], do_query, NULL);
+ if (ret == 0) {
+ ret = sql_get_statement("", do_query, NULL);
+ }
+ if (ret) {
+ break;
+ }
argc--, argv++;
} while (argv[1] && argv[1][0] != '-');
argc--, argv++;
diff --git a/tools/msiinfo.c b/tools/msiinfo.c
index 4669932..62b7f89 100644
--- a/tools/msiinfo.c
+++ b/tools/msiinfo.c
@@ -217,6 +217,8 @@ static int cmd_streams(struct Command *cmd, int argc, char **argv)
libmsi_unref(query);
libmsi_unref(db);
+
+ return 0;
}
static int cmd_tables(struct Command *cmd, int argc, char **argv)
@@ -251,6 +253,8 @@ static int cmd_tables(struct Command *cmd, int argc, char **argv)
libmsi_unref(query);
libmsi_unref(db);
+
+ return 0;
}
static void print_suminfo(LibmsiSummaryInfo *si, int prop, const char *name)
@@ -339,6 +343,8 @@ static int cmd_suminfo(struct Command *cmd, int argc, char **argv)
libmsi_unref(db);
libmsi_unref(si);
+
+ return 0;
}
static void full_write(int fd, char *buf, size_t sz)
@@ -412,6 +418,8 @@ static int cmd_extract(struct Command *cmd, int argc, char **argv)
libmsi_unref(rec);
libmsi_unref(query);
libmsi_unref(db);
+
+ return 0;
}
static unsigned export_create_table(const char *table,
@@ -426,6 +434,12 @@ static unsigned export_create_table(const char *table,
char type[30], name[100], size[20], extra[30];
unsigned sz;
+ if (!strcmp(table, "_Tables") ||
+ !strcmp(table, "_Columns") ||
+ !strcmp(table, "_Streams") ||
+ !strcmp(table, "_Storages")) {
+ return 0;
+ }
printf("CREATE TABLE `%s` (", table);
for (i = 1; i <= num_columns; i++)
@@ -522,6 +536,10 @@ static unsigned export_insert(const char *table,
printf("INSERT INTO `%s` (", table);
for (i = 1; i <= num_columns; i++)
{
+ if (libmsi_record_is_null(vals, i)) {
+ continue;
+ }
+
sz = sizeof(name);
r = libmsi_record_get_string(names, i, name, &sz);
if (r) {
@@ -537,15 +555,14 @@ static unsigned export_insert(const char *table,
printf(") VALUES (");
for (i = 1; i <= num_columns; i++)
{
- if (i > 1) {
- printf(", ");
- }
-
if (libmsi_record_is_null(vals, i)) {
- printf("NULL");
continue;
}
+ if (i > 1) {
+ printf(", ");
+ }
+
sz = sizeof(type);
r = libmsi_record_get_string(types, i, type, &sz);
if (r) {
@@ -678,6 +695,8 @@ static int cmd_export(struct Command *cmd, int argc, char **argv)
}
libmsi_unref(db);
+
+ return 0;
}
static int cmd_version(struct Command *cmd, int argc, char **argv)
@@ -696,6 +715,8 @@ static int cmd_help(struct Command *cmd, int argc, char **argv)
}
usage(stdout);
+
+ return 0;
}
static struct Command cmds[] = {
diff --git a/tools/sqldelim.c b/tools/sqldelim.c
new file mode 100644
index 0000000..1eebf09
--- /dev/null
+++ b/tools/sqldelim.c
@@ -0,0 +1,253 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** A tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens, groups them back into statements, and passes the
+** statements up to a user-defined callback.
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <glib.h>
+
+/*
+** All the keywords of the SQL language are stored as in a hash
+** table composed of instances of the following structure.
+*/
+typedef struct Keyword Keyword;
+struct Keyword {
+ const uint8_t *zName; /* The keyword name */
+};
+
+#define MAX_TOKEN_LEN 11
+
+/*
+** These are the keywords that begin a new SQL statement.
+** They MUST be in alphabetical order
+*/
+static const Keyword aKeywordTable[] = {
+ { "ALTER" },
+ { "CREATE" },
+ { "DELETE" },
+ { "DROP" },
+ { "INSERT" },
+ { "SELECT" },
+ { "UPDATE" },
+};
+
+#define KEYWORD_COUNT (sizeof aKeywordTable / sizeof (Keyword))
+
+/*
+** Comparison function for binary search.
+*/
+static int sql_compare_keyword(const void *m1, const void *m2){
+ const uint8_t *p = m1;
+ const Keyword *k = m2;
+ const uint8_t *q = k->zName;
+
+ for (; *p; p++, q++) {
+ uint8_t c;
+ if ((uint16_t) *p > 127)
+ return 1;
+ c = *p;
+ if (c >= 'a' && c <= 'z')
+ c ^= 'A' ^ 'a';
+ if (c != *q)
+ return (unsigned)c - (unsigned)*q;
+ }
+
+ return (unsigned)*p - (unsigned)*q;
+}
+
+/*
+** This function looks up an identifier to determine if it is a
+** keyword. If it is a keyword, the token code of that keyword is
+** returned. If the input is not a keyword, TK_ID is returned.
+*/
+static int sqlite_find_keyword(const char *z, int n)
+{
+ char str[MAX_TOKEN_LEN + 1];
+ Keyword *r;
+
+ if (n > MAX_TOKEN_LEN)
+ return false;
+
+ memcpy(str, z, n);
+ str[n] = 0;
+ r = bsearch(str, aKeywordTable, KEYWORD_COUNT, sizeof (Keyword), sql_compare_keyword);
+ return r != NULL;
+}
+
+
+/*
+** If X is a character that can be used in an identifier then
+** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0.
+**
+** In this implementation, an identifier can be a string of
+** alphabetic characters, digits, and "_" plus any character
+** with the high-order bit set. The latter rule means that
+** any sequence of UTF-8 characters or characters taken from
+** an extended ISO8859 character set can form an identifier.
+*/
+static const uint8_t isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 2x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
+};
+
+
+/*
+** Return the length of the token that begins at z[0]. Return
+** -1 if the token is (or might be) incomplete. Store the token
+** type in *tokenType before returning.
+*/
+static int sql_skip_token(const char **p, bool *cont)
+{
+ int i = 1;
+ const uint8_t *z = (uint8_t *) *p;
+ bool get_keyword = *cont;
+
+ *cont = true;
+ switch (*z) {
+ case ' ': case '\t': case '\n': case '\f':
+ while (isspace(z[i]) && z[i] != '\r') i++;
+ *p += i;
+ return false;
+ case '-':
+ case '(':
+ case ')':
+ case '*':
+ case '=':
+ case '<':
+ case '>':
+ case '!':
+ case '?':
+ case ',':
+ case '.':
+ *p += 1;
+ return false;
+ case '`': case '\'': {
+ int delim = z[0];
+ while (z[i])
+ if (z[i++] == delim)
+ break;
+ *p += i;
+ return false;
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ while (isdigit(z[i])) i++;
+ *p += i;
+ return false;
+ case '[':
+ while (z[i] && z[i-1] != ']') i++;
+ *p += i;
+ return false;
+ default:
+ if (!isIdChar[*z]) {
+ *p += 1;
+ return true;
+ }
+ while (isIdChar[z[i]]) i++;
+ if (get_keyword && sqlite_find_keyword(z, i)) {
+ return true;
+ } else {
+ /* Do not recognize a keyword at the beginning of the next chunk. */
+ if (!z[i]) {
+ *cont = false;
+ }
+ *p += i;
+ return false;
+ }
+ }
+}
+
+int sql_get_statement(const char *start,
+ int (*fn)(const char *stmt, void *opaque),
+ void *opaque)
+{
+ static GString str;
+ static bool cont = false;
+
+ const char *p = start;
+ char *stmt;
+ bool done;
+ int ret = 0;
+
+ /* Final part? Build a statement with what's left. */
+ if (!*p) {
+ goto stmt;
+ }
+
+ while (*p) {
+ start = p;
+ /* A semicolon is not part of the SQL syntax, skip it and conclude
+ * this statement.
+ */
+ if (*p == ';') {
+ done = true;
+ p++;
+ } else {
+ done = sql_skip_token(&p, &cont);
+ g_string_append_len(&str, start, p - start);
+ }
+
+ if (done) {
+stmt:
+ cont = false;
+ stmt = g_strndup(str.str, str.len);
+ g_string_erase(&str, 0, str.len);
+ if (stmt[0]) {
+ ret = fn(stmt, opaque);
+ }
+ free(stmt);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+#if 0
+int main()
+{
+ uint8_t line[100], *stmt;
+ const uint8_t *p;
+
+ while (fgets(line, sizeof(line), stdin)) {
+ sql_get_statement(line, puts);
+ }
+ sql_get_statement("", puts);
+}
+#endif
diff --git a/tools/sqldelim.h b/tools/sqldelim.h
new file mode 100644
index 0000000..ade9de0
--- /dev/null
+++ b/tools/sqldelim.h
@@ -0,0 +1,21 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** A tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens and sends those tokens one-by-one over to the
+** parser for analysis.
+*/
+
+int sql_get_statement(const char *start,
+ int (*fn)(const char *stmt, void *opaque),
+ void *opaque);