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.
376 lines
11 KiB
376 lines
11 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sxspath.h
|
|
|
|
Abstract:
|
|
|
|
popular cousin of "String.h" and "Wheel.h"
|
|
|
|
Author:
|
|
|
|
Jay Krell (a-JayK, JayKrell) 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
|
|
|
|
<string.h> / <wchar.h>
|
|
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 <typename PCWSTRorPWSTR = PCWSTR>
|
|
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<PWSTR> 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 <typename T>
|
|
inline CFullPathSplitPointersTemplate<T>::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 <typename T>
|
|
inline BOOL
|
|
CFullPathSplitPointersTemplate<T>::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 <typename T>
|
|
inline BOOL
|
|
CFullPathSplitPointersTemplate<T>::IsBaseEmpty(
|
|
) const
|
|
{
|
|
return (m_base == m_baseEnd);
|
|
}
|
|
|
|
template <typename T>
|
|
inline BOOL
|
|
CFullPathSplitPointersTemplate<T>::IsBaseEqual(
|
|
const CFullPathSplitPointersTemplate& other
|
|
) const
|
|
{
|
|
BOOL fEqual = FALSE;
|
|
const INT length1 = static_cast<INT>(m_baseEnd - m_base);
|
|
const INT length2 = static_cast<INT>(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;
|
|
}
|