// Common/Wildcard.cpp | |
#include "StdAfx.h" | |
#include "Wildcard.h" | |
bool g_CaseSensitive = | |
#ifdef _WIN32 | |
false; | |
#else | |
true; | |
#endif | |
bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2) | |
{ | |
if (g_CaseSensitive) | |
return IsString1PrefixedByString2(s1, s2); | |
return IsString1PrefixedByString2_NoCase(s1, s2); | |
} | |
int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW | |
{ | |
if (g_CaseSensitive) | |
return MyStringCompare(s1, s2); | |
return MyStringCompareNoCase(s1, s2); | |
} | |
#ifndef USE_UNICODE_FSTRING | |
int CompareFileNames(const char *s1, const char *s2) | |
{ | |
const UString u1 = fs2us(s1); | |
const UString u2 = fs2us(s2); | |
if (g_CaseSensitive) | |
return MyStringCompare(u1, u2); | |
return MyStringCompareNoCase(u1, u2); | |
} | |
#endif | |
// ----------------------------------------- | |
// 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 == '*') | |
{ | |
if (EnhancedMaskTest(mask + 1, name)) | |
return true; | |
if (c == 0) | |
return false; | |
} | |
else | |
{ | |
if (m == '?') | |
{ | |
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(); | |
unsigned len = path.Len(); | |
if (len == 0) | |
return; | |
UString name; | |
unsigned prev = 0; | |
for (unsigned i = 0; i < len; i++) | |
if (IsPathSepar(path[i])) | |
{ | |
name.SetFrom(path.Ptr(prev), i - prev); | |
pathParts.Add(name); | |
prev = i + 1; | |
} | |
name.SetFrom(path.Ptr(prev), len - prev); | |
pathParts.Add(name); | |
} | |
void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name) | |
{ | |
const wchar_t *start = path; | |
const wchar_t *p = start + path.Len(); | |
for (; p != start; p--) | |
if (IsPathSepar(*(p - 1))) | |
break; | |
dirPrefix.SetFrom(path, (unsigned)(p - start)); | |
name = p; | |
} | |
void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name) | |
{ | |
const wchar_t *start = path; | |
const wchar_t *p = start + path.Len(); | |
if (p != start) | |
{ | |
if (IsPathSepar(*(p - 1))) | |
p--; | |
for (; p != start; p--) | |
if (IsPathSepar(*(p - 1))) | |
break; | |
} | |
dirPrefix.SetFrom(path, (unsigned)(p - start)); | |
name = p; | |
} | |
/* | |
UString ExtractDirPrefixFromPath(const UString &path) | |
{ | |
return path.Left(path.ReverseFind_PathSepar() + 1)); | |
} | |
*/ | |
UString ExtractFileNameFromPath(const UString &path) | |
{ | |
return UString(path.Ptr(path.ReverseFind_PathSepar() + 1)); | |
} | |
bool DoesWildcardMatchName(const UString &mask, const UString &name) | |
{ | |
return EnhancedMaskTest(mask, name); | |
} | |
bool DoesNameContainWildcard(const UString &path) | |
{ | |
for (unsigned i = 0; i < path.Len(); i++) | |
{ | |
wchar_t c = path[i]; | |
if (c == '*' || c == '?') | |
return true; | |
} | |
return false; | |
} | |
// ----------------------------------------------------------' | |
// NWildcard | |
namespace NWildcard { | |
/* | |
M = MaskParts.Size(); | |
N = TestNameParts.Size(); | |
File Dir | |
ForFile rec M<=N [N-M, N) - | |
!ForDir nonrec M=N [0, M) - | |
ForDir rec M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File | |
!ForFile nonrec [0, M) same as ForBoth-File | |
ForFile rec m<=N [0, M) ... [N-M, N) same as ForBoth-File | |
ForDir nonrec [0, M) same as ForBoth-File | |
*/ | |
bool CItem::AreAllAllowed() const | |
{ | |
return ForFile && ForDir && WildcardMatching && PathParts.Size() == 1 && PathParts.Front() == L"*"; | |
} | |
bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const | |
{ | |
if (!isFile && !ForDir) | |
return false; | |
/* | |
if (PathParts.IsEmpty()) | |
{ | |
// PathParts.IsEmpty() means all items (universal wildcard) | |
if (!isFile) | |
return true; | |
if (pathParts.Size() <= 1) | |
return ForFile; | |
return (ForDir || Recursive && ForFile); | |
} | |
*/ | |
int delta = (int)pathParts.Size() - (int)PathParts.Size(); | |
if (delta < 0) | |
return false; | |
int start = 0; | |
int finish = 0; | |
if (isFile) | |
{ | |
if (!ForDir) | |
{ | |
if (Recursive) | |
start = delta; | |
else if (delta !=0) | |
return false; | |
} | |
if (!ForFile && delta == 0) | |
return false; | |
} | |
if (Recursive) | |
{ | |
finish = delta; | |
if (isFile && !ForFile) | |
finish = delta - 1; | |
} | |
for (int d = start; d <= finish; d++) | |
{ | |
unsigned i; | |
for (i = 0; i < PathParts.Size(); i++) | |
{ | |
if (WildcardMatching) | |
{ | |
if (!DoesWildcardMatchName(PathParts[i], pathParts[i + d])) | |
break; | |
} | |
else | |
{ | |
if (CompareFileNames(PathParts[i], pathParts[i + d]) != 0) | |
break; | |
} | |
} | |
if (i == PathParts.Size()) | |
return true; | |
} | |
return false; | |
} | |
bool CCensorNode::AreAllAllowed() const | |
{ | |
if (!Name.IsEmpty() || | |
!SubNodes.IsEmpty() || | |
!ExcludeItems.IsEmpty() || | |
IncludeItems.Size() != 1) | |
return false; | |
return IncludeItems.Front().AreAllAllowed(); | |
} | |
int CCensorNode::FindSubNode(const UString &name) const | |
{ | |
FOR_VECTOR (i, SubNodes) | |
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, int ignoreWildcardIndex) | |
{ | |
if (item.PathParts.Size() <= 1) | |
{ | |
if (item.PathParts.Size() != 0 && item.WildcardMatching) | |
{ | |
if (!DoesNameContainWildcard(item.PathParts.Front())) | |
item.WildcardMatching = false; | |
} | |
AddItemSimple(include, item); | |
return; | |
} | |
const UString &front = item.PathParts.Front(); | |
// WIN32 doesn't support wildcards in file names | |
if (item.WildcardMatching | |
&& ignoreWildcardIndex != 0 | |
&& 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, ignoreWildcardIndex - 1); | |
} | |
void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir, bool wildcardMatching) | |
{ | |
CItem item; | |
SplitPathToParts(path, item.PathParts); | |
item.Recursive = recursive; | |
item.ForFile = forFile; | |
item.ForDir = forDir; | |
item.WildcardMatching = wildcardMatching; | |
AddItem(include, item); | |
} | |
bool CCensorNode::NeedCheckSubDirs() const | |
{ | |
FOR_VECTOR (i, IncludeItems) | |
{ | |
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_VECTOR (i, SubNodes) | |
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_VECTOR (i, items) | |
if (items[i].CheckPath(pathParts, isFile)) | |
return true; | |
return false; | |
} | |
bool CCensorNode::CheckPathVect(const 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].CheckPathVect(pathParts2, isFile, include)) | |
return true; | |
} | |
return finded; | |
} | |
/* | |
bool CCensorNode::CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const | |
{ | |
UStringVector pathParts; | |
SplitPathToParts(path, pathParts); | |
if (CheckPathVect(pathParts, isFile, include)) | |
{ | |
if (!include || !isAltStream) | |
return true; | |
} | |
if (isAltStream && !pathParts.IsEmpty()) | |
{ | |
UString &back = pathParts.Back(); | |
int pos = back.Find(L':'); | |
if (pos > 0) | |
{ | |
back.DeleteFrom(pos); | |
return CheckPathVect(pathParts, isFile, include); | |
} | |
} | |
return false; | |
} | |
bool CCensorNode::CheckPath(bool isAltStream, const UString &path, bool isFile) const | |
{ | |
bool include; | |
if (CheckPath2(isAltStream, 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, bool wildcardMatching) | |
{ | |
if (path.IsEmpty()) | |
return; | |
bool forFile = true; | |
bool forFolder = true; | |
UString path2 (path); | |
if (IsPathSepar(path.Back())) | |
{ | |
path2.DeleteBack(); | |
forFile = false; | |
} | |
AddItem(include, path2, recursive, forFile, forFolder, wildcardMatching); | |
} | |
void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) | |
{ | |
ExcludeItems += fromNodes.ExcludeItems; | |
FOR_VECTOR (i, fromNodes.SubNodes) | |
{ | |
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_VECTOR (i, Pairs) | |
if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) | |
return i; | |
return -1; | |
} | |
#ifdef _WIN32 | |
bool IsDriveColonName(const wchar_t *s) | |
{ | |
wchar_t c = s[0]; | |
return c != 0 && s[1] == ':' && s[2] == 0 && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); | |
} | |
unsigned GetNumPrefixParts_if_DrivePath(UStringVector &pathParts) | |
{ | |
if (pathParts.IsEmpty()) | |
return 0; | |
unsigned testIndex = 0; | |
if (pathParts[0].IsEmpty()) | |
{ | |
if (pathParts.Size() < 4 | |
|| !pathParts[1].IsEmpty() | |
|| pathParts[2] != L"?") | |
return 0; | |
testIndex = 3; | |
} | |
if (NWildcard::IsDriveColonName(pathParts[testIndex])) | |
return testIndex + 1; | |
return 0; | |
} | |
#endif | |
static unsigned GetNumPrefixParts(const UStringVector &pathParts) | |
{ | |
if (pathParts.IsEmpty()) | |
return 0; | |
#ifdef _WIN32 | |
if (IsDriveColonName(pathParts[0])) | |
return 1; | |
if (!pathParts[0].IsEmpty()) | |
return 0; | |
if (pathParts.Size() == 1) | |
return 1; | |
if (!pathParts[1].IsEmpty()) | |
return 1; | |
if (pathParts.Size() == 2) | |
return 2; | |
if (pathParts[2] == L".") | |
return 3; | |
unsigned networkParts = 2; | |
if (pathParts[2] == L"?") | |
{ | |
if (pathParts.Size() == 3) | |
return 3; | |
if (IsDriveColonName(pathParts[3])) | |
return 4; | |
if (!pathParts[3].IsEqualTo_Ascii_NoCase("UNC")) | |
return 3; | |
networkParts = 4; | |
} | |
networkParts += | |
// 2; // server/share | |
1; // server | |
if (pathParts.Size() <= networkParts) | |
return pathParts.Size(); | |
return networkParts; | |
#else | |
return pathParts[0].IsEmpty() ? 1 : 0; | |
#endif | |
} | |
void CCensor::AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching) | |
{ | |
if (path.IsEmpty()) | |
throw "Empty file path"; | |
UStringVector pathParts; | |
SplitPathToParts(path, pathParts); | |
bool forFile = true; | |
if (pathParts.Back().IsEmpty()) | |
{ | |
forFile = false; | |
pathParts.DeleteBack(); | |
} | |
UString prefix; | |
int ignoreWildcardIndex = -1; | |
// #ifdef _WIN32 | |
// we ignore "?" wildcard in "\\?\" prefix. | |
if (pathParts.Size() >= 3 | |
&& pathParts[0].IsEmpty() | |
&& pathParts[1].IsEmpty() | |
&& pathParts[2] == L"?") | |
ignoreWildcardIndex = 2; | |
// #endif | |
if (pathMode != k_AbsPath) | |
{ | |
ignoreWildcardIndex = -1; | |
const unsigned numPrefixParts = GetNumPrefixParts(pathParts); | |
unsigned numSkipParts = numPrefixParts; | |
if (pathMode != k_FullPath) | |
{ | |
if (numPrefixParts != 0 && pathParts.Size() > numPrefixParts) | |
numSkipParts = pathParts.Size() - 1; | |
} | |
{ | |
int dotsIndex = -1; | |
for (unsigned i = numPrefixParts; i < pathParts.Size(); i++) | |
{ | |
const UString &part = pathParts[i]; | |
if (part == L".." || part == L".") | |
dotsIndex = i; | |
} | |
if (dotsIndex >= 0) | |
if (dotsIndex == (int)pathParts.Size() - 1) | |
numSkipParts = pathParts.Size(); | |
else | |
numSkipParts = pathParts.Size() - 1; | |
} | |
for (unsigned i = 0; i < numSkipParts; i++) | |
{ | |
{ | |
const UString &front = pathParts.Front(); | |
// WIN32 doesn't support wildcards in file names | |
if (wildcardMatching) | |
if (i >= numPrefixParts && DoesNameContainWildcard(front)) | |
break; | |
prefix += front; | |
prefix.Add_PathSepar(); | |
} | |
pathParts.Delete(0); | |
} | |
} | |
int index = FindPrefix(prefix); | |
if (index < 0) | |
index = Pairs.Add(CPair(prefix)); | |
if (pathMode != k_AbsPath) | |
{ | |
if (pathParts.IsEmpty() || pathParts.Size() == 1 && pathParts[0].IsEmpty()) | |
{ | |
// we create universal item, if we skip all parts as prefix (like \ or L:\ ) | |
pathParts.Clear(); | |
pathParts.Add(UString("*")); | |
forFile = true; | |
wildcardMatching = true; | |
recursive = false; | |
} | |
} | |
CItem item; | |
item.PathParts = pathParts; | |
item.ForDir = true; | |
item.ForFile = forFile; | |
item.Recursive = recursive; | |
item.WildcardMatching = wildcardMatching; | |
Pairs[index].Head.AddItem(include, item, ignoreWildcardIndex); | |
} | |
/* | |
bool CCensor::CheckPath(bool isAltStream, const UString &path, bool isFile) const | |
{ | |
bool finded = false; | |
FOR_VECTOR (i, Pairs) | |
{ | |
bool include; | |
if (Pairs[i].Head.CheckPath2(isAltStream, path, isFile, include)) | |
{ | |
if (!include) | |
return false; | |
finded = true; | |
} | |
} | |
return finded; | |
} | |
*/ | |
void CCensor::ExtendExclude() | |
{ | |
unsigned i; | |
for (i = 0; i < Pairs.Size(); i++) | |
if (Pairs[i].Prefix.IsEmpty()) | |
break; | |
if (i == Pairs.Size()) | |
return; | |
unsigned index = i; | |
for (i = 0; i < Pairs.Size(); i++) | |
if (index != i) | |
Pairs[i].Head.ExtendExclude(Pairs[index].Head); | |
} | |
void CCensor::AddPathsToCensor(ECensorPathMode censorPathMode) | |
{ | |
FOR_VECTOR(i, CensorPaths) | |
{ | |
const CCensorPath &cp = CensorPaths[i]; | |
AddItem(censorPathMode, cp.Include, cp.Path, cp.Recursive, cp.WildcardMatching); | |
} | |
CensorPaths.Clear(); | |
} | |
void CCensor::AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching) | |
{ | |
CCensorPath &cp = CensorPaths.AddNew(); | |
cp.Path = path; | |
cp.Include = include; | |
cp.Recursive = recursive; | |
cp.WildcardMatching = wildcardMatching; | |
} | |
} |