// Common/Wildcard.cpp | |
#include "StdAfx.h" | |
#include "../../C/Types.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; | |
if (path.IsEmpty()) | |
throw "Empty file path"; | |
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); | |
} | |
} |