ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

string.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.