From 9043b32bb1835628c5a1d8be4a271c848443c629 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Tue, 24 May 2016 22:10:57 +0200 Subject: 8.1 --- zen/build_info.h | 2 +- zen/dir_watcher.cpp | 9 +++-- zen/file_access.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++-------- zen/file_access.h | 3 ++ zen/file_error.h | 1 + zen/file_id_def.h | 36 +++++++++++-------- zen/file_io.cpp | 10 +++--- zen/file_traverser.cpp | 18 +++++----- zen/format_unit.cpp | 2 +- zen/guid.h | 2 +- zen/scope_guard.h | 64 ++++++++++++++++++++++----------- zen/shell_execute.h | 12 +++---- zen/stl_tools.h | 21 ++++++++--- zen/string_tools.h | 8 ++--- zen/symlink_target.h | 17 ++++----- zen/utf.h | 8 ++--- 16 files changed, 210 insertions(+), 99 deletions(-) (limited to 'zen') diff --git a/zen/build_info.h b/zen/build_info.h index 09e1c5a7..c430a49a 100644 --- a/zen/build_info.h +++ b/zen/build_info.h @@ -12,7 +12,7 @@ namespace zen //determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT #ifdef ZEN_WIN - #ifdef _WIN64 + #ifdef _WIN64 //_WIN32 is defined by the compiler for both 32 and 64 bit builds, unlike _WIN64 #define ZEN_BUILD_64BIT #else #define ZEN_BUILD_32BIT diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 702a8e32..bb78939f 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -564,9 +564,12 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : baseDirPath(dirPath), pimpl_(std::make_unique()) { - CFStringRef dirpathCf = osx::createCFString(baseDirPath.c_str()); //returns nullptr on error - if (!dirpathCf) - throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"Function call failed: createCFString"); //no error code documented! + CFStringRef dirpathCf = nullptr; + try + { + dirpathCf = osx::createCFString(baseDirPath.c_str()); //throw SysError + } + catch (const SysError& e) { throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), e.toString()); } ZEN_ON_SCOPE_EXIT(::CFRelease(dirpathCf)); CFArrayRef dirpathCfArray = ::CFArrayCreate(nullptr, //CFAllocatorRef allocator, diff --git a/zen/file_access.cpp b/zen/file_access.cpp index ea13d381..78da0220 100644 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -167,6 +167,31 @@ bool isFatDrive(const Zstring& filePath) //noexcept return &buffer[0] == Zstring(L"FAT") || &buffer[0] == Zstring(L"FAT32"); } + + +#ifdef ZEN_WIN_VISTA_AND_LATER +bool isFatDrive(HANDLE hFile) //noexcept +{ + const DWORD bufferSize = MAX_PATH + 1; //"The length of the file system name buffer, in TCHARs. The maximum buffer size is MAX_PATH + 1" + std::vector buffer(bufferSize); + + if (!::GetVolumeInformationByHandleW(hFile, //_In_ HANDLE hFile, + nullptr, //_Out_writes_opt_(nVolumeNameSize) LPWSTR lpVolumeNameBuffer, + 0, //_In_ DWORD nVolumeNameSize, + nullptr, //_Out_opt_ LPDWORD lpVolumeSerialNumber, + nullptr, //_Out_opt_ LPDWORD lpMaximumComponentLength, + nullptr, //_Out_opt_ LPDWORD lpFileSystemFlags, + &buffer[0], //_Out_writes_opt_(nFileSystemNameSize) LPWSTR lpFileSystemNameBuffer, + bufferSize)) //_In_ DWORD nFileSystemNameSize + { + assert(false); + return false; + } + + return &buffer[0] == Zstring(L"FAT") || + &buffer[0] == Zstring(L"FAT32"); +} +#endif #endif } @@ -247,6 +272,43 @@ std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, retu } +VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError +{ +#ifdef ZEN_WIN + //this works for: + //- root paths "C:\", "D:\" + //- network shares: \\share\dirname + //- indirection: subst S: %USERPROFILE% + // -> GetVolumePathName() + GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try... + const HANDLE hItem = ::CreateFile(zen::applyLongPathPrefix(itemPath).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks! + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + /*needed to open a directory*/ + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hItem == INVALID_HANDLE_VALUE) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"CreateFile"); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hItem)); + + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (!::GetFileInformationByHandle(hItem, &fileInfo)) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"GetFileInformationByHandle"); + + return fileInfo.dwVolumeSerialNumber; + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::stat(itemPath.c_str(), &fileInfo) != 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat"); + + return fileInfo.st_dev; +#endif +} + + bool zen::removeFile(const Zstring& filePath) //throw FileError { #ifdef ZEN_WIN @@ -398,7 +460,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro if (!::MoveFileEx(pathSourceFmt.c_str(), //__in LPCTSTR lpExistingFileName, pathTargetFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, - 0)) //__in DWORD dwFlags + 0)) //__in DWORD dwFlags { DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! @@ -412,7 +474,7 @@ void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //thro //try again... if (::MoveFileEx(pathSourceFmt.c_str(), //__in LPCTSTR lpExistingFileName, pathTargetFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, - 0)) //__in DWORD dwFlags + 0)) //__in DWORD dwFlags { //(try to) restore file attributes ::SetFileAttributes(pathTargetFmt.c_str(), oldAttr); //don't handle error @@ -623,7 +685,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError nullptr, //__in_opt const FILETIME *lpLastAccessTime, &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime { - DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! + const DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER { @@ -667,8 +729,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError setFileInfo(basicInfo2); //throw FileError } catch (FileError&) {} - - ec = ERROR_SUCCESS; + return; } } #endif @@ -676,7 +737,11 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError 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 (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath)) +#ifdef ZEN_WIN_VISTA_AND_LATER + if (ec == ERROR_INVALID_PARAMETER && isFatDrive(hFile)) +#else + if (ec == ERROR_INVALID_PARAMETER && isFatDrive(filePath)) +#endif { //let's not fail due to an invalid creation time on FAT: http://www.freefilesync.org/forum/viewtopic.php?t=2278 if (creationTime) //retry (single-level recursion at most!) @@ -718,8 +783,7 @@ void setFileTimeByHandle(HANDLE hFile, //throw FileError } } - if (ec != ERROR_SUCCESS) - throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec)); + throw FileError(errorMsg, formatSystemError(L"SetFileTime", ec)); } } @@ -1398,7 +1462,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) if (!::CreateDirectory(applyLongPathPrefixCreateDir(targetPath).c_str(), //__in LPCTSTR lpPathName, - nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes + nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes { DWORD ec = ::GetLastError(); //copy before directly/indirectly making other system calls! @@ -1473,7 +1537,7 @@ void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, if (::GetFileInformationByHandle(hDirSrc, &dirInfo)) { ::SetFileAttributes(applyLongPathPrefix(targetPath).c_str(), dirInfo.dwFileAttributes); - //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx + //copy "read-only and system attributes": https://blogs.msdn.microsoft.com/oldnewthing/20030930-00/?p=42353/ const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; @@ -1544,7 +1608,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); }(); - typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); + using CreateSymbolicLinkFunc = BOOLEAN (WINAPI*)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); const SysDllFun createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); if (!createSymbolicLink) @@ -1663,7 +1727,10 @@ bool canCopyAsSparse(DWORD fileAttrSource, Function getTargetFsFlags) //throw () DWORD targetFsFlags = 0; if (!getTargetFsFlags(targetFsFlags)) - return false; + { + assert(false); + return false; + } assert(targetFsFlags != 0); return (targetFsFlags & FILE_SUPPORTS_SPARSE_FILES) != 0; @@ -2069,9 +2136,8 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, 0, //_In_ DWORD nOutBufferSize, &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped - {} //may legitimately fail with ERROR_INVALID_FUNCTION if - - // - if target folder is encrypted + {} //may legitimately fail with ERROR_INVALID_FUNCTION if: + // - target folder is encrypted // - target volume does not support compressed attribute //############################################################################# } diff --git a/zen/file_access.h b/zen/file_access.h index 026027e7..9c1b37ef 100644 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -15,6 +15,8 @@ namespace zen { +//note: certain functions require COM initialization! (vista_file_op.h) + bool fileExists (const Zstring& filePath); //noexcept; check whether file or file-symlink exists bool dirExists (const Zstring& dirPath ); //noexcept; check whether directory or dir-symlink exists bool symlinkExists (const Zstring& linkPath); //noexcept; check whether a symbolic link exists @@ -31,6 +33,7 @@ void setFileTime(const Zstring& filePath, std::int64_t modificationTime, ProcSym //symlink handling: always evaluate target std::uint64_t getFilesize(const Zstring& filePath); //throw FileError std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available +VolumeId getVolumeId(const Zstring& itemPath); //throw FileError bool removeFile(const Zstring& filePath); //throw FileError; return "false" if file is not existing diff --git a/zen/file_error.h b/zen/file_error.h index f41a878a..61a2e89c 100644 --- a/zen/file_error.h +++ b/zen/file_error.h @@ -39,6 +39,7 @@ DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume); //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 +// https://connect.microsoft.com/VisualStudio/feedback/details/1775690/calling-operator-new-may-set-lasterror-to-0 #ifdef _MSC_VER #define THROW_LAST_FILE_ERROR(msg, functionName) \ do \ diff --git a/zen/file_id_def.h b/zen/file_id_def.h index 24e45795..2880121c 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -20,12 +20,28 @@ namespace zen { #ifdef ZEN_WIN -typedef DWORD DeviceId; -typedef ULONGLONG FileIndex; +using VolumeId = DWORD; +using FileIndex = ULONGLONG; -typedef std::pair FileId; //optional! (however, always set on Linux, and *generally* available on Windows) +#elif defined ZEN_LINUX || defined ZEN_MAC +namespace impl { typedef struct ::stat StatDummy; } //sigh... + +using VolumeId = decltype(impl::StatDummy::st_dev); +using FileIndex = decltype(impl::StatDummy::st_ino); +#endif + + +struct FileId //always available on Linux, and *generally* available on Windows) +{ + FileId() {} + FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) {} + VolumeId volumeId = 0; + FileIndex fileIndex = 0; +}; +inline bool operator==(const FileId& lhs, const FileId& rhs) { return lhs.volumeId == rhs.volumeId && lhs.fileIndex == rhs.fileIndex; } +#ifdef ZEN_WIN inline FileId extractFileId(const BY_HANDLE_FILE_INFORMATION& fileInfo) { @@ -44,19 +60,11 @@ FileId extractFileId(DWORD volumeSerialNumber, ULONGLONG fileIndex) FileId(volumeSerialNumber, fileIndex) : FileId(); } -static_assert(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), ""); -static_assert(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), ""); -static_assert(sizeof(FileId().second) == sizeof(ULARGE_INTEGER), ""); - +static_assert(sizeof(FileId().volumeId ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), ""); +static_assert(sizeof(FileId().fileIndex) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), ""); +static_assert(sizeof(FileId().fileIndex) == sizeof(ULARGE_INTEGER), ""); #elif defined ZEN_LINUX || defined ZEN_MAC -namespace impl { typedef struct ::stat StatDummy; } //sigh... - -typedef decltype(impl::StatDummy::st_dev) DeviceId; -typedef decltype(impl::StatDummy::st_ino) FileIndex; - -typedef std::pair FileId; - inline FileId extractFileId(const struct ::stat& fileInfo) { diff --git a/zen/file_io.cpp b/zen/file_io.cpp index b4351ee8..3891abe6 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -114,7 +114,7 @@ FileInput::FileInput(const Zstring& filepath) : //throw FileError, ErrorFileLock fileHandle = createHandle(FILE_SHARE_READ | FILE_SHARE_DELETE); if (fileHandle == INVALID_HANDLE_VALUE) { - //=> support reading files which are open for write (e.g. Firefox db files): follow CopyFileEx() by addding FILE_SHARE_WRITE only for second try: + //=> support reading files which are open for write (e.g. Firefox .db, .sqlite files): follow CopyFileEx() by addding FILE_SHARE_WRITE only for second try: if (::GetLastError() == ERROR_SHARING_VIOLATION) fileHandle = createHandle(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE); @@ -185,7 +185,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; m throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); #ifdef ZEN_WIN - //posix ::read() semantics: test for end of file: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690%28v=vs.85%29.aspx + //posix ::read() semantics: test for end of file: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365690 DWORD bytesRead = 0; if (!::ReadFile(fileHandle, //__in HANDLE hFile, buffer, //__out LPVOID lpBuffer, @@ -233,7 +233,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil { return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, - /* http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files quote: When an application creates a file across a network, it is better to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone. The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data. @@ -253,7 +253,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil { 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 + //CREATE_ALWAYS fails with ERROR_ACCESS_DENIED if the existing file is hidden or "system": https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858#files if (ec == ERROR_ACCESS_DENIED && dwCreationDisposition == CREATE_ALWAYS) { const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str()); @@ -355,7 +355,7 @@ size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw Fil throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); #ifdef ZEN_WIN - DWORD bytesWritten = 0; //this parameter is NOT optional: http://blogs.msdn.com/b/oldnewthing/archive/2013/04/04/10407417.aspx + DWORD bytesWritten = 0; //this parameter is NOT optional: https://blogs.msdn.microsoft.com/oldnewthing/20130404-00/?p=4753/ if (!::WriteFile(fileHandle, //__in HANDLE hFile, buffer, //__out LPVOID lpBuffer, static_cast(bytesToWrite), //__in DWORD nNumberOfBytesToWrite, diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index aa39e508..89eb6e48 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -68,13 +68,13 @@ void zen::traverseFolder(const Zstring& dirPath, //skip "." and ".." const wchar_t* const itemNameRaw = findData.cFileName; - 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; + if (itemNameRaw[0] == 0) + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"FindNextFile: Data corruption; item with empty name."); + const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw; if (zen::isSymlink(findData)) //check first! @@ -121,9 +121,6 @@ void zen::traverseFolder(const Zstring& dirPath, //don't return "." and ".." 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 (itemNameRaw[0] == '.' && (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0))) continue; @@ -134,15 +131,18 @@ void zen::traverseFolder(const Zstring& dirPath, { itemName = osx::convertToPrecomposedUtf(itemNameRaw); //throw SysError } - catch (const SysError& e) //failure is not an item-level error since wo don't have the proper decomposed name!!! + catch (const SysError& e) //failure is not an item-level error since we don't have the proper decomposed name!!! { throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"Failed to generate precomposed file name: " + fmtPath(itemNameRaw) + L"\n" + e.toString()); //too obscure to warrant translation } - const Zstring& itemPath = appendSeparator(dirPath) + itemName; #else - const Zstring& itemPath = appendSeparator(dirPath) + itemNameRaw; + const Zstring& itemName = itemNameRaw; #endif + if (itemName.empty()) //checks result of osx::convertToPrecomposedUtf, too! + throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name."); + + const Zstring& itemPath = appendSeparator(dirPath) + itemName; struct ::stat statData = {}; try diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp index 3dfe805b..a880552e 100644 --- a/zen/format_unit.cpp +++ b/zen/format_unit.cpp @@ -302,7 +302,7 @@ std::wstring zen::utcToLocalTimeString(std::int64_t utcTime) #endif static const bool useNewLocalTimeCalculation = zen::vistaOrLater(); - //http://msdn.microsoft.com/en-us/library/ms724277(VS.85).aspx + //https://msdn.microsoft.com/en-us/library/ms724277 if (useNewLocalTimeCalculation) //DST conversion like in Windows 7: NTFS stays fixed, but FAT jumps by one hour { SYSTEMTIME systemTimeUtc = {}; diff --git a/zen/guid.h b/zen/guid.h index e8326ce6..914674af 100644 --- a/zen/guid.h +++ b/zen/guid.h @@ -22,7 +22,7 @@ namespace zen { inline -std::string generateGUID() //creates a 16 byte GUID +std::string generateGUID() //creates a 16-byte GUID { boost::uuids::uuid nativeRep = boost::uuids::random_generator()(); //generator is only thread-safe like an int, so we keep it local until we need to optimize perf diff --git a/zen/scope_guard.h b/zen/scope_guard.h index 67eb3053..f8c32127 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -22,7 +22,7 @@ inline int getUncaughtExceptionCount() { return std::uncaught_exceptions(); } #ifdef ZEN_LINUX static_assert(__GNUC__ < 5 || (__GNUC__ == 5 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support"); #else - static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 0), "check std::uncaught_exceptions support"); + static_assert(__clang_major__ < 7 || (__clang_major__ == 7 && __clang_minor__ <= 3), "check std::uncaught_exceptions support"); #endif namespace __cxxabiv1 @@ -60,6 +60,47 @@ enum class ScopeGuardRunMode }; +template +struct ScopeGuardDestructor; + +//specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant" +template +struct ScopeGuardDestructor +{ + static void run(F& fun, int exeptionCountOld) + { + (void)exeptionCountOld; //silence unused parameter warning + try { fun(); } + catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! + } +}; + + +template +struct ScopeGuardDestructor +{ + static void run(F& fun, int exeptionCountOld) + { + const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + if (!failed) + fun(); //throw X + } +}; + + +template +struct ScopeGuardDestructor +{ + static void run(F& fun, int exeptionCountOld) + { + const bool failed = getUncaughtExceptionCount() > exeptionCountOld; + if (failed) + try { fun(); } + catch (...) { assert(false); } + } +}; + + template class ScopeGuard { @@ -74,26 +115,7 @@ public: ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS) { if (!dismissed) - { -#ifdef _MSC_VER -#pragma warning(suppress: 4127) //"conditional expression is constant" -#endif - if (runMode != ScopeGuardRunMode::ON_EXIT) - { - const bool failed = getUncaughtExceptionCount() > exeptionCount; - if ((runMode == ScopeGuardRunMode::ON_FAIL) != failed) - return; - } - -#ifdef _MSC_VER -#pragma warning(suppress: 4127) //"conditional expression is constant" -#endif - if (runMode == ScopeGuardRunMode::ON_SUCCESS) - fun_(); //throw X - else - try { fun_(); } - catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"! - } + ScopeGuardDestructor::run(fun_, exeptionCount); } void dismiss() { dismissed = true; } diff --git a/zen/shell_execute.h b/zen/shell_execute.h index e6dcf7f5..060ba84d 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -82,14 +82,14 @@ void shellExecute(const Zstring& command, ExecutionType type) //throw FileError trim(commandTmp, true, false); //CommandLineToArgvW() does not like leading spaces std::vector argv; - { - int argc = 0; - LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc); - if (!tmp) - THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + commandTmp.c_str(), L"CommandLineToArgvW"); + { + int argc = 0; + LPWSTR* tmp = ::CommandLineToArgvW(commandTmp.c_str(), &argc); + if (!tmp) + THROW_LAST_FILE_ERROR(_("Incorrect command line:") + L"\n" + commandTmp.c_str(), L"CommandLineToArgvW"); ZEN_ON_SCOPE_EXIT(::LocalFree(tmp)); std::copy(tmp, tmp + argc, std::back_inserter(argv)); - } + } Zstring filepath; Zstring arguments; diff --git a/zen/stl_tools.h b/zen/stl_tools.h index b78dd5dd..058609e6 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -62,6 +62,7 @@ bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); size_t hashBytes(const unsigned char* ptr, size_t len); +size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len); //support for custom string classes in std::unordered_set/map @@ -216,20 +217,30 @@ 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; +#endif + return hashBytesAppend(basis, ptr, len); +} + + +inline +size_t hashBytesAppend(size_t hashVal, const unsigned char* ptr, size_t len) +{ +#ifdef ZEN_BUILD_32BIT + const size_t prime = 16777619U; +#elif defined ZEN_BUILD_64BIT const size_t prime = 1099511628211ULL; #endif - size_t val = basis; for (size_t i = 0; i < len; ++i) { - val ^= static_cast(ptr[i]); - val *= prime; + hashVal ^= static_cast(ptr[i]); + hashVal *= prime; } - return val; + return hashVal; } + } #endif //STL_TOOLS_H_84567184321434 diff --git a/zen/string_tools.h b/zen/string_tools.h index 92ca1654..ed6a1a54 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -77,7 +77,7 @@ template T copyStringTo(S&& str); template <> inline bool isWhiteSpace(char ch) { - assert(ch != 0); //std C++ does not consider 0 as white space + assert(ch != 0); //std C++ does not consider 0 as white space //caveat 1: std::isspace() takes an int, but expects an unsigned char //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) return static_cast(ch) < 128 && @@ -85,10 +85,10 @@ bool isWhiteSpace(char ch) } template <> inline -bool isWhiteSpace(wchar_t ch) +bool isWhiteSpace(wchar_t ch) { - assert(ch != 0); //std C++ does not consider 0 as white space - return std::iswspace(ch) != 0; + assert(ch != 0); //std C++ does not consider 0 as white space + return std::iswspace(ch) != 0; } diff --git a/zen/symlink_target.h b/zen/symlink_target.h index f50d1806..c4e166e8 100644 --- a/zen/symlink_target.h +++ b/zen/symlink_target.h @@ -135,7 +135,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"Not a symbolic link or junction."); //absolute symlinks and junctions use NT namespace naming convention while relative ones do not: - //http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#NT_Namespaces + //https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247#NT_Namespaces return ntPathToWin32Path(output); #elif defined ZEN_LINUX || defined ZEN_MAC @@ -158,7 +158,7 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError using namespace zen; #ifdef ZEN_WIN //GetFinalPathNameByHandle() is not available before Vista! - typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); + using GetFinalPathNameByHandleWFunc = DWORD (WINAPI*)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); const SysDllFun 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\"")); @@ -184,15 +184,12 @@ Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError &targetPath[0], //__out LPTSTR lpszFilePath, bufferSize, //__in DWORD cchFilePath, 0); //__in DWORD dwFlags - if (charsWritten == 0 || charsWritten >= bufferSize) - { - const std::wstring errorMsg = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)); - if (charsWritten == 0) - THROW_LAST_FILE_ERROR(errorMsg, L"GetFinalPathNameByHandle"); - throw FileError(errorMsg); - } + if (charsWritten == 0) + THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle"); + if (charsWritten >= bufferSize) + throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"GetFinalPathNameByHandle: buffer overflow."); - return Zstring(&targetPath[0], charsWritten); + return removeLongPathPrefix(Zstring(&targetPath[0], charsWritten)); //MSDN: GetFinalPathNameByHandle() always prepends "\\?\" #elif defined ZEN_LINUX || defined ZEN_MAC char* targetPath = ::realpath(linkPath.c_str(), nullptr); diff --git a/zen/utf.h b/zen/utf.h index 117b13bc..c0b2b4af 100644 --- a/zen/utf.h +++ b/zen/utf.h @@ -238,7 +238,7 @@ void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput template inline size_t unicodeLength(const CharString& str, char) //utf8 { - typedef typename GetCharType::Type CharType; + using CharType = typename GetCharType::Type; const CharType* strFirst = strBegin(str); const CharType* const strLast = strFirst + strLength(str); @@ -258,7 +258,7 @@ size_t unicodeLength(const CharString& str, char) //utf8 template inline size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wchar_t { - typedef typename GetCharType::Type CharType; + using CharType = typename GetCharType::Type; const CharType* strFirst = strBegin(str); const CharType* const strLast = strFirst + strLength(str); @@ -302,7 +302,7 @@ namespace implementation template inline size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-char { - typedef typename GetCharType::Type CharType; + using CharType = typename GetCharType::Type; const CharType* strFirst = strBegin(str); const size_t strLen = strLength(str); @@ -326,7 +326,7 @@ size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-cha template inline size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) //windows: utf16-wchar_t { - typedef typename GetCharType::Type CharType; + using CharType = typename GetCharType::Type; const CharType* strFirst = strBegin(str); const size_t strLen = strLength(str); -- cgit