ShellExecuteEx, ExtractIconEx, SHFileOperation, SHGetFileInfo,
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "wine/debug.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "objbase.h"
34 #include "objidl.h"
35 #include "msipriv.h"
36 #include "winnls.h"
37
38 #include "wine/unicode.h"
39
40 #include "query.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43
44 typedef struct tagMSICOLUMNINFO
45 {
46     LPWSTR tablename;
47     UINT   number;
48     LPWSTR colname;
49     UINT   type;
50     UINT   offset;
51 } MSICOLUMNINFO;
52
53 struct tagMSITABLE
54 {
55     USHORT **data;
56     UINT ref_count;
57     UINT row_count;
58     struct tagMSITABLE *next;
59     struct tagMSITABLE *prev;
60     WCHAR name[1];
61 };
62
63 #define MAX_STREAM_NAME 0x1f
64
65 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
66        MSICOLUMNINFO **pcols, UINT *pcount );
67 static UINT get_tablecolumns( MSIDATABASE *db, 
68        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
69
70 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
71 {
72     if( col->type & MSITYPE_STRING )
73         return 2;
74     if( (col->type & 0xff) > 4 )
75         ERR("Invalid column size!\n");
76     return col->type & 0xff;
77 }
78
79 static int utf2mime(int x)
80 {
81     if( (x>='0') && (x<='9') )
82         return x-'0';
83     if( (x>='A') && (x<='Z') )
84         return x-'A'+10;
85     if( (x>='a') && (x<='z') )
86         return x-'a'+10+26;
87     if( x=='.' )
88         return 10+26+26;
89     if( x=='_' )
90         return 10+26+26+1;
91     return -1;
92 }
93
94 static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
95 {
96     DWORD count = MAX_STREAM_NAME;
97     DWORD ch, next;
98     LPWSTR out, p;
99
100     if( !bTable )
101         count = strlenW( in )+2;
102     out = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
103     p = out;
104
105     if( bTable )
106     {
107          *p++ = 0x4840;
108          count --;
109     }
110     while( count -- ) 
111     {
112         ch = *in++;
113         if( !ch )
114         {
115             *p = ch;
116             return out;
117         }
118         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
119         {
120             ch = utf2mime(ch) + 0x4800;
121             next = *in;
122             if( next && (next<0x80) )
123             {
124                 next = utf2mime(next);
125                 if( next >= 0  )
126                 {
127                      next += 0x3ffffc0;
128                      ch += (next<<6);
129                      in++;
130                 }
131             }
132         }
133         *p++ = ch;
134     }
135     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
136     HeapFree( GetProcessHeap(), 0, out );
137     return NULL;
138 }
139
140 static int mime2utf(int x)
141 {
142     if( x<10 )
143         return x + '0';
144     if( x<(10+26))
145         return x - 10 + 'A';
146     if( x<(10+26+26))
147         return x - 10 - 26 + 'a';
148     if( x == (10+26+26) )
149         return '.';
150     return '_';
151 }
152
153 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
154 {
155     WCHAR ch;
156     DWORD count = 0;
157
158     while ( (ch = *in++) )
159     {
160         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
161         {
162             if( ch >= 0x4800 )
163                 ch = mime2utf(ch-0x4800);
164             else
165             {
166                 ch -= 0x3800;
167                 *out++ = mime2utf(ch&0x3f);
168                 count++;
169                 ch = mime2utf((ch>>6)&0x3f);
170             }
171         }
172         *out++ = ch;
173         count++;
174     }
175     *out = 0;
176     return count;
177 }
178
179 void enum_stream_names( IStorage *stg )
180 {
181     IEnumSTATSTG *stgenum = NULL;
182     HRESULT r;
183     STATSTG stat;
184     ULONG n, count;
185     WCHAR name[0x40];
186
187     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
188     if( FAILED( r ) )
189         return;
190
191     n = 0;
192     while( 1 )
193     {
194         count = 0;
195         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
196         if( FAILED( r ) || !count )
197             break;
198         decode_streamname( stat.pwcsName, name );
199         ERR("stream %2ld -> %s %s\n", n, 
200             debugstr_w(stat.pwcsName), debugstr_w(name) );
201         n++;
202     }
203
204     IEnumSTATSTG_Release( stgenum );
205 }
206
207 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
208                               USHORT **pdata, UINT *psz )
209 {
210     HRESULT r;
211     UINT ret = ERROR_FUNCTION_FAILED;
212     VOID *data;
213     ULONG sz, count;
214     IStream *stm = NULL;
215     STATSTG stat;
216     LPWSTR encname;
217
218     encname = encode_streamname(TRUE, stname);
219
220     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
221
222     r = IStorage_OpenStream(stg, encname, NULL, 
223             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
224     HeapFree( GetProcessHeap(), 0, encname );
225     if( FAILED( r ) )
226     {
227         WARN("open stream failed r = %08lx - empty table?\n",r);
228         return ret;
229     }
230
231     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
232     if( FAILED( r ) )
233     {
234         ERR("open stream failed r = %08lx!\n",r);
235         goto end;
236     }
237
238     if( stat.cbSize.QuadPart >> 32 )
239     {
240         ERR("Too big!\n");
241         goto end;
242     }
243         
244     sz = stat.cbSize.QuadPart;
245     data = HeapAlloc( GetProcessHeap(), 0, sz );
246     if( !data )
247     {
248         ERR("couldn't allocate memory r=%08lx!\n",r);
249         ret = ERROR_NOT_ENOUGH_MEMORY;
250         goto end;
251     }
252         
253     r = IStream_Read(stm, data, sz, &count );
254     if( FAILED( r ) || ( count != sz ) )
255     {
256         HeapFree( GetProcessHeap(), 0, data );
257         ERR("read stream failed r = %08lx!\n",r);
258         goto end;
259     }
260
261     *pdata = data;
262     *psz = sz;
263     ret = ERROR_SUCCESS;
264
265 end:
266     IStream_Release( stm );
267
268     return ret;
269 }
270
271 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
272 {
273     LPWSTR encname;
274     HRESULT r;
275
276     encname = encode_streamname(FALSE, stname);
277
278     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
279
280     r = IStorage_OpenStream(db->storage, encname, NULL, 
281             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
282     HeapFree( GetProcessHeap(), 0, encname );
283     if( FAILED( r ) )
284     {
285         WARN("open stream failed r = %08lx - empty table?\n",r);
286         return ERROR_FUNCTION_FAILED;
287     }
288
289     return ERROR_SUCCESS;
290 }
291
292 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
293                               USHORT **pdata, UINT *psz )
294 {
295     HRESULT r;
296     UINT ret = ERROR_FUNCTION_FAILED;
297     VOID *data;
298     ULONG sz, count;
299     IStream *stm = NULL;
300     STATSTG stat;
301
302     r = db_get_raw_stream( db, stname, &stm );
303     if( r != ERROR_SUCCESS)
304         return ret;
305     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
306     if( FAILED( r ) )
307     {
308         ERR("open stream failed r = %08lx!\n",r);
309         goto end;
310     }
311
312     if( stat.cbSize.QuadPart >> 32 )
313     {
314         ERR("Too big!\n");
315         goto end;
316     }
317         
318     sz = stat.cbSize.QuadPart;
319     data = HeapAlloc( GetProcessHeap(), 0, sz );
320     if( !data )
321     {
322         ERR("couldn't allocate memory r=%08lx!\n",r);
323         ret = ERROR_NOT_ENOUGH_MEMORY;
324         goto end;
325     }
326         
327     r = IStream_Read(stm, data, sz, &count );
328     if( FAILED( r ) || ( count != sz ) )
329     {
330         HeapFree( GetProcessHeap(), 0, data );
331         ERR("read stream failed r = %08lx!\n",r);
332         goto end;
333     }
334
335     *pdata = data;
336     *psz = sz;
337     ret = ERROR_SUCCESS;
338
339 end:
340     IStream_Release( stm );
341
342     return ret;
343 }
344
345 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
346                                LPVOID data, UINT sz )
347 {
348     HRESULT r;
349     UINT ret = ERROR_FUNCTION_FAILED;
350     ULONG count;
351     IStream *stm = NULL;
352     ULARGE_INTEGER size;
353     LARGE_INTEGER pos;
354     LPWSTR encname;
355
356     encname = encode_streamname(TRUE, stname );
357     r = IStorage_OpenStream( stg, encname, NULL, 
358             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
359     HeapFree( GetProcessHeap(), 0, encname );
360     if( FAILED(r) )
361     {
362         r = IStorage_CreateStream( stg, encname,
363                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
364     }
365     if( FAILED( r ) )
366     {
367         ERR("open stream failed r = %08lx\n",r);
368         return ret;
369     }
370
371     size.QuadPart = sz;
372     r = IStream_SetSize( stm, size );
373     if( FAILED( r ) )
374     {
375         ERR("Failed to SetSize\n");
376         goto end;
377     }
378
379     pos.QuadPart = 0;
380     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
381     if( FAILED( r ) )
382     {
383         ERR("Failed to Seek\n");
384         goto end;
385     }
386
387     r = IStream_Write(stm, data, sz, &count );
388     if( FAILED( r ) || ( count != sz ) )
389     {
390         ERR("Failed to Write\n");
391         goto end;
392     }
393
394     ret = ERROR_SUCCESS;
395
396 end:
397     IStream_Release( stm );
398
399     return ret;
400 }
401
402 UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
403 {
404     MSITABLE *t;
405     USHORT *rawdata = NULL;
406     UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
407     MSICOLUMNINFO *cols, *last_col;
408
409     TRACE("%s\n",debugstr_w(name));
410
411     /* non-existing tables should be interpretted as empty tables */
412     t = HeapAlloc( GetProcessHeap(), 0, 
413                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
414     if( !t )
415         return ERROR_NOT_ENOUGH_MEMORY;
416
417     r = table_get_column_info( db, name, &cols, &num_cols );
418     if( r != ERROR_SUCCESS )
419     {
420         HeapFree( GetProcessHeap(), 0, t );
421         return r;
422     }
423     last_col = &cols[num_cols-1];
424     row_size = last_col->offset + bytes_per_column( last_col );
425
426     t->row_count = 0;
427     t->data = NULL;
428     lstrcpyW( t->name, name );
429     t->ref_count = 1;
430     *ptable = t;
431
432     /* if we can't read the table, just assume that it's empty */
433     read_stream_data( db->storage, name, &rawdata, &rawsize );
434     if( !rawdata )
435         return ERROR_SUCCESS;
436
437     TRACE("Read %d bytes\n", rawsize );
438
439     if( rawsize % row_size )
440     {
441         ERR("Table size is invalid %d/%d\n", rawsize, row_size );
442         return ERROR_FUNCTION_FAILED;
443     }
444
445     t->row_count = rawsize / row_size;
446     t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
447                          t->row_count * sizeof (USHORT*) );
448     if( !t->data )
449         return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
450
451     /* transpose all the data */
452     TRACE("Transposing data from %d columns\n", t->row_count );
453     for( i=0; i<t->row_count; i++ )
454     {
455         t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
456         if( !t->data[i] )
457             return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
458         for( j=0; j<num_cols; j++ )
459         {
460             UINT ofs = cols[j].offset/2;
461             UINT n = bytes_per_column( &cols[j] );
462
463             switch( n )
464             {
465             case 2:
466                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
467                 break;
468             case 4:
469                 t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
470                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
471                 break;
472             default:
473                 ERR("oops - unknown column width %d\n", n);
474                 return ERROR_FUNCTION_FAILED;
475             }
476         }
477     }
478
479     HeapFree( GetProcessHeap(), 0, cols );
480     HeapFree( GetProcessHeap(), 0, rawdata );
481
482     return ERROR_SUCCESS;
483 }
484
485 /* add this table to the list of cached tables in the database */
486 void add_table(MSIDATABASE *db, MSITABLE *table)
487 {
488     table->next = db->first_table;
489     table->prev = NULL;
490     if( db->first_table )
491         db->first_table->prev = table;
492     else
493         db->last_table = table;
494     db->first_table = table;
495 }
496  
497 /* remove from the list of cached tables */
498 void remove_table( MSIDATABASE *db, MSITABLE *table )
499 {
500     if( table->next )
501         table->next->prev = table->prev;
502     else
503         db->last_table = table->prev;
504     if( table->prev )
505         table->prev->next = table->next;
506     else
507         db->first_table = table->next;
508     table->next = NULL;
509     table->prev = NULL;
510 }
511
512 void release_table( MSIDATABASE *db, MSITABLE *table )
513 {
514     if( !table->ref_count )
515         ERR("Trying to destroy table with refcount 0\n");
516     table->ref_count --;
517     if( !table->ref_count )
518     {
519         remove_table( db, table );
520         HeapFree( GetProcessHeap(), 0, table->data );
521         HeapFree( GetProcessHeap(), 0, table );
522         TRACE("Destroyed table %s\n", debugstr_w(table->name));
523     }
524 }
525
526 void free_cached_tables( MSIDATABASE *db )
527 {
528     while( db->first_table )
529     {
530         MSITABLE *t = db->first_table;
531
532         if ( --t->ref_count )
533             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
534         remove_table( db, t );
535         HeapFree( GetProcessHeap(), 0, t->data );
536         HeapFree( GetProcessHeap(), 0, t );
537     }
538 }
539
540 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
541 {
542     MSITABLE *t;
543
544     for( t = db->first_table; t; t=t->next )
545     {
546         if( !lstrcmpW( name, t->name ) )
547         {
548             *ptable = t;
549             return ERROR_SUCCESS;
550         }
551     }
552
553     return ERROR_FUNCTION_FAILED;
554 }
555
556 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
557 {
558     UINT r, column_count;
559     MSICOLUMNINFO *columns;
560
561     /* get the number of columns in this table */
562     column_count = 0;
563     r = get_tablecolumns( db, name, NULL, &column_count );
564     if( r != ERROR_SUCCESS )
565         return r;
566
567     /* if there's no columns, there's no table */
568     if( column_count == 0 )
569         return ERROR_INVALID_PARAMETER;
570
571     TRACE("Table %s found\n", debugstr_w(name) );
572
573     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
574     if( !columns )
575         return ERROR_FUNCTION_FAILED;
576
577     r = get_tablecolumns( db, name, columns, &column_count );
578     if( r != ERROR_SUCCESS )
579     {
580         HeapFree( GetProcessHeap(), 0, columns );
581         return ERROR_FUNCTION_FAILED;
582     }
583
584     *pcols = columns;
585     *pcount = column_count;
586
587     return r;
588 }
589
590 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
591 {
592     UINT r;
593
594     *ptable = NULL;
595
596     /* first, see if the table is cached */
597     r = find_cached_table( db, name, ptable );
598     if( r == ERROR_SUCCESS )
599     {
600         (*ptable)->ref_count++;
601         return r;
602     }
603
604     r = read_table_from_storage( db, name, ptable );
605     if( r != ERROR_SUCCESS )
606         return r;
607
608     /* add the table to the list */
609     add_table( db, *ptable );
610     (*ptable)->ref_count++;
611
612     return ERROR_SUCCESS;
613 }
614
615 UINT save_table( MSIDATABASE *db, MSITABLE *t )
616 {
617     USHORT *rawdata = NULL, *p;
618     UINT rawsize, r, i, j, row_size, num_cols = 0;
619     MSICOLUMNINFO *cols, *last_col;
620
621     TRACE("Saving %s\n", debugstr_w( t->name ) );
622
623     r = table_get_column_info( db, t->name, &cols, &num_cols );
624     if( r != ERROR_SUCCESS )
625         return r;
626     
627     last_col = &cols[num_cols-1];
628     row_size = last_col->offset + bytes_per_column( last_col );
629
630     rawsize = t->row_count * row_size;
631     rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
632     if( !rawdata )
633         return ERROR_NOT_ENOUGH_MEMORY;
634
635     p = rawdata;
636     for( i=0; i<num_cols; i++ )
637     {
638         for( j=0; j<t->row_count; j++ )
639         {
640             UINT offset = cols[i].offset;
641
642             *p++ = t->data[j][offset/2];
643             if( 4 == bytes_per_column( &cols[i] ) )
644                 *p++ = t->data[j][offset/2+1];
645         }
646     }
647
648     TRACE("writing %d bytes\n", rawsize);
649     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
650
651     HeapFree( GetProcessHeap(), 0, rawdata );
652
653     return r;
654 }
655
656 HRESULT init_string_table( IStorage *stg )
657 {
658     HRESULT r;
659     static const WCHAR szStringData[] = {
660         '_','S','t','r','i','n','g','D','a','t','a',0 };
661     static const WCHAR szStringPool[] = {
662         '_','S','t','r','i','n','g','P','o','o','l',0 };
663     USHORT zero[2] = { 0, 0 };
664     ULONG count = 0;
665     IStream *stm = NULL;
666     LPWSTR encname;
667
668     encname = encode_streamname(TRUE, szStringPool );
669
670     /* create the StringPool stream... add the zero string to it*/
671     r = IStorage_CreateStream( stg, encname,
672             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
673     HeapFree( GetProcessHeap(), 0, encname );
674     if( r ) 
675     {
676         TRACE("Failed\n");
677         return r;
678     }
679
680     r = IStream_Write(stm, zero, sizeof zero, &count );
681     IStream_Release( stm );
682
683     if( FAILED( r ) || ( count != sizeof zero ) )
684     {
685         TRACE("Failed\n");
686         return E_FAIL;
687     }
688
689     /* create the StringData stream... make it zero length */
690     encname = encode_streamname(TRUE, szStringData );
691     r = IStorage_CreateStream( stg, encname,
692             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
693     HeapFree( GetProcessHeap(), 0, encname );
694     if( r ) 
695     {
696         TRACE("Failed\n");
697         return E_FAIL;
698     }
699     IStream_Release( stm );
700
701     return r;
702 }
703
704 UINT load_string_table( MSIDATABASE *db )
705 {
706     CHAR *data;
707     USHORT *pool;
708     UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
709     DWORD i, count, offset, len, n;
710     static const WCHAR szStringData[] = {
711         '_','S','t','r','i','n','g','D','a','t','a',0 };
712     static const WCHAR szStringPool[] = {
713         '_','S','t','r','i','n','g','P','o','o','l',0 };
714
715     if( db->strings )
716     {
717         msi_destroy_stringtable( db->strings );
718         db->strings = NULL;
719     }
720
721     r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
722     if( r != ERROR_SUCCESS)
723         goto end;
724     r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
725     if( r != ERROR_SUCCESS)
726         goto end;
727
728     count = poolsize/4;
729     if( poolsize > 4 )
730         codepage = pool[0] | ( pool[1] << 16 );
731     else
732         codepage = CP_ACP;
733     db->strings = msi_init_stringtable( count, codepage );
734
735     offset = 0;
736     for( i=1; i<count; i++ )
737     {
738         len = pool[i*2];
739         n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
740         if( n != i )
741             ERR("Failed to add string %ld\n", i );
742         offset += len;
743     }
744
745     TRACE("Loaded %ld strings\n", count);
746
747     ret = ERROR_SUCCESS;
748
749 end:
750     if( pool )
751         HeapFree( GetProcessHeap(), 0, pool );
752     if( data )
753         HeapFree( GetProcessHeap(), 0, data );
754
755     return ret;
756 }
757
758 UINT save_string_table( MSIDATABASE *db )
759 {
760     UINT i, count, datasize, poolsize, sz, used, r, codepage;
761     UINT ret = ERROR_FUNCTION_FAILED;
762     static const WCHAR szStringData[] = {
763         '_','S','t','r','i','n','g','D','a','t','a',0 };
764     static const WCHAR szStringPool[] = {
765         '_','S','t','r','i','n','g','P','o','o','l',0 };
766     CHAR *data = NULL;
767     USHORT *pool = NULL;
768
769     TRACE("\n");
770
771     /* construct the new table in memory first */
772     datasize = msi_string_totalsize( db->strings, &count );
773     poolsize = count*2*sizeof(USHORT);
774
775     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
776     if( ! pool )
777     {
778         ERR("Failed to alloc pool %d bytes\n", poolsize );
779         goto err;
780     }
781     data = HeapAlloc( GetProcessHeap(), 0, datasize );
782     if( ! data )
783     {
784         ERR("Failed to alloc data %d bytes\n", poolsize );
785         goto err;
786     }
787
788     used = 0;
789     codepage = msi_string_get_codepage( db->strings );
790     pool[0]=codepage&0xffff;
791     pool[1]=(codepage>>16);
792     for( i=1; i<count; i++ )
793     {
794         sz = datasize - used;
795         r = msi_id2stringA( db->strings, i, data+used, &sz );
796         if( r != ERROR_SUCCESS )
797         {
798             ERR("failed to fetch string\n");
799             sz = 0;
800         }
801         if( sz && (sz < (datasize - used ) ) )
802             sz--;
803         TRACE("adding %u bytes %s\n", sz, data+used );
804         pool[ i*2 ] = sz;
805         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
806         used += sz;
807         if( used > datasize  )
808         {
809             ERR("oops overran %d >= %d\n", used, datasize);
810             goto err;
811         }
812     }
813
814     if( used != datasize )
815     {
816         ERR("oops used %d != datasize %d\n", used, datasize);
817         goto err;
818     }
819
820     /* write the streams */
821     r = write_stream_data( db->storage, szStringData, data, datasize );
822     TRACE("Wrote StringData r=%08x\n", r);
823     if( r )
824         goto err;
825     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
826     TRACE("Wrote StringPool r=%08x\n", r);
827     if( r )
828         goto err;
829
830     ret = ERROR_SUCCESS;
831
832 err:
833     if( data )
834         HeapFree( GetProcessHeap(), 0, data );
835     if( pool )
836         HeapFree( GetProcessHeap(), 0, pool );
837
838     return ret;
839 }
840
841 static LPWSTR strdupW( LPCWSTR str )
842 {
843     UINT len = lstrlenW( str ) + 1;
844     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
845     if( ret )
846         lstrcpyW( ret, str );
847     return ret;
848 }
849
850 /* information for default tables */
851 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
852 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
853 static const WCHAR szName[]    = { 'N','a','m','e',0 };
854 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
855 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
856 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
857 static const WCHAR szType[]    = { 'T','y','p','e',0 };
858
859 struct standard_table {
860     LPCWSTR tablename;
861     LPCWSTR columnname;
862     UINT number;
863     UINT type;
864 } MSI_standard_tables[] =
865 {
866   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
867   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
868   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
869   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
870   { szColumns, szType,   4, MSITYPE_VALID | 2},
871 };
872
873 #define STANDARD_TABLE_COUNT \
874      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
875
876 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
877 {
878     DWORD i, n=0;
879
880     for(i=0; i<STANDARD_TABLE_COUNT; i++)
881     {
882         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
883             continue;
884         if(colinfo && (n < *sz) )
885         {
886             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
887             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
888             colinfo[n].number = MSI_standard_tables[i].number;
889             colinfo[n].type = MSI_standard_tables[i].type;
890             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
891                     debugstr_w(colinfo[n].colname)); */
892             if( n )
893                 colinfo[n].offset = colinfo[n-1].offset
894                                   + bytes_per_column( &colinfo[n-1] );
895             else
896                 colinfo[n].offset = 0;
897         }
898         n++;
899         if( colinfo && (n >= *sz) )
900             break;
901     }
902     *sz = n;
903     return ERROR_SUCCESS;
904 }
905
906 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
907 {
908     UINT sz=0, r;
909     LPWSTR str;
910
911     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
912     if( r != ERROR_SUCCESS )
913         return NULL;
914     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
915     if( !str )
916         return str;
917     r = msi_id2stringW( db->strings, stringid, str, &sz );
918     if( r == ERROR_SUCCESS )
919         return str;
920     HeapFree(  GetProcessHeap(), 0, str );
921     return NULL;
922 }
923
924 static UINT get_tablecolumns( MSIDATABASE *db, 
925        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
926 {
927     UINT r, i, n=0, table_id, count, maxcount = *sz;
928     MSITABLE *table = NULL;
929     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
930
931     /* first check if there is a default table with that name */
932     r = get_defaulttablecolumns( szTableName, colinfo, sz );
933     if( ( r == ERROR_SUCCESS ) && *sz )
934         return r;
935
936     r = get_table( db, szColumns, &table);
937     if( r != ERROR_SUCCESS )
938     {
939         ERR("table %s not available\n", debugstr_w(szColumns));
940         return r;
941     }
942
943     /* convert table and column names to IDs from the string table */
944     r = msi_string2idW( db->strings, szTableName, &table_id );
945     if( r != ERROR_SUCCESS )
946     {
947         release_table( db, table );
948         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
949         return r;
950     }
951
952     TRACE("Table id is %d\n", table_id);
953
954     count = table->row_count;
955     for( i=0; i<count; i++ )
956     {
957         if( table->data[ i ][ 0 ] != table_id )
958             continue;
959         if( colinfo )
960         {
961             UINT id = table->data[ i ] [ 2 ];
962             colinfo[n].tablename = MSI_makestring( db, table_id );
963             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
964             colinfo[n].colname = MSI_makestring( db, id );
965             colinfo[n].type = table->data[ i ] [ 3 ];
966             /* this assumes that columns are in order in the table */
967             if( n )
968                 colinfo[n].offset = colinfo[n-1].offset
969                                   + bytes_per_column( &colinfo[n-1] );
970             else
971                 colinfo[n].offset = 0;
972             TRACE("table %s column %d is [%s] (%d) with type %08x "
973                   "offset %d at row %d\n", debugstr_w(szTableName),
974                    colinfo[n].number, debugstr_w(colinfo[n].colname),
975                    id, colinfo[n].type, colinfo[n].offset, i);
976             if( n != (colinfo[n].number-1) )
977             {
978                 ERR("oops. data in the _Columns table isn't in the right "
979                     "order for table %s\n", debugstr_w(szTableName));
980                 return ERROR_FUNCTION_FAILED;
981             }
982         }
983         n++;
984         if( colinfo && ( n >= maxcount ) )
985             break;
986     }
987     *sz = n;
988
989     release_table( db, table );
990
991     return ERROR_SUCCESS;
992 }
993
994 /* try to find the table name in the _Tables table */
995 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
996 {
997     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
998     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
999     UINT r, table_id = 0, i, count;
1000     MSITABLE *table = NULL;
1001
1002     if( !lstrcmpW( name, szTables ) )
1003         return TRUE;
1004     if( !lstrcmpW( name, szColumns ) )
1005         return TRUE;
1006
1007     r = msi_string2idW( db->strings, name, &table_id );
1008     if( r != ERROR_SUCCESS )
1009     {
1010         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1011         return FALSE;
1012     }
1013
1014     r = get_table( db, szTables, &table);
1015     if( r != ERROR_SUCCESS )
1016     {
1017         ERR("table %s not available\n", debugstr_w(szTables));
1018         return FALSE;
1019     }
1020
1021     /* count = table->size/2; */
1022     count = table->row_count;
1023     for( i=0; i<count; i++ )
1024         if( table->data[ i ][ 0 ] == table_id )
1025             break;
1026
1027     release_table( db, table );
1028
1029     if (i!=count)
1030         return TRUE;
1031
1032     ERR("Searched %d tables, but %d was not found\n", count, table_id );
1033
1034     return FALSE;
1035 }
1036
1037 /* below is the query interface to a table */
1038
1039 typedef struct tagMSITABLEVIEW
1040 {
1041     MSIVIEW        view;
1042     MSIDATABASE   *db;
1043     MSITABLE      *table;
1044     MSICOLUMNINFO *columns;
1045     UINT           num_cols;
1046     UINT           row_size;
1047     WCHAR          name[1];
1048 } MSITABLEVIEW;
1049
1050 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1051 {
1052     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1053     UINT offset, num_rows, n;
1054
1055     if( !tv->table )
1056         return ERROR_INVALID_PARAMETER;
1057
1058     if( (col==0) || (col>tv->num_cols) )
1059         return ERROR_INVALID_PARAMETER;
1060
1061     /* how many rows are there ? */
1062     num_rows = tv->table->row_count;
1063     if( row >= num_rows )
1064         return ERROR_NO_MORE_ITEMS;
1065
1066     if( tv->columns[col-1].offset >= tv->row_size )
1067     {
1068         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1069         ERR("%p %p\n", tv, tv->columns );
1070         return ERROR_FUNCTION_FAILED;
1071     }
1072
1073     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1074     n = bytes_per_column( &tv->columns[col-1] );
1075     switch( n )
1076     {
1077     case 4:
1078         offset = tv->columns[col-1].offset/2;
1079         *val = tv->table->data[row][offset] + 
1080                (tv->table->data[row][offset + 1] << 16);
1081         break;
1082     case 2:
1083         offset = tv->columns[col-1].offset/2;
1084         *val = tv->table->data[row][offset];
1085         break;
1086     default:
1087         ERR("oops! what is %d bytes per column?\n", n );
1088         return ERROR_FUNCTION_FAILED;
1089     }
1090
1091     TRACE("Data [%d][%d] = %d \n", row, col, *val );
1092
1093     return ERROR_SUCCESS;
1094 }
1095
1096 /*
1097  * We need a special case for streams, as we need to reference column with
1098  * the name of the stream in the same table, and the table name
1099  * which may not be available at higher levels of the query
1100  */
1101 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1102 {
1103     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1104     UINT ival = 0, refcol = 0, r;
1105     LPWSTR sval;
1106     LPWSTR full_name;
1107     DWORD len;
1108     static const WCHAR szDot[] = { '.', 0 };
1109
1110     if( !view->ops->fetch_int )
1111         return ERROR_INVALID_PARAMETER;
1112
1113     /*
1114      * The column marked with the type stream data seems to have a single number
1115      * which references the column containing the name of the stream data
1116      *
1117      * Fetch the column to reference first.
1118      */
1119     r = view->ops->fetch_int( view, row, col, &ival );
1120     if( r != ERROR_SUCCESS )
1121         return r;
1122
1123     /* now get the column with the name of the stream */
1124     r = view->ops->fetch_int( view, row, ival, &refcol );
1125     if( r != ERROR_SUCCESS )
1126         return r;
1127
1128     /* lookup the string value from the string table */
1129     sval = MSI_makestring( tv->db, refcol );
1130     if( !sval )
1131         return ERROR_INVALID_PARAMETER;
1132
1133     len = strlenW( tv->name ) + 2 + strlenW( sval );
1134     full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1135     strcpyW( full_name, tv->name );
1136     strcatW( full_name, szDot );
1137     strcatW( full_name, sval );
1138
1139     r = db_get_raw_stream( tv->db, full_name, stm );
1140     if( r )
1141         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1142     HeapFree( GetProcessHeap(), 0, full_name );
1143     HeapFree( GetProcessHeap(), 0, sval );
1144
1145     return r;
1146 }
1147
1148 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1149 {
1150     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1151     UINT offset, n;
1152
1153     if( !tv->table )
1154         return ERROR_INVALID_PARAMETER;
1155
1156     if( (col==0) || (col>tv->num_cols) )
1157         return ERROR_INVALID_PARAMETER;
1158
1159     if( tv->columns[col-1].offset >= tv->row_size )
1160     {
1161         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1162         ERR("%p %p\n", tv, tv->columns );
1163         return ERROR_FUNCTION_FAILED;
1164     }
1165
1166     n = bytes_per_column( &tv->columns[col-1] );
1167     switch( n )
1168     {
1169     case 4:
1170         offset = tv->columns[col-1].offset/2;
1171         tv->table->data[row][offset]     = val & 0xffff;
1172         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1173         break;
1174     case 2:
1175         offset = tv->columns[col-1].offset/2;
1176         tv->table->data[row][offset] = val;
1177         break;
1178     default:
1179         ERR("oops! what is %d bytes per column?\n", n );
1180         return ERROR_FUNCTION_FAILED;
1181     }
1182     return ERROR_SUCCESS;
1183 }
1184
1185 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1186 {
1187     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1188     USHORT **p, *row;
1189     UINT sz;
1190
1191     TRACE("%p\n", view);
1192
1193     if( !tv->table )
1194         return ERROR_INVALID_PARAMETER;
1195
1196     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1197     if( !row )
1198         return ERROR_NOT_ENOUGH_MEMORY;
1199
1200     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1201     if( tv->table->data )
1202         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1203     else
1204         p = HeapAlloc( GetProcessHeap(), 0, sz );
1205     if( !p )
1206     {
1207         HeapFree( GetProcessHeap(), 0, row );
1208         return ERROR_NOT_ENOUGH_MEMORY;
1209     }
1210
1211     tv->table->data = p;
1212     tv->table->data[tv->table->row_count] = row;
1213     *num = tv->table->row_count;
1214     tv->table->row_count++;
1215
1216     return ERROR_SUCCESS;
1217 }
1218
1219 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1220 {
1221     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1222     UINT r;
1223
1224     TRACE("%p %p\n", tv, record);
1225
1226     if( tv->table )
1227         return ERROR_FUNCTION_FAILED;
1228
1229     r = get_table( tv->db, tv->name, &tv->table );
1230     if( r != ERROR_SUCCESS )
1231         return r;
1232     
1233     return ERROR_SUCCESS;
1234 }
1235
1236 static UINT TABLE_close( struct tagMSIVIEW *view )
1237 {
1238     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1239
1240     TRACE("%p\n", view );
1241
1242     if( !tv->table )
1243         return ERROR_FUNCTION_FAILED;
1244
1245     release_table( tv->db, tv->table );
1246     tv->table = NULL;
1247     
1248     return ERROR_SUCCESS;
1249 }
1250
1251 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1252 {
1253     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1254
1255     TRACE("%p %p %p\n", view, rows, cols );
1256
1257     if( cols )
1258         *cols = tv->num_cols;
1259     if( rows )
1260     {
1261         if( !tv->table )
1262             return ERROR_INVALID_PARAMETER;
1263         *rows = tv->table->row_count;
1264     }
1265
1266     return ERROR_SUCCESS;
1267 }
1268
1269 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1270                 UINT n, LPWSTR *name, UINT *type )
1271 {
1272     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1273
1274     TRACE("%p %d %p %p\n", tv, n, name, type );
1275
1276     if( ( n == 0 ) || ( n > tv->num_cols ) )
1277         return ERROR_INVALID_PARAMETER;
1278
1279     if( name )
1280     {
1281         *name = strdupW( tv->columns[n-1].colname );
1282         if( !*name )
1283             return ERROR_FUNCTION_FAILED;
1284     }
1285     if( type )
1286         *type = tv->columns[n-1].type;
1287
1288     return ERROR_SUCCESS;
1289 }
1290
1291 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
1292 {
1293     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
1294     return ERROR_CALL_NOT_IMPLEMENTED;
1295 }
1296
1297 static UINT TABLE_delete( struct tagMSIVIEW *view )
1298 {
1299     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1300
1301     TRACE("%p\n", view );
1302
1303     if( tv->table )
1304         release_table( tv->db, tv->table );
1305     tv->table = NULL;
1306
1307     if( tv->columns )
1308     {
1309         UINT i;
1310         for( i=0; i<tv->num_cols; i++)
1311         {
1312             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1313             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1314         }
1315         HeapFree( GetProcessHeap(), 0, tv->columns );
1316     }
1317     tv->columns = NULL;
1318
1319     HeapFree( GetProcessHeap(), 0, tv );
1320
1321     return ERROR_SUCCESS;
1322 }
1323
1324
1325 MSIVIEWOPS table_ops =
1326 {
1327     TABLE_fetch_int,
1328     TABLE_fetch_stream,
1329     TABLE_set_int,
1330     TABLE_insert_row,
1331     TABLE_execute,
1332     TABLE_close,
1333     TABLE_get_dimensions,
1334     TABLE_get_column_info,
1335     TABLE_modify,
1336     TABLE_delete
1337 };
1338
1339 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1340 {
1341     MSITABLEVIEW *tv ;
1342     UINT r, sz, column_count;
1343     MSICOLUMNINFO *columns, *last_col;
1344
1345     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1346
1347     /* get the number of columns in this table */
1348     column_count = 0;
1349     r = get_tablecolumns( db, name, NULL, &column_count );
1350     if( r != ERROR_SUCCESS )
1351         return r;
1352
1353     /* if there's no columns, there's no table */
1354     if( column_count == 0 )
1355         return ERROR_INVALID_PARAMETER;
1356
1357     TRACE("Table found\n");
1358
1359     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1360     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1361     if( !tv )
1362         return ERROR_FUNCTION_FAILED;
1363     
1364     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1365     if( !columns )
1366     {
1367         HeapFree( GetProcessHeap(), 0, tv );
1368         return ERROR_FUNCTION_FAILED;
1369     }
1370
1371     r = get_tablecolumns( db, name, columns, &column_count );
1372     if( r != ERROR_SUCCESS )
1373     {
1374         HeapFree( GetProcessHeap(), 0, columns );
1375         HeapFree( GetProcessHeap(), 0, tv );
1376         return ERROR_FUNCTION_FAILED;
1377     }
1378
1379     TRACE("Table has %d columns\n", column_count);
1380
1381     last_col = &columns[column_count-1];
1382
1383     /* fill the structure */
1384     tv->view.ops = &table_ops;
1385     tv->db = db;
1386     tv->columns = columns;
1387     tv->num_cols = column_count;
1388     tv->table = NULL;
1389     tv->row_size = last_col->offset + bytes_per_column( last_col );
1390
1391     TRACE("one row is %d bytes\n", tv->row_size );
1392
1393     *view = (MSIVIEW*) tv;
1394     lstrcpyW( tv->name, name );
1395
1396     return ERROR_SUCCESS;
1397 }
1398
1399 UINT MSI_CommitTables( MSIDATABASE *db )
1400 {
1401     UINT r;
1402     MSITABLE *table = NULL;
1403
1404     TRACE("%p\n",db);
1405
1406     r = save_string_table( db );
1407     if( r != ERROR_SUCCESS )
1408     {
1409         ERR("failed to save string table r=%08x\n",r);
1410         return r;
1411     }
1412
1413     for( table = db->first_table; table; table = table->next )
1414     {
1415         r = save_table( db, table );
1416         if( r != ERROR_SUCCESS )
1417         {
1418             ERR("failed to save table %s (r=%08x)\n",
1419                   debugstr_w(table->name), r);
1420             return r;
1421         }
1422     }
1423
1424     /* force everything to reload next time */
1425     free_cached_tables( db );
1426
1427     return ERROR_SUCCESS;
1428 }