1 // Windows/FileFind.cpp
2
3 #include "StdAfx.h"
4
5 #include "FileFind.h"
6 #include "FileIO.h"
7 #ifndef _UNICODE
8 #include "../Common/StringConvert.h"
9 #endif
10
11 #ifndef _UNICODE
12 extern bool g_IsNT;
13 #endif
14
15 namespace NWindows {
16 namespace NFile {
17
18 #ifdef SUPPORT_DEVICE_FILE
19 bool IsDeviceName(LPCTSTR n);
20 #ifndef _UNICODE
21 bool IsDeviceName(LPCWSTR n);
22 #endif
23 #endif
24
25 #if defined(WIN_LONG_PATH) && defined(_UNICODE)
26 #define WIN_LONG_PATH2
27 #endif
28
29 bool GetLongPath(LPCWSTR fileName, UString &res);
30
31 namespace NFind {
32
33 static const TCHAR kDot = TEXT('.');
34
IsDots() const35 bool CFileInfo::IsDots() const
36 {
37 if (!IsDir() || Name.IsEmpty())
38 return false;
39 if (Name[0] != kDot)
40 return false;
41 return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2);
42 }
43
44 #ifndef _UNICODE
IsDots() const45 bool CFileInfoW::IsDots() const
46 {
47 if (!IsDir() || Name.IsEmpty())
48 return false;
49 if (Name[0] != kDot)
50 return false;
51 return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2);
52 }
53 #endif
54
55 #define WIN_FD_TO_MY_FI(fi, fd) \
56 fi.Attrib = fd.dwFileAttributes; \
57 fi.CTime = fd.ftCreationTime; \
58 fi.ATime = fd.ftLastAccessTime; \
59 fi.MTime = fd.ftLastWriteTime; \
60 fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \
61 fi.IsDevice = false;
62
63 /*
64 #ifdef UNDER_CE
65 fi.ObjectID = fd.dwOID;
66 #else
67 fi.ReparseTag = fd.dwReserved0;
68 #endif
69 */
70
ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA & fd,CFileInfo & fi)71 static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi)
72 {
73 WIN_FD_TO_MY_FI(fi, fd);
74 fi.Name = fd.cFileName;
75 }
76
77 #ifndef _UNICODE
78
GetCurrentCodePage()79 static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
80
ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATAW & fd,CFileInfoW & fi)81 static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfoW &fi)
82 {
83 WIN_FD_TO_MY_FI(fi, fd);
84 fi.Name = fd.cFileName;
85 }
86
ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA & fd,CFileInfoW & fi)87 static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfoW &fi)
88 {
89 WIN_FD_TO_MY_FI(fi, fd);
90 fi.Name = GetUnicodeString(fd.cFileName, GetCurrentCodePage());
91 }
92 #endif
93
94 ////////////////////////////////
95 // CFindFile
96
Close()97 bool CFindFile::Close()
98 {
99 if (_handle == INVALID_HANDLE_VALUE)
100 return true;
101 if (!::FindClose(_handle))
102 return false;
103 _handle = INVALID_HANDLE_VALUE;
104 return true;
105 }
106
107
FindFirst(LPCTSTR wildcard,CFileInfo & fi)108 bool CFindFile::FindFirst(LPCTSTR wildcard, CFileInfo &fi)
109 {
110 if (!Close())
111 return false;
112 WIN32_FIND_DATA fd;
113 _handle = ::FindFirstFile(wildcard, &fd);
114 #ifdef WIN_LONG_PATH2
115 if (_handle == INVALID_HANDLE_VALUE)
116 {
117 UString longPath;
118 if (GetLongPath(wildcard, longPath))
119 _handle = ::FindFirstFileW(longPath, &fd);
120 }
121 #endif
122 if (_handle == INVALID_HANDLE_VALUE)
123 return false;
124 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
125 return true;
126 }
127
128 #ifndef _UNICODE
FindFirst(LPCWSTR wildcard,CFileInfoW & fi)129 bool CFindFile::FindFirst(LPCWSTR wildcard, CFileInfoW &fi)
130 {
131 if (!Close())
132 return false;
133 if (g_IsNT)
134 {
135 WIN32_FIND_DATAW fd;
136 _handle = ::FindFirstFileW(wildcard, &fd);
137 #ifdef WIN_LONG_PATH
138 if (_handle == INVALID_HANDLE_VALUE)
139 {
140 UString longPath;
141 if (GetLongPath(wildcard, longPath))
142 _handle = ::FindFirstFileW(longPath, &fd);
143 }
144 #endif
145 if (_handle != INVALID_HANDLE_VALUE)
146 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
147 }
148 else
149 {
150 WIN32_FIND_DATAA fd;
151 _handle = ::FindFirstFileA(UnicodeStringToMultiByte(wildcard,
152 GetCurrentCodePage()), &fd);
153 if (_handle != INVALID_HANDLE_VALUE)
154 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
155 }
156 return (_handle != INVALID_HANDLE_VALUE);
157 }
158 #endif
159
FindNext(CFileInfo & fi)160 bool CFindFile::FindNext(CFileInfo &fi)
161 {
162 WIN32_FIND_DATA fd;
163 bool result = BOOLToBool(::FindNextFile(_handle, &fd));
164 if (result)
165 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
166 return result;
167 }
168
169 #ifndef _UNICODE
FindNext(CFileInfoW & fi)170 bool CFindFile::FindNext(CFileInfoW &fi)
171 {
172 if (g_IsNT)
173 {
174 WIN32_FIND_DATAW fd;
175 if (!::FindNextFileW(_handle, &fd))
176 return false;
177 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
178 }
179 else
180 {
181 WIN32_FIND_DATAA fd;
182 if (!::FindNextFileA(_handle, &fd))
183 return false;
184 ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi);
185 }
186 return true;
187 }
188 #endif
189
190 #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0;
191
Clear()192 void CFileInfoBase::Clear()
193 {
194 Size = 0;
195 MY_CLEAR_FILETIME(CTime);
196 MY_CLEAR_FILETIME(ATime);
197 MY_CLEAR_FILETIME(MTime);
198 Attrib = 0;
199 }
200
Find(LPCTSTR wildcard)201 bool CFileInfo::Find(LPCTSTR wildcard)
202 {
203 #ifdef SUPPORT_DEVICE_FILE
204 if (IsDeviceName(wildcard))
205 {
206 Clear();
207 IsDevice = true;
208 NIO::CInFile inFile;
209 if (!inFile.Open(wildcard))
210 return false;
211 Name = wildcard + 4;
212 if (inFile.LengthDefined)
213 Size = inFile.Length;
214 return true;
215 }
216 #endif
217 CFindFile finder;
218 return finder.FindFirst(wildcard, *this);
219 }
220
221
222 #ifndef _UNICODE
Find(LPCWSTR wildcard)223 bool CFileInfoW::Find(LPCWSTR wildcard)
224 {
225 #ifdef SUPPORT_DEVICE_FILE
226 if (IsDeviceName(wildcard))
227 {
228 Clear();
229 IsDevice = true;
230 NIO::CInFile inFile;
231 if (!inFile.Open(wildcard))
232 return false;
233 Name = wildcard + 4;
234 if (inFile.LengthDefined)
235 Size = inFile.Length;
236 return true;
237 }
238 #endif
239 CFindFile finder;
240 return finder.FindFirst(wildcard, *this);
241 }
242 #endif
243
DoesFileExist(LPCTSTR name)244 bool DoesFileExist(LPCTSTR name)
245 {
246 CFileInfo fi;
247 return fi.Find(name) && !fi.IsDir();
248 }
249
DoesDirExist(LPCTSTR name)250 bool DoesDirExist(LPCTSTR name)
251 {
252 CFileInfo fi;
253 return fi.Find(name) && fi.IsDir();
254 }
255
DoesFileOrDirExist(LPCTSTR name)256 bool DoesFileOrDirExist(LPCTSTR name)
257 {
258 CFileInfo fi;
259 return fi.Find(name);
260 }
261
262 #ifndef _UNICODE
DoesFileExist(LPCWSTR name)263 bool DoesFileExist(LPCWSTR name)
264 {
265 CFileInfoW fi;
266 return fi.Find(name) && !fi.IsDir();
267 }
268
DoesDirExist(LPCWSTR name)269 bool DoesDirExist(LPCWSTR name)
270 {
271 CFileInfoW fi;
272 return fi.Find(name) && fi.IsDir();
273 }
DoesFileOrDirExist(LPCWSTR name)274 bool DoesFileOrDirExist(LPCWSTR name)
275 {
276 CFileInfoW fi;
277 return fi.Find(name);
278 }
279 #endif
280
281 /////////////////////////////////////
282 // CEnumerator
283
NextAny(CFileInfo & fi)284 bool CEnumerator::NextAny(CFileInfo &fi)
285 {
286 if (_findFile.IsHandleAllocated())
287 return _findFile.FindNext(fi);
288 else
289 return _findFile.FindFirst(_wildcard, fi);
290 }
291
Next(CFileInfo & fi)292 bool CEnumerator::Next(CFileInfo &fi)
293 {
294 for (;;)
295 {
296 if (!NextAny(fi))
297 return false;
298 if (!fi.IsDots())
299 return true;
300 }
301 }
302
Next(CFileInfo & fi,bool & found)303 bool CEnumerator::Next(CFileInfo &fi, bool &found)
304 {
305 if (Next(fi))
306 {
307 found = true;
308 return true;
309 }
310 found = false;
311 return (::GetLastError() == ERROR_NO_MORE_FILES);
312 }
313
314 #ifndef _UNICODE
NextAny(CFileInfoW & fi)315 bool CEnumeratorW::NextAny(CFileInfoW &fi)
316 {
317 if (_findFile.IsHandleAllocated())
318 return _findFile.FindNext(fi);
319 else
320 return _findFile.FindFirst(_wildcard, fi);
321 }
322
Next(CFileInfoW & fi)323 bool CEnumeratorW::Next(CFileInfoW &fi)
324 {
325 for (;;)
326 {
327 if (!NextAny(fi))
328 return false;
329 if (!fi.IsDots())
330 return true;
331 }
332 }
333
Next(CFileInfoW & fi,bool & found)334 bool CEnumeratorW::Next(CFileInfoW &fi, bool &found)
335 {
336 if (Next(fi))
337 {
338 found = true;
339 return true;
340 }
341 found = false;
342 return (::GetLastError() == ERROR_NO_MORE_FILES);
343 }
344
345 #endif
346
347 ////////////////////////////////
348 // CFindChangeNotification
349 // FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
350
Close()351 bool CFindChangeNotification::Close()
352 {
353 if (!IsHandleAllocated())
354 return true;
355 if (!::FindCloseChangeNotification(_handle))
356 return false;
357 _handle = INVALID_HANDLE_VALUE;
358 return true;
359 }
360
FindFirst(LPCTSTR pathName,bool watchSubtree,DWORD notifyFilter)361 HANDLE CFindChangeNotification::FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter)
362 {
363 _handle = ::FindFirstChangeNotification(pathName, BoolToBOOL(watchSubtree), notifyFilter);
364 #ifdef WIN_LONG_PATH2
365 if (!IsHandleAllocated())
366 {
367 UString longPath;
368 if (GetLongPath(pathName, longPath))
369 _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter);
370 }
371 #endif
372 return _handle;
373 }
374
375 #ifndef _UNICODE
FindFirst(LPCWSTR pathName,bool watchSubtree,DWORD notifyFilter)376 HANDLE CFindChangeNotification::FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter)
377 {
378 if (!g_IsNT)
379 return FindFirst(UnicodeStringToMultiByte(pathName, GetCurrentCodePage()), watchSubtree, notifyFilter);
380 _handle = ::FindFirstChangeNotificationW(pathName, BoolToBOOL(watchSubtree), notifyFilter);
381 #ifdef WIN_LONG_PATH
382 if (!IsHandleAllocated())
383 {
384 UString longPath;
385 if (GetLongPath(pathName, longPath))
386 _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter);
387 }
388 #endif
389 return _handle;
390 }
391 #endif
392
393 #ifndef UNDER_CE
MyGetLogicalDriveStrings(CSysStringVector & driveStrings)394 bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings)
395 {
396 driveStrings.Clear();
397 UINT32 size = GetLogicalDriveStrings(0, NULL);
398 if (size == 0)
399 return false;
400 CSysString buffer;
401 UINT32 newSize = GetLogicalDriveStrings(size, buffer.GetBuffer(size));
402 if (newSize == 0)
403 return false;
404 if (newSize > size)
405 return false;
406 CSysString string;
407 for (UINT32 i = 0; i < newSize; i++)
408 {
409 TCHAR c = buffer[i];
410 if (c == TEXT('\0'))
411 {
412 driveStrings.Add(string);
413 string.Empty();
414 }
415 else
416 string += c;
417 }
418 if (!string.IsEmpty())
419 return false;
420 return true;
421 }
422
423 #ifndef _UNICODE
MyGetLogicalDriveStrings(UStringVector & driveStrings)424 bool MyGetLogicalDriveStrings(UStringVector &driveStrings)
425 {
426 driveStrings.Clear();
427 if (g_IsNT)
428 {
429 UINT32 size = GetLogicalDriveStringsW(0, NULL);
430 if (size == 0)
431 return false;
432 UString buffer;
433 UINT32 newSize = GetLogicalDriveStringsW(size, buffer.GetBuffer(size));
434 if (newSize == 0)
435 return false;
436 if (newSize > size)
437 return false;
438 UString string;
439 for (UINT32 i = 0; i < newSize; i++)
440 {
441 WCHAR c = buffer[i];
442 if (c == L'\0')
443 {
444 driveStrings.Add(string);
445 string.Empty();
446 }
447 else
448 string += c;
449 }
450 return string.IsEmpty();
451 }
452 CSysStringVector driveStringsA;
453 bool res = MyGetLogicalDriveStrings(driveStringsA);
454 for (int i = 0; i < driveStringsA.Size(); i++)
455 driveStrings.Add(GetUnicodeString(driveStringsA[i]));
456 return res;
457 }
458 #endif
459
460 #endif
461
462 }}}
463