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 
AddDirFileInfo(int phyParent,int logParent,const NFind::CFileInfoW & fi,CObjectVector<CDirItem> & dirItems)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 
GetPrefixesPath(const CIntVector & parents,int index,const UString & name) const26 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 
GetPhyPath(int index) const48 UString CDirItems::GetPhyPath(int index) const
49 {
50   const CDirItem &di = Items[index];
51   return GetPrefixesPath(PhyParents, di.PhyParent, di.Name);
52 }
53 
GetLogPath(int index) const54 UString CDirItems::GetLogPath(int index) const
55 {
56   const CDirItem &di = Items[index];
57   return GetPrefixesPath(LogParents, di.LogParent, di.Name);
58 }
59 
ReserveDown()60 void CDirItems::ReserveDown()
61 {
62   Prefixes.ReserveDown();
63   PhyParents.ReserveDown();
64   LogParents.ReserveDown();
65   Items.ReserveDown();
66 }
67 
AddPrefix(int phyParent,int logParent,const UString & prefix)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 
DeleteLastPrefix()75 void CDirItems::DeleteLastPrefix()
76 {
77   PhyParents.DeleteBack();
78   LogParents.DeleteBack();
79   Prefixes.DeleteBack();
80 }
81 
EnumerateDirectory(int phyParent,int logParent,const UString & phyPrefix,UStringVector & errorPaths,CRecordVector<DWORD> & errorCodes)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 
EnumerateDirItems2(const UString & phyPrefix,const UString & logPrefix,const UStringVector & filePaths,UStringVector & errorPaths,CRecordVector<DWORD> & errorCodes)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 
EnumerateDirItems_Spec(const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const UString & curFolderName,const UString & phyPrefix,const UStringVector & addArchivePrefix,CDirItems & dirItems,bool enterToSubFolders,IEnumDirItemCallback * callback,UStringVector & errorPaths,CRecordVector<DWORD> & errorCodes)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 
EnumerateDirItems(const NWildcard::CCensorNode & curNode,int phyParent,int logParent,const UString & phyPrefix,const UStringVector & addArchivePrefix,CDirItems & dirItems,bool enterToSubFolders,IEnumDirItemCallback * callback,UStringVector & errorPaths,CRecordVector<DWORD> & errorCodes)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 
EnumerateItems(const NWildcard::CCensor & censor,CDirItems & dirItems,IEnumDirItemCallback * callback,UStringVector & errorPaths,CRecordVector<DWORD> & errorCodes)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