// Windows/FileFind.cpp | |
#include "StdAfx.h" | |
#include "FileFind.h" | |
#include "FileIO.h" | |
#ifndef _UNICODE | |
#include "../Common/StringConvert.h" | |
#endif | |
#ifndef _UNICODE | |
extern bool g_IsNT; | |
#endif | |
namespace NWindows { | |
namespace NFile { | |
#ifdef SUPPORT_DEVICE_FILE | |
bool IsDeviceName(LPCTSTR n); | |
#ifndef _UNICODE | |
bool IsDeviceName(LPCWSTR n); | |
#endif | |
#endif | |
#if defined(WIN_LONG_PATH) && defined(_UNICODE) | |
#define WIN_LONG_PATH2 | |
#endif | |
bool GetLongPath(LPCWSTR fileName, UString &res); | |
namespace NFind { | |
static const TCHAR kDot = TEXT('.'); | |
bool CFileInfo::IsDots() const | |
{ | |
if (!IsDir() || Name.IsEmpty()) | |
return false; | |
if (Name[0] != kDot) | |
return false; | |
return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); | |
} | |
#ifndef _UNICODE | |
bool CFileInfoW::IsDots() const | |
{ | |
if (!IsDir() || Name.IsEmpty()) | |
return false; | |
if (Name[0] != kDot) | |
return false; | |
return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2); | |
} | |
#endif | |
#define WIN_FD_TO_MY_FI(fi, fd) \ | |
fi.Attrib = fd.dwFileAttributes; \ | |
fi.CTime = fd.ftCreationTime; \ | |
fi.ATime = fd.ftLastAccessTime; \ | |
fi.MTime = fd.ftLastWriteTime; \ | |
fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \ | |
fi.IsDevice = false; | |
/* | |
#ifdef UNDER_CE | |
fi.ObjectID = fd.dwOID; | |
#else | |
fi.ReparseTag = fd.dwReserved0; | |
#endif | |
*/ | |
static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi) | |
{ | |
WIN_FD_TO_MY_FI(fi, fd); | |
fi.Name = fd.cFileName; | |
} | |
#ifndef _UNICODE | |
static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; } | |
static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfoW &fi) | |
{ | |
WIN_FD_TO_MY_FI(fi, fd); | |
fi.Name = fd.cFileName; | |
} | |
static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfoW &fi) | |
{ | |
WIN_FD_TO_MY_FI(fi, fd); | |
fi.Name = GetUnicodeString(fd.cFileName, GetCurrentCodePage()); | |
} | |
#endif | |
//////////////////////////////// | |
// CFindFile | |
bool CFindFile::Close() | |
{ | |
if (_handle == INVALID_HANDLE_VALUE) | |
return true; | |
if (!::FindClose(_handle)) | |
return false; | |
_handle = INVALID_HANDLE_VALUE; | |
return true; | |
} | |
bool CFindFile::FindFirst(LPCTSTR wildcard, CFileInfo &fi) | |
{ | |
if (!Close()) | |
return false; | |
WIN32_FIND_DATA fd; | |
_handle = ::FindFirstFile(wildcard, &fd); | |
#ifdef WIN_LONG_PATH2 | |
if (_handle == INVALID_HANDLE_VALUE) | |
{ | |
UString longPath; | |
if (GetLongPath(wildcard, longPath)) | |
_handle = ::FindFirstFileW(longPath, &fd); | |
} | |
#endif | |
if (_handle == INVALID_HANDLE_VALUE) | |
return false; | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
return true; | |
} | |
#ifndef _UNICODE | |
bool CFindFile::FindFirst(LPCWSTR wildcard, CFileInfoW &fi) | |
{ | |
if (!Close()) | |
return false; | |
if (g_IsNT) | |
{ | |
WIN32_FIND_DATAW fd; | |
_handle = ::FindFirstFileW(wildcard, &fd); | |
#ifdef WIN_LONG_PATH | |
if (_handle == INVALID_HANDLE_VALUE) | |
{ | |
UString longPath; | |
if (GetLongPath(wildcard, longPath)) | |
_handle = ::FindFirstFileW(longPath, &fd); | |
} | |
#endif | |
if (_handle != INVALID_HANDLE_VALUE) | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
} | |
else | |
{ | |
WIN32_FIND_DATAA fd; | |
_handle = ::FindFirstFileA(UnicodeStringToMultiByte(wildcard, | |
GetCurrentCodePage()), &fd); | |
if (_handle != INVALID_HANDLE_VALUE) | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
} | |
return (_handle != INVALID_HANDLE_VALUE); | |
} | |
#endif | |
bool CFindFile::FindNext(CFileInfo &fi) | |
{ | |
WIN32_FIND_DATA fd; | |
bool result = BOOLToBool(::FindNextFile(_handle, &fd)); | |
if (result) | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
return result; | |
} | |
#ifndef _UNICODE | |
bool CFindFile::FindNext(CFileInfoW &fi) | |
{ | |
if (g_IsNT) | |
{ | |
WIN32_FIND_DATAW fd; | |
if (!::FindNextFileW(_handle, &fd)) | |
return false; | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
} | |
else | |
{ | |
WIN32_FIND_DATAA fd; | |
if (!::FindNextFileA(_handle, &fd)) | |
return false; | |
ConvertWIN32_FIND_DATA_To_FileInfo(fd, fi); | |
} | |
return true; | |
} | |
#endif | |
#define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0; | |
void CFileInfoBase::Clear() | |
{ | |
Size = 0; | |
MY_CLEAR_FILETIME(CTime); | |
MY_CLEAR_FILETIME(ATime); | |
MY_CLEAR_FILETIME(MTime); | |
Attrib = 0; | |
} | |
bool CFileInfo::Find(LPCTSTR wildcard) | |
{ | |
#ifdef SUPPORT_DEVICE_FILE | |
if (IsDeviceName(wildcard)) | |
{ | |
Clear(); | |
IsDevice = true; | |
NIO::CInFile inFile; | |
if (!inFile.Open(wildcard)) | |
return false; | |
Name = wildcard + 4; | |
if (inFile.LengthDefined) | |
Size = inFile.Length; | |
return true; | |
} | |
#endif | |
CFindFile finder; | |
return finder.FindFirst(wildcard, *this); | |
} | |
#ifndef _UNICODE | |
bool CFileInfoW::Find(LPCWSTR wildcard) | |
{ | |
#ifdef SUPPORT_DEVICE_FILE | |
if (IsDeviceName(wildcard)) | |
{ | |
Clear(); | |
IsDevice = true; | |
NIO::CInFile inFile; | |
if (!inFile.Open(wildcard)) | |
return false; | |
Name = wildcard + 4; | |
if (inFile.LengthDefined) | |
Size = inFile.Length; | |
return true; | |
} | |
#endif | |
CFindFile finder; | |
return finder.FindFirst(wildcard, *this); | |
} | |
#endif | |
bool DoesFileExist(LPCTSTR name) | |
{ | |
CFileInfo fi; | |
return fi.Find(name) && !fi.IsDir(); | |
} | |
bool DoesDirExist(LPCTSTR name) | |
{ | |
CFileInfo fi; | |
return fi.Find(name) && fi.IsDir(); | |
} | |
bool DoesFileOrDirExist(LPCTSTR name) | |
{ | |
CFileInfo fi; | |
return fi.Find(name); | |
} | |
#ifndef _UNICODE | |
bool DoesFileExist(LPCWSTR name) | |
{ | |
CFileInfoW fi; | |
return fi.Find(name) && !fi.IsDir(); | |
} | |
bool DoesDirExist(LPCWSTR name) | |
{ | |
CFileInfoW fi; | |
return fi.Find(name) && fi.IsDir(); | |
} | |
bool DoesFileOrDirExist(LPCWSTR name) | |
{ | |
CFileInfoW fi; | |
return fi.Find(name); | |
} | |
#endif | |
///////////////////////////////////// | |
// CEnumerator | |
bool CEnumerator::NextAny(CFileInfo &fi) | |
{ | |
if (_findFile.IsHandleAllocated()) | |
return _findFile.FindNext(fi); | |
else | |
return _findFile.FindFirst(_wildcard, fi); | |
} | |
bool CEnumerator::Next(CFileInfo &fi) | |
{ | |
for (;;) | |
{ | |
if (!NextAny(fi)) | |
return false; | |
if (!fi.IsDots()) | |
return true; | |
} | |
} | |
bool CEnumerator::Next(CFileInfo &fi, bool &found) | |
{ | |
if (Next(fi)) | |
{ | |
found = true; | |
return true; | |
} | |
found = false; | |
return (::GetLastError() == ERROR_NO_MORE_FILES); | |
} | |
#ifndef _UNICODE | |
bool CEnumeratorW::NextAny(CFileInfoW &fi) | |
{ | |
if (_findFile.IsHandleAllocated()) | |
return _findFile.FindNext(fi); | |
else | |
return _findFile.FindFirst(_wildcard, fi); | |
} | |
bool CEnumeratorW::Next(CFileInfoW &fi) | |
{ | |
for (;;) | |
{ | |
if (!NextAny(fi)) | |
return false; | |
if (!fi.IsDots()) | |
return true; | |
} | |
} | |
bool CEnumeratorW::Next(CFileInfoW &fi, bool &found) | |
{ | |
if (Next(fi)) | |
{ | |
found = true; | |
return true; | |
} | |
found = false; | |
return (::GetLastError() == ERROR_NO_MORE_FILES); | |
} | |
#endif | |
//////////////////////////////// | |
// CFindChangeNotification | |
// FindFirstChangeNotification can return 0. MSDN doesn't tell about it. | |
bool CFindChangeNotification::Close() | |
{ | |
if (!IsHandleAllocated()) | |
return true; | |
if (!::FindCloseChangeNotification(_handle)) | |
return false; | |
_handle = INVALID_HANDLE_VALUE; | |
return true; | |
} | |
HANDLE CFindChangeNotification::FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter) | |
{ | |
_handle = ::FindFirstChangeNotification(pathName, BoolToBOOL(watchSubtree), notifyFilter); | |
#ifdef WIN_LONG_PATH2 | |
if (!IsHandleAllocated()) | |
{ | |
UString longPath; | |
if (GetLongPath(pathName, longPath)) | |
_handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); | |
} | |
#endif | |
return _handle; | |
} | |
#ifndef _UNICODE | |
HANDLE CFindChangeNotification::FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter) | |
{ | |
if (!g_IsNT) | |
return FindFirst(UnicodeStringToMultiByte(pathName, GetCurrentCodePage()), watchSubtree, notifyFilter); | |
_handle = ::FindFirstChangeNotificationW(pathName, BoolToBOOL(watchSubtree), notifyFilter); | |
#ifdef WIN_LONG_PATH | |
if (!IsHandleAllocated()) | |
{ | |
UString longPath; | |
if (GetLongPath(pathName, longPath)) | |
_handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter); | |
} | |
#endif | |
return _handle; | |
} | |
#endif | |
#ifndef UNDER_CE | |
bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings) | |
{ | |
driveStrings.Clear(); | |
UINT32 size = GetLogicalDriveStrings(0, NULL); | |
if (size == 0) | |
return false; | |
CSysString buffer; | |
UINT32 newSize = GetLogicalDriveStrings(size, buffer.GetBuffer(size)); | |
if (newSize == 0) | |
return false; | |
if (newSize > size) | |
return false; | |
CSysString string; | |
for (UINT32 i = 0; i < newSize; i++) | |
{ | |
TCHAR c = buffer[i]; | |
if (c == TEXT('\0')) | |
{ | |
driveStrings.Add(string); | |
string.Empty(); | |
} | |
else | |
string += c; | |
} | |
if (!string.IsEmpty()) | |
return false; | |
return true; | |
} | |
#ifndef _UNICODE | |
bool MyGetLogicalDriveStrings(UStringVector &driveStrings) | |
{ | |
driveStrings.Clear(); | |
if (g_IsNT) | |
{ | |
UINT32 size = GetLogicalDriveStringsW(0, NULL); | |
if (size == 0) | |
return false; | |
UString buffer; | |
UINT32 newSize = GetLogicalDriveStringsW(size, buffer.GetBuffer(size)); | |
if (newSize == 0) | |
return false; | |
if (newSize > size) | |
return false; | |
UString string; | |
for (UINT32 i = 0; i < newSize; i++) | |
{ | |
WCHAR c = buffer[i]; | |
if (c == L'\0') | |
{ | |
driveStrings.Add(string); | |
string.Empty(); | |
} | |
else | |
string += c; | |
} | |
return string.IsEmpty(); | |
} | |
CSysStringVector driveStringsA; | |
bool res = MyGetLogicalDriveStrings(driveStringsA); | |
for (int i = 0; i < driveStringsA.Size(); i++) | |
driveStrings.Add(GetUnicodeString(driveStringsA[i])); | |
return res; | |
} | |
#endif | |
#endif | |
}}} |