Allocate space for terminating null.
[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         TRACE("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     if( FAILED(r) )
360     {
361         r = IStorage_CreateStream( stg, encname,
362                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
363     }
364     HeapFree( GetProcessHeap(), 0, encname );
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     /* nonexistent tables should be interpreted 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     HeapFree( GetProcessHeap(), 0, pool );
751     HeapFree( GetProcessHeap(), 0, data );
752
753     return ret;
754 }
755
756 UINT save_string_table( MSIDATABASE *db )
757 {
758     UINT i, count, datasize, poolsize, sz, used, r, codepage;
759     UINT ret = ERROR_FUNCTION_FAILED;
760     static const WCHAR szStringData[] = {
761         '_','S','t','r','i','n','g','D','a','t','a',0 };
762     static const WCHAR szStringPool[] = {
763         '_','S','t','r','i','n','g','P','o','o','l',0 };
764     CHAR *data = NULL;
765     USHORT *pool = NULL;
766
767     TRACE("\n");
768
769     /* construct the new table in memory first */
770     datasize = msi_string_totalsize( db->strings, &count );
771     poolsize = count*2*sizeof(USHORT);
772
773     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
774     if( ! pool )
775     {
776         ERR("Failed to alloc pool %d bytes\n", poolsize );
777         goto err;
778     }
779     data = HeapAlloc( GetProcessHeap(), 0, datasize );
780     if( ! data )
781     {
782         ERR("Failed to alloc data %d bytes\n", poolsize );
783         goto err;
784     }
785
786     used = 0;
787     codepage = msi_string_get_codepage( db->strings );
788     pool[0]=codepage&0xffff;
789     pool[1]=(codepage>>16);
790     for( i=1; i<count; i++ )
791     {
792         sz = datasize - used;
793         r = msi_id2stringA( db->strings, i, data+used, &sz );
794         if( r != ERROR_SUCCESS )
795         {
796             ERR("failed to fetch string\n");
797             sz = 0;
798         }
799         if( sz && (sz < (datasize - used ) ) )
800             sz--;
801         TRACE("adding %u bytes %s\n", sz, data+used );
802         pool[ i*2 ] = sz;
803         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
804         used += sz;
805         if( used > datasize  )
806         {
807             ERR("oops overran %d >= %d\n", used, datasize);
808             goto err;
809         }
810     }
811
812     if( used != datasize )
813     {
814         ERR("oops used %d != datasize %d\n", used, datasize);
815         goto err;
816     }
817
818     /* write the streams */
819     r = write_stream_data( db->storage, szStringData, data, datasize );
820     TRACE("Wrote StringData r=%08x\n", r);
821     if( r )
822         goto err;
823     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
824     TRACE("Wrote StringPool r=%08x\n", r);
825     if( r )
826         goto err;
827
828     ret = ERROR_SUCCESS;
829
830 err:
831     HeapFree( GetProcessHeap(), 0, data );
832     HeapFree( GetProcessHeap(), 0, pool );
833
834     return ret;
835 }
836
837 static LPWSTR strdupW( LPCWSTR str )
838 {
839     UINT len = lstrlenW( str ) + 1;
840     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
841     if( ret )
842         lstrcpyW( ret, str );
843     return ret;
844 }
845
846 /* information for default tables */
847 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
848 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
849 static const WCHAR szName[]    = { 'N','a','m','e',0 };
850 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
851 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
852 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
853 static const WCHAR szType[]    = { 'T','y','p','e',0 };
854
855 struct standard_table {
856     LPCWSTR tablename;
857     LPCWSTR columnname;
858     UINT number;
859     UINT type;
860 } MSI_standard_tables[] =
861 {
862   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
863   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
864   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
865   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
866   { szColumns, szType,   4, MSITYPE_VALID | 2},
867 };
868
869 #define STANDARD_TABLE_COUNT \
870      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
871
872 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
873 {
874     DWORD i, n=0;
875
876     for(i=0; i<STANDARD_TABLE_COUNT; i++)
877     {
878         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
879             continue;
880         if(colinfo && (n < *sz) )
881         {
882             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
883             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
884             colinfo[n].number = MSI_standard_tables[i].number;
885             colinfo[n].type = MSI_standard_tables[i].type;
886             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
887                     debugstr_w(colinfo[n].colname)); */
888             if( n )
889                 colinfo[n].offset = colinfo[n-1].offset
890                                   + bytes_per_column( &colinfo[n-1] );
891             else
892                 colinfo[n].offset = 0;
893         }
894         n++;
895         if( colinfo && (n >= *sz) )
896             break;
897     }
898     *sz = n;
899     return ERROR_SUCCESS;
900 }
901
902 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
903 {
904     UINT sz=0, r;
905     LPWSTR str;
906
907     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
908     if( r != ERROR_SUCCESS )
909         return NULL;
910     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
911     if( !str )
912         return str;
913     r = msi_id2stringW( db->strings, stringid, str, &sz );
914     if( r == ERROR_SUCCESS )
915         return str;
916     HeapFree(  GetProcessHeap(), 0, str );
917     return NULL;
918 }
919
920 static UINT get_tablecolumns( MSIDATABASE *db, 
921        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
922 {
923     UINT r, i, n=0, table_id, count, maxcount = *sz;
924     MSITABLE *table = NULL;
925     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
926
927     /* first check if there is a default table with that name */
928     r = get_defaulttablecolumns( szTableName, colinfo, sz );
929     if( ( r == ERROR_SUCCESS ) && *sz )
930         return r;
931
932     r = get_table( db, szColumns, &table);
933     if( r != ERROR_SUCCESS )
934     {
935         ERR("table %s not available\n", debugstr_w(szColumns));
936         return r;
937     }
938
939     /* convert table and column names to IDs from the string table */
940     r = msi_string2idW( db->strings, szTableName, &table_id );
941     if( r != ERROR_SUCCESS )
942     {
943         release_table( db, table );
944         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
945         return r;
946     }
947
948     TRACE("Table id is %d\n", table_id);
949
950     count = table->row_count;
951     for( i=0; i<count; i++ )
952     {
953         if( table->data[ i ][ 0 ] != table_id )
954             continue;
955         if( colinfo )
956         {
957             UINT id = table->data[ i ] [ 2 ];
958             colinfo[n].tablename = MSI_makestring( db, table_id );
959             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
960             colinfo[n].colname = MSI_makestring( db, id );
961             colinfo[n].type = table->data[ i ] [ 3 ];
962             /* this assumes that columns are in order in the table */
963             if( n )
964                 colinfo[n].offset = colinfo[n-1].offset
965                                   + bytes_per_column( &colinfo[n-1] );
966             else
967                 colinfo[n].offset = 0;
968             TRACE("table %s column %d is [%s] (%d) with type %08x "
969                   "offset %d at row %d\n", debugstr_w(szTableName),
970                    colinfo[n].number, debugstr_w(colinfo[n].colname),
971                    id, colinfo[n].type, colinfo[n].offset, i);
972             if( n != (colinfo[n].number-1) )
973             {
974                 ERR("oops. data in the _Columns table isn't in the right "
975                     "order for table %s\n", debugstr_w(szTableName));
976                 return ERROR_FUNCTION_FAILED;
977             }
978         }
979         n++;
980         if( colinfo && ( n >= maxcount ) )
981             break;
982     }
983     *sz = n;
984
985     release_table( db, table );
986
987     return ERROR_SUCCESS;
988 }
989
990 /* try to find the table name in the _Tables table */
991 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
992 {
993     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
994     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
995     UINT r, table_id = 0, i, count;
996     MSITABLE *table = NULL;
997
998     if( !lstrcmpW( name, szTables ) )
999         return TRUE;
1000     if( !lstrcmpW( name, szColumns ) )
1001         return TRUE;
1002
1003     r = msi_string2idW( db->strings, name, &table_id );
1004     if( r != ERROR_SUCCESS )
1005     {
1006         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1007         return FALSE;
1008     }
1009
1010     r = get_table( db, szTables, &table);
1011     if( r != ERROR_SUCCESS )
1012     {
1013         ERR("table %s not available\n", debugstr_w(szTables));
1014         return FALSE;
1015     }
1016
1017     /* count = table->size/2; */
1018     count = table->row_count;
1019     for( i=0; i<count; i++ )
1020         if( table->data[ i ][ 0 ] == table_id )
1021             break;
1022
1023     release_table( db, table );
1024
1025     if (i!=count)
1026         return TRUE;
1027
1028     ERR("Searched %d tables, but %d was not found\n", count, table_id );
1029
1030     return FALSE;
1031 }
1032
1033 /* below is the query interface to a table */
1034
1035 typedef struct tagMSITABLEVIEW
1036 {
1037     MSIVIEW        view;
1038     MSIDATABASE   *db;
1039     MSITABLE      *table;
1040     MSICOLUMNINFO *columns;
1041     UINT           num_cols;
1042     UINT           row_size;
1043     WCHAR          name[1];
1044 } MSITABLEVIEW;
1045
1046 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1047 {
1048     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1049     UINT offset, num_rows, n;
1050
1051     if( !tv->table )
1052         return ERROR_INVALID_PARAMETER;
1053
1054     if( (col==0) || (col>tv->num_cols) )
1055         return ERROR_INVALID_PARAMETER;
1056
1057     /* how many rows are there ? */
1058     num_rows = tv->table->row_count;
1059     if( row >= num_rows )
1060         return ERROR_NO_MORE_ITEMS;
1061
1062     if( tv->columns[col-1].offset >= tv->row_size )
1063     {
1064         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1065         ERR("%p %p\n", tv, tv->columns );
1066         return ERROR_FUNCTION_FAILED;
1067     }
1068
1069     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1070     n = bytes_per_column( &tv->columns[col-1] );
1071     switch( n )
1072     {
1073     case 4:
1074         offset = tv->columns[col-1].offset/2;
1075         *val = tv->table->data[row][offset] + 
1076                (tv->table->data[row][offset + 1] << 16);
1077         break;
1078     case 2:
1079         offset = tv->columns[col-1].offset/2;
1080         *val = tv->table->data[row][offset];
1081         break;
1082     default:
1083         ERR("oops! what is %d bytes per column?\n", n );
1084         return ERROR_FUNCTION_FAILED;
1085     }
1086
1087     /* TRACE("Data [%d][%d] = %d \n", row, col, *val ); */
1088
1089     return ERROR_SUCCESS;
1090 }
1091
1092 /*
1093  * We need a special case for streams, as we need to reference column with
1094  * the name of the stream in the same table, and the table name
1095  * which may not be available at higher levels of the query
1096  */
1097 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1098 {
1099     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1100     UINT ival = 0, refcol = 0, r;
1101     LPWSTR sval;
1102     LPWSTR full_name;
1103     DWORD len;
1104     static const WCHAR szDot[] = { '.', 0 };
1105
1106     if( !view->ops->fetch_int )
1107         return ERROR_INVALID_PARAMETER;
1108
1109     /*
1110      * The column marked with the type stream data seems to have a single number
1111      * which references the column containing the name of the stream data
1112      *
1113      * Fetch the column to reference first.
1114      */
1115     r = view->ops->fetch_int( view, row, col, &ival );
1116     if( r != ERROR_SUCCESS )
1117         return r;
1118
1119     /* now get the column with the name of the stream */
1120     r = view->ops->fetch_int( view, row, ival, &refcol );
1121     if( r != ERROR_SUCCESS )
1122         return r;
1123
1124     /* lookup the string value from the string table */
1125     sval = MSI_makestring( tv->db, refcol );
1126     if( !sval )
1127         return ERROR_INVALID_PARAMETER;
1128
1129     len = strlenW( tv->name ) + 2 + strlenW( sval );
1130     full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1131     strcpyW( full_name, tv->name );
1132     strcatW( full_name, szDot );
1133     strcatW( full_name, sval );
1134
1135     r = db_get_raw_stream( tv->db, full_name, stm );
1136     if( r )
1137         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1138     HeapFree( GetProcessHeap(), 0, full_name );
1139     HeapFree( GetProcessHeap(), 0, sval );
1140
1141     return r;
1142 }
1143
1144 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1145 {
1146     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1147     UINT offset, n;
1148
1149     if( !tv->table )
1150         return ERROR_INVALID_PARAMETER;
1151
1152     if( (col==0) || (col>tv->num_cols) )
1153         return ERROR_INVALID_PARAMETER;
1154
1155     if( tv->columns[col-1].offset >= tv->row_size )
1156     {
1157         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1158         ERR("%p %p\n", tv, tv->columns );
1159         return ERROR_FUNCTION_FAILED;
1160     }
1161
1162     n = bytes_per_column( &tv->columns[col-1] );
1163     switch( n )
1164     {
1165     case 4:
1166         offset = tv->columns[col-1].offset/2;
1167         tv->table->data[row][offset]     = val & 0xffff;
1168         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1169         break;
1170     case 2:
1171         offset = tv->columns[col-1].offset/2;
1172         tv->table->data[row][offset] = val;
1173         break;
1174     default:
1175         ERR("oops! what is %d bytes per column?\n", n );
1176         return ERROR_FUNCTION_FAILED;
1177     }
1178     return ERROR_SUCCESS;
1179 }
1180
1181 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1182 {
1183     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1184     USHORT **p, *row;
1185     UINT sz;
1186
1187     TRACE("%p\n", view);
1188
1189     if( !tv->table )
1190         return ERROR_INVALID_PARAMETER;
1191
1192     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1193     if( !row )
1194         return ERROR_NOT_ENOUGH_MEMORY;
1195
1196     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1197     if( tv->table->data )
1198         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1199     else
1200         p = HeapAlloc( GetProcessHeap(), 0, sz );
1201     if( !p )
1202     {
1203         HeapFree( GetProcessHeap(), 0, row );
1204         return ERROR_NOT_ENOUGH_MEMORY;
1205     }
1206
1207     tv->table->data = p;
1208     tv->table->data[tv->table->row_count] = row;
1209     *num = tv->table->row_count;
1210     tv->table->row_count++;
1211
1212     return ERROR_SUCCESS;
1213 }
1214
1215 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1216 {
1217     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1218     UINT r;
1219
1220     TRACE("%p %p\n", tv, record);
1221
1222     if( tv->table )
1223     {
1224         release_table( tv->db, tv->table );
1225         tv->table = NULL;
1226     }
1227
1228     r = get_table( tv->db, tv->name, &tv->table );
1229     if( r != ERROR_SUCCESS )
1230         return r;
1231     
1232     return ERROR_SUCCESS;
1233 }
1234
1235 static UINT TABLE_close( struct tagMSIVIEW *view )
1236 {
1237     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1238
1239     TRACE("%p\n", view );
1240
1241     if( !tv->table )
1242         return ERROR_FUNCTION_FAILED;
1243
1244     release_table( tv->db, tv->table );
1245     tv->table = NULL;
1246     
1247     return ERROR_SUCCESS;
1248 }
1249
1250 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1251 {
1252     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1253
1254     TRACE("%p %p %p\n", view, rows, cols );
1255
1256     if( cols )
1257         *cols = tv->num_cols;
1258     if( rows )
1259     {
1260         if( !tv->table )
1261             return ERROR_INVALID_PARAMETER;
1262         *rows = tv->table->row_count;
1263     }
1264
1265     return ERROR_SUCCESS;
1266 }
1267
1268 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1269                 UINT n, LPWSTR *name, UINT *type )
1270 {
1271     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1272
1273     TRACE("%p %d %p %p\n", tv, n, name, type );
1274
1275     if( ( n == 0 ) || ( n > tv->num_cols ) )
1276         return ERROR_INVALID_PARAMETER;
1277
1278     if( name )
1279     {
1280         *name = strdupW( tv->columns[n-1].colname );
1281         if( !*name )
1282             return ERROR_FUNCTION_FAILED;
1283     }
1284     if( type )
1285         *type = tv->columns[n-1].type;
1286
1287     return ERROR_SUCCESS;
1288 }
1289
1290 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1291                 MSIRECORD *rec)
1292 {
1293     FIXME("%p %d %p\n", view, eModifyMode, rec );
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 }