blob: 798cbd957b4fcf6d6b2344079e3e68b15a34db61 [file] [log] [blame]
// Common/Wildcard.cpp
#include "StdAfx.h"
#include "Wildcard.h"
extern
bool g_CaseSensitive;
bool g_CaseSensitive =
#ifdef _WIN32
false;
#elif defined (__APPLE__)
#ifdef TARGET_OS_IPHONE
true;
#else
false;
#endif
#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);
}
// #include <stdio.h>
/*
static int MyStringCompare_PathLinux(const wchar_t *s1, const wchar_t *s2) throw()
{
for (;;)
{
wchar_t c1 = *s1++;
wchar_t c2 = *s2++;
if (c1 != c2)
{
if (c1 == 0) return -1;
if (c2 == 0) return 1;
if (c1 == '/') c1 = 0;
if (c2 == '/') c2 = 0;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
continue;
}
if (c1 == 0) return 0;
}
}
*/
static int MyStringCompare_Path(const wchar_t *s1, const wchar_t *s2) throw()
{
for (;;)
{
wchar_t c1 = *s1++;
wchar_t c2 = *s2++;
if (c1 != c2)
{
if (c1 == 0) return -1;
if (c2 == 0) return 1;
if (IS_PATH_SEPAR(c1)) c1 = 0;
if (IS_PATH_SEPAR(c2)) c2 = 0;
if (c1 < c2) return -1;
if (c1 > c2) return 1;
continue;
}
if (c1 == 0) return 0;
}
}
static int MyStringCompareNoCase_Path(const wchar_t *s1, const wchar_t *s2) throw()
{
for (;;)
{
wchar_t c1 = *s1++;
wchar_t c2 = *s2++;
if (c1 != c2)
{
if (c1 == 0) return -1;
if (c2 == 0) return 1;
if (IS_PATH_SEPAR(c1)) c1 = 0;
if (IS_PATH_SEPAR(c2)) c2 = 0;
c1 = MyCharUpper(c1);
c2 = MyCharUpper(c2);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
continue;
}
if (c1 == 0) return 0;
}
}
int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW
{
/*
printf("\nCompareFileNames");
printf("\n S1: %ls", s1);
printf("\n S2: %ls", s2);
printf("\n");
*/
// 21.07 : we parse PATH_SEPARATOR so: 0 < PATH_SEPARATOR < 1
if (g_CaseSensitive)
return MyStringCompare_Path(s1, s2);
return MyStringCompareNoCase_Path(s1, s2);
}
#ifndef USE_UNICODE_FSTRING
int CompareFileNames(const char *s1, const char *s2)
{
const UString u1 = fs2us(s1);
const UString u2 = fs2us(s2);
return CompareFileNames(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 (;;)
{
const wchar_t m = *mask;
const 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((unsigned)(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 + (unsigned)d]))
break;
}
else
{
if (CompareFileNames(PathParts[i], pathParts[i + (unsigned)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 (int)i;
return -1;
}
void CCensorNode::AddItemSimple(bool include, CItem &item)
{
CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
items.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;
}
CCensorNode &subNode = Find_SubNode_Or_Add_New(front);
item.PathParts.Delete(0);
subNode.AddItem(include, item, ignoreWildcardIndex - 1);
}
/*
void CCensorNode::AddItem(bool include, const UString &path, const CCensorPathProps &props)
{
CItem item;
SplitPathToParts(path, item.PathParts);
item.Recursive = props.Recursive;
item.ForFile = props.ForFile;
item.ForDir = props.ForDir;
item.WildcardMatching = props.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;
}
if (pathParts.Size() > 1)
{
int index = FindSubNode(pathParts.Front());
if (index >= 0)
{
UStringVector pathParts2 = pathParts;
pathParts2.Delete(0);
if (SubNodes[(unsigned)index].CheckPathVect(pathParts2, isFile, include))
return true;
}
}
bool finded = CheckPathCurrent(true, pathParts, isFile);
include = finded; // if (!finded), then (true) is allowed also
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_Change(bool include, UStringVector &pathParts, bool isFile) const
{
if (CheckPathCurrent(include, pathParts, isFile))
return true;
if (!Parent)
return false;
pathParts.Insert(0, Name);
return Parent->CheckPathToRoot_Change(include, pathParts, isFile);
}
bool CCensorNode::CheckPathToRoot(bool include, const UStringVector &pathParts, bool isFile) const
{
if (CheckPathCurrent(include, pathParts, isFile))
return true;
if (!Parent)
return false;
UStringVector pathParts2;
pathParts2.Add(Name);
pathParts2 += pathParts;
return Parent->CheckPathToRoot_Change(include, pathParts2, isFile);
}
/*
bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const
{
UStringVector pathParts;
SplitPathToParts(path, pathParts);
return CheckPathToRoot(include, pathParts, isFile);
}
*/
void CCensorNode::ExtendExclude(const CCensorNode &fromNodes)
{
ExcludeItems += fromNodes.ExcludeItems;
FOR_VECTOR (i, fromNodes.SubNodes)
{
const CCensorNode &node = fromNodes.SubNodes[i];
Find_SubNode_Or_Add_New(node.Name).ExtendExclude(node);
}
}
int CCensor::FindPairForPrefix(const UString &prefix) const
{
FOR_VECTOR (i, Pairs)
if (CompareFileNames(Pairs[i].Prefix, prefix) == 0)
return (int)i;
return -1;
}
#ifdef _WIN32
bool IsDriveColonName(const wchar_t *s)
{
unsigned c = s[0];
c |= 0x20;
c -= 'a';
return c <= (unsigned)('z' - 'a') && s[1] == ':' && s[2] == 0;
}
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;
/* empty last part could be removed already from (pathParts),
if there was tail path separator (slash) in original full path string. */
#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,
const CCensorPathProps &props)
{
if (path.IsEmpty())
throw "Empty file path";
UStringVector pathParts;
SplitPathToParts(path, pathParts);
CCensorPathProps props2 = props;
bool forFile = true;
bool forDir = true;
const UString &back = pathParts.Back();
if (back.IsEmpty())
{
// we have tail path separator. So it's directory.
// we delete tail path separator here even for "\" and "c:\"
forFile = false;
pathParts.DeleteBack();
}
else
{
if (props.MarkMode == kMark_StrictFile
|| (props.MarkMode == kMark_StrictFile_IfWildcard
&& DoesNameContainWildcard(back)))
forDir = false;
}
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)
{
// detection of the number of Skip Parts for prefix
ignoreWildcardIndex = -1;
const unsigned numPrefixParts = GetNumPrefixParts(pathParts);
unsigned numSkipParts = numPrefixParts;
if (pathMode != k_FullPath)
{
// if absolute path, then all parts before last part will be in prefix
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 = (int)i;
}
if (dotsIndex >= 0)
{
if (dotsIndex == (int)pathParts.Size() - 1)
numSkipParts = pathParts.Size();
else
numSkipParts = pathParts.Size() - 1;
}
}
// we split (pathParts) to (prefix) and (pathParts).
for (unsigned i = 0; i < numSkipParts; i++)
{
{
const UString &front = pathParts.Front();
// WIN32 doesn't support wildcards in file names
if (props.WildcardMatching)
if (i >= numPrefixParts && DoesNameContainWildcard(front))
break;
prefix += front;
prefix.Add_PathSepar();
}
pathParts.Delete(0);
}
}
int index = FindPairForPrefix(prefix);
if (index < 0)
{
index = (int)Pairs.Size();
Pairs.AddNew().Prefix = 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;
forDir = true;
props2.WildcardMatching = true;
props2.Recursive = false;
}
}
/*
// not possible now
if (!forDir && !forFile)
{
UString s ("file path was blocked for files and directories: ");
s += path;
throw s;
// return; // for debug : ignore item (don't create Item)
}
*/
CItem item;
item.PathParts = pathParts;
item.ForDir = forDir;
item.ForFile = forFile;
item.Recursive = props2.Recursive;
item.WildcardMatching = props2.WildcardMatching;
Pairs[(unsigned)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.Props);
}
CensorPaths.Clear();
}
void CCensor::AddPreItem(bool include, const UString &path, const CCensorPathProps &props)
{
CCensorPath &cp = CensorPaths.AddNew();
cp.Path = path;
cp.Include = include;
cp.Props = props;
}
}