458 lines
10 KiB
C++
458 lines
10 KiB
C++
// Common/Wildcard.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "Wildcard.h"
|
|
|
|
bool g_CaseSensitive =
|
|
#ifdef _WIN32
|
|
false;
|
|
#else
|
|
true;
|
|
#endif
|
|
|
|
static const wchar_t kAnyCharsChar = L'*';
|
|
static const wchar_t kAnyCharChar = L'?';
|
|
|
|
#ifdef _WIN32
|
|
static const wchar_t kDirDelimiter1 = L'\\';
|
|
#endif
|
|
static const wchar_t kDirDelimiter2 = L'/';
|
|
|
|
static const UString kWildCardCharSet = L"?*";
|
|
|
|
static const UString kIllegalWildCardFileNameChars=
|
|
L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF"
|
|
L"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
|
|
L"\"/:<>\\|";
|
|
|
|
|
|
static inline bool IsCharDirLimiter(wchar_t c)
|
|
{
|
|
return (
|
|
#ifdef _WIN32
|
|
c == kDirDelimiter1 ||
|
|
#endif
|
|
c == kDirDelimiter2);
|
|
}
|
|
|
|
int CompareFileNames(const UString &s1, const UString &s2)
|
|
{
|
|
if (g_CaseSensitive)
|
|
return s1.Compare(s2);
|
|
return s1.CompareNoCase(s2);
|
|
}
|
|
|
|
// -----------------------------------------
|
|
// this function compares name with mask
|
|
// ? - any char
|
|
// * - any char or empty
|
|
|
|
static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name)
|
|
{
|
|
for (;;)
|
|
{
|
|
wchar_t m = *mask;
|
|
wchar_t c = *name;
|
|
if (m == 0)
|
|
return (c == 0);
|
|
if (m == kAnyCharsChar)
|
|
{
|
|
if (EnhancedMaskTest(mask + 1, name))
|
|
return true;
|
|
if (c == 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (m == kAnyCharChar)
|
|
{
|
|
if (c == 0)
|
|
return false;
|
|
}
|
|
else if (m != c)
|
|
if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c))
|
|
return false;
|
|
mask++;
|
|
}
|
|
name++;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// Splits path to strings
|
|
|
|
void SplitPathToParts(const UString &path, UStringVector &pathParts)
|
|
{
|
|
pathParts.Clear();
|
|
UString name;
|
|
int len = path.Length();
|
|
if (len == 0)
|
|
return;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
wchar_t c = path[i];
|
|
if (IsCharDirLimiter(c))
|
|
{
|
|
pathParts.Add(name);
|
|
name.Empty();
|
|
}
|
|
else
|
|
name += c;
|
|
}
|
|
pathParts.Add(name);
|
|
}
|
|
|
|
void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name)
|
|
{
|
|
int i;
|
|
for(i = path.Length() - 1; i >= 0; i--)
|
|
if(IsCharDirLimiter(path[i]))
|
|
break;
|
|
dirPrefix = path.Left(i + 1);
|
|
name = path.Mid(i + 1);
|
|
}
|
|
|
|
UString ExtractDirPrefixFromPath(const UString &path)
|
|
{
|
|
int i;
|
|
for(i = path.Length() - 1; i >= 0; i--)
|
|
if(IsCharDirLimiter(path[i]))
|
|
break;
|
|
return path.Left(i + 1);
|
|
}
|
|
|
|
UString ExtractFileNameFromPath(const UString &path)
|
|
{
|
|
int i;
|
|
for(i = path.Length() - 1; i >= 0; i--)
|
|
if(IsCharDirLimiter(path[i]))
|
|
break;
|
|
return path.Mid(i + 1);
|
|
}
|
|
|
|
|
|
bool CompareWildCardWithName(const UString &mask, const UString &name)
|
|
{
|
|
return EnhancedMaskTest(mask, name);
|
|
}
|
|
|
|
bool DoesNameContainWildCard(const UString &path)
|
|
{
|
|
return (path.FindOneOf(kWildCardCharSet) >= 0);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------'
|
|
// NWildcard
|
|
|
|
namespace NWildcard {
|
|
|
|
|
|
/*
|
|
M = MaskParts.Size();
|
|
N = TestNameParts.Size();
|
|
|
|
File Dir
|
|
ForFile req M<=N [N-M, N) -
|
|
nonreq M=N [0, M) -
|
|
|
|
ForDir req M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File
|
|
nonreq [0, M) same as ForBoth-File
|
|
|
|
ForBoth req m<=N [0, M) ... [N-M, N) same as ForBoth-File
|
|
nonreq [0, M) same as ForBoth-File
|
|
|
|
*/
|
|
|
|
bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const
|
|
{
|
|
if (!isFile && !ForDir)
|
|
return false;
|
|
int delta = (int)pathParts.Size() - (int)PathParts.Size();
|
|
if (delta < 0)
|
|
return false;
|
|
int start = 0;
|
|
int finish = 0;
|
|
if (isFile)
|
|
{
|
|
if (!ForDir && !Recursive && delta !=0)
|
|
return false;
|
|
if (!ForFile && delta == 0)
|
|
return false;
|
|
if (!ForDir && Recursive)
|
|
start = delta;
|
|
}
|
|
if (Recursive)
|
|
{
|
|
finish = delta;
|
|
if (isFile && !ForFile)
|
|
finish = delta - 1;
|
|
}
|
|
for (int d = start; d <= finish; d++)
|
|
{
|
|
int i;
|
|
for (i = 0; i < PathParts.Size(); i++)
|
|
if (!CompareWildCardWithName(PathParts[i], pathParts[i + d]))
|
|
break;
|
|
if (i == PathParts.Size())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int CCensorNode::FindSubNode(const UString &name) const
|
|
{
|
|
for (int i = 0; i < SubNodes.Size(); i++)
|
|
if (CompareFileNames(SubNodes[i].Name, name) == 0)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void CCensorNode::AddItemSimple(bool include, CItem &item)
|
|
{
|
|
if (include)
|
|
IncludeItems.Add(item);
|
|
else
|
|
ExcludeItems.Add(item);
|
|
}
|
|
|
|
void CCensorNode::AddItem(bool include, CItem &item)
|
|
{
|
|
if (item.PathParts.Size() <= 1)
|
|
{
|
|
AddItemSimple(include, item);
|
|
return;
|
|
}
|
|
const UString &front = item.PathParts.Front();
|
|
if (DoesNameContainWildCard(front))
|
|
{
|
|
AddItemSimple(include, item);
|
|
return;
|
|
}
|
|
int index = FindSubNode(front);
|
|
if (index < 0)
|
|
index = SubNodes.Add(CCensorNode(front, this));
|
|
item.PathParts.Delete(0);
|
|
SubNodes[index].AddItem(include, item);
|
|
}
|
|
|
|
void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir)
|
|
{
|
|
CItem item;
|
|
SplitPathToParts(path, item.PathParts);
|
|
item.Recursive = recursive;
|
|
item.ForFile = forFile;
|
|
item.ForDir = forDir;
|
|
AddItem(include, item);
|
|
}
|
|
|
|
bool CCensorNode::NeedCheckSubDirs() const
|
|
{
|
|
for (int i = 0; i < IncludeItems.Size(); i++)
|
|
{
|
|
const CItem &item = IncludeItems[i];
|
|
if (item.Recursive || item.PathParts.Size() > 1)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CCensorNode::AreThereIncludeItems() const
|
|
{
|
|
if (IncludeItems.Size() > 0)
|
|
return true;
|
|
for (int i = 0; i < SubNodes.Size(); i++)
|
|
if (SubNodes[i].AreThereIncludeItems())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const
|
|
{
|
|
const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
|
|
for (int i = 0; i < items.Size(); i++)
|
|
if (items[i].CheckPath(pathParts, isFile))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool CCensorNode::CheckPath(UStringVector &pathParts, bool isFile, bool &include) const
|
|
{
|
|
if (CheckPathCurrent(false, pathParts, isFile))
|
|
{
|
|
include = false;
|
|
return true;
|
|
}
|
|
include = true;
|
|
bool finded = CheckPathCurrent(true, pathParts, isFile);
|
|
if (pathParts.Size() == 1)
|
|
return finded;
|
|
int index = FindSubNode(pathParts.Front());
|
|
if (index >= 0)
|
|
{
|
|
UStringVector pathParts2 = pathParts;
|
|
pathParts2.Delete(0);
|
|
if (SubNodes[index].CheckPath(pathParts2, isFile, include))
|
|
return true;
|
|
}
|
|
return finded;
|
|
}
|
|
|
|
bool CCensorNode::CheckPath(const UString &path, bool isFile, bool &include) const
|
|
{
|
|
UStringVector pathParts;
|
|
SplitPathToParts(path, pathParts);
|
|
return CheckPath(pathParts, isFile, include);
|
|
}
|
|
|
|
bool CCensorNode::CheckPath(const UString &path, bool isFile) const
|
|
{
|
|
bool include;
|
|
if(CheckPath(path, isFile, include))
|
|
return include;
|
|
return false;
|
|
}
|
|
|
|
bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const
|
|
{
|
|
if (CheckPathCurrent(include, pathParts, isFile))
|
|
return true;
|
|
if (Parent == 0)
|
|
return false;
|
|
pathParts.Insert(0, Name);
|
|
return Parent->CheckPathToRoot(include, pathParts, isFile);
|
|
}
|
|
|
|
/*
|
|
bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const
|
|
{
|
|
UStringVector pathParts;
|
|
SplitPathToParts(path, pathParts);
|
|
return CheckPathToRoot(include, pathParts, isFile);
|
|
}
|
|
*/
|
|
|
|
void CCensorNode::AddItem2(bool include, const UString &path, bool recursive)
|
|
{
|
|
if (path.IsEmpty())
|
|
return;
|
|
bool forFile = true;
|
|
bool forFolder = true;
|
|
UString path2 = path;
|
|
if (IsCharDirLimiter(path[path.Length() - 1]))
|
|
{
|
|
path2.Delete(path.Length() - 1);
|
|
forFile = false;
|
|
}
|
|
AddItem(include, path2, recursive, forFile, forFolder);
|
|
}
|
|
|
|
void CCensorNode::ExtendExclude(const CCensorNode &fromNodes)
|
|
{
|
|
ExcludeItems += fromNodes.ExcludeItems;
|
|
for (int i = 0; i < fromNodes.SubNodes.Size(); i++)
|
|
{
|
|
const CCensorNode &node = fromNodes.SubNodes[i];
|
|
int subNodeIndex = FindSubNode(node.Name);
|
|
if (subNodeIndex < 0)
|
|
subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this));
|
|
SubNodes[subNodeIndex].ExtendExclude(node);
|
|
}
|
|
}
|
|
|
|
int CCensor::FindPrefix(const UString &prefix) const
|
|
{
|
|
for (int i = 0; i < Pairs.Size(); i++)
|
|
if (CompareFileNames(Pairs[i].Prefix, prefix) == 0)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
void CCensor::AddItem(bool include, const UString &path, bool recursive)
|
|
{
|
|
UStringVector pathParts;
|
|
SplitPathToParts(path, pathParts);
|
|
bool forFile = true;
|
|
if (pathParts.Back().IsEmpty())
|
|
{
|
|
forFile = false;
|
|
pathParts.DeleteBack();
|
|
}
|
|
const UString &front = pathParts.Front();
|
|
bool isAbs = false;
|
|
if (front.IsEmpty())
|
|
isAbs = true;
|
|
else if (front.Length() == 2 && front[1] == L':')
|
|
isAbs = true;
|
|
else
|
|
{
|
|
for (int i = 0; i < pathParts.Size(); i++)
|
|
{
|
|
const UString &part = pathParts[i];
|
|
if (part == L".." || part == L".")
|
|
{
|
|
isAbs = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
int numAbsParts = 0;
|
|
if (isAbs)
|
|
if (pathParts.Size() > 1)
|
|
numAbsParts = pathParts.Size() - 1;
|
|
else
|
|
numAbsParts = 1;
|
|
UString prefix;
|
|
for (int i = 0; i < numAbsParts; i++)
|
|
{
|
|
const UString &front = pathParts.Front();
|
|
if (DoesNameContainWildCard(front))
|
|
break;
|
|
prefix += front;
|
|
prefix += WCHAR_PATH_SEPARATOR;
|
|
pathParts.Delete(0);
|
|
}
|
|
int index = FindPrefix(prefix);
|
|
if (index < 0)
|
|
index = Pairs.Add(CPair(prefix));
|
|
|
|
CItem item;
|
|
item.PathParts = pathParts;
|
|
item.ForDir = true;
|
|
item.ForFile = forFile;
|
|
item.Recursive = recursive;
|
|
Pairs[index].Head.AddItem(include, item);
|
|
}
|
|
|
|
bool CCensor::CheckPath(const UString &path, bool isFile) const
|
|
{
|
|
bool finded = false;
|
|
for (int i = 0; i < Pairs.Size(); i++)
|
|
{
|
|
bool include;
|
|
if (Pairs[i].Head.CheckPath(path, isFile, include))
|
|
{
|
|
if (!include)
|
|
return false;
|
|
finded = true;
|
|
}
|
|
}
|
|
return finded;
|
|
}
|
|
|
|
void CCensor::ExtendExclude()
|
|
{
|
|
int i;
|
|
for (i = 0; i < Pairs.Size(); i++)
|
|
if (Pairs[i].Prefix.IsEmpty())
|
|
break;
|
|
if (i == Pairs.Size())
|
|
return;
|
|
int index = i;
|
|
for (i = 0; i < Pairs.Size(); i++)
|
|
if (index != i)
|
|
Pairs[i].Head.ExtendExclude(Pairs[index].Head);
|
|
}
|
|
|
|
}
|