/*++ Copyright (c) 1997 Microsoft Corporation Module Name: string.cpp Abstract: Author: Vlad Sadovsky (vlads) 26-Jan-1997 Revision History: 26-Jan-1997 VladS created --*/ // // Normal includes only for this module to be active // #include "cplusinc.h" #include "sticomm.h" // // Private Definations // // // When appending data, this is the extra amount we request to avoid // reallocations // #define STR_SLOP 128 // // Converts a value between zero and fifteen to the appropriate hex digit // #define HEXDIGIT( nDigit ) \ (TCHAR)((nDigit) > 9 ? \ (nDigit) - 10 + 'A' \ : (nDigit) + '0') // // Converts a single hex digit to its decimal equivalent // #define TOHEX( ch ) \ ((ch) > '9' ? \ (ch) >= 'a' ? \ (ch) - 'a' + 10 : \ (ch) - 'A' + 10 \ : (ch) - '0') // // Private Globals // WCHAR STR::_pszEmptyString[] = L""; // // Construction/Destruction // STR::STR( const CHAR * pchInit ) { AuxInit( (PBYTE) pchInit, FALSE ); } STR::STR( const WCHAR * pwchInit ) { AuxInit( (PBYTE) pwchInit, TRUE ); } STR::STR( const STR & str ) { AuxInit( (PBYTE) str.QueryPtr(), str.IsUnicode() ); } VOID STR::AuxInit( PBYTE pInit, BOOL fUnicode ) { BOOL fRet; _fUnicode = fUnicode; _fValid = TRUE; if ( pInit ) { INT cbCopy = fUnicode ? (::wcslen( (WCHAR *) pInit ) + 1) * sizeof(WCHAR) : (::strlen( (CHAR *) pInit ) + 1) * sizeof(CHAR); fRet = Resize( cbCopy ); if ( !fRet ) { _fValid = FALSE; return; } ::memcpy( QueryPtr(), pInit, cbCopy ); } } // // Appends the string onto this one. // BOOL STR::Append( const CHAR * pchStr ) { if ( pchStr ) { ASSERT( !IsUnicode() ); return AuxAppend( (PBYTE) pchStr, ::strlen( pchStr ) ); } return TRUE; } BOOL STR::Append( const WCHAR * pwchStr ) { if ( pwchStr ) { ASSERT( IsUnicode() ); return AuxAppend( (PBYTE) pwchStr, ::wcslen( pwchStr ) * sizeof(WCHAR) ); } return TRUE; } BOOL STR::Append( const STR & str ) { if ( str.IsUnicode() ) return Append( (const WCHAR *) str.QueryStrW() ); else return Append( (const CHAR *) str.QueryStrA() ); } BOOL STR::AuxAppend( PBYTE pStr, UINT cbStr, BOOL fAddSlop ) { ASSERT( pStr != NULL ); UINT cbThis = QueryCB(); // // Only resize when we have to. When we do resize, we tack on // some extra space to avoid extra reallocations. // // Note: QuerySize returns the requested size of the string buffer, // *not* the strlen of the buffer // if ( QuerySize() < cbThis + cbStr + sizeof(WCHAR) ) { if ( !Resize( cbThis + cbStr + (fAddSlop ? STR_SLOP : sizeof(WCHAR) )) ) return FALSE; } memcpy( (BYTE *) QueryPtr() + cbThis, pStr, cbStr + (IsUnicode() ? sizeof(WCHAR) : sizeof(CHAR)) ); return TRUE; } // // Convert in place // BOOL STR::ConvertToW(VOID) { if (IsUnicode()) { return TRUE; } UINT cbNeeded = (QueryCB()+1)*sizeof(WCHAR); // // Only resize when we have to. // if ( QuerySize() < cbNeeded ) { if ( !Resize( cbNeeded)) { return FALSE; } } BUFFER buf(cbNeeded); if (!buf.QueryPtr()) { return FALSE; } int iRet; int cch; cch = QueryCCH() + 1; iRet = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, QueryStrA(), -1, (WCHAR *)buf.QueryPtr(), cch); if ( iRet == 0 ) { // // Error in conversion. // return FALSE; } memcpy( (BYTE *) QueryPtr(), buf.QueryPtr(), cbNeeded); SetUnicode(TRUE); return TRUE; } BOOL STR::ConvertToA(VOID) { if (!IsUnicode()) { return TRUE; } UINT cbNeeded = (QueryCB()+1)*sizeof(CHAR); BUFFER buf(cbNeeded); if (!buf.QueryPtr()) { return FALSE; } int iRet; int cch; cch = cbNeeded; iRet = WideCharToMultiByte(CP_ACP, 0L, QueryStrW(), -1, (CHAR *)buf.QueryPtr(), cch, NULL, NULL ); if ( iRet == 0 ) { // // Error in conversion. // return FALSE; } // Careful here , there might be DBCS characters in resultant buffer memcpy( (BYTE *) QueryPtr(), buf.QueryPtr(), iRet); SetUnicode(FALSE); return TRUE; } // // Copies the string into this one. // BOOL STR::Copy( const CHAR * pchStr ) { _fUnicode = FALSE; if ( QueryPtr() ) *(QueryStrA()) = '\0'; if ( pchStr ) { return AuxAppend( (PBYTE) pchStr, ::strlen( pchStr ), FALSE ); } return TRUE; } BOOL STR::Copy( const WCHAR * pwchStr ) { _fUnicode = TRUE; if ( QueryPtr() ) *(QueryStrW()) = TEXT('\0'); if ( pwchStr ) { return AuxAppend( (PBYTE) pwchStr, ::wcslen( pwchStr ) * sizeof(WCHAR), FALSE ); } return TRUE; } BOOL STR::Copy( const STR & str ) { _fUnicode = str.IsUnicode(); if ( str.IsEmpty() && QueryPtr() == NULL) { // To avoid pathological allocation of small chunk of memory return ( TRUE); } if ( str.IsUnicode() ) return Copy( str.QueryStrW() ); else return Copy( str.QueryStrA() ); } // // Resizes or allocates string memory, NULL terminating if necessary // BOOL STR::Resize( UINT cbNewRequestedSize ) { BOOL fTerminate = QueryPtr() == NULL; if ( !BUFFER::Resize( cbNewRequestedSize )) return FALSE; if ( fTerminate && cbNewRequestedSize > 0 ) { if ( IsUnicode() ) { ASSERT( cbNewRequestedSize > 1 ); *QueryStrW() = TEXT('\0'); } else *QueryStrA() = '\0'; } return TRUE; } // // Loads a string resource from this module's string table // or from the system string table // // dwResID - System error or module string ID // lpszModuleName - name of the module from which to load. // If NULL, then load the string from system table. // // BOOL STR::LoadString( IN DWORD dwResID, IN LPCTSTR lpszModuleName // Optional ) { BOOL fReturn = FALSE; INT cch; // // If lpszModuleName is NULL, load the string from system's string table. // if ( lpszModuleName == NULL) { BYTE * pchBuff = NULL; // // Call the appropriate function so we don't have to do the Unicode // conversion // if ( IsUnicode() ) { cch = ::FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwResID, 0, (LPWSTR) &pchBuff, 1024, NULL ); if ( cch ) { fReturn = Copy( (LPCWSTR) pchBuff ); } } else { cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwResID, 0, (LPSTR) &pchBuff, 1024, NULL ); if ( cch ) { fReturn = Copy( (LPCSTR) pchBuff ); } } // // Free the buffer FormatMessage allocated // if ( cch ) { ::LocalFree( (VOID*) pchBuff ); } } else { WCHAR ach[STR_MAX_RES_SIZE]; if ( IsUnicode() ) { cch = ::LoadStringW( GetModuleHandle( lpszModuleName), dwResID, (WCHAR *) ach, sizeof(ach)); if ( cch ) { fReturn = Copy( (LPWSTR) ach ); } } else { cch = ::LoadStringA( GetModuleHandle( lpszModuleName), dwResID, (CHAR *) ach, sizeof(ach)); if ( cch ) { fReturn = Copy( (LPSTR) ach ); } } } return ( fReturn); } // STR::LoadString() BOOL STR::LoadString( IN DWORD dwResID, IN HMODULE hModule ) { BOOL fReturn = FALSE; INT cch; WCHAR ach[STR_MAX_RES_SIZE]; if ( IsUnicode()) { cch = ::LoadStringW(hModule, dwResID, (WCHAR *) ach, sizeof(ach)); if ( cch ) { fReturn = Copy( (LPWSTR) ach ); } } else { cch = ::LoadStringA(hModule, dwResID, (CHAR *) ach, sizeof(ach)); if ( cch ) { fReturn = Copy( (LPSTR) ach ); } } return ( fReturn); } // STR::LoadString() BOOL STR::FormatStringV( IN LPCTSTR lpszModuleName, ... ) { DWORD cch; LPSTR pchBuff; BOOL fRet = FALSE; DWORD dwErr; va_list va; va_start(va,lpszModuleName); cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING, QueryStrA(), 0L, 0, (LPSTR) &pchBuff, 1024, &va); dwErr = ::GetLastError(); if ( cch ) { fRet = Copy( (LPCSTR) pchBuff ); ::LocalFree( (VOID*) pchBuff ); } return fRet; } BOOL STR::FormatString( IN DWORD dwResID, IN LPCTSTR apszInsertParams[], IN LPCTSTR lpszModuleName ) { DWORD cch; LPSTR pchBuff; BOOL fRet; if (!dwResID) { cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_STRING, QueryStrA(), dwResID, 0, (LPSTR) &pchBuff, 1024, (va_list *) apszInsertParams ); } else { cch = ::FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE, GetModuleHandle( lpszModuleName ), dwResID, 0, (LPSTR) &pchBuff, 1024, (va_list *) apszInsertParams ); } if ( cch ) { fRet = Copy( (LPCSTR) pchBuff ); ::LocalFree( (VOID*) pchBuff ); } return fRet; } #if 1 CHAR * STR::QueryStrA( VOID ) const { ASSERT( !IsUnicode() ); ASSERT( *_pszEmptyString == TEXT('\0') ); return (QueryPtr() ? (CHAR *) QueryPtr() : (CHAR *) _pszEmptyString); } WCHAR * STR::QueryStrW( VOID ) const { ASSERT( IsUnicode() ); ASSERT( *_pszEmptyString == TEXT('\0') ); return (QueryPtr() ? (WCHAR *) QueryPtr() : (WCHAR *) _pszEmptyString); } #endif //DBG BOOL STR::CopyToBuffer( WCHAR * lpszBuffer, LPDWORD lpcch) const /*++ Description: Copies the string into the WCHAR buffer passed in if the buffer is sufficient to hold the translated string. If the buffer is small, the function returns small and sets *lpcch to contain the required number of characters. Arguments: lpszBuffer pointer to WCHAR buffer which on return contains the UNICODE version of string on success. lpcch pointer to DWORD containing the length of the buffer. If *lpcch == 0 then the function returns TRUE with the count of characters required stored in *lpcch. Also in this case lpszBuffer is not affected. Returns: TRUE on success. FALSE on failure. Use GetLastError() for further details. --*/ { BOOL fReturn = TRUE; if ( lpcch == NULL) { SetLastError( ERROR_INVALID_PARAMETER); return ( FALSE); } if ( *lpcch == 0) { // // Inquiring the size of buffer alone // *lpcch = QueryCCH() + 1; // add one character for terminating null } else { // // Copy data to buffer // if ( IsUnicode()) { // // Do plain copy of the data. // if ( *lpcch >= QueryCCH()) { wcscpy( lpszBuffer, QueryStrW()); } else { SetLastError( ERROR_INSUFFICIENT_BUFFER); fReturn = FALSE; } } else { // // Copy after conversion from ANSI to Unicode // int iRet; iRet = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, QueryStrA(), QueryCCH() + 1, lpszBuffer, (int )*lpcch); if ( iRet == 0 || iRet != (int ) *lpcch) { // // Error in conversion. // fReturn = FALSE; } } } return ( fReturn); } // STR::CopyToBuffer() BOOL STR::CopyToBufferA( CHAR * lpszBuffer, LPDWORD lpcch) const /*++ Description: Copies the string into the CHAR buffer passed in if the buffer is sufficient to hold the translated string. If the buffer is small, the function returns small and sets *lpcch to contain the required number of characters. Arguments: lpszBuffer pointer to CHAR buffer which on return contains the MBCS version of string on success. lpcch pointer to DWORD containing the length of the buffer. If *lpcch == 0 then the function returns TRUE with the count of characters required stored in *lpcch. Also in this case lpszBuffer is not affected. Returns: TRUE on success. FALSE on failure. Use GetLastError() for further details. --*/ { BOOL fReturn = TRUE; if ( lpcch == NULL) { SetLastError( ERROR_INVALID_PARAMETER); return ( FALSE); } if ( *lpcch == 0) { // // Inquiring the size of buffer alone // *lpcch = 2*(QueryCCH() + 1); // add one character for terminating null and on pessimistic side // ask for largest possible buffer } else { // // Copy data to buffer // if ( !IsUnicode()) { lstrcpyA( lpszBuffer, QueryStrA()); } else { // // Copy after conversion from Unicode to MBCS // int iRet; iRet = WideCharToMultiByte(CP_ACP, 0L, QueryStrW(), QueryCCH()+1, lpszBuffer, *lpcch, NULL,NULL ); if ( iRet == 0 || *lpcch < (DWORD)iRet) { *lpcch = iRet; fReturn = FALSE; } } } return ( fReturn); } // STR::CopyToBuffer() /* STRArray class implementation- this isn't very efficient, as we'll copy every string over again whenever we grow the array, but then again, that part of the code may never even get executed, anyway */ void STRArray::Grow() { // We need to add some more strings to the array STR *pcsNew = new STR[m_ucMax += m_uGrowBy]; if (!pcsNew) { // We recover gracelessly by replacing the final // string. m_ucMax -= m_uGrowBy; m_ucItems--; return; } for (unsigned u = 0; u < m_ucItems; u++) pcsNew[u] = (LPCTSTR) m_pcsContents[u]; delete[] m_pcsContents; m_pcsContents = pcsNew; } STRArray::STRArray(unsigned uGrowBy) { m_uGrowBy = uGrowBy ? uGrowBy : 10; m_ucItems = m_ucMax = 0; m_pcsContents = NULL; } STRArray::~STRArray() { if (m_pcsContents) delete[] m_pcsContents; } void STRArray::Add(LPCSTR lpstrNew) { if (m_ucItems >= m_ucMax) Grow(); m_pcsContents[m_ucItems++].Copy(lpstrNew); } void STRArray::Add(LPCWSTR lpstrNew) { if (m_ucItems >= m_ucMax) Grow(); m_pcsContents[m_ucItems++].Copy(lpstrNew); } void STRArray::Tokenize(LPCTSTR lpstrIn, TCHAR tcSplitter) { if (m_pcsContents) { delete[] m_pcsContents; m_ucItems = m_ucMax = 0; m_pcsContents = NULL; } if (!lpstrIn || !*lpstrIn) return; while (*lpstrIn) { // First, strip off any leading blanks while (*lpstrIn && *lpstrIn == _TEXT(' ')) lpstrIn++; for (LPCTSTR lpstrMoi = lpstrIn; *lpstrMoi && *lpstrMoi != tcSplitter; lpstrMoi++) ; // If we hit the end, just add the whole thing to the array if (!*lpstrMoi) { if (*lpstrIn) Add(lpstrIn); return; } // Otherwise, just add the string up to the splitter TCHAR szNew[MAX_PATH]; SIZE_T uiLen = (SIZE_T)(lpstrMoi - lpstrIn); if (uiLen < (sizeof(szNew)/sizeof(szNew[0])) - 1) { lstrcpyn(szNew,lpstrIn,(UINT)uiLen); szNew[uiLen] = TCHAR('\0'); Add((LPCTSTR) szNew); } lpstrIn = lpstrMoi + 1; } }