MSI: first go at write support

Mike McCormack mike at codeweavers.com
Thu Mar 18 00:52:30 CST 2004


ChangeLog:
* first go at write support
-------------- next part --------------
Index: dlls/msi/create.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/create.c,v
retrieving revision 1.1
diff -u -r1.1 create.c
--- dlls/msi/create.c	16 Mar 2004 19:18:22 -0000	1.1
+++ dlls/msi/create.c	18 Mar 2004 06:04:01 -0000
@@ -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_execute( struct tagMSIVIEW *view, MSIHANDLE record )
 {
     MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
-    create_col_info *c;
-
-    FIXME("%p %ld\n", cv, record);
+    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("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 )
+    /* only add tables that don't exist already */
+    if( TABLE_Exists(cv->db, cv->name ) )
+        return ERROR_BAD_QUERY_SYNTAX;
+
+    /* 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 )
     {
-        FIXME("Column %s  type %04x\n", debugstr_w(c->colname), c->type );
+        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;
 
-    return ERROR_SUCCESS;
-    return ERROR_FUNCTION_FAILED;
+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 @@
 MSIVIEWOPS create_ops =
 {
     CREATE_fetch_int,
+    NULL,
+    NULL,
     CREATE_execute,
     CREATE_close,
     CREATE_get_dimensions,
Index: dlls/msi/distinct.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/distinct.c,v
retrieving revision 1.3
diff -u -r1.3 distinct.c
--- dlls/msi/distinct.c	8 Sep 2003 19:38:46 -0000	1.3
+++ dlls/msi/distinct.c	18 Mar 2004 06:04:01 -0000
@@ -250,6 +250,8 @@
 MSIVIEWOPS distinct_ops =
 {
     DISTINCT_fetch_int,
+    NULL,
+    NULL,
     DISTINCT_execute,
     DISTINCT_close,
     DISTINCT_get_dimensions,
Index: dlls/msi/msi.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/msi.c,v
retrieving revision 1.13
diff -u -r1.13 msi.c
--- dlls/msi/msi.c	18 Mar 2004 04:04:08 -0000	1.13
+++ dlls/msi/msi.c	18 Mar 2004 06:04:01 -0000
@@ -211,6 +211,8 @@
     {
         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 )
     {
Index: dlls/msi/msipriv.h
===================================================================
RCS file: /home/wine/wine/dlls/msi/msipriv.h,v
retrieving revision 1.7
diff -u -r1.7 msipriv.h
--- dlls/msi/msipriv.h	18 Mar 2004 04:04:08 -0000	1.7
+++ dlls/msi/msipriv.h	18 Mar 2004 06:04:01 -0000
@@ -65,6 +65,19 @@
     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
      */
     UINT (*execute)( struct tagMSIVIEW *, MSIHANDLE );
@@ -154,14 +167,18 @@
 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 );
@@ -169,5 +186,7 @@
 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__ */
Index: dlls/msi/order.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/order.c,v
retrieving revision 1.3
diff -u -r1.3 order.c
--- dlls/msi/order.c	8 Sep 2003 19:38:46 -0000	1.3
+++ dlls/msi/order.c	18 Mar 2004 06:04:02 -0000
@@ -253,6 +253,8 @@
 MSIVIEWOPS order_ops =
 {
     ORDER_fetch_int,
+    NULL,
+    NULL,
     ORDER_execute,
     ORDER_close,
     ORDER_get_dimensions,
Index: dlls/msi/query.h
===================================================================
RCS file: /home/wine/wine/dlls/msi/query.h,v
retrieving revision 1.4
diff -u -r1.4 query.h
--- dlls/msi/query.h	16 Mar 2004 19:18:22 -0000	1.4
+++ dlls/msi/query.h	18 Mar 2004 06:04:02 -0000
@@ -79,7 +79,7 @@
 
 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 );
Index: dlls/msi/select.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/select.c,v
retrieving revision 1.3
diff -u -r1.3 select.c
--- dlls/msi/select.c	8 Sep 2003 19:38:46 -0000	1.3
+++ dlls/msi/select.c	18 Mar 2004 06:04:02 -0000
@@ -152,6 +152,8 @@
 MSIVIEWOPS select_ops =
 {
     SELECT_fetch_int,
+    NULL,
+    NULL,
     SELECT_execute,
     SELECT_close,
     SELECT_get_dimensions,
Index: dlls/msi/sql.y
===================================================================
RCS file: /home/wine/wine/dlls/msi/sql.y,v
retrieving revision 1.6
diff -u -r1.6 sql.y
--- dlls/msi/sql.y	16 Mar 2004 19:18:22 -0000	1.6
+++ dlls/msi/sql.y	18 Mar 2004 06:04:02 -0000
@@ -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 @@
 %token TK_HAVING TK_HOLD
 %token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
 %token <string> 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 @@
 %type <column_list> selcollist
 %type <query> from unorderedsel oneselect onequery onecreate
 %type <expr> expr val column_val
-%type <column_type> column_type data_type data_count
+%type <column_type> column_type data_type data_type_l data_count
 %type <column_info> column_def table_def
 
 %%
@@ -183,15 +185,24 @@
 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_type:
+    data_type_l
+        {
+            $$ = $1;
+        }
+  | data_type_l TK_LOCALIZABLE
+        {
+            FIXME("LOCALIZABLE ignored\n");
+            $$ = $1;
+        }
+    ;
+
+data_type_l:
     data_type
         {
             $$ |= MSITYPE_NULLABLE;
Index: dlls/msi/string.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/string.c,v
retrieving revision 1.2
diff -u -r1.2 string.c
--- dlls/msi/string.c	18 Mar 2004 04:04:08 -0000	1.2
+++ dlls/msi/string.c	18 Mar 2004 06:04:02 -0000
@@ -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 @@
     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 @@
         return NULL;    
     }
     st->count = entries;
-    st->freeslot = 0;
+    st->freeslot = 1;
 
     return st;
 }
@@ -98,18 +98,28 @@
 
 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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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; i<st->count; i++)
