// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. #include "precomp.h" #include "multisza.h" #include // // Private Definitions // #define MAXULONG 4294967295 #define ISWHITE( ch ) ((ch) == L' ' || (ch) == L'\t' || (ch) == L'\r') // // When appending data, this is the extra amount we request to avoid // reallocations // #define STR_SLOP 128 DWORD MULTISZA::CalcLength( const CHAR * str, LPDWORD pcStrings ) { DWORD count = 0; DWORD total = 1; DWORD len; while( *str ) { len = ::strlen( str ) + 1; total += len; str += len; count++; } if( pcStrings != NULL ) { *pcStrings = count; } return total; } // MULTISZA::CalcLength BOOL MULTISZA::FindString( const CHAR * str ) { CHAR * multisz; // // Sanity check. // DBG_ASSERT( QueryStr() != NULL ); DBG_ASSERT( str != NULL ); DBG_ASSERT( *str != '\0' ); // // Scan it. // multisz = QueryStr(); while( *multisz != '\0' ) { if( !::strcmp( multisz, str ) ) { return TRUE; } multisz += ::strlen( multisz ) + 1; } return FALSE; } // MULTISZA::FindString BOOL MULTISZA::FindStringNoCase( const CHAR * str ) { CHAR * multisz; // // Sanity check. // DBG_ASSERT( QueryStr() != NULL ); DBG_ASSERT( str != NULL ); DBG_ASSERT( *str != '\0' ); // // Scan it. // multisz = QueryStr(); while( *multisz != '\0' ) { if( !_stricmp( multisz, str ) ) { return TRUE; } multisz += strlen( multisz ) + 1; } return FALSE; } // MULTISZA::FindStringNoCase VOID MULTISZA::AuxInit( const CHAR * pInit ) { BOOL fRet; if ( pInit ) { DWORD cStrings; int cbCopy = CalcLength( pInit, &cStrings ) * sizeof(CHAR); fRet = Resize( cbCopy ); if ( fRet ) { CopyMemory( QueryPtr(), pInit, cbCopy ); m_cchLen = (cbCopy)/sizeof(CHAR); m_cStrings = cStrings; } else { // BUFFER::SetValid( FALSE); } } else { Reset(); } } // MULTISZA::AuxInit() /******************************************************************* NAME: MULTISZA::AuxAppend SYNOPSIS: Appends the string onto the MULTISZA. ENTRY: Object to append ********************************************************************/ BOOL MULTISZA::AuxAppend( const CHAR * pStr, UINT cbStr, BOOL fAddSlop ) { DBG_ASSERT( pStr != NULL ); UINT cbThis = QueryCB(); if( cbThis == 2 ) { // // It's empty, so start at the beginning. // cbThis = 0; } else { // // It's not empty, so back up over the final terminating NULL. // cbThis -= sizeof(CHAR); } // // 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 // //AcIncrement( CacMultiszAppend); // // Check for the arithmetic overflow // // ( 2 * sizeof( CHAR ) ) is for the double terminator // ULONGLONG cb64Required = (ULONGLONG)cbThis + cbStr + 2 * sizeof(CHAR); if ( cb64Required > MAXULONG ) { SetLastError( ERROR_ARITHMETIC_OVERFLOW ); return FALSE; } if ( QuerySize() < (DWORD) cb64Required ) { ULONGLONG cb64AllocSize = cb64Required + (fAddSlop ? STR_SLOP : 0 ); // // Check for the arithmetic overflow // if ( cb64AllocSize > MAXULONG ) { SetLastError( ERROR_ARITHMETIC_OVERFLOW ); return FALSE; } if ( !Resize( (DWORD) cb64AllocSize ) ) return FALSE; } // copy the exact string and tack on the double terminator memcpy( (BYTE *) QueryPtr() + cbThis, pStr, cbStr); *(CHAR *)((BYTE *)QueryPtr() + cbThis + cbStr) = L'\0'; *(CHAR *)((BYTE *)QueryPtr() + cbThis + cbStr + sizeof(CHAR) ) = L'\0'; m_cchLen = CalcLength( (const CHAR *)QueryPtr(), &m_cStrings ); return TRUE; } // MULTISZA::AuxAppend() BOOL MULTISZA::CopyToBuffer( __out_ecount_opt(*lpcch) 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 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); } register DWORD cch = QueryCCH(); if ( *lpcch >= cch) { DBG_ASSERT( lpszBuffer); memcpy( lpszBuffer, QueryStr(), cch * sizeof(CHAR)); } else { DBG_ASSERT( *lpcch < cch); SetLastError( ERROR_INSUFFICIENT_BUFFER); fReturn = FALSE; } *lpcch = cch; return ( fReturn); } // MULTISZA::CopyToBuffer() BOOL MULTISZA::Equals( MULTISZA* pmszRhs ) // // Compares this to pmszRhs, returns TRUE if equal // { DBG_ASSERT( NULL != pmszRhs ); PCSTR pszLhs = First( ); PCSTR pszRhs = pmszRhs->First( ); if( m_cStrings != pmszRhs->m_cStrings ) { return FALSE; } while( NULL != pszLhs ) { DBG_ASSERT( NULL != pszRhs ); if( 0 != strcmp( pszLhs, pszRhs ) ) { return FALSE; } pszLhs = Next( pszLhs ); pszRhs = pmszRhs->Next( pszRhs ); } return TRUE; } HRESULT SplitCommaDelimitedString( PCSTR pszList, BOOL fTrimEntries, BOOL fRemoveEmptyEntries, MULTISZA * pmszList ) /*++ Routine Description: Split comma delimited string into a MULTISZA. Additional leading empty entries after the first are discarded. Arguments: pszList - List to split up fTrimEntries - Whether each entry should be trimmed before added to MULTISZA fRemoveEmptyEntries - Whether empty entires should be discarded pmszList - Filled with MULTISZA list Return Value: HRESULT --*/ { HRESULT hr = S_OK; if ( pszList == NULL || pmszList == NULL ) { DBG_ASSERT( FALSE ); hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); goto Finished; } pmszList->Reset(); /* pszCurrent: start of the current entry which may be the comma that precedes the next entry if the entry is empty pszNext: the comma that precedes the next entry. If pszCurrent == pszNext, then the entry is empty pszEnd: just past the end of the current entry */ for ( PCSTR pszCurrent = pszList, pszNext = strchr( pszCurrent, L',' ) ; ; pszCurrent = pszNext + 1, pszNext = strchr( pszCurrent, L',' ) ) { PCSTR pszEnd = NULL; if ( pszNext != NULL ) { pszEnd = pszNext; } else { pszEnd = pszCurrent + strlen( pszCurrent ); } if ( fTrimEntries ) { while ( pszCurrent < pszEnd && ISWHITE( pszCurrent[ 0 ] ) ) { pszCurrent++; } while ( pszEnd > pszCurrent && ISWHITE( pszEnd[ -1 ] ) ) { pszEnd--; } } if ( pszCurrent != pszEnd || !fRemoveEmptyEntries ) { if ( !pmszList->Append( pszCurrent, (DWORD) ( pszEnd - pszCurrent ) ) ) { hr = HRESULT_FROM_WIN32( GetLastError() ); goto Finished; } } if ( pszNext == NULL ) { break; } } Finished: return hr; }