diff options
author | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:57:46 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2015-10-02 14:57:46 +0200 |
commit | ad4e3d2c55e75193c41356c23619f80add41db18 (patch) | |
tree | dd836d120f50e472106e04968ef8185c25e4242e /zen | |
parent | 7.4 (diff) | |
download | FreeFileSync-ad4e3d2c55e75193c41356c23619f80add41db18.tar.gz FreeFileSync-ad4e3d2c55e75193c41356c23619f80add41db18.tar.bz2 FreeFileSync-ad4e3d2c55e75193c41356c23619f80add41db18.zip |
7.5
Diffstat (limited to 'zen')
-rw-r--r-- | zen/async_task.h | 73 | ||||
-rw-r--r-- | zen/build_info.h | 34 | ||||
-rw-r--r-- | zen/dir_watcher.cpp | 32 | ||||
-rw-r--r-- | zen/dir_watcher.h | 16 | ||||
-rw-r--r-- | zen/file_access.cpp | 289 | ||||
-rw-r--r-- | zen/file_error.h | 23 | ||||
-rw-r--r-- | zen/file_io.cpp | 20 | ||||
-rw-r--r-- | zen/file_traverser.cpp | 60 | ||||
-rw-r--r-- | zen/file_traverser.h | 3 | ||||
-rw-r--r-- | zen/fixed_list.h | 11 | ||||
-rw-r--r-- | zen/i18n.h | 1 | ||||
-rw-r--r-- | zen/long_path_prefix.h | 6 | ||||
-rw-r--r-- | zen/process_priority.cpp | 50 | ||||
-rw-r--r-- | zen/recycler.cpp | 12 | ||||
-rw-r--r-- | zen/recycler.h | 12 | ||||
-rw-r--r-- | zen/scope_guard.h | 10 | ||||
-rw-r--r-- | zen/serialize.h | 2 | ||||
-rw-r--r-- | zen/shell_execute.h | 26 | ||||
-rw-r--r-- | zen/stl_tools.h | 38 | ||||
-rw-r--r-- | zen/string_base.h | 29 | ||||
-rw-r--r-- | zen/string_tools.h | 8 | ||||
-rw-r--r-- | zen/string_traits.h | 18 | ||||
-rw-r--r-- | zen/symlink_target.h | 24 | ||||
-rw-r--r-- | zen/thread.h | 52 | ||||
-rw-r--r-- | zen/tick_count.h | 8 | ||||
-rw-r--r-- | zen/time.h | 14 | ||||
-rw-r--r-- | zen/type_traits.h | 80 | ||||
-rw-r--r-- | zen/warn_static.h | 2 | ||||
-rw-r--r-- | zen/zstring.cpp | 7 |
29 files changed, 478 insertions, 482 deletions
diff --git a/zen/async_task.h b/zen/async_task.h deleted file mode 100644 index 76e7824f..00000000 --- a/zen/async_task.h +++ /dev/null @@ -1,73 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * -// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ASYNC_JOB_839147839170432143214321 -#define ASYNC_JOB_839147839170432143214321 - -#include <list> -#include <functional> -#include "thread.h" -#include "scope_guard.h" - - -namespace zen -{ -//run a job in an async thread, but process result on GUI event loop -class AsyncTasks -{ -public: - AsyncTasks() {} - - template <class Fun, class Fun2> - void add(Fun runAsync, Fun2 evalOnGui) - //equivalent to "evalOnGui(runAsync())" - // -> runAsync: the usual thread-safety requirements apply! - // -> evalOnGui: no thread-safety concerns, but must only reference variables with greater-equal lifetime than the AsyncTask instance! - { - tasks.push_back(zen::runAsync([=]() -> std::function<void()> - { - auto result = runAsync(); - return [=]{ evalOnGui(result); }; - })); - } - - template <class Fun, class Fun2> - void add2(Fun runAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters - { - tasks.push_back(zen::runAsync([runAsync, evalOnGui]() -> std::function<void()> { runAsync(); return [evalOnGui]{ evalOnGui(); }; })); - } - - void evalResults() //call from gui thread repreatedly - { - if (!inRecursion) //prevent implicit recursion, e.g. if we're called from an idle event and spawn another one via the callback below - { - inRecursion = true; - ZEN_ON_SCOPE_EXIT(inRecursion = false); - - tasks.remove_if([](std::future<std::function<void()>>& ft) -> bool - { - if (isReady(ft)) - { - (ft.get())(); - return true; - } - return false; - }); - } - } - - bool empty() const { return tasks.empty(); } - -private: - AsyncTasks (const AsyncTasks&) = delete; - AsyncTasks& operator=(const AsyncTasks&) = delete; - - bool inRecursion = false; - std::list<std::future<std::function<void()>>> tasks; -}; -} - -#endif //ASYNC_JOB_839147839170432143214321 diff --git a/zen/build_info.h b/zen/build_info.h index 4eeb8195..7c738847 100644 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -4,17 +4,35 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef BUILDINFO_H_INCLUDED -#define BUILDINFO_H_INCLUDED +#ifndef BUILDINFO_H_5928539285603428657 +#define BUILDINFO_H_5928539285603428657 namespace zen { -//determine build info -//safer than checking for _WIN64 (defined on windows for 64-bit compilations only) while _WIN32 is always defined (even for x64 compiler!) -static const bool is32BitBuild = sizeof(void*) == 4; -static const bool is64BitBuild = sizeof(void*) == 8; +//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT -static_assert(is32BitBuild || is64BitBuild, ""); +#ifdef ZEN_WIN + #ifdef _WIN64 + #define ZEN_BUILD_64BIT + #else + #define ZEN_BUILD_32BIT + #endif + +#else + #ifdef __LP64__ + #define ZEN_BUILD_64BIT + #else + #define ZEN_BUILD_32BIT + #endif +#endif + +#ifdef ZEN_BUILD_32BIT + static_assert(sizeof(void*) == 4, ""); +#endif + +#ifdef ZEN_BUILD_64BIT + static_assert(sizeof(void*) == 8, ""); +#endif } -#endif //BUILDINFO_H_INCLUDED +#endif //BUILDINFO_H_5928539285603428657 diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 4abf3c0a..3bab8d34 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -121,7 +121,7 @@ public: std::lock_guard<std::mutex> dummy(lockAccess); ErrorInfo newInfo = { copyStringTo<BasicWString>(msg), copyStringTo<BasicWString>(description), errorCode }; - errorInfo = make_unique<ErrorInfo>(newInfo); + errorInfo = std::make_unique<ErrorInfo>(newInfo); } private: @@ -159,7 +159,7 @@ public: FILE_FLAG_OVERLAPPED, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (hDir == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(directory)), L"CreateFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(directory)), L"CreateFile"); //end of constructor, no need to start managing "hDir" } @@ -193,7 +193,7 @@ public: nullptr); //__in_opt LPCTSTR lpName if (overlapped.hEvent == nullptr) { - const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(dirpathPf)), formatSystemError(L"CreateEvent", ec), ec); } ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); @@ -213,7 +213,7 @@ public: &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine { - const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", ec), ec); } @@ -239,7 +239,7 @@ public: &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred, false)) //__in BOOL bWait { - const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! if (ec != ERROR_IO_INCOMPLETE) return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(dirpathPf)), formatSystemError(L"GetOverlappedResult", ec), ec); @@ -327,12 +327,12 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError baseDirPath(dirPath), - pimpl_(zen::make_unique<Pimpl>()) + pimpl_(std::make_unique<Pimpl>()) { pimpl_->shared = std::make_shared<SharedData>(); ReadChangesAsync reader(dirPath, pimpl_->shared); //throw FileError - pimpl_->volRemoval = zen::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), dirPath, pimpl_->worker); //throw FileError + pimpl_->volRemoval = std::make_unique<HandleVolumeRemoval>(reader.getDirHandle(), dirPath, pimpl_->worker); //throw FileError pimpl_->worker = InterruptibleThread(std::move(reader)); } @@ -384,7 +384,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError baseDirPath(dirPath), - pimpl_(zen::make_unique<Pimpl>()) + pimpl_(std::make_unique<Pimpl>()) { //get all subdirectories std::vector<Zstring> fullDirList { baseDirPath }; @@ -405,7 +405,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError //init pimpl_->notifDescr = ::inotify_init(); if (pimpl_->notifDescr == -1) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init"); zen::ScopeGuard guardDescr = zen::makeGuard([&] { ::close(pimpl_->notifDescr); }); @@ -417,7 +417,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1; } if (!initSuccess) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl"); //add watches for (const Zstring& subDirPath : fullDirList) @@ -425,17 +425,17 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(), IN_ONLYDIR | //"Only watch pathname if it is a directory." IN_DONT_FOLLOW | //don't follow symbolic links - IN_CREATE | - IN_MODIFY | + IN_CREATE | + IN_MODIFY | IN_CLOSE_WRITE | - IN_DELETE | + IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_MOVE_SELF); if (wd == -1) { - const auto ec = getLastError(); + const ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls! if (ec == ENOSPC) //fix misleading system message "No space left on device" throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource.")); @@ -473,7 +473,7 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found return std::vector<Entry>(); - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read"); } std::vector<Entry> output; @@ -565,7 +565,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& dirPath) : baseDirPath(dirPath), - pimpl_(zen::make_unique<Pimpl>()) + pimpl_(std::make_unique<Pimpl>()) { CFStringRef dirpathCf = osx::createCFString(baseDirPath.c_str()); //returns nullptr on error if (!dirpathCf) diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h index 7a1ada96..1d6d53cc 100644 --- a/zen/dir_watcher.h +++ b/zen/dir_watcher.h @@ -22,18 +22,18 @@ namespace zen //watch directory including subdirectories /* !Note handling of directories!: - Windows: removal of top watched directory is NOT notified when watching the dir handle, e.g. brute force usb stick removal, - (watchting for GUID_DEVINTERFACE_WPD OTOH works fine!) - however manual unmount IS notified (e.g. usb stick removal, then re-insert), but watching is stopped! - Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders - now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!) + Windows: removal of top watched directory is NOT notified when watching the dir handle, e.g. brute force usb stick removal, + (watchting for GUID_DEVINTERFACE_WPD OTOH works fine!) + however manual unmount IS notified (e.g. usb stick removal, then re-insert), but watching is stopped! + Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders + now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!) Linux: newly added subdirectories are reported but not automatically added for watching! -> reset Dirwatcher! - removal of top watched directory is NOT notified! + removal of top watched directory is NOT notified! - OS X: everything works as expected; renaming of top level folder is also detected + OS X: everything works as expected; renaming of top level folder is also detected - Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected + Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected */ class DirWatcher { diff --git a/zen/file_access.cpp b/zen/file_access.cpp index c898c5d2..e04673d3 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -21,7 +21,7 @@ #include "long_path_prefix.h" #include "win_ver.h" #ifdef ZEN_WIN_VISTA_AND_LATER - #include <zen/vista_file_op.h> + #include <zen/vista_file_op.h> //requires COM initialization! #endif @@ -105,14 +105,14 @@ bool zen::somethingExists(const Zstring& itemPath) const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(itemPath).c_str()); if (attr != INVALID_FILE_ATTRIBUTES) return true; - const DWORD lastError = ::GetLastError(); + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! //handle obscure file permission problem where ::GetFileAttributes() fails with ERROR_ACCESS_DENIED or ERROR_SHARING_VIOLATION //while parent directory traversal is successful: e.g. "C:\pagefile.sys" - if (lastError != ERROR_PATH_NOT_FOUND && //perf: short circuit for common "not existing" error codes - lastError != ERROR_FILE_NOT_FOUND && // - lastError != ERROR_BAD_NETPATH && // - lastError != ERROR_BAD_NET_NAME) // + if (ec != ERROR_PATH_NOT_FOUND && //perf: short circuit for common "not existing" error codes + ec != ERROR_FILE_NOT_FOUND && // + ec != ERROR_BAD_NETPATH && // + ec != ERROR_BAD_NET_NAME) // { WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(itemPath).c_str(), &fileInfo); @@ -179,7 +179,7 @@ std::uint64_t zen::getFilesize(const Zstring& filePath) //throw FileError WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filePath).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"FindFirstFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"FindFirstFile"); ::FindClose(searchHandle); if (!isSymlink(fileInfo)) @@ -199,20 +199,20 @@ std::uint64_t zen::getFilesize(const Zstring& filePath) //throw FileError FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (hFile == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"CreateFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"CreateFile"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); //why not use ::GetFileSizeEx() instead??? BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileInformationByHandle"); return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); #elif defined ZEN_LINUX || defined ZEN_MAC struct ::stat fileInfo = {}; if (::stat(filePath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat"); return fileInfo.st_size; #endif @@ -227,7 +227,7 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu &bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable, nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes, nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes - throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"GetDiskFreeSpaceEx", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"GetDiskFreeSpaceEx"); //return 0 if info is not available: "The GetDiskFreeSpaceEx function returns zero for lpFreeBytesAvailable for all CD requests" return get64BitUInt(bytesFree.LowPart, bytesFree.HighPart); @@ -235,7 +235,7 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu #elif defined ZEN_LINUX || defined ZEN_MAC struct ::statfs info = {}; if (::statfs(path.c_str(), &info) != 0) - throwFileError(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"statfs", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"statfs"); return static_cast<std::uint64_t>(info.f_bsize) * info.f_bavail; #endif @@ -253,15 +253,15 @@ bool zen::removeFile(const Zstring& filePath) //throw FileError if (::unlink(filePath.c_str()) != 0) #endif { - ErrorCode lastError = getLastError(); + ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls! #ifdef ZEN_WIN - if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only + if (ec == ERROR_ACCESS_DENIED) //function fails if file is read-only { ::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes if (::DeleteFile(applyLongPathPrefix(filePath).c_str())) //now try again... return true; - lastError = ::GetLastError(); + ec = ::GetLastError(); } #endif if (!somethingExists(filePath)) //warning: changes global error code!! @@ -269,11 +269,11 @@ bool zen::removeFile(const Zstring& filePath) //throw FileError //begin of "regular" error reporting const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath)); - std::wstring errorDescr = formatSystemError(functionName, lastError); + std::wstring errorDescr = formatSystemError(functionName, ec); #ifdef ZEN_WIN_VISTA_AND_LATER - if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! - lastError == ERROR_LOCK_VIOLATION) + if (ec == ERROR_SHARING_VIOLATION || //-> enhance error message! + ec == ERROR_LOCK_VIOLATION) { const std::wstring procList = vista::getLockingProcesses(filePath); //noexcept if (!procList.empty()) @@ -299,7 +299,7 @@ void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError if (::rmdir(dirPath.c_str()) != 0) #endif { - const ErrorCode ec = getLastError(); + const ErrorCode ec = getLastError(); //copy before making other system calls! if (!somethingExists(dirPath)) //warning: changes global error code!! return; @@ -308,12 +308,12 @@ void zen::removeDirectorySimple(const Zstring& dirPath) //throw FileError if (symlinkExists(dirPath)) { if (::unlink(dirPath.c_str()) != 0) - throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink"); return; } #endif - throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), functionName, ec); + throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(functionName, ec)); } //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have //successfully been *marked* for deletion, but some application still has a handle open! @@ -332,10 +332,11 @@ void removeDirectoryImpl(const Zstring& dirPath) //throw FileError std::vector<Zstring> fileList; std::vector<Zstring> dirLinkList; std::vector<Zstring> dirList; + //get all files and directories from current directory (WITHOUT subdirectories!) traverseFolder(dirPath, [&](const FileInfo& fi) { fileList.push_back(fi.fullPath); }, - [&](const DirInfo& di) { dirList .push_back(di.fullPath); }, + [&](const DirInfo& di) { dirList .push_back(di.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies! [&](const SymlinkInfo& si) { #ifdef ZEN_WIN @@ -391,9 +392,9 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro pathTargetFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, 0)) //__in DWORD dwFlags { - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! - if (lastError == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this + if (ec == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this { const DWORD oldAttr = ::GetFileAttributes(pathSourceFmt.c_str()); if (oldAttr != INVALID_FILE_ATTRIBUTES && (oldAttr & FILE_ATTRIBUTE_READONLY)) @@ -411,7 +412,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro } else { - lastError = ::GetLastError(); //use error code from second call to ::MoveFileEx() + ec = ::GetLastError(); //use error code from second call to ::MoveFileEx() //cleanup: (try to) restore file attributes: assume pathSource is still existing ::SetFileAttributes(pathSourceFmt.c_str(), oldAttr); } @@ -420,11 +421,11 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro } //begin of "regular" error reporting const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget)); - std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError); + std::wstring errorDescr = formatSystemError(L"MoveFileEx", ec); #ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) + if (ec == ERROR_SHARING_VIOLATION || + ec == ERROR_LOCK_VIOLATION) { const std::wstring procList = vista::getLockingProcesses(pathSource); //noexcept if (!procList.empty()) @@ -432,37 +433,37 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro } #endif - if (lastError == ERROR_NOT_SAME_DEVICE) + if (ec == ERROR_NOT_SAME_DEVICE) throw ErrorDifferentVolume(errorMsg, errorDescr); - if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 - lastError == ERROR_FILE_EXISTS) //-> used by XP??? + if (ec == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 + ec == ERROR_FILE_EXISTS) //-> used by XP??? throw ErrorTargetExisting(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } #elif defined ZEN_LINUX || defined ZEN_MAC - //rename() will never fail with EEXIST, but always overwrite! - //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy - //=> OS X: no solution + //rename() will never fail with EEXIST, but always overwrite! + //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy + //=> OS X: no solution - auto throwException = [&](int ec) - { + auto throwException = [&](int ec) + { const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget)); const std::wstring errorDescr = formatSystemError(L"rename", ec); if (ec == EXDEV) throw ErrorDifferentVolume(errorMsg, errorDescr); - if (ec == EEXIST) - throw ErrorTargetExisting(errorMsg, errorDescr); + if (ec == EEXIST) + throw ErrorTargetExisting(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); - }; + }; - if (!EqualFilePath()(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! - if (somethingExists(pathTarget)) - throwException(EEXIST); + if (!EqualFilePath()(pathSource, pathTarget)) //OS X: changing file name case is not an "already exists" error! + if (somethingExists(pathTarget)) + throwException(EEXIST); if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0) - throwException(errno); + throwException(errno); #endif } @@ -645,12 +646,12 @@ void setFileTimeRaw(const Zstring& filePath, { const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filePath).c_str()); if (tmpAttr == INVALID_FILE_ATTRIBUTES) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileAttributes", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"GetFileAttributes"); if (tmpAttr & FILE_ATTRIBUTE_READONLY) { if (!::SetFileAttributes(applyLongPathPrefix(filePath).c_str(), FILE_ATTRIBUTE_NORMAL)) - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileAttributes", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileAttributes"); attribs = tmpAttr; //reapplied on scope exit return true; @@ -694,13 +695,13 @@ void setFileTimeRaw(const Zstring& filePath, hFile = openFile(false); if (hFile == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError == ERROR_ACCESS_DENIED) + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! + if (ec == ERROR_ACCESS_DENIED) if (removeReadonly()) //throw FileError continue; //3. after these herculean stunts we give up... - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"CreateFile", lastError); + throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), formatSystemError(L"CreateFile", ec)); } } break; @@ -713,10 +714,10 @@ void setFileTimeRaw(const Zstring& filePath, nullptr, //__in_opt const FILETIME *lpLastAccessTime, &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime { - ErrorCode lastError = getLastError(); //copy before directly or indirectly making other system calls! + DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! //function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - if (lastError == ERROR_ACCESS_DENIED) + if (ec == ERROR_ACCESS_DENIED) { //dynamically load windows API function: available with Windows Vista and later typedef BOOL (WINAPI* SetFileInformationByHandleFunc)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); @@ -730,7 +731,7 @@ void setFileTimeRaw(const Zstring& filePath, FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, &basicInfo, //__in LPVOID lpFileInformation, sizeof(basicInfo))) //__in DWORD dwBufferSize - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileInformationByHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(filePath)), L"SetFileInformationByHandle"); }; auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER @@ -763,7 +764,7 @@ void setFileTimeRaw(const Zstring& filePath, } catch (FileError&) {} - lastError = ERROR_SUCCESS; + ec = ERROR_SUCCESS; } } } @@ -771,7 +772,7 @@ void setFileTimeRaw(const Zstring& filePath, std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)); //add more meaningful message: FAT accepts only a subset of the NTFS date range - if (lastError == ERROR_INVALID_PARAMETER && + if (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath)) { //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! @@ -790,10 +791,10 @@ void setFileTimeRaw(const Zstring& filePath, std::vector<wchar_t> buffer(bufferSize); if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale, 0, //_In_ DWORD dwFlags, - &st, //_In_opt_ const SYSTEMTIME *lpDate, - nullptr, //_In_opt_ LPCTSTR lpFormat, - &buffer[0], //_Out_opt_ LPTSTR lpDateStr, - bufferSize) > 0) //_In_ int cchDate + &st, //_In_opt_ const SYSTEMTIME *lpDate, + nullptr, //_In_opt_ LPCTSTR lpFormat, + &buffer[0], //_Out_opt_ LPTSTR lpDateStr, + bufferSize) > 0) //_In_ int cchDate dateTime = &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! } } @@ -816,8 +817,8 @@ void setFileTimeRaw(const Zstring& filePath, (creationTime ? L"\n\tcreate (UTC): \t" + fmtDate(*creationTime) : L""); } - if (lastError != ERROR_SUCCESS) - throwFileError(errorMsg, L"SetFileTime", lastError); + if (ec != ERROR_SUCCESS) + throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec)); } } #ifndef NDEBUG //verify written data: mainly required to check consistency of DST hack @@ -877,17 +878,17 @@ void setFileTimeRaw(const Zstring& filePath, const struct ::timespec& modTime, P if (errno == EACCES) //bullshit, access denied even with 0777 permissions! => utimes should work! throw ErrorLinuxFallbackToUtimes(L""); - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"open", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"open"); } ZEN_ON_SCOPE_EXIT(::close(fdFile)); if (::futimens(fdFile, newTimes) != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"futimens", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"futimens"); } else { if (::utimensat(AT_FDCWD, filePath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimensat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimensat"); } } @@ -929,7 +930,7 @@ void setFileTimeRaw(const Zstring& filePath, (createTime ? sizeof(newTimes.createTime) : 0) + sizeof(newTimes.writeTime), //size_t attrBufSize, procSl == ProcSymlink::DIRECT ? FSOPT_NOFOLLOW : 0); //unsigned long options if (rv != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"setattrlist", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"setattrlist"); } /* @@ -951,7 +952,7 @@ void getFileTimeRaw(int fd, //throw FileError sizeof(fileTimes), //size_t attrBufSize, 0); //unsigned long options if (rv != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"getattrlist", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"getattrlist"); createTime.tv_sec = fileTimes.createTime.tv_sec; createTime.tv_nsec = fileTimes.createTime.tv_nsec; @@ -984,12 +985,12 @@ void zen::setFileTime(const Zstring& filePath, std::int64_t modTime, ProcSymlink if (procSl == ProcSymlink::FOLLOW) { if (::utimes(filePath.c_str(), writeTime) != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimes", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"utimes"); } else { if (::lutimes(filePath.c_str(), writeTime) != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"lutimes", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(filePath)), L"lutimes"); } } @@ -1010,7 +1011,7 @@ bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError if (!::GetVolumePathName(dirpath.c_str(), //__in LPCTSTR lpszFileName, &buffer[0], //__out LPTSTR lpszVolumePathName, bufferSize)) //__in DWORD cchBufferLength - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumePathName", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumePathName"); const Zstring volumePath = appendSeparator(&buffer[0]); @@ -1023,7 +1024,7 @@ bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, nullptr, //__out LPTSTR lpFileSystemNameBuffer, 0)) //__in DWORD nFileSystemNameSize - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumeInformation", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(dirpath)), L"GetVolumeInformation"); return (fsFlags & FILE_PERSISTENT_ACLS) != 0; @@ -1049,7 +1050,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem return; - throwFileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtPath(source)), L"getfilecon", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtPath(source)), L"getfilecon"); } ZEN_ON_SCOPE_EXIT(::freecon(contextSource)); @@ -1077,7 +1078,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli ::setfilecon(target.c_str(), contextSource) : ::lsetfilecon(target.c_str(), contextSource); if (rv3 < 0) - throwFileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon"); } #endif //HAVE_SELINUX @@ -1128,7 +1129,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (bytesNeeded > buffer.size()) buffer.resize(bytesNeeded); else - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourceResolved)), L"GetFileSecurity", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourceResolved)), L"GetFileSecurity"); } SECURITY_DESCRIPTOR& secDescr = reinterpret_cast<SECURITY_DESCRIPTOR&>(buffer[0]); @@ -1152,7 +1153,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation, &secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetResolved)), L"SetFileSecurity", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetResolved)), L"SetFileSecurity"); /* PSECURITY_DESCRIPTOR buffer = nullptr; @@ -1212,13 +1213,13 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (targetHandle.get() == INVALID_HANDLE_VALUE) throw FileError - SECURITY_INFORMATION secFlags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; + SECURITY_INFORMATION secFlags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; - //SACL/DACL inheritence flag is NOT copied by default: we have to tell ::SetSecurityInfo(() to enable/disable it manually! - //if (secCtrl & SE_DACL_PRESENT) - secFlags |= (secCtrl & SE_DACL_PROTECTED) ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION; - //if (secCtrl & SE_SACL_PRESENT) - secFlags |= (secCtrl & SE_SACL_PROTECTED) ? PROTECTED_SACL_SECURITY_INFORMATION : UNPROTECTED_SACL_SECURITY_INFORMATION; + //SACL/DACL inheritence flag is NOT copied by default: we have to tell ::SetSecurityInfo(() to enable/disable it manually! + //if (secCtrl & SE_DACL_PRESENT) + secFlags |= (secCtrl & SE_DACL_PROTECTED) ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION; + //if (secCtrl & SE_SACL_PRESENT) + secFlags |= (secCtrl & SE_SACL_PROTECTED) ? PROTECTED_SACL_SECURITY_INFORMATION : UNPROTECTED_SACL_SECURITY_INFORMATION; // rc = ::SetNamedSecurityInfo(const_cast<WCHAR*>(applyLongPathPrefix(target).c_str()), //__in LPTSTR pObjectName, -> does NOT dereference symlinks! @@ -1232,7 +1233,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (rc != ERROR_SUCCESS) throw FileError - */ + */ #elif defined ZEN_LINUX @@ -1244,25 +1245,25 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (procSl == ProcSymlink::FOLLOW) { if (::stat(sourcePath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat"); if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown"); if (::chmod(targetPath.c_str(), fileInfo.st_mode) != 0) - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod"); } else { if (::lstat(sourcePath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat"); if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown"); if (!symlinkExists(targetPath) && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0) - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod"); } #elif defined ZEN_MAC @@ -1271,7 +1272,7 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P flags |= COPYFILE_NOFOLLOW; if (::copyfile(sourcePath.c_str(), targetPath.c_str(), 0, flags) != 0) - throwFileError(replaceCpy(replaceCpy(_("Cannot copy permissions from %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"copyfile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy permissions from %x to %y."), L"%x", L"\n" + fmtPath(sourcePath)), L"%y", L"\n" + fmtPath(targetPath)), L"copyfile"); //owner is *not* copied with ::copyfile(): @@ -1279,18 +1280,18 @@ void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, P if (procSl == ProcSymlink::FOLLOW) { if (::stat(sourcePath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat"); if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown"); } else { if (::lstat(sourcePath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat"); if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown"); } #endif } @@ -1365,29 +1366,29 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (!::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), //__in LPCTSTR lpPathName, nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes { - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! //handle issues with already existing short 8.3 file names on Windows - if (lastError == ERROR_ALREADY_EXISTS) + if (ec == ERROR_ALREADY_EXISTS) if (have8dot3NameClash(targetPath)) { Fix8Dot3NameClash dummy(targetPath); //throw FileError; move clashing object to the side //now try again... if (::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), nullptr)) - lastError = ERROR_SUCCESS; + ec = ERROR_SUCCESS; else - lastError = ::GetLastError(); + ec = ::GetLastError(); } - if (lastError != ERROR_SUCCESS) + if (ec != ERROR_SUCCESS) { const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath)); - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", ec); - if (lastError == ERROR_ALREADY_EXISTS) + if (ec == ERROR_ALREADY_EXISTS) throw ErrorTargetExisting(errorMsg, errorDescr); - else if (lastError == ERROR_PATH_NOT_FOUND) + else if (ec == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -1525,7 +1526,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool const wchar_t functionName[] = L"symlink"; if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) #endif - throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName, getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName); //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist! zen::ScopeGuard guardNewLink = zen::makeGuard([&] @@ -1548,24 +1549,24 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName, GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, &sourceAttr)) //__out LPVOID lpFileInformation - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"GetFileAttributesEx", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"GetFileAttributesEx"); setFileTimeRaw(targetLink, &sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError #elif defined ZEN_LINUX struct ::stat sourceInfo = {}; if (::lstat(sourceLink.c_str(), &sourceInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat"); setFileTime(targetLink, sourceInfo.st_mtime, ProcSymlink::DIRECT); //throw FileError #elif defined ZEN_MAC struct ::stat sourceInfo = {}; if (::lstat(sourceLink.c_str(), &sourceInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat"); if (::copyfile(sourceLink.c_str(), targetLink.c_str(), 0, COPYFILE_XATTR | COPYFILE_NOFOLLOW) != 0) - throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), L"copyfile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), L"copyfile"); setFileTimeRaw(targetLink, &sourceInfo.st_birthtimespec, sourceInfo.st_mtimespec, ProcSymlink::DIRECT); //throw FileError #endif @@ -1582,15 +1583,15 @@ namespace #ifdef ZEN_WIN /* CopyFileEx() BackupRead() FileRead() - -------------------------------------------- -Attributes YES NO NO + -------------------------------------------- +Attributes YES NO NO create time NO NO NO -ADS YES YES NO -Encrypted YES NO(silent fail!) NO -Compressed NO NO NO -Sparse NO YES NO +ADS YES YES NO +Encrypted YES NO(silent fail!) NO +Compressed NO NO NO +Sparse NO YES NO Nonstandard FS YES UNKNOWN -> error writing ADS to Samba, issues reading from NAS, error copying files having "blocked" state... ect. -PERF - 6% faster +PERF - 6% faster Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and FileRead() @@ -1730,14 +1731,14 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw nullptr); //_In_opt_ HANDLE hTemplateFile if (hFileSource == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)); - std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); + std::wstring errorDescr = formatSystemError(L"CreateFile", ec); //if file is locked throw "ErrorFileLocked" instead! - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) + if (ec == ERROR_SHARING_VIOLATION || + ec == ERROR_LOCK_VIOLATION) { #ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message const std::wstring procList = vista::getLockingProcesses(sourceFile); //noexcept @@ -1754,7 +1755,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw //---------------------------------------------------------------------- BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"GetFileInformationByHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"GetFileInformationByHandle"); //encrypted files cannot be read with BackupRead which would fail silently! const bool sourceIsEncrypted = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; @@ -1785,15 +1786,15 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw nullptr); //_In_opt_ HANDLE hTemplateFile if (hFileTarget == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)); - const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); + const std::wstring errorDescr = formatSystemError(L"CreateFile", ec); - if (lastError == ERROR_FILE_EXISTS || //confirmed to be used - lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 + if (ec == ERROR_FILE_EXISTS || //confirmed to be used + ec == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 throw ErrorTargetExisting(errorMsg, errorDescr); - //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (ec == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -1803,7 +1804,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw //---------------------------------------------------------------------- BY_HANDLE_FILE_INFORMATION fileInfoTarget = {}; if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"GetFileInformationByHandle"); //return up-to-date file attributes InSyncAttributes newAttrib = {}; @@ -1854,7 +1855,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw 0, //_In_ DWORD nOutBufferSize, &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtPath(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE"); } //---------------------------------------------------------------------- @@ -1881,7 +1882,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw false, //__in BOOL bAbort, false, //__in BOOL bProcessSecurity, &contextRead)) //__out LPVOID *lpContext - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"! + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead"); //better use fine-granular error messages "reading/writing"! if (bytesRead > BUFFER_SIZE) throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(sourceFile)), L"BackupRead: buffer overflow."); //user should never see this @@ -1897,7 +1898,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw false, //__in BOOL bAbort, false, //__in BOOL bProcessSecurity, &contextWrite)) //__out LPVOID *lpContext - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite"); if (bytesWritten != bytesRead) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile)), L"BackupWrite: incomplete write."); //user should never see this @@ -1920,7 +1921,7 @@ InSyncAttributes copyFileWindowsBackupStream(const Zstring& sourceFile, //throw &fileInfoSource.ftCreationTime, nullptr, &fileInfoSource.ftLastWriteTime)) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(targetFile)), L"SetFileTime", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(targetFile)), L"SetFileTime"); guardTarget.dismiss(); return newAttrib; @@ -1975,13 +1976,13 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, file time handling: ::CopyFileEx() will (only) copy file modification time over from source file AFTER the last invokation of this callback => it is possible to adapt file creation time of target in here, but NOT file modification time! - CAVEAT: if ::CopyFileEx() fails to set modification time, it silently ignores this error and returns success!!! (confirmed with Process Monitor) + CAVEAT: if ::CopyFileEx() fails to set modification time, it silently ignores this error and returns success!!! (confirmed with Process Monitor) alternate data stream handling: CopyFileEx() processes multiple streams one after another, stream 1 is the file data stream and always available! Each stream is initialized with CALLBACK_STREAM_SWITCH and provides *new* hSourceFile, hDestinationFile. Calling GetFileInformationByHandle() on hDestinationFile for stream > 1 results in ERROR_ACCESS_DENIED! - totalBytesTransferred contains size of *all* streams and so can be larger than the "file size" file attribute + totalBytesTransferred contains size of *all* streams and so can be larger than the "file size" file attribute */ CallbackData& cbd = *static_cast<CallbackData*>(lpData); @@ -1993,10 +1994,10 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, { //#################### return source file attributes ################################ if (!::GetFileInformationByHandle(hSourceFile, &cbd.fileInfoSrc)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.sourceFile_)), L"GetFileInformationByHandle", ::GetLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.sourceFile_)), L"GetFileInformationByHandle"); if (!::GetFileInformationByHandle(hDestinationFile, &cbd.fileInfoTrg)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.targetFile_)), L"GetFileInformationByHandle", ::GetLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(cbd.targetFile_)), L"GetFileInformationByHandle"); //#################### switch to sparse file copy if req. ####################### #ifdef ZEN_WIN_VISTA_AND_LATER @@ -2087,7 +2088,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE if (!success) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition! @@ -2095,23 +2096,23 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE if (canCopyAsSparse(sourceFile, targetFile)) //noexcept throw ErrorFallbackToCopyAsBackupStream(L"sparse, copy failure"); - if (lastError == ERROR_ACCESS_DENIED && backupPrivilegesActive) + if (ec == ERROR_ACCESS_DENIED && backupPrivilegesActive) //chances are good this will work with copyFileWindowsBackupStream: https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/1998ebf2/ throw ErrorFallbackToCopyAsBackupStream(L"access denied"); //copying ADS may incorrectly fail with ERROR_FILE_NOT_FOUND: https://sourceforge.net/p/freefilesync/discussion/help/thread/a18a2c02/ - if (lastError == ERROR_FILE_NOT_FOUND && + if (ec == ERROR_FILE_NOT_FOUND && cbd.fileInfoSrc.nNumberOfLinks > 0 && cbd.fileInfoTrg.nNumberOfLinks > 0) throw ErrorFallbackToCopyAsBackupStream(L"bogus file not found"); //assemble error message... const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile)); - std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError); + std::wstring errorDescr = formatSystemError(L"CopyFileEx", ec); //if file is locked throw "ErrorFileLocked" instead! - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) + if (ec == ERROR_SHARING_VIOLATION || + ec == ERROR_LOCK_VIOLATION) { #ifdef ZEN_WIN_VISTA_AND_LATER //(try to) enhance error message const std::wstring procList = vista::getLockingProcesses(sourceFile); //noexcept @@ -2122,8 +2123,8 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE } //if target is existing this functions is expected to throw ErrorTargetExisting!!! - if (lastError == ERROR_FILE_EXISTS || //confirmed to be used - lastError == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!! + if (ec == ERROR_FILE_EXISTS || //confirmed to be used + ec == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!! { guardTarget.dismiss(); //don't delete file that existed previously! throw ErrorTargetExisting(errorMsg, errorDescr); @@ -2134,7 +2135,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE try //add more meaningful message { //trying to copy > 4GB file to FAT/FAT32 volume gives obscure ERROR_INVALID_PARAMETER (FAT can indeed handle files up to 4 Gig, tested!) - if (lastError == ERROR_INVALID_PARAMETER && + if (ec == ERROR_INVALID_PARAMETER && isFatDrive(targetFile) && getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabytes."; @@ -2148,7 +2149,7 @@ InSyncAttributes copyFileWindowsDefault(const Zstring& sourceFile, //throw FileE } //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! - // - perf: recent measurements show no slow down at all for buffered USB sticks! + // - perf: recent measurements show no slow down at all for buffered USB sticks! setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError guardTarget.dismiss(); //target has been created successfully! @@ -2209,7 +2210,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError struct ::stat sourceInfo = {}; if (::fstat(fileIn.getHandle(), &sourceInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"fstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"fstat"); const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, sourceInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); //analog to "cp" which copies "mode" (considering umask) by default @@ -2240,7 +2241,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError struct ::stat targetInfo = {}; if (::fstat(fileOut.getHandle(), &targetInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat"); newAttrib.fileSize = sourceInfo.st_size; #ifdef ZEN_MAC @@ -2258,7 +2259,7 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError //docs: http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/copyfile.3.html //source: http://www.opensource.apple.com/source/copyfile/copyfile-103.92.1/copyfile.c if (::fcopyfile(fileIn.getHandle(), fileOut.getHandle(), 0, COPYFILE_XATTR) != 0) - throwFileError(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile)), L"copyfile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy attributes from %x to %y."), L"%x", L"\n" + fmtPath(sourceFile)), L"%y", L"\n" + fmtPath(targetFile)), L"copyfile"); #endif fileOut.close(); //throw FileError -> optional, but good place to catch errors when closing stream! @@ -2290,8 +2291,8 @@ InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError | copyFileOsSpecific (solve 8.3 issue on Windows) | - copyFileWindowsSelectRoutine - / \ + copyFileWindowsSelectRoutine + / \ copyFileWindowsDefault(::CopyFileEx) copyFileWindowsBackupStream(::BackupRead/::BackupWrite) */ } diff --git a/zen/file_error.h b/zen/file_error.h index d8c6224c..7be52282 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -36,14 +36,23 @@ DEFINE_NEW_FILE_ERROR(ErrorFileLocked); DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); -//CAVEAT: evalulate global error code *before* "throw" statement which may overwrite error code -//due to a memory allocation before it creates the thrown instance! (e.g. affects MinGW + Win XP!!!) -template <class FE = FileError> inline -void throwFileError(const std::wstring& msg, const std::wstring& functionName, const ErrorCode ec) //throw FileError -{ - throw FE(msg, formatSystemError(functionName, ec)); -} +//CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls: +//-> MinGW + Win XP: "throw" statement allocates memory to hold the exception object => error code is cleared +//-> VC 2015, Debug: std::wstring allocator internally calls ::FlsGetValue() => error code is cleared +#ifdef _MSC_VER +#define THROW_LAST_FILE_ERROR(msg, functionName) \ + do \ + { \ + const ErrorCode ecInternal = getLastError(); \ + throw FileError(msg, formatSystemError(functionName, ecInternal)); \ + \ + __pragma(warning(suppress: 4127)) /*"conditional expression is constant"*/ \ + } while (false) +#else //variant witout "__pragma": +#define THROW_LAST_FILE_ERROR(msg, functionName) \ + do { const ErrorCode ecInternal = getLastError(); throw FileError(msg, formatSystemError(functionName, ecInternal)); } while (false) +#endif //----------- facilitate usage of std::wstring for error messages -------------------- diff --git a/zen/file_io.cpp b/zen/file_io.cpp index 1ea8b1b1..68e852da 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -121,7 +121,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock //begin of "regular" error reporting if (fileHandle == INVALID_HANDLE_VALUE) { - const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! const std::wstring errorMsg = replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath)); std::wstring errorDescr = formatSystemError(L"CreateFile", ec); @@ -145,7 +145,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock //don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html fileHandle = ::open(filepath.c_str(), O_RDONLY); if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle - throwFileError(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath)), L"open", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filepath)), L"open"); #endif //------------------------------------------------------------------------------------------------------ @@ -162,7 +162,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock #ifdef ZEN_LINUX //handle still un-owned => need constructor guard //optimize read-ahead on input file: if (::posix_fadvise(fileHandle, 0, 0, POSIX_FADV_SEQUENTIAL) != 0) - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filepath)), L"posix_fadvise", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filepath)), L"posix_fadvise"); #elif defined ZEN_MAC //"dtruss" doesn't show use of "fcntl() F_RDAHEAD/F_RDADVISE" for "cp") @@ -197,7 +197,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; retu static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead, &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile"); #elif defined ZEN_LINUX || defined ZEN_MAC ssize_t bytesRead = 0; @@ -208,7 +208,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError; retu while (bytesRead < 0 && errno == EINTR); //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz if (bytesRead < 0) - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"read", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"read"); #endif if (bytesRead == 0) //"zero indicates end of file" return bytesReadTotal; @@ -262,7 +262,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil fileHandle = createHandle(FILE_ATTRIBUTE_NORMAL); if (fileHandle == INVALID_HANDLE_VALUE) { - DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! //CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system" http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx if (ec == ERROR_ACCESS_DENIED && dwCreationDisposition == CREATE_ALWAYS) @@ -352,10 +352,10 @@ void FileOutput::close() //throw FileError #ifdef ZEN_WIN if (!::CloseHandle(fileHandle)) - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"CloseHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"CloseHandle"); #elif defined ZEN_LINUX || defined ZEN_MAC if (::close(fileHandle) != 0) - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"close", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"close"); #endif } @@ -369,7 +369,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite, &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"WriteFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"WriteFile"); if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes! throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"WriteFile: incomplete write."); //user should never see this @@ -389,7 +389,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers errno = ENOSPC; - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write"); } if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write: buffer overflow."); //user should never see this diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index 3ef94032..facce75c 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -39,15 +39,15 @@ void zen::traverseFolder(const Zstring& dirPath, HANDLE hDir = ::FindFirstFile(applyLongPathPrefix(appendSeparator(dirPath) + L'*').c_str(), &findData); if (hDir == INVALID_HANDLE_VALUE) { - const DWORD lastError = ::GetLastError(); //copy before making other system calls! - if (lastError == ERROR_FILE_NOT_FOUND) + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! + if (ec == ERROR_FILE_NOT_FOUND) { //1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility if (dirExists(dirPath)) //yes, a race-condition, still the best we can do return; } - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"FindFirstFile", lastError); + throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindFirstFile", ec)); } ZEN_ON_SCOPE_EXIT(::FindClose(hDir)); @@ -58,37 +58,39 @@ void zen::traverseFolder(const Zstring& dirPath, firstIteration = false; else if (!::FindNextFile(hDir, &findData)) { - const DWORD lastError = ::GetLastError(); - if (lastError == ERROR_NO_MORE_FILES) //not an error situation + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! + if (ec == ERROR_NO_MORE_FILES) //not an error situation return; //else we have a problem... report it: - throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile", lastError); + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(L"FindNextFile", ec)); } //skip "." and ".." - const Zchar* const shortName = findData.cFileName; + const wchar_t* const itemNameRaw = findData.cFileName; - if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item is missing a name."); - if (shortName[0] == L'.' && - (shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0))) + if (itemNameRaw[0] == 0) + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); + + if (itemNameRaw[0] == L'.' && + (itemNameRaw[1] == 0 || (itemNameRaw[1] == L'.' && itemNameRaw[2] == 0))) continue; - const Zstring& itempath = appendSeparator(dirPath) + shortName; + const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw; if (zen::isSymlink(findData)) //check first! { if (onLink) - onLink({ shortName, itempath, filetimeToTimeT(findData.ftLastWriteTime) }); + onLink({ itemPath, filetimeToTimeT(findData.ftLastWriteTime) }); } else if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { if (onDir) - onDir({ shortName, itempath }); + onDir({ itemPath }); } else //a file { if (onFile) - onFile({ shortName, itempath, get64BitUInt(findData.nFileSizeLow, findData.nFileSizeHigh), filetimeToTimeT(findData.ftLastWriteTime) }); + onFile({ itemPath, get64BitUInt(findData.nFileSizeLow, findData.nFileSizeHigh), filetimeToTimeT(findData.ftLastWriteTime) }); } } @@ -106,32 +108,34 @@ void zen::traverseFolder(const Zstring& dirPath, DIR* dirObj = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/" if (!dirObj) - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir"); ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash for (;;) { struct ::dirent* dirEntry = nullptr; if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0) - throwFileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r"); //don't retry but restart dir traversal on error! http://blogs.msdn.com/b/oldnewthing/archive/2014/06/12/10533529.aspx if (!dirEntry) //no more items return; //don't return "." and ".." - const char* shortName = dirEntry->d_name; + const char* itemNameRaw = dirEntry->d_name; + + if (itemNameRaw[0] == 0) + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); - if (shortName[0] == 0) throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item is missing a name."); - if (shortName[0] == '.' && - (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0))) + if (itemNameRaw[0] == '.' && + (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) continue; #ifdef ZEN_MAC //some file system abstraction layers fail to properly return decomposed UTF8: http://developer.apple.com/library/mac/#qa/qa1173/_index.html //so we need to do it ourselves; perf: ~600 ns per conversion //note: it's not sufficient to apply this in z_impl::compareFilenamesNoCase: if UTF8 forms differ, FFS assumes a rename in case sensitivity and // will try to propagate the rename => this won't work if target drive reports a particular UTF8 form only! - if (CFStringRef cfStr = osx::createCFString(shortName)) + if (CFStringRef cfStr = osx::createCFString(itemNameRaw)) { ZEN_ON_SCOPE_EXIT(::CFRelease(cfStr)); @@ -140,19 +144,19 @@ void zen::traverseFolder(const Zstring& dirPath, { bufferUtfDecomposed.resize(lenMax); if (::CFStringGetFileSystemRepresentation(cfStr, &bufferUtfDecomposed[0], lenMax)) //get decomposed UTF form (verified!) despite ambiguous documentation - shortName = &bufferUtfDecomposed[0]; + itemNameRaw = &bufferUtfDecomposed[0]; } } //const char* sampleDecomposed = "\x6f\xcc\x81.txt"; //const char* samplePrecomposed = "\xc3\xb3.txt"; #endif - const Zstring& itempath = appendSeparator(dirPath) + shortName; + const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw; struct ::stat statData = {}; try { - if (::lstat(itempath.c_str(), &statData) != 0) //lstat() does not resolve symlinks - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itempath)), L"lstat", getLastError()); + if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat"); } catch (const FileError& e) { @@ -164,17 +168,17 @@ void zen::traverseFolder(const Zstring& dirPath, if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks! { if (onLink) - onLink({ shortName, itempath, statData.st_mtime}); + onLink({ itemPath, statData.st_mtime}); } else if (S_ISDIR(statData.st_mode)) //a directory { if (onDir) - onDir({ shortName, itempath }); + onDir({ itemPath }); } else //a file or named pipe, ect. { if (onFile) - onFile({ shortName, itempath, makeUnsigned(statData.st_size), statData.st_mtime }); + onFile({ itemPath, makeUnsigned(statData.st_size), statData.st_mtime }); } /* It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios: diff --git a/zen/file_traverser.h b/zen/file_traverser.h index 3f8030d3..75c7660c 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -16,7 +16,6 @@ namespace zen { struct FileInfo { - const Zchar* shortName; const Zstring& fullPath; std::uint64_t fileSize; //[bytes] std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC @@ -24,13 +23,11 @@ struct FileInfo struct DirInfo { - const Zchar* shortName; const Zstring& fullPath; }; struct SymlinkInfo { - const Zchar* shortName; const Zstring& fullPath; std::int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC }; diff --git a/zen/fixed_list.h b/zen/fixed_list.h index a1f83eb4..61ce3f16 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -27,10 +27,7 @@ class FixedList }; public: - FixedList() : - firstInsert(nullptr), - lastInsert(nullptr), - sz(0) {} + FixedList() {} ~FixedList() { clear(); } @@ -151,9 +148,9 @@ private: delete oldNode; } - Node* firstInsert; - Node* lastInsert; //point to last insertion; required by efficient emplace_back() - size_t sz; + Node* firstInsert = nullptr; + Node* lastInsert = nullptr; //point to last insertion; required by efficient emplace_back() + size_t sz = 0; }; } @@ -33,6 +33,7 @@ struct TranslationHandler { virtual ~TranslationHandler() {} + //C++11: std::wstring should be thread-safe like an int virtual std::wstring translate(const std::wstring& text) = 0; //simple translation virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0; }; diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h index 4b79e051..6f6ebfdc 100644 --- a/zen/long_path_prefix.h +++ b/zen/long_path_prefix.h @@ -30,9 +30,9 @@ Zstring ntPathToWin32Path(const Zstring& path); //noexcept http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#NT_Namespaces As used by GetModuleFileNameEx() and symlinks (FSCTL_GET_REPARSE_POINT): - E.g.: - \??\C:\folder -> C:\folder - \SystemRoot -> C:\Windows + E.g.: + \??\C:\folder -> C:\folder + \SystemRoot -> C:\Windows */ } diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp index 577e33a6..017eaa8b 100644 --- a/zen/process_priority.cpp +++ b/zen/process_priority.cpp @@ -41,7 +41,7 @@ struct ScheduleForBackgroundProcessing::Pimpl {}; ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() { if (!::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN)) //this call lowers CPU priority, too!! - throwFileError(_("Cannot change process I/O priorities."), L"SetPriorityClass", getLastError()); + THROW_LAST_FILE_ERROR(_("Cannot change process I/O priorities."), L"SetPriorityClass"); } @@ -64,32 +64,32 @@ ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {}; /* struct ScheduleForBackgroundProcessing { - - required functions ioprio_get/ioprio_set are not part of glibc: http://linux.die.net/man/2/ioprio_set - - and probably never will: http://sourceware.org/bugzilla/show_bug.cgi?id=4464 - - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead - - ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid())) - { - if (oldIoPrio != -1) - setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); - } - ~ScheduleForBackgroundProcessing() - { - if (oldIoPrio != -1) - setIoPriority(::getpid(), oldIoPrio); - } + - required functions ioprio_get/ioprio_set are not part of glibc: http://linux.die.net/man/2/ioprio_set + - and probably never will: http://sourceware.org/bugzilla/show_bug.cgi?id=4464 + - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead + + ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid())) + { + if (oldIoPrio != -1) + setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)); + } + ~ScheduleForBackgroundProcessing() + { + if (oldIoPrio != -1) + setIoPriority(::getpid(), oldIoPrio); + } private: - static int getIoPriority(pid_t pid) - { - return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid); - } - static int setIoPriority(pid_t pid, int ioprio) - { - return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio); - } - - const int oldIoPrio; + static int getIoPriority(pid_t pid) + { + return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid); + } + static int setIoPriority(pid_t pid, int ioprio) + { + return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio); + } + + const int oldIoPrio; }; */ #endif diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 75083d57..d49e686c 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -172,18 +172,18 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError #ifdef ZEN_WIN -bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>& onUpdateGui) //throw FileError +bool zen::recycleBinExists(const Zstring& dirPath, const std::function<void ()>& onUpdateGui) //throw FileError { #ifdef ZEN_WIN_VISTA_AND_LATER - return vista::supportsRecycleBin(dirpath); //throw FileError + return vista::supportsRecycleBin(dirPath); //throw FileError #else //excessive runtime if recycle bin exists, is full and drive is slow: - auto ft = runAsync([dirpath]() + auto ft = runAsync([dirPath]() { SHQUERYRBINFO recInfo = {}; recInfo.cbSize = sizeof(recInfo); - return ::SHQueryRecycleBin(dirpath.c_str(), //__in_opt LPCTSTR pszRootPath, + return ::SHQueryRecycleBin(dirPath.c_str(), //__in_opt LPCTSTR pszRootPath, &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo }); @@ -197,7 +197,7 @@ bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>& //1. ::SHQueryRecycleBin() is excessive: traverses whole $Recycle.Bin directory tree each time!!!! But it's safe and correct. //2. we can't simply buffer the ::SHQueryRecycleBin() based on volume serial number: - // "subst S:\ C:\" => GetVolumeInformation() returns same serial for C:\ and S:\, but S:\ does not support recycle bin! + // "subst S:\ C:\" => GetVolumeInformation() returns same serial for C:\ and S:\, but S:\ does not support recycle bin! //3. we would prefer to use CLSID_RecycleBinManager beginning with Vista... if only this interface were documented!!! @@ -210,7 +210,7 @@ bool zen::recycleBinExists(const Zstring& dirpath, const std::function<void ()>& /* Zstring rootPathPf = appendSeparator(&buffer[0]); - const bool canUseFastCheckForRecycler = winXpOrLater(); + const bool canUseFastCheckForRecycler = winXpOrLater(); if (!canUseFastCheckForRecycler) //== "checkForRecycleBin" return STATUS_REC_UNKNOWN; diff --git a/zen/recycler.h b/zen/recycler.h index 7c63cf8a..3df7fda2 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -21,7 +21,8 @@ namespace zen Windows ------- -Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected +-> Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected +-> COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize Linux ----- @@ -31,15 +32,18 @@ Linker flags: `pkg-config --libs gio-2.0` Already included in package "gtk+-2.0"! */ + + //move a file or folder to Recycle Bin (deletes permanently if recycler is not available) -> crappy semantics, but we have no choice thanks to Windows' design -bool recycleOrDelete(const Zstring& itempath); //throw FileError, return "true" if file/dir was actually deleted +bool recycleOrDelete(const Zstring& itemPath); //throw FileError, return "true" if file/dir was actually deleted #ifdef ZEN_WIN //Win XP: can take a long time if recycle bin is full and drive is slow!!! => buffer result! -bool recycleBinExists(const Zstring& dirpath, const std::function<void ()>& onUpdateGui); //throw FileError +//Vista and later: dirPath must exist for a valid check! +bool recycleBinExists(const Zstring& dirPath, const std::function<void ()>& onUpdateGui); //throw FileError -void recycleOrDelete(const std::vector<Zstring>& filepaths, //throw FileError, return "true" if file/dir was actually deleted +void recycleOrDelete(const std::vector<Zstring>& filePaths, //throw FileError, return "true" if file/dir was actually deleted const std::function<void (const std::wstring& displayPath)>& onRecycleItem); //optional; currentItem may be empty #endif } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 5e917853..b5564c9b 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -17,13 +17,13 @@ namespace zen //Scope Guard /* zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CloseHandle(hDir); }); - ... - lockAio.dismiss(); + ... + lockAio.dismiss(); */ //Scope Exit /* - ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir)); */ class ScopeGuardBase @@ -32,7 +32,7 @@ public: void dismiss() { dismissed_ = true; } protected: - ScopeGuardBase() : dismissed_(false) {} + ScopeGuardBase() {} ScopeGuardBase(ScopeGuardBase&& other) : dismissed_(other.dismissed_) { other.dismiss(); } //take over responsibility ~ScopeGuardBase() {} //[!] protected non-virtual base class destructor @@ -42,7 +42,7 @@ private: ScopeGuardBase (const ScopeGuardBase&) = delete; ScopeGuardBase& operator=(const ScopeGuardBase&) = delete; - bool dismissed_; + bool dismissed_ = false; }; diff --git a/zen/serialize.h b/zen/serialize.h index 07d9362c..ff2871b1 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -67,7 +67,7 @@ template <class BinContainer> BinContainer loadBinStream(const Zstring& filepath ----------------------------- struct BinInputStream { - size_t read(void* data, size_t len); //return "len" bytes unless end of stream! + size_t read(void* data, size_t len); //return "len" bytes unless end of stream! }; ------------------------------ diff --git a/zen/shell_execute.h b/zen/shell_execute.h index b36bc5ea..2adce179 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -62,12 +62,14 @@ bool shellExecuteImpl(Function fillExecInfo, ExecutionType type) void shellExecute(const void* /*PCIDLIST_ABSOLUTE*/ shellItemPidl, const std::wstring& displayPath, ExecutionType type) //throw FileError { - if (!shellExecuteImpl([&](SHELLEXECUTEINFO& execInfo) -{ - execInfo.fMask |= SEE_MASK_IDLIST; - execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE! - }, type)) //throw FileError - throwFileError(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx", ::GetLastError()); + auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo) + { + execInfo.fMask |= SEE_MASK_IDLIST; + execInfo.lpIDList = const_cast<void*>(shellItemPidl); //lpIDList is documented as PCIDLIST_ABSOLUTE! + }; + + if (!shellExecuteImpl(fillExecInfo , type)) //throw FileError + THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + fmtPath(displayPath), L"ShellExecuteEx"); } #endif @@ -97,12 +99,14 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError (iter->empty() || std::any_of(iter->begin(), iter->end(), &isWhiteSpace<wchar_t>) ? L"\"" + *iter + L"\"" : *iter); } - if (!shellExecuteImpl([&](SHELLEXECUTEINFO& execInfo) -{ - execInfo.lpFile = filepath.c_str(); + auto fillExecInfo = [&](SHELLEXECUTEINFO& execInfo) + { + execInfo.lpFile = filepath.c_str(); execInfo.lpParameters = arguments.c_str(); - }, type)) - throwFileError(_("Incorrect command line:") + L"\nFile: " + fmtPath(filepath) + L"\nArg: " + copyStringTo<std::wstring>(arguments), L"ShellExecuteEx", ::GetLastError()); + }; + + if (!shellExecuteImpl(fillExecInfo, type)) + THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\nFile: " + fmtPath(filepath) + L"\nArg: " + copyStringTo<std::wstring>(arguments), L"ShellExecuteEx"); #elif defined ZEN_LINUX || defined ZEN_MAC /* diff --git a/zen/stl_tools.h b/zen/stl_tools.h index e949c5c9..1e38f1b0 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -13,6 +13,7 @@ #include <memory> #include <algorithm> #include "type_tools.h" +#include "build_info.h" //enhancements for <algorithm> namespace zen @@ -59,12 +60,19 @@ template <class InputIterator1, class InputIterator2> bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); -//until std::make_unique is available in GCC: -template <class T, class... Args> inline -std::unique_ptr<T> make_unique(Args&& ... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } - +size_t hashBytes(const unsigned char* ptr, size_t len); +//support for custom string classes in std::unordered_set/map +struct StringHash +{ + template <class String> + size_t operator()(const String& str) const + { + const auto* strFirst = strBegin(str); + return hashBytes(reinterpret_cast<const unsigned char*>(strFirst), strLength(str) * sizeof(strFirst[0])); + } +}; @@ -199,6 +207,28 @@ bool equal(InputIterator1 first1, InputIterator1 last1, #if defined _MSC_VER && _MSC_VER <= 1600 static_assert(false, "VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11"); #endif + + +inline +size_t hashBytes(const unsigned char* ptr, size_t len) +{ + //http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +#ifdef ZEN_BUILD_32BIT + const size_t basis = 2166136261U; + const size_t prime = 16777619U; +#elif defined ZEN_BUILD_64BIT + const size_t basis = 14695981039346656037ULL; + const size_t prime = 1099511628211ULL; +#endif + + size_t val = basis; + for (size_t i = 0; i < len; ++i) + { + val ^= static_cast<size_t>(ptr[i]); + val *= prime; + } + return val; +} } #endif //STL_TOOLS_HEADER_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index 1bf8ed68..224797e8 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -198,7 +198,7 @@ private: length (static_cast<std::uint32_t>(len)), capacity(static_cast<std::uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "the types are always lock-free" - std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default! + std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default! std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination }; @@ -211,9 +211,9 @@ private: //perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison -template <class Char, //Character Type +template <class Char, //Character Type template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy - class AP = AllocatorOptimalSpeed> //Allocator Policy + class AP = AllocatorOptimalSpeed> //Allocator Policy class Zbase : public SP<Char, AP> { public: @@ -222,10 +222,10 @@ public: Zbase(const Char* source, size_t length); Zbase(const Zbase& source); Zbase(Zbase&& tmp) noexcept; - explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! - -//allow explicit construction from different string type, prevent ambiguity via SFINAE -//template <class S> explicit Zbase(const S& other, typename S::value_type = 0); + //explicit Zbase(Char source); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!! + + //allow explicit construction from different string type, prevent ambiguity via SFINAE + //template <class S> explicit Zbase(const S& other, typename S::value_type = 0); ~Zbase(); @@ -275,7 +275,7 @@ public: Zbase& operator+=(const Char* other); Zbase& operator+=(Char ch); - static const size_t npos = static_cast<size_t>(-1); + static const size_t npos = static_cast<size_t>(-1); private: Zbase (int) = delete; // @@ -307,8 +307,8 @@ template <class Char, template <class, class> class SP, class AP> inline Zbase<C template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter... template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(Zbase<Char, SP, AP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision -template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; } -template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs) += rhs; } +template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+( Char lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(&lhs, 1) += rhs; } +template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP> operator+(const Char* lhs, const Zbase<Char, SP, AP>& rhs) { return Zbase<Char, SP, AP>(lhs ) += rhs; } @@ -333,15 +333,6 @@ Zbase<Char, SP, AP>::Zbase() template <class Char, template <class, class> class SP, class AP> inline -Zbase<Char, SP, AP>::Zbase(Char source) -{ - rawStr = this->create(1); - rawStr[0] = source; - rawStr[1] = 0; -} - - -template <class Char, template <class, class> class SP, class AP> inline Zbase<Char, SP, AP>::Zbase(const Char* source) { const size_t sourceLen = strLength(source); diff --git a/zen/string_tools.h b/zen/string_tools.h index 9708464e..8f83b9cd 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -18,7 +18,7 @@ #include "stl_tools.h" #include "string_traits.h" - + //enhance arbitray string class with useful non-member functions: namespace zen { @@ -444,9 +444,9 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>) /* perf: integer to string: (executed 10 mio. times) - std::stringstream - 14796 ms - std::sprintf - 3086 ms - formatInteger - 778 ms + std::stringstream - 14796 ms + std::sprintf - 3086 ms + formatInteger - 778 ms */ template <class OutputIterator, class Num> inline diff --git a/zen/string_traits.h b/zen/string_traits.h index add53d3a..61fa2625 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -16,21 +16,21 @@ namespace zen /* IsStringLike<>::value: IsStringLike<const wchar_t*>::value; //equals "true" - IsStringLike<const int*> ::value; //equals "false" + IsStringLike<const int*> ::value; //equals "false" GetCharType<>::Type: - GetCharType<std::wstring>::Type //equals wchar_t - GetCharType<wchar_t[5]> ::Type //equals wchar_t + GetCharType<std::wstring>::Type //equals wchar_t + GetCharType<wchar_t[5]> ::Type //equals wchar_t strLength(): - strLength(str); //equals str.length() - strLength(array); //equals cStringLength(array) + strLength(str); //equals str.length() + strLength(array); //equals cStringLength(array) strBegin(): -> not null-terminated! -> may be nullptr if length is 0! - std::wstring str(L"dummy"); - char array[] = "dummy"; - strBegin(str); //returns str.c_str() - strBegin(array); //returns array + std::wstring str(L"dummy"); + char array[] = "dummy"; + strBegin(str); //returns str.c_str() + strBegin(array); //returns array */ //reference a sub-string for consumption by zen string_tools diff --git a/zen/symlink_target.h b/zen/symlink_target.h index c4557559..b5ca8191 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -100,7 +100,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (hLink == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"CreateFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"CreateFile"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hLink)); //respect alignment issues... @@ -116,7 +116,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro bufferSize, //__in DWORD nOutBufferSize, &bytesReturned, //__out_opt LPDWORD lpBytesReturned, nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped - throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"DeviceIoControl, FSCTL_GET_REPARSE_POINT", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"DeviceIoControl, FSCTL_GET_REPARSE_POINT"); REPARSE_DATA_BUFFER& reparseData = *reinterpret_cast<REPARSE_DATA_BUFFER*>(&buffer[0]); //REPARSE_DATA_BUFFER needs to be artificially enlarged! @@ -144,7 +144,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE); if (bytesWritten < 0) - throwFileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink"); if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation, not an error for readlink! throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink: buffer truncated."); @@ -162,7 +162,7 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW"); if (!getFinalPathNameByHandle) throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\"")); - + const HANDLE hFile = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName, 0, //_In_ DWORD dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, @@ -172,12 +172,12 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, nullptr); //_In_opt_ HANDLE hTemplateFile if (hFile == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"CreateFile", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"CreateFile"); ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); const DWORD bufferSize = getFinalPathNameByHandle(hFile, nullptr, 0, 0); if (bufferSize == 0) - throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle"); std::vector<wchar_t> targetPath(bufferSize); const DWORD charsWritten = getFinalPathNameByHandle(hFile, //__in HANDLE hFile, @@ -188,7 +188,7 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError { const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)); if (charsWritten == 0) - throwFileError(errorMsg, L"GetFinalPathNameByHandle", getLastError()); + THROW_LAST_FILE_ERROR(errorMsg, L"GetFinalPathNameByHandle"); throw FileError(errorMsg); } @@ -197,7 +197,7 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError #elif defined ZEN_LINUX || defined ZEN_MAC char* targetPath = ::realpath(linkPath.c_str(), nullptr); if (!targetPath) - throwFileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"realpath", getLastError()); + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"realpath"); ZEN_ON_SCOPE_EXIT(::free(targetPath)); return targetPath; #endif @@ -216,13 +216,13 @@ Zstring getResolvedSymlinkPath(const Zstring& linkPath) { return getResolvedSyml #ifdef ZEN_WIN /* Reparse Point Tags - http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx + http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx WIN32_FIND_DATA structure - http://msdn.microsoft.com/en-us/library/windows/desktop/aa365740(v=vs.85).aspx + http://msdn.microsoft.com/en-us/library/windows/desktop/aa365740(v=vs.85).aspx The only surrogate reparse points are; - IO_REPARSE_TAG_MOUNT_POINT - IO_REPARSE_TAG_SYMLINK + IO_REPARSE_TAG_MOUNT_POINT + IO_REPARSE_TAG_SYMLINK */ inline diff --git a/zen/thread.h b/zen/thread.h index a3b8760b..b10dd342 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -25,7 +25,7 @@ public: InterruptibleThread& operator=(InterruptibleThread&& tmp) = default; template <class Function> - InterruptibleThread(Function f); + InterruptibleThread(Function&& f); bool joinable () const { return stdThread.joinable(); } void interrupt(); @@ -62,8 +62,8 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //th /* std::async replacement without crappy semantics: - 1. guaranteed to run asynchronously - 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor + 1. guaranteed to run asynchronously + 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor Example: Zstring dirpath = ... @@ -72,7 +72,7 @@ Example: //dir exising */ template <class Function> -auto runAsync(Function fun) -> std::future<decltype(fun())>; +auto runAsync(Function&& fun) -> std::future<decltype(fun())>; //wait for all with a time limit: return true if *all* results are available! template<class InputIterator, class Duration> @@ -90,7 +90,7 @@ public: GetFirstResult(); template <class Fun> - void addJob(Fun f); //f must return a std::unique_ptr<T> containing a value if successful + void addJob(Fun&& f); //f must return a std::unique_ptr<T> containing a value if successful template <class Duration> bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed @@ -139,18 +139,38 @@ private: //###################### implementation ###################### +namespace impl +{ template <class Function> inline -auto runAsync(Function fun) -> std::future<decltype(fun())> +auto runAsync(Function&& fun, TrueType /*copy-constructible*/) -> std::future<decltype(fun())> { typedef decltype(fun()) ResultType; - std::packaged_task<ResultType()> pt(std::move(fun)); + //note: std::packaged_task does NOT support move-only function objects! + std::packaged_task<ResultType()> pt(std::forward<Function>(fun)); auto fut = pt.get_future(); std::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! return fut; } +template <class Function> inline +auto runAsync(Function&& fun, FalseType /*copy-constructible*/) -> std::future<decltype(fun())> +{ + //support move-only function objects! + auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun)); + return runAsync([sharedFun]() { return (*sharedFun)(); }, TrueType()); +} +} + + +template <class Function> inline +auto runAsync(Function&& fun) -> std::future<decltype(fun())> +{ + return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>()); +} + + template<class InputIterator, class Duration> inline bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration) { @@ -166,12 +186,6 @@ template <class T> class GetFirstResult<T>::AsyncResult { public: - AsyncResult() : -#ifndef NDEBUG - returnedResult(false), -#endif - jobsFinished(0) {} - //context: worker threads void reportFinished(std::unique_ptr<T>&& result) { @@ -208,11 +222,11 @@ private: bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked! #ifndef NDEBUG - bool returnedResult; + bool returnedResult = false; #endif std::mutex lockResult; - size_t jobsFinished; // + size_t jobsFinished = 0; // std::unique_ptr<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal" std::condition_variable conditionJobDone; }; @@ -225,9 +239,9 @@ GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult> template <class T> template <class Fun> inline -void GetFirstResult<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success +void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::unique_ptr<T> containing a value on success { - std::thread t([asyncResult = this->asyncResult_, f = std::move(f)] { asyncResult->reportFinished(f()); }); + std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! } @@ -367,12 +381,12 @@ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //thr template <class Function> inline -InterruptibleThread::InterruptibleThread(Function f) : intStatus_(std::make_shared<InterruptionStatus>()) +InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>()) { std::promise<void> pFinished; threadCompleted = pFinished.get_future(); - stdThread = std::thread([f = std::move(f), + stdThread = std::thread([f = std::forward<Function>(f), intStatus = this->intStatus_, pFinished = std::move(pFinished)]() mutable { diff --git a/zen/tick_count.h b/zen/tick_count.h index bedc66a5..b5667c6c 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -65,8 +65,8 @@ public: return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow! #elif defined ZEN_LINUX //structure timespec documented with members: - // time_t tv_sec seconds - // long tv_nsec nanoseconds + // time_t tv_sec seconds + // long tv_nsec nanoseconds const int64_t deltaSec = lhs.val_.tv_sec - rhs.val_.tv_sec; const int64_t deltaNsec = lhs.val_.tv_nsec - rhs.val_.tv_nsec; return numeric::abs(deltaSec * 1000000000 + deltaNsec); @@ -114,8 +114,8 @@ int64_t ticksPerSec() //return 0 on error if (::mach_timebase_info(&tbi) != KERN_SUCCESS) return 0; //structure mach_timebase_info_data_t documented with members: - // uint32_t numer; - // uint32_t denom; + // uint32_t numer; + // uint32_t denom; return static_cast<int64_t>(1000000000) * tbi.denom / tbi.numer; #endif } @@ -154,8 +154,8 @@ struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14: //strftime() craziness on invalid input: -// VS 2010: CRASH unless "_invalid_parameter_handler" is set: http://msdn.microsoft.com/en-us/library/ksazx244.aspx -// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! +// VS 2010: CRASH unless "_invalid_parameter_handler" is set: http://msdn.microsoft.com/en-us/library/ksazx244.aspx +// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst! inline size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const struct std::tm* timeptr) { @@ -173,12 +173,12 @@ size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* form inline bool isValid(const struct std::tm& t) { - -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: - _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) - _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE) - -> also std::mktime does *not* help here at all! + -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions: + _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE) + _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE) + -> also std::mktime does *not* help here at all! - auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; }; + auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; }; //http://www.cplusplus.com/reference/clibrary/ctime/tm/ return inRange(t.tm_sec , 0, 61) && diff --git a/zen/type_traits.h b/zen/type_traits.h index e03085d9..3ec90f49 100644 --- a/zen/type_traits.h +++ b/zen/type_traits.h @@ -60,20 +60,20 @@ template <class T> struct IsArithmetic; //IsInteger or IsFloat //################# Class Members ######################## /* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_ - Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); - 2. HasMember_c_str<T>::value -> use as boolean + Example: 1. ZEN_INIT_DETECT_MEMBER(c_str); + 2. HasMember_c_str<T>::value -> use as boolean */ /* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_ - Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); - 2. HasMember_size<T>::value -> use as boolean + Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const); + 2. HasMember_size<T>::value -> use as boolean */ /* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_ - Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); - 2. HasMemberType_value_type<T>::value -> use as boolean + Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type); + 2. HasMemberType_value_type<T>::value -> use as boolean */ @@ -130,29 +130,29 @@ template <class T> struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; //#################################################################### -#define ZEN_INIT_DETECT_MEMBER(NAME) \ +#define ZEN_INIT_DETECT_MEMBER(NAME) \ \ - template<bool isClass, class T> \ - struct HasMemberImpl_##NAME \ - { \ + template<bool isClass, class T> \ + struct HasMemberImpl_##NAME \ + { \ private: \ - typedef char Yes[1]; \ - typedef char No [2]; \ + typedef char Yes[1]; \ + typedef char No [2]; \ \ template <typename U, U t> \ - class Helper {}; \ - struct Fallback { int NAME; }; \ + class Helper {}; \ + struct Fallback { int NAME; }; \ \ - template <class U> \ - struct Helper2 : public U, public Fallback {}; /*this works only for class types!!!*/ \ + template <class U> \ + struct Helper2 : public U, public Fallback {}; /*this works only for class types!!!*/ \ \ - template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \ - template <class U> static Yes& hasMember(...); \ - public: \ - enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \ + template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \ + template <class U> static Yes& hasMember(...); \ + public: \ + enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \ }; \ \ - template<class T> \ + template<class T> \ struct HasMemberImpl_##NAME<false, T> : FalseType {}; \ \ template<typename T> \ @@ -160,37 +160,37 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {}; //#################################################################### -#define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \ +#define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \ \ - template<typename U> \ - class HasMember_##NAME \ - { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + template<typename U> \ + class HasMember_##NAME \ + { \ + typedef char Yes[1]; \ + typedef char No [2]; \ \ template <typename T, T t> class Helper {}; \ \ - template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \ - template <class T> static No& hasMember(...); \ - public: \ + template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \ + template <class T> static No& hasMember(...); \ + public: \ enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \ }; //#################################################################### -#define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \ +#define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \ \ - template<typename T> \ - class HasMemberType_##TYPENAME \ - { \ - typedef char Yes[1]; \ - typedef char No [2]; \ + template<typename T> \ + class HasMemberType_##TYPENAME \ + { \ + typedef char Yes[1]; \ + typedef char No [2]; \ \ - template <typename U> class Helper {}; \ + template <typename U> class Helper {}; \ \ template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \ - template <class U> static No& hasMemberType(...); \ - public: \ - enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \ + template <class U> static No& hasMemberType(...); \ + public: \ + enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \ }; } diff --git a/zen/warn_static.h b/zen/warn_static.h index 1e942031..3f268ec3 100644 --- a/zen/warn_static.h +++ b/zen/warn_static.h @@ -19,7 +19,7 @@ Usage: #define STATIC_WARNING_MAKE_STRINGIZE(NUM) STATIC_WARNING_MAKE_STRINGIZE_SUB(NUM) #define warn_static(TXT) \ - __pragma(message (__FILE__ "(" STATIC_WARNING_MAKE_STRINGIZE(__LINE__) "): Warning: " ## TXT)) + __pragma(message(__FILE__ "(" STATIC_WARNING_MAKE_STRINGIZE(__LINE__) "): Warning: " ## TXT)) #elif defined __GNUC__ #define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y diff --git a/zen/zstring.cpp b/zen/zstring.cpp index e2a756e6..3b29e664 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -21,8 +21,8 @@ using namespace zen; /* Perf test: compare strings 10 mio times; 64 bit build ----------------------------------------------------- - string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-" - string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf" + string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-" + string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf" Windows (UTF16 wchar_t) 4 ns | wcscmp @@ -125,8 +125,7 @@ Zstring makeUpperCopy(const Zstring& str) if (len == 0) //LCMapString does not allow input sizes of 0! return str; - Zstring output; - output.resize(len); + Zstring output = str; //LOCALE_INVARIANT is NOT available with Windows 2000 -> ok |