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