+    for( i=0; i<st->count; i++ )
     {
         if( ( st->strings[i].hash == hash ) &&
             !strcmp( st->strings[i].str, str ) )
@@ -231,11 +283,32 @@
         }
     }
 
+    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 )
 {
Index: dlls/msi/table.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/table.c,v
retrieving revision 1.8
diff -u -r1.8 table.c
--- dlls/msi/table.c	18 Mar 2004 04:04:08 -0000	1.8
+++ dlls/msi/table.c	18 Mar 2004 06:04:03 -0000
@@ -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 <stdarg.h>
 
 #include "windef.h"
@@ -237,17 +240,25 @@
     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 @@
 
     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 @@
     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 @@
     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; i<num_cols; i++ )
+    {
+        for( j=0; j<t->row_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 @@
     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 @@
     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; i<count; i++ )
+    for( i=1; i<count; i++ )
     {
         len = pool[i*2];
-        msi_addstring( db->strings, 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 @@
 
 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 @@
     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 @@
 
     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; i<count; i++ )
     {
-        sz = remaining;
-        r = msi_id2stringA( db->strings, 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 @@
     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 @@
     if (i!=count)
         return TRUE;
 
+    ERR("Searched %d tables, but %d was not found\n", count, table_id );
+
     return FALSE;
 }
 
@@ -844,7 +957,8 @@
     {
     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 @@
     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 @@
 MSIVIEWOPS table_ops =
 {
     TABLE_fetch_int,
+    TABLE_set_int,
+    TABLE_insert_row,
     TABLE_execute,
     TABLE_close,
     TABLE_get_dimensions,
@@ -977,7 +1162,7 @@
     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 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 */
Index: dlls/msi/tokenize.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/tokenize.c,v
retrieving revision 1.5
diff -u -r1.5 tokenize.c
--- dlls/msi/tokenize.c	16 Mar 2004 19:18:22 -0000	1.5
+++ dlls/msi/tokenize.c	18 Mar 2004 06:04:03 -0000
@@ -106,6 +106,7 @@
   { "LEFT", TK_JOIN_KW },
   { "LIKE", TK_LIKE },
   { "LIMIT", TK_LIMIT },
+  { "LOCALIZABLE", TK_LOCALIZABLE },
   { "LONG", TK_LONG },
   { "LONGCHAR", TK_LONGCHAR },
   { "MATCH", TK_MATCH },
Index: dlls/msi/where.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/where.c,v
retrieving revision 1.3
diff -u -r1.3 where.c
--- dlls/msi/where.c	8 Sep 2003 19:38:46 -0000	1.3
+++ dlls/msi/where.c	18 Mar 2004 06:04:04 -0000
@@ -268,6 +268,8 @@
 MSIVIEWOPS where_ops =
 {
     WHERE_fetch_int,
+    NULL,
+    NULL,
     WHERE_execute,
     WHERE_close,
     WHERE_get_dimensions,


More information about the wine-patches mailing list