// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. #include "precomp.h" STRA::STRA( VOID ) : m_cchLen( 0 ) { *( QueryStr() ) = '\0'; } STRA::STRA( __inout_ecount(cchInit) CHAR* pbInit, __in DWORD cchInit ) : m_Buff( pbInit, cchInit * sizeof( CHAR ) ), m_cchLen(0) /*++ Description: Used by STACK_STRA. Initially populates underlying buffer with pbInit. pbInit is not freed. Arguments: pbInit - initial memory to use cchInit - count, in characters, of pbInit Returns: None. --*/ { _ASSERTE( NULL != pbInit ); _ASSERTE( cchInit > 0 ); _ASSERTE( pbInit[0] == '\0' ); } BOOL STRA::IsEmpty( VOID ) const { return ( m_cchLen == 0 ); } BOOL STRA::Equals( __in PCSTR pszRhs, __in BOOL fIgnoreCase /*= FALSE*/ ) const { _ASSERTE( NULL != pszRhs ); if( fIgnoreCase ) { return ( 0 == _stricmp( QueryStr(), pszRhs ) ); } return ( 0 == strcmp( QueryStr(), pszRhs ) ); } BOOL STRA::Equals( __in const STRA * pstrRhs, __in BOOL fIgnoreCase /*= FALSE*/ ) const { _ASSERTE( NULL != pstrRhs ); return Equals( pstrRhs->QueryStr(), fIgnoreCase ); } BOOL STRA::Equals( __in const STRA & strRhs, __in BOOL fIgnoreCase /*= FALSE*/ ) const { return Equals( strRhs.QueryStr(), fIgnoreCase ); } DWORD STRA::QueryCB( VOID ) const // // Returns the number of bytes in the string excluding the terminating NULL // { return m_cchLen * sizeof( CHAR ); } DWORD STRA::QueryCCH( VOID ) const // // Returns the number of characters in the string excluding the terminating NULL // { return m_cchLen; } DWORD STRA::QuerySizeCCH( VOID ) const // // Returns size of the underlying storage buffer, in characters // { return m_Buff.QuerySize() / sizeof( CHAR ); } DWORD STRA::QuerySize( VOID ) const // // Returns the size of the storage buffer in bytes // { return m_Buff.QuerySize(); } __nullterminated __bcount(this->m_cchLen) CHAR * STRA::QueryStr( VOID ) const // // Return the string buffer // { return m_Buff.QueryPtr(); } VOID STRA::Reset( VOID ) // // Resets the internal string to be NULL string. Buffer remains cached. // { _ASSERTE( QueryStr() != NULL ); *(QueryStr()) = '\0'; m_cchLen = 0; } HRESULT STRA::Resize( __in DWORD cchSize ) { if( !m_Buff.Resize( cchSize * sizeof( CHAR ) ) ) { return E_OUTOFMEMORY; } return S_OK; } HRESULT STRA::SyncWithBuffer( VOID ) // // Recalculate the length of the string, etc. because we've modified // the buffer directly. // { HRESULT hr; size_t size; hr = StringCchLengthA( QueryStr(), QuerySizeCCH(), &size ); if ( SUCCEEDED( hr ) ) { m_cchLen = static_cast(size); } return hr; } HRESULT STRA::Copy( __in PCSTR pszCopy ) { HRESULT hr; size_t cbLen; hr = StringCbLengthA( pszCopy, STRSAFE_MAX_CCH, &cbLen ); if ( FAILED( hr ) ) { return hr; } return Copy( pszCopy, cbLen ); } HRESULT STRA::Copy( __in_ecount(cchLen) PCSTR pszCopy, __in SIZE_T cbLen ) // // Copy the contents of another string to this one // { _ASSERTE( cbLen <= MAXDWORD ); return AuxAppend( pszCopy, static_cast(cbLen), 0 ); } HRESULT STRA::Copy( __in const STRA * pstrRhs ) { _ASSERTE( pstrRhs != NULL ); return Copy( pstrRhs->QueryStr(), pstrRhs->QueryCCH() ); } HRESULT STRA::Copy( __in const STRA & strRhs ) { return Copy( strRhs.QueryStr(), strRhs.QueryCCH() ); } HRESULT STRA::CopyW( __in PCWSTR pszCopyW ) { HRESULT hr; size_t cchLen; hr = StringCchLengthW( pszCopyW, STRSAFE_MAX_CCH, &cchLen ); if ( FAILED( hr ) ) { return hr; } return CopyW( pszCopyW, cchLen ); } HRESULT STRA::CopyWTruncate( __in PCWSTR pszCopyWTruncate ) { HRESULT hr; size_t cchLen; hr = StringCchLengthW( pszCopyWTruncate, STRSAFE_MAX_CCH, &cchLen ); if ( FAILED( hr ) ) { return hr; } return CopyWTruncate( pszCopyWTruncate, cchLen ); } HRESULT STRA::CopyWTruncate( __in_ecount(cchLen) PCWSTR pszCopyWTruncate, __in SIZE_T cchLen ) // // The "Truncate" methods do not do proper conversion. They do a (CHAR) caste // { _ASSERTE( cchLen <= MAXDWORD ); return AuxAppendWTruncate( pszCopyWTruncate, static_cast(cchLen), 0 ); } HRESULT STRA::Append( __in PCSTR pszAppend ) { HRESULT hr; size_t cbLen; hr = StringCbLengthA( pszAppend, STRSAFE_MAX_CCH, &cbLen ); if ( FAILED( hr ) ) { return hr; } return Append( pszAppend, cbLen ); } HRESULT STRA::Append( __in_ecount(cchLen) PCSTR pszAppend, __in SIZE_T cbLen ) { _ASSERTE( cbLen <= MAXDWORD ); if ( cbLen == 0 ) { return S_OK; } return AuxAppend( pszAppend, static_cast(cbLen), QueryCB() ); } HRESULT STRA::Append( __in const STRA * pstrRhs ) { _ASSERTE( pstrRhs != NULL ); return Append( pstrRhs->QueryStr(), pstrRhs->QueryCCH() ); } HRESULT STRA::Append( __in const STRA & strRhs ) { return Append( strRhs.QueryStr(), strRhs.QueryCCH() ); } HRESULT STRA::AppendWTruncate( __in PCWSTR pszAppendWTruncate ) { HRESULT hr; size_t cchLen; hr = StringCchLengthW( pszAppendWTruncate, STRSAFE_MAX_CCH, &cchLen ); if ( FAILED( hr ) ) { return hr; } return AppendWTruncate( pszAppendWTruncate, cchLen ); } HRESULT STRA::AppendWTruncate( __in_ecount(cchLen) PCWSTR pszAppendWTruncate, __in SIZE_T cchLen ) // // The "Truncate" methods do not do proper conversion. They do a (CHAR) caste // { _ASSERTE( cchLen <= MAXDWORD ); if ( cchLen == 0 ) { return S_OK; } return AuxAppendWTruncate( pszAppendWTruncate, static_cast(cchLen), QueryCB() ); } HRESULT STRA::CopyToBuffer( __out_bcount(*pcb) CHAR* pszBuffer, __inout DWORD * pcb ) const // // Makes a copy of the stored string into the given buffer // { _ASSERTE( NULL != pszBuffer ); _ASSERTE( NULL != pcb ); HRESULT hr = S_OK; DWORD cbNeeded = QueryCB() + sizeof( CHAR ); if( *pcb < cbNeeded ) { hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ); goto Finished; } memcpy( pszBuffer, QueryStr(), cbNeeded ); Finished: *pcb = cbNeeded; return hr; } HRESULT STRA::SetLen( __in DWORD cchLen ) /*++ * Routine Description: Set the length of the string and null terminate, if there is sufficient buffer already allocated. Will not reallocate. Arguments: cchLen - The number of characters in the new string. Return Value: HRESULT --*/ { if( cchLen >= QuerySizeCCH() ) { return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *( QueryStr() + cchLen ) = '\0'; m_cchLen = cchLen; return S_OK; } HRESULT STRA::SafeSnprintf( __in __format_string PCSTR pszFormatString, ... ) /*++ Routine Description: Writes to a STRA, growing it as needed. It arbitrarily caps growth at 64k chars. Arguments: pszFormatString - printf format ... - printf args Return Value: HRESULT --*/ { HRESULT hr = S_OK; va_list argsList; va_start( argsList, pszFormatString ); hr = SafeVsnprintf(pszFormatString, argsList); va_end( argsList ); return hr; } HRESULT STRA::SafeVsnprintf( __in __format_string PCSTR pszFormatString, va_list argsList ) /*++ Routine Description: Writes to a STRA, growing it as needed. It arbitrarily caps growth at 64k chars. Arguments: pszFormatString - printf format argsList - printf va_list Return Value: HRESULT --*/ { HRESULT hr = S_OK; int cchOutput; int cchNeeded; // // Format the incoming message using vsnprintf() // so that the overflows are captured // cchOutput = _vsnprintf_s( QueryStr(), QuerySizeCCH(), QuerySizeCCH() - 1, pszFormatString, argsList ); if( cchOutput == -1 ) { // // Couldn't fit this in the original STRU size. // cchNeeded = _vscprintf( pszFormatString, argsList ); if( cchNeeded > 64 * 1024 ) { // // If we're trying to produce a string > 64k chars, then // there is probably a problem // hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Finished; } // // _vscprintf doesn't include terminating null character // cchNeeded++; hr = Resize( cchNeeded ); if( FAILED( hr ) ) { goto Finished; } cchOutput = _vsnprintf_s( QueryStr(), QuerySizeCCH(), QuerySizeCCH() - 1, pszFormatString, argsList ); if( -1 == cchOutput ) { // // This should never happen, cause we should already have correctly sized memory // _ASSERTE( FALSE ); hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); goto Finished; } } // // always null terminate at the last WCHAR // QueryStr()[ QuerySizeCCH() - 1 ] = L'\0'; // // we directly touched the buffer - therefore: // hr = SyncWithBuffer(); if( FAILED( hr ) ) { goto Finished; } Finished: if( FAILED( hr ) ) { Reset(); } return hr; } bool FShouldEscapeUtf8( BYTE ch ) { if ( ( ch >= 128 ) ) { return true; } return false; } bool FShouldEscapeUrl( BYTE ch ) { if ( ( ch >= 128 || ch <= 32 || ch == '<' || ch == '>' || ch == '%' || ch == '?' || ch == '#' ) && !( ch == '\n' || ch == '\r' ) ) { return true; } return false; } HRESULT STRA::Escape( VOID ) /*++ Routine Description: Escapes a STRA Arguments: None Return Value: None --*/ { return EscapeInternal( FShouldEscapeUrl ); } HRESULT STRA::EscapeUtf8( VOID ) /*++ Routine Description: Escapes the high-bit chars in a STRA. LWS, CR, LF & controls are untouched. Arguments: None Return Value: None --*/ { return EscapeInternal( FShouldEscapeUtf8 ); } HRESULT STRA::EscapeInternal( PFN_F_SHOULD_ESCAPE pfnFShouldEscape ) /*++ Routine Description: Escapes a STRA according to the predicate function passed in Arguments: None Return Value: None --*/ { LPCSTR pch = QueryStr(); __analysis_assume( pch != NULL ); int i = 0; BYTE ch; HRESULT hr = S_OK; BOOL fRet = FALSE; SIZE_T NewSize = 0; // Set to true if any % escaping occurs BOOL fEscapingDone = FALSE; // // If there are any characters that need to be escaped we copy the entire string // character by character into straTemp, escaping as we go, then at the end // copy all of straTemp over. Don't modify InlineBuffer directly. // CHAR InlineBuffer[512]; InlineBuffer[0] = '\0'; STRA straTemp(InlineBuffer, sizeof(InlineBuffer)/sizeof(*InlineBuffer)); _ASSERTE( pch ); while (ch = pch[i]) { // // Escape characters that are in the non-printable range // but ignore CR and LF // if ( pfnFShouldEscape( ch ) ) { if (FALSE == fEscapingDone) { // first character in the string that needed escaping fEscapingDone = TRUE; // guess that the size needs to be larger than // what we used to have times two NewSize = QueryCCH() * 2; if ( NewSize > MAXDWORD ) { hr = HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW ); return hr; } hr = straTemp.Resize( static_cast(NewSize) ); if (FAILED(hr)) { return hr; } // Copy all of the previous buffer into buffTemp, only if it is not the first character: if ( i > 0) { hr = straTemp.Copy(QueryStr(), i * sizeof(CHAR)); if (FAILED(hr)) { return hr; } } } // resize the temporary (if needed) with the slop of the entire buffer length // this fixes constant reallocation if the entire string needs to be escaped NewSize = QueryCCH() + 2 * sizeof(CHAR) + 1 * sizeof(CHAR); if ( NewSize > MAXDWORD ) { hr = HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW ); return hr; } fRet = straTemp.m_Buff.Resize( NewSize ); if ( !fRet ) { hr = HRESULT_FROM_WIN32(GetLastError()); return hr; } // // Create the string to append for the current character // CHAR chHex[3]; chHex[0] = '%'; // // Convert the low then the high character to hex // UINT nLowDigit = (UINT)(ch % 16); chHex[2] = TODIGIT( nLowDigit ); ch /= 16; UINT nHighDigit = (UINT)(ch % 16); chHex[1] = TODIGIT( nHighDigit ); // // Actually append the converted character to the end of the temporary // hr = straTemp.Append(chHex, 3); if (FAILED(hr)) { return hr; } } else { // if no escaping done, no need to copy if (fEscapingDone) { // if ANY escaping done, copy current character into new buffer straTemp.Append(&pch[i], 1); } } // inspect the next character in the string i++; } if (fEscapingDone) { // the escaped string is now in straTemp hr = Copy(straTemp); } return hr; } // EscapeInternal() VOID STRA::Unescape( VOID ) /*++ Routine Description: Unescapes a STRA Supported escape sequences are: %uxxxx unescapes Unicode character xxxx into system codepage %xx unescapes character xx % without following hex digits is ignored Arguments: None Return Value: None --*/ { CHAR *pScan; CHAR *pDest; CHAR *pNextScan; WCHAR wch; DWORD dwLen; BOOL fChanged = FALSE; // // Now take care of any escape characters // pDest = pScan = strchr(QueryStr(), '%'); while (pScan) { if ((pScan[1] == 'u' || pScan[1] == 'U') && SAFEIsXDigit(pScan[2]) && SAFEIsXDigit(pScan[3]) && SAFEIsXDigit(pScan[4]) && SAFEIsXDigit(pScan[5])) { wch = TOHEX(pScan[2]) * 4096 + TOHEX(pScan[3]) * 256 + TOHEX(pScan[4]) * 16 + TOHEX(pScan[5]); dwLen = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, &wch, 1, (LPSTR) pDest, 6, NULL, NULL); pDest += dwLen; pScan += 6; fChanged = TRUE; } else if (SAFEIsXDigit(pScan[1]) && SAFEIsXDigit(pScan[2])) { *pDest = TOHEX(pScan[1]) * 16 + TOHEX(pScan[2]); pDest ++; pScan += 3; fChanged = TRUE; } else // Not an escaped char, just a '%' { if (fChanged) { *pDest = *pScan; } pDest++; pScan++; } // // Copy all the information between this and the next escaped char // pNextScan = strchr(pScan, '%'); if (fChanged) // pScan!=pDest, so we have to copy the char's { if (!pNextScan) // That was the last '%' in the string { memmove(pDest, pScan, QueryCCH() - DIFF(pScan - QueryStr()) + 1); } else { // There is another '%', move intermediate chars if ((dwLen = (DWORD)DIFF(pNextScan - pScan)) != 0) { memmove(pDest, pScan, dwLen); pDest += dwLen; } } } pScan = pNextScan; } if (fChanged) { m_cchLen = (DWORD)strlen(QueryStr()); // for safety recalc the length } return; } HRESULT STRA::CopyWToUTF8Unescaped( __in LPCWSTR cpchStr ) { return STRA::CopyWToUTF8Unescaped(cpchStr, (DWORD) wcslen(cpchStr)); } HRESULT STRA::CopyWToUTF8Unescaped( __in_ecount(cch) LPCWSTR cpchStr, __in DWORD cch ) { HRESULT hr = S_OK; int iRet; if (cch == 0) { Reset(); return S_OK; } iRet = ConvertUnicodeToUTF8(cpchStr, &m_Buff, cch); if (-1 == iRet) { // could not convert hr = HRESULT_FROM_WIN32(GetLastError()); goto Finished; } m_cchLen = iRet; _ASSERTE(strlen(m_Buff.QueryPtr()) == m_cchLen); Finished: return hr; } HRESULT STRA::CopyWToUTF8Escaped( __in LPCWSTR cpchStr ) { return STRA::CopyWToUTF8Escaped(cpchStr, (DWORD) wcslen(cpchStr)); } HRESULT STRA::CopyWToUTF8Escaped( __in_ecount(cch) LPCWSTR cpchStr, __in DWORD cch ) { HRESULT hr = S_OK; hr = CopyWToUTF8Unescaped(cpchStr, cch); if (FAILED(hr)) { goto Finished; } hr = Escape(); if (FAILED(hr)) { goto Finished; } hr = S_OK; Finished: return hr; } HRESULT STRA::AuxAppend( __in_ecount(cbLen) LPCSTR pStr, __in DWORD cbLen, __in DWORD cbOffset ) { _ASSERTE( NULL != pStr ); _ASSERTE( cbOffset <= QueryCB() ); ULONGLONG cb64NewSize = (ULONGLONG)cbOffset + cbLen + sizeof( CHAR ); if( cb64NewSize > MAXDWORD ) { return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW ); } if( m_Buff.QuerySize() < cb64NewSize ) { if( !m_Buff.Resize( static_cast(cb64NewSize) ) ) { return E_OUTOFMEMORY; } } memcpy( reinterpret_cast(m_Buff.QueryPtr()) + cbOffset, pStr, cbLen ); m_cchLen = cbLen + cbOffset; *( QueryStr() + m_cchLen ) = '\0'; return S_OK; } HRESULT STRA::AuxAppendW( __in_ecount(cchAppendW) PCWSTR pszAppendW, __in DWORD cchAppendW, __in DWORD cbOffset, __in UINT CodePage, __in BOOL fFailIfNoTranslation, __in DWORD dwFlags ) { HRESULT hr = S_OK; DWORD cbAvailable = 0; DWORD cbRet = 0; // // There are only two expect places to append // _ASSERTE( 0 == cbOffset || QueryCB() == cbOffset ); if ( cchAppendW == 0 ) { goto Finished; } // // start by assuming 1 char to 1 char will be enough space // if( !m_Buff.Resize( cbOffset + cchAppendW + sizeof( CHAR ) ) ) { hr = E_OUTOFMEMORY; goto Finished; } cbAvailable = m_Buff.QuerySize() - cbOffset; cbRet = WideCharToMultiByte( CodePage, dwFlags, pszAppendW, cchAppendW, QueryStr() + cbOffset, cbAvailable, NULL, NULL ); if( 0 != cbRet ) { if(!m_Buff.Resize(cbOffset + cbRet + 1)) { hr = E_OUTOFMEMORY; } // // not zero --> success, so we're done // goto Finished; } // // We only know how to handle ERROR_INSUFFICIENT_BUFFER // hr = HRESULT_FROM_WIN32( GetLastError() ); if( hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) ) { goto Finished; } // // Reset HResult because we need to get the number of bytes needed // hr = S_OK; cbRet = WideCharToMultiByte( CodePage, dwFlags, pszAppendW, cchAppendW, NULL, 0, NULL, NULL ); if( 0 == cbRet ) { // // no idea how we could ever reach here // hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } if( !m_Buff.Resize( cbOffset + cbRet + 1) ) { hr = E_OUTOFMEMORY; goto Finished; } cbAvailable = m_Buff.QuerySize() - cbOffset; cbRet = WideCharToMultiByte( CodePage, dwFlags, pszAppendW, cchAppendW, QueryStr() + cbOffset, cbAvailable, NULL, NULL ); if( 0 == cbRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } Finished: if( SUCCEEDED( hr ) && 0 != cbRet ) { m_cchLen = cbRet + cbOffset; } // // ensure we're still NULL terminated in the right spot // (regardless of success or failure) // QueryStr()[m_cchLen] = '\0'; return hr; } HRESULT STRA::AuxAppendWTruncate( __in_ecount(cchAppendW) __in PCWSTR pszAppendW, __in DWORD cchAppendW, __in DWORD cbOffset ) // // Cheesey WCHAR --> CHAR conversion // { HRESULT hr = S_OK; CHAR* pszBuffer; _ASSERTE( NULL != pszAppendW ); _ASSERTE( 0 == cbOffset || cbOffset == QueryCB() ); if( !pszAppendW ) { hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); goto Finished; } ULONGLONG cbNeeded = (ULONGLONG)cbOffset + cchAppendW + sizeof( CHAR ); if( cbNeeded > MAXDWORD ) { hr = HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW ); goto Finished; } if( !m_Buff.Resize( static_cast(cbNeeded) ) ) { hr = E_OUTOFMEMORY; goto Finished; } // // Copy/convert the UNICODE string over (by making two bytes into one) // pszBuffer = QueryStr() + cbOffset; for( DWORD i = 0; i < cchAppendW; i++ ) { pszBuffer[i] = static_cast(pszAppendW[i]); } m_cchLen = cchAppendW + cbOffset; *( QueryStr() + m_cchLen ) = '\0'; Finished: return hr; } // static int STRA::ConvertUnicodeToCodePage( __in_ecount(dwStringLen) LPCWSTR pszSrcUnicodeString, __inout BUFFER_T * pbufDstAnsiString, __in DWORD dwStringLen, __in UINT uCodePage ) { _ASSERTE(NULL != pszSrcUnicodeString); _ASSERTE(NULL != pbufDstAnsiString); BOOL bTemp; int iStrLen = 0; DWORD dwFlags; if (uCodePage == CP_ACP) { dwFlags = WC_NO_BEST_FIT_CHARS; } else { dwFlags = 0; } iStrLen = WideCharToMultiByte(uCodePage, dwFlags, pszSrcUnicodeString, dwStringLen, (LPSTR)pbufDstAnsiString->QueryPtr(), (int)pbufDstAnsiString->QuerySize(), NULL, NULL); if ((iStrLen == 0) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { iStrLen = WideCharToMultiByte(uCodePage, dwFlags, pszSrcUnicodeString, dwStringLen, NULL, 0, NULL, NULL); if (iStrLen != 0) { // add one just for the extra NULL bTemp = pbufDstAnsiString->Resize(iStrLen + 1); if (!bTemp) { iStrLen = 0; } else { iStrLen = WideCharToMultiByte(uCodePage, dwFlags, pszSrcUnicodeString, dwStringLen, (LPSTR)pbufDstAnsiString->QueryPtr(), (int)pbufDstAnsiString->QuerySize(), NULL, NULL); } } } if (0 != iStrLen && pbufDstAnsiString->Resize(iStrLen + 1)) { // insert a terminating NULL into buffer for the dwStringLen+1 in the case that the dwStringLen+1 was not a NULL. ((CHAR*)pbufDstAnsiString->QueryPtr())[iStrLen] = '\0'; } else { iStrLen = -1; } return iStrLen; } // static HRESULT STRA::ConvertUnicodeToMultiByte( __in_ecount(dwStringLen) LPCWSTR pszSrcUnicodeString, __in BUFFER_T * pbufDstAnsiString, __in DWORD dwStringLen ) { return ConvertUnicodeToCodePage( pszSrcUnicodeString, pbufDstAnsiString, dwStringLen, CP_ACP ); } // static HRESULT STRA::ConvertUnicodeToUTF8( __in_ecount(dwStringLen) LPCWSTR pszSrcUnicodeString, __in BUFFER_T * pbufDstAnsiString, __in DWORD dwStringLen ) { return ConvertUnicodeToCodePage( pszSrcUnicodeString, pbufDstAnsiString, dwStringLen, CP_UTF8 ); } /*++ Routine Description: Removes leading and trailing whitespace --*/ VOID STRA::Trim() { PSTR pszString = QueryStr(); DWORD cchNewLength = m_cchLen; DWORD cchLeadingWhitespace = 0; DWORD cchTempLength = 0; for (LONG ixString = m_cchLen - 1; ixString >= 0; ixString--) { if (isspace((unsigned char) pszString[ixString]) != 0) { pszString[ixString] = '\0'; cchNewLength--; } else { break; } } cchTempLength = cchNewLength; for (DWORD ixString = 0; ixString < cchTempLength; ixString++) { if (isspace((unsigned char) pszString[ixString]) != 0) { cchLeadingWhitespace++; cchNewLength--; } else { break; } } if (cchNewLength == 0) { Reset(); } else if (cchLeadingWhitespace > 0) { memmove(pszString, pszString + cchLeadingWhitespace, cchNewLength * sizeof(CHAR)); pszString[cchNewLength] = '\0'; } SyncWithBuffer(); } /*++ Routine Description: Compares the string to the provided prefix to check for equality Arguments: pStraPrefix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if prefix string matches with internal string, FALSE otherwise --*/ BOOL STRA::StartsWith( __in const STRA * pStraPrefix, __in bool fIgnoreCase) const { _ASSERTE( pStraPrefix != NULL ); return StartsWith(pStraPrefix->QueryStr(), fIgnoreCase); } /*++ Routine Description: Compares the string to the provided prefix to check for equality Arguments: straPrefix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if prefix string matches with internal string, FALSE otherwise --*/ BOOL STRA::StartsWith( __in const STRA & straPrefix, __in bool fIgnoreCase) const { return StartsWith(straPrefix.QueryStr(), fIgnoreCase); } /*++ Routine Description: Compares the string to the provided prefix to check for equality Arguments: pszPrefix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if prefix string matches with internal string, FALSE otherwise --*/ BOOL STRA::StartsWith( __in PCSTR pszPrefix, __in bool fIgnoreCase) const { HRESULT hr = S_OK; BOOL fMatch = FALSE; size_t cchPrefix = 0; if (pszPrefix == NULL) { goto Finished; } hr = StringCchLengthA( pszPrefix, STRSAFE_MAX_CCH, &cchPrefix ); if (FAILED(hr)) { goto Finished; } _ASSERTE( cchPrefix <= MAXDWORD ); if (cchPrefix > m_cchLen) { goto Finished; } if( fIgnoreCase ) { fMatch = ( 0 == _strnicmp( QueryStr(), pszPrefix, cchPrefix ) ); } else { fMatch = ( 0 == strncmp( QueryStr(), pszPrefix, cchPrefix ) ); } Finished: return fMatch; } /*++ Routine Description: Compares the string to the provided suffix to check for equality Arguments: pStraSuffix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if suffix string matches with internal string, FALSE otherwise --*/ BOOL STRA::EndsWith( __in const STRA * pStraSuffix, __in bool fIgnoreCase) const { _ASSERTE( pStraSuffix != NULL ); return EndsWith(pStraSuffix->QueryStr(), fIgnoreCase); } /*++ Routine Description: Compares the string to the provided suffix to check for equality Arguments: straSuffix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if suffix string matches with internal string, FALSE otherwise --*/ BOOL STRA::EndsWith( __in const STRA & straSuffix, __in bool fIgnoreCase) const { return EndsWith(straSuffix.QueryStr(), fIgnoreCase); } /*++ Routine Description: Compares the string to the provided suffix to check for equality Arguments: pszSuffix - string to compare with fIgnoreCase - indicates whether the string comparison should be case-sensitive Return Value: TRUE if suffix string matches with internal string, FALSE otherwise --*/ BOOL STRA::EndsWith( __in PCSTR pszSuffix, __in bool fIgnoreCase) const { HRESULT hr = S_OK; PSTR pszString = QueryStr(); BOOL fMatch = FALSE; size_t cchSuffix = 0; ptrdiff_t ixOffset = 0; if (pszSuffix == NULL) { goto Finished; } hr = StringCchLengthA( pszSuffix, STRSAFE_MAX_CCH, &cchSuffix ); if (FAILED(hr)) { goto Finished; } _ASSERTE( cchSuffix <= MAXDWORD ); if (cchSuffix > m_cchLen) { goto Finished; } ixOffset = m_cchLen - cchSuffix; _ASSERTE(ixOffset >= 0 && ixOffset <= MAXDWORD); if( fIgnoreCase ) { fMatch = ( 0 == _strnicmp( pszString + ixOffset, pszSuffix, cchSuffix ) ); } else { fMatch = ( 0 == strncmp( pszString + ixOffset, pszSuffix, cchSuffix ) ); } Finished: return fMatch; } /*++ Routine Description: Searches the string for the first occurrence of the specified character. Arguments: charValue - character to find dwStartIndex - the initial index. Return Value: The index for the first character occurence in the string. -1 if not found. --*/ INT STRA::IndexOf( __in CHAR charValue, __in DWORD dwStartIndex ) const { INT nIndex = -1; // Make sure that there are no buffer overruns. if( dwStartIndex >= QueryCCH() ) { goto Finished; } const CHAR* pChar = strchr( QueryStr() + dwStartIndex, charValue ); // Determine the index if found if( pChar ) { // nIndex will be set to -1 on failure. (VOID)SizeTToInt( pChar - QueryStr(), &nIndex ); } Finished: return nIndex; } /*++ Routine Description: Searches the string for the first occurrence of the specified substring. Arguments: pszValue - substring to find dwStartIndex - initial index. Return Value: The index for the first character occurence in the string. -1 if not found. --*/ INT STRA::IndexOf( __in PCSTR pszValue, __in DWORD dwStartIndex ) const { HRESULT hr = S_OK; INT nIndex = -1; SIZE_T cchValue = 0; // Validate input parameters if( dwStartIndex >= QueryCCH() || !pszValue ) { goto Finished; } const CHAR* pChar = strstr( QueryStr() + dwStartIndex, pszValue ); // Determine the index if found if( pChar ) { // nIndex will be set to -1 on failure. (VOID)SizeTToInt( pChar - QueryStr(), &nIndex ); } Finished: return nIndex; } /*++ Routine Description: Searches the string for the last occurrence of the specified character. Arguments: charValue - character to find dwStartIndex - initial index. Return Value: The index for the last character occurence in the string. -1 if not found. --*/ INT STRA::LastIndexOf( __in CHAR charValue, __in DWORD dwStartIndex ) const { INT nIndex = -1; // Make sure that there are no buffer overruns. if( dwStartIndex >= QueryCCH() ) { goto Finished; } const CHAR* pChar = strrchr( QueryStr() + dwStartIndex, charValue ); // Determine the index if found if( pChar ) { // nIndex will be set to -1 on failure. (VOID)SizeTToInt( pChar - QueryStr(), &nIndex ); } Finished: return nIndex; }