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