From 068b4ec7d49209e7d7cec37be3da5f40f26f1631 Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Fri, 19 Mar 2004 01:16:36 +0000 Subject: [PATCH] First go at write support. --- dlls/msi/create.c | 105 ++++++++++++++++--- dlls/msi/distinct.c | 2 + dlls/msi/msi.c | 2 + dlls/msi/msipriv.h | 21 +++- dlls/msi/order.c | 2 + dlls/msi/query.h | 2 +- dlls/msi/select.c | 2 + dlls/msi/sql.y | 39 +++++-- dlls/msi/string.c | 135 ++++++++++++++++++------ dlls/msi/table.c | 244 +++++++++++++++++++++++++++++++++++++++----- dlls/msi/tokenize.c | 1 + dlls/msi/where.c | 2 + 12 files changed, 479 insertions(+), 78 deletions(-) diff --git a/dlls/msi/create.c b/dlls/msi/create.c index 806188fe7a6..1222832ff9b 100644 --- a/dlls/msi/create.c +++ b/dlls/msi/create.c @@ -1,7 +1,7 @@ /* * Implementation of the Microsoft Installer (msi.dll) * - * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2002-2004 Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -59,30 +59,107 @@ static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT static UINT CREATE_execute( struct tagMSIVIEW *view, MSIHANDLE record ) { MSICREATEVIEW *cv = (MSICREATEVIEW*)view; - create_col_info *c; + create_col_info *col; + UINT r, nField, row, table_val, column_val; + const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; + const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; + MSIVIEW *tv = NULL; - FIXME("%p %ld\n", cv, record); - - FIXME("Table %s (%s)\n", debugstr_w(cv->name), + TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name), cv->bIsTemp?"temporary":"permanent"); - for( c = cv->col_info; c; c = c->next ) - { - FIXME("Column %s type %04x\n", debugstr_w(c->colname), c->type ); - } + /* only add tables that don't exist already */ + if( TABLE_Exists(cv->db, cv->name ) ) + return ERROR_BAD_QUERY_SYNTAX; - return ERROR_SUCCESS; - return ERROR_FUNCTION_FAILED; + /* add the name to the _Tables table */ + table_val = msi_addstringW( cv->db->strings, 0, cv->name, -1, 1 ); + TRACE("New string %s -> %d\n", debugstr_w( cv->name ), table_val ); + if( table_val < 0 ) + return ERROR_FUNCTION_FAILED; + + r = TABLE_CreateView( cv->db, szTables, &tv ); + TRACE("CreateView returned %x\n", r); + if( r ) + return r; + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + return r; + + row = -1; + r = tv->ops->insert_row( tv, &row ); + TRACE("insert_row returned %x\n", r); + if( r ) + goto err; + + r = tv->ops->set_int( tv, row, 1, table_val ); + if( r ) + goto err; + tv->ops->delete( tv ); + tv = NULL; + + /* add each column to the _Columns table */ + r = TABLE_CreateView( cv->db, szColumns, &tv ); + if( r ) + return r; + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + return r; + + row = -1; + r = tv->ops->insert_row( tv, &row ); + if( r ) + goto err; + + /* + * need to set the table, column number, col name and type + * for each column we enter in the table + */ + nField = 1; + for( col = cv->col_info; col; col = col->next ) + { + column_val = msi_addstringW( cv->db->strings, 0, col->colname, -1, 1 ); + TRACE("New string %s -> %d\n", debugstr_w( col->colname ), column_val ); + if( column_val < 0 ) + break; + + r = tv->ops->set_int( tv, row, 1, table_val ); + if( r ) + break; + + r = tv->ops->set_int( tv, row, 2, 0x8000|nField ); + if( r ) + break; + + r = tv->ops->set_int( tv, row, 3, column_val ); + if( r ) + break; + + r = tv->ops->set_int( tv, row, 4, 0x8000|col->type ); + if( r ) + break; + } + if( !col ) + r = ERROR_SUCCESS; + +err: + /* FIXME: remove values from the string table on error */ + if( tv ) + tv->ops->delete( tv ); + return r; } static UINT CREATE_close( struct tagMSIVIEW *view ) { MSICREATEVIEW *cv = (MSICREATEVIEW*)view; - FIXME("%p\n", cv ); + TRACE("%p\n", cv); return ERROR_SUCCESS; - return ERROR_FUNCTION_FAILED; } static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) @@ -138,6 +215,8 @@ static UINT CREATE_delete( struct tagMSIVIEW *view ) MSIVIEWOPS create_ops = { CREATE_fetch_int, + NULL, + NULL, CREATE_execute, CREATE_close, CREATE_get_dimensions, diff --git a/dlls/msi/distinct.c b/dlls/msi/distinct.c index 7dc3b0104de..b6ed59c9b53 100644 --- a/dlls/msi/distinct.c +++ b/dlls/msi/distinct.c @@ -250,6 +250,8 @@ static UINT DISTINCT_delete( struct tagMSIVIEW *view ) MSIVIEWOPS distinct_ops = { DISTINCT_fetch_int, + NULL, + NULL, DISTINCT_execute, DISTINCT_close, DISTINCT_get_dimensions, diff --git a/dlls/msi/msi.c b/dlls/msi/msi.c index 3301bc470f0..e9c204948b1 100644 --- a/dlls/msi/msi.c +++ b/dlls/msi/msi.c @@ -211,6 +211,8 @@ UINT WINAPI MsiOpenDatabaseW( { r = StgCreateDocfile( szDBPath, STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg); + if( r == ERROR_SUCCESS ) + r = init_string_table( stg ); } else if( szPersist == MSIDBOPEN_TRANSACT ) { diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 72eb756d08b..595c385512d 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -64,6 +64,19 @@ typedef struct tagMSIVIEWOPS */ UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val ); + /* + * get_int - sets one integer at {row,col} in the table + * + * Similar semantics to fetch_int + */ + UINT (*set_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT val ); + + /* + * Inserts a new, blank row into the database + * *row receives the number of the new row + */ + UINT (*insert_row)( struct tagMSIVIEW *, UINT *row ); + /* * execute - loads the underlying data into memory so it can be read */ @@ -154,14 +167,18 @@ extern UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table); extern UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table); extern UINT load_string_table( MSIDATABASE *db ); extern UINT MSI_CommitTables( MSIDATABASE *db ); +extern HRESULT init_string_table( IStorage *stg ); /* string table functions */ -extern BOOL msi_addstring( string_table *st, UINT string_no, CHAR *data, UINT len, UINT refcount ); +extern BOOL msi_addstring( string_table *st, UINT string_no, const CHAR *data, UINT len, UINT refcount ); +extern BOOL msi_addstringW( string_table *st, UINT string_no, const WCHAR *data, UINT len, UINT refcount ); extern UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ); extern UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz ); + extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid); extern UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id ); +extern UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id ); extern string_table *msi_init_stringtable( int entries ); extern VOID msi_destroy_stringtable( string_table *st ); extern UINT msi_string_count( string_table *st ); @@ -170,4 +187,6 @@ extern UINT msi_string_totalsize( string_table *st ); UINT VIEW_find_column( MSIVIEW *view, LPWSTR name, UINT *n ); +extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ); + #endif /* __WINE_MSI_PRIVATE__ */ diff --git a/dlls/msi/order.c b/dlls/msi/order.c index 14b138203f1..3cea1c01c35 100644 --- a/dlls/msi/order.c +++ b/dlls/msi/order.c @@ -253,6 +253,8 @@ static UINT ORDER_delete( struct tagMSIVIEW *view ) MSIVIEWOPS order_ops = { ORDER_fetch_int, + NULL, + NULL, ORDER_execute, ORDER_close, ORDER_get_dimensions, diff --git a/dlls/msi/query.h b/dlls/msi/query.h index 685052229ee..2870bc3c3b8 100644 --- a/dlls/msi/query.h +++ b/dlls/msi/query.h @@ -79,7 +79,7 @@ typedef struct _create_col_info UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phView); -UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view ); +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ); UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); UINT SELECT_AddColumn( MSIVIEW *select, LPWSTR name ); diff --git a/dlls/msi/select.c b/dlls/msi/select.c index c7b50b63871..7a50c95ca92 100644 --- a/dlls/msi/select.c +++ b/dlls/msi/select.c @@ -152,6 +152,8 @@ static UINT SELECT_delete( struct tagMSIVIEW *view ) MSIVIEWOPS select_ops = { SELECT_fetch_int, + NULL, + NULL, SELECT_execute, SELECT_close, SELECT_get_dimensions, diff --git a/dlls/msi/sql.y b/dlls/msi/sql.y index 1e8b8dc1477..292fbe35d63 100644 --- a/dlls/msi/sql.y +++ b/dlls/msi/sql.y @@ -3,7 +3,7 @@ /* * Implementation of the Microsoft Installer (msi.dll) * - * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2002-2004 Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -98,10 +98,12 @@ static struct expr * EXPR_sval( LPWSTR string ); %token TK_HAVING TK_HOLD %token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY %token TK_ID -%token TK_INSERT TK_INSTEAD TK_INT TK_INTEGER TK_INTERSECT TK_INTO TK_IS TK_ISNULL +%token TK_INSERT TK_INSTEAD TK_INT TK_INTEGER TK_INTERSECT TK_INTO TK_IS +%token TK_ISNULL %token TK_JOIN TK_JOIN_KW %token TK_KEY %token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT +%token TK_LOCALIZABLE %token TK_MATCH TK_MINUS %token TK_NE TK_NOT TK_NOTNULL TK_NULL %token TK_OBJECT TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER @@ -129,7 +131,7 @@ static struct expr * EXPR_sval( LPWSTR string ); %type selcollist %type from unorderedsel oneselect onequery onecreate %type expr val column_val -%type column_type data_type data_count +%type column_type data_type data_type_l data_count %type column_def table_def %% @@ -183,15 +185,24 @@ table_def: column_def: column_def TK_COMMA column column_type { - $$ = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ ); - if( $$ ) + create_col_info *ci; + + for( ci = $1; ci->next; ci = ci->next ) + ; + + ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ ); + if( ci->next ) { - $$->colname = $3; - $$->type = $4; - $$->next = $1; + ci->next->colname = $3; + ci->next->type = $4; + ci->next->next = NULL; } else if( $1 ) + { HeapFree( GetProcessHeap(), 0, $1 ); + $1 = NULL; + } + $$ = $1; } | column column_type { @@ -206,6 +217,18 @@ column_def: ; column_type: + data_type_l + { + $$ = $1; + } + | data_type_l TK_LOCALIZABLE + { + FIXME("LOCALIZABLE ignored\n"); + $$ = $1; + } + ; + +data_type_l: data_type { $$ |= MSITYPE_NULLABLE; diff --git a/dlls/msi/string.c b/dlls/msi/string.c index a1345f25e20..610cd72bc55 100644 --- a/dlls/msi/string.c +++ b/dlls/msi/string.c @@ -1,7 +1,7 @@ /* * Implementation of the Microsoft Installer (msi.dll) * - * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2002-2004, Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,7 +50,7 @@ struct string_table msistring *strings; /* an array of strings (in the tree) */ }; -static int msistring_makehash( char *str ) +static int msistring_makehash( const char *str ) { int hash = 0; @@ -78,7 +78,7 @@ string_table *msi_init_stringtable( int entries ) return NULL; } st->count = entries; - st->freeslot = 0; + st->freeslot = 1; return st; } @@ -98,18 +98,28 @@ VOID msi_destroy_stringtable( string_table *st ) static int st_find_free_entry( string_table *st ) { - int i; + int i, sz; + msistring *p; for( i = st->freeslot; i < st->count; i++ ) if( !st->strings[i].refcount ) return i; - for( i = 0; i < st->freeslot; i++ ) + for( i = 1; i < st->freeslot; i++ ) if( !st->strings[i].refcount ) return i; - FIXME("dynamically resize\n"); - - return -1; + /* dynamically resize */ + sz = st->count + 1 + st->count/2; + p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, + st->strings, sz*sizeof(msistring) ); + if( !p ) + return -1; + st->strings = p; + st->freeslot = st->count; + st->count = sz; + if( st->strings[st->freeslot].refcount ) + ERR("oops. expected freeslot to be free...\n"); + return st->freeslot; } static void st_mark_entry_used( string_table *st, int n ) @@ -119,15 +129,28 @@ static void st_mark_entry_used( string_table *st, int n ) st->freeslot = n + 1; } -int msi_addstring( string_table *st, UINT string_no, CHAR *data, UINT len, UINT refcount ) +int msi_addstring( string_table *st, UINT n, const CHAR *data, UINT len, UINT refcount ) { - int n; - /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */ - n = st_find_free_entry( st ); - if( n < 0 ) - return -1; + if( !data[0] ) + return 0; + if( n > 0 ) + { + if( st->strings[n].refcount ) + return -1; + } + else + { + if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) ) + { + st->strings[n].refcount++; + return n; + } + n = st_find_free_entry( st ); + if( n < 0 ) + return -1; + } /* allocate a new string */ if( len < 0 ) @@ -145,6 +168,47 @@ int msi_addstring( string_table *st, UINT string_no, CHAR *data, UINT len, UINT return n; } +int msi_addstringW( string_table *st, UINT n, const WCHAR *data, UINT len, UINT refcount ) +{ + int sz; + + /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */ + + if( !data[0] ) + return 0; + if( n > 0 ) + { + if( st->strings[n].refcount ) + return -1; + } + else + { + if( ERROR_SUCCESS == msi_string2id( st, data, &n ) ) + { + st->strings[n].refcount++; + return n; + } + n = st_find_free_entry( st ); + if( n < 0 ) + return -1; + } + + /* allocate a new string */ + sz = WideCharToMultiByte( CP_UTF8, 0, data, len, NULL, 0, NULL, NULL ); + st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, sz + 1 ); + if( !st->strings[n].str ) + return -1; + WideCharToMultiByte( CP_UTF8, 0, data, len, + st->strings[n].str, sz, NULL, NULL ); + st->strings[n].str[sz] = 0; + st->strings[n].refcount = 1; + st->strings[n].hash = msistring_makehash( st->strings[n].str ); + + st_mark_entry_used( st, n ); + + return n; +} + UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ) { UINT len; @@ -154,7 +218,7 @@ UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ) if( string_no >= st->count ) return ERROR_FUNCTION_FAILED; - if( !st->strings[string_no].refcount ) + if( string_no && !st->strings[string_no].refcount ) return ERROR_FUNCTION_FAILED; str = st->strings[string_no].str; @@ -182,7 +246,7 @@ UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz ) if( string_no >= st->count ) return ERROR_FUNCTION_FAILED; - if( !st->strings[string_no].refcount ) + if( string_no && !st->strings[string_no].refcount ) return ERROR_FUNCTION_FAILED; str = st->strings[string_no].str; @@ -202,25 +266,13 @@ UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz ) return ERROR_SUCCESS; } -UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id ) +UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id ) { - DWORD sz; - UINT i, r = ERROR_INVALID_PARAMETER; - LPSTR str; int hash; - - TRACE("Finding string %s in string table\n", debugstr_w(buffer) ); - - sz = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL ); - if( sz <= 0 ) - return r; - str = HeapAlloc( GetProcessHeap(), 0, sz ); - if( !str ) - return ERROR_NOT_ENOUGH_MEMORY; - WideCharToMultiByte( CP_ACP, 0, buffer, -1, str, sz, NULL, NULL ); + UINT i, r = ERROR_INVALID_PARAMETER; hash = msistring_makehash( str ); - for( i=0; icount; i++) + for( i=0; icount; i++ ) { if( ( st->strings[i].hash == hash ) && !strcmp( st->strings[i].str, str ) ) @@ -231,12 +283,33 @@ UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id ) } } + return r; +} + +UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id ) +{ + DWORD sz; + UINT r = ERROR_INVALID_PARAMETER; + LPSTR str; + + TRACE("Finding string %s in string table\n", debugstr_w(buffer) ); + + sz = WideCharToMultiByte( CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL ); + if( sz <= 0 ) + return r; + str = HeapAlloc( GetProcessHeap(), 0, sz ); + if( !str ) + return ERROR_NOT_ENOUGH_MEMORY; + WideCharToMultiByte( CP_UTF8, 0, buffer, -1, str, sz, NULL, NULL ); + + r = msi_string2idA( st, str, id ); if( str ) HeapFree( GetProcessHeap(), 0, str ); return r; } + UINT msi_string_count( string_table *st ) { return st->count; diff --git a/dlls/msi/table.c b/dlls/msi/table.c index 737e3f58a6d..dd7c51eb850 100644 --- a/dlls/msi/table.c +++ b/dlls/msi/table.c @@ -1,7 +1,7 @@ /* * Implementation of the Microsoft Installer (msi.dll) * - * Copyright 2002 Mike McCormack for CodeWeavers + * Copyright 2002-2004 Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,6 +18,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + #include #include "windef.h" @@ -237,17 +240,25 @@ static UINT write_stream_data( IStorage *stg, LPCWSTR stname, ULARGE_INTEGER size; LARGE_INTEGER pos; - r = IStorage_OpenStream( stg, stname, NULL, + WCHAR encname[0x20]; + + encode_streamname(TRUE, stname, encname); + r = IStorage_OpenStream( stg, encname, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + if( FAILED(r) ) + { + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + } if( FAILED( r ) ) { - WARN("open stream failed r = %08lx - empty table?\n",r); + ERR("open stream failed r = %08lx\n",r); return ret; } size.QuadPart = sz; r = IStream_SetSize( stm, size ); - if( FAILED( r ) || ( count != sz ) ) + if( FAILED( r ) ) { ERR("Failed to SetSize\n"); goto end; @@ -255,7 +266,7 @@ static UINT write_stream_data( IStorage *stg, LPCWSTR stname, pos.QuadPart = 0; r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); - if( FAILED( r ) || ( count != sz ) ) + if( FAILED( r ) ) { ERR("Failed to Seek\n"); goto end; @@ -300,6 +311,7 @@ UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable) last_col = &cols[num_cols-1]; row_size = last_col->offset + bytes_per_column( last_col ); + t->row_count = 0; t->data = NULL; lstrcpyW( t->name, name ); t->ref_count = 1; @@ -488,10 +500,91 @@ UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable) return ERROR_SUCCESS; } -UINT save_table( MSIDATABASE *db, MSITABLE *ptable ) +UINT save_table( MSIDATABASE *db, MSITABLE *t ) { + USHORT *rawdata = NULL, *p; + UINT rawsize, r, i, j, row_size, num_cols = 0; + MSICOLUMNINFO *cols, *last_col; + + TRACE("Saving %s\n", debugstr_w( t->name ) ); + + r = table_get_column_info( db, t->name, &cols, &num_cols ); + if( r != ERROR_SUCCESS ) + return r; - return ERROR_SUCCESS; + last_col = &cols[num_cols-1]; + row_size = last_col->offset + bytes_per_column( last_col ); + + rawsize = t->row_count * row_size; + rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize ); + if( !rawdata ) + return ERROR_NOT_ENOUGH_MEMORY; + + p = rawdata; + for( i=0; irow_count; j++ ) + { + UINT offset = cols[i].offset; + + *p++ = t->data[j][offset/2]; + if( 4 == bytes_per_column( &cols[i] ) ) + *p++ = t->data[j][offset/2+1]; + } + } + + TRACE("writing %d bytes\n", rawsize); + r = write_stream_data( db->storage, t->name, rawdata, rawsize ); + + HeapFree( GetProcessHeap(), 0, rawdata ); + + return r; +} + +HRESULT init_string_table( IStorage *stg ) +{ + HRESULT r; + const WCHAR szStringData[] = { + '_','S','t','r','i','n','g','D','a','t','a',0 }; + const WCHAR szStringPool[] = { + '_','S','t','r','i','n','g','P','o','o','l',0 }; + USHORT zero[2] = { 0, 0 }; + ULONG count = 0; + IStream *stm = NULL; + WCHAR encname[0x20]; + + encode_streamname(TRUE, szStringData, encname); + + /* create the StringData stream... add the zero string to it*/ + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + if( r ) + { + TRACE("Failed\n"); + return r; + } + + r = IStream_Write(stm, zero, sizeof zero, &count ); + IStream_Release( stm ); + + if( FAILED( r ) || ( count != sizeof zero ) ) + { + TRACE("Failed\n"); + return E_FAIL; + } + + /* create the StringPool stream... make it zero length */ + encode_streamname(TRUE, szStringPool, encname); + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + if( r ) + { + TRACE("Failed\n"); + return E_FAIL; + } + IStream_Release( stm ); + + return r; } UINT load_string_table( MSIDATABASE *db ) @@ -499,7 +592,7 @@ UINT load_string_table( MSIDATABASE *db ) CHAR *data; USHORT *pool; UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0; - DWORD i, count, offset, len; + DWORD i, count, offset, len, n; const WCHAR szStringData[] = { '_','S','t','r','i','n','g','D','a','t','a',0 }; const WCHAR szStringPool[] = { @@ -521,11 +614,15 @@ UINT load_string_table( MSIDATABASE *db ) count = poolsize/4; db->strings = msi_init_stringtable( count ); + if( pool[0] || pool[1] ) + ERR("The first string should be nul, but isn't\n"); offset = 0; - for( i=0; istrings, i, data+offset, len, pool[i*2+1] ); + n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] ); + if( n != i ) + ERR("Failed to add string %ld\n", i ); offset += len; } @@ -544,7 +641,7 @@ end: UINT save_string_table( MSIDATABASE *db ) { - UINT i, count, datasize, poolsize, sz, remaining, r; + UINT i, count, datasize, poolsize, sz, used, r; UINT ret = ERROR_FUNCTION_FAILED; const WCHAR szStringData[] = { '_','S','t','r','i','n','g','D','a','t','a',0 }; @@ -553,6 +650,8 @@ UINT save_string_table( MSIDATABASE *db ) CHAR *data = NULL; USHORT *pool = NULL; + TRACE("\n"); + /* construct the new table in memory first */ count = msi_string_count( db->strings ); poolsize = count*2*sizeof(USHORT); @@ -560,39 +659,51 @@ UINT save_string_table( MSIDATABASE *db ) pool = HeapAlloc( GetProcessHeap(), 0, poolsize ); if( ! pool ) + { + ERR("Failed to alloc pool %d bytes\n", poolsize ); goto err; + } data = HeapAlloc( GetProcessHeap(), 0, datasize ); if( ! data ) + { + ERR("Failed to alloc data %d bytes\n", poolsize ); goto err; + } - remaining = datasize; + used = 0; for( i=0; istrings, i, data+remaining, &sz ); + sz = datasize - used; + r = msi_id2stringA( db->strings, i, data+used, &sz ); if( r != ERROR_SUCCESS ) - goto err; + { + sz = 0; + } + else + sz--; pool[ i*2 ] = sz; pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i ); - remaining -= sz; - if( remaining < 0 ) + used += sz; + if( used > datasize ) { - ERR("oops remaining %d < 0\n", remaining); + ERR("oops overran %d >= %d\n", used, datasize); goto err; } } - if( remaining != 0 ) + if( used != datasize ) { - ERR("oops remaining %d != 0\n", remaining); + ERR("oops used %d != datasize %d\n", used, datasize); goto err; } /* write the streams */ r = write_stream_data( db->storage, szStringData, data, datasize ); + TRACE("Wrote StringData r=%08x\n", r); if( r ) goto err; r = write_stream_data( db->storage, szStringPool, pool, poolsize ); + TRACE("Wrote StringPool r=%08x\n", r); if( r ) goto err; @@ -777,7 +888,7 @@ BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ) r = msi_string2id( db->strings, name, &table_id ); if( r != ERROR_SUCCESS ) { - ERR("Couldn't find id for %s\n", debugstr_w(name)); + TRACE("Couldn't find id for %s\n", debugstr_w(name)); return FALSE; } @@ -799,6 +910,8 @@ BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ) if (i!=count) return TRUE; + ERR("Searched %d tables, but %d was not found\n", count, table_id ); + return FALSE; } @@ -844,7 +957,8 @@ static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT * { case 4: offset = tv->columns[col-1].offset/2; - *val = tv->table->data[row][offset] + (tv->table->data[row][offset + 1] << 16); + *val = tv->table->data[row][offset] + + (tv->table->data[row][offset + 1] << 16); break; case 2: offset = tv->columns[col-1].offset/2; @@ -860,6 +974,75 @@ static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT * return ERROR_SUCCESS; } +static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT offset, num_rows, n; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + offset = row + (tv->columns[col-1].offset/2) * num_rows; + n = bytes_per_column( &tv->columns[col-1] ); + switch( n ) + { + case 4: + offset = tv->columns[col-1].offset/2; + tv->table->data[row][offset] = val & 0xffff; + tv->table->data[row][offset + 1] = (val>>16)&0xffff; + break; + case 2: + offset = tv->columns[col-1].offset/2; + tv->table->data[row][offset] = val; + break; + default: + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + return ERROR_SUCCESS; +} + +UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + USHORT **p, *row; + UINT sz; + + TRACE("%p\n", view); + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size ); + if( !row ) + return ERROR_NOT_ENOUGH_MEMORY; + + sz = (tv->table->row_count + 1) * sizeof (UINT*); + if( tv->table->data ) + p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz ); + else + p = HeapAlloc( GetProcessHeap(), 0, sz ); + if( !p ) + return ERROR_NOT_ENOUGH_MEMORY; + + tv->table->data = p; + tv->table->data[tv->table->row_count] = row; + *num = tv->table->row_count; + tv->table->row_count++; + + return ERROR_SUCCESS; +} + static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record ) { MSITABLEVIEW *tv = (MSITABLEVIEW*)view; @@ -969,6 +1152,8 @@ static UINT TABLE_delete( struct tagMSIVIEW *view ) MSIVIEWOPS table_ops = { TABLE_fetch_int, + TABLE_set_int, + TABLE_insert_row, TABLE_execute, TABLE_close, TABLE_get_dimensions, @@ -977,7 +1162,7 @@ MSIVIEWOPS table_ops = TABLE_delete }; -UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view ) +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) { MSITABLEVIEW *tv ; UINT r, sz, column_count; @@ -1042,13 +1227,24 @@ UINT MSI_CommitTables( MSIDATABASE *db ) UINT r; MSITABLE *table = NULL; - save_string_table( db ); + TRACE("%p\n",db); + + r = save_string_table( db ); + if( r != ERROR_SUCCESS ) + { + ERR("failed to save string table r=%08x\n",r); + return r; + } for( table = db->first_table; table; table = table->next ) { r = save_table( db, table ); if( r != ERROR_SUCCESS ) + { + ERR("failed to save table %s (r=%08x)\n", + debugstr_w(table->name), r); return r; + } } /* force everything to reload next time */ diff --git a/dlls/msi/tokenize.c b/dlls/msi/tokenize.c index cb8a27dd339..2627fd8c807 100644 --- a/dlls/msi/tokenize.c +++ b/dlls/msi/tokenize.c @@ -106,6 +106,7 @@ static const Keyword aKeywordTable[] = { { "LEFT", TK_JOIN_KW }, { "LIKE", TK_LIKE }, { "LIMIT", TK_LIMIT }, + { "LOCALIZABLE", TK_LOCALIZABLE }, { "LONG", TK_LONG }, { "LONGCHAR", TK_LONGCHAR }, { "MATCH", TK_MATCH }, diff --git a/dlls/msi/where.c b/dlls/msi/where.c index 32c7a8ce37d..c115b0e8267 100644 --- a/dlls/msi/where.c +++ b/dlls/msi/where.c @@ -268,6 +268,8 @@ static UINT WHERE_delete( struct tagMSIVIEW *view ) MSIVIEWOPS where_ops = { WHERE_fetch_int, + NULL, + NULL, WHERE_execute, WHERE_close, WHERE_get_dimensions,