1 // Common/Wildcard.cpp 2 3 #include "StdAfx.h" 4 5 #include "Wildcard.h" 6 7 bool g_CaseSensitive = 8 #ifdef _WIN32 9 false; 10 #else 11 true; 12 #endif 13 14 15 bool IsPath1PrefixedByPath2(const wchar_t *s1, const wchar_t *s2) 16 { 17 if (g_CaseSensitive) 18 return IsString1PrefixedByString2(s1, s2); 19 return IsString1PrefixedByString2_NoCase(s1, s2); 20 } 21 22 int CompareFileNames(const wchar_t *s1, const wchar_t *s2) STRING_UNICODE_THROW 23 { 24 if (g_CaseSensitive) 25 return MyStringCompare(s1, s2); 26 return MyStringCompareNoCase(s1, s2); 27 } 28 29 #ifndef USE_UNICODE_FSTRING 30 int CompareFileNames(const char *s1, const char *s2) 31 { 32 const UString u1 = fs2us(s1); 33 const UString u2 = fs2us(s2); 34 if (g_CaseSensitive) 35 return MyStringCompare(u1, u2); 36 return MyStringCompareNoCase(u1, u2); 37 } 38 #endif 39 40 // ----------------------------------------- 41 // this function compares name with mask 42 // ? - any char 43 // * - any char or empty 44 45 static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name) 46 { 47 for (;;) 48 { 49 wchar_t m = *mask; 50 wchar_t c = *name; 51 if (m == 0) 52 return (c == 0); 53 if (m == '*') 54 { 55 if (EnhancedMaskTest(mask + 1, name)) 56 return true; 57 if (c == 0) 58 return false; 59 } 60 else 61 { 62 if (m == '?') 63 { 64 if (c == 0) 65 return false; 66 } 67 else if (m != c) 68 if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c)) 69 return false; 70 mask++; 71 } 72 name++; 73 } 74 } 75 76 // -------------------------------------------------- 77 // Splits path to strings 78 79 void SplitPathToParts(const UString &path, UStringVector &pathParts) 80 { 81 pathParts.Clear(); 82 unsigned len = path.Len(); 83 if (len == 0) 84 return; 85 UString name; 86 unsigned prev = 0; 87 for (unsigned i = 0; i < len; i++) 88 if (IsPathSepar(path[i])) 89 { 90 name.SetFrom(path.Ptr(prev), i - prev); 91 pathParts.Add(name); 92 prev = i + 1; 93 } 94 name.SetFrom(path.Ptr(prev), len - prev); 95 pathParts.Add(name); 96 } 97 98 void SplitPathToParts_2(const UString &path, UString &dirPrefix, UString &name) 99 { 100 const wchar_t *start = path; 101 const wchar_t *p = start + path.Len(); 102 for (; p != start; p--) 103 if (IsPathSepar(*(p - 1))) 104 break; 105 dirPrefix.SetFrom(path, (unsigned)(p - start)); 106 name = p; 107 } 108 109 void SplitPathToParts_Smart(const UString &path, UString &dirPrefix, UString &name) 110 { 111 const wchar_t *start = path; 112 const wchar_t *p = start + path.Len(); 113 if (p != start) 114 { 115 if (IsPathSepar(*(p - 1))) 116 p--; 117 for (; p != start; p--) 118 if (IsPathSepar(*(p - 1))) 119 break; 120 } 121 dirPrefix.SetFrom(path, (unsigned)(p - start)); 122 name = p; 123 } 124 125 /* 126 UString ExtractDirPrefixFromPath(const UString &path) 127 { 128 return path.Left(path.ReverseFind_PathSepar() + 1)); 129 } 130 */ 131 132 UString ExtractFileNameFromPath(const UString &path) 133 { 134 return UString(path.Ptr(path.ReverseFind_PathSepar() + 1)); 135 } 136 137 138 bool DoesWildcardMatchName(const UString &mask, const UString &name) 139 { 140 return EnhancedMaskTest(mask, name); 141 } 142 143 bool DoesNameContainWildcard(const UString &path) 144 { 145 for (unsigned i = 0; i < path.Len(); i++) 146 { 147 wchar_t c = path[i]; 148 if (c == '*' || c == '?') 149 return true; 150 } 151 return false; 152 } 153 154 155 // ----------------------------------------------------------' 156 // NWildcard 157 158 namespace NWildcard { 159 160 /* 161 162 M = MaskParts.Size(); 163 N = TestNameParts.Size(); 164 165 File Dir 166 ForFile rec M<=N [N-M, N) - 167 !ForDir nonrec M=N [0, M) - 168 169 ForDir rec M<N [0, M) ... [N-M-1, N-1) same as ForBoth-File 170 !ForFile nonrec [0, M) same as ForBoth-File 171 172 ForFile rec m<=N [0, M) ... [N-M, N) same as ForBoth-File 173 ForDir nonrec [0, M) same as ForBoth-File 174 175 */ 176 177 bool CItem::AreAllAllowed() const 178 { 179 return ForFile && ForDir && WildcardMatching && PathParts.Size() == 1 && PathParts.Front() == L"*"; 180 } 181 182 bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const 183 { 184 if (!isFile && !ForDir) 185 return false; 186 187 /* 188 if (PathParts.IsEmpty()) 189 { 190 // PathParts.IsEmpty() means all items (universal wildcard) 191 if (!isFile) 192 return true; 193 if (pathParts.Size() <= 1) 194 return ForFile; 195 return (ForDir || Recursive && ForFile); 196 } 197 */ 198 199 int delta = (int)pathParts.Size() - (int)PathParts.Size(); 200 if (delta < 0) 201 return false; 202 int start = 0; 203 int finish = 0; 204 205 if (isFile) 206 { 207 if (!ForDir) 208 { 209 if (Recursive) 210 start = delta; 211 else if (delta !=0) 212 return false; 213 } 214 if (!ForFile && delta == 0) 215 return false; 216 } 217 218 if (Recursive) 219 { 220 finish = delta; 221 if (isFile && !ForFile) 222 finish = delta - 1; 223 } 224 225 for (int d = start; d <= finish; d++) 226 { 227 unsigned i; 228 for (i = 0; i < PathParts.Size(); i++) 229 { 230 if (WildcardMatching) 231 { 232 if (!DoesWildcardMatchName(PathParts[i], pathParts[i + d])) 233 break; 234 } 235 else 236 { 237 if (CompareFileNames(PathParts[i], pathParts[i + d]) != 0) 238 break; 239 } 240 } 241 if (i == PathParts.Size()) 242 return true; 243 } 244 return false; 245 } 246 247 bool CCensorNode::AreAllAllowed() const 248 { 249 if (!Name.IsEmpty() || 250 !SubNodes.IsEmpty() || 251 !ExcludeItems.IsEmpty() || 252 IncludeItems.Size() != 1) 253 return false; 254 return IncludeItems.Front().AreAllAllowed(); 255 } 256 257 int CCensorNode::FindSubNode(const UString &name) const 258 { 259 FOR_VECTOR (i, SubNodes) 260 if (CompareFileNames(SubNodes[i].Name, name) == 0) 261 return i; 262 return -1; 263 } 264 265 void CCensorNode::AddItemSimple(bool include, CItem &item) 266 { 267 if (include) 268 IncludeItems.Add(item); 269 else 270 ExcludeItems.Add(item); 271 } 272 273 void CCensorNode::AddItem(bool include, CItem &item, int ignoreWildcardIndex) 274 { 275 if (item.PathParts.Size() <= 1) 276 { 277 if (item.PathParts.Size() != 0 && item.WildcardMatching) 278 { 279 if (!DoesNameContainWildcard(item.PathParts.Front())) 280 item.WildcardMatching = false; 281 } 282 AddItemSimple(include, item); 283 return; 284 } 285 const UString &front = item.PathParts.Front(); 286 287 // WIN32 doesn't support wildcards in file names 288 if (item.WildcardMatching 289 && ignoreWildcardIndex != 0 290 && DoesNameContainWildcard(front)) 291 { 292 AddItemSimple(include, item); 293 return; 294 } 295 int index = FindSubNode(front); 296 if (index < 0) 297 index = SubNodes.Add(CCensorNode(front, this)); 298 item.PathParts.Delete(0); 299 SubNodes[index].AddItem(include, item, ignoreWildcardIndex - 1); 300 } 301 302 void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir, bool wildcardMatching) 303 { 304 CItem item; 305 SplitPathToParts(path, item.PathParts); 306 item.Recursive = recursive; 307 item.ForFile = forFile; 308 item.ForDir = forDir; 309 item.WildcardMatching = wildcardMatching; 310 AddItem(include, item); 311 } 312 313 bool CCensorNode::NeedCheckSubDirs() const 314 { 315 FOR_VECTOR (i, IncludeItems) 316 { 317 const CItem &item = IncludeItems[i]; 318 if (item.Recursive || item.PathParts.Size() > 1) 319 return true; 320 } 321 return false; 322 } 323 324 bool CCensorNode::AreThereIncludeItems() const 325 { 326 if (IncludeItems.Size() > 0) 327 return true; 328 FOR_VECTOR (i, SubNodes) 329 if (SubNodes[i].AreThereIncludeItems()) 330 return true; 331 return false; 332 } 333 334 bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const 335 { 336 const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems; 337 FOR_VECTOR (i, items) 338 if (items[i].CheckPath(pathParts, isFile)) 339 return true; 340 return false; 341 } 342 343 bool CCensorNode::CheckPathVect(const UStringVector &pathParts, bool isFile, bool &include) const 344 { 345 if (CheckPathCurrent(false, pathParts, isFile)) 346 { 347 include = false; 348 return true; 349 } 350 include = true; 351 bool finded = CheckPathCurrent(true, pathParts, isFile); 352 if (pathParts.Size() <= 1) 353 return finded; 354 int index = FindSubNode(pathParts.Front()); 355 if (index >= 0) 356 { 357 UStringVector pathParts2 = pathParts; 358 pathParts2.Delete(0); 359 if (SubNodes[index].CheckPathVect(pathParts2, isFile, include)) 360 return true; 361 } 362 return finded; 363 } 364 365 /* 366 bool CCensorNode::CheckPath2(bool isAltStream, const UString &path, bool isFile, bool &include) const 367 { 368 UStringVector pathParts; 369 SplitPathToParts(path, pathParts); 370 if (CheckPathVect(pathParts, isFile, include)) 371 { 372 if (!include || !isAltStream) 373 return true; 374 } 375 if (isAltStream && !pathParts.IsEmpty()) 376 { 377 UString &back = pathParts.Back(); 378 int pos = back.Find(L':'); 379 if (pos > 0) 380 { 381 back.DeleteFrom(pos); 382 return CheckPathVect(pathParts, isFile, include); 383 } 384 } 385 return false; 386 } 387 388 bool CCensorNode::CheckPath(bool isAltStream, const UString &path, bool isFile) const 389 { 390 bool include; 391 if (CheckPath2(isAltStream, path, isFile, include)) 392 return include; 393 return false; 394 } 395 */ 396 397 bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const 398 { 399 if (CheckPathCurrent(include, pathParts, isFile)) 400 return true; 401 if (Parent == 0) 402 return false; 403 pathParts.Insert(0, Name); 404 return Parent->CheckPathToRoot(include, pathParts, isFile); 405 } 406 407 /* 408 bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const 409 { 410 UStringVector pathParts; 411 SplitPathToParts(path, pathParts); 412 return CheckPathToRoot(include, pathParts, isFile); 413 } 414 */ 415 416 void CCensorNode::AddItem2(bool include, const UString &path, bool recursive, bool wildcardMatching) 417 { 418 if (path.IsEmpty()) 419 return; 420 bool forFile = true; 421 bool forFolder = true; 422 UString path2 (path); 423 if (IsPathSepar(path.Back())) 424 { 425 path2.DeleteBack(); 426 forFile = false; 427 } 428 AddItem(include, path2, recursive, forFile, forFolder, wildcardMatching); 429 } 430 431 void CCensorNode::ExtendExclude(const CCensorNode &fromNodes) 432 { 433 ExcludeItems += fromNodes.ExcludeItems; 434 FOR_VECTOR (i, fromNodes.SubNodes) 435 { 436 const CCensorNode &node = fromNodes.SubNodes[i]; 437 int subNodeIndex = FindSubNode(node.Name); 438 if (subNodeIndex < 0) 439 subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this)); 440 SubNodes[subNodeIndex].ExtendExclude(node); 441 } 442 } 443 444 int CCensor::FindPrefix(const UString &prefix) const 445 { 446 FOR_VECTOR (i, Pairs) 447 if (CompareFileNames(Pairs[i].Prefix, prefix) == 0) 448 return i; 449 return -1; 450 } 451 452 #ifdef _WIN32 453 454 bool IsDriveColonName(const wchar_t *s) 455 { 456 wchar_t c = s[0]; 457 return c != 0 && s[1] == ':' && s[2] == 0 && (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'); 458 } 459 460 unsigned GetNumPrefixParts_if_DrivePath(UStringVector &pathParts) 461 { 462 if (pathParts.IsEmpty()) 463 return 0; 464 465 unsigned testIndex = 0; 466 if (pathParts[0].IsEmpty()) 467 { 468 if (pathParts.Size() < 4 469 || !pathParts[1].IsEmpty() 470 || pathParts[2] != L"?") 471 return 0; 472 testIndex = 3; 473 } 474 if (NWildcard::IsDriveColonName(pathParts[testIndex])) 475 return testIndex + 1; 476 return 0; 477 } 478 479 #endif 480 481 static unsigned GetNumPrefixParts(const UStringVector &pathParts) 482 { 483 if (pathParts.IsEmpty()) 484 return 0; 485 486 #ifdef _WIN32 487 488 if (IsDriveColonName(pathParts[0])) 489 return 1; 490 if (!pathParts[0].IsEmpty()) 491 return 0; 492 493 if (pathParts.Size() == 1) 494 return 1; 495 if (!pathParts[1].IsEmpty()) 496 return 1; 497 if (pathParts.Size() == 2) 498 return 2; 499 if (pathParts[2] == L".") 500 return 3; 501 502 unsigned networkParts = 2; 503 if (pathParts[2] == L"?") 504 { 505 if (pathParts.Size() == 3) 506 return 3; 507 if (IsDriveColonName(pathParts[3])) 508 return 4; 509 if (!pathParts[3].IsEqualTo_Ascii_NoCase("UNC")) 510 return 3; 511 networkParts = 4; 512 } 513 514 networkParts += 515 // 2; // server/share 516 1; // server 517 if (pathParts.Size() <= networkParts) 518 return pathParts.Size(); 519 return networkParts; 520 521 #else 522 523 return pathParts[0].IsEmpty() ? 1 : 0; 524 525 #endif 526 } 527 528 void CCensor::AddItem(ECensorPathMode pathMode, bool include, const UString &path, bool recursive, bool wildcardMatching) 529 { 530 if (path.IsEmpty()) 531 throw "Empty file path"; 532 533 UStringVector pathParts; 534 SplitPathToParts(path, pathParts); 535 536 bool forFile = true; 537 if (pathParts.Back().IsEmpty()) 538 { 539 forFile = false; 540 pathParts.DeleteBack(); 541 } 542 543 UString prefix; 544 545 int ignoreWildcardIndex = -1; 546 547 // #ifdef _WIN32 548 // we ignore "?" wildcard in "\\?\" prefix. 549 if (pathParts.Size() >= 3 550 && pathParts[0].IsEmpty() 551 && pathParts[1].IsEmpty() 552 && pathParts[2] == L"?") 553 ignoreWildcardIndex = 2; 554 // #endif 555 556 if (pathMode != k_AbsPath) 557 { 558 ignoreWildcardIndex = -1; 559 560 const unsigned numPrefixParts = GetNumPrefixParts(pathParts); 561 unsigned numSkipParts = numPrefixParts; 562 563 if (pathMode != k_FullPath) 564 { 565 if (numPrefixParts != 0 && pathParts.Size() > numPrefixParts) 566 numSkipParts = pathParts.Size() - 1; 567 } 568 { 569 int dotsIndex = -1; 570 for (unsigned i = numPrefixParts; i < pathParts.Size(); i++) 571 { 572 const UString &part = pathParts[i]; 573 if (part == L".." || part == L".") 574 dotsIndex = i; 575 } 576 577 if (dotsIndex >= 0) 578 if (dotsIndex == (int)pathParts.Size() - 1) 579 numSkipParts = pathParts.Size(); 580 else 581 numSkipParts = pathParts.Size() - 1; 582 } 583 584 for (unsigned i = 0; i < numSkipParts; i++) 585 { 586 { 587 const UString &front = pathParts.Front(); 588 // WIN32 doesn't support wildcards in file names 589 if (wildcardMatching) 590 if (i >= numPrefixParts && DoesNameContainWildcard(front)) 591 break; 592 prefix += front; 593 prefix.Add_PathSepar(); 594 } 595 pathParts.Delete(0); 596 } 597 } 598 599 int index = FindPrefix(prefix); 600 if (index < 0) 601 index = Pairs.Add(CPair(prefix)); 602 603 if (pathMode != k_AbsPath) 604 { 605 if (pathParts.IsEmpty() || pathParts.Size() == 1 && pathParts[0].IsEmpty()) 606 { 607 // we create universal item, if we skip all parts as prefix (like \ or L:\ ) 608 pathParts.Clear(); 609 pathParts.Add(UString("*")); 610 forFile = true; 611 wildcardMatching = true; 612 recursive = false; 613 } 614 } 615 616 CItem item; 617 item.PathParts = pathParts; 618 item.ForDir = true; 619 item.ForFile = forFile; 620 item.Recursive = recursive; 621 item.WildcardMatching = wildcardMatching; 622 Pairs[index].Head.AddItem(include, item, ignoreWildcardIndex); 623 } 624 625 /* 626 bool CCensor::CheckPath(bool isAltStream, const UString &path, bool isFile) const 627 { 628 bool finded = false; 629 FOR_VECTOR (i, Pairs) 630 { 631 bool include; 632 if (Pairs[i].Head.CheckPath2(isAltStream, path, isFile, include)) 633 { 634 if (!include) 635 return false; 636 finded = true; 637 } 638 } 639 return finded; 640 } 641 */ 642 643 void CCensor::ExtendExclude() 644 { 645 unsigned i; 646 for (i = 0; i < Pairs.Size(); i++) 647 if (Pairs[i].Prefix.IsEmpty()) 648 break; 649 if (i == Pairs.Size()) 650 return; 651 unsigned index = i; 652 for (i = 0; i < Pairs.Size(); i++) 653 if (index != i) 654 Pairs[i].Head.ExtendExclude(Pairs[index].Head); 655 } 656 657 void CCensor::AddPathsToCensor(ECensorPathMode censorPathMode) 658 { 659 FOR_VECTOR(i, CensorPaths) 660 { 661 const CCensorPath &cp = CensorPaths[i]; 662 AddItem(censorPathMode, cp.Include, cp.Path, cp.Recursive, cp.WildcardMatching); 663 } 664 CensorPaths.Clear(); 665 } 666 667 void CCensor::AddPreItem(bool include, const UString &path, bool recursive, bool wildcardMatching) 668 { 669 CCensorPath &cp = CensorPaths.AddNew(); 670 cp.Path = path; 671 cp.Include = include; 672 cp.Recursive = recursive; 673 cp.WildcardMatching = wildcardMatching; 674 } 675 676 } 677