/*++ Copyright (c) 2000 Microsoft Corporation Module Name: SxsPath.h Abstract: popular cousin of "String.h" and "Wheel.h" Author: Jay Krell (a-JayK) April 2000 Revision History: --*/ #pragma once #include "FusionString.h" /* See also If you find yourself scanning strings with for loops, stop, and learn to use: FusionString.h StringReverseSpan StringReverseComplementSpan / wcschr wcsrchr wcsspn wcscspn wcsstr StringBuffer.h Member functions with "Path" in their name. */ /*----------------------------------------------------------------------------- Split a path into root, path, base, extension the full path is roughly root + "\\" + path + "\\" + base + "." + extension but path, base, and extension can be empty (but not all three) and root might end in a slash, so you shouldn't do that blindly. Forward and backward slashes are accepted. Runs of slashes are accepted and mean the same thing as individual slashes (a run is sort of special case at the start to indicate UNC, but other than the call out to ntdll.dll, that doesn't effect the logic here) This class has no path length limitations (ntdll.dll?) The output of this class is its public member data. -----------------------------------------------------------------------------*/ template class CFullPathSplitPointersTemplate { public: CFullPathSplitPointersTemplate(); BOOL Initialize(PCWSTR full) { return GenericInitialize(full); } BOOL Initialize(PWSTR full) { return GenericInitialize(full); } protected: BOOL GenericInitialize(PCWSTRorPWSTR full); public: BOOL IsBaseEmpty() const; BOOL IsBaseEqual(const CFullPathSplitPointersTemplate&) const; // // This public data is the output of this class. // none of these have trailing slashes except possibly "c:\" // all "end" values are "one past the end", STL style // extension doesn't include the dot, but you can be paranoid // and check for that (it'll be NULL or point to nul if there's no extension) // path must be "well formed", full, Win32 drive letter or UNC, // and not end in a slash, otherwise Initialize will return false // // These are not in general nul terminated. // m_extensionEnd usually does point to a nul. // // Generally, you yourself put stick a nul over m_*end, // because generally none of the pieces overlap, but a // root of c:\ is a common exception. CSetupCopyQueuePathParameters // sticks in nuls, and allows for this exception. // // The length of any element is m_*end - m_*, as is the STL iterator way. // PCWSTRorPWSTR m_root; // never NULL or "" PCWSTRorPWSTR m_rootEnd; // never == m_root PCWSTRorPWSTR m_path; // NULL or "" in case of c:\foo.txt PCWSTRorPWSTR m_pathEnd; // == m_path in case of c:\foo.txt PCWSTRorPWSTR m_base; // NULL or "" in case of c:\.foo PCWSTRorPWSTR m_baseEnd; // == m_base in case of c:\.foo PCWSTRorPWSTR m_extension; // NULL or "" in case c:\foo PCWSTRorPWSTR m_extensionEnd; // == fullEnd if there is no extension // if the file has a base, this points to it // followed by dot and extension if it has one // if the file has no base, this points to the extension, including the dot // this is always nul terminated // this is never null, and shouldn't be empty either PCWSTRorPWSTR m_name; }; typedef CFullPathSplitPointersTemplate<> CFullPathSplitPointers; typedef CFullPathSplitPointersTemplate CMutableFullPathSplitPointers; /*----------------------------------------------------------------------------- Building on CFullPathSplitPointers, take two strings and split them up exactly as SetupCopyQueue wants them, into source root, root path, source name (base + extension) destination directory (root + path), destination name (base + extension) The output of this class is its public member data. -----------------------------------------------------------------------------*/ class CSetupCopyQueuePathParameters { public: CSetupCopyQueuePathParameters(); BOOL Initialize( PCWSTR pszSource, PCWSTR pszDestination ); // rather than copy each substring into its own buffer, put nuls // in place, over the delimiting slashes (and dot), BUT watch out // for c:\foo, so if the root is three characters, copy it // into a seperate buffer. public: // // These are null terminated. // PCWSTR m_sourceRoot; PCWSTR m_sourcePath; PCWSTR m_sourceName; // base and extension PCWSTR m_destinationDirectory; // root and path PCWSTR m_destinationName; // base and extension protected: WCHAR m_sourceRootStorage[4]; CStringBuffer m_sourceBuffer; CMutableFullPathSplitPointers m_sourceSplit; WCHAR m_destinationDirectoryStorage[4]; // root and path, if path is empty CStringBuffer m_destinationBuffer; CMutableFullPathSplitPointers m_destinationSplit; private: CSetupCopyQueuePathParameters(const CSetupCopyQueuePathParameters &); void operator =(const CSetupCopyQueuePathParameters &); }; /*----------------------------------------------------------------------------- inline implementation of CFullPathSplitPointersTemplate, because it is a template -----------------------------------------------------------------------------*/ template inline CFullPathSplitPointersTemplate::CFullPathSplitPointersTemplate() : m_root(NULL), m_rootEnd(NULL), m_path(NULL), m_pathEnd(NULL), m_base(NULL), m_baseEnd(NULL), m_extension(NULL), m_extensionEnd(NULL), m_name(L"") { } template inline BOOL CFullPathSplitPointersTemplate::GenericInitialize(T full) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); RTL_PATH_TYPE pathType; pathType = SxspDetermineDosPathNameType(full); PARAMETER_CHECK( (pathType == RtlPathTypeUncAbsolute) || (pathType == RtlPathTypeLocalDevice) || (pathType == RtlPathTypeDriveAbsolute)); T fullEnd; fullEnd = full + StringLength(full); m_root = full; if (m_root[1] == ':') { m_path = m_root + 3; m_rootEnd = m_path; m_path += wcsspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip path separators } else { m_path = m_root; m_path += wcsspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip "\\" m_path += wcscspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip "\\computer" m_path += wcsspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip "\\computer\" m_path += wcscspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip "\\computer\share" m_rootEnd = m_path; m_path += wcsspn(m_path, CUnicodeCharTraits::PathSeparators()); // skip "\\computer\share\" } // // now work from the right // first find the last dot and last slash, and determine // where the base and extension are, if any // INT nameExtLength; INT extLength; nameExtLength = ::StringReverseComplementSpan(m_path, fullEnd, CUnicodeCharTraits::PathSeparators()); extLength = ::StringReverseComplementSpan(m_path, fullEnd, L"."); // // determine the extension // if (extLength > nameExtLength) { // no extension on leaf, but one on a parent, slightly unusual // c:\foo.bar\abc m_extension = NULL; m_extensionEnd = NULL; } else { // c:\foo\abc.txt or c:\foo.bar\abc.txt (or c:\foo\.txt or some others) m_extension = fullEnd - extLength; m_extensionEnd = fullEnd; } // // determine the base // if (extLength + 1 == nameExtLength) { // unusual case, extension but no base // c:\.foo or c:\abc\.foo m_base = NULL; m_baseEnd = NULL; } else { m_base = fullEnd - nameExtLength; if (m_extension != NULL) { // normal case, base.extension m_baseEnd = m_extension - 1; } else { // no extension m_baseEnd = fullEnd; } } // // determine the path // if (m_base != NULL) { if (m_path == m_base) { // no path c:\foo.txt m_path = NULL; m_pathEnd = NULL; } else { // normal case of path ends at base c:\abc\def.txt m_pathEnd = m_base - 1; } } else if (m_extension != NULL) { // no base, but an extension // c:\.txt or c:\abc\.txt if (m_path + 1 == m_extension) { // no path c:\.txt m_path = NULL; m_pathEnd = NULL; } else { // path ends at extension c:\abc\.txt m_pathEnd = m_extension - 2; } } else { // no path and no extension // this probably happens when we have a terminal slash // we've already filtered out empty strings m_pathEnd = fullEnd - ::StringReverseSpan(m_path, fullEnd, CUnicodeCharTraits::PathSeparators()); } // there is always a root (paths are always full) ASSERT(m_root != NULL && m_rootEnd != NULL); // there is always a base or an extension (or both) ASSERT(m_base != NULL || m_extension != NULL); // if there's a start, there's an end ASSERT((m_base != NULL) == (m_baseEnd != NULL)); ASSERT((m_extension != NULL) == (m_extensionEnd != NULL)); m_name = (m_base != NULL) ? m_base : (m_extension - 1); fSuccess = TRUE; Exit: return fSuccess; } template inline BOOL CFullPathSplitPointersTemplate::IsBaseEmpty( ) const { return (m_base == m_baseEnd); } template inline BOOL CFullPathSplitPointersTemplate::IsBaseEqual( const CFullPathSplitPointersTemplate& other ) const { BOOL fEqual = FALSE; const INT length1 = static_cast(m_baseEnd - m_base); const INT length2 = static_cast(other.m_baseEnd - other.m_base); if (length1 != length2) goto Exit; fEqual = (FusionpCompareStrings(m_base, length1, other.m_base, length1, true) == 0); Exit: return fEqual; } /*----------------------------------------------------------------------------- inline implementation of CSetupCopyQueuePathParameters -----------------------------------------------------------------------------*/ inline CSetupCopyQueuePathParameters::CSetupCopyQueuePathParameters() : m_sourcePath(NULL), m_sourceName(NULL), m_destinationDirectory(NULL), m_destinationName(NULL) { m_sourceRootStorage[0] = 0; m_destinationDirectoryStorage[0] = 0; }