Home | History | Annotate | Download | only in Common
      1 // EnumDirItems.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "EnumDirItems.h"
      6 
      7 using namespace NWindows;
      8 using namespace NFile;
      9 using namespace NName;
     10 
     11 void AddDirFileInfo(int phyParent, int logParent,
     12     const NFind::CFileInfoW &fi, CObjectVector<CDirItem> &dirItems)
     13 {
     14   CDirItem di;
     15   di.Size = fi.Size;
     16   di.CTime = fi.CTime;
     17   di.ATime = fi.ATime;
     18   di.MTime = fi.MTime;
     19   di.Attrib = fi.Attrib;
     20   di.PhyParent = phyParent;
     21   di.LogParent = logParent;
     22   di.Name = fi.Name;
     23   dirItems.Add(di);
     24 }
     25 
     26 UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
     27 {
     28   UString path;
     29   int len = name.Length();
     30   int i;
     31   for (i = index; i >= 0; i = parents[i])
     32     len += Prefixes[i].Length();
     33   int totalLen = len;
     34   wchar_t *p = path.GetBuffer(len);
     35   p[len] = 0;
     36   len -= name.Length();
     37   memcpy(p + len, (const wchar_t *)name, name.Length() * sizeof(wchar_t));
     38   for (i = index; i >= 0; i = parents[i])
     39   {
     40     const UString &s = Prefixes[i];
     41     len -= s.Length();
     42     memcpy(p + len, (const wchar_t *)s, s.Length() * sizeof(wchar_t));
     43   }
     44   path.ReleaseBuffer(totalLen);
     45   return path;
     46 }
     47 
     48 UString CDirItems::GetPhyPath(int index) const
     49 {
     50   const CDirItem &di = Items[index];
     51   return GetPrefixesPath(PhyParents, di.PhyParent, di.Name);
     52 }
     53 
     54 UString CDirItems::GetLogPath(int index) const
     55 {
     56   const CDirItem &di = Items[index];
     57   return GetPrefixesPath(LogParents, di.LogParent, di.Name);
     58 }
     59 
     60 void CDirItems::ReserveDown()
     61 {
     62   Prefixes.ReserveDown();
     63   PhyParents.ReserveDown();
     64   LogParents.ReserveDown();
     65   Items.ReserveDown();
     66 }
     67 
     68 int CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
     69 {
     70   PhyParents.Add(phyParent);
     71   LogParents.Add(logParent);
     72   return Prefixes.Add(prefix);
     73 }
     74 
     75 void CDirItems::DeleteLastPrefix()
     76 {
     77   PhyParents.DeleteBack();
     78   LogParents.DeleteBack();
     79   Prefixes.DeleteBack();
     80 }
     81 
     82 void CDirItems::EnumerateDirectory(int phyParent, int logParent, const UString &phyPrefix,
     83     UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes)
     84 {
     85   NFind::CEnumeratorW enumerator(phyPrefix + (wchar_t)kAnyStringWildcard);
     86   for (;;)
     87   {
     88     NFind::CFileInfoW fi;
     89     bool found;
     90     if (!enumerator.Next(fi, found))
     91     {
     92       errorCodes.Add(::GetLastError());
     93       errorPaths.Add(phyPrefix);
     94       return;
     95     }
     96     if (!found)
     97       break;
     98     AddDirFileInfo(phyParent, logParent, fi, Items);
     99     if (fi.IsDir())
    100     {
    101       const UString name2 = fi.Name + (wchar_t)kDirDelimiter;
    102       int parent = AddPrefix(phyParent, logParent, name2);
    103       EnumerateDirectory(parent, parent, phyPrefix + name2, errorPaths, errorCodes);
    104     }
    105   }
    106 }
    107 
    108 void CDirItems::EnumerateDirItems2(const UString &phyPrefix, const UString &logPrefix,
    109     const UStringVector &filePaths, UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes)
    110 {
    111   int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, phyPrefix);
    112   int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix);
    113 
    114   for (int i = 0; i < filePaths.Size(); i++)
    115   {
    116     const UString &filePath = filePaths[i];
    117     NFind::CFileInfoW fi;
    118     const UString phyPath = phyPrefix + filePath;
    119     if (!fi.Find(phyPath))
    120     {
    121       errorCodes.Add(::GetLastError());
    122       errorPaths.Add(phyPath);
    123       continue;
    124     }
    125     int delimiter = filePath.ReverseFind((wchar_t)kDirDelimiter);
    126     UString phyPrefixCur;
    127     int phyParentCur = phyParent;
    128     if (delimiter >= 0)
    129     {
    130       phyPrefixCur = filePath.Left(delimiter + 1);
    131       phyParentCur = AddPrefix(phyParent, logParent, phyPrefixCur);
    132     }
    133     AddDirFileInfo(phyParentCur, logParent, fi, Items);
    134     if (fi.IsDir())
    135     {
    136       const UString name2 = fi.Name + (wchar_t)kDirDelimiter;
    137       int parent = AddPrefix(phyParentCur, logParent, name2);
    138       EnumerateDirectory(parent, parent, phyPrefix + phyPrefixCur + name2, errorPaths, errorCodes);
    139     }
    140   }
    141   ReserveDown();
    142 }
    143 
    144 static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode,
    145     int phyParent, int logParent, const UString &phyPrefix,
    146     const UStringVector &addArchivePrefix,
    147     CDirItems &dirItems,
    148     bool enterToSubFolders,
    149     IEnumDirItemCallback *callback,
    150     UStringVector &errorPaths,
    151     CRecordVector<DWORD> &errorCodes);
    152 
    153 static HRESULT EnumerateDirItems_Spec(const NWildcard::CCensorNode &curNode,
    154     int phyParent, int logParent, const UString &curFolderName,
    155     const UString &phyPrefix,
    156     const UStringVector &addArchivePrefix,
    157     CDirItems &dirItems,
    158     bool enterToSubFolders,
    159     IEnumDirItemCallback *callback,
    160     UStringVector &errorPaths,
    161     CRecordVector<DWORD> &errorCodes)
    162 
    163 {
    164   const UString name2 = curFolderName + (wchar_t)kDirDelimiter;
    165   int parent = dirItems.AddPrefix(phyParent, logParent, name2);
    166   int numItems = dirItems.Items.Size();
    167   HRESULT res = EnumerateDirItems(curNode, parent, parent, phyPrefix + name2,
    168     addArchivePrefix, dirItems, enterToSubFolders, callback, errorPaths, errorCodes);
    169   if (numItems == dirItems.Items.Size())
    170     dirItems.DeleteLastPrefix();
    171   return res;
    172 }
    173 
    174 
    175 static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode,
    176     int phyParent, int logParent, const UString &phyPrefix,
    177     const UStringVector &addArchivePrefix,  // prefix from curNode
    178     CDirItems &dirItems,
    179     bool enterToSubFolders,
    180     IEnumDirItemCallback *callback,
    181     UStringVector &errorPaths,
    182     CRecordVector<DWORD> &errorCodes)
    183 {
    184   if (!enterToSubFolders)
    185     if (curNode.NeedCheckSubDirs())
    186       enterToSubFolders = true;
    187   if (callback)
    188     RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), phyPrefix));
    189 
    190   // try direct_names case at first
    191   if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
    192   {
    193     // check that all names are direct
    194     int i;
    195     for (i = 0; i < curNode.IncludeItems.Size(); i++)
    196     {
    197       const NWildcard::CItem &item = curNode.IncludeItems[i];
    198       if (item.Recursive || item.PathParts.Size() != 1)
    199         break;
    200       const UString &name = item.PathParts.Front();
    201       if (name.IsEmpty() || DoesNameContainWildCard(name))
    202         break;
    203     }
    204     if (i == curNode.IncludeItems.Size())
    205     {
    206       // all names are direct (no wildcards)
    207       // so we don't need file_system's dir enumerator
    208       CRecordVector<bool> needEnterVector;
    209       for (i = 0; i < curNode.IncludeItems.Size(); i++)
    210       {
    211         const NWildcard::CItem &item = curNode.IncludeItems[i];
    212         const UString &name = item.PathParts.Front();
    213         const UString fullPath = phyPrefix + name;
    214         NFind::CFileInfoW fi;
    215         if (!fi.Find(fullPath))
    216         {
    217           errorCodes.Add(::GetLastError());
    218           errorPaths.Add(fullPath);
    219           continue;
    220         }
    221         bool isDir = fi.IsDir();
    222         if (isDir && !item.ForDir || !isDir && !item.ForFile)
    223         {
    224           errorCodes.Add((DWORD)E_FAIL);
    225           errorPaths.Add(fullPath);
    226           continue;
    227         }
    228         {
    229           UStringVector pathParts;
    230           pathParts.Add(fi.Name);
    231           if (curNode.CheckPathToRoot(false, pathParts, !isDir))
    232             continue;
    233         }
    234         AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
    235         if (!isDir)
    236           continue;
    237 
    238         UStringVector addArchivePrefixNew;
    239         const NWildcard::CCensorNode *nextNode = 0;
    240         int index = curNode.FindSubNode(name);
    241         if (index >= 0)
    242         {
    243           for (int t = needEnterVector.Size(); t <= index; t++)
    244             needEnterVector.Add(true);
    245           needEnterVector[index] = false;
    246           nextNode = &curNode.SubNodes[index];
    247         }
    248         else
    249         {
    250           nextNode = &curNode;
    251           addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support
    252         }
    253 
    254         RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
    255             addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes));
    256       }
    257       for (i = 0; i < curNode.SubNodes.Size(); i++)
    258       {
    259         if (i < needEnterVector.Size())
    260           if (!needEnterVector[i])
    261             continue;
    262         const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
    263         const UString fullPath = phyPrefix + nextNode.Name;
    264         NFind::CFileInfoW fi;
    265         if (!fi.Find(fullPath))
    266         {
    267           if (!nextNode.AreThereIncludeItems())
    268             continue;
    269           errorCodes.Add(::GetLastError());
    270           errorPaths.Add(fullPath);
    271           continue;
    272         }
    273         if (!fi.IsDir())
    274         {
    275           errorCodes.Add((DWORD)E_FAIL);
    276           errorPaths.Add(fullPath);
    277           continue;
    278         }
    279 
    280         RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
    281             UStringVector(), dirItems, false, callback, errorPaths, errorCodes));
    282       }
    283       return S_OK;
    284     }
    285   }
    286 
    287 
    288   NFind::CEnumeratorW enumerator(phyPrefix + wchar_t(kAnyStringWildcard));
    289   for (int ttt = 0; ; ttt++)
    290   {
    291     NFind::CFileInfoW fi;
    292     bool found;
    293     if (!enumerator.Next(fi, found))
    294     {
    295       errorCodes.Add(::GetLastError());
    296       errorPaths.Add(phyPrefix);
    297       break;
    298     }
    299     if (!found)
    300       break;
    301 
    302     if (callback && (ttt & 0xFF) == 0xFF)
    303       RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), phyPrefix));
    304     const UString &name = fi.Name;
    305     bool enterToSubFolders2 = enterToSubFolders;
    306     UStringVector addArchivePrefixNew = addArchivePrefix;
    307     addArchivePrefixNew.Add(name);
    308     {
    309       UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
    310       if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir()))
    311         continue;
    312     }
    313     if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir()))
    314     {
    315       AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
    316       if (fi.IsDir())
    317         enterToSubFolders2 = true;
    318     }
    319     if (!fi.IsDir())
    320       continue;
    321 
    322     const NWildcard::CCensorNode *nextNode = 0;
    323     if (addArchivePrefix.IsEmpty())
    324     {
    325       int index = curNode.FindSubNode(name);
    326       if (index >= 0)
    327         nextNode = &curNode.SubNodes[index];
    328     }
    329     if (!enterToSubFolders2 && nextNode == 0)
    330       continue;
    331 
    332     addArchivePrefixNew = addArchivePrefix;
    333     if (nextNode == 0)
    334     {
    335       nextNode = &curNode;
    336       addArchivePrefixNew.Add(name);
    337     }
    338 
    339     RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, name, phyPrefix,
    340         addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes));
    341   }
    342   return S_OK;
    343 }
    344 
    345 HRESULT EnumerateItems(
    346     const NWildcard::CCensor &censor,
    347     CDirItems &dirItems,
    348     IEnumDirItemCallback *callback,
    349     UStringVector &errorPaths,
    350     CRecordVector<DWORD> &errorCodes)
    351 {
    352   for (int i = 0; i < censor.Pairs.Size(); i++)
    353   {
    354     const NWildcard::CPair &pair = censor.Pairs[i];
    355     int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix);
    356     RINOK(EnumerateDirItems(pair.Head, phyParent, -1, pair.Prefix, UStringVector(), dirItems, false,
    357         callback, errorPaths, errorCodes));
    358   }
    359   dirItems.ReserveDown();
    360   return S_OK;
    361 }
    362