Return error from low level driver when necessary.
[wine.git] / dlls / msi / string.c
blob8cc4dc1d53297f48df4630f5b3ad64f3ddf772bd
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-2004, Mike McCormack for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
22 #include <assert.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 #include "msi.h"
30 #include "msiquery.h"
31 #include "objbase.h"
32 #include "objidl.h"
33 #include "msipriv.h"
34 #include "winnls.h"
36 #include "query.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40 typedef struct _msistring
42 UINT hash;
43 UINT refcount;
44 LPWSTR str;
45 } msistring;
47 struct string_table
49 UINT maxcount; /* the number of strings */
50 UINT freeslot;
51 UINT codepage;
52 msistring *strings; /* an array of strings (in the tree) */
55 static UINT msistring_makehash( const WCHAR *str )
57 UINT hash = 0;
59 if (str==NULL)
60 return hash;
62 while( *str )
64 hash ^= *str++;
65 hash *= 53;
66 hash = (hash<<5) | (hash>>27);
68 return hash;
71 string_table *msi_init_stringtable( int entries, UINT codepage )
73 string_table *st;
75 st = HeapAlloc( GetProcessHeap(), 0, sizeof (string_table) );
76 if( !st )
77 return NULL;
78 st->strings = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
79 sizeof (msistring) * entries );
80 if( !st )
82 HeapFree( GetProcessHeap(), 0, st );
83 return NULL;
85 if( entries < 1 )
86 entries = 1;
87 st->maxcount = entries;
88 st->freeslot = 1;
89 st->codepage = codepage;
91 return st;
94 VOID msi_destroy_stringtable( string_table *st )
96 UINT i;
98 for( i=0; i<st->maxcount; i++ )
100 if( st->strings[i].refcount )
101 HeapFree( GetProcessHeap(), 0, st->strings[i].str );
103 HeapFree( GetProcessHeap(), 0, st->strings );
104 HeapFree( GetProcessHeap(), 0, st );
107 static int st_find_free_entry( string_table *st )
109 UINT i, sz;
110 msistring *p;
112 TRACE("%p\n", st);
114 if( st->freeslot )
116 for( i = st->freeslot; i < st->maxcount; i++ )
117 if( !st->strings[i].refcount )
118 return i;
120 for( i = 1; i < st->maxcount; i++ )
121 if( !st->strings[i].refcount )
122 return i;
124 /* dynamically resize */
125 sz = st->maxcount + 1 + st->maxcount/2;
126 p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
127 st->strings, sz*sizeof(msistring) );
128 if( !p )
129 return -1;
130 st->strings = p;
131 st->freeslot = st->maxcount;
132 st->maxcount = sz;
133 if( st->strings[st->freeslot].refcount )
134 ERR("oops. expected freeslot to be free...\n");
135 return st->freeslot;
138 static void st_mark_entry_used( string_table *st, UINT n )
140 if( n >= st->maxcount )
141 return;
142 st->freeslot = n + 1;
145 int msi_addstring( string_table *st, int n, const CHAR *data, int len, UINT refcount )
147 int sz;
149 if( !data )
150 return 0;
151 if( !data[0] )
152 return 0;
153 if( n > 0 )
155 if( st->strings[n].refcount )
156 return -1;
158 else
160 if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
162 st->strings[n].refcount++;
163 return n;
165 n = st_find_free_entry( st );
166 if( n < 0 )
167 return -1;
170 if( n < 1 )
172 ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n );
173 return -1;
176 /* allocate a new string */
177 if( len < 0 )
178 len = strlen(data);
179 sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 );
180 st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (sz+1)*sizeof(WCHAR) );
181 if( !st->strings[n].str )
182 return -1;
183 MultiByteToWideChar( st->codepage, 0, data, len, st->strings[n].str, sz );
184 st->strings[n].str[sz] = 0;
185 st->strings[n].refcount = 1;
186 st->strings[n].hash = msistring_makehash( st->strings[n].str );
188 st_mark_entry_used( st, n );
190 return n;
193 int msi_addstringW( string_table *st, int n, const WCHAR *data, int len, UINT refcount )
195 /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
197 if( !data )
198 return 0;
199 if( !data[0] )
200 return 0;
201 if( n > 0 )
203 if( st->strings[n].refcount )
204 return -1;
206 else
208 if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
210 st->strings[n].refcount++;
211 return n;
213 n = st_find_free_entry( st );
214 if( n < 0 )
215 return -1;
218 if( n < 1 )
220 ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
221 return -1;
224 /* allocate a new string */
225 if(len<0)
226 len = strlenW(data);
227 TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len );
229 st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof(WCHAR) );
230 if( !st->strings[n].str )
231 return -1;
232 TRACE("%d\n",__LINE__);
233 memcpy( st->strings[n].str, data, len*sizeof(WCHAR) );
234 st->strings[n].str[len] = 0;
235 st->strings[n].refcount = 1;
236 st->strings[n].hash = msistring_makehash( st->strings[n].str );
238 st_mark_entry_used( st, n );
240 return n;
243 /* find the string identified by an id - return null if there's none */
244 const WCHAR *msi_string_lookup_id( string_table *st, UINT id )
246 static const WCHAR zero[] = { 0 };
247 if( id == 0 )
248 return zero;
250 if( id >= st->maxcount )
251 return NULL;
253 if( id && !st->strings[id].refcount )
254 return NULL;
256 return st->strings[id].str;
260 * msi_id2stringW
262 * [in] st - pointer to the string table
263 * [in] id - id of the string to retrieve
264 * [out] buffer - destination of the string
265 * [in/out] sz - number of bytes available in the buffer on input
266 * number of bytes used on output
268 * The size includes the terminating nul character. Short buffers
269 * will be filled, but not nul terminated.
271 UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz )
273 UINT len;
274 const WCHAR *str;
276 TRACE("Finding string %d of %d\n", id, st->maxcount);
278 str = msi_string_lookup_id( st, id );
279 if( !str )
280 return ERROR_FUNCTION_FAILED;
282 len = strlenW( str ) + 1;
284 if( !buffer )
286 *sz = len;
287 return ERROR_SUCCESS;
290 if( *sz < len )
291 *sz = len;
292 memcpy( buffer, str, (*sz)*sizeof(WCHAR) );
293 *sz = len;
295 return ERROR_SUCCESS;
299 * msi_id2stringA
301 * [in] st - pointer to the string table
302 * [in] id - id of the string to retrieve
303 * [out] buffer - destination of the UTF8 string
304 * [in/out] sz - number of bytes available in the buffer on input
305 * number of bytes used on output
307 * The size includes the terminating nul character. Short buffers
308 * will be filled, but not nul terminated.
310 UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz )
312 UINT len;
313 const WCHAR *str;
314 int n;
316 TRACE("Finding string %d of %d\n", id, st->maxcount);
318 str = msi_string_lookup_id( st, id );
319 if( !str )
320 return ERROR_FUNCTION_FAILED;
322 len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL );
324 if( !buffer )
326 *sz = len;
327 return ERROR_SUCCESS;
330 if( len > *sz )
332 n = strlenW( str ) + 1;
333 while( n && (len > *sz) )
334 len = WideCharToMultiByte( st->codepage, 0,
335 str, --n, NULL, 0, NULL, NULL );
337 else
338 n = -1;
340 *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL );
342 return ERROR_SUCCESS;
346 * msi_string2idW
348 * [in] st - pointer to the string table
349 * [in] str - string to find in the string table
350 * [out] id - id of the string, if found
352 UINT msi_string2idW( string_table *st, LPCWSTR str, UINT *id )
354 UINT hash;
355 UINT i, r = ERROR_INVALID_PARAMETER;
357 hash = msistring_makehash( str );
358 for( i=0; i<st->maxcount; i++ )
360 if ( (str == NULL && st->strings[i].str == NULL) ||
361 ( ( st->strings[i].hash == hash ) &&
362 !strcmpW( st->strings[i].str, str ) ))
364 r = ERROR_SUCCESS;
365 *id = i;
366 break;
370 return r;
373 UINT msi_string2idA( string_table *st, LPCSTR buffer, UINT *id )
375 DWORD sz;
376 UINT r = ERROR_INVALID_PARAMETER;
377 LPWSTR str;
379 TRACE("Finding string %s in string table\n", debugstr_a(buffer) );
381 if( buffer[0] == 0 )
383 *id = 0;
384 return ERROR_SUCCESS;
387 sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 );
388 if( sz <= 0 )
389 return r;
390 str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
391 if( !str )
392 return ERROR_NOT_ENOUGH_MEMORY;
393 MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz );
395 r = msi_string2idW( st, str, id );
396 if( str )
397 HeapFree( GetProcessHeap(), 0, str );
399 return r;
402 UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res )
404 const WCHAR *l_str, *r_str;
406 l_str = msi_string_lookup_id( st, lval );
407 if( !l_str )
408 return ERROR_INVALID_PARAMETER;
410 r_str = msi_string_lookup_id( st, rval );
411 if( !r_str )
412 return ERROR_INVALID_PARAMETER;
414 /* does this do the right thing for all UTF-8 strings? */
415 *res = strcmpW( l_str, r_str );
417 return ERROR_SUCCESS;
420 UINT msi_string_count( string_table *st )
422 return st->maxcount;
425 UINT msi_id_refcount( string_table *st, UINT i )
427 if( i >= st->maxcount )
428 return 0;
429 return st->strings[i].refcount;
432 UINT msi_string_totalsize( string_table *st, UINT *total )
434 UINT size = 0, i, len;
436 if( st->strings[0].str || st->strings[0].refcount )
437 ERR("oops. element 0 has a string\n");
438 *total = 0;
439 for( i=1; i<st->maxcount; i++ )
441 if( st->strings[i].str )
443 TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str));
444 len = WideCharToMultiByte( st->codepage, 0,
445 st->strings[i].str, -1, NULL, 0, NULL, NULL);
446 if( len )
447 len--;
448 size += len;
449 *total = (i+1);
452 TRACE("%u/%u strings %u bytes codepage %x\n", *total, st->maxcount, size, st->codepage );
453 return size;
456 UINT msi_string_get_codepage( string_table *st )
458 return st->codepage;