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.
744 lines
23 KiB
744 lines
23 KiB
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1996-2002 Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
// QuorumUtils.cpp
|
|
//
|
|
// Description:
|
|
// Utility functions for retrieving the root path from a cluster.
|
|
//
|
|
// Maintained By:
|
|
// George Potts (GPotts) 22-OCT-2001
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <windows.h>
|
|
#include <StrSafe.h>
|
|
#include "QuorumUtils.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// SplitRootPath
|
|
//
|
|
// Routine Description:
|
|
// Take the current quorum path (from GetClusterQuorumResource) and compare
|
|
// it to the device names returned from the resource. From this take the
|
|
// additional path from the quorum path and set that as our root path.
|
|
//
|
|
// It is expected that the IN buffers are at least of size _MAX_PATH.
|
|
//
|
|
// Arguments:
|
|
// hClusterIn Handle to the cluster.
|
|
//
|
|
// pszPartitionNameOut Partition name buffer to fill.
|
|
//
|
|
// pcchPartitionIn Max char count of buffer.
|
|
//
|
|
// pszRootPathOut Root path buffer to fill.
|
|
//
|
|
// pcchRootPathIn Max char count of buffer.
|
|
//
|
|
// Return Value:
|
|
// ERROR_SUCCESS on success.
|
|
//
|
|
// ERROR_MORE_DATA
|
|
// pcchPartitionInout and pcchRootPathInout will contain the
|
|
// minimum sizes needed for the buffers.
|
|
//
|
|
// Win32 Error code on failure.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD SplitRootPath(
|
|
HCLUSTER hClusterIn
|
|
, WCHAR * pszPartitionNameOut
|
|
, DWORD * pcchPartitionInout
|
|
, WCHAR * pszRootPathOut
|
|
, DWORD * pcchRootPathInout
|
|
)
|
|
{
|
|
HRESOURCE hQuorumResource = NULL;
|
|
WCHAR * pszResourceName = NULL;
|
|
WCHAR * pszQuorumPath = NULL;
|
|
WCHAR * pszDeviceTemp = NULL;
|
|
WCHAR * pszTemp = NULL;
|
|
CLUSPROP_BUFFER_HELPER buf;
|
|
DWORD cbData;
|
|
DWORD cchDeviceName;
|
|
WCHAR * pszDevice;
|
|
DWORD dwVal;
|
|
DWORD sc;
|
|
PVOID pbDiskInfo = NULL;
|
|
DWORD cbDiskInfo = 0;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
if ( hClusterIn == NULL ||
|
|
pszPartitionNameOut == NULL || pcchPartitionInout == NULL ||
|
|
pszRootPathOut == NULL || pcchRootPathInout == NULL )
|
|
{
|
|
sc = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the info about the quorum resource.
|
|
//
|
|
sc = WrapGetClusterQuorumResource( hClusterIn, &pszResourceName, &pszQuorumPath, NULL );
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open a handle to the quorum resource to interrogate it.
|
|
//
|
|
hQuorumResource = OpenClusterResource( hClusterIn, pszResourceName );
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the disk info from the resource.
|
|
//
|
|
sc = ScWrapClusterResourceControlGet(
|
|
hQuorumResource
|
|
, NULL
|
|
, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO
|
|
, NULL
|
|
, 0
|
|
, &pbDiskInfo
|
|
, &cbDiskInfo
|
|
);
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Cycle through the buffer looking for the first partition.
|
|
//
|
|
buf.pb = (BYTE*)pbDiskInfo;
|
|
while (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
|
|
{
|
|
// Calculate the size of the value.
|
|
cbData = sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
|
|
|
|
// Parse the value.
|
|
if (buf.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO)
|
|
{
|
|
//
|
|
// A resource may have multiple partitions defined - make sure that ours matches the quorum path.
|
|
// For any partition that is an SMB share we have to be careful - the quorum path may differ from the device name
|
|
// by the first 8 characters - "\\" vs. "\\?\UNC\". If it's an SMB path do special parsing, otherwise compare
|
|
// the beginning of the quorum path against the full device name. The reason for this is
|
|
// that SetClusterQuorumResource will convert any given SMB path to a UNC path.
|
|
//
|
|
|
|
// Make it easier to follow.
|
|
pszDevice = buf.pPartitionInfoValue->szDeviceName;
|
|
|
|
if ( (wcslen( pszDevice ) >= 2) && (ClRtlStrNICmp( L"\\\\", pszDevice, 2 ) == 0 ) )
|
|
{
|
|
//
|
|
// We found an SMB/UNC match.
|
|
//
|
|
|
|
//
|
|
// SMB and UNC paths always lead off with two leading backslashes - remove these from the
|
|
// partition name since a compare of "\\<part>" and "\\?\UNC\<part>" will never match.
|
|
// Instead, we'll just search for "<part>" in the quorum path.
|
|
//
|
|
|
|
// Allocate a new buffer to copy the trimmed code to.
|
|
// You can use the same buffer for both params of TrimLeft and TrimRight.
|
|
pszDeviceTemp = (WCHAR *) LocalAlloc( LMEM_ZEROINIT, ( wcslen( pszDevice ) + 1 ) * sizeof( WCHAR ) );
|
|
if ( pszDeviceTemp == NULL )
|
|
{
|
|
sc = ERROR_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// This will remove all leading backslashes.
|
|
dwVal = TrimLeft( pszDevice, L"\\", pszDeviceTemp );
|
|
|
|
// It may end with a \ - remove this if present.
|
|
dwVal = TrimRight( pszDeviceTemp, L"\\", pszDeviceTemp );
|
|
|
|
// Find out if pszDeviceTemp is a substring of pszQuorumPath.
|
|
pszTemp = wcsstr( pszQuorumPath, pszDeviceTemp );
|
|
if ( pszTemp != NULL )
|
|
{
|
|
// We found a match, now find the offset of the root path.
|
|
pszTemp += wcslen( pszDeviceTemp );
|
|
|
|
// Make sure our buffers are big enough.
|
|
if ( wcslen( pszDevice ) >= *pcchPartitionInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
}
|
|
|
|
if ( wcslen( pszTemp ) >= *pcchRootPathInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
}
|
|
|
|
*pcchPartitionInout = static_cast< DWORD >( wcslen( pszDevice ) + 1 );
|
|
*pcchRootPathInout = static_cast< DWORD >( wcslen( pszTemp ) + 1 );
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Copy the partition and NULL terminate it.
|
|
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszDevice );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
break;
|
|
}
|
|
|
|
// Copy the root path and NULL terminate it.
|
|
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, pszTemp );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
} // if: pszDeviceTemp is a substring of pszQuorumPath
|
|
} // if: SMB or UNC path
|
|
else if ( ClRtlStrNICmp( pszQuorumPath, pszDevice, wcslen( pszDevice )) == 0 )
|
|
{
|
|
// We found a non-SMB match match - pszDevice is a substring of pszQuorumPath.
|
|
cchDeviceName = static_cast< DWORD >( wcslen( pszDevice ) );
|
|
|
|
if ( cchDeviceName >= *pcchPartitionInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
}
|
|
|
|
if ( wcslen( &(pszQuorumPath[cchDeviceName]) ) >= *pcchRootPathInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
}
|
|
|
|
*pcchPartitionInout = cchDeviceName + 1;
|
|
*pcchRootPathInout = static_cast< DWORD >( wcslen( &(pszQuorumPath[cchDeviceName]) ) + 1 );
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszDevice );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
break;
|
|
}
|
|
|
|
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, &(pszQuorumPath[cchDeviceName]) );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
break;
|
|
}
|
|
break;
|
|
|
|
} // if: same partition
|
|
|
|
} // if: partition info
|
|
|
|
// Advance the buffer pointer
|
|
buf.pb += cbData;
|
|
} // while: more values
|
|
|
|
//
|
|
// Something failed - we weren't able to find a partition. Default to the quorum path
|
|
// and a single backslash.
|
|
//
|
|
if ( wcslen( pszPartitionNameOut ) == 0 )
|
|
{
|
|
hr = StringCchCopyW( pszPartitionNameOut, *pcchPartitionInout, pszQuorumPath );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( wcslen( pszRootPathOut ) == 0 )
|
|
{
|
|
hr = StringCchCopyW( pszRootPathOut, *pcchRootPathInout, L"\\" );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
LocalFree( pszResourceName );
|
|
LocalFree( pszQuorumPath );
|
|
LocalFree( pbDiskInfo );
|
|
|
|
if ( hQuorumResource != NULL )
|
|
{
|
|
CloseClusterResource( hQuorumResource );
|
|
}
|
|
|
|
return sc;
|
|
|
|
} // *** SplitRootPath()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// ConstructQuorumPath
|
|
//
|
|
// Routine Description:
|
|
// Construct a quorum path to pass to SetClusterQuorumResource given
|
|
// the parsed root path. This function enumerates the resources
|
|
// partitions and the first one that it finds it takes the device name
|
|
// and appends the rootpath to it.
|
|
//
|
|
// Arguments:
|
|
// hResourceIn Resource that is going to become the quorum.
|
|
//
|
|
// pszRootPathIn Root path to append to one of the resource's partitions.
|
|
//
|
|
// pszQuorumPathOut Buffer to receive the constructed quorum path.
|
|
//
|
|
// pcchQuorumPathInout Count of characters in pszQuorumPathOut.
|
|
//
|
|
//
|
|
// Return Value:
|
|
// ERROR_SUCCESS on success.
|
|
// The number of characters written (including NULL) is in
|
|
// pcchQuorumPathInout.
|
|
//
|
|
// ERROR_MORE_DATA
|
|
// pszQuorumPathOut is too small. The necessary buffer size
|
|
// in characters (including NULL) is in pcchQuorumPathInout.
|
|
//
|
|
// Win32 Error code on failure.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD ConstructQuorumPath(
|
|
HRESOURCE hResourceIn
|
|
, const WCHAR * pszRootPathIn
|
|
, WCHAR * pszQuorumPathOut
|
|
, DWORD * pcchQuorumPathInout
|
|
)
|
|
{
|
|
DWORD sc = ERROR_SUCCESS;
|
|
PVOID pbDiskInfo = NULL;
|
|
DWORD cbDiskInfo = 0;
|
|
DWORD cbData = 0;
|
|
WCHAR * pszDevice = NULL;
|
|
size_t cchNeeded = 0;
|
|
HRESULT hr;
|
|
CLUSPROP_BUFFER_HELPER buf;
|
|
|
|
//
|
|
// Check params.
|
|
//
|
|
if ( pszRootPathIn == NULL || pszQuorumPathOut == NULL || pcchQuorumPathInout == NULL )
|
|
{
|
|
sc = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the disk info from the resource.
|
|
//
|
|
sc = ScWrapClusterResourceControlGet(
|
|
hResourceIn
|
|
, NULL
|
|
, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO
|
|
, NULL
|
|
, 0
|
|
, &pbDiskInfo
|
|
, &cbDiskInfo
|
|
);
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
buf.pb = (BYTE*) pbDiskInfo;
|
|
while (buf.pSyntax->dw != CLUSPROP_SYNTAX_ENDMARK)
|
|
{
|
|
// Calculate the size of the value.
|
|
cbData = sizeof(*buf.pValue) + ALIGN_CLUSPROP(buf.pValue->cbLength);
|
|
|
|
//
|
|
// See if this property contains partition info. We grab the first partition.
|
|
//
|
|
if (buf.pSyntax->dw == CLUSPROP_SYNTAX_PARTITION_INFO)
|
|
{
|
|
pszDevice = buf.pPartitionInfoValue->szDeviceName;
|
|
|
|
//
|
|
// Calculate the size of the buffer that we need.
|
|
//
|
|
cchNeeded = wcslen( pszDevice ) + 1;
|
|
cchNeeded += wcslen( pszRootPathIn );
|
|
|
|
if ( pszDevice[ wcslen( pszDevice ) - 1 ] == L'\\' && pszRootPathIn[ 0 ] == L'\\' )
|
|
{
|
|
//
|
|
// We'd have two backslashes if we concatenated them. Prune one of them off.
|
|
//
|
|
|
|
// Decrement one for the removed backslash.
|
|
cchNeeded--;
|
|
|
|
if ( cchNeeded > *pcchQuorumPathInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Construct the path.
|
|
//
|
|
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%ws%ws", pszDevice, &pszRootPathIn[ 1 ] );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
goto Cleanup;
|
|
}
|
|
|
|
} // if: concatenating would introduce double backslashes
|
|
else if( pszDevice[ wcslen( pszDevice ) - 1 ] != L'\\' && pszRootPathIn[ 0 ] != L'\\' )
|
|
{
|
|
//
|
|
// We need to insert a backslash between the concatenated strings.
|
|
//
|
|
|
|
// Increment by one for the added backslash.
|
|
cchNeeded++;
|
|
|
|
if ( cchNeeded > *pcchQuorumPathInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Construct the path.
|
|
//
|
|
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%s\\%s", pszDevice, pszRootPathIn );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
goto Cleanup;
|
|
}
|
|
|
|
} // if: we need to introduce a backslash between the concatenation
|
|
else
|
|
{
|
|
//
|
|
// We're fine to just construct the path.
|
|
//
|
|
if ( cchNeeded > *pcchQuorumPathInout )
|
|
{
|
|
sc = ERROR_MORE_DATA;
|
|
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Construct the path.
|
|
//
|
|
hr = StringCchPrintfW( pszQuorumPathOut, *pcchQuorumPathInout, L"%s%s", pszDevice, pszRootPathIn );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
sc = HRESULT_CODE( hr );
|
|
goto Cleanup;
|
|
}
|
|
|
|
} // if: we can just concatenate the strings
|
|
|
|
//
|
|
// Return the number of bytes that we needed in the buffer.
|
|
//
|
|
*pcchQuorumPathInout = static_cast< DWORD >( cchNeeded );
|
|
|
|
break;
|
|
} // if: partition info
|
|
|
|
// Advance the buffer pointer
|
|
buf.pb += cbData;
|
|
|
|
} // while: more values
|
|
|
|
Cleanup:
|
|
LocalFree( pbDiskInfo );
|
|
|
|
return sc;
|
|
|
|
} //*** ConstructQuorumPath
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// TrimLeft
|
|
//
|
|
// Routine Description:
|
|
// Trim all leading whitespace as well as any leading characters specified.
|
|
//
|
|
// Arguments:
|
|
// pszTargetIn String to trim characters from.
|
|
//
|
|
// pszCharsIn List of characters to remove in addition to white space.
|
|
//
|
|
// pszTrimmedOut Target buffer in which the trimmed string will be
|
|
// placed. This may be the same buffer as pszTargetIn.
|
|
// This buffer is expected to be at least the size of
|
|
// pszTargetIn (in case no characters are removed).
|
|
//
|
|
// Return Value:
|
|
// The count of characters trimmed.
|
|
//
|
|
// -1. Call GetLastError for more information.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD TrimLeft(
|
|
const WCHAR * pszTargetIn
|
|
, const WCHAR * pszCharsIn
|
|
, WCHAR * pszTrimmedOut
|
|
)
|
|
{
|
|
const WCHAR * pszTargetPtr = pszTargetIn;
|
|
const WCHAR * pszTemp = NULL;
|
|
BOOL fContinue;
|
|
DWORD sc = ERROR_SUCCESS;
|
|
DWORD cchTrimmed = 0; // Number of characters trimmed.
|
|
|
|
if ( pszTargetIn == NULL || pszTrimmedOut == NULL )
|
|
{
|
|
sc = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Loop until we find non-whitespace or a char not in pszCharsIn or
|
|
// we've reached the end of the string.
|
|
//
|
|
fContinue = TRUE;
|
|
while ( *pszTargetPtr != L'\0' && fContinue == TRUE )
|
|
{
|
|
fContinue = FALSE;
|
|
|
|
//
|
|
// Is the character white space?
|
|
//
|
|
if ( 0 == iswspace( pszTargetPtr[0] ) )
|
|
{
|
|
//
|
|
// No, it's not. Does it match CharsIn?
|
|
//
|
|
for( pszTemp = pszCharsIn; pszTemp != NULL && *pszTemp != L'\0'; pszTemp++ )
|
|
{
|
|
if ( pszTargetPtr[ 0 ] == pszTemp[ 0 ] )
|
|
{
|
|
//
|
|
// We've got a match - trim it and loop on the next character.
|
|
//
|
|
fContinue = TRUE;
|
|
cchTrimmed++;
|
|
pszTargetPtr++;
|
|
break;
|
|
} // if:
|
|
} // for:
|
|
} // if:
|
|
else
|
|
{
|
|
//
|
|
// We've got some whitespace - trim it.
|
|
//
|
|
fContinue = TRUE;
|
|
cchTrimmed++;
|
|
pszTargetPtr++;
|
|
} // else:
|
|
} // while:
|
|
|
|
//
|
|
// Copy the truncated string to the pszTrimmedOut buffer.
|
|
// If we truncated everything from the string make sure
|
|
// we NULL terminate the string.
|
|
//
|
|
if ( wcslen( pszTargetPtr ) == 0 )
|
|
{
|
|
*pszTrimmedOut = L'\0';
|
|
|
|
}
|
|
else
|
|
{
|
|
// Use memmove because the caller may have passed in the same buffer for both variables.
|
|
memmove( pszTrimmedOut, pszTargetPtr, ( wcslen( pszTargetPtr ) + 1 ) * sizeof( WCHAR ) );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
cchTrimmed = static_cast< DWORD >( -1 );
|
|
}
|
|
SetLastError( sc );
|
|
return cchTrimmed;
|
|
|
|
} //*** TrimLeft
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//++
|
|
//
|
|
// TrimRight
|
|
//
|
|
// Routine Description:
|
|
// Trim all trailing whitespace as well as any trailing characters specified.
|
|
//
|
|
// Arguments:
|
|
// pszTargetIn String to trim characters from.
|
|
//
|
|
// pszCharsIn List of characters to remove in addition to white space.
|
|
//
|
|
// pszTrimmedOut Target buffer in which the trimmed string will be
|
|
// placed. This may be the same buffer as pszTargetIn.
|
|
// This buffer is expected to be at least the size of
|
|
// pszTargetIn (in case no characters are removed).
|
|
//
|
|
// Return Value:
|
|
// The count of characters trimmed.
|
|
//
|
|
// -1. Call GetLastError for more information.
|
|
//
|
|
//--
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
DWORD TrimRight(
|
|
const WCHAR * pszTargetIn
|
|
, const WCHAR * pszCharsIn
|
|
, WCHAR * pszTrimmedOut
|
|
)
|
|
{
|
|
const WCHAR * pszTargetPtr = pszTargetIn;
|
|
const WCHAR * pszTemp = NULL;
|
|
BOOL fContinue;
|
|
DWORD sc = ERROR_SUCCESS;
|
|
DWORD cchTrimmed = 0; // Number of characters trimmed.
|
|
size_t cchLen = 0;
|
|
|
|
if ( pszTargetIn == NULL || pszTrimmedOut == NULL )
|
|
{
|
|
sc = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
cchLen = wcslen( pszTargetIn );
|
|
|
|
if ( cchLen == 0 )
|
|
{
|
|
//
|
|
// We've got an empty string.
|
|
//
|
|
pszTargetPtr = pszTargetIn;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Point to the last character in the string.
|
|
//
|
|
pszTargetPtr = &(pszTargetIn[ cchLen - 1 ] );
|
|
}
|
|
|
|
//
|
|
// Loop until we find non-whitespace or a char not in pszCharsIn or
|
|
// we've reached the beginning of the string.
|
|
//
|
|
fContinue = TRUE;
|
|
while ( pszTargetPtr >= pszTargetIn && fContinue == TRUE )
|
|
{
|
|
fContinue = FALSE;
|
|
|
|
//
|
|
// Is the character white space?
|
|
//
|
|
if ( 0 == iswspace( pszTargetPtr[0] ) )
|
|
{
|
|
//
|
|
// No, it's not. Does it match CharsIn?
|
|
//
|
|
for( pszTemp = pszCharsIn; pszTemp != NULL && *pszTemp != L'\0'; pszTemp++ )
|
|
{
|
|
if ( pszTargetPtr[ 0 ] == pszTemp[ 0 ] )
|
|
{
|
|
//
|
|
// We've got a match - trim it and loop on the next character.
|
|
//
|
|
fContinue = TRUE;
|
|
cchTrimmed++;
|
|
pszTargetPtr--;
|
|
break;
|
|
} // if:
|
|
} // for:
|
|
} // if:
|
|
else
|
|
{
|
|
//
|
|
// We've got some whitespace - trim it.
|
|
//
|
|
fContinue = TRUE;
|
|
cchTrimmed++;
|
|
pszTargetPtr--;
|
|
} // else:
|
|
} // while:
|
|
|
|
//
|
|
// Copy the truncated string to the pszTrimmedOut buffer.
|
|
// If we truncated everything from the string make sure
|
|
// we NULL terminate the string.
|
|
//
|
|
if ( wcslen( pszTargetPtr ) == 0 )
|
|
{
|
|
*pszTrimmedOut = L'\0';
|
|
}
|
|
else
|
|
{
|
|
// Use memmove because they may have passed in the same buffer for both variables.
|
|
memmove( pszTrimmedOut, pszTargetIn, ( cchLen - cchTrimmed ) * sizeof( WCHAR ) );
|
|
pszTrimmedOut[ cchLen - cchTrimmed ] = L'\0';
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( sc != ERROR_SUCCESS )
|
|
{
|
|
cchTrimmed = static_cast< DWORD >( -1 );
|
|
}
|
|
SetLastError( sc );
|
|
|
|
return cchTrimmed;
|
|
|
|
} //*** TrimRight
|
|
|