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
12extern bool g_IsNT;
13#endif
14
15namespace NWindows {
16namespace NFile {
17
18#ifdef SUPPORT_DEVICE_FILE
19bool IsDeviceName(LPCTSTR n);
20#ifndef _UNICODE
21bool IsDeviceName(LPCWSTR n);
22#endif
23#endif
24
25#if defined(WIN_LONG_PATH) && defined(_UNICODE)
26#define WIN_LONG_PATH2
27#endif
28
29bool GetLongPath(LPCWSTR fileName, UString &res);
30
31namespace NFind {
32
33static const TCHAR kDot = TEXT('.');
34
35bool 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
45bool 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
71static 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
79static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
80
81static 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
87static 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
97bool 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
108bool 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
129bool 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
160bool 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
170bool 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
192void 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
201bool 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
223bool 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
244bool DoesFileExist(LPCTSTR name)
245{
246  CFileInfo fi;
247  return fi.Find(name) && !fi.IsDir();
248}
249
250bool DoesDirExist(LPCTSTR name)
251{
252  CFileInfo fi;
253  return fi.Find(name) && fi.IsDir();
254}
255
256bool DoesFileOrDirExist(LPCTSTR name)
257{
258  CFileInfo fi;
259  return fi.Find(name);
260}
261
262#ifndef _UNICODE
263bool DoesFileExist(LPCWSTR name)
264{
265  CFileInfoW fi;
266  return fi.Find(name) && !fi.IsDir();
267}
268
269bool DoesDirExist(LPCWSTR name)
270{
271  CFileInfoW fi;
272  return fi.Find(name) && fi.IsDir();
273}
274bool DoesFileOrDirExist(LPCWSTR name)
275{
276  CFileInfoW fi;
277  return fi.Find(name);
278}
279#endif
280
281/////////////////////////////////////
282// CEnumerator
283
284bool CEnumerator::NextAny(CFileInfo &fi)
285{
286  if (_findFile.IsHandleAllocated())
287    return _findFile.FindNext(fi);
288  else
289    return _findFile.FindFirst(_wildcard, fi);
290}
291
292bool 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
303bool 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
315bool CEnumeratorW::NextAny(CFileInfoW &fi)
316{
317  if (_findFile.IsHandleAllocated())
318    return _findFile.FindNext(fi);
319  else
320    return _findFile.FindFirst(_wildcard, fi);
321}
322
323bool 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
334bool 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
351bool 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
361HANDLE 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
376HANDLE 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
394bool 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
424bool 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