// 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); } }