diff options
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | libmsi/msipriv.h | 17 | ||||
-rw-r--r-- | libmsi/msiquery.c | 3 | ||||
-rw-r--r-- | libmsi/record.c | 47 | ||||
-rw-r--r-- | tools/msibuild.c | 41 | ||||
-rw-r--r-- | tools/msiinfo.c | 31 | ||||
-rw-r--r-- | tools/sqldelim.c | 253 | ||||
-rw-r--r-- | tools/sqldelim.h | 21 |
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); |