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