1 // Common/Wildcard.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../C/Types.h" 6 7 #include "Wildcard.h" 8 9 bool g_CaseSensitive = 10 #ifdef _WIN32 11 false; 12 #else 13 true; 14 #endif 15 16 static const wchar_t kAnyCharsChar = L'*'; 17 static const wchar_t kAnyCharChar = L'?'; 18 19 #ifdef _WIN32 20 static const wchar_t kDirDelimiter1 = L'\\'; 21 #endif 22 static const wchar_t kDirDelimiter2 = L'/'; 23 24 static const UString kWildCardCharSet = L"?*"; 25 26 static const UString kIllegalWildCardFileNameChars= 27 L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF" 28 L"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" 29 L"\"/:<>\\|"; 30 31 32 static inline bool IsCharDirLimiter(wchar_t c) 33 { 34 return ( 35 #ifdef _WIN32 36 c == kDirDelimiter1 || 37 #endif 38 c == kDirDelimiter2); 39 } 40 41 int CompareFileNames(const UString &s1, const UString &s2) 42 { 43 if (g_CaseSensitive) 44 return s1.Compare(s2); 45 return s1.CompareNoCase(s2); 46 } 47 48 // ----------------------------------------- 49 // this function compares name with mask 50 // ? - any char 51 // * - any char or empty 52 53 static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name) 54 { 55 for (;;) 56 { 57 wchar_t m = *mask; 58 wchar_t c = *name; 59 if (m == 0) 60 return (c == 0); 61 if (m == kAnyCharsChar) 62 { 63 if (EnhancedMaskTest(mask + 1, name)) 64 return true; 65 if (c == 0) 66 return false; 67 } 68 else 69 { 70 if (m == kAnyCharChar) 71 { 72 if (c == 0) 73 return false; 74 } 75 else if (m != c) 76 if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c)) 77 return false; 78 mask++; 79 } 80 name++; 81 } 82 } 83 84 // -------------------------------------------------- 85 // Splits path to strings 86 87 void SplitPathToParts(const UString &path, UStringVector &pathParts) 88 { 89 pathParts.Clear(); 90 UString name; 91 int len = path.Length(); 92 if (len == 0) 93 return; 94 for (int i = 0; i < len; i++) 95 { 96 wchar_t c = path[i]; 97 if (IsCharDirLimiter(c)) 98 { 99 pathParts.Add(name); 100 name.Empty(); 101 } 102 else 103 name += c; 104 } 105 pathParts.Add(name); 106 } 107 108 void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name) 109 { 110 int i; 111 for (i = path.Length() - 1; i >= 0; i--) 112 if (IsCharDirLimiter(path[i])) 113 break; 114 dirPrefix = path.Left(i + 1); 115 name = path.Mid(i + 1); 116 } 117 118 UString ExtractDirPrefixFromPath(const UString &path) 119 { 120 int i; 121 for (i = path.Length() - 1; i >= 0; i--) 122 if (IsCharDirLimiter(path[i])) 123 break; 124 return path.Left(i + 1); 125 } 126 127 UString ExtractFileNameFromPath(const UString &path) 128 { 129 int i; 130 for (i = path.Length() - 1; i >= 0; i--) 131 if (IsCharDirLimiter(path[i])) 132 break; 133 return path.Mid(i + 1); 134 } 135 136 137 bool CompareWildCardWithName(const UString &mask, const UString &name) 138 { 139 return EnhancedMaskTest(mask, name); 140 } 141 142 bool DoesNameContainWildCard(const UString &path) 143 { 144 return (path.FindOneOf(kWildCardCharSet) >= 0); 145 } 146 147 148 // ----------------------------------------------------------' 149 // NWildcard 150 151 namespace NWildcard { 152 153 154 /* 155 M = MaskParts.Size(); 156 N = TestNameParts.Size(); 157 158 File Dir 159 ForFile req M<=N [N-M, N) - 160 nonreq M=N [0, M) - 161 162 ForDir req M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File 163 nonreq [0, M) same as ForBoth-File 164 165 ForBoth req m<=N [0, M) ... [N-M, N) same as ForBoth-File 166 nonreq [0, M) same as ForBoth-File 167 168 */ 169 170 bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const 171 { 172 if (!isFile && !ForDir) 173 return false; 174 int delta = (int)pathParts.Size() - (int)PathParts.Size(); 175 if (delta < 0) 176 return false; 177 int start = 0; 178 int finish = 0; 179 if (isFile) 180 { 181 if (!ForDir && !Recursive && delta !=0) 182 return false; 183 if (!ForFile && delta == 0) 184 return false; 185 if (!ForDir && Recursive) 186 start = delta; 187 } 188 if (Recursive) 189 { 190 finish = delta; 191 if (isFile && !ForFile) 192 finish = delta - 1; 193 } 194 for (int d = start; d <= finish; d++) 195 { 196 int i; 197 for (i = 0; i < PathParts.Size(); i++) 198 if (!CompareWildCardWithName(PathParts[i], pathParts[i + d])) 199 break; 200 if (i == PathParts.Size()) 201 return true; 202 } 203 return false; 204 } 205 206 int CCensorNode::FindSubNode(const UString &name) const 207 { 208 for (int i = 0; i < SubNodes.Size(); i++) 209 if (CompareFileNames(SubNodes[i].Name, name) == 0) 210 return i; 211 return -1; 212 } 213 214 void CCensorNode::AddItemSimple(bool include, CItem &item) 215 { 216 if (include) 217 IncludeItems.Add(item); 218 else 219 ExcludeItems.Add(item); 220 } 221 222 void CCensorNode::AddItem(bool include, CItem &item) 223 { 224 if (item.PathParts.Size() <= 1) 225 { 226 AddItemSimple(include, item); 227 return; 228 } 229 const UString &front = item.PathParts.Front(); 230 if (DoesNameContainWildCard(front)) 231 { 232 AddItemSimple(include, item); 233 return; 234 } 235 int index = FindSubNode(front); 236 if (index < 0) 237 index = SubNodes.Add(CCensorNode(front, this)); 238 item.PathParts.Delete(0); 239 SubNodes[index].AddItem(include, item); 240 } 241 242 void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir) 243 { 244 CItem item; 245 SplitPathToParts(path, item.PathParts); 246 item.Recursive = recursive; 247 item.ForFile = forFile; 248 item.ForDir = forDir; 249 AddItem(include, item); 250 } 251 252 bool CCensorNode::NeedCheckSubDirs() const 253 { 254 for (int i = 0; i < IncludeItems.Size(); i++) 255 { 256 const CItem &item = IncludeItems[i]; 257 if (item.Recursive || item.PathParts.Size() > 1) 258 return true; 259 } 260 return false; 261 } 262 263 bool CCensorNode::AreThereIncludeItems() const 264 { 265 if (IncludeItems.Size() > 0) 266 return true; 267 for (int i = 0; i < SubNodes.Size(); i++) 268 if (SubNodes[i].AreThereIncludeItems()) 269 return true; 270 return false; 271 } 272 273 bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const 274 { 275 const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems; 276 for (int i = 0; i < items.Size(); i++) 277 if (items[i].CheckPath(pathParts, isFile)) 278 return true; 279 return false; 280 } 281 282 bool CCensorNode::CheckPath(UStringVector &pathParts, bool isFile, bool &include) const 283 { 284 if (CheckPathCurrent(false, pathParts, isFile)) 285 { 286 include = false; 287 return true; 288 } 289 include = true; 290 bool finded = CheckPathCurrent(true, pathParts, isFile); 291 if (pathParts.Size() == 1) 292 return finded; 293 int index = FindSubNode(pathParts.Front()); 294 if (index >= 0) 295 { 296 UStringVector pathParts2 = pathParts; 297 pathParts2.Delete(0); 298 if (SubNodes[index].CheckPath(pathParts2, isFile, include)) 299 return true; 300 } 301 return finded; 302 } 303 304 bool CCensorNode::CheckPath(const UString &path, bool isFile, bool &include) const 305 { 306 UStringVector pathParts; 307 SplitPathToParts(path, pathParts); 308 return CheckPath(pathParts, isFile, include); 309 } 310 311 bool CCensorNode::CheckPath(const UString &path, bool isFile) const 312 { 313 bool include; 314 if (CheckPath(path, isFile, include)) 315 return include; 316 return false; 317 } 318 319 bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const 320 { 321 if (CheckPathCurrent(include, pathParts, isFile)) 322 return true; 323 if (Parent == 0) 324 return false; 325 pathParts.Insert(0, Name); 326 return Parent->CheckPathToRoot(include, pathParts, isFile); 327 } 328 329 /* 330 bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const 331 { 332 UStringVector pathParts; 333 SplitPathToParts(path, pathParts); 334 return CheckPathToRoot(include, pathParts, isFile); 335 } 336 */ 337 338 void CCensorNode::AddItem2(bool include, const UString &path, bool recursive) 339 { 340 if (path.IsEmpty()) 341 return; 342 bool forFile = true; 343 bool forFolder = true; 344 UString path2 = path; 345 if (IsCharDirLimiter(path[path.Length() - 1])) 346 { 347 path2.Delete(path.Length() - 1); 348 forFile = false; 349 } 350 AddItem(include, path2, recursive, forFile, forFolder); 351 } 352 353 void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) 354 { 355 ExcludeItems += fromNodes.ExcludeItems; 356 for (int i = 0; i < fromNodes.SubNodes.Size(); i++) 357 { 358 const CCensorNode &node = fromNodes.SubNodes[i]; 359 int subNodeIndex = FindSubNode(node.Name); 360 if (subNodeIndex < 0) 361 subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this)); 362 SubNodes[subNodeIndex].ExtendExclude(node); 363 } 364 } 365 366 int CCensor::FindPrefix(const UString &prefix) const 367 { 368 for (int i = 0; i < Pairs.Size(); i++) 369 if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) 370 return i; 371 return -1; 372 } 373 374 void CCensor::AddItem(bool include, const UString &path, bool recursive) 375 { 376 UStringVector pathParts; 377 if (path.IsEmpty()) 378 throw "Empty file path"; 379 SplitPathToParts(path, pathParts); 380 bool forFile = true; 381 if (pathParts.Back().IsEmpty()) 382 { 383 forFile = false; 384 pathParts.DeleteBack(); 385 } 386 const UString &front = pathParts.Front(); 387 bool isAbs = false; 388 if (front.IsEmpty()) 389 isAbs = true; 390 else if (front.Length() == 2 && front[1] == L':') 391 isAbs = true; 392 else 393 { 394 for (int i = 0; i < pathParts.Size(); i++) 395 { 396 const UString &part = pathParts[i]; 397 if (part == L".." || part == L".") 398 { 399 isAbs = true; 400 break; 401 } 402 } 403 } 404 int numAbsParts = 0; 405 if (isAbs) 406 if (pathParts.Size() > 1) 407 numAbsParts = pathParts.Size() - 1; 408 else 409 numAbsParts = 1; 410 UString prefix; 411 for (int i = 0; i < numAbsParts; i++) 412 { 413 const UString &front = pathParts.Front(); 414 if (DoesNameContainWildCard(front)) 415 break; 416 prefix += front; 417 prefix += WCHAR_PATH_SEPARATOR; 418 pathParts.Delete(0); 419 } 420 int index = FindPrefix(prefix); 421 if (index < 0) 422 index = Pairs.Add(CPair(prefix)); 423 424 CItem item; 425 item.PathParts = pathParts; 426 item.ForDir = true; 427 item.ForFile = forFile; 428 item.Recursive = recursive; 429 Pairs[index].Head.AddItem(include, item); 430 } 431 432 bool CCensor::CheckPath(const UString &path, bool isFile) const 433 { 434 bool finded = false; 435 for (int i = 0; i < Pairs.Size(); i++) 436 { 437 bool include; 438 if (Pairs[i].Head.CheckPath(path, isFile, include)) 439 { 440 if (!include) 441 return false; 442 finded = true; 443 } 444 } 445 return finded; 446 } 447 448 void CCensor::ExtendExclude() 449 { 450 int i; 451 for (i = 0; i < Pairs.Size(); i++) 452 if (Pairs[i].Prefix.IsEmpty()) 453 break; 454 if (i == Pairs.Size()) 455 return; 456 int index = i; 457 for (i = 0; i < Pairs.Size(); i++) 458 if (index != i) 459 Pairs[i].Head.ExtendExclude(Pairs[index].Head); 460 } 461 462 } 463