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