summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2016-05-24 22:10:57 +0200
committerDaniel Wilhelm <daniel@wili.li>2016-05-24 22:10:57 +0200
commit9043b32bb1835628c5a1d8be4a271c848443c629 (patch)
tree98ccb4936562731d9cae02a486441dfd446e8a4e /zen
parent8.0 (diff)
downloadFreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.tar.gz
FreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.tar.bz2
FreeFileSync-9043b32bb1835628c5a1d8be4a271c848443c629.zip
8.1
Diffstat (limited to 'zen')
-rw-r--r--zen/build_info.h2
-rw-r--r--zen/dir_watcher.cpp9
-rw-r--r--zen/file_access.cpp96
-rw-r--r--zen/file_access.h3
-rw-r--r--zen/file_error.h1
-rw-r--r--zen/file_id_def.h36
-rw-r--r--zen/file_io.cpp10
-rw-r--r--zen/file_traverser.cpp18
-rw-r--r--zen/format_unit.cpp2
-rw-r--r--zen/guid.h2
-rw-r--r--zen/scope_guard.h64
-rw-r--r--zen/shell_execute.h12
-rw-r--r--zen/stl_tools.h21
-rw-r--r--zen/string_tools.h8
-rw-r--r--zen/symlink_target.h17
-rw-r--r--zen/utf.h8
16 files changed, 210 insertions, 99 deletions
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<Pimpl>())
{
- 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<wchar_t> 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<CreateSymbolicLinkFunc> 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<DeviceId, FileIndex> 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<DeviceId, FileIndex> 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<std::string>(__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<std::string>(__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<DWORD>(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
@@ -61,6 +61,47 @@ enum class ScopeGuardRunMode
template <ScopeGuardRunMode runMode, typename F>
+struct ScopeGuardDestructor;
+
+//specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
+template <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_EXIT, F>
+{
+ 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 <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_SUCCESS, F>
+{
+ static void run(F& fun, int exeptionCountOld)
+ {
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (!failed)
+ fun(); //throw X
+ }
+};
+
+
+template <typename F>
+struct ScopeGuardDestructor<ScopeGuardRunMode::ON_FAIL, F>
+{
+ static void run(F& fun, int exeptionCountOld)
+ {
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (failed)
+ try { fun(); }
+ catch (...) { assert(false); }
+ }
+};
+
+
+template <ScopeGuardRunMode runMode, typename F>
class ScopeGuard
{
public:
@@ -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<runMode, F>::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<Zstring> 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<size_t>(ptr[i]);
- val *= prime;
+ hashVal ^= static_cast<size_t>(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 <class T, class S> 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<unsigned char>(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<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\""));
@@ -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 <class CharString> inline
size_t unicodeLength(const CharString& str, char) //utf8
{
- typedef typename GetCharType<CharString>::Type CharType;
+ using CharType = typename GetCharType<CharString>::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 <class WideString> inline
size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wchar_t
{
- typedef typename GetCharType<WideString>::Type CharType;
+ using CharType = typename GetCharType<WideString>::Type;
const CharType* strFirst = strBegin(str);
const CharType* const strLast = strFirst + strLength(str);
@@ -302,7 +302,7 @@ namespace implementation
template <class CharString> inline
size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-char
{
- typedef typename GetCharType<CharString>::Type CharType;
+ using CharType = typename GetCharType<CharString>::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 <class WideString> inline
size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) //windows: utf16-wchar_t
{
- typedef typename GetCharType<WideString>::Type CharType;
+ using CharType = typename GetCharType<WideString>::Type;
const CharType* strFirst = strBegin(str);
const size_t strLen = strLength(str);
bgstack15