Expand some handling of special properties in MsiGetProductInfo.
[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 /* information for default tables */
838 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
839 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
840 static const WCHAR szName[]    = { 'N','a','m','e',0 };
841 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
842 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
843 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
844 static const WCHAR szType[]    = { 'T','y','p','e',0 };
845
846 struct standard_table {
847     LPCWSTR tablename;
848     LPCWSTR columnname;
849     UINT number;
850     UINT type;
851 } MSI_standard_tables[] =
852 {
853   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
854   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
855   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
856   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
857   { szColumns, szType,   4, MSITYPE_VALID | 2},
858 };
859
860 #define STANDARD_TABLE_COUNT \
861      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
862
863 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
864 {
865     DWORD i, n=0;
866
867     for(i=0; i<STANDARD_TABLE_COUNT; i++)
868     {
869         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
870             continue;
871         if(colinfo && (n < *sz) )
872         {
873             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
874             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
875             colinfo[n].number = MSI_standard_tables[i].number;
876             colinfo[n].type = MSI_standard_tables[i].type;
877             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
878                     debugstr_w(colinfo[n].colname)); */
879             if( n )
880                 colinfo[n].offset = colinfo[n-1].offset
881                                   + bytes_per_column( &colinfo[n-1] );
882             else
883                 colinfo[n].offset = 0;
884         }
885         n++;
886         if( colinfo && (n >= *sz) )
887             break;
888     }
889     *sz = n;
890     return ERROR_SUCCESS;
891 }
892
893 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
894 {
895     UINT sz=0, r;
896     LPWSTR str;
897
898     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
899     if( r != ERROR_SUCCESS )
900         return NULL;
901     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
902     if( !str )
903         return str;
904     r = msi_id2stringW( db->strings, stringid, str, &sz );
905     if( r == ERROR_SUCCESS )
906         return str;
907     HeapFree(  GetProcessHeap(), 0, str );
908     return NULL;
909 }
910
911 static UINT get_tablecolumns( MSIDATABASE *db, 
912        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
913 {
914     UINT r, i, n=0, table_id, count, maxcount = *sz;
915     MSITABLE *table = NULL;
916     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
917
918     /* first check if there is a default table with that name */
919     r = get_defaulttablecolumns( szTableName, colinfo, sz );
920     if( ( r == ERROR_SUCCESS ) && *sz )
921         return r;
922
923     r = get_table( db, szColumns, &table);
924     if( r != ERROR_SUCCESS )
925     {
926         ERR("table %s not available\n", debugstr_w(szColumns));
927         return r;
928     }
929
930     /* convert table and column names to IDs from the string table */
931     r = msi_string2idW( db->strings, szTableName, &table_id );
932     if( r != ERROR_SUCCESS )
933     {
934         release_table( db, table );
935         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
936         return r;
937     }
938
939     TRACE("Table id is %d\n", table_id);
940
941     count = table->row_count;
942     for( i=0; i<count; i++ )
943     {
944         if( table->data[ i ][ 0 ] != table_id )
945             continue;
946         if( colinfo )
947         {
948             UINT id = table->data[ i ] [ 2 ];
949             colinfo[n].tablename = MSI_makestring( db, table_id );
950             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
951             colinfo[n].colname = MSI_makestring( db, id );
952             colinfo[n].type = table->data[ i ] [ 3 ];
953             /* this assumes that columns are in order in the table */
954             if( n )
955                 colinfo[n].offset = colinfo[n-1].offset
956                                   + bytes_per_column( &colinfo[n-1] );
957             else
958                 colinfo[n].offset = 0;
959             TRACE("table %s column %d is [%s] (%d) with type %08x "
960                   "offset %d at row %d\n", debugstr_w(szTableName),
961                    colinfo[n].number, debugstr_w(colinfo[n].colname),
962                    id, colinfo[n].type, colinfo[n].offset, i);
963             if( n != (colinfo[n].number-1) )
964             {
965                 ERR("oops. data in the _Columns table isn't in the right "
966                     "order for table %s\n", debugstr_w(szTableName));
967                 return ERROR_FUNCTION_FAILED;
968             }
969         }
970         n++;
971         if( colinfo && ( n >= maxcount ) )
972             break;
973     }
974     *sz = n;
975
976     release_table( db, table );
977
978     return ERROR_SUCCESS;
979 }
980
981 /* try to find the table name in the _Tables table */
982 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
983 {
984     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
985     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
986     UINT r, table_id = 0, i, count;
987     MSITABLE *table = NULL;
988
989     if( !lstrcmpW( name, szTables ) )
990         return TRUE;
991     if( !lstrcmpW( name, szColumns ) )
992         return TRUE;
993
994     r = msi_string2idW( db->strings, name, &table_id );
995     if( r != ERROR_SUCCESS )
996     {
997         TRACE("Couldn't find id for %s\n", debugstr_w(name));
998         return FALSE;
999     }
1000
1001     r = get_table( db, szTables, &table);
1002     if( r != ERROR_SUCCESS )
1003     {
1004         ERR("table %s not available\n", debugstr_w(szTables));
1005         return FALSE;
1006     }
1007
1008     /* count = table->size/2; */
1009     count = table->row_count;
1010     for( i=0; i<count; i++ )
1011         if( table->data[ i ][ 0 ] == table_id )
1012             break;
1013
1014     release_table( db, table );
1015
1016     if (i!=count)
1017         return TRUE;
1018
1019     ERR("Searched %d tables, but %d was not found\n", count, table_id );
1020
1021     return FALSE;
1022 }
1023
1024 /* below is the query interface to a table */
1025
1026 typedef struct tagMSITABLEVIEW
1027 {
1028     MSIVIEW        view;
1029     MSIDATABASE   *db;
1030     MSITABLE      *table;
1031     MSICOLUMNINFO *columns;
1032     UINT           num_cols;
1033     UINT           row_size;
1034     WCHAR          name[1];
1035 } MSITABLEVIEW;
1036
1037 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1038 {
1039     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1040     UINT offset, num_rows, n;
1041
1042     if( !tv->table )
1043         return ERROR_INVALID_PARAMETER;
1044
1045     if( (col==0) || (col>tv->num_cols) )
1046         return ERROR_INVALID_PARAMETER;
1047
1048     /* how many rows are there ? */
1049     num_rows = tv->table->row_count;
1050     if( row >= num_rows )
1051         return ERROR_NO_MORE_ITEMS;
1052
1053     if( tv->columns[col-1].offset >= tv->row_size )
1054     {
1055         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1056         ERR("%p %p\n", tv, tv->columns );
1057         return ERROR_FUNCTION_FAILED;
1058     }
1059
1060     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1061     n = bytes_per_column( &tv->columns[col-1] );
1062     switch( n )
1063     {
1064     case 4:
1065         offset = tv->columns[col-1].offset/2;
1066         *val = tv->table->data[row][offset] + 
1067                (tv->table->data[row][offset + 1] << 16);
1068         break;
1069     case 2:
1070         offset = tv->columns[col-1].offset/2;
1071         *val = tv->table->data[row][offset];
1072         break;
1073     default:
1074         ERR("oops! what is %d bytes per column?\n", n );
1075         return ERROR_FUNCTION_FAILED;
1076     }
1077
1078     /* TRACE("Data [%d][%d] = %d \n", row, col, *val ); */
1079
1080     return ERROR_SUCCESS;
1081 }
1082
1083 /*
1084  * We need a special case for streams, as we need to reference column with
1085  * the name of the stream in the same table, and the table name
1086  * which may not be available at higher levels of the query
1087  */
1088 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1089 {
1090     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1091     UINT ival = 0, refcol = 0, r;
1092     LPWSTR sval;
1093     LPWSTR full_name;
1094     DWORD len;
1095     static const WCHAR szDot[] = { '.', 0 };
1096
1097     if( !view->ops->fetch_int )
1098         return ERROR_INVALID_PARAMETER;
1099
1100     /*
1101      * The column marked with the type stream data seems to have a single number
1102      * which references the column containing the name of the stream data
1103      *
1104      * Fetch the column to reference first.
1105      */
1106     r = view->ops->fetch_int( view, row, col, &ival );
1107     if( r != ERROR_SUCCESS )
1108         return r;
1109
1110     /* now get the column with the name of the stream */
1111     r = view->ops->fetch_int( view, row, ival, &refcol );
1112     if( r != ERROR_SUCCESS )
1113         return r;
1114
1115     /* lookup the string value from the string table */
1116     sval = MSI_makestring( tv->db, refcol );
1117     if( !sval )
1118         return ERROR_INVALID_PARAMETER;
1119
1120     len = strlenW( tv->name ) + 2 + strlenW( sval );
1121     full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
1122     strcpyW( full_name, tv->name );
1123     strcatW( full_name, szDot );
1124     strcatW( full_name, sval );
1125
1126     r = db_get_raw_stream( tv->db, full_name, stm );
1127     if( r )
1128         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1129     HeapFree( GetProcessHeap(), 0, full_name );
1130     HeapFree( GetProcessHeap(), 0, sval );
1131
1132     return r;
1133 }
1134
1135 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1136 {
1137     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1138     UINT offset, n;
1139
1140     if( !tv->table )
1141         return ERROR_INVALID_PARAMETER;
1142
1143     if( (col==0) || (col>tv->num_cols) )
1144         return ERROR_INVALID_PARAMETER;
1145
1146     if( tv->columns[col-1].offset >= tv->row_size )
1147     {
1148         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1149         ERR("%p %p\n", tv, tv->columns );
1150         return ERROR_FUNCTION_FAILED;
1151     }
1152
1153     n = bytes_per_column( &tv->columns[col-1] );
1154     switch( n )
1155     {
1156     case 4:
1157         offset = tv->columns[col-1].offset/2;
1158         tv->table->data[row][offset]     = val & 0xffff;
1159         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1160         break;
1161     case 2:
1162         offset = tv->columns[col-1].offset/2;
1163         tv->table->data[row][offset] = val;
1164         break;
1165     default:
1166         ERR("oops! what is %d bytes per column?\n", n );
1167         return ERROR_FUNCTION_FAILED;
1168     }
1169     return ERROR_SUCCESS;
1170 }
1171
1172 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1173 {
1174     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1175     USHORT **p, *row;
1176     UINT sz;
1177
1178     TRACE("%p\n", view);
1179
1180     if( !tv->table )
1181         return ERROR_INVALID_PARAMETER;
1182
1183     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1184     if( !row )
1185         return ERROR_NOT_ENOUGH_MEMORY;
1186
1187     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1188     if( tv->table->data )
1189         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1190     else
1191         p = HeapAlloc( GetProcessHeap(), 0, sz );
1192     if( !p )
1193     {
1194         HeapFree( GetProcessHeap(), 0, row );
1195         return ERROR_NOT_ENOUGH_MEMORY;
1196     }
1197
1198     tv->table->data = p;
1199     tv->table->data[tv->table->row_count] = row;
1200     *num = tv->table->row_count;
1201     tv->table->row_count++;
1202
1203     return ERROR_SUCCESS;
1204 }
1205
1206 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1207 {
1208     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1209     UINT r;
1210
1211     TRACE("%p %p\n", tv, record);
1212
1213     if( tv->table )
1214     {
1215         release_table( tv->db, tv->table );
1216         tv->table = NULL;
1217     }
1218
1219     r = get_table( tv->db, tv->name, &tv->table );
1220     if( r != ERROR_SUCCESS )
1221         return r;
1222     
1223     return ERROR_SUCCESS;
1224 }
1225
1226 static UINT TABLE_close( struct tagMSIVIEW *view )
1227 {
1228     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1229
1230     TRACE("%p\n", view );
1231
1232     if( !tv->table )
1233         return ERROR_FUNCTION_FAILED;
1234
1235     release_table( tv->db, tv->table );
1236     tv->table = NULL;
1237     
1238     return ERROR_SUCCESS;
1239 }
1240
1241 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1242 {
1243     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1244
1245     TRACE("%p %p %p\n", view, rows, cols );
1246
1247     if( cols )
1248         *cols = tv->num_cols;
1249     if( rows )
1250     {
1251         if( !tv->table )
1252             return ERROR_INVALID_PARAMETER;
1253         *rows = tv->table->row_count;
1254     }
1255
1256     return ERROR_SUCCESS;
1257 }
1258
1259 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1260                 UINT n, LPWSTR *name, UINT *type )
1261 {
1262     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1263
1264     TRACE("%p %d %p %p\n", tv, n, name, type );
1265
1266     if( ( n == 0 ) || ( n > tv->num_cols ) )
1267         return ERROR_INVALID_PARAMETER;
1268
1269     if( name )
1270     {
1271         *name = strdupW( tv->columns[n-1].colname );
1272         if( !*name )
1273             return ERROR_FUNCTION_FAILED;
1274     }
1275     if( type )
1276         *type = tv->columns[n-1].type;
1277
1278     return ERROR_SUCCESS;
1279 }
1280
1281 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1282                 MSIRECORD *rec)
1283 {
1284     FIXME("%p %d %p\n", view, eModifyMode, rec );
1285     return ERROR_CALL_NOT_IMPLEMENTED;
1286 }
1287
1288 static UINT TABLE_delete( struct tagMSIVIEW *view )
1289 {
1290     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1291
1292     TRACE("%p\n", view );
1293
1294     if( tv->table )
1295         release_table( tv->db, tv->table );
1296     tv->table = NULL;
1297
1298     if( tv->columns )
1299     {
1300         UINT i;
1301         for( i=0; i<tv->num_cols; i++)
1302         {
1303             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1304             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1305         }
1306         HeapFree( GetProcessHeap(), 0, tv->columns );
1307     }
1308     tv->columns = NULL;
1309
1310     HeapFree( GetProcessHeap(), 0, tv );
1311
1312     return ERROR_SUCCESS;
1313 }
1314
1315
1316 MSIVIEWOPS table_ops =
1317 {
1318     TABLE_fetch_int,
1319     TABLE_fetch_stream,
1320     TABLE_set_int,
1321     TABLE_insert_row,
1322     TABLE_execute,
1323     TABLE_close,
1324     TABLE_get_dimensions,
1325     TABLE_get_column_info,
1326     TABLE_modify,
1327     TABLE_delete
1328 };
1329
1330 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1331 {
1332     MSITABLEVIEW *tv ;
1333     UINT r, sz, column_count;
1334     MSICOLUMNINFO *columns, *last_col;
1335
1336     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1337
1338     /* get the number of columns in this table */
1339     column_count = 0;
1340     r = get_tablecolumns( db, name, NULL, &column_count );
1341     if( r != ERROR_SUCCESS )
1342         return r;
1343
1344     /* if there's no columns, there's no table */
1345     if( column_count == 0 )
1346         return ERROR_INVALID_PARAMETER;
1347
1348     TRACE("Table found\n");
1349
1350     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1351     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1352     if( !tv )
1353         return ERROR_FUNCTION_FAILED;
1354     
1355     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1356     if( !columns )
1357     {
1358         HeapFree( GetProcessHeap(), 0, tv );
1359         return ERROR_FUNCTION_FAILED;
1360     }
1361
1362     r = get_tablecolumns( db, name, columns, &column_count );
1363     if( r != ERROR_SUCCESS )
1364     {
1365         HeapFree( GetProcessHeap(), 0, columns );
1366         HeapFree( GetProcessHeap(), 0, tv );
1367         return ERROR_FUNCTION_FAILED;
1368     }
1369
1370     TRACE("Table has %d columns\n", column_count);
1371
1372     last_col = &columns[column_count-1];
1373
1374     /* fill the structure */
1375     tv->view.ops = &table_ops;
1376     tv->db = db;
1377     tv->columns = columns;
1378     tv->num_cols = column_count;
1379     tv->table = NULL;
1380     tv->row_size = last_col->offset + bytes_per_column( last_col );
1381
1382     TRACE("one row is %d bytes\n", tv->row_size );
1383
1384     *view = (MSIVIEW*) tv;
1385     lstrcpyW( tv->name, name );
1386
1387     return ERROR_SUCCESS;
1388 }
1389
1390 UINT MSI_CommitTables( MSIDATABASE *db )
1391 {
1392     UINT r;
1393     MSITABLE *table = NULL;
1394
1395     TRACE("%p\n",db);
1396
1397     r = save_string_table( db );
1398     if( r != ERROR_SUCCESS )
1399     {
1400         ERR("failed to save string table r=%08x\n",r);
1401         return r;
1402     }
1403
1404     for( table = db->first_table; table; table = table->next )
1405     {
1406         r = save_table( db, table );
1407         if( r != ERROR_SUCCESS )
1408         {
1409             ERR("failed to save table %s (r=%08x)\n",
1410                   debugstr_w(table->name), r);
1411             return r;
1412         }
1413     }
1414
1415     /* force everything to reload next time */
1416     free_cached_tables( db );
1417
1418     return ERROR_SUCCESS;
1419 }