Added version info to MSI dll.
[wine] / dlls / msi / string.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 #include <assert.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "wine/debug.h"
28 #include "msi.h"
29 #include "msiquery.h"
30 #include "objbase.h"
31 #include "objidl.h"
32 #include "msipriv.h"
33 #include "winnls.h"
34
35 #include "query.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msi);
38
39 typedef struct _msistring
40 {
41     UINT hash;
42     UINT refcount;
43     LPSTR str;
44 } msistring;
45
46 struct string_table
47 {
48     UINT count;         /* the number of strings */
49     UINT freeslot;
50     msistring *strings; /* an array of strings (in the tree) */
51 };
52
53 static int msistring_makehash( const char *str )
54 {
55     int hash = 0;
56
57     while( *str )
58     {
59         hash ^= *str++;
60         hash *= 53;
61         hash = (hash<<5) || (hash>>27);
62     }
63     return hash;
64 }
65
66 string_table *msi_init_stringtable( int entries )
67 {
68     string_table *st;
69
70     st = HeapAlloc( GetProcessHeap(), 0, sizeof (string_table) );
71     if( !st )
72         return NULL;    
73     st->strings = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
74                               sizeof (msistring) * entries );
75     if( !st )
76     {
77         HeapFree( GetProcessHeap(), 0, st );
78         return NULL;    
79     }
80     st->count = entries;
81     st->freeslot = 1;
82
83     return st;
84 }
85
86 VOID msi_destroy_stringtable( string_table *st )
87 {
88     UINT i;
89
90     for( i=0; i<st->count; i++ )
91     {
92         if( st->strings[i].refcount )
93             HeapFree( GetProcessHeap(), 0, st->strings[i].str );
94     }
95     HeapFree( GetProcessHeap(), 0, st->strings );
96     HeapFree( GetProcessHeap(), 0, st );
97 }
98
99 static int st_find_free_entry( string_table *st )
100 {
101     int i, sz;
102     msistring *p;
103
104     for( i = st->freeslot; i < st->count; i++ )
105         if( !st->strings[i].refcount )
106             return i;
107     for( i = 1; i < st->freeslot; i++ )
108         if( !st->strings[i].refcount )
109             return i;
110
111     /* dynamically resize */
112     sz = st->count + 1 + st->count/2;
113     p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
114                      st->strings, sz*sizeof(msistring) );
115     if( !p )
116         return -1;
117     st->strings = p;
118     st->freeslot = st->count;
119     st->count = sz;
120     if( st->strings[st->freeslot].refcount )
121         ERR("oops. expected freeslot to be free...\n");
122     return st->freeslot;
123 }
124
125 static void st_mark_entry_used( string_table *st, int n )
126 {
127     if( n >= st->count )
128         return;
129     st->freeslot = n + 1;
130 }
131
132 int msi_addstring( string_table *st, UINT n, const CHAR *data, UINT len, UINT refcount )
133 {
134     if( !data[0] )
135         return 0;
136     if( n > 0 )
137     {
138         if( st->strings[n].refcount )
139             return -1;
140     }
141     else
142     {
143         if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
144         {
145             st->strings[n].refcount++;
146             return n;
147         }
148         n = st_find_free_entry( st );
149         if( n < 0 )
150             return -1;
151     }
152
153     /* allocate a new string */
154     if( len < 0 )
155         len = strlen( data );
156     st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, len + 1 );
157     if( !st->strings[n].str )
158         return -1;
159     memcpy( st->strings[n].str, data, len );
160     st->strings[n].str[len] = 0;
161     st->strings[n].refcount = 1;
162     st->strings[n].hash = msistring_makehash( st->strings[n].str );
163
164     st_mark_entry_used( st, n );
165
166     return n;
167 }
168
169 int msi_addstringW( string_table *st, UINT n, const WCHAR *data, UINT len, UINT refcount )
170 {
171     int sz;
172
173     /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
174
175     if( !data[0] )
176         return 0;
177     if( n > 0 )
178     {
179         if( st->strings[n].refcount )
180             return -1;
181     }
182     else
183     {
184         if( ERROR_SUCCESS == msi_string2id( st, data, &n ) )
185         {
186             st->strings[n].refcount++;
187             return n;
188         }
189         n = st_find_free_entry( st );
190         if( n < 0 )
191             return -1;
192     }
193
194     /* allocate a new string */
195     sz = WideCharToMultiByte( CP_UTF8, 0, data, len, NULL, 0, NULL, NULL );
196     st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, sz + 1 );
197     if( !st->strings[n].str )
198         return -1;
199     WideCharToMultiByte( CP_UTF8, 0, data, len, 
200                          st->strings[n].str, sz, NULL, NULL );
201     st->strings[n].str[sz] = 0;
202     st->strings[n].refcount = 1;
203     st->strings[n].hash = msistring_makehash( st->strings[n].str );
204
205     st_mark_entry_used( st, n );
206
207     return n;
208 }
209
210 /* find the string identified by an id - return null if there's none */
211 static const char *string_lookup_id( string_table *st, UINT id )
212 {
213     if( id == 0 )
214         return "";
215
216     if( id >= st->count )
217         return NULL;
218
219     if( id && !st->strings[id].refcount )
220         return NULL;
221
222     return st->strings[id].str;
223 }
224
225 /*
226  *  msi_id2stringW
227  *
228  *  [in] st         - pointer to the string table
229  *  [in] id  - id of the string to retreive
230  *  [out] buffer    - destination of the string
231  *  [in/out] sz     - number of bytes available in the buffer on input
232  *                    number of bytes used on output
233  *
234  *   The size includes the terminating nul character.  Short buffers
235  *  will be filled, but not nul terminated.
236  */
237 UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz )
238 {
239     UINT len;
240     const char *str;
241
242     TRACE("Finding string %d of %d\n", id, st->count);
243
244     str = string_lookup_id( st, id );
245     if( !str )
246         return ERROR_FUNCTION_FAILED;
247
248     len = MultiByteToWideChar(CP_UTF8,0,str,-1,NULL,0); 
249
250     if( !buffer )
251     {
252         *sz = len;
253         return ERROR_SUCCESS;
254     }
255
256     *sz = MultiByteToWideChar(CP_UTF8,0,str,-1,buffer,*sz); 
257
258     return ERROR_SUCCESS;
259 }
260
261 /*
262  *  msi_id2stringA
263  *
264  *  [in] st         - pointer to the string table
265  *  [in] id         - id of the string to retreive
266  *  [out] buffer    - destination of the UTF8 string
267  *  [in/out] sz     - number of bytes available in the buffer on input
268  *                    number of bytes used on output
269  *
270  *   The size includes the terminating nul character.  Short buffers
271  *  will be filled, but not nul terminated.
272  */
273 UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz )
274 {
275     UINT len;
276     const char *str;
277
278     TRACE("Finding string %d of %d\n", id, st->count);
279
280     str = string_lookup_id( st, id );
281     if( !str )
282         return ERROR_FUNCTION_FAILED;
283
284     len = strlen( str ) + 1;
285
286     if( !buffer )
287     {
288         *sz = len;
289         return ERROR_SUCCESS;
290     }
291
292     if( *sz < len )
293         *sz = len;
294     memcpy( buffer, str, *sz ); 
295     *sz = len;
296
297     return ERROR_SUCCESS;
298 }
299
300 /*
301  *  msi_string2idA
302  *
303  *  [in] st         - pointer to the string table
304  *  [in] str        - UTF8 string to find in the string table
305  *  [out] id        - id of the string, if found
306  */
307 UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id )
308 {
309     int hash;
310     UINT i, r = ERROR_INVALID_PARAMETER;
311
312     hash = msistring_makehash( str );
313     for( i=0; i<st->count; i++ )
314     {
315         if( ( st->strings[i].hash == hash ) &&
316             !strcmp( st->strings[i].str, str ) )
317         {
318             r = ERROR_SUCCESS;
319             *id = i;
320             break;
321         }
322     }
323
324     return r;
325 }
326
327 UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id )
328 {
329     DWORD sz;
330     UINT r = ERROR_INVALID_PARAMETER;
331     LPSTR str;
332
333     TRACE("Finding string %s in string table\n", debugstr_w(buffer) );
334
335     if( buffer[0] == 0 )
336     {
337         *id = 0;
338         return ERROR_SUCCESS;
339     }
340
341     sz = WideCharToMultiByte( CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL );
342     if( sz <= 0 )
343         return r;
344     str = HeapAlloc( GetProcessHeap(), 0, sz );
345     if( !str )
346         return ERROR_NOT_ENOUGH_MEMORY;
347     WideCharToMultiByte( CP_UTF8, 0, buffer, -1, str, sz, NULL, NULL );
348
349     r = msi_string2idA( st, str, id );
350     if( str )
351         HeapFree( GetProcessHeap(), 0, str );
352
353     return r;
354 }
355
356
357 UINT msi_string_count( string_table *st )
358 {
359     return st->count;
360 }
361
362 UINT msi_id_refcount( string_table *st, UINT i )
363 {
364     if( i >= st->count )
365         return 0;
366     return st->strings[i].refcount;
367 }
368
369 UINT msi_string_totalsize( string_table *st )
370 {
371     UINT size = 0, i;
372
373     for( i=0; i<st->count; i++)
374     {
375         if( st->strings[i].str )
376             size += strlen( st->strings[i].str );
377     }
378     return size;
379 }