Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenstring.c
Go to the documentation of this file.
00001 /* 00002 * String Table Functions 00003 * 00004 * Copyright 2002-2004, Mike McCormack for CodeWeavers 00005 * Copyright 2007 Robert Shearman for CodeWeavers 00006 * Copyright 2010 Hans Leidekker for CodeWeavers 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00021 */ 00022 00023 #define COBJMACROS 00024 00025 #include <stdarg.h> 00026 #include <assert.h> 00027 00028 #include "windef.h" 00029 #include "winbase.h" 00030 #include "winerror.h" 00031 #include "wine/debug.h" 00032 #include "wine/unicode.h" 00033 #include "msi.h" 00034 #include "msiquery.h" 00035 #include "objbase.h" 00036 #include "objidl.h" 00037 #include "msipriv.h" 00038 #include "winnls.h" 00039 00040 #include "query.h" 00041 00042 WINE_DEFAULT_DEBUG_CHANNEL(msidb); 00043 00044 struct msistring 00045 { 00046 USHORT persistent_refcount; 00047 USHORT nonpersistent_refcount; 00048 LPWSTR str; 00049 }; 00050 00051 struct string_table 00052 { 00053 UINT maxcount; /* the number of strings */ 00054 UINT freeslot; 00055 UINT codepage; 00056 UINT sortcount; 00057 struct msistring *strings; /* an array of strings */ 00058 UINT *sorted; /* index */ 00059 }; 00060 00061 static BOOL validate_codepage( UINT codepage ) 00062 { 00063 if (codepage != CP_ACP && !IsValidCodePage( codepage )) 00064 { 00065 WARN("invalid codepage %u\n", codepage); 00066 return FALSE; 00067 } 00068 return TRUE; 00069 } 00070 00071 static string_table *init_stringtable( int entries, UINT codepage ) 00072 { 00073 string_table *st; 00074 00075 if (!validate_codepage( codepage )) 00076 return NULL; 00077 00078 st = msi_alloc( sizeof (string_table) ); 00079 if( !st ) 00080 return NULL; 00081 if( entries < 1 ) 00082 entries = 1; 00083 00084 st->strings = msi_alloc_zero( sizeof(struct msistring) * entries ); 00085 if( !st->strings ) 00086 { 00087 msi_free( st ); 00088 return NULL; 00089 } 00090 00091 st->sorted = msi_alloc( sizeof (UINT) * entries ); 00092 if( !st->sorted ) 00093 { 00094 msi_free( st->strings ); 00095 msi_free( st ); 00096 return NULL; 00097 } 00098 00099 st->maxcount = entries; 00100 st->freeslot = 1; 00101 st->codepage = codepage; 00102 st->sortcount = 0; 00103 00104 return st; 00105 } 00106 00107 VOID msi_destroy_stringtable( string_table *st ) 00108 { 00109 UINT i; 00110 00111 for( i=0; i<st->maxcount; i++ ) 00112 { 00113 if( st->strings[i].persistent_refcount || 00114 st->strings[i].nonpersistent_refcount ) 00115 msi_free( st->strings[i].str ); 00116 } 00117 msi_free( st->strings ); 00118 msi_free( st->sorted ); 00119 msi_free( st ); 00120 } 00121 00122 static int st_find_free_entry( string_table *st ) 00123 { 00124 UINT i, sz, *s; 00125 struct msistring *p; 00126 00127 TRACE("%p\n", st); 00128 00129 if( st->freeslot ) 00130 { 00131 for( i = st->freeslot; i < st->maxcount; i++ ) 00132 if( !st->strings[i].persistent_refcount && 00133 !st->strings[i].nonpersistent_refcount ) 00134 return i; 00135 } 00136 for( i = 1; i < st->maxcount; i++ ) 00137 if( !st->strings[i].persistent_refcount && 00138 !st->strings[i].nonpersistent_refcount ) 00139 return i; 00140 00141 /* dynamically resize */ 00142 sz = st->maxcount + 1 + st->maxcount/2; 00143 p = msi_realloc_zero( st->strings, sz * sizeof(struct msistring) ); 00144 if( !p ) 00145 return -1; 00146 00147 s = msi_realloc( st->sorted, sz*sizeof(UINT) ); 00148 if( !s ) 00149 { 00150 msi_free( p ); 00151 return -1; 00152 } 00153 00154 st->strings = p; 00155 st->sorted = s; 00156 00157 st->freeslot = st->maxcount; 00158 st->maxcount = sz; 00159 if( st->strings[st->freeslot].persistent_refcount || 00160 st->strings[st->freeslot].nonpersistent_refcount ) 00161 ERR("oops. expected freeslot to be free...\n"); 00162 return st->freeslot; 00163 } 00164 00165 static int find_insert_index( const string_table *st, UINT string_id ) 00166 { 00167 int i, c, low = 0, high = st->sortcount - 1; 00168 00169 while (low <= high) 00170 { 00171 i = (low + high) / 2; 00172 c = strcmpW( st->strings[string_id].str, st->strings[st->sorted[i]].str ); 00173 00174 if (c < 0) 00175 high = i - 1; 00176 else if (c > 0) 00177 low = i + 1; 00178 else 00179 return -1; /* already exists */ 00180 } 00181 return high + 1; 00182 } 00183 00184 static void insert_string_sorted( string_table *st, UINT string_id ) 00185 { 00186 int i; 00187 00188 i = find_insert_index( st, string_id ); 00189 if (i == -1) 00190 return; 00191 00192 memmove( &st->sorted[i] + 1, &st->sorted[i], (st->sortcount - i) * sizeof(UINT) ); 00193 st->sorted[i] = string_id; 00194 st->sortcount++; 00195 } 00196 00197 static void set_st_entry( string_table *st, UINT n, LPWSTR str, USHORT refcount, enum StringPersistence persistence ) 00198 { 00199 if (persistence == StringPersistent) 00200 { 00201 st->strings[n].persistent_refcount = refcount; 00202 st->strings[n].nonpersistent_refcount = 0; 00203 } 00204 else 00205 { 00206 st->strings[n].persistent_refcount = 0; 00207 st->strings[n].nonpersistent_refcount = refcount; 00208 } 00209 00210 st->strings[n].str = str; 00211 00212 insert_string_sorted( st, n ); 00213 00214 if( n < st->maxcount ) 00215 st->freeslot = n + 1; 00216 } 00217 00218 static UINT msi_string2idA( const string_table *st, LPCSTR buffer, UINT *id ) 00219 { 00220 DWORD sz; 00221 UINT r = ERROR_INVALID_PARAMETER; 00222 LPWSTR str; 00223 00224 TRACE("Finding string %s in string table\n", debugstr_a(buffer) ); 00225 00226 if( buffer[0] == 0 ) 00227 { 00228 *id = 0; 00229 return ERROR_SUCCESS; 00230 } 00231 00232 sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 ); 00233 if( sz <= 0 ) 00234 return r; 00235 str = msi_alloc( sz*sizeof(WCHAR) ); 00236 if( !str ) 00237 return ERROR_NOT_ENOUGH_MEMORY; 00238 MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz ); 00239 00240 r = msi_string2idW( st, str, id ); 00241 msi_free( str ); 00242 00243 return r; 00244 } 00245 00246 static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, USHORT refcount, enum StringPersistence persistence ) 00247 { 00248 LPWSTR str; 00249 int sz; 00250 00251 if( !data ) 00252 return 0; 00253 if( !data[0] ) 00254 return 0; 00255 if( n > 0 ) 00256 { 00257 if( st->strings[n].persistent_refcount || 00258 st->strings[n].nonpersistent_refcount ) 00259 return -1; 00260 } 00261 else 00262 { 00263 if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) ) 00264 { 00265 if (persistence == StringPersistent) 00266 st->strings[n].persistent_refcount += refcount; 00267 else 00268 st->strings[n].nonpersistent_refcount += refcount; 00269 return n; 00270 } 00271 n = st_find_free_entry( st ); 00272 if( n == -1 ) 00273 return -1; 00274 } 00275 00276 if( n < 1 ) 00277 { 00278 ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n ); 00279 return -1; 00280 } 00281 00282 /* allocate a new string */ 00283 if( len < 0 ) 00284 len = strlen(data); 00285 sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 ); 00286 str = msi_alloc( (sz+1)*sizeof(WCHAR) ); 00287 if( !str ) 00288 return -1; 00289 MultiByteToWideChar( st->codepage, 0, data, len, str, sz ); 00290 str[sz] = 0; 00291 00292 set_st_entry( st, n, str, refcount, persistence ); 00293 00294 return n; 00295 } 00296 00297 int msi_addstringW( string_table *st, const WCHAR *data, int len, USHORT refcount, enum StringPersistence persistence ) 00298 { 00299 UINT n; 00300 LPWSTR str; 00301 00302 if( !data ) 00303 return 0; 00304 if( !data[0] ) 00305 return 0; 00306 00307 if( msi_string2idW( st, data, &n ) == ERROR_SUCCESS ) 00308 { 00309 if (persistence == StringPersistent) 00310 st->strings[n].persistent_refcount += refcount; 00311 else 00312 st->strings[n].nonpersistent_refcount += refcount; 00313 return n; 00314 } 00315 00316 n = st_find_free_entry( st ); 00317 if( n == -1 ) 00318 return -1; 00319 00320 /* allocate a new string */ 00321 if(len<0) 00322 len = strlenW(data); 00323 TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len ); 00324 00325 str = msi_alloc( (len+1)*sizeof(WCHAR) ); 00326 if( !str ) 00327 return -1; 00328 memcpy( str, data, len*sizeof(WCHAR) ); 00329 str[len] = 0; 00330 00331 set_st_entry( st, n, str, refcount, persistence ); 00332 00333 return n; 00334 } 00335 00336 /* find the string identified by an id - return null if there's none */ 00337 const WCHAR *msi_string_lookup_id( const string_table *st, UINT id ) 00338 { 00339 if( id == 0 ) 00340 return szEmpty; 00341 00342 if( id >= st->maxcount ) 00343 return NULL; 00344 00345 if( id && !st->strings[id].persistent_refcount && !st->strings[id].nonpersistent_refcount) 00346 return NULL; 00347 00348 return st->strings[id].str; 00349 } 00350 00351 /* 00352 * msi_id2stringA 00353 * 00354 * [in] st - pointer to the string table 00355 * [in] id - id of the string to retrieve 00356 * [out] buffer - destination of the UTF8 string 00357 * [in/out] sz - number of bytes available in the buffer on input 00358 * number of bytes used on output 00359 * 00360 * The size includes the terminating nul character. Short buffers 00361 * will be filled, but not nul terminated. 00362 */ 00363 static UINT msi_id2stringA( const string_table *st, UINT id, LPSTR buffer, UINT *sz ) 00364 { 00365 UINT len; 00366 const WCHAR *str; 00367 int n; 00368 00369 TRACE("Finding string %d of %d\n", id, st->maxcount); 00370 00371 str = msi_string_lookup_id( st, id ); 00372 if( !str ) 00373 return ERROR_FUNCTION_FAILED; 00374 00375 len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL ); 00376 00377 if( !buffer ) 00378 { 00379 *sz = len; 00380 return ERROR_SUCCESS; 00381 } 00382 00383 if( len > *sz ) 00384 { 00385 n = strlenW( str ) + 1; 00386 while( n && (len > *sz) ) 00387 len = WideCharToMultiByte( st->codepage, 0, 00388 str, --n, NULL, 0, NULL, NULL ); 00389 } 00390 else 00391 n = -1; 00392 00393 *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL ); 00394 00395 return ERROR_SUCCESS; 00396 } 00397 00398 /* 00399 * msi_string2idW 00400 * 00401 * [in] st - pointer to the string table 00402 * [in] str - string to find in the string table 00403 * [out] id - id of the string, if found 00404 */ 00405 UINT msi_string2idW( const string_table *st, LPCWSTR str, UINT *id ) 00406 { 00407 int i, c, low = 0, high = st->sortcount - 1; 00408 00409 while (low <= high) 00410 { 00411 i = (low + high) / 2; 00412 c = strcmpW( str, st->strings[st->sorted[i]].str ); 00413 00414 if (c < 0) 00415 high = i - 1; 00416 else if (c > 0) 00417 low = i + 1; 00418 else 00419 { 00420 *id = st->sorted[i]; 00421 return ERROR_SUCCESS; 00422 } 00423 } 00424 00425 return ERROR_INVALID_PARAMETER; 00426 } 00427 00428 static void string_totalsize( const string_table *st, UINT *datasize, UINT *poolsize ) 00429 { 00430 UINT i, len, holesize; 00431 00432 if( st->strings[0].str || st->strings[0].persistent_refcount || st->strings[0].nonpersistent_refcount) 00433 ERR("oops. element 0 has a string\n"); 00434 00435 *poolsize = 4; 00436 *datasize = 0; 00437 holesize = 0; 00438 for( i=1; i<st->maxcount; i++ ) 00439 { 00440 if( !st->strings[i].persistent_refcount ) 00441 { 00442 TRACE("[%u] nonpersistent = %s\n", i, debugstr_w(st->strings[i].str)); 00443 (*poolsize) += 4; 00444 } 00445 else if( st->strings[i].str ) 00446 { 00447 TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str)); 00448 len = WideCharToMultiByte( st->codepage, 0, 00449 st->strings[i].str, -1, NULL, 0, NULL, NULL); 00450 if( len ) 00451 len--; 00452 (*datasize) += len; 00453 if (len>0xffff) 00454 (*poolsize) += 4; 00455 (*poolsize) += holesize + 4; 00456 holesize = 0; 00457 } 00458 else 00459 holesize += 4; 00460 } 00461 TRACE("data %u pool %u codepage %x\n", *datasize, *poolsize, st->codepage ); 00462 } 00463 00464 HRESULT msi_init_string_table( IStorage *stg ) 00465 { 00466 USHORT zero[2] = { 0, 0 }; 00467 UINT ret; 00468 00469 /* create the StringPool stream... add the zero string to it*/ 00470 ret = write_stream_data(stg, szStringPool, zero, sizeof zero, TRUE); 00471 if (ret != ERROR_SUCCESS) 00472 return E_FAIL; 00473 00474 /* create the StringData stream... make it zero length */ 00475 ret = write_stream_data(stg, szStringData, NULL, 0, TRUE); 00476 if (ret != ERROR_SUCCESS) 00477 return E_FAIL; 00478 00479 return S_OK; 00480 } 00481 00482 string_table *msi_load_string_table( IStorage *stg, UINT *bytes_per_strref ) 00483 { 00484 string_table *st = NULL; 00485 CHAR *data = NULL; 00486 USHORT *pool = NULL; 00487 UINT r, datasize = 0, poolsize = 0, codepage; 00488 DWORD i, count, offset, len, n, refs; 00489 00490 r = read_stream_data( stg, szStringPool, TRUE, (BYTE **)&pool, &poolsize ); 00491 if( r != ERROR_SUCCESS) 00492 goto end; 00493 r = read_stream_data( stg, szStringData, TRUE, (BYTE **)&data, &datasize ); 00494 if( r != ERROR_SUCCESS) 00495 goto end; 00496 00497 if ( (poolsize > 4) && (pool[1] & 0x8000) ) 00498 *bytes_per_strref = LONG_STR_BYTES; 00499 else 00500 *bytes_per_strref = sizeof(USHORT); 00501 00502 count = poolsize/4; 00503 if( poolsize > 4 ) 00504 codepage = pool[0] | ( (pool[1] & ~0x8000) << 16 ); 00505 else 00506 codepage = CP_ACP; 00507 st = init_stringtable( count, codepage ); 00508 if (!st) 00509 goto end; 00510 00511 offset = 0; 00512 n = 1; 00513 i = 1; 00514 while( i<count ) 00515 { 00516 /* the string reference count is always the second word */ 00517 refs = pool[i*2+1]; 00518 00519 /* empty entries have two zeros, still have a string id */ 00520 if (pool[i*2] == 0 && refs == 0) 00521 { 00522 i++; 00523 n++; 00524 continue; 00525 } 00526 00527 /* 00528 * If a string is over 64k, the previous string entry is made null 00529 * and its the high word of the length is inserted in the null string's 00530 * reference count field. 00531 */ 00532 if( pool[i*2] == 0) 00533 { 00534 len = (pool[i*2+3] << 16) + pool[i*2+2]; 00535 i += 2; 00536 } 00537 else 00538 { 00539 len = pool[i*2]; 00540 i += 1; 00541 } 00542 00543 if ( (offset + len) > datasize ) 00544 { 00545 ERR("string table corrupt?\n"); 00546 break; 00547 } 00548 00549 r = msi_addstring( st, n, data+offset, len, refs, StringPersistent ); 00550 if( r != n ) 00551 ERR("Failed to add string %d\n", n ); 00552 n++; 00553 offset += len; 00554 } 00555 00556 if ( datasize != offset ) 00557 ERR("string table load failed! (%08x != %08x), please report\n", datasize, offset ); 00558 00559 TRACE("Loaded %d strings\n", count); 00560 00561 end: 00562 msi_free( pool ); 00563 msi_free( data ); 00564 00565 return st; 00566 } 00567 00568 UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref ) 00569 { 00570 UINT i, datasize = 0, poolsize = 0, sz, used, r, codepage, n; 00571 UINT ret = ERROR_FUNCTION_FAILED; 00572 CHAR *data = NULL; 00573 USHORT *pool = NULL; 00574 00575 TRACE("\n"); 00576 00577 /* construct the new table in memory first */ 00578 string_totalsize( st, &datasize, &poolsize ); 00579 00580 TRACE("%u %u %u\n", st->maxcount, datasize, poolsize ); 00581 00582 pool = msi_alloc( poolsize ); 00583 if( ! pool ) 00584 { 00585 WARN("Failed to alloc pool %d bytes\n", poolsize ); 00586 goto err; 00587 } 00588 data = msi_alloc( datasize ); 00589 if( ! data ) 00590 { 00591 WARN("Failed to alloc data %d bytes\n", poolsize ); 00592 goto err; 00593 } 00594 00595 used = 0; 00596 codepage = st->codepage; 00597 pool[0] = codepage & 0xffff; 00598 pool[1] = codepage >> 16; 00599 if (st->maxcount > 0xffff) 00600 { 00601 pool[1] |= 0x8000; 00602 *bytes_per_strref = LONG_STR_BYTES; 00603 } 00604 else 00605 *bytes_per_strref = sizeof(USHORT); 00606 00607 n = 1; 00608 for( i=1; i<st->maxcount; i++ ) 00609 { 00610 if( !st->strings[i].persistent_refcount ) 00611 { 00612 pool[ n*2 ] = 0; 00613 pool[ n*2 + 1] = 0; 00614 n++; 00615 continue; 00616 } 00617 00618 sz = datasize - used; 00619 r = msi_id2stringA( st, i, data+used, &sz ); 00620 if( r != ERROR_SUCCESS ) 00621 { 00622 ERR("failed to fetch string\n"); 00623 sz = 0; 00624 } 00625 if( sz && (sz < (datasize - used ) ) ) 00626 sz--; 00627 00628 if (sz) 00629 pool[ n*2 + 1 ] = st->strings[i].persistent_refcount; 00630 else 00631 pool[ n*2 + 1 ] = 0; 00632 if (sz < 0x10000) 00633 { 00634 pool[ n*2 ] = sz; 00635 n++; 00636 } 00637 else 00638 { 00639 pool[ n*2 ] = 0; 00640 pool[ n*2 + 2 ] = sz&0xffff; 00641 pool[ n*2 + 3 ] = (sz>>16); 00642 n += 2; 00643 } 00644 used += sz; 00645 if( used > datasize ) 00646 { 00647 ERR("oops overran %d >= %d\n", used, datasize); 00648 goto err; 00649 } 00650 } 00651 00652 if( used != datasize ) 00653 { 00654 ERR("oops used %d != datasize %d\n", used, datasize); 00655 goto err; 00656 } 00657 00658 /* write the streams */ 00659 r = write_stream_data( storage, szStringData, data, datasize, TRUE ); 00660 TRACE("Wrote StringData r=%08x\n", r); 00661 if( r ) 00662 goto err; 00663 r = write_stream_data( storage, szStringPool, pool, poolsize, TRUE ); 00664 TRACE("Wrote StringPool r=%08x\n", r); 00665 if( r ) 00666 goto err; 00667 00668 ret = ERROR_SUCCESS; 00669 00670 err: 00671 msi_free( data ); 00672 msi_free( pool ); 00673 00674 return ret; 00675 } 00676 00677 UINT msi_get_string_table_codepage( const string_table *st ) 00678 { 00679 return st->codepage; 00680 } 00681 00682 UINT msi_set_string_table_codepage( string_table *st, UINT codepage ) 00683 { 00684 if (validate_codepage( codepage )) 00685 { 00686 st->codepage = codepage; 00687 return ERROR_SUCCESS; 00688 } 00689 return ERROR_FUNCTION_FAILED; 00690 } Generated on Sat May 26 2012 04:16:31 for ReactOS by
1.7.6.1
|