summaryrefslogtreecommitdiff
path: root/zen/file_traverser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zen/file_traverser.cpp')
-rw-r--r--zen/file_traverser.cpp135
1 files changed, 75 insertions, 60 deletions
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 8a8a8b6f..236581a3 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -31,13 +31,13 @@ namespace
//implement "retry" in a generic way:
template <class Command> inline //function object expecting to throw FileError if operation fails
-void tryReportingError(Command cmd, zen::TraverseCallback& callback)
+bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored
{
for (;;)
try
{
cmd();
- return;
+ return true;
}
catch (const FileError& e)
{
@@ -46,7 +46,7 @@ void tryReportingError(Command cmd, zen::TraverseCallback& callback)
case TraverseCallback::TRAV_ERROR_RETRY:
break;
case TraverseCallback::TRAV_ERROR_IGNORE:
- return;
+ return false;
default:
assert(false);
break;
@@ -119,18 +119,17 @@ const DllFun<findplus::OpenDirFunc> openDir (findplus::getDllName(), findplus::
const DllFun<findplus::ReadDirFunc> readDir (findplus::getDllName(), findplus::readDirFuncName ); //load at startup: avoid pre C++11 static initialization MT issues
const DllFun<findplus::CloseDirFunc> closeDir(findplus::getDllName(), findplus::closeDirFuncName); //
-
/*
Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir():
-struct X //see "policy based design"
+struct TraverserPolicy //see "policy based design"
{
-typedef ... Handle;
+typedef ... DirHandle;
typedef ... FindData;
-static Handle create(const Zstring& directoryPf, FindData& fileInfo); //throw FileError - concession to FindFirstFile(): implement two operations: 1. open handle, 2. retrieve first data set
-static void destroy(Handle hnd); //throw()
-static bool next(Handle hnd, const Zstring& directory, WIN32_FIND_DATA& fileInfo) //throw FileError
+static void create(const Zstring& directory, DirHandle& hnd); //throw FileError - *no* concession to FindFirstFile(): open handle only, *no* return of data!
+static void destroy(DirHandle hnd); //throw()
+static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
-//helper routines
+//FindData "member" functions
static void extractFileInfo (const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output);
static Int64 getModTime (const FindData& fileInfo);
static const FILETIME& getModTimeRaw (const FindData& fileInfo); //yet another concession to DST hack
@@ -140,35 +139,54 @@ static bool isDirectory (const FindData& fileInfo);
static bool isSymlink (const FindData& fileInfo);
}
-Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat(),
- so unfortunately we have to use former as a greatest common divisor
+Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat()
*/
struct Win32Traverser
{
- typedef HANDLE Handle;
+ struct DirHandle
+ {
+ DirHandle() : searchHandle(NULL), firstRead(true) {}
+
+ HANDLE searchHandle;
+ bool firstRead;
+ WIN32_FIND_DATA firstData;
+ };
+
typedef WIN32_FIND_DATA FindData;
- static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
+ static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
{
const Zstring& directoryPf = endsWith(directory, FILE_NAME_SEPARATOR) ?
directory :
directory + FILE_NAME_SEPARATOR;
- HANDLE output = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &fileInfo);
+ hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.firstData);
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
- if (output == INVALID_HANDLE_VALUE)
+ if (hnd.searchHandle == INVALID_HANDLE_VALUE)
throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
- //::GetLastError() == ERROR_FILE_NOT_FOUND -> actually NOT okay, even for an empty directory this should not occur (., ..)
- return output;
+
+ //::GetLastError() == ERROR_FILE_NOT_FOUND -> *usually* NOT okay:
+ //however it is unclear whether this indicates a missing directory or a completely empty directory
+ // note: not all directories contain "., .." entries! E.g. a drive's root directory or NetDrive + ftp.gnu.org\CRYPTO.README"
+ // -> addon: this is NOT a directory, it looks like one in NetDrive, but it's a file in Opera!
+ //we have to guess it's former and let the error propagate
+ // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility and its consequences
}
- static void destroy(Handle hnd) { ::FindClose(hnd); } //throw()
+ static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw()
- static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
{
- if (!::FindNextFile(hnd, &fileInfo))
+ if (hnd.firstRead)
+ {
+ hnd.firstRead = false;
+ ::memcpy(&fileInfo, &hnd.firstData, sizeof(fileInfo));
+ return true;
+ }
+
+ if (!::FindNextFile(hnd.searchHandle, &fileInfo))
{
if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
@@ -178,7 +196,6 @@ struct Win32Traverser
return true;
}
- //helper routines
template <class FindData>
static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
{
@@ -208,27 +225,27 @@ struct Win32Traverser
struct FilePlusTraverser
{
- typedef findplus::FindHandle Handle;
+ struct DirHandle
+ {
+ DirHandle() : searchHandle(NULL) {}
+
+ findplus::FindHandle searchHandle;
+ };
+
typedef findplus::FileInformation FindData;
- static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
+ static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
{
- Handle output = ::openDir(applyLongPathPrefix(directory).c_str());
- if (output == NULL)
+ hnd.searchHandle = ::openDir(applyLongPathPrefix(directory).c_str());
+ if (hnd.searchHandle == NULL)
throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
-
- bool rv = next(output, directory, fileInfo); //throw FileError
- if (!rv) //we expect at least two successful reads, even for an empty directory: ., ..
- throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(ERROR_NO_MORE_FILES));
-
- return output;
}
- static void destroy(Handle hnd) { ::closeDir(hnd); } //throw()
+ static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
- static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
{
- if (!::readDir(hnd, fileInfo))
+ if (!::readDir(hnd.searchHandle, fileInfo))
{
if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
@@ -238,7 +255,6 @@ struct FilePlusTraverser
return true;
}
- //helper routines
template <class FindData>
static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
{
@@ -305,31 +321,43 @@ private:
throw FileError(_("Endless loop when traversing directory:") + L"\n\"" + directory + L"\"");
}, sink);
- typename Trav::FindData fileInfo = {};
-
- typename Trav::Handle searchHandle = 0;
+ typename Trav::DirHandle searchHandle;
- tryReportingError([&]
+ const bool openSuccess = tryReportingError([&]
{
typedef Trav Trav; //f u VS!
- searchHandle = Trav::create(directory, fileInfo); //throw FileError
+ Trav::create(directory, searchHandle); //throw FileError
}, sink);
- if (searchHandle == 0)
+ if (!openSuccess)
return; //ignored error
ZEN_ON_BLOCK_EXIT(typedef Trav Trav; Trav::destroy(searchHandle));
- do
+ typename Trav::FindData fileInfo = {};
+
+ while ([&]() -> bool
+ {
+ bool moreData = false;
+
+ typedef Trav Trav1; //f u VS!
+ tryReportingError([&]
{
- //don't return "." and ".."
+ typedef Trav1 Trav; //f u VS!
+ moreData = Trav::getEntry(searchHandle, directory, fileInfo); //throw FileError
+ }, sink);
+
+ return moreData;
+ }())
+ {
+ //skip "." and ".."
const Zchar* const shortName = Trav::getShortName(fileInfo);
if (shortName[0] == L'.' &&
- (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
+ (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
continue;
const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ?
- directory + shortName :
- directory + FILE_NAME_SEPARATOR + shortName;
+ directory + shortName :
+ directory + FILE_NAME_SEPARATOR + shortName;
if (Trav::isSymlink(fileInfo) && !followSymlinks_) //evaluate symlink directly
{
@@ -383,19 +411,6 @@ private:
sink.onFile(shortName, fullName, details);
}
}
- while ([&]() -> bool
- {
- bool moreData = false;
-
- typedef Trav Trav1; //f u VS!
- tryReportingError([&]
- {
- typedef Trav1 Trav; //f u VS!
- moreData = Trav::next(searchHandle, directory, fileInfo); //throw FileError
- }, sink);
-
- return moreData;
- }());
}
bgstack15