You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
994 lines
21 KiB
994 lines
21 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
tstr.cxx
|
|
|
|
Abstract:
|
|
|
|
Class that is used for string manipulation
|
|
|
|
Author:
|
|
|
|
Christopher Achille (cachille)
|
|
|
|
Project:
|
|
|
|
Internet Services Setup
|
|
|
|
Revision History:
|
|
|
|
January 2002: Created
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
// Constructor
|
|
//
|
|
TSTR::TSTR()
|
|
: m_bSensitiveData( FALSE )
|
|
{
|
|
Copy( _T("\0") );
|
|
}
|
|
|
|
// Constructor
|
|
//
|
|
// Allows you to construct the buffer, of a certain size.
|
|
// This is really just a suggestion, since on failure we have
|
|
// no way to alert the user. It is okay though, since subsequest
|
|
// copies and appends will fail if it can not resize
|
|
//
|
|
// Parameters:
|
|
// dwInitialSize - Initial size for the buffer (in chars)
|
|
//
|
|
TSTR::TSTR(DWORD dwInitialSize)
|
|
: m_bSensitiveData( FALSE )
|
|
{
|
|
Copy( _T("\0") );
|
|
|
|
// Ignore return value, since if we don't resize, it is okay,
|
|
// subsequent calls will fail in that case
|
|
Resize( dwInitialSize );
|
|
}
|
|
|
|
// Destructor
|
|
//
|
|
TSTR::~TSTR()
|
|
{
|
|
if ( m_bSensitiveData )
|
|
{
|
|
// Clear old contents
|
|
SecureZeroMemory( m_buff.QueryPtr(), m_buff.QuerySize() );
|
|
}
|
|
}
|
|
|
|
// MarkSensitiveData
|
|
//
|
|
// Marks this information as sensitive data. What this does is it
|
|
// makes sure than when we delete it, SecureZeroMemory is used
|
|
// to delete the old contents. It does not save us from the
|
|
// Resize functions, but we will worry about that later.
|
|
//
|
|
// Parameters:
|
|
// bIsSensitive - Is the data sensitive or not?
|
|
//
|
|
void
|
|
TSTR::MarkSensitiveData( BOOL bIsSensitive )
|
|
{
|
|
if ( !m_bSensitiveData )
|
|
{
|
|
// We will allow the use to go from unsensitive to sensitive, but
|
|
// not to go back, since what was there before might be sensitive
|
|
m_bSensitiveData = bIsSensitive;
|
|
}
|
|
}
|
|
|
|
// function: Resize
|
|
//
|
|
// resize the string to the size needed
|
|
//
|
|
// Parameters:
|
|
// dwChars - The size in characters, including the terminating NULL
|
|
BOOL
|
|
TSTR::Resize(DWORD dwChars)
|
|
{
|
|
if ( dwChars <= QuerySize() )
|
|
{
|
|
// No resize needed
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !m_bSensitiveData )
|
|
{
|
|
// If this is not sensitive data, this is an easy operation
|
|
return m_buff.Resize( dwChars * sizeof(TCHAR) );
|
|
}
|
|
|
|
// Since this is sensitive data, we must make sure that during the resize,
|
|
// data is not left in memory somewhere
|
|
TSTR strTemp;
|
|
BOOL bRet;
|
|
|
|
if ( !strTemp.Copy( QueryStr() ) )
|
|
{
|
|
// Could not copy into temp for buffer
|
|
return FALSE;
|
|
}
|
|
|
|
strTemp.MarkSensitiveData( TRUE );
|
|
|
|
// Clear old contents before resizing and possibly losing old pointer with data
|
|
SecureZeroMemory( m_buff.QueryPtr(), m_buff.QuerySize() );
|
|
bRet = m_buff.Resize( dwChars * sizeof(TCHAR) );
|
|
|
|
// This can not fail, since the buffer could not have gotten smaller
|
|
// This call will copy only the bytes before the NULL terminator, the other
|
|
// ones will be lost. This is by design, it is more secure if we don't
|
|
// copy those that aren't being used
|
|
DBG_REQUIRE( Copy( strTemp.QueryStr() ) );
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// function: QueryStr
|
|
//
|
|
// Query a pointer to the string
|
|
//
|
|
LPTSTR
|
|
TSTR::QueryStr()
|
|
{
|
|
return (LPTSTR) m_buff.QueryPtr();
|
|
}
|
|
|
|
// function: Copy
|
|
//
|
|
// Copy the Source string into this string itself
|
|
//
|
|
// Parameters:
|
|
// szSource - The string to be copied
|
|
//
|
|
BOOL
|
|
TSTR::Copy(LPCTSTR szSource)
|
|
{
|
|
DWORD dwSize = _tcslen(szSource) + 1;
|
|
BOOL bRet = TRUE;
|
|
|
|
if ( dwSize > QuerySize() )
|
|
{
|
|
bRet = Resize( dwSize );
|
|
}
|
|
|
|
if ( bRet )
|
|
{
|
|
_tcscpy( QueryStr(), szSource );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// function: Copy
|
|
//
|
|
// Copy the Source string into this string itself
|
|
//
|
|
// Parameters:
|
|
// strSource - The string to be copied
|
|
//
|
|
BOOL
|
|
TSTR::Copy(TSTR &strSource)
|
|
{
|
|
return Copy( strSource.QueryStr() );
|
|
}
|
|
|
|
// funtion: QueryLen
|
|
//
|
|
// Query the length of the string (in chars) without the null terminator
|
|
//
|
|
DWORD
|
|
TSTR::QueryLen()
|
|
{
|
|
return _tcslen( QueryStr() );
|
|
}
|
|
|
|
// function: QuerySize
|
|
//
|
|
// Query the size of the buffer in chars
|
|
//
|
|
DWORD
|
|
TSTR::QuerySize()
|
|
{
|
|
return ( m_buff.QuerySize() / sizeof( TCHAR ) );
|
|
}
|
|
|
|
// function: Append
|
|
//
|
|
// Append a string to the end of what we have
|
|
//
|
|
// Parameters:
|
|
// szSource - The string to be appended
|
|
//
|
|
BOOL
|
|
TSTR::Append(LPCTSTR szSource)
|
|
{
|
|
DWORD dwCurrentLen = QueryLen();
|
|
DWORD dwSize = _tcslen(szSource) + 1 + dwCurrentLen;
|
|
|
|
BOOL bRet = TRUE;
|
|
|
|
if ( dwSize > QuerySize() )
|
|
{
|
|
bRet = Resize( dwSize );
|
|
}
|
|
|
|
if ( bRet )
|
|
{
|
|
_tcscpy( QueryStr() + dwCurrentLen , szSource );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// function: Append
|
|
//
|
|
// Append a string to the end of what we have
|
|
//
|
|
// Parameters:
|
|
// strSource - The string to be appended
|
|
//
|
|
BOOL
|
|
TSTR::Append(TSTR &strSource)
|
|
{
|
|
return Append( strSource.QueryStr() );
|
|
}
|
|
|
|
// function: IsEqual
|
|
//
|
|
// Is the string passed in, equal to the one in the class
|
|
//
|
|
// Parameters:
|
|
// szCompareString - The string to compare it with
|
|
// bCaseSensitive - Is the test case sensitive
|
|
//
|
|
BOOL
|
|
TSTR::IsEqual(LPCTSTR szCompareString, BOOL bCaseSensitive)
|
|
{
|
|
BOOL bRet;
|
|
|
|
if ( bCaseSensitive )
|
|
{
|
|
bRet = _tcscmp( QueryStr(), szCompareString ) == 0;
|
|
}
|
|
else
|
|
{
|
|
bRet = _tcsicmp( QueryStr(), szCompareString ) == 0;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// function: FindSubString
|
|
//
|
|
// Find a substring inside of a string(ie. "st" inside of "Test"
|
|
//
|
|
// Parameters:
|
|
// szSubString - the sub string to find
|
|
// bCaseSensitive - Is the search case sensitive?
|
|
//
|
|
// Return Values:
|
|
// NULL - Not found.
|
|
// pointer - pointer to substring
|
|
//
|
|
LPTSTR
|
|
TSTR::FindSubString(LPTSTR szSubString, BOOL bCaseSensitive )
|
|
{
|
|
if ( bCaseSensitive )
|
|
{
|
|
// If case sensitive, then we can just call tscstr
|
|
return _tcsstr( QueryStr(), szSubString );
|
|
}
|
|
|
|
LPTSTR szSubCurrent; // Current position in substring
|
|
LPTSTR szStringCurrent = QueryStr(); // Current position in full string
|
|
|
|
while ( *szStringCurrent )
|
|
{
|
|
// Walk through string comparing each character in string, with the first
|
|
// one in the sub string
|
|
if ( ( bCaseSensitive && ( *szStringCurrent == *szSubString ) ) ||
|
|
( !bCaseSensitive && ( ToLower( *szStringCurrent ) == ToLower( *szSubString ) ) )
|
|
)
|
|
{
|
|
// The first character matched, so lets check the rest of the sub string
|
|
szSubCurrent = szSubString + 1;
|
|
LPTSTR szStringTemp = szStringCurrent + 1;
|
|
|
|
while ( ( *szSubCurrent != '\0' ) &&
|
|
( *szStringTemp != '\0' ) &&
|
|
( bCaseSensitive || ( ToLower( *szStringTemp ) == ToLower( *szSubCurrent ) ) ) &&
|
|
( !bCaseSensitive || ( *szStringTemp == *szSubCurrent ) )
|
|
)
|
|
{
|
|
szStringTemp++;
|
|
szSubCurrent++;
|
|
}
|
|
|
|
if ( *szSubCurrent == '\0' )
|
|
{
|
|
// We found a match, so return the pointer to this substring in the main string
|
|
return szStringCurrent;
|
|
}
|
|
}
|
|
|
|
// Increment pointer in string
|
|
szStringCurrent++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// function: ToLower
|
|
//
|
|
// Get the lower case of a character
|
|
//
|
|
// Parameters:
|
|
// cChar - The character to make lower
|
|
//
|
|
// Return Values
|
|
// Either the character that was sent in, or its lower value if possible
|
|
//
|
|
TCHAR
|
|
TSTR::ToLower(TCHAR cChar)
|
|
{
|
|
#ifdef UNICODE
|
|
if ( iswupper( cChar ) )
|
|
{
|
|
return towlower( cChar );
|
|
}
|
|
else
|
|
{
|
|
return cChar;
|
|
}
|
|
#else
|
|
return (TCHAR) tolower( cChar );
|
|
#endif
|
|
}
|
|
|
|
// function: SubStringExists
|
|
//
|
|
// Does a substring exist inside a string (ie. "st" inside of "test"
|
|
//
|
|
// Parameters:
|
|
// szSubString - the sub string to find
|
|
// bCaseSensitive - Is the search case sensitive?
|
|
//
|
|
// Return Values:
|
|
// TRUE - Found
|
|
// FALSE - Not found
|
|
BOOL
|
|
TSTR::SubStringExists(LPTSTR szCompareString, BOOL bCaseSensitive )
|
|
{
|
|
return ( FindSubString( szCompareString, bCaseSensitive ) != NULL );
|
|
}
|
|
|
|
// function: Format
|
|
//
|
|
// format a string, same exact functionality as snprintf
|
|
//
|
|
// Parameters:
|
|
// szFormat - The format for the string
|
|
// ... - The options parameters to snprintf
|
|
//
|
|
// Return Value
|
|
// TRUE - Successfully copied
|
|
// FALSE - Error, probly couldn't get memory
|
|
//
|
|
BOOL
|
|
TSTR::Format(LPTSTR szFormat ... )
|
|
{
|
|
DWORD dwTryCount = 0;
|
|
va_list va;
|
|
INT iRet;
|
|
|
|
// Initialize the variable length arguments
|
|
va_start(va, szFormat);
|
|
|
|
do {
|
|
iRet = _vsntprintf( QueryStr(), QuerySize(), szFormat, va);
|
|
|
|
if ( iRet > 0 )
|
|
{
|
|
// Successfully written to string
|
|
if ( ( (DWORD) iRet ) == QuerySize() )
|
|
{
|
|
// Lets null terminate just to be sure
|
|
*( QueryStr() + iRet - 1 ) = _T( '\0' );
|
|
}
|
|
|
|
va_end(va);
|
|
return TRUE;
|
|
}
|
|
|
|
dwTryCount++;
|
|
} while ( ( dwTryCount < TSTR_SNPRINTF_RESIZER_TRIES ) &&
|
|
( Resize( QuerySize() * 2 + TSTR_SNPRINTF_RESIZER_SIZE ) )
|
|
);
|
|
|
|
// Uninitialize the arguments
|
|
va_end(va);
|
|
|
|
// We fail
|
|
return FALSE;
|
|
}
|
|
|
|
// LoadString
|
|
//
|
|
// Load a string into our class
|
|
//
|
|
BOOL
|
|
TSTR::LoadString( UINT uResourceId )
|
|
{
|
|
INT iRet;
|
|
|
|
// Lets resize first, so that we can hopefully fit the string
|
|
if ( !Resize( MAX_PATH ) )
|
|
{
|
|
// Could not resize
|
|
return FALSE;
|
|
}
|
|
|
|
iRet = ::LoadString( (HINSTANCE) g_MyModuleHandle,
|
|
uResourceId,
|
|
QueryStr(),
|
|
QuerySize() );
|
|
|
|
ASSERT( iRet < (INT) QuerySize() );
|
|
|
|
return ( iRet > 0 );
|
|
}
|
|
|
|
// Constructor
|
|
TSTR_PATH::TSTR_PATH()
|
|
{
|
|
|
|
}
|
|
|
|
// Constructor
|
|
TSTR_PATH::TSTR_PATH(DWORD dwInitialSize)
|
|
: TSTR( dwInitialSize )
|
|
{
|
|
|
|
}
|
|
|
|
// ExpandEnvironmentVariables
|
|
//
|
|
// Expand the environment variables in the string
|
|
// No parameters needed, it takes the string already
|
|
// in the TSTR
|
|
//
|
|
BOOL
|
|
TSTR_PATH::ExpandEnvironmentVariables()
|
|
{
|
|
TSTR strResult;
|
|
DWORD dwRequiredLen;
|
|
|
|
if ( _tcschr( QueryStr(), _T('%') ) == NULL )
|
|
{
|
|
// If there is not a '%' in the string, then we don't have
|
|
// to do any expanding, so lets quit
|
|
return TRUE;
|
|
}
|
|
|
|
// Try to expand the strings
|
|
dwRequiredLen = ExpandEnvironmentStrings( QueryStr(),
|
|
strResult.QueryStr(),
|
|
strResult.QuerySize() );
|
|
|
|
if ( dwRequiredLen == 0 )
|
|
{
|
|
// Failure
|
|
return FALSE;
|
|
}
|
|
|
|
if ( dwRequiredLen <= strResult.QuerySize() )
|
|
{
|
|
// This indicates success, since the size of the result is
|
|
// smaller or equal to the size of the buffer we gave it
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !strResult.Resize( dwRequiredLen ) )
|
|
{
|
|
// Could not resize to required size
|
|
return FALSE;
|
|
}
|
|
|
|
// Try to expand the strings
|
|
dwRequiredLen = ExpandEnvironmentStrings( QueryStr(),
|
|
strResult.QueryStr(),
|
|
strResult.QuerySize() );
|
|
|
|
|
|
if ( dwRequiredLen == 0 )
|
|
{
|
|
// Failure
|
|
return FALSE;
|
|
}
|
|
|
|
if ( strResult.QuerySize() < dwRequiredLen )
|
|
{
|
|
// We should not fail the second time because the buffer is too small
|
|
ASSERT( !(strResult.QuerySize() < dwRequiredLen) );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Copy into our bufffer now
|
|
return Copy( strResult );
|
|
}
|
|
|
|
// PathAppend
|
|
//
|
|
// Appends a Path to the current Path
|
|
//
|
|
BOOL
|
|
TSTR_PATH::PathAppend(LPCTSTR szSource)
|
|
{
|
|
DWORD dwLen = QueryLen();
|
|
BOOL bRet = TRUE;
|
|
|
|
if ( ( dwLen != 0 ) &&
|
|
( *( QueryStr() + dwLen - 1 ) != _T('\\') )
|
|
)
|
|
{
|
|
bRet = Append( _T("\\") );
|
|
}
|
|
|
|
if ( bRet )
|
|
{
|
|
if ( *szSource == '\\' )
|
|
{
|
|
// If it starts with a \, then move it up one char
|
|
szSource++;
|
|
}
|
|
|
|
bRet = Append( szSource );
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// PathAppend
|
|
//
|
|
// Appends a Path to the current path
|
|
//
|
|
BOOL
|
|
TSTR_PATH::PathAppend(TSTR &strSource)
|
|
{
|
|
return PathAppend( strSource.QueryStr() );
|
|
}
|
|
|
|
// RetrieveSystemDir
|
|
//
|
|
// Retrieve the System Directory (%windir%\system32), and put it into
|
|
// out string
|
|
//
|
|
BOOL
|
|
TSTR_PATH::RetrieveSystemDir()
|
|
{
|
|
DWORD dwLength;
|
|
|
|
if ( !Resize( MAX_PATH ) )
|
|
{
|
|
// Failed to resize
|
|
return FALSE;
|
|
}
|
|
|
|
dwLength = GetSystemDirectory( QueryStr(), QuerySize() );
|
|
|
|
if ( ( dwLength == 0 ) ||
|
|
( dwLength > QuerySize() ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// RetrieveWindowsDir
|
|
//
|
|
// Retrieve the Windows Directory and put it into our string
|
|
//
|
|
BOOL
|
|
TSTR_PATH::RetrieveWindowsDir()
|
|
{
|
|
DWORD dwLength;
|
|
|
|
if ( !Resize( MAX_PATH ) )
|
|
{
|
|
// Failed to resize
|
|
return FALSE;
|
|
}
|
|
|
|
dwLength = GetSystemWindowsDirectory( QueryStr(), QuerySize() );
|
|
|
|
if ( ( dwLength == 0 ) ||
|
|
( dwLength > QuerySize() ) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// RemoveTrailingPath
|
|
//
|
|
// Remove the path trailing at the end of this path
|
|
// ie. c:\foo\test -> c:\foo
|
|
// c:\foo\test\bar.asp -> c:\foo\test
|
|
// c:\foo.asp -> c:\
|
|
// c:\ -> c:\
|
|
//
|
|
// We succeeded no matter what, the return value just signifies
|
|
// if something was removed
|
|
//
|
|
// Return Values
|
|
// TRUE - The trailing path was removed
|
|
// FALSE - There was not trailing path to remove
|
|
//
|
|
BOOL
|
|
TSTR_PATH::RemoveTrailingPath()
|
|
{
|
|
LPTSTR szFirstSlash;
|
|
LPTSTR szLastSlash;
|
|
|
|
if ( QueryLen() <= 3 )
|
|
{
|
|
// Not long enough to be a real path
|
|
return FALSE;
|
|
}
|
|
|
|
szFirstSlash = _tcschr( QueryStr() + 2, _T('\\') ); // Move path c:\ or \\
|
|
szLastSlash = _tcsrchr( QueryStr() - 1, _T('\\') ); // Move back one
|
|
|
|
if ( ( szFirstSlash == NULL ) ||
|
|
( szLastSlash == NULL ) ||
|
|
( szFirstSlash == szLastSlash ) ||
|
|
( szFirstSlash > szLastSlash ) )
|
|
{
|
|
// No path to remove
|
|
return FALSE;
|
|
}
|
|
|
|
// Terminate string at last slash
|
|
*szLastSlash = _T('\0');
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// function: Construtor
|
|
//
|
|
// Initialize the list with nothing in it.
|
|
//
|
|
TSTR_MSZ::TSTR_MSZ()
|
|
{
|
|
BOOL bRet;
|
|
|
|
// We are pretty much assuming this will never fail, because
|
|
// 1) We can't notify anyone if it does
|
|
// 2) The implimentation of the buffer class, has static space, so this won't fail
|
|
bRet = Empty();
|
|
|
|
ASSERT( bRet );
|
|
}
|
|
|
|
// function: Empty
|
|
//
|
|
// Empty the list, so there is nothing in it
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::Empty()
|
|
{
|
|
if ( !Resize( 1 ) ) // The size of '\0'
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return Copy( _T("") );
|
|
}
|
|
|
|
// function: Resize
|
|
//
|
|
// Resize the buffer so it is big enough for the multisz
|
|
//
|
|
// Parameters:
|
|
// dwChars - The number of characters that you need (this includes all
|
|
// the null terminators)
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::Resize(DWORD dwChars)
|
|
{
|
|
return m_buff.Resize( dwChars * sizeof(TCHAR) );
|
|
}
|
|
|
|
// function: QueryMultiSz
|
|
//
|
|
// Return a pointer to the multisz
|
|
//
|
|
LPTSTR
|
|
TSTR_MSZ::QueryMultiSz()
|
|
{
|
|
return (LPTSTR) m_buff.QueryPtr();
|
|
}
|
|
|
|
// function: FindNextString
|
|
//
|
|
// Given the pointer to the begining of a string in a multisz,
|
|
// return a pointer to the begining of the next string
|
|
//
|
|
// Note: This should not be called on the terminating '\0' of the multisz
|
|
//
|
|
// Paramters:
|
|
// FindNextString - A pointer to the begining of a string in a mutlisz
|
|
// (not at the terminating multisz)
|
|
//
|
|
// Return Values
|
|
// pointer to the next string
|
|
// if the value at the returned string is '\0', then it is the end of the
|
|
// multisz
|
|
//
|
|
LPTSTR
|
|
TSTR_MSZ::FindNextString(LPTSTR szCurrentString)
|
|
{
|
|
// Make sure that this is not the terminating '\0' of the multisz,
|
|
// (signified by a '\0' is the [0] position of the string)
|
|
if ( *szCurrentString == _T('\0') )
|
|
{
|
|
// This should never be called on the last terminator in a multisz,
|
|
ASSERT( FALSE );
|
|
return szCurrentString;
|
|
}
|
|
|
|
while ( *szCurrentString != _T('\0') )
|
|
{
|
|
szCurrentString++;
|
|
}
|
|
|
|
// Move to the first character of the next string
|
|
szCurrentString++;
|
|
|
|
return szCurrentString;
|
|
}
|
|
|
|
// function: Find
|
|
//
|
|
// Check to see if 'szSource' string is in the multisz
|
|
//
|
|
// Parameters:
|
|
// szSource - The string to find a match for
|
|
// bCaseSensitive - Is the search case sensitive or not (default==not)
|
|
//
|
|
LPTSTR
|
|
TSTR_MSZ::Find(LPTSTR szSource, BOOL bCaseSensitive )
|
|
{
|
|
LPTSTR szCurrent = QueryMultiSz();
|
|
|
|
while ( *szCurrent != '\0' )
|
|
{
|
|
if ( ( bCaseSensitive &&
|
|
( _tcscmp( szCurrent, szSource ) == 0 )
|
|
) ||
|
|
( !bCaseSensitive &&
|
|
( _tcsicmp( szCurrent, szSource ) == 0 )
|
|
)
|
|
)
|
|
{
|
|
return szCurrent;
|
|
}
|
|
|
|
// Move to next string
|
|
szCurrent = FindNextString( szCurrent );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// function: IsPresent
|
|
//
|
|
// Checks to see if a string is present.
|
|
// Same functionality as Find, but returns BOOL
|
|
//
|
|
// Parameters:
|
|
// szSource - The string to find a match for
|
|
// bCaseSensitive - Is the search case sensitive or not (default==not)
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::IsPresent(LPTSTR szSource, BOOL bCaseSensitive )
|
|
{
|
|
return ( Find( szSource, bCaseSensitive ) != NULL );
|
|
}
|
|
|
|
// function: QueryString
|
|
//
|
|
// Retrieve a string from inside the multisz
|
|
//
|
|
// Parameters:
|
|
// dwIndex - The index of the string you want to retrieve ( 0 based, of course )
|
|
//
|
|
// Return Values:
|
|
// NULL - It is not found (ie. there aren't that many string in the multisz)
|
|
// pointer - The string you are looking for
|
|
LPTSTR
|
|
TSTR_MSZ::QueryString( DWORD dwIndex )
|
|
{
|
|
LPTSTR szCurrent = QueryMultiSz();
|
|
|
|
while ( ( *szCurrent != _T('\0') ) &&
|
|
( dwIndex != 0 )
|
|
)
|
|
{
|
|
// Move to next string
|
|
szCurrent = FindNextString( szCurrent );
|
|
dwIndex--;
|
|
}
|
|
|
|
if ( dwIndex != 0 )
|
|
{
|
|
// We prematurely exited, so there must not be enough strings in
|
|
// the multisz
|
|
return NULL;
|
|
}
|
|
|
|
return szCurrent;
|
|
}
|
|
|
|
// function: FindEnd
|
|
//
|
|
// Find the end of the multisz
|
|
// This returns a pointer to the last '\0' of the multisz
|
|
//
|
|
LPTSTR
|
|
TSTR_MSZ::FindEnd(LPTSTR szCurrentString)
|
|
{
|
|
while ( *szCurrentString != _T('\0') )
|
|
{
|
|
// Move to next string
|
|
szCurrentString = FindNextString( szCurrentString );
|
|
}
|
|
|
|
return szCurrentString;
|
|
}
|
|
|
|
// function: Add
|
|
//
|
|
// Add a string to the end of the list
|
|
//
|
|
// Parameters:
|
|
// szSource - The string to add to the multisz
|
|
//
|
|
// Return Values
|
|
// TRUE - Successfully added
|
|
// FALSE - Failure to add
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::Add(LPCTSTR szSource)
|
|
{
|
|
LPTSTR szCurrent;
|
|
|
|
ASSERT( *szSource != _T('\0') ); // Assert that it is not an empty string
|
|
|
|
if ( !Resize( QueryLen() + _tcslen( szSource ) + 1 ) )
|
|
{
|
|
// We could not expand the string large enough, so fail
|
|
return FALSE;
|
|
}
|
|
|
|
szCurrent = FindEnd( QueryMultiSz() );
|
|
|
|
// Copy New String in
|
|
_tcscpy( szCurrent, szSource );
|
|
|
|
// Null terminate the multisz
|
|
*( szCurrent + _tcslen(szSource) + 1 ) = _T('\0');
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// function: Remove
|
|
//
|
|
// Removes a string from a multisz
|
|
//
|
|
// Parameters:
|
|
// szSource - The string you want to remove
|
|
// bCaseSensitive - Is the check going to be case sensitive on if it exists
|
|
//
|
|
// Return Values
|
|
// TRUE - Successfully removed
|
|
// FALSE - Was not found, so it was not removed
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::Remove(LPTSTR szSource, BOOL bCaseSensitive )
|
|
{
|
|
LPTSTR szRemoveString = Find( szSource, bCaseSensitive );
|
|
LPTSTR szEndofMultiSz;
|
|
LPTSTR szNextString;
|
|
|
|
if ( szRemoveString == NULL )
|
|
{
|
|
// String was not found
|
|
return FALSE;
|
|
}
|
|
|
|
szEndofMultiSz = FindEnd( szRemoveString );
|
|
szNextString = FindNextString( szRemoveString );
|
|
|
|
// Assert just to make sure that the next string is before the end
|
|
ASSERT( szNextString <= szEndofMultiSz );
|
|
|
|
// Squish the Multisz to remove the string
|
|
// Use memmove, since the strings overlap
|
|
memmove( szRemoveString,
|
|
szNextString,
|
|
( szEndofMultiSz - szNextString + 1 ) * sizeof (TCHAR) );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// function: Copy
|
|
//
|
|
// Copy the multisz that is being send in, into our multisz,
|
|
// deleting anything that was there before
|
|
//
|
|
// Parameters:
|
|
// szSource - The multisz to copy
|
|
//
|
|
BOOL
|
|
TSTR_MSZ::Copy(LPTSTR szSource)
|
|
{
|
|
if ( !Resize( (DWORD) ( FindEnd(szSource) - szSource + 1 ) ) )
|
|
{
|
|
// We could not resize to copy the string
|
|
return FALSE;
|
|
}
|
|
|
|
LPTSTR szDestination = QueryMultiSz();
|
|
DWORD dwLen;
|
|
|
|
while ( *szSource )
|
|
{
|
|
// Calc length
|
|
dwLen = _tcslen( szSource ) + 1;
|
|
|
|
// Copy string
|
|
_tcscpy( szDestination, szSource );
|
|
|
|
// Move to next string locations
|
|
szSource += dwLen;
|
|
szDestination += dwLen;
|
|
}
|
|
|
|
// Null terminate Multisz
|
|
*szDestination = _T('\0');
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// function: QueryLen
|
|
//
|
|
// Query the length of the multisz (in chars)
|
|
// (including the terminating NULL)
|
|
//
|
|
DWORD
|
|
TSTR_MSZ::QueryLen()
|
|
{
|
|
return ( (DWORD) ( FindEnd( QueryMultiSz() ) - QueryMultiSz() + 1 ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|