596 lines
10 KiB
C++
596 lines
10 KiB
C++
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
String.cxx
|
|
|
|
Abstract:
|
|
|
|
Short strings
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 9-June-1994
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "spllibp.hxx"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Class specific NULL state.
|
|
//
|
|
TCHAR TString::gszNullState[2] = {0,0};
|
|
|
|
//
|
|
// Default construction.
|
|
//
|
|
TString::
|
|
TString(
|
|
VOID
|
|
) : _pszString( &TString::gszNullState[kValid] )
|
|
{
|
|
}
|
|
|
|
//
|
|
// Construction using an existing LPCTSTR string.
|
|
//
|
|
TString::
|
|
TString(
|
|
IN LPCTSTR psz
|
|
) : _pszString( &TString::gszNullState[kValid] )
|
|
{
|
|
bUpdate( psz );
|
|
}
|
|
|
|
//
|
|
// Destruction, ensure we don't free our NULL state.
|
|
//
|
|
TString::
|
|
~TString(
|
|
VOID
|
|
)
|
|
{
|
|
vFree( _pszString );
|
|
}
|
|
|
|
//
|
|
// Copy constructor.
|
|
//
|
|
TString::
|
|
TString(
|
|
const TString &String
|
|
) : _pszString( &TString::gszNullState[kValid] )
|
|
{
|
|
bUpdate( String._pszString );
|
|
}
|
|
|
|
//
|
|
// Indicates if a string has any usable data.
|
|
//
|
|
BOOL
|
|
TString::
|
|
bEmpty(
|
|
VOID
|
|
) const
|
|
{
|
|
return _pszString[0] == 0;
|
|
}
|
|
|
|
//
|
|
// Indicates if a string object is valid.
|
|
//
|
|
BOOL
|
|
TString::
|
|
bValid(
|
|
VOID
|
|
) const
|
|
{
|
|
return _pszString != &TString::gszNullState[kInValid];
|
|
}
|
|
|
|
//
|
|
// Return the length of the string.
|
|
//
|
|
UINT
|
|
TString::
|
|
uLen(
|
|
VOID
|
|
) const
|
|
{
|
|
return lstrlen( _pszString );
|
|
}
|
|
|
|
BOOL
|
|
TString::
|
|
bCat(
|
|
IN LPCTSTR psz
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Safe concatenation of the specified string to the string
|
|
object. If the allocation fails, return FALSE and the
|
|
original string is not modified.
|
|
|
|
Arguments:
|
|
|
|
psz - Input string, may be NULL.
|
|
|
|
Return Value:
|
|
|
|
TRUE = update successful
|
|
FALSE = update failed
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bStatus = FALSE;
|
|
|
|
//
|
|
// If a valid string was passed.
|
|
//
|
|
if( psz ){
|
|
|
|
LPTSTR pszTmp = _pszString;
|
|
|
|
//
|
|
// Allocate the new buffer consisting of the size of the orginal
|
|
// string plus the sizeof of the new string plus the null terminator.
|
|
//
|
|
_pszString = (LPTSTR)AllocMem(
|
|
( lstrlen( pszTmp ) +
|
|
lstrlen( psz ) +
|
|
1 ) *
|
|
sizeof ( pszTmp[0] ) );
|
|
|
|
//
|
|
// If memory was not available.
|
|
//
|
|
if( !_pszString ){
|
|
|
|
//
|
|
// Release the original buffer.
|
|
//
|
|
vFree( pszTmp );
|
|
|
|
//
|
|
// Mark the string object as invalid.
|
|
//
|
|
_pszString = &TString::gszNullState[kInValid];
|
|
|
|
} else {
|
|
|
|
//
|
|
// Copy the string and concatenate the passed string.
|
|
//
|
|
lstrcpy( _pszString, pszTmp );
|
|
lstrcat( _pszString, psz );
|
|
|
|
//
|
|
// Release the original buffer.
|
|
//
|
|
vFree( pszTmp );
|
|
|
|
//
|
|
// Indicate success.
|
|
//
|
|
bStatus = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Skip null pointers, not an error.
|
|
//
|
|
} else {
|
|
|
|
bStatus = TRUE;
|
|
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
TString::
|
|
bUpdate(
|
|
IN LPCTSTR psz
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Safe updating of string. If the allocation fails, return FALSE
|
|
and leave the string as is.
|
|
|
|
Arguments:
|
|
|
|
psz - Input string, may be NULL.
|
|
|
|
Return Value:
|
|
|
|
TRUE = update successful
|
|
FALSE = update failed
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Check if the null pointer is passed.
|
|
//
|
|
if( !psz ){
|
|
|
|
//
|
|
// If not pointing to the gszNullState
|
|
//
|
|
vFree( _pszString );
|
|
|
|
//
|
|
// Mark the object as valid.
|
|
//
|
|
_pszString = &TString::gszNullState[kValid];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Create temp pointer and allocate the new string.
|
|
//
|
|
LPTSTR pszTmp = _pszString;
|
|
_pszString = (LPTSTR) AllocMem(( lstrlen(psz)+1 ) * sizeof( psz[0] ));
|
|
|
|
//
|
|
// If memory was not available.
|
|
//
|
|
if( !_pszString ){
|
|
|
|
//
|
|
// Ensure we free any previous string.
|
|
//
|
|
vFree( pszTmp );
|
|
|
|
//
|
|
// Mark the string object as invalid.
|
|
//
|
|
_pszString = &TString::gszNullState[kInValid];
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the string and
|
|
//
|
|
lstrcpy( _pszString, psz );
|
|
|
|
//
|
|
// If the old string object was not pointing to our
|
|
// class specific gszNullStates then release the memory.
|
|
//
|
|
vFree( pszTmp );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
TString::
|
|
bLoadString(
|
|
IN HINSTANCE hInst,
|
|
IN UINT uID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Safe load of a string from a resources file.
|
|
|
|
Arguments:
|
|
|
|
hInst - Instance handle of resource file.
|
|
uId - Resource id to load.
|
|
|
|
Return Value:
|
|
|
|
TRUE = load successful
|
|
FALSE = load failed
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR pszString = NULL;
|
|
BOOL bStatus = FALSE;
|
|
INT iSize;
|
|
INT iLen;
|
|
|
|
//
|
|
// Continue increasing the buffer until
|
|
// the buffer is big enought to hold the entire string.
|
|
//
|
|
for( iSize = kStrMax; ; iSize += kStrMax ){
|
|
|
|
//
|
|
// Allocate string buffer.
|
|
//
|
|
pszString = (LPTSTR)AllocMem( iSize * sizeof( pszString[0] ) );
|
|
|
|
if( pszString ){
|
|
|
|
iLen = LoadString( hInst, uID, pszString, iSize );
|
|
|
|
if( iLen == 0 ) {
|
|
|
|
DBGMSG( DBG_ERROR, ( "String.vLoadString: failed to load IDS 0x%x, %d\n", uID, GetLastError() ));
|
|
FreeMem( pszString );
|
|
break;
|
|
|
|
//
|
|
// Since LoadString does not indicate if the string was truncated or it
|
|
// just happened to fit. When we detect this ambiguous case we will
|
|
// try one more time just to be sure.
|
|
//
|
|
} else if( iSize - iLen <= sizeof( pszString[0] ) ){
|
|
|
|
FreeMem( pszString );
|
|
|
|
//
|
|
// LoadString was successful release original string buffer
|
|
// and update new buffer pointer.
|
|
//
|
|
} else {
|
|
|
|
vFree( _pszString );
|
|
_pszString = pszString;
|
|
bStatus = TRUE;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
DBGMSG( DBG_ERROR, ( "String.vLoadString: unable to allocate memory, %d\n", GetLastError() ));
|
|
break;
|
|
}
|
|
}
|
|
return bStatus;
|
|
}
|
|
|
|
VOID
|
|
TString::
|
|
vFree(
|
|
IN LPTSTR pszString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Safe free, frees the string memory. Ensures
|
|
we do not try an free our global memory block.
|
|
|
|
Arguments:
|
|
|
|
pszString pointer to string meory to free.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// If this memory was not pointing to our
|
|
// class specific gszNullStates then release the memory.
|
|
//
|
|
if( pszString != &TString::gszNullState[kValid] &&
|
|
pszString != &TString::gszNullState[kInValid] ){
|
|
|
|
FreeMem( pszString );
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
TString::
|
|
bFormat(
|
|
IN LPCTSTR pszFmt,
|
|
IN ...
|
|
)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Format the string opbject similar to sprintf.
|
|
|
|
Arguments:
|
|
|
|
pszFmt pointer format string.
|
|
.. variable number of arguments similar to sprintf.
|
|
|
|
Return Value:
|
|
|
|
TRUE if string was format successfully. FALSE if error
|
|
occurred creating the format string, string object will be
|
|
invalid and the previous string lost.
|
|
|
|
--*/
|
|
|
|
BOOL bStatus = TRUE;
|
|
|
|
va_list pArgs;
|
|
|
|
va_start( pArgs, pszFmt );
|
|
|
|
bStatus = bvFormat( pszFmt, pArgs );
|
|
|
|
va_end( pArgs );
|
|
|
|
return bStatus;
|
|
|
|
}
|
|
|
|
BOOL
|
|
TString::
|
|
bvFormat(
|
|
IN LPCTSTR pszFmt,
|
|
IN va_list avlist
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Format the string opbject similar to vsprintf.
|
|
|
|
Arguments:
|
|
|
|
pszFmt pointer format string.
|
|
pointer to variable number of arguments similar to vsprintf.
|
|
|
|
Return Value:
|
|
|
|
TRUE if string was format successfully. FALSE if error
|
|
occurred creating the format string, string object will be
|
|
invalid and the previous string lost.
|
|
|
|
--*/
|
|
{
|
|
BOOL bStatus;
|
|
|
|
//
|
|
// Save previous string value.
|
|
//
|
|
LPTSTR pszTemp = _pszString;
|
|
|
|
//
|
|
// Format the string.
|
|
//
|
|
_pszString = vsntprintf( pszFmt, avlist );
|
|
|
|
//
|
|
// If format failed mark object as invalid and
|
|
// set the return value.
|
|
//
|
|
if( !_pszString )
|
|
{
|
|
_pszString = &TString::gszNullState[kInValid];
|
|
bStatus = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bStatus = TRUE;
|
|
}
|
|
|
|
//
|
|
// Release near the end because the format string or arguments
|
|
// may be referencing this string object.
|
|
//
|
|
vFree( pszTemp );
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
LPTSTR
|
|
TString::
|
|
vsntprintf(
|
|
IN LPCTSTR szFmt,
|
|
IN va_list pArgs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
//
|
|
// Formats a string and returns a heap allocated string with the
|
|
// formated data. This routine can be used to for extremely
|
|
// long format strings. Note: If a valid pointer is returned
|
|
// the callng functions must release the data with a call to delete.
|
|
// Example:
|
|
//
|
|
// LPCTSTR p = vsntprintf("Test %s", pString );
|
|
//
|
|
// SetTitle( p );
|
|
//
|
|
// delete [] p;
|
|
//
|
|
|
|
Arguments:
|
|
|
|
pszString pointer format string.
|
|
pointer to a variable number of arguments.
|
|
|
|
Return Value:
|
|
|
|
Pointer to format string. NULL if error.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR pszBuff = NULL;
|
|
INT iSize = kStrIncrement;
|
|
|
|
for( ; ; )
|
|
{
|
|
//
|
|
// Allocate the message buffer.
|
|
//
|
|
pszBuff = (LPTSTR)AllocMem( iSize * sizeof(TCHAR) );
|
|
|
|
if( !pszBuff )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Attempt to format the string. snprintf fails with a
|
|
// negative number when the buffer is too small.
|
|
//
|
|
INT iReturn = _vsntprintf( pszBuff, iSize, szFmt, pArgs );
|
|
|
|
//
|
|
// If the return value positive and not equal to the buffer size
|
|
// then the format succeeded. _vsntprintf will not null terminate
|
|
// the string if the resultant string is exactly the lenght of the
|
|
// provided buffer.
|
|
//
|
|
if( iReturn > 0 && iReturn != iSize )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// String did not fit release the current buffer.
|
|
//
|
|
if( pszBuff )
|
|
{
|
|
FreeMem( pszBuff );
|
|
}
|
|
|
|
//
|
|
// Double the buffer size after each failure.
|
|
//
|
|
iSize *= 2;
|
|
|
|
//
|
|
// If the size is greater than 100k exit without formatting a string.
|
|
//
|
|
if( iSize > kStrMaxFormatSize )
|
|
{
|
|
DBGMSG( DBG_ERROR, ("TString::vsntprintf failed string too long.\n") );
|
|
pszBuff = NULL;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return pszBuff;
|
|
|
|
}
|
|
|
|
|