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 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