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