1// EnumDirItems.cpp
2
3#include "StdAfx.h"
4
5#include "EnumDirItems.h"
6
7using namespace NWindows;
8using namespace NFile;
9using namespace NName;
10
11void 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
26UString 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
48UString CDirItems::GetPhyPath(int index) const
49{
50  const CDirItem &di = Items[index];
51  return GetPrefixesPath(PhyParents, di.PhyParent, di.Name);
52}
53
54UString CDirItems::GetLogPath(int index) const
55{
56  const CDirItem &di = Items[index];
57  return GetPrefixesPath(LogParents, di.LogParent, di.Name);
58}
59
60void CDirItems::ReserveDown()
61{
62  Prefixes.ReserveDown();
63  PhyParents.ReserveDown();
64  LogParents.ReserveDown();
65  Items.ReserveDown();
66}
67
68int 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
75void CDirItems::DeleteLastPrefix()
76{
77  PhyParents.DeleteBack();
78  LogParents.DeleteBack();
79  Prefixes.DeleteBack();
80}
81
82void 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
108void 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
144static 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
153static 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
175static 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
345HRESULT 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