Home | History | Annotate | Download | only in Common
      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