/*++ 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. // size_t cchSize = lstrlen( pszTmp ) + lstrlen( psz ) + 1; _pszString = (LPTSTR)AllocMem(cchSize * 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. // StringCchCopy( _pszString, cchSize, pszTmp); StringCchCat(_pszString, cchSize, 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; SIZE_T cchLen = lstrlen(psz) + 1; _pszString = (LPTSTR) AllocMem(static_cast(cchLen * 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 // StringCchCopy( _pszString, cchLen, psz); // // If the old string object was not pointing to our // class specific gszNullStates then release the memory. // vFree( pszTmp ); return TRUE; } /*++ Routine Name: Update Routine Description: Safe updating of string. If the allocation fails, return E_OUTOFMEMORY. Arguments: psz - Input string, may be NULL. Return Value: An HRESULT --*/ HRESULT TString:: Update( IN LPCTSTR psz ) { return bUpdate(psz) ? S_OK : E_OUTOFMEMORY; } 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. if (SUCCEEDED(StringCchVPrintf( pszBuff, iSize, szFmt, pArgs ))) { 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; }