aspnetcore/src/AspNetCoreModuleV1/IISLib/stringa.cpp

1768 lines
34 KiB
C++

// 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<DWORD>(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<DWORD>(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<DWORD>(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<DWORD>(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<DWORD>(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<DWORD>(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<SIZE_T>(cb64NewSize) ) )
{
return E_OUTOFMEMORY;
}
}
memcpy( reinterpret_cast<BYTE*>(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<SIZE_T>(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<CHAR>(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<CHAR,1> * 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<CHAR,1> * pbufDstAnsiString,
__in DWORD dwStringLen
)
{
return ConvertUnicodeToCodePage( pszSrcUnicodeString,
pbufDstAnsiString,
dwStringLen,
CP_ACP );
}
// static
HRESULT
STRA::ConvertUnicodeToUTF8(
__in_ecount(dwStringLen)
LPCWSTR pszSrcUnicodeString,
__in BUFFER_T<CHAR,1> * 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;
